det365和万博

如何利用OOB数据绕过防火墙对shellcode的拦截

作者:   深圳市网安计算机安全检测技术有限公司 2019/03/21 17:03:54 215次阅读 国际
文章来源:https://www.anquanke.com/post/id/173610

<time>发布时间:2019-03-20 11:00:17</time>

译文声明

本文是翻译文章,文章原作者shelliscoming,文章来源:shelliscoming.com?
原文地址:https://www.shelliscoming.com/2019/03/one-way-shellcode-for-firewall-evasion.html


译文仅供参考,具体内容表达以及含义原文为准

<button type="button" aria-label="Close">×</button>

alt

?

一、前言

在最近一篇文章中,我分享了可以绕过防火墙的一种shellcode技术,这种技术基于socket本身的生命周期,在某些利用场景中可以派上用场。关于这类shellcode(复用socket/连接)我想与大家分享另一种技术,这种技术在针对Windows系统的某些远程利用场景中非常有用(比如防火墙禁用出站连接,无法使用反向shell的场景)。

alt

对其他系统而言(比如Linux系统),这并不是一种全新的技术。实际上bkbll(HTRAN的开发者之一)曾在多年前使用这种方法来复用连接,这也是我在Windows上实现这种技术的原因所在。请大家记住一点,与我在前一篇文章中提到的一样,这类shellcode非常特别,只适用于某些特定的利用场景,并且有时候利用起来比较麻烦。可能这种方法在每个目标的应用上较为困难、耗时较久,因此攻击者以及渗透测试人员更倾向于使用“通用型”payload。

?

二、OOB数据

TCP允许我们在同一信道中发送alt

为了感知到这类数据,接收端必须使用MSG_OOB标志来调用recv函数,否则只会从数据流中读取“正常的”数据(没有使用此处链接

这一点对我们而言非常重要,如果某个应用程序无法处理OOB数据,那么即使我们发送了OOB数据,该应用也会像正常情况下处理TCP流。只有调用了对应的API,才可以获取到这类数据。那么我们如何利用这种特性呢?非常简单,在构造实际利用场景时,我们只需要在shellcode运行之前,在某些数据包中发送OOB数据(只需1字节即可)。stager只需要暴力遍历可能的socket,寻找带有OOB特征的socket即可。C语言版的实现如下所示:

alt

我以Metasploit中的reverse TCP shellcode作为模板来开发能够处理这种逻辑的stager。我将模板中用来生成反向连接的代码段替换为如下代码:

alt

红框中的asm代码负责遍历所有的socket描述符,直至找到带有OOB字节的socket为止。请注意,由于这种shellcode利用的是socket的生命周期(参考之前的文章),因此stager不会受NAT影响。

?

三、PoC:FTP Exploit

我们来看一下如何在Windows远程利用场景中使用这种技术。我们的测试目标为Konica Minolta FTP服务器,利用了该服务器中的一个漏洞。如果我们在漏洞利用中使用了之前的payload(windows/shell_reverse_tcp),那么就会生成两个连接:(1)用来触发漏洞的连接;(2)由stager创建的连接,回连到我们的4444端口。

alt

如果防火墙限制了出站连接,那么就会阻止反向shell,导致我们攻击失败。现在来看看如何构造我们的“单向式shellcode”。

首先我们稍微修改发送到目标服务的数据,来看一下服务端会有什么反应。我们可以在USER Anonymous字符串末尾添加一个新字节(一个A),(通过MSG_OOB标志)将其作为OOB数据发送。

alt

为了全面了解FTP服务对通信数据的处理过程,我选择使用Frida。Frida是我很喜欢的一款工具,可以帮我们节省大量的调试时间。我使用如下脚本来执行frida-trace,以便获取recv?API返回的所有参数及值(之前我也使用frida-trace来识别用来发送/接收数据的API,如sendsendtorecvrecvfromWSASend以及WSARecv等)。

alt

开始利用漏洞后,我们可以观察到如下结果,其中最重要的数据已用红框标注出来。需要注意的是,recv函数在获取User anonymous时返回的是0x10字节(而非0x11字节),也就是说,该函数并没有考虑到以OOB形式发送的额外字节。根据这一信息,我们可以推测目标服务并没有使用SO_OOBINLINE来设置socket句柄(如果设置该标志,则会在读取正常数据流时一并读取OOB数据)。

alt

因此,我们只需要知道用来收集存在漏洞的命令(这里为CWD)的缓冲区大小,以便调整偏移量。当stager找到socket句柄时,就会执行如下代码。这里我没有发送payload的大小,而是直接调用VirtualAlloc来保留一个足够大的缓冲区(4MB)。之所以在eax等于FFFFFFF时停止接收数据,是因为这种情况下socket为非阻塞(non-blocking)状态,如果无法从缓冲区中获取更多数据,那么就会返回WSAEWOULDBLOCK。这种方式不是特别稳定,可以添加更多逻辑(如添加GetLastError?API),但作为PoC来说这样处理已经足够。

alt

接下来生成汇编代码,使用msfvenom进行混淆处理:

alt

我使用Visual Studio编译的一个简单程序作为payload,功能特别简单,只是弹出一个MsgBox。为了将.exe转换为“可映射”版本(这样才能以反射方式加载),我使用了Amber这款工具。

alt

alt

最终利用代码如下:

alt

这里还要强调一点。对于这次利用过程,我将OOB字节在恶意缓冲区之前发送(并且只发送一次)。正确的做法是尽可能将OOB字节贴近能够触发漏洞的代码,具体原因参考如下解释

如果没有设置SO_OOBINLINE?socket选项,并且发送程序发送的OOB数据大于1字节,那么除最后1字节之外的所有字节都会被当成普通数据(普通数据意味着接收程序不需要设置MSG_OOB标志就能接收数据)。已发送的OOB数据的最后1个字节并没有存储在普通数据中,只有通过MSG_OOB标志调用recv()recvmsg()或者recvfrom()?API才能提取该字节。如果没有通过MSG_OOB标志来接收数据,那么收到普通数据后OOB字节就会被删除。此外,如果发送了多个OOB数据,那么前一次OOB数据就会被丢弃,接收端只会记住最后一次出现的OOB数据的具体位置。

再次攻击该服务后,Wireshark的抓包结果如下所示,这里只看到一个会话:

alt

需要注意的是,虽然这种利用方式非常容易构造,但前面我提到过,有时候我们很难或者不可能在某些利用场景中使用这种方法。有时候被利用的进程本身并没有使用socket句柄,有时候即便进程中用到了socket句柄,但watchdog或者其他线程可能会破坏我们payload的功能。

大家可以访问我的Github获取相关代码,演示过程可参考此处视频


指导单位
广东省公安厅网络警察总队 广东省信息安全等级保护协调小组办公室