Snitch-1302/reverse-shell
GitHub: Snitch-1302/reverse-shell
用 Python 标准库实现的多客户端反向 shell,帮助安全学习者从基本原理理解 C2 通信和远程命令执行机制。
Stars: 1 | Forks: 0
# reverse-shell
一个反向 shell 的 Python 实现 —— 旨在从基本原理出发,理解攻击性的网络通信和命令与控制机制,使用原始套接字而不是高级框架。
## 这是什么
反向 shell 攻击的工作原理是让受感染的机器(这里的**客户端**)主动向攻击者的机器(**服务器**)发起出站连接,而不是攻击者发起入站连接 —— 这正是它能绕过那些阻止入站连接但允许出站连接的防火墙的原因。一旦连接成功,服务器就可以向客户端发送 shell 命令并接收其输出,从而实现远程控制。
此实现支持:
- 多个同时进行的客户端连接(最多排队 5 个)
- 服务器端的 `list` / `select` 接口,用于选择要控制哪个已连接的客户端
- 连接断开时客户端自动重连
- 服务器端多线程:一个线程接受传入连接,另一个线程处理交互式命令提示符
## 技术栈
- Python 3.x
- `socket` —— 客户端和服务器之间的原始 TCP 通信
- `threading` —— 并发处理接受连接和命令提示符
- `subprocess` —— 在客户端执行 shell 命令并捕获输出
- `queue` —— 协调服务器的工作线程
## 项目结构
```
reverse-shell/
├── client.py # runs on the target machine; connects out to the server
├── server.py # runs on the attacker machine; accepts connections, sends commands
└── README.md
```
## 设置
无需外部依赖 —— 两个文件仅使用 Python 的标准库。
1. 在 `client.py` 中,将 `host` 设置为运行 `server.py` 的机器的 IP 地址
2. 两个文件默认都使用端口 `9999` —— 如有需要,请在两个文件中一致地进行更改
## 运行它
**在“攻击者”机器上:**
```
python server.py
```
**在“目标”机器上:**
```
python client.py
```
连接成功后,在服务器端:
```
rshell> list # shows all connected clients
rshell> select 0 # selects client at index 0
127.0.0.1> whoami # runs a command on that client, shows output
127.0.0.1> quit # returns to the rshell> prompt
```
## 我学到的 / 我会改进的地方
构建和调试这个过程暴露了两个值得理解的真实 bug,而不仅仅是让 shell 连接成功的“理想路径”:
**1. 无限递归而不是循环。** 最初的 `client.py` 在最后无条件地再次调用 `main()` 自身 —— 而不仅仅是在发生错误之后。由于每次调用都是一个新栈帧,而不是一次循环迭代,这最终会触及 Python 的递归限制,并在足够多的重连尝试后因 `RecursionError` 崩溃,而不是像逻辑预期的那样无限运行。修复方法是将递归自调用替换为 `while True:` 循环 —— 同样是“不断重试”的行为,但不会增加栈。
**2. 在遍历列表时修改列表。** 服务器上的 `list_connections()` 在遍历 `enumerate(all_connections)` 的同一个循环中,从 `all_connections`/`all_addresses` 中删除了失效的连接。在迭代过程中删除会使后面每个索引向下移动一位,这可能导致循环跳过对下一个连接的检查。我具体证明了这一点:对于两个*连续的*失效连接,原始逻辑只删除了其中一个,留下了一个过时的失效连接被列为活动状态。修复方法是先收集失效索引,然后以逆序将它们删除。
**3. 关闭的 stdin 上未处理的 `EOFError`。** 如果服务器的输入流关闭(例如 `Ctrl+D`,或者在我自己的测试中,有限的管道输入源耗尽),`input()` 会引发 `EOFError`,而它在任何地方都没有被捕获 —— 导致整个提示符线程因 traceback 而崩溃。修复方法是在两个 `input()` 调用周围捕获 `EOFError`,并干净地退出提示符循环。
我通过在真实的本地套接字连接上实际运行相互通信的客户端和服务器来验证了所有这三个修复 —— 而不仅仅是推理代码 —— 包括在每个修复前后故意重现确切的崩溃场景,以确认行为确实发生了变化。
如果有更多时间,我仍然会改进的地方:
- 加密命令通道(目前是明文 TCP —— 对任何监控网络的人来说都是可见的)
- 用更具体的异常类型替换整个代码中的裸异常处理
- 通过命令行参数而不是硬编码常量来配置 host/port
## 已知的局限性
- 命令通道是未加密的明文 —— 对网络监控可见
- 客户端和服务器之间没有身份验证 —— 任何连接到所配置端口的客户端都会被接受
- Host/port 是硬编码常量,而不是在运行时可配置的
## 完整说明
这两个真实的 bug、我是如何发现并证明它们的,以及关于授权使用背景的更深入探讨:[Hashnode 文章链接]
标签:Python, 命令与控制(C2), 无后门, 网络安全, 远程控制, 逆向Shell, 逆向工具, 隐私保护