PjSalty/pvpgn-pro-hardened
GitHub: PjSalty/pvpgn-pro-hardened
PvPGN-PRO Battle.net 模拟器的安全强化 fork,通过现代编译器缓解措施、模糊测试和供应链签名,为暴露在公网上的经典游戏服务器提供可直接替换的内存安全防护。
Stars: 0 | Forks: 0
# pvpgn-pro-hardened
[PvPGN-PRO](https://github.com/pvpgn/pvpgn-server) 的安全强化构建版本,这是一个开源的 Battle.net 模拟器,用于运行私有的魔兽争霸 III(以及其他经典的 Blizzard/Westwood 游戏)服务器。
它是原版 PvPGN-PRO 构建版本的直接替代品:相同的 `bnetd`,相同的配置,相同的 `6112/tcp` (bnet) 和 `6200/tcp` (w3route) 端口,相同的行为。只有构建、测试和发布流水线发生了改变。你可以直接将其替换使用,在无需更改配置的情况下获得实质性的安全提升。
## 为什么会有这个项目
每个自建经典 WC3 服务器的人都在运行同一个 C++ daemon。它直接在互联网上解析不受信任的网络输入,即 bnet 和 w3route 线路协议,而且该代码库从未进行过现代的安全审计。PvPGN-PRO 的最后一个标签发布版本是在 2018 年。
这个 fork 并不重写该 daemon。而是对其进行强化:
- 将代码树更新为与上游 `master` (2021) 保持一致,比上一个标签发布版本领先了 102 次提交,
- 使用全套现代缓解措施对其进行编译,并证明这些缓解措施确实存在于二进制文件中,
- 在 sanitizers 下对 packet parser 进行模糊测试,并修复由此发现的内存安全 bug,
- 提供了一个可重现、已签名的容器镜像,并附带 SBOM 和构建出处证明。
目标很简单:packet parser 中的内存安全 bug 应该导致干净的崩溃并重启,而不是成为远程代码执行的原语。
## 强化带来的好处
该构建版本是按照 [OpenSSF Compiler Options Hardening Guide](https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html) 进行编译的。
`test/checksec.sh` 会对每次构建进行门控检查,如果缺少任何缓解措施,镜像将拒绝构建。
| 缓解措施 | 原版 PvPGN-PRO | pvpgn-pro-hardened |
|---|---|---|
| RELRO | Partial | Full (GOT read-only) |
| Stack canary | 无 | `-fstack-protector-strong` |
| FORTIFY_SOURCE | 无 | `=3` (28 个强化调用) |
| Stack-clash protection | 无 | 有 |
| CET (IBT + SHSTK) | 无 | 有 (9255 个 endbr64 填充 + property note) |
| PIE | 有 | 有 |
| NX / non-exec stack | 有 | 有 |
| libstdc++ assertions | 无 | `_GLIBCXX_ASSERTIONS` |
| auto-var-init | 无 | `-ftrivial-auto-var-init=zero` |
FORTIFY_SOURCE=3 以及 libstdc++ assertions 为 daemon 中数百个遗留的 `sprintf`/`strcpy`/`strcat` 调用点加装了边界检查,因此溢出会终止运行,而不是破坏内存。`-Werror=format-security` 会将格式化字符串 bug 变为构建失败。
有关完整的 flag 集及其验证方法,请参阅 [HARDENING.md](HARDENING.md)。
## 通过模糊测试发现并修复的 Bug
libFuzzer harnesses (`fuzz/`) 在 ASan + UBSan 下驱动字节级别的 packet parser。每一个被发现的问题都已修复,确保输入能被安全拒绝,并且每一个修复都已证明不会改变有效流量的行为(参见下文的测试部分)。
- `packet_get_str_const`:当最大长度的 packet 不包含字符串终止符时,会导致在 packet 缓冲区之后超出一个字节的越界读取。
- `packet_get_data_const`:一个 `offset + len` 无符号整数溢出,使得一个越界窗口能够绕过长度检查。
- `tag_str_to_uint` / `tag_case_str_to_uint`:在 4 字符客户端/架构 tag 解码器中存在短输入过度读取和未初始化暂存区读取问题。
详情见 [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md)。
## 运行
容器(推荐):
```
docker run -d --name pvpgn \
-p 6112:6112/tcp -p 6200:6200/tcp \
-v pvpgn-data:/usr/local/pvpgn/var/pvpgn \
ghcr.io/pjsalty/pvpgn-pro-hardened:latest
```
如需完整的运行时沙箱(只读根文件系统、降权 capabilities、`no-new-privileges`、seccomp、资源限制),请使用
[`deploy/docker-compose.hardened.yml`](deploy/docker-compose.hardened.yml)。
从源码构建:
```
cmake -S . -B build -D CMAKE_BUILD_TYPE=Release -D WITH_LUA=true
cmake --build build -j"$(nproc)"
bash test/checksec.sh build/src/bnetd/bnetd # confirm the mitigations landed
```
强化默认是开启的。可以通过 `-D PVPGN_HARDENING=OFF` 来禁用。
## 验证镜像
```
# provenance + SBOM attestations
gh attestation verify oci://ghcr.io/pjsalty/pvpgn-pro-hardened:latest -R pjsalty/pvpgn-pro-hardened
# cosign keyless signature
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp 'https://github.com/PjSalty/pvpgn-pro-hardened/.github/workflows/.*' \
ghcr.io/pjsalty/pvpgn-pro-hardened:latest
```
## 测试
已证明这些更改不会破坏 daemon:
- `test/run-difftest.sh`:一个差异性验证预言机会将约 11 万个有效输入通过修补后的 parser 进行测试,并与原始的上游代码进行比对,断言输出完全一致。
- `test/build-and-verify.sh`:在强化后的二进制文件上执行完整构建、checksec 门控检查以及上游单元测试(SRP3、bignum)。
- `fuzz/run-regression.sh`:修复后的崩溃重现测试运行正常,并进行了一轮简短的模糊测试活动。
所有这些都会在每次推送和 pull request 时的 CI 中运行。
## 与上游的关系
这是一个强化 fork,而不是一个竞争项目。它跟踪 `pvpgn/pvpgn-server`,并将分歧保持在较小范围内,记录在 [DIVERGENCE.md](DIVERGENCE.md) 中;parser 的修复也已回馈给上游。有关更新的工作流,请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
支持的客户端、配置和协议与上游保持一致;请参考 [PvPGN-PRO 文档](https://github.com/pvpgn/pvpgn-server)。
## 许可证
GPL-2.0-or-later,与上游 PvPGN 和 PvPGN-PRO 相同。请参阅 [COPYING](COPYING)。
最初的 PvPGN 是 PvPGN 和 PvPGN-PRO 作者的心血结晶;这个 fork 在他们工作的基础上增加了构建强化、模糊测试、测试和发布工具。
标签:Bash脚本, C++, DevSecOps, GitHub Advanced Security, 上游代理, 内核驱动, 安全加固, 数据擦除, 游戏服务器, 漏洞缓解, 网络协议, 请求拦截