carlmasters02/python-static-analyzer

GitHub: carlmasters02/python-static-analyzer

一个基于 AST 解析与污点跟踪的教学型 Python 静态分析工具,用于在不执行代码的情况下检测 SQL 注入和命令注入漏洞。

Stars: 0 | Forks: 0

# pytaint [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/carlmasters02/python-static-analyzer/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/carlmasters02/python-static-analyzer/branch/main/graph/badge.svg)](https://codecov.io/gh/carlmasters02/python-static-analyzer) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) `pytaint` 将 Python 文件解析为 AST,识别不可信的 *sources* 和危险的 *sinks*,并使用简单的污点跟踪来标记那些在没有经过净化(sanitization)的情 况下从 source 流向 sink 的数据。它会报告文件名、行号,以及每个发现结果可 被利用的 *原因*。该项目作为一个学习项目增量构建而成——每个模块都有大量注 释,并且每个核心安全概念都在代码中进行了内联解释。 ## 演示 ``` $ pytaint samples/ samples/vuln_command_injection.py:15 [command-injection] untrusted data (variable 'host') reaches os.system() — os.system() passes its string argument to the system shell samples/vuln_sql_injection.py:12 [sql-injection] untrusted data (variable 'username') reaches cursor.execute() — a DB cursor .execute() runs its string as SQL ... 4 finding(s) across 2 file(s) scanned ``` ## 安装 需要 Python 3.9+。除标准库外无其他依赖。 ### 选项 A — 使用 Python (pip) ``` git clone https://github.com/carlmasters02/python-static-analyzer.git cd python-static-analyzer pip install . # installs the `pytaint` command pytaint samples/ ``` ### 选项 B — 使用 Docker(无需 Python) ``` git clone https://github.com/carlmasters02/python-static-analyzer.git cd python-static-analyzer docker build -t pytaint . docker run --rm -v "$PWD/samples":/scan pytaint /scan ``` ## 用法 ``` pytaint PATH [--format {text,json}] [--no-taint-params] [--no-color] [--version] PATH file or directory to scan (recursive) --format output format: text (default) or json --no-taint-params don't treat function parameters as tainted (higher precision, lower recall) --no-color disable ANSI colors in text output --version show version and exit ``` 当干净时退出代码为 `0`,存在发现结果时为 `1`(适用于 CI),路径错误时为 `2`。你也可以将其作为模块运行:`python -m pytaint samples/`。 ## 工作原理 — 四个核心概念 **AST (抽象语法树)。** 纯文本形式的源代码很难进行分析(`grep` 无法区分代码 和注释)。Python 内置的 `ast` 模块将文本解析为反映代码结构的 *树*,因此 `os.system(user_input)` 会变成一个我们可以精确检查的 `Call` 节点。参见 [src/pytaint/parser.py](src/pytaint/parser.py)。 **Sources 和 sinks。** **Source** 是不可信数据进入程序的地方——例如 `input()`、`request.args`、`sys.argv`,以及(假设的)函数参数。**Sink** 是 危险的操作——例如 `os.system()` / `subprocess.*(shell=True)`(→ 命令注入) 和 `cursor.execute()`(→ SQL 注入)。知识库位于 [src/pytaint/signatures.py](src/pytaint/signatures.py)。 **污点分析。** 我们将来自 source 的数据标记为“已污染”(tainted),并在赋值 和字符串操作(`+`、f-strings、`.format()`、`%`)中对其进行追踪。如果被污染 的数据到达了 sink,并且没有被 sanitizer(`shlex.quote`、`int(...)` 或 SQL 参数化)清理掉,我们就会将其报告出来。这正是让该工具变得 *精确* 的原因,而不是盲目地标记每一个 `os.system` 调用。参见 [src/pytaint/taint.py](src/pytaint/taint.py)。 **精确率与召回率。** *精确率* = 在我们标记出的结果中,有多少是真实的? *召回率* = 在真实的漏洞中,我们发现了多少个?它们之间存在权衡;而 F1 分数 则平衡了这两者。根据下方的带标签基准测试进行了测量。 ``` source file ──ast.parse──► AST ──find sources & sinks──► taint engine (introduce → propagate → check sinks w/ sanitizers, shell=, SQL params) ──► findings ──► text / JSON report ``` ## 基准测试结果(精确率 / 召回率) 基于 [benchmark/cases/](benchmark/cases/) 进行评分——包含 22 个带标签的测 试用例,涵盖了真实的漏洞、安全的“相似代码”(参数化 SQL、`shell=False`、已 净化的输入),以及特意用来测试引擎已知盲点的用例。可以使用 `python -m benchmark.score` 复现该结果。 | 模式 | TP | FP | FN | TN | 精确率 | 召回率 | F1 | |------|---:|---:|---:|---:|----------:|-------:|---:| | default(参数被污染) | 9 | 1 | 3 | 9 | 90% | 75% | 82% | | `--no-taint-params` | 4 | 1 | 8 | 9 | 80% | 33% | 47% | 客观地来看:那 **1 个误报** 是因为在到达 sink 之前,变量被重新赋值为了一 个常量(我们在同一个作用域内对流不敏感)。那 **3 个漏报** 是我们遗漏的真实 漏洞——一个未建模的 source(`os.getenv`)、通过返回值传递的污点(没有过程 间分析),以及通过列表元素传递的污点。第二行以数字形式展示了精确率与召回率 之间的权衡。 ## 真实世界扫描 针对五个真实的 GitHub 仓库(275 个 `.py` 文件)运行了扫描;完整的分类见 [REAL_WORLD_SCAN.md](REAL_WORLD_SCAN.md)。重点:在 253 个成熟的库代码文件 (flask / requests / httpie)中实现了 **零误报**;在故意存在漏洞的 `dvpwa` 中发现了 **一个正确的真实漏洞**;而在 `DSVW` 中则有一整个应用规模的 **遗漏 漏洞**,因为它的输入 source(`self.path`)不在我们的签名列表中——这最清楚 地表明,*一个污点分析工具的效果仅取决于它的 source/sink 列表的质量*。 ## 开发 ``` pip install -e ".[dev]" # install with test tools (pytest, coverage) pytest # run the suite + coverage report python -m benchmark.score # run the precision/recall benchmark python explore_ast.py --scan samples/vuln_sql_injection.py # teaching tool ``` 每次推送到 `main` 分支时,都会在 GitHub Actions 上跨 Python 3.9、3.11 和 3.13 运行测试套件。测试覆盖率会报告给 Codecov。 ## 已知局限性(出于设计考量——这是一个教学工具) - **绑定签名:** 仅能捕获在 [signatures.py](src/pytaint/signatures.py) 中定义的 sources/sinks;缺失的 source 会导致隐性的漏报。 - **仅限过程内分析:** 对于流入辅助函数并从该函数内的 sink 流出的污点,不会 跨越调用边界进行追踪。 - **作用域内流不敏感:** 忽略语句的顺序。 - **无容器/属性追踪:** 不会追踪通过 list/dict 元素或对象属性传递的污点。 ## 项目结构 ``` python-static-analyzer/ ├── src/pytaint/ # the package │ ├── __init__.py # version │ ├── parser.py # AST parsing + call discovery │ ├── signatures.py # source/sink knowledge base │ ├── detector.py # classify calls as source/sink │ ├── taint.py # the taint engine │ ├── scanner.py # directory walk + robustness │ ├── reporting.py # text / JSON output │ ├── cli.py # the `pytaint` command │ └── __main__.py # enables `python -m pytaint` ├── tests/ # pytest / unittest suite ├── samples/ # labeled vulnerable/safe demo files ├── benchmark/ # labeled cases + precision/recall scorer ├── explore_ast.py # teaching CLI (AST dump / classify / scan) ├── REAL_WORLD_SCAN.md # results on real repos ├── .github/workflows/ci.yml ├── Dockerfile ├── pyproject.toml ├── LICENSE # MIT └── README.md ``` ## 许可证 基于 [MIT 许可证](LICENSE) 发布——可自由使用、修改和分发;只需保留 版权声明即可。
标签:CISA项目, Python, SQL注入检测, 代码安全审计, 命令注入检测, 安全规则引擎, 无后门, 自动化payload嵌入, 请求拦截, 逆向工具, 错误基检测, 静态代码分析