Python 程序分析工具调研

Github 上的开源工具,* 号表示仓库已经不再维护

工具 描述 开发语言
PySonar2 语义索引器,用于批量处理大型代码库。牺牲实时索引能力,使用过程间分析来推断变量、参数和函数的类型。目前作为大型代码索引服务的基础引擎,Sourcegraph 中也有使用该工具。 Java
Sourcegraph 代码搜索和导航引擎,帮助阅读和理解大型项目的代码。目前支持 Go、Java、Python 等多种编程语言,能够以浏览器插件的形式提供在线代码阅读的功能。 Go、TypeScript
coala 使用配置文件检测和修复代码,可自定义规则和标准检查代码质量,扩展性强。支持 Python、Java、C/C++、JavaScript 等多种编程语言,支持作为编辑器插件使用。 Python
vprof 性能分析工具,为 Python 程序的运行时间、内存使用情况等提供交互式可视化,不适用于大型代码的分析。 Python、JavaScript
Code2Flow 为动态语言生成生成调用图(Call Graph),目前支持 Python、JavaScript、Ruby、PHP。基础流程:AST -> 函数定义 -> 函数调用点 -> 连接点 ,主要是提供一个粗略的概览。缺点是没有定义的函数、不同命名空间相同名字的函数将被跳过等,因此并不适用于大型代码的分析。 Python、JavaScript
pydeps Python 模块依赖可视化,导出 .svg.png 文件。只考虑导入的文件(模块必须安装),在 Python 字节码中查找导入操作码,同时支持外部模块的分析。 Python
pycallgraph* 为 Python 应用生成调用图(Call Graph),包括函数的调用次数、执行时间等信息,提供一定程度的性能分析功能。同时支持过滤函数,避免生成的图太大无法分析,默认导出 .png 文件。 Python
undebt* 执行大规模自动化代码重构,根据自定义模式对代码进行查找和替换,适用于任何语言。 Python
pyt* 基于理论基础的 Python Web 应用静态分析(控制流图、定点、数据流分析),能够检测 SSRF、SQL 注入、XSS、目录遍历等攻击,支持自定义源点和汇点、传播污点的函数。目前已停更,作者建议使用 Pyre 。 Python
Pyre (Facebook) 性能类型检查器,集成静态分析工具 Pysa(污点分析)Pysa 通过跟踪数据流实现安全检查,依赖类型注释,能够高速处理大型代码。 OCaml、Python
Bandit 构建 AST 并使用插件检查安全问题,最初在 OpenStack 安全项目中开发,后来被重新定位到 PyCQA。主要用于扫描危险函数,支持自定义漏洞测试和扩展插件。 Python
Sourcetrail 跨平台的源代码浏览器,支持 C/C++、Java、Python,提供搜索、代码、图形三个交互式视图。亮点是给出了源代码的结构,但只有符号名称。 C++、Java
radon 计算源代码的度量(metrics),包括 McCabe、Halstead、可维护性索引三种度量指标,支持在 coala 中使用。主要用于评估代码的复杂度。 Python
xenon 基于 radon 的监测工具,检查代码复杂性。可应用于 CI/CD,在每次提交代码时运行,根据自定义代码复杂性阈值返回成功或失败。 Python
jedi Python 静态分析工具,侧重于自动补全和跳转,通常作为编辑器的插件使用。 Python
mypy 静态类型检查器(PEP 484),能够对 Python 程序中的类型注释执行静态检查。 Python
pyright (Microsoft) 可用于大型 Python 源代码库的快速类型检查器,使用内置的类型存根进行类型推断,可以在监视模式下运行,支持增量更新。 TypeScript、Python
pytype (Google) 静态分析器,无需类型注释就能检查和推断 Python 代码的类型,也利用了类型存根(pyi 文件)。 Python
Vulture 查找 Python 程序中未使用的代码(死代码),存在漏报,并且隐式调用可能会被误报。支持设置最小置信度、白名单等功能,可以对单个文件或目录的 py 文件执行分析。 Python
PyCG 使用静态分析为 Python 代码生成调用图,支持高阶函数、类继承、导入的模块、嵌套定义。详见论文 ICSE 2021 paper Python
Wily 复杂性检查 Python
McCabe 复杂性检查 Python
it 代码检查工具/框架 Python

以及一些闭源工具

工具
CodeQL 代码分析引擎
Fortify SCA 静态代码审计工具
Pylance (Microsoft) 基于 pyright 的 Python 代码静态分析工具
DeepSource 支持多种语言的静态分析,个人免费
Codacy 能够通过静态代码分析获得关于安全问题、代码覆盖率、代码重复率、代码复杂性的信息

前段时间的 log4j 貌似就是用 CodeQL 找到的👍

Bandit

Bandit 可以用来搜索 Python 代码中常见的安全问题,在检测过程中,Bandit 会对每一份 Python 代码文件进行处理,基于 ast 模块生成抽象语法树,然后针对每一个 AST 节点运行相应的检测插件。完成安全扫描之后,Bandit 会直接给用户生成检测报告。

拿 keystone 源码进行测试,测试插件分组如下,可以通过配置进行选择:

ID Description
B1xx misc tests
B2xx application/framework misconfiguration
B3xx blacklists (calls)
B4xx blacklists (imports)
B5xx cryptography
B6xx injection
B7xx XSS

测试 keystone

环境准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# virtualenv
sudo apt install python3-virtualenv -y

# 创建虚拟环境
virtualenv bandit-env
python3 -m venv bandit-env

# 激活虚拟环境
source bandit-env/bin/activate

# 安装 Bandit
pip install bandit

# 创建测试目录
mkdir bandit-test && cd bandit-test

# 下载 keystone 源码
git clone https://github.com/openstack/keystone.git --single-branch --branch stable/victoria

主要的功能就是黑名单和自定义检测规则。

1
2
3
4
5
6
7
8
9
# 运行 Bandit
bandit -r keystone -o test.txt -f txt
bandit -r keystone -o test.html -f html

# 复制到本地查看
scp -r jck@10.123.123.123:~/bandit-test/test.html /c/Users/linki/Desktop

# 配置文件
.bandit

导出的文件 test.html 显示扫描了 118379 行代码,蓝色表示低危,橙色表示中危,这里所有的都只出现在 tests 的测试文件中。

过滤测试文件夹,重新执行检测,得到 html 文件:testx.html 显示扫描 41415 行代码,没有用到危险函数。

Bandit 原先是 OpenStack 的项目,部分组件的开发有使用 Bandit 进行检查,其中就包括 keystone ,因此用 Bandit 扫描组件的意义可能不是很大。

1
2
3
4
5
# 过滤测试文件夹
bandit -r keystone/keystone/ --exclude keystone/keystone/tests/ -o testx.html -f html

# 复制到本地查看
scp -r jck@10.123.123.123:~/bandit-test/testx.html /c/Users/linki/Desktop

Vulture

同样利用了 ast 模块生成抽象语法树,在遍历时记录已定义和使用的对象名称,最后报告未使用的对象(死代码)。还通过查找 return、break、continue、raise、不满足条件的 if 条件、while 条件来检测不可达的代码。因为只考虑对象名称,没有考虑作用域,因此可能存在漏报,而且隐式调用可能会被误报。

测试 keystone

具体的安装与测试操作如下,执行结果导出为 unused.txt,最后给出了置信度。执行速度非常快,但给出的结果信息量少,只能作为简单的参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建文件夹
mkdir vulture-test

# 设置虚拟环境
python -m venv vulture-test

# 激活虚拟环境
cd vulture-test
source bin/activate

# 安装 Vulture
pip install vulture

# 下载 keystone 源码
git clone https://github.com/openstack/keystone.git --single-branch --branch stable/victoria

# 执行分析
vulture keystone/keystone/ --exclude keystone/keystone/tests/ --sort-by-size > unused.txt

Pysa

入门教程可以看:Pyre 污点分析工具 Pysa 使用教程

测试 keystone

环境准备

安装依赖

1
2
3
4
5
6
7
# 更新 Python3.8.x
sudo apt-get update
sudo apt install python3.8 python3.8-venv python3.8-dev python3-pip -y

# update-alternatives 配置 python 指向 python3.8.x 后
python -m pip install --upgrade setuptools
pip install wheel

初始配置

1
2
3
4
5
6
7
8
9
10
11
12
# 创建虚拟环境
python -m venv ~/.venvs/pysa

