Packj(发音为 package)是一款帮助缓解软件供应链攻击的工具。它可以检测来自流行开源包注册中心(如 NPM、RubyGems 和 PyPI)的恶意、存在漏洞、已废弃、拼写错误以及其他“高风险”包。该工具易于定制以降低噪音。Packj 始于一个博士研究项目,目前正在多个政府资助下进行开发。
注意 本月晚些时候将推出自托管 Packj Web 服务器和多项集成 👊 关注此仓库以获取最新信息。

我们支持多种部署模型:
使用 Packj 审计拉取请求中的依赖项。```yaml
在 GitHub [marketplace](https://github.com/marketplace/actions/packj-security-audit) 上查看。示例 [PR 运行](https://github.com/ossillate-inc/packj-github-action-demo/pull/3#issuecomment-1274797138)。
### 2. Docker 镜像(推荐)
尝试/测试 Packj 的最快方式是使用 Docker。此外还支持 Podman 进行容器化(隔离)运行。```
docker run -v /tmp:/tmp/packj -it ossillate/packj:latest --help
克隆此仓库,``` git clone https://github.com/ossillate-inc/packj.git && cd packj
安装依赖```
bundle install && pip3 install -r requirements.txt
从帮助开始:``` python3 main.py --help
# 支持的生态系统 #
Packj 可以对来自 NPM、PyPI、Rust、PHP 和 Rubygems 包注册表的已发布包进行审查。Rust 和 PHP 支持正在开发中。我们正在积极增加对注册表的支持。
它还支持审查本地(未发布)的 NPM 和 PyPI 包。
| Registry | Ecosystem | Supported |
| --------- | ---------- | ------------------ |
| NPM | JavaScript | :white_check_mark: |
| PyPI | Python | :white_check_mark: |
| Cargo | Rust | :white_check_mark: |
| Rubygems | Ruby | :white_check_mark: |
| Packagist | PHP | :white_check_mark: |
| Docker | Docker | :x: |
| Nuget | .NET | :white_check_mark: |
| Maven | Java | :white_check_mark: |
| Cocoapods | Swift | :x: |
# 功能 #
Packj 提供以下工具:
* [审计](#auditing-a-package) - 审查包的“风险”属性。
* [沙箱](#sandboxed-package-installation) - 用于安全安装包。
## 审查包 ##
Packj 审查开源软件包的“风险”属性,这些属性使其容易受到供应链攻击。例如,具有过期邮件域名(缺乏2FA)、发布间隔时间长、敏感API或访问权限等的包会被标记为风险。
支持审查以下内容:
- 多个包:`python3 main.py audit -p pypi:requests rubygems:overcommit`
- 依赖文件:`python3 main.py audit -f npm:package.json pypi:requirements.txt`
默认情况下,`audit` 只执行静态代码分析来检测风险代码。你可以传递 `-t` 或 `--trace` 标志来同时执行动态代码分析,这将在 strace 下安装所有请求的包并监控包的安装时行为。请参见下面的示例输出。
<details>
<summary><h4>显示示例运行/输出</h4></summary>
$ docker run -v /tmp:/tmp/packj -it ossillate/packj:latest audit --trace -p npm:browserify
[+] Fetching 'browserify' from npm..........PASS [ver 17.0.0]
[+] Checking package description.........PASS [browser-side require() the node way]
[+] Checking release history.............PASS [484 version(s)]
[+] Checking version........................RISK [702 days old]
[+] Checking release time gap............PASS [68 days since last release]
[+] Checking author.........................PASS [[email protected]]
[+] Checking email/domain validity.......RISK [expired author email domain]
[+] Checking readme.........................PASS [26838 bytes]
[+] Checking homepage.......................PASS [https://github.com/browserify/browserify#readme]
[+] Checking downloads......................PASS [2M weekly]
[+] Checking repo URL.......................PASS [https://github.com/browserify/browserify]
[+] Checking repo data...................PASS [stars: 14189, forks: 1244]
[+] Checking if repo is a forked copy....PASS [original, not forked]
[+] Checking repo description............PASS [browser-side require() the node.js way]
[+] Checking repo activity...............PASS [commits: 2290, contributors: 207, tags: 413]
[+] Checking for CVEs.......................PASS [none found]
[+] Checking dependencies...................RISK [48 found]
[+] Downloading package from npm............PASS [163.83 KB]
[+] Analyzing code..........................RISK [needs 3 perm(s): decode,codegen,file]
[+] Checking files/funcs....................PASS [429 files (383 .js), 744 funcs, LoC: 9.7K]
[+] Installing package and tracing code.....PASS [found 5 process,1130 files,22 network syscalls]
=============================================
[+] 5 risk(s) found, package is undesirable!
=> Complete report: /tmp/packj_54rbjhgm/report_npm-browserify-17.0.0_hlr1rhcz.json
{
"undesirable": [
"old package: 702 days old",
"invalid or no author email: expired author email domain",
"generates new code at runtime",
"reads files and dirs",
"forks or exits OS processes",
]
}
</details>
> 警告:由于包在安装过程中可能执行恶意代码,建议仅在 Docker 容器或虚拟机内使用 `-t` 或 `--trace`。
审计也可以在 Docker/Podman 容器中执行。有关风险属性的详细信息及使用方法,请参见 [审计 README](https://github.com/ossillate-inc/packj/blob/main/packj/audit/README.md)。
## 沙箱化包安装 ##
Packj 为包的“安全安装”提供轻量级沙箱。具体来说,它防止恶意包窃取敏感数据、访问敏感文件(如 SSH 密钥)和持久化恶意软件。
它对安装时脚本(包括任何原生编译)进行沙箱化。它使用 **strace**(即**不需要** VM/容器)。
有关沙箱机制及使用方法的详细信息,请参见 [沙箱 README](https://github.com/ossillate-inc/packj/blob/main/packj/sandbox/README.md)。
<details>
<summary><h4>显示示例运行/输出</h4></summary>
$ python3 main.py sandbox gem install overcommit
Fetching: overcommit-0.59.1.gem (100%)
Install hooks by running `overcommit --install` in your Git repository
Successfully installed overcommit-0.59.1
Parsing documentation for overcommit-0.59.1
Installing ri documentation for overcommit-0.59.1
#############################
# Review summarized activity
#############################
[+] Network connections
[+] DNS (1 IPv4 addresses) at port 53 [rule: ALLOW]
[+] rubygems.org (4 IPv6 addresses) at port 443 [rule: IPv6 rules not supported]
[+] rubygems.org (4 IPv4 addresses) at port 443 [rule: ALLOW]
[+] Filesystem changes
/
└── home
└── ubuntu
└── .ruby
├── gems
│ ├── iniparse-1.5.0 [new: DIR, 15 files, 46.6K bytes]
│ ├── rexml-3.2.5 [new: DIR, 77 files, 455.6K bytes]
│ ├── overcommit-0.59.1 [new: DIR, 252 files, 432.7K bytes]
│ └── childprocess-4.1.0 [new: DIR, 57 files, 141.2K bytes]
├── cache
│ ├── iniparse-1.5.0.gem [new: FILE, 16.4K bytes]
│ ├── rexml-3.2.5.gem [new: FILE, 93.2K bytes]
│ ├── childprocess-4.1.0.gem [new: FILE, 34.3K bytes]
│ └── overcommit-0.59.1.gem [new: FILE, 84K bytes]
├── specifications
│ ├── rexml-3.2.5.gemspec [new: FILE, 2.7K bytes]
│ ├── overcommit-0.59.1.gemspec [new: FILE, 1.7K bytes]
│ ├── childprocess-4.1.0.gemspec [new: FILE, 1.8K bytes]
│ └── iniparse-1.5.0.gemspec [new: FILE, 1.3K bytes]
├── bin
│ └── overcommit [new: FILE, 622 bytes]
└── doc
├── iniparse-1.5.0
│ └── ri [new: DIR, 119 files, 131.7K bytes]
├── rexml-3.2.5
│ └── ri [new: DIR, 836 files, 841K bytes]
├── overcommit-0.59.1
│ └── ri [new: DIR, 1046 files, 1.5M bytes]
└── childprocess-4.1.0
└── ri [new: DIR, 272 files, 297.8K bytes]
[C]ommit all changes, [Q|q]uit & discard changes, [L|l]ist details:
</details>
# 我们的故事
**TL;DR** Packj 始于一个博士研究项目。它得到了多项政府资助。
<details>
<summary><h4>显示详细回答</h4></summary>
Packj 始于一个学术研究项目。具体来说,Packj 使用的静态代码分析技术基于我们在佐治亚理工学院的[研究组](http://cyfi.ece.gatech.edu)的前沿网络安全研究项目 [MalOSS](https://github.com/osssanitizer/maloss)。
<a href="https://arxiv.org/pdf/2002.01139v1.pdf" target="_blank">
<img src="https://assets.kitploit.com/production/public/readmes/5562/c4061028051de04a9aebaf4188c08fdd1bcdb47efa8a818e959d3b3fc7bc5dc7.png" width="300" alt="学术论文">
</a>
Packj 得到了 [NSF](https://www.sbir.gov/node/2083473)、[GRA](https://gra.org/company/227/OSSPolice.html) 和 [ALInnovate](https://innovatealabama.org) 的慷慨资助。
</details>
# 为什么选择 Packj
**TL;DR** 最先进的漏洞扫描器假设第三方开源代码是良性的。因此,所有这些工具仅处理良性代码中意外编程错误(即 CVEs,如 Log4J)带来的威胁。它们无法防范类似 Solarwinds 的现代软件供应链攻击,这些攻击源自故意有害(即恶意)的代码,由不良行为者利用供应链中的新漏洞传播,包括依赖混淆、拼写错误抢注、抗议软件(破坏)、账户劫持和社会工程。近期(2022年12月)的一个例子是 PyTorch 包因依赖混淆漏洞(未分配 CVE)而被入侵。
Packj 不仅审计 CVEs,还执行深入的静态+动态代码分析以及元数据检查,以检测任何“风险”行为和属性,例如生成 shell、使用 SSH 密钥、GitHub 代码与打包代码(来源)不匹配、缺乏 2FA 等。这种不安全的属性不符合 CVEs 的条件,这就是为什么现有工具都无法标记它们。Packj 可以标记恶意、拼写错误抢注、废弃、易受攻击以及软件供应链中的其他不安全依赖项(薄弱环节)。
<details>
<summary><h4>显示详细回答</h4></summary>
当前的软件供应链威胁模型**假设**第三方开源代码是良性的,因此安全漏洞仅针对意外编程错误(即 CVEs)进行追踪。因此,所有现有的开源漏洞扫描器**仅**报告公开已知的 CVEs,并处理来自良性代码中意外错误带来的威胁。
意外编程错误的一个典型例子是缺少对用户输入的边界检查,这使得代码容易受到缓冲区溢出攻击。现实世界中流行的例子包括 Log4J 和 HeartBleed。攻击者需要开发一个 exploit 来触发 CVEs(例如,针对 HeartBleed 构造一个 TCP/IP 数据包,或者导致缓冲区溢出的高数值输入)。CVEs 可以通过修补或升级到库的新版本来修复(例如,Log4J 的新版本修复了该 CVE)。
现代软件供应链威胁格局在 Solarwinds 攻击后发生了**转变**。不良行为者发现了新的漏洞,但这次是在供应渠道中,而非代码中。这些新漏洞,如依赖混淆、拼写错误抢注、抗议软件(破坏)、账户劫持和社会工程,正在被利用来传播恶意软件。已有数千个受感染的 NPM/PyPI/Ruby 包被报告。
与 CVEs 相比,恶意软件是故意有害(即恶意)的代码。此外,恶意软件本身就是一个 exploit,无法通过升级到新版本进行修补或修复。例如,[依赖混淆攻击](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610) 是故意恶意的;它没有利用代码中的任何意外编程错误。同样,某个流行包的作者出于[抗议](https://en.wikipedia.org/wiki/Peacenotwar)战争而破坏自己的代码,这也是有意为之,并未利用任何 CVEs。拼写错误抢注是另一个攻击向量,不良行为者利用它在流行的开源包注册表中传播恶意软件:它利用[开发者的拼写错误和经验不足](https://discuss.python.org/t/improving-risks-and-consequences-against-typosquatting-on-pypi/5090),而非代码中的意外编程错误或 CVEs。
现有扫描器**无法**检测这些类似 Solarwinds 的现代软件供应链攻击,这些攻击源自故意易受攻击(恶意)的代码。这些工具仅扫描源代码中的开源依赖项,汇总所有使用的依赖项列表,然后在数据库(如 NVD)中查找每个 <依赖项名称, 依赖项版本>,以报告受影响的包版本(例如,Log4J 的易受攻击版本,受 HeartBleed 影响的 LibSSL 版本)。
Packj 不仅审计 CVEs,还执行深入的静态+动态代码分析以及元数据检查,以检测任何“风险”行为和属性,例如生成 shell、使用 SSH 密钥、GitHub 代码与打包代码(来源)不匹配、缺乏 2FA 等。这种不安全的属性不符合 CVEs 的条件,这就是为什么现有工具都无法标记它们。Packj 可以标记恶意、拼写错误抢注、废弃、易受攻击以及软件供应链中的其他不安全依赖项(薄弱环节)。请阅读 [审计 README](https://github.com/ossillate-inc/packj/blob/main/packj/audit/README.md#faq) 了解更多。
</details>
# 自定义 #
Packj 可以轻松自定义(零噪音)以适应您的威胁模型。只需在您的仓库/项目的顶级目录中添加一个 [.packj.yaml](https://github.com/ossillate-inc/packj/blob/main/.packj.yaml) 文件,并通过注释掉不需要的属性来减少告警疲劳。
# 已发现的恶意软件 #
我们使用此工具分别在 PyPI 和 Rubygems 上发现了超过 40 个和 20 个恶意包。其中不少已被下架。参考以下示例:
<details>
<summary><h4>显示恶意软件示例</h4></summary>
$ python3 main.py audit pypi:krisqian
[+] Fetching 'krisqian' from pypi...OK [ver 0.0.7]
[+] Checking version...OK [256 days old]
[+] Checking release history...OK [7 version(s)]
[+] Checking release time gap...OK [1 days since last release]
[+] Checking author...OK [[email protected]]
[+] Checking email/domain validity...OK [[email protected]]
[+] Checking readme...ALERT [no readme]
[+] Checking homepage...OK [https://www.bilibili.com/bangumi/media/md140632]
[+] Checking downloads...OK [13 weekly]
[+] Checking repo_url URL...OK [None]
[+] Checking for CVEs...OK [none found]
[+] Checking dependencies...OK [none found]
[+] Downloading package 'KrisQian' (ver 0.0.7) from pypi...OK [1.94 KB]
[+] Analyzing code...ALERT [needs 3 perms: process,network,file]
[+] Checking files/funcs...OK [9 files (2 .py), 6 funcs, LoC: 184]
=============================================
[+] 6 risk(s) found, package is undesirable!
{
"undesirable": [
"no readme",
"only 45 weekly downloads",
"no source repo found",
"generates new code at runtime",
"fetches data over the network: ['KrisQian-0.0.7/setup.py:40', 'KrisQian-0.0.7/setup.py:50']",
"reads files and dirs: ['KrisQian-0.0.7/setup.py:59', 'KrisQian-0.0.7/setup.py:70']"
]
}
=> Complete report: pypi-KrisQian-0.0.7.json
=> View pre-vetted package report at https://packj.dev/package/PyPi/KrisQian/0.0.7
</details>
Packj 将 KrisQian (v0.0.7) 标记为可疑,因其缺少源代码仓库且在包安装时(在 setup.py 中)使用了敏感 API(网络、代码生成)。我们决定深入调查,并发现该包是恶意的。请在我们的详细分析 [https://packj.dev/malware/krisqian](https://packj.dev/malware/krisqian) 中查看。
我们发现的更多恶意软件示例列在 [https://packj.dev/malware](https://packj.dev/malware)。如需完整列表,请通过 [[email protected]](mailto:[email protected]) 联系我们。
# 资源 #
要了解有关 Packj 工具或开源软件供应链攻击的更多信息,请参阅我们的
[](https://www.youtube.com/watch?v=Rcuqn56uCDk)
[](https://www.youtube.com/watch?v=a7BfDGeW_jY)
- PyConUS'22 [演讲](https://www.youtube.com/watch?v=Rcuqn56uCDk) 和 [幻灯片](https://speakerdeck.com/ashishbijlani/pyconus22-slides)。
- BlackHAT Asia'22 Arsenal [演讲](https://www.blackhat.com/asia-22/arsenal/schedule/#mitigating-open-source-software-supply-chain-attacks-26241)
- PackagingCon'21 [演讲](https://www.youtube.com/watch?v=PHfN-NrUCoo) 和 [幻灯片](https://speakerdeck.com/ashishbijlani/mitigating-open-source-software-supply-chain-attacks)
- BlackHat USA'22 Arsenal talk [Detecting typo-squatting, backdoored, abandoned, and other "risky" open-source packages using Packj](https://www.blackhat.com/us-22/arsenal/schedule/#detecting-typo-squatting-backdoored-abandoned-and-other-risky-open-source-packages-using-packj-28075)
- Academic [dissertation](https://cyfi.ece.gatech.edu/publications/DUAN-DISSERTATION-2019.pdf) on open-source software security and the [paper](https://www.ndss-symposium.org/wp-content/uploads/ndss2021_1B-1_23055_paper.pdf) from our group at Georgia Tech that started this research.
- Open Source Summit, Europe'22 talk [Scoring dependencies to detect “weak links” in your open-source software supply chain](https://osseu2022.sched.com/overview/type/SupplyChainSecurityCon) - presentation video on [YouTube](https://www.youtube.com/watch?v=a7BfDGeW_jY)
- Presentation [video](https://www.youtube.com/watch?v=PgvlSjl-mrY) and [slides](https://drive.google.com/file/d/1qLXIXzsIhRlS0mo8nwWI9KmD12CImZY9/view?usp=sharing) at NullCon'22 [Unearthing Malicious And Other “Risky” Open-Source Packages Using Packj](https://archive.nullcon.net/website/goa-2022/speakers/unearthing-malicious-and-other-risky-open-source-packages-using-packj.php)
# 功能路线图 #
* 添加 Rust 分析器。Rust 正在开发中 [预计完成时间:2024年2月]。
* 添加检测多个(TODO)“风险”代码以及元数据属性的功能 [预计完成时间:2024年2月]。
* 自托管的 Packj 网页服务器及若干有用的集成(例如 GitLab runner)[预计完成时间:2024年4月]。
关注 :eyes: 此仓库以保持更新。
有功能或支持请求?请访问我们的 [GitHub 讨论页面](https://github.com/ossillate-inc/packj/discussions/) 或加入我们的 [Discord 社区](https://discord.gg/qFcqaV2wYa) 进行讨论和请求。
# 团队与贡献者 #
Packj 由 [Ossillate Inc.](https://packj.dev/team) 的网络安全研究人员和外部合作者开发,旨在帮助开发者在获取不受信任的第三方开源软件依赖时降低供应链攻击风险。我们感谢我们的开发者和合作者。如果您喜欢我们的工作,请给我们 :star: 以示赞赏。
创始成员:
* Ashish Bijlani
* Devdutt Patnaik
* Ajinkya Rajput
我们热忱欢迎代码贡献。请参阅 [CONTRIBUTING.md](https://github.com/ossillate-inc/packj/blob/main/CONTRIBUTING.md) 指南。发现错误?请提交 issue。请参考我们的 [SECURITY.md](https://github.com/ossillate-inc/packj/blob/main/SECURITY.md) 指南报告安全问题。
# 常见问题 #
<details>
<summary><b>支持哪些包管理器(注册表)?</b></summary>
Packj 目前可以审查 NPM、PyPI 和 RubyGems 包的“风险”属性。我们正在添加对 Rust 的支持。
</details>
<details>
<summary><b>Packj 使用哪些技术来检测风险/恶意包?</b></summary>
Packj 使用静态代码分析、动态追踪和元数据分析进行全面审计。仅静态分析不足以标记那些通过代码混淆更好地隐藏自己的复杂恶意软件。动态分析通过将包安装在 `strace` 下并监控其运行时行为来进行。请阅读 [审计 README](https://github.com/ossillate-inc/packj/blob/main/packj/audit/README.md) 了解更多。
</details>
<details>
<summary><b>它能否处理混淆的调用?例如,一个经过 base64 加密的字符串被解密后传递给 shell?</b></summary>这是一种非常常见的恶意行为。Packj 检测代码混淆以及 shell 命令的衍生(exec 系统调用)。例如,Packj 可以标记使用 `getattr()` 和 `eval()` API,因为它们指示"运行时代码生成";开发人员随后可以进一步查看。详情请参阅 [main.py](https://github.com/ossillate-inc/packj/blob/main/packj/audit/main.py#L512)。
</details>