Xcheck之Python安全检查引擎
0x00 Python安全检查引擎
Xcheck的Python分析引擎,能够自动分析Python写的Web应用,检测诸如命令注入、SQL注入、URL跳转、SSRF、XXE等常见的Web安全漏洞。
Python语言的Web框架十分丰富,对于主流的Web框架如Django、Flask、Tornado、Webpy、Bottle等,Xcheck已经内建支持;对于其他Web框架,用户可以基于Xcheck的扩展框架编写自定义规则,简单快速地完成适配。
0x01 Python作者谈Python静态分析
为什么Python静态分析这么难? 吉多的答案是:
- Python解释器的原因
- Python没有类型标注(3.5版本之后支持,但不是强制)
- Python的动态语言特性
- Python用户偏好写一些奇技淫巧的代码(crazy hacks)
本文重点谈Xcheck静态分析如何处理程序的动态特性。
0x02 一般程序的动态性
工具在静态分析时遇到的最大挑战,即程序的动态性。
所谓动态性,是指程序的行为受运行时外部输入数据的影响。
以下面这个代码片段为例:
name = input()
now = Date.Now()
if now.is_morning():
print('Good morning! %s.' % name)
else:
print('Good afternoon! %s.' % name)
这里name
和now
都是运行时才确定的数据,程序实际执行哪个分支是运行时才确定的。
类似这样的“动态性”在程序中无处不在:
- 走哪条分支
- 循环执行多少次
- 取数组哪个下标的数据
- 取Map里哪个key的数据
- 等等
0x03 Python程序的动态性
Python写的程序除了一般程序都有的“动态性”之外,还有语言特性带来的“动态性”,因而给静态分析带来了更大的挑战。
比如(具体的代码示例可以在吉多的材料里找到):
- 模块以怎样的顺序导入,
sys.path.insert(0, ??)
- 存取对象的哪个属性?
setattr(A, ??, x)
- 任意修改某个局部、全局变量?
locals()[??] = 12
- 任意修改内建模块的内容和行为,
__builtins__.len = xxx
- 任意魔改操作符的行为,
def __add__(xx)...
- 等等
以第二条为例简要说明。
当你这么写代码的时候,你要操作对象的哪个属性是静态确定的(名字为foo
的属性):
A.foo = xxx
print(A.foo)
但是,Python允许开发者这样写代码:
setattr(A, ???, xxx)
print(getattr(A, ???))
???
是一个变量,是运行时才确定的。
0x04 Xcheck的设计
像Python这样的动态语言特性,为Python开发者提供了遍历,但同时增加了静态分析的难度。
在实践中,静态分析工具往往无法准确理解这类动态特性的代码,尤其是实现动态调用相关功能的时候。
我们在设计Xcheck时就十分理性地考虑到静态分析工具在实践中的局限性。工具是死的,但人是活的。Xcheck更加关注:怎样将人的知识准确简单地“传授”给工具?
因此,我们为Xcheck设计了一套扩展框架,将用户基于Xcheck接口编写的“经验”通过扩展规则传入分析引擎,发挥工具极致的静态分析能力。
0x05 SaltStack漏洞案例
SaltStack是Python语言写的一个分布式运维系统。
SaltStack官方在2020年11月3日发布安全补丁修复了三个漏洞,其中包括一个在未授权的情况下通过salt-api接口执行任意命令的严重漏洞。
经过人工分析,这个漏洞的关键入口在salt/netapi/__init__.py:NetapiClient.run()
方法,其中low
变量的内容是用户可控的(称之为污点)。
- 如上所述,
salt.netapi.NetapiClient.run
方法中引入了静态分析工具无法理解的动态特性代码,这里X.override
的意思是告诉工具重载这个方法,而这个方法的行为定义在mock_run
函数里 mock_run
方法首先拿到self
变量所引用的对象,即一个NetapiClient
类实例- 定义合法的
client
字段取值 - 取每个合法的
client
值,取self
的属性,即attr = self.get_attr(name)
- 属性应该是个方法,将其kwargs参数设置为污点,然后触发调用,
attr.call()
加上这个规则之后,工具的分析结果如下: