ChatBug 0.0.1__tar.gz → 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
chatbug-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: ChatBug
3
+ Version: 0.1.0
4
+ Summary: Authorized website vulnerability validation with safe PHP2Shell checks and isolated reproduction labs
5
+ Author-email: ChatArch <rex@chatarch.org>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ChatArch/ChatBug
8
+ Project-URL: Repository, https://github.com/ChatArch/ChatBug
9
+ Project-URL: Documentation, https://arch.gh.wzhecnu.cn/ChatBug/
10
+ Keywords: chatbug,chatarch,security,php2shell,cli
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Security
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: click>=8.0
19
+ Requires-Dist: chatstyle<0.3.0,>=0.2.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: build; extra == "dev"
22
+ Requires-Dist: pytest; extra == "dev"
23
+ Requires-Dist: twine; extra == "dev"
24
+ Provides-Extra: docs
25
+ Requires-Dist: mkdocs<2.0,>=1.6; extra == "docs"
26
+ Requires-Dist: mkdocs-material<9.7,>=9.5; extra == "docs"
27
+ Requires-Dist: mkdocs-static-i18n<2.0,>=1.2; extra == "docs"
28
+ Requires-Dist: mike<3.0,>=2.0; extra == "docs"
29
+ Dynamic: license-file
30
+
31
+ # ChatBug
32
+
33
+ ChatBug 是 ChatArch 系列的授权网站漏洞验证包。首个能力聚焦 **PHP2Shell**:先对自己拥有的普通网站做无破坏性版本/入口检查,再用一次性、无真实数据的隔离实验环境复现“运行时 + LFI include gadget → session upload-progress 注入 → `ls/cat` 输出”的完整测试链。
34
+
35
+ 它不是通用攻击框架:不发送 OOM/UAF 崩溃 payload,不对生产执行命令,不提供反弹 shell,也不接受任意远程命令参数。
36
+
37
+ 文档入口:<https://arch.gh.wzhecnu.cn/ChatBug/>
38
+
39
+ ## 适用场景
40
+
41
+ - 判断自有 PHP 网站是否暴露在受影响版本范围。
42
+ - 用普通 multipart 请求确认上传解析入口是否可达。
43
+ - 用固定、小规模的常见 include 参数形态做**标记文件读取**探测。
44
+ - 在本地进程实验或远端一次性 Docker 实验中,跑通可重复的 `ls -la /tmp` / `cat /tmp/chatbug-marker.txt` 验收。
45
+ - 向团队输出可审计 JSON,区分“版本暴露”“解析器可达”“隔离链复现成功”和“生产利用未证明”。
46
+
47
+ ## 安全边界
48
+
49
+ | 命令 | 作用对象 | 是否执行命令 | 是否接触生产数据 |
50
+ | --- | --- | --- | --- |
51
+ | `chatbug php2shell web check <URL>` | 自有普通网站 URL | 否 | 否;只发 HEAD、普通 multipart 和固定 gadget 探测 |
52
+ | `chatbug php2shell lab local-list` | 本机临时 HTTP lab | 否;不启动系统 shell | 否;进程内合成数据 |
53
+ | `chatbug php2shell lab docker-list` | 远端一次性内部 Docker lab | 仅 lab 固定 allowlist 命令 | 否;无生产卷、无发布端口、合成 marker |
54
+
55
+ `docker-list` 复现的是传统、确定性的 PHP `include` 文件包含链:`PHP_SESSION_UPLOAD_PROGRESS` 写入 session 文件,lab-only 的 `include($_GET['page'])` 再包含该文件。它证明“受影响 PHP-FPM 运行时 + 应用存在 LFI gadget”时可以走到 `ls/cat`,**不单独证明 CVE-2026-109618/109619 的内存破坏 RCE**。
56
+
57
+ ## 安装
58
+
59
+ ```bash
60
+ python -m pip install ChatBug
61
+ chatbug --version
62
+ chatbug --tree
63
+ ```
64
+
65
+ 开发环境:
66
+
67
+ ```bash
68
+ python -m pip install -e ".[dev]"
69
+ python -m pytest -q
70
+ ```
71
+
72
+ ## 快速复现:本机可运行 test case
73
+
74
+ 无需 Docker、无需 root、无需网络:
75
+
76
+ ```bash
77
+ chatbug php2shell lab local-list
78
+ ```
79
+
80
+ 成功时输出 JSON,关键字段包括:
81
+
82
+ - `accepted: true`
83
+ - `positive_status: 200`
84
+ - `negative_status: 200`
85
+ - `positive_output` 包含 `/tmp`、`chatbug-marker.txt` 和随机 marker
86
+ - `negative_body` 只有 `Hello World`,证明普通安全入口不会把 session 文件当 PHP 执行
87
+
88
+ 这个命令也由测试覆盖:
89
+
90
+ ```bash
91
+ python -m pytest -q tests/test_php2shell.py
92
+ ```
93
+
94
+ ## 完整隔离复现:PHP-FPM + Nginx
95
+
96
+ 需要一个可通过 SSH 使用 Docker 的自有主机。该命令会:
97
+
98
+ 1. 拉取固定 digest 的 PHP/PHP-FPM 镜像作为运行时;
99
+ 2. 生成只含 `Hello World` 和 lab-only `include` sink 的临时 webroot;
100
+ 3. 创建 internal Docker network,不发布主机端口;
101
+ 4. 在 lab 容器的 `/tmp/chatbug-marker.txt` 写入随机合成 marker;
102
+ 5. 从同一内部网络的一次性 client 容器发送 session upload-progress 请求;
103
+ 6. 正向请求通过 lab-only LFI sink 返回 `ls/cat` 输出;
104
+ 7. 负向请求访问普通 `hello.php`,只返回 `Hello World`;
105
+ 8. 删除容器、网络和远端临时目录。
106
+
107
+ ```bash
108
+ chatbug php2shell lab docker-list \
109
+ --ssh-host <your-ssh-host> \
110
+ [--fpm-image <digest-pinned-php-fpm-image>] \
111
+ --proof list-and-marker
112
+ ```
113
+
114
+ `--proof` 只允许三个固定模式:
115
+
116
+ - `list-tmp`:执行 lab 内 `ls -la /tmp`;
117
+ - `cat-marker`:读取 lab 内合成 marker 文件;
118
+ - `list-and-marker`:同时列目录并读取 marker(默认)。
119
+
120
+ 真实验收输出会在 `positive_output` 中包含 lab 的 `/tmp` 列表、`sess_<id>` 文件、`MARKER` 和随机 marker;负向输出必须保持 `Hello World`。
121
+
122
+ ## 检查自有普通网站
123
+
124
+ ```bash
125
+ chatbug php2shell web check https://example-owned-site.example/
126
+ ```
127
+
128
+ 输出字段解释:
129
+
130
+ - `verdict=vulnerable_version_exposed`:版本头显示在受影响范围,只代表暴露,不代表 RCE;
131
+ - `evidence_tier=tier_2_parser_reachable`:普通 multipart 解析入口可达;
132
+ - `evidence_tier=tier_3_gadget_found`:固定探测中发现服务端返回了独立 marker;
133
+ - `rce_or_file_read_proven=true`:只有在标记不能由请求反射解释、确实从服务端 session 文件包含返回时才成立。
134
+
135
+ 可跳过较重的探测:
136
+
137
+ ```bash
138
+ chatbug php2shell web check https://example-owned-site.example/ --skip-gadget-scan
139
+ ```
140
+
141
+ ## 证据层级
142
+
143
+ ChatBug 不把弱证据升级成强结论:
144
+
145
+ 1. 版本/配置暴露;
146
+ 2. 解析器或前置条件可达;
147
+ 3. 一次性隔离环境中完成 marker/命令输出验收;
148
+ 4. 经单独风险授权后的生产验证;
149
+ 5. 第三方真实入侵指标。
150
+
151
+ 对邮件、文档等含真实用户数据的生产服务,不应发送内存破坏、OOM、崩溃或重复利用流量;先用同镜像、无数据 lab 完成验收。
152
+
153
+ ## 文档
154
+
155
+ - [CLI 树](docs/cli-tree.md)
156
+ - [能力地图](docs/capability-map.md)
157
+ - [Python 接口树](docs/interface-tree.md)
158
+ - [PHP2Shell 复现实验](docs/php2shell-lab.md)
159
+
160
+ ## 开发说明
161
+
162
+ 扩展能力前先阅读 `DEVELOP.md` 和 `AGENTS.md`。新增生产检查必须保持非破坏性;任何需要固定命令以外的实验能力,应先增加独立、隔离、可清理的 lab,而不是扩展远程任意命令接口。
@@ -0,0 +1,132 @@
1
+ # ChatBug
2
+
3
+ ChatBug 是 ChatArch 系列的授权网站漏洞验证包。首个能力聚焦 **PHP2Shell**:先对自己拥有的普通网站做无破坏性版本/入口检查,再用一次性、无真实数据的隔离实验环境复现“运行时 + LFI include gadget → session upload-progress 注入 → `ls/cat` 输出”的完整测试链。
4
+
5
+ 它不是通用攻击框架:不发送 OOM/UAF 崩溃 payload,不对生产执行命令,不提供反弹 shell,也不接受任意远程命令参数。
6
+
7
+ 文档入口:<https://arch.gh.wzhecnu.cn/ChatBug/>
8
+
9
+ ## 适用场景
10
+
11
+ - 判断自有 PHP 网站是否暴露在受影响版本范围。
12
+ - 用普通 multipart 请求确认上传解析入口是否可达。
13
+ - 用固定、小规模的常见 include 参数形态做**标记文件读取**探测。
14
+ - 在本地进程实验或远端一次性 Docker 实验中,跑通可重复的 `ls -la /tmp` / `cat /tmp/chatbug-marker.txt` 验收。
15
+ - 向团队输出可审计 JSON,区分“版本暴露”“解析器可达”“隔离链复现成功”和“生产利用未证明”。
16
+
17
+ ## 安全边界
18
+
19
+ | 命令 | 作用对象 | 是否执行命令 | 是否接触生产数据 |
20
+ | --- | --- | --- | --- |
21
+ | `chatbug php2shell web check <URL>` | 自有普通网站 URL | 否 | 否;只发 HEAD、普通 multipart 和固定 gadget 探测 |
22
+ | `chatbug php2shell lab local-list` | 本机临时 HTTP lab | 否;不启动系统 shell | 否;进程内合成数据 |
23
+ | `chatbug php2shell lab docker-list` | 远端一次性内部 Docker lab | 仅 lab 固定 allowlist 命令 | 否;无生产卷、无发布端口、合成 marker |
24
+
25
+ `docker-list` 复现的是传统、确定性的 PHP `include` 文件包含链:`PHP_SESSION_UPLOAD_PROGRESS` 写入 session 文件,lab-only 的 `include($_GET['page'])` 再包含该文件。它证明“受影响 PHP-FPM 运行时 + 应用存在 LFI gadget”时可以走到 `ls/cat`,**不单独证明 CVE-2026-109618/109619 的内存破坏 RCE**。
26
+
27
+ ## 安装
28
+
29
+ ```bash
30
+ python -m pip install ChatBug
31
+ chatbug --version
32
+ chatbug --tree
33
+ ```
34
+
35
+ 开发环境:
36
+
37
+ ```bash
38
+ python -m pip install -e ".[dev]"
39
+ python -m pytest -q
40
+ ```
41
+
42
+ ## 快速复现:本机可运行 test case
43
+
44
+ 无需 Docker、无需 root、无需网络:
45
+
46
+ ```bash
47
+ chatbug php2shell lab local-list
48
+ ```
49
+
50
+ 成功时输出 JSON,关键字段包括:
51
+
52
+ - `accepted: true`
53
+ - `positive_status: 200`
54
+ - `negative_status: 200`
55
+ - `positive_output` 包含 `/tmp`、`chatbug-marker.txt` 和随机 marker
56
+ - `negative_body` 只有 `Hello World`,证明普通安全入口不会把 session 文件当 PHP 执行
57
+
58
+ 这个命令也由测试覆盖:
59
+
60
+ ```bash
61
+ python -m pytest -q tests/test_php2shell.py
62
+ ```
63
+
64
+ ## 完整隔离复现:PHP-FPM + Nginx
65
+
66
+ 需要一个可通过 SSH 使用 Docker 的自有主机。该命令会:
67
+
68
+ 1. 拉取固定 digest 的 PHP/PHP-FPM 镜像作为运行时;
69
+ 2. 生成只含 `Hello World` 和 lab-only `include` sink 的临时 webroot;
70
+ 3. 创建 internal Docker network,不发布主机端口;
71
+ 4. 在 lab 容器的 `/tmp/chatbug-marker.txt` 写入随机合成 marker;
72
+ 5. 从同一内部网络的一次性 client 容器发送 session upload-progress 请求;
73
+ 6. 正向请求通过 lab-only LFI sink 返回 `ls/cat` 输出;
74
+ 7. 负向请求访问普通 `hello.php`,只返回 `Hello World`;
75
+ 8. 删除容器、网络和远端临时目录。
76
+
77
+ ```bash
78
+ chatbug php2shell lab docker-list \
79
+ --ssh-host <your-ssh-host> \
80
+ [--fpm-image <digest-pinned-php-fpm-image>] \
81
+ --proof list-and-marker
82
+ ```
83
+
84
+ `--proof` 只允许三个固定模式:
85
+
86
+ - `list-tmp`:执行 lab 内 `ls -la /tmp`;
87
+ - `cat-marker`:读取 lab 内合成 marker 文件;
88
+ - `list-and-marker`:同时列目录并读取 marker(默认)。
89
+
90
+ 真实验收输出会在 `positive_output` 中包含 lab 的 `/tmp` 列表、`sess_<id>` 文件、`MARKER` 和随机 marker;负向输出必须保持 `Hello World`。
91
+
92
+ ## 检查自有普通网站
93
+
94
+ ```bash
95
+ chatbug php2shell web check https://example-owned-site.example/
96
+ ```
97
+
98
+ 输出字段解释:
99
+
100
+ - `verdict=vulnerable_version_exposed`:版本头显示在受影响范围,只代表暴露,不代表 RCE;
101
+ - `evidence_tier=tier_2_parser_reachable`:普通 multipart 解析入口可达;
102
+ - `evidence_tier=tier_3_gadget_found`:固定探测中发现服务端返回了独立 marker;
103
+ - `rce_or_file_read_proven=true`:只有在标记不能由请求反射解释、确实从服务端 session 文件包含返回时才成立。
104
+
105
+ 可跳过较重的探测:
106
+
107
+ ```bash
108
+ chatbug php2shell web check https://example-owned-site.example/ --skip-gadget-scan
109
+ ```
110
+
111
+ ## 证据层级
112
+
113
+ ChatBug 不把弱证据升级成强结论:
114
+
115
+ 1. 版本/配置暴露;
116
+ 2. 解析器或前置条件可达;
117
+ 3. 一次性隔离环境中完成 marker/命令输出验收;
118
+ 4. 经单独风险授权后的生产验证;
119
+ 5. 第三方真实入侵指标。
120
+
121
+ 对邮件、文档等含真实用户数据的生产服务,不应发送内存破坏、OOM、崩溃或重复利用流量;先用同镜像、无数据 lab 完成验收。
122
+
123
+ ## 文档
124
+
125
+ - [CLI 树](docs/cli-tree.md)
126
+ - [能力地图](docs/capability-map.md)
127
+ - [Python 接口树](docs/interface-tree.md)
128
+ - [PHP2Shell 复现实验](docs/php2shell-lab.md)
129
+
130
+ ## 开发说明
131
+
132
+ 扩展能力前先阅读 `DEVELOP.md` 和 `AGENTS.md`。新增生产检查必须保持非破坏性;任何需要固定命令以外的实验能力,应先增加独立、隔离、可清理的 lab,而不是扩展远程任意命令接口。
@@ -5,16 +5,18 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "ChatBug"
7
7
  dynamic = ["version"]
8
- description = "ChatArch reserved Python package for future bug investigation tools"
8
+ description = "Authorized website vulnerability validation with safe PHP2Shell checks and isolated reproduction labs"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = "MIT"
12
- dependencies = ["click>=8.0", "chatstyle>=0.2.0,<0.3.0", "chatenv>=0.2.11,<0.3.0"]
12
+ dependencies = ["click>=8.0", "chatstyle>=0.2.0,<0.3.0"]
13
13
  authors = [{name = "ChatArch", email = "rex@chatarch.org"}]
14
- keywords = ["chatbug", "chatarch", "cli"]
14
+ keywords = ["chatbug", "chatarch", "security", "php2shell", "cli"]
15
15
  classifiers = [
16
16
  "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
17
18
  "Operating System :: OS Independent",
19
+ "Topic :: Security",
18
20
  ]
19
21
 
20
22
  [project.urls]
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: ChatBug
3
+ Version: 0.1.0
4
+ Summary: Authorized website vulnerability validation with safe PHP2Shell checks and isolated reproduction labs
5
+ Author-email: ChatArch <rex@chatarch.org>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ChatArch/ChatBug
8
+ Project-URL: Repository, https://github.com/ChatArch/ChatBug
9
+ Project-URL: Documentation, https://arch.gh.wzhecnu.cn/ChatBug/
10
+ Keywords: chatbug,chatarch,security,php2shell,cli
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Security
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: click>=8.0
19
+ Requires-Dist: chatstyle<0.3.0,>=0.2.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: build; extra == "dev"
22
+ Requires-Dist: pytest; extra == "dev"
23
+ Requires-Dist: twine; extra == "dev"
24
+ Provides-Extra: docs
25
+ Requires-Dist: mkdocs<2.0,>=1.6; extra == "docs"
26
+ Requires-Dist: mkdocs-material<9.7,>=9.5; extra == "docs"
27
+ Requires-Dist: mkdocs-static-i18n<2.0,>=1.2; extra == "docs"
28
+ Requires-Dist: mike<3.0,>=2.0; extra == "docs"
29
+ Dynamic: license-file
30
+
31
+ # ChatBug
32
+
33
+ ChatBug 是 ChatArch 系列的授权网站漏洞验证包。首个能力聚焦 **PHP2Shell**:先对自己拥有的普通网站做无破坏性版本/入口检查,再用一次性、无真实数据的隔离实验环境复现“运行时 + LFI include gadget → session upload-progress 注入 → `ls/cat` 输出”的完整测试链。
34
+
35
+ 它不是通用攻击框架:不发送 OOM/UAF 崩溃 payload,不对生产执行命令,不提供反弹 shell,也不接受任意远程命令参数。
36
+
37
+ 文档入口:<https://arch.gh.wzhecnu.cn/ChatBug/>
38
+
39
+ ## 适用场景
40
+
41
+ - 判断自有 PHP 网站是否暴露在受影响版本范围。
42
+ - 用普通 multipart 请求确认上传解析入口是否可达。
43
+ - 用固定、小规模的常见 include 参数形态做**标记文件读取**探测。
44
+ - 在本地进程实验或远端一次性 Docker 实验中,跑通可重复的 `ls -la /tmp` / `cat /tmp/chatbug-marker.txt` 验收。
45
+ - 向团队输出可审计 JSON,区分“版本暴露”“解析器可达”“隔离链复现成功”和“生产利用未证明”。
46
+
47
+ ## 安全边界
48
+
49
+ | 命令 | 作用对象 | 是否执行命令 | 是否接触生产数据 |
50
+ | --- | --- | --- | --- |
51
+ | `chatbug php2shell web check <URL>` | 自有普通网站 URL | 否 | 否;只发 HEAD、普通 multipart 和固定 gadget 探测 |
52
+ | `chatbug php2shell lab local-list` | 本机临时 HTTP lab | 否;不启动系统 shell | 否;进程内合成数据 |
53
+ | `chatbug php2shell lab docker-list` | 远端一次性内部 Docker lab | 仅 lab 固定 allowlist 命令 | 否;无生产卷、无发布端口、合成 marker |
54
+
55
+ `docker-list` 复现的是传统、确定性的 PHP `include` 文件包含链:`PHP_SESSION_UPLOAD_PROGRESS` 写入 session 文件,lab-only 的 `include($_GET['page'])` 再包含该文件。它证明“受影响 PHP-FPM 运行时 + 应用存在 LFI gadget”时可以走到 `ls/cat`,**不单独证明 CVE-2026-109618/109619 的内存破坏 RCE**。
56
+
57
+ ## 安装
58
+
59
+ ```bash
60
+ python -m pip install ChatBug
61
+ chatbug --version
62
+ chatbug --tree
63
+ ```
64
+
65
+ 开发环境:
66
+
67
+ ```bash
68
+ python -m pip install -e ".[dev]"
69
+ python -m pytest -q
70
+ ```
71
+
72
+ ## 快速复现:本机可运行 test case
73
+
74
+ 无需 Docker、无需 root、无需网络:
75
+
76
+ ```bash
77
+ chatbug php2shell lab local-list
78
+ ```
79
+
80
+ 成功时输出 JSON,关键字段包括:
81
+
82
+ - `accepted: true`
83
+ - `positive_status: 200`
84
+ - `negative_status: 200`
85
+ - `positive_output` 包含 `/tmp`、`chatbug-marker.txt` 和随机 marker
86
+ - `negative_body` 只有 `Hello World`,证明普通安全入口不会把 session 文件当 PHP 执行
87
+
88
+ 这个命令也由测试覆盖:
89
+
90
+ ```bash
91
+ python -m pytest -q tests/test_php2shell.py
92
+ ```
93
+
94
+ ## 完整隔离复现:PHP-FPM + Nginx
95
+
96
+ 需要一个可通过 SSH 使用 Docker 的自有主机。该命令会:
97
+
98
+ 1. 拉取固定 digest 的 PHP/PHP-FPM 镜像作为运行时;
99
+ 2. 生成只含 `Hello World` 和 lab-only `include` sink 的临时 webroot;
100
+ 3. 创建 internal Docker network,不发布主机端口;
101
+ 4. 在 lab 容器的 `/tmp/chatbug-marker.txt` 写入随机合成 marker;
102
+ 5. 从同一内部网络的一次性 client 容器发送 session upload-progress 请求;
103
+ 6. 正向请求通过 lab-only LFI sink 返回 `ls/cat` 输出;
104
+ 7. 负向请求访问普通 `hello.php`,只返回 `Hello World`;
105
+ 8. 删除容器、网络和远端临时目录。
106
+
107
+ ```bash
108
+ chatbug php2shell lab docker-list \
109
+ --ssh-host <your-ssh-host> \
110
+ [--fpm-image <digest-pinned-php-fpm-image>] \
111
+ --proof list-and-marker
112
+ ```
113
+
114
+ `--proof` 只允许三个固定模式:
115
+
116
+ - `list-tmp`:执行 lab 内 `ls -la /tmp`;
117
+ - `cat-marker`:读取 lab 内合成 marker 文件;
118
+ - `list-and-marker`:同时列目录并读取 marker(默认)。
119
+
120
+ 真实验收输出会在 `positive_output` 中包含 lab 的 `/tmp` 列表、`sess_<id>` 文件、`MARKER` 和随机 marker;负向输出必须保持 `Hello World`。
121
+
122
+ ## 检查自有普通网站
123
+
124
+ ```bash
125
+ chatbug php2shell web check https://example-owned-site.example/
126
+ ```
127
+
128
+ 输出字段解释:
129
+
130
+ - `verdict=vulnerable_version_exposed`:版本头显示在受影响范围,只代表暴露,不代表 RCE;
131
+ - `evidence_tier=tier_2_parser_reachable`:普通 multipart 解析入口可达;
132
+ - `evidence_tier=tier_3_gadget_found`:固定探测中发现服务端返回了独立 marker;
133
+ - `rce_or_file_read_proven=true`:只有在标记不能由请求反射解释、确实从服务端 session 文件包含返回时才成立。
134
+
135
+ 可跳过较重的探测:
136
+
137
+ ```bash
138
+ chatbug php2shell web check https://example-owned-site.example/ --skip-gadget-scan
139
+ ```
140
+
141
+ ## 证据层级
142
+
143
+ ChatBug 不把弱证据升级成强结论:
144
+
145
+ 1. 版本/配置暴露;
146
+ 2. 解析器或前置条件可达;
147
+ 3. 一次性隔离环境中完成 marker/命令输出验收;
148
+ 4. 经单独风险授权后的生产验证;
149
+ 5. 第三方真实入侵指标。
150
+
151
+ 对邮件、文档等含真实用户数据的生产服务,不应发送内存破坏、OOM、崩溃或重复利用流量;先用同镜像、无数据 lab 完成验收。
152
+
153
+ ## 文档
154
+
155
+ - [CLI 树](docs/cli-tree.md)
156
+ - [能力地图](docs/capability-map.md)
157
+ - [Python 接口树](docs/interface-tree.md)
158
+ - [PHP2Shell 复现实验](docs/php2shell-lab.md)
159
+
160
+ ## 开发说明
161
+
162
+ 扩展能力前先阅读 `DEVELOP.md` 和 `AGENTS.md`。新增生产检查必须保持非破坏性;任何需要固定命令以外的实验能力,应先增加独立、隔离、可清理的 lab,而不是扩展远程任意命令接口。
@@ -9,5 +9,9 @@ src/ChatBug.egg-info/requires.txt
9
9
  src/ChatBug.egg-info/top_level.txt
