首先看一下启动程序run.sh
#!/bin/sh
#Using to build docker environment
#export LD_LIBRARY_PATH=/lib/x86_64-linux-gnu/pulseaudio
./qemu-system-x86_64 -L ./dependency -kernel ./vmlinuz-5.4.0-58-generic -initrd ./rootfs.cpio -cpu kvm64,+smep \\\\
-m 64M \\\\
-monitor none \\\\
-device fun \\\\
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \\\\
-nographic
漏洞肯定位于fun
设备中,用ida
分析一下qemu-system-x86_64
,从fun_class_init
函数中我们可以得到vendor_id,class_id
。在qemu
中查看一下resource
,看到只有一个内存空间
/ # lspci
00:01.0 Class 0601: 8086:7000
00:04.0 Class 00ff: cafe:babe
00:00.0 Class 0600: 8086:1237
00:01.3 Class 0680: 8086:7113
00:03.0 Class 0200: 8086:100e
00:01.1 Class 0101: 8086:7010
00:02.0 Class 0300: 1234:1111
/ # cat /sys/devices/pci0000\\\\:00/0000\\\\:00\\\\:04.0/resource
0x00000000febf1000 0x00000000febf1fff 0x0000000000040200
这里我们直接进入mmio_read/write
函数进行分析,先来看一下mmio_write
函数。其中FunState
的结构如下
00000000 FunState struc ; (sizeof=0xA00, align=0x10, copyof_4860)
00000000 pdev PCIDevice_0 ?
000008F0 mmio MemoryRegion_0 ?
000009E0 addr dd ?
000009E4 size dd ?
000009E8 idx dd ?
000009EC result_addr dd ?
000009F0 req dq ? ; offset
000009F8 as dq ? ; offset
00000A00 FunState ends
void __cdecl fun_mmio_write(FunState *opaque, hwaddr cmd, uint32_t_0 val)
{
switch ( cmd )
{
case 0uLL:
opaque->size = val;
break;
case 4uLL:
opaque->addr = val;
break;
case 8uLL:
opaque->result_addr = val;
break;
case 0xCuLL:
opaque->idx = val;
break;
case 0x10uLL:
if ( opaque->req )
handle_data_read(opaque, opaque->req, opaque->idx);
break;
case 0x14uLL:
if ( !opaque->req )
opaque->req = create_req(opaque->size);
break;
case 0x18uLL:
if ( opaque->req )
delete_req(opaque->req);
opaque->req = 0LL;
opaque->size = 0;
break;
default:
return;
}
}
函数根据cmd
为fun
结构体中的不同的成员变量赋值,其中当cmd=0x14
的时候会为req
创建相应的结构体,我们看一下这个函数
FunReq *__cdecl create_req(uint32_t_0 size)
{
uint32_t_0 i; // [rsp+10h] [rbp-10h]
uint32_t_0 t; // [rsp+14h] [rbp-Ch]
FunReq *req; // [rsp+18h] [rbp-8h]
if ( size > 0x1FBFF )
return 0LL;
req = (FunReq *)malloc(0x400uLL);
memset(req, 0, sizeof(FunReq));
req->total_size = size;
t = (req->total_size >> 10) + 1;
for ( i = 0; i < t; ++i )
req->list[i] = (char *)malloc(0x400uLL);
return req;
}
即按照opaque->size
的值为req
的成员变量total_size
赋值,req->list
中堆块指针数量由(size>>10) + 1
决定,其中最多为127
个。当cmd
为0x18
的时候会调用delete_req
函数,
void __cdecl delete_req(FunReq *req)
{
uint32_t_0 i; // [rsp+18h] [rbp-8h]
uint32_t_0 t; // [rsp+1Ch] [rbp-4h]
t = (req->total_size >> 10) + 1;
for ( i = 0; i < t; ++i )
free(req->list[i]);
free(req);
}
根据req->total_size
的值,依次释放req->list[i]
指向的内存空间。当cmd=0x10
且req!=NULL
的时候会调用handle_data_read
函数
void __cdecl handle_data_read(FunState *fun, FunReq *req, uint32_t_0 val)
{
if ( req->total_size && val <= 0x7E && val < (req->total_size >> 10) + 1 )
{
put_result(fun, 1u);
dma_memory_read_9(fun->as, (val << 10) + fun->addr, req->list[val], 0x400uLL);
put_result(fun, 2u);
}
}
void __cdecl put_result(FunState *fun, uint32_t_0 val)
{
uint32_t_0 result; // [rsp+14h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
result = val;
dma_memory_write_9(fun->as, fun->result_addr, &result, 4uLL);
}
int __cdecl dma_memory_write_9(AddressSpace_0 *as, dma_addr_t addr, const void *buf, dma_addr_t len)
{
return dma_memory_rw_24(as, addr, (void *)buf, len, DMA_DIRECTION_FROM_DEVICE);
}
int __cdecl dma_memory_read_9(AddressSpace_0 *as, dma_addr_t addr, void *buf, dma_addr_t len)
{
return dma_memory_rw_24(as, addr, buf, len, DMA_DIRECTION_TO_DEVICE);
}
这里首先调用put_result
将固定的值写入到fun->result_addr
指向的物理内存中,然后调用dma_memory_read_9
函数将fun->addr+(val << 10)
指向的物理内存中的内容写入到req->list[index]
指向的内存空间中。即写入数据。
再来看一下mmio_read
函数
uint32_t_0 __cdecl fun_mmio_read(FunState *opaque, hwaddr cmd)
{
uint32_t_0 val; // [rsp+20h] [rbp-10h]
val = -1;
switch ( cmd )
{
case 0uLL:
val = opaque->size;
break;
case 4uLL:
val = opaque->addr;
break;
case 8uLL:
val = opaque->result_addr;
break;
case 0xCuLL:
val = opaque->idx;
break;
case 0x10uLL:
if ( opaque->req )
handle_data_write(opaque, opaque->req, opaque->idx);
break;
default:
return val;
}
return val;
}
可以看到是根据cmd
的值来决定返回的FunState
中的成员变量的值,当cmd=0x10
的时候如果req!=NUll
那么就会调用handle_data_write
函数看一下该函数