KCon 2018 议题解读:Python 动态代码审计
作者:niexinming
作者博客:https://www.n0tr00t.com/2018/08/30/Python-dynamic-codereview.html
议题 PPT 下载地址:/static/ppt/KCon-2018-Python-dynamic-codereview.pptx
0x00 动态代码审计用处
- 大型项目代码结构复杂
- 有些危险的功能隐藏较深(危险的定时计划任务、sqlite数据库任意创建导致任意文件覆盖……)
- 提高效率,希望通过一些黑盒的方法比较快速的找到漏洞。
0x01 常见漏洞分类
- 数据库操作
- 敏感函数的调用和传参
- 文件读写操作
- 网络访问操作
0x02 正文目录
- 数据库general log 日志
- hook关键函数
- 结合auditd
- http盲攻击
- fuzzing
0x03 数据库日志
general-log是记录所有的操作日志,不过他会耗费数据库5%-10%的性能,所以一般没什么特别需要,大多数情况是不开的,例如一些sql审计和代码审计等,那就是打开来使用了
Mysql通过命令行的方式打开general log:
set global general_log_file=''; set global general_log=on;
Postgresql 通过编辑配置文件打开general log:
编辑:postgresql.conf
log_directory = 'pg_log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_statement = 'all'
打开之后用burp向web api发送一些包含sql注入的畸形数据
利用Linux的grep指令过滤出sql执行中的ERROR关键字,就可以很快的找到sql注入漏洞
0x04 Hook关键函数
根据基于python的自动化代码审计和pyekaboo这两个文章的启发而来
Python对象可以被轻易的改变,通过设置PYTHONPATH这个环境变量,使python在加载string这个模块的时候优先加载我自定义的string模块,下图演示的是劫持string模块的upper函数
之后我的思路是,通过劫持python的函数,把输入到危险函数的参数输出到日志中,最后调用正常的python函数
这样就可以劫持我们认为敏感的函数
劫持模块的demo:
import imp import sys
class _InstallFcnHook(object):
def __init__(self,fcn):
self._fcn=fcn
def _pre_hook(self,*args,**kwargs):
print "hook:"+str(args)
return (args,kwargs)
def __call__(self,*args,**kwargs):
(_hook_args,_hook_kwargs)=self._pre_hook(*args,**kwargs)
retval=self._fcn(*_hook_args,**_hook_kwargs)
return retval
fd,pathname,desc=imp.find_module(__name__,sys.path[::-1])
mod =imp.load_module(__name__,fd,pathname,desc)
system=_InstallFcnHook(system)
劫持效果:
这就意味着我们可以劫持危险的函数,把参数输出到日志里面,通过日志信息,可以判断这些参数是否可以被输入控制。通过这种方式可以方便找到ssti、pickle反序列化漏洞和命令执行漏洞等其他的漏洞
而且这些可以很方面的拓展到其他的模块中
- cd hook/ #进入到hook模块所在目录
- cp os.py xxx.py #把os模块复制一份,xxx模块是你想hook的模块
- 编辑xxx.py :注释掉原来被hook的函数,添加想要hook的函数,下面的示例是hook了subprocess模块中check_call函数
Ps 需要填一些坑:
- 修改启动代码从shell中启动python web
因为有一些python web是从wsgi中启动的,这些只要简单修改启动代码就可以从WSGI方式启动切换到shell启动
- 从内存中删掉已加载的模块
一些模块通过import动态导入,需要在动态导入后通过del modules删掉被装载的模块
- 关闭调试选项
例如在flask启动时将debug选项设置为false,否则会产生两个python进程
- 其他问题
Python web性能下降、代码不兼容、有些模块无法被hook,其中python的内置函数open就无法通过这样的方式被hook。
0x05 结合Auditd
网上有很多方法去获取python web的文件读写操作,其中有一种是在文件读写函数前面添加装饰器,但是我觉得那种方法太过于烦琐,且不能覆盖到所有的文件读写操作,那怎么不通过修改原始代码去获取文件读写操作?
可以利用Auditd:
auditd(或 auditd 守护进程)是Linux审计系统中用户空间的一个组件,其可以记录Linux中文件,进程等操作,且安装方便
CentOS 默认安装
Ubuntu 安装:apt-get install auditd
只要简单的配置就可以监视一些文件操作
sudo auditctl -a exclude,always -F msgtype!=PATH -F msgtype!=SYSCALL #记录文件操作 sudo auditctl -a always,exit -F arch=b64 -S execve -k rule01_exec_command #记录所有的shell指令的执行
sudo auditctl -a always,exit -F pid=$mypid #记录指定进程文件操作
执行 sudo auditctl -l 查看所有的规则
只要发送一些包含目录跳转的畸形数据给webapi,如有任意文件读取的漏洞就可以很快的发现。
因为Auditd日志中包含大量其他的日志,通过grep和关键字高亮工具(https://github.com/paoloantinori/hhighlighter)进行查看日志(grep过滤PATH,高亮name)
除了记录文件读取,还能记录文件的其他操作
这样就可以找到:
- 任意文件上传
- 任意文件创建
- 任意文件读取
- 任意文件删除
0x06 http盲攻击
怎么解决诸如ssrf等网络操作的问题?
ssrf攻击可以让黑客越过防火墙来探测或者攻击一个大型企业的内网
那么在动态代码审计过程中就可以通过构造特定的poc,来探测代码中是否有对应的漏洞
可以构造请求dns解析的数据
- 命令执行的常见形式
Ping –c 1 xxx.pw
- ssrf的常见形式
url=http://xxx.pw
- xxe的常见形式
通过dns日志的记录可以很快找到诸如ssrf、xxe、命令执行等,包括可以找到一些隐藏的比较深的定时计划任务的中漏洞
0x07 fuzzing
做完上面的一些工作之后,我在想如何提高我的工作效率,毕竟我们部门产品众多,但是代码审计这个工作只有我一个在做。我把正常数据,畸形数据,poc数据,等其他数据全发给web api,然后在审计数据库日志,危险函数日志,auditd日志,dns日志,web报错日志,希望能从日志中发现潜藏的漏洞
利用burp的intruder这个功能就可以实现,测试用例可以使用wooyun提供的fuzzing数据
Ps 还是有几个自己需要处理的问题:
- 需要根据自己的业务类型制定自己的测试用例
- 自己要想办法处理产生的大量的日志
- 其他问题
0x08 TODO
- 自动化部署客户端
- 开发一个日志处理平台
- 尽可能的覆盖更多的漏洞类型
- 丰富测试用例
- 开源( https://github.com/niexinming/python_hook)
以上是 KCon 2018 议题解读:Python 动态代码审计 的全部内容, 来源链接: utcz.com/p/199107.html