10
10
  src/chatbug/__init__.py
11
11
  src/chatbug/cli.py
12
+ src/chatbug/lab.py
13
+ src/chatbug/local_lab.py
14
+ src/chatbug/php2shell.py
12
15
  tests/test_cli.py
16
+ tests/test_php2shell.py
13
17
  tests/test_version.py
@@ -1,6 +1,5 @@
1
1
  click>=8.0
2
2
  chatstyle<0.3.0,>=0.2.0
3
- chatenv<0.3.0,>=0.2.11
4
3
 
5
4
  [dev]
6
5
  build
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.0.1"
5
+ __version__ = "0.1.0"
@@ -0,0 +1,165 @@
1
+ """ChatBug command line interface."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import secrets
7
+ import sys
8
+ from typing import Any
9
+
10
+ import click
11
+ from chatstyle import add_tree_option
12
+
13
+ from chatbug import __version__
14
+ from chatbug.lab import DEFAULT_PHP_FPM_IMAGE, LabSpec, logs, run_lfi_proof, seed_marker, start, stop
15
+ from chatbug.php2shell import build_lab_session_payload, extract_command_output, http_request
16
+ from chatbug.php2shell import random_marker, safe_url_check
17
+
18
+
19
+ def emit(payload: dict[str, Any]) -> None:
20
+ click.echo(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True))
21
+
22
+
23
+ @click.group(name="chatbug", invoke_without_command=True, no_args_is_help=True)
24
+ @click.version_option(__version__, prog_name="chatbug")
25
+ @add_tree_option(renderer_options={"root_name": "chatbug"})
26
+ def main() -> None:
27
+ """Owned website vulnerability validation."""
28
+
29
+
30
+ @main.group(name="php2shell")
31
+ def php2shell() -> None:
32
+ """PHP2Shell web checks and data-free lab proof."""
33
+
34
+
35
+ @php2shell.group(name="web")
36
+ def web() -> None:
37
+ """Checks for an ordinary owned website URL."""
38
+
39
+
40
+ @web.command(name="check")
41
+ @click.argument("url")
42
+ @click.option("--skip-multipart", is_flag=True, help="Do not send the benign multipart request.")
43
+ @click.option("--skip-gadget-scan", is_flag=True, help="Do not probe common include parameter shapes.")
44
+ def web_check(url: str, skip_multipart: bool, skip_gadget_scan: bool) -> None:
45
+ """Check version exposure, parser reachability, and common include gadgets."""
46
+
47
+ result = safe_url_check(
48
+ url,
49
+ include_multipart=not skip_multipart,
50
+ include_gadgets=not skip_gadget_scan,
51
+ )
52
+ emit(result)
53
+ sys.exit(0 if result["verdict"] == "vulnerable_version_exposed" else 3)
54
+
55
+
56
+ @php2shell.group(name="lab")
57
+ def lab_group() -> None:
58
+ """Disposable, data-free lab workflows."""
59
+
60
+
61
+ @lab_group.command(name="local-list")
62
+ def lab_local_list() -> None:
63
+ """Run a complete positive/negative example in an in-process local lab."""
64
+
65
+ import chatbug.local_lab as local_lab
66
+ from chatbug.local_lab import serve_local_lab
67
+
68
+ sid = "localtest" + secrets.token_hex(8)
69
+ marker = random_marker()
70
+ headers, body = build_lab_session_payload(sid, "list-and-marker")
71
+ synthetic_output = f"___START___\n/tmp\nchatbug-marker.txt\n{marker}\n___END___"
72
+ with serve_local_lab() as local:
73
+ post_status, _, _ = http_request(f"{local.url}/hello.php", method="POST", headers=headers, data=body)
74
+ local_lab.SESSIONS[sid] = synthetic_output
75
+ positive_status, _, positive_body = http_request(
76
+ f"{local.url}/vuln.php?page=/tmp/sessions/sess_{sid}",
77
+ headers={"Cookie": f"PHPSESSID={sid}"},
78
+ )
79
+ negative_status, _, negative_body = http_request(
80
+ f"{local.url}/hello.php?page=/tmp/sessions/sess_{sid}",
81
+ headers={"Cookie": f"PHPSESSID={sid}"},
82
+ )
83
+ command_output = extract_command_output(positive_body) or ""
84
+ result = {
85
+ "schema": "chatbug.php2shell.local_lab/v1",
86
+ "accepted": marker in command_output and marker not in negative_body.decode(),
87
+ "post_status": post_status,
88
+ "positive_status": positive_status,
89
+ "negative_status": negative_status,
90
+ "positive_output": command_output,
91
+ "negative_body": negative_body.decode(),
92
+ "boundary": "in-process lab; no Docker, shell, production network, or private data",
93
+ }
94
+ emit(result)
95
+ sys.exit(0 if result["accepted"] else 1)
96
+
97
+
98
+ @lab_group.command(name="docker-list")
99
+ @click.option("--ssh-host", required=True, help="SSH alias for the owned Docker host.")
100
+ @click.option(
101
+ "--fpm-image",
102
+ default=DEFAULT_PHP_FPM_IMAGE,
103
+ show_default=True,
104
+ help="Digest-pinned or exact vulnerable PHP-FPM image to validate in isolation.",
105
+ )
106
+ @click.option(
107
+ "--proof",
108
+ type=click.Choice(["list-tmp", "cat-marker", "list-and-marker"], case_sensitive=False),
109
+ default="list-and-marker",
110
+ show_default=True,
111
+ )
112
+ @click.option("--keep", is_flag=True, help="Leave the disposable lab running for manual inspection.")
113
+ def lab_docker_list(ssh_host: str, fpm_image: str, proof: str, keep: bool) -> None:
114
+ """Prove list/cat inside a data-free PHP-FPM lab with an intentional include sink."""
115
+
116
+ spec = LabSpec(ssh_host=ssh_host, keep=keep, fpm_image=fpm_image)
117
+ marker = random_marker()
118
+ sid = "lab" + secrets.token_hex(10)
119
+ report: dict[str, Any] = {
120
+ "schema": "chatbug.php2shell.docker_lab/v1",
121
+ "project": spec.project,
122
+ "network": spec.network,
123
+ "proof": proof,
124
+ "fpm_image": fpm_image,
125
+ "boundary": "internal Docker network; synthetic data only; no published host port; production untouched",
126
+ "marker": marker,
127
+ }
128
+ remote_dir: str | None = None
129
+ try:
130
+ report["start"] = start(spec)
131
+ remote_dir = report["start"]["remote_dir"]
132
+ assert isinstance(remote_dir, str)
133
+ report["seed"] = seed_marker(spec, remote_dir, marker)
134
+ report["result"] = run_lfi_proof(spec, remote_dir, sid, proof)
135
+ positive_output = report["result"]["positive_output"]
136
+ negative_output = report["result"]["negative_output"]
137
+ marker_expected = proof in {"cat-marker", "list-and-marker"}
138
+ positive_evidence = (
139
+ marker in positive_output
140
+ if marker_expected
141
+ else "chatbug-marker.txt" in positive_output
142
+ )
143
+ report["accepted"] = (
144
+ report["result"]["positive_status"] == 200
145
+ and report["result"]["negative_status"] == 200
146
+ and positive_evidence
147
+ and marker not in negative_output
148
+ )
149
+ report["interpretation"] = "Runtime plus intentional lab-only include gadget proven."
150
+ except Exception as exc:
151
+ report["accepted"] = False
152
+ report["error"] = str(exc)
153
+ if remote_dir:
154
+ report["logs"] = logs(spec, remote_dir)
155
+ emit(report)
156
+ sys.exit(1)
157
+ finally:
158
+ if not keep and remote_dir:
159
+ report["cleanup"] = stop(spec, remote_dir)
160
+ emit(report)
161
+ sys.exit(0 if report.get("accepted") else 1)
162
+
163
+
164
+ if __name__ == "__main__":
165
+ main()