# 激活虚拟环境
source ~/.venvs/pysa/bin/activate

# 更新工具
pip install wheel
python -m pip install --upgrade setuptools

# 安装 Pyre 和 SAPP
pip install pyre-check fb-sapp

分析 keystone 源码

下载 Victoria 版本的 keystone 源码并安装依赖,能够让 Pysa 使用和导入模块对应的污点模型,检测导入模块内部的污点源和汇。

1
2
3
4
5
6
7
8
9
10
11
# 创建测试目录
mkdir pysa-test && cd pysa-test

# 下载源码
git clone https://github.com/openstack/keystone.git --single-branch --branch stable/victoria

# 进入源码目录
cd keystone

# 安装 keystone 依赖
pip install -r requirements.txt

初始化配置,添加类型注释(Python 3.5+)部分会失败,导入过滤器

1
2
3
4
5
6
7
8
9
10
11
# 初始化配置
pyre init

# 编辑配置文件
vim .pyre_configuration

# 为源码添加类型注释
pyre infer -r -i

# 导入过滤器设置 SAPP
sapp filter import ~/.venvs/pysa/lib/pyre_check/pysa_filters

过滤器 pysa_filters 包含以下配置文件

  • 5001-code-command-injection.json
  • 5005-sql-injection.json
  • 5011-userdata-to-filesystem.json
  • 6065-argument-injection.json
  • 6066-unsafe-deserialization.json
  • 6073-server-side-template-injection.json

以 5001-code-command-injection.json 为例,设置不展示元数据(feature)以及显示的追踪长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"name": "Command and Code Injection",
"description": "High signal filter for 5001 Shell Injection",
"features": [
{
"mode": "none of",
"features": [
"always-type:scalar",
"always-type:bool",
"via:shell_escape",
"always-via:shell_escape"
]
}
],
"codes": [
5001
],
"traceLengthFromSources": [
0,
3
],
"traceLengthToSinks": [
0,
4
]
}

运行 Pyre 并将结果存储在 pysa-runs 文件夹,使用 SAPP 可视化展示

1
2
3
4
5
6
7
8
# 运行 Pysa
pyre analyze --no-verify --save-results-to ./pysa-runs --dump-call-graph

# 导入文件 运行 SAPP
sapp analyze ./pysa-runs/taint-output.json

# 运行服务器
sapp server

由于运行 OpenStack 服务的主机上 keystone 绑定了 5000 端口,而 SAPP 不提供修改端口的选项,所以重新弄了台主机专门用于分析。另外,SAPP 的 UI 默认绑定在 localhost 上,需要在本地使用 iptables 配置转发。

1
2
3
4
5
# 启用本地转发
sudo sysctl -w net.ipv4.conf.ens160.route_localnet=1

# iptables 配置规则
sudo iptables -t nat -I PREROUTING -p tcp -d 10.112.148.73/24 --dport 2222 -j DNAT --to-destination 127.0.0.1:5000

通过 Web UI 查看结果,一共找出 13 个问题,全是可能的 XSS 漏洞:

导入的过滤器可以在 Filter 中使用,不过 keystone 中没有检测出这些问题

点开一个问题的 Traces 展示:

  • UserControlled 类型的污点数据一直传播到 keystone.api.os_oauth1.RequestTokenResource.post 函数
    • 最短的传播距离
  • 源点和汇点的追踪在 keystone.api.os_oauth1.RequestTokenResource.post 交汇
  • UserControlled 类型的污点数据从 keystone.api.os_oauth1.RequestTokenResource.post 开始,传播到 XSS 类型的汇点
    • 和交汇相同

使用 pyre query 导出数据:graph.jsonhierarchy.json

1
2
3
4
5
6
7
8
9
10
11
# 启动服务器
pyre start

# 导出调用图
pyre query "dump_call_graph()" | python -m json.tool > graph.json

# 导出类层次结构(省略变量类型)
pyre query "dump_class_hierarchy()" | python -m json.tool > hierarchy.json

# 关闭服务器
pyre stop

结语

差不多快半年前的东西了,而且目前也没有在做 Python 的静态程序分析。既然当时都调研和试用过了,那就稍微理一下存档这份流水账.jpg