Содержание статьи
Я постараюсь объяснить все аспекты, которые обычно мешают успешно эксплуатировать SSRF. Пройдем от самых азов до продвинутых техник.
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Азы SSRF
Для практики соберем простое веб‑приложение со скрытой админкой. Создай текстовый файл docker-composer. с таким содержимым:
version: "3.9"services: public-web: build: ./public-web ports: - "8090:80" networks: - ssrfnet internal-admin: build: ./internal-admin networks: - ssrfnet expose: - "80"networks: ssrfnet: driver: bridgeМашина public-web будет играть роль уязвимого веб‑приложения. Машина internal-admin доступна исключительно внутри сети Docker, то есть с хост‑машины или откуда‑то еще невозможно до нее достучаться.
Рядом с docker-composer. создай папки public-web и internal-admin. В internal-admin положи admin.. Это скрытая страница, имитирующая админку:
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <h1>Ты нашел админку</h1> <p>Это скрытый внутренний ресурс</p></body></html>Добавь Dockerfile:
FROM nginx:1.25-alpineCOPY admin.html /usr/share/nginx/html/admin.htmlВ папке public-web добавь еще один Dockerfile, который поднимет сервис на базе PHP:
FROM php:8.2-apacheCOPY index.php /var/www/html/Код уязвимого веб‑приложения:
<?phpif (isset($_GET['download']) && isset($_GET['api_url'])) { $url = $_GET['api_url']; $data = @file_get_contents($url); if ($data === false) { http_response_code(500); echo "Не удалось скачать данные: " . htmlspecialchars($url); exit; } $filename = basename(parse_url($url, PHP_URL_PATH)); if (!$filename) { $filename = "currencies.zml"; } $tmpDir = sys_get_temp_dir(); $filePath = $tmpDir . "/" . $filename; $zipPath = $tmpDir . "/export.zip"; file_put_contents($filePath, $data); $zip = new ZipArchive(); $zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE); $zip->addFile($filePath, $filename); $zip->close(); header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename="export.zip""); header("Content-Length: " . filesize($zipPath)); readfile($zipPath); exit;}if (isset($_GET['api_url']) && !isset($_GET['download'])) { $url = $_GET['api_url']; $resp = @file_get_contents($url); echo "<h3>Результат запроса:</h3><pre>" . htmlspecialchars($resp) . "</pre>"; echo "<hr><a href='/'>Назад</a>"; exit;}$ru = "https://www.cbr.ru/scripts/XML_daily.asp";$en = "https://www.cbr-xml-daily.ru/daily_eng.xml";?><!DOCTYPE html><html lang="ru"><head> <meta charset="UTF-8"> <title>Демо SSRF</title></head><body> <h2>Проверка курсов валют</h2> <form method="GET"> <select name="api_url"> <option value="<?= $ru ?>">Русский (ЦБ РФ)</option> <option value="<?= $en ?>">English API</option> </select> <button type="submit">Узнать курсы валют</button> <button type="submit" name="download" value="1">Скачать курсы (ZIP)</button> </form></body></html>
Приложение получает курсы валют с сайта ЦБ. Оно уязвимо не только к SSRF, но и к LFI и RFI. Но мы сконцентрируемся на SSRF. Когда пользователь выберет язык и нажмет «Узнать курсы валют», выполнится GET-запрос. В параметрах api_url, который сообщает приложению адрес, по которому можно получить данные. Выполни тестовый запрос, чтобы убедиться, что данные приходят.
info
О том, как работает техника LFI, читай в статье «File Inclusion и Path Traversal. Разбираем две базовые веб‑уязвимости».
Ты видишь исходники всего проекта. Но представь, что просто просматриваешь запросы к веб‑приложению. На что ты обратишь внимание? Думаю, что на указание URL в одной из переменных. Это и есть простой способ предположить, что сервер уязвим к SSRF.

Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
