-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cpython历史漏洞分析及其fuzzer编写 #64
Comments
从python代码层面fuzz怎么样?这样就不用自己写很多的c代码了。 比如
|
@leveryd 没有太理解你说的,你说的是,写fuzz工具用python,还是什么其他的?如果使用python代码写fuzz工具,这是完全可以的,但是有一点就是python有一个进程锁的概念,除非你用多进程multiprocessing库,但是这里又有一个问题,系统开销会过大。我自己的工具第一版是用纯python写的,效率要比我现在用rust写的工具低很多,你都可以试试 |
额,我之前的意思是用 afl去fuzz
|
非常感谢您的文章,学习到了很多东西。最后的fuzzer编写章节我不会复现,请问在fuzzer.c中添加完成代码以后,如何编译?如何开始启动Fuzzer?期待您的回复! |
@tylzh97 你参照cpython的编译即可,还有就是libfuzzer的调用你也可以搜搜相关的资料 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
cpython历史漏洞分析及其fuzzer编写
历史漏洞分析
主要历史漏洞来源于cpython hackerone
这篇文章首先分析三个
cpython
历史漏洞,在我们简单熟悉了cpython
的源码结构以后,再来编写一个fuzzer
,其实算是添加fuzzer
Integer overflow in _json_encode_unicode
调试环境
漏洞官方issue
找到最近的一个未修复漏洞的
commit
确定漏洞复现
commit: 758d60baaa3c041d0982c84d514719ab197bd6ed
使用
gcc
编译该commit
代码使用的
poc.py
使用
gdb
调试可以发现程序确实是崩溃了,但是我们没有看到
output_size
的值,为了观察其值,我们将Makefile
中的-O3
优化改为-O0
,重新编译,再次使用gdb
调试来分析一下溢出原因,溢出出现在
_json.c:escape_unicode
函数中修复
Integer overflow in _pickle.c
漏洞官方issue
利用上面的方法找到最近的未修复
commit:614bfcc953141cfdd38606f87a09d39f17367fa3
poc.py
编译之后直接利用
gdb
调试poc
(编译不使用-fsanitize
选项)可以发现由于整数溢出,已经导致了一个越界写的漏洞。
根据其调用栈,我们来一步一步分析其溢出的原因
来看一下最后出错函数
根据
gdb
调试显示,由于溢出导致new_size * sizeof(PyObject *)
数值为0
,当其为0
时传入也就是
realloc(p, 1)
,执行成功,接下来就会造成越界写继续回溯,看看
new_size
如何得到再次回溯,寻找
idx
的来源查看
calc_binsize
函数其最终来源于我们的输入值,所以通过修改我们的输入值,可以成功导致基于堆的越界写
修复
int and float constructing from non NUL-terminated buffer
找到未修复
commit:9ad0aae6566311c6982a20955381cda5a2954519
官方issues
这个issue我找到了
commit
,搭建了环境,但是没有复现成功,最主要的是,对我们寻找fuzz
方面没有太大帮助,但是对我们理解字符串转换的危害还是很有帮助的,所以我们从原理上来跟一下源码那就通过
issue
中提到的代码,从理论上来复现一下poc.py
调用栈
直接看代码,首先是
floatobject.c
中的PyFloat_FromString
跟进
PyOS_string_to_double
跟进
PyErr_Format
函数继续跟进
PyUnicode_FromFormatV
根据调用栈跟进
unicode_fromformat_arg
由于
format
是由%s
构成,所以我们只看s
部分利用
va_arg
直接读取了参数,并将指针s
指向该地址,继续跟进unicode_fromformat_write_cstr
直接利用
strlen
计算上面的参数长度,如果str
不是一个以\0
结尾的字符串,那么接下来利用长度访问该地址的数据将会出现越界读写的问题该漏洞主要原因来源于
floatobject.c
中的代码,%s
的数据由强制转换而来提醒我们,在做强制转换时,要注意检查是否可以转换,转换后会不会造成漏洞
fuzzer编写
上文我们已经分析完
cpython
的三个漏洞了,对cpython
有了一定的了解,那么我们就开始编写cpython
的fuzzer
代码。在编写前,我们来看看
cpython
自己有没有fuzz
测试模块,简单搜索一下,发现在Modules/_xxtestfuzz/
目录下存在fuzz
代码,这就好办了,我们直接在此基础上添加我们想要测试的模块的fuzz代码就行首先阅读一下
fuzz.c
大概的代码逻辑就会发现,如果想要添加模块的fuzz
代码,还是很简单的主要需要修改的就两个部分,拿
struck.unpack
来举例子第一步,初始化
第二步,调用需要
fuzz
的函数,并过滤一些不必要的错误再添加一下
libfuzzer
调用代码整个过程完事
这里其实比较麻烦的是过滤错误信息,因为你不一定能知道你要
fuzz
的模块的所有错误信息,很有可能过滤不全,在fuzz的时候会出错,导致需要重新添加过滤条件,再重新开启fuzz,整个过程,我也没有很好的办法,就是不停的试错,最后把无关的错误信息都过滤,下面就会遇到这样的问题我们上面分析的第一个漏洞
json
已经存在fuzz
模块了,那么我们就添加第二个pickle
模块的fuzz
代码首先初始化
pickle
本身的错误对象,我们需要到_pickle.c
里面去找,在该文件的最后我们找到了添加错误对象的代码进一步完善初始化代码
继续编写调用代码
添加
libfuzzer
调用代码这里需要有一点注意的,如果我们直接利用上面的编译,可以使用,但是很快
fuzz_pickle_loads
就会退出,退出的原因在于
libfuzzer
会有内存限制,即使提高了libfuzzer
的内存使用量,但随着我们测试的深入,依然会因为内存不足导致出问题,这个问题困扰了我很久,在不断试错,不断调试后发现最后通过修改
cpython
的源码解决具体修改
Include\pyport.h
里面的代码修改为
这样就解决了
libfuzzer
内存限制,导致fuzz
不断失败的问题修改完后,可能
cpython
某些模块会因为内存过小导致编译失败,这里可以略过,只要我们的fuzzer
程序能跑起来就行整个过程折腾了我两天的时间,各种编译和运行错误,最后成功执行
我用六个线程,大概跑了一周的时间,没有发现任何
crash
,果然这种顶级开源项目相对来说代码质量还是不错的。有兴趣的可以自己跑一下,万一跑出来漏洞了呢:)总结
最近大部分时间都是在看开源软件的漏洞,比如网络组件,开源语言等等,开源软件的好处就是我们可以直接根据
commit
,定位到漏洞,了解其漏洞原理和修复方法,之后就是不断分析其中的漏洞,然后想办法能不能自己编写一个fuzzer
把这些漏洞跑出来,整个过程不断提高自己编写fuzzer
的能力和分析漏洞的能力。这类文章我应该会有一个开源漏洞
fuzz
系列,这个是第一篇,感兴趣的话可以关注一下我的博客The text was updated successfully, but these errors were encountered: