Загрузка локальных файлов при помощи SQL-инъекции

Загрузка локальных файлов при помощи SQL-инъекции

SQL-инъекция является прародительницей многих хакерских атак и хорошо известной уязвимостью, которая стала причиной многих проблем в киберпространстве.

Введение
SQL-инъекция является прародительницей многих хакерских атак и хорошо известной уязвимостью, которая стала причиной многих проблем в киберпространстве. Специалисты опубликовали множество исследований, связанных с техниками подмены входных данных, для выполнения различных атак, включая доступ к базам данных, чтение запись кода с/на сервер при помощи инструкций load/into outfile в MySQL и выполнение команд от имени учетной записи SA в MSSQL.
В этой статье мы рассмотрим эксплуатацию SQL-инъекции на примере функции, которая будет загружать файлы с сервера на базе информации, возвращаемой уязвимым запросом.
Рассмотрим сценарий, когда пользователь передает параметр, обрабатываемый в SQL-запросе, который затем возвращает местонахождения файла. Далее значение, возвращаемое запросом, передается в функцию загрузки локального файла с сервера. В этом случае если информация, вводимая пользователем, не проверяется в веб-приложении, злоумышленник может манипулировать SQL-запросом и загружать любые файлы с сервера, у которых известно местонахождения (у файла обязательно должны быть права на чтение).
В этом документе я продемонстрирую загрузку файлов в веб-приложении, использующего PHP и MySQL. SQL-инъекция будет использоваться в параметре, связанного с загрузкой файла.
Тестовая среда:
В целях демонстрации работы эксплоита я установил следующие приложения:

  1. Веб-сервер (в моем случае – Apache).
  2. Интерпретатор PHP.
  3. База данных MySQL.
  4. Простейшее уязвимое приложение, которое можно скачать со страницы на github: https://github.com/incredibleindishell/Local-file-disclosure-SQL-Injection-Lab   

После загрузки тестового приложения создайте базу данных dsqli и пользователя dsqli с паролем icadsqli.
Для создания базы данных и пользователя с правами на чтение выполните следующие шаги:
Авторизуйтесь в консоли MySQL под именем суперпользователя (root).
Команда для создания новой базы данных:
Create database dsqli;
Команда для создания пользователя dsqli с паролем icadsqli, у которого будут права на чтение/запись в базу данных dsqli:
grant all on dsqli.* to dsqli@localhost IDENTIFIED BY 'icadsqli';
После создания базы данных и учетной записи пользователя импортируем дамп (файл dsqli.sql) в базу данных dsqli.
После импорта в базе данных появится таблица Download с колонками id, image_name и location.
Суть проблемы
Техника эксплуатации применима только в случае, если функция загрузки файлов использует результаты, возвращаемые SQL-запросом.
На рисунке ниже показан код, уязвимый к SQL-инъекции, где результаты SQL-запроса передаются в функцию file_download, предназначенную для загрузки файлов.

Рисунок 1: Пример уязвимого кода
В коде выше видно, что, если мы изменим SQL-запрос так, чтобы в переменной $row[‘location’] было местонахождение локального файла или файла с исходным текстом веб-приложения, функция загрузит этот файл.
Эта задача решается посредством добавления еще одного SQL-запроса в сочетании с инструкцией union во время инъекции, когда мы добавляем полный путь (локального) файла, который нужно загрузить, в шестнадцатеричной форме.
Схема эксплуатации
Во-первых, нужно выяснить, к какому типу инъекции уязвимо приложение: на базе целого числа или на базе строки. Затем нужно выяснить количество колонок в таблице, к которой выполняется SQL-запрос.
В нашем случае приложение уязвимо к SQL-инъекции с использованием целого числа.
Метод определения количества колонок основывается на переборе целого значения в секции order by. Если запрос выполняется корректно, и файл загружается, мы увеличиваем значение в секции order by до тех пор, пока не произойдет ошибка при выполнении запроса.
Веб-приложение имеет уязвимость в файле index.php. Запрос на загрузку файла отправляется на странице
index.php
с параметрами
image=1&image_download=Download

Рисунок 2: Страница для загрузки файла
Попробуем добавить секцию order by с номером колонки.
Страница: Index.php
Параметры запроса: image=1 order by 1--&image_download=Download

Рисунок 3: Результат выполнения запроса с секцией order by
Теперь попробуем изменить номер колонки в секции order by
Страница: Index.php
Параметры запроса: image=1 order by 5--&image_download=Download

Рисунок 4: Ошибка при выполнении запроса с параметром order by 5
После изменения значения параметра с 1 на 5 во время загрузки файла возникла ошибка. Сей факт свидетельствует о том, что количество колонок, допустимое в запросе, меньше 5.
Пробуем уменьшить значение:
Страница: Index.php
Параметры запроса: image=1 order by 4--&image_download=Download

Рисунок 5: Повторное сообщение об ошибке
Возникло то же самое сообщение об ошибке. Продолжаем уменьшать значение параметра:
Страница: Index.php
Параметры запроса: image=1 order by 3--&image_download=Download

Рисунок 6: Успешная загрузка файла
На этот раз загрузка завершилась успешно, и теперь мы знаем, что в таблице находится 3 колонки.
Попробуем добавить запрос с конструкцией union.
Страница: Index.php
Параметры запроса: image=1 union select 1,2,3--&image_download=Download

Рисунок 7: Успешная загрузка файлов с конструкцией union
Запуск двух запросов не помешал загрузке файла.
Поиск колонки, используемой при загрузке файла
После выяснения количества колонок, необходимо найти колонку, которая используется для загрузки файлов. Для решения этой задачи нужно поочередно помещать шестнадцатеричное представление пути к файлу (который мы хотим загрузить) в колонки до тех пор, пока не выяснится колонка, используемая для загрузки.
В качестве примера попробуем загрузить файл /etc/passwd. Вначале нужно преобразовать строку /etc/passwd в шестнадцатеричный формат (я использую дополнение в firefox). После преобразования к полученной строке добавляем префикс 0x. Поскольку мы не знаем номер нужной колонки, используемой веб-приложением для получения местонахождения файла, будем поочередно заменять числа во втором запросе на шестнадцатеричное значение /etc/passwd (см. рисунок ниже).

Рисунок 8: Замена одной из колонок на шестнадцатеричное представление местонахождения файла
Теперь текущий запрос:
Страница: Index.php
Параметры запроса: image=1 union select 1,2,3--&image_download=Download
Меняем на следующий запрос:
Страница: Index.php
Параметры запроса: image=1337 union select 1,2,3--&image_download=Download
Нам нужно изменить значение уязвимого параметра на нечто не существующее так, чтобы при выполнении запросов не возвращалось ничего, кроме результатов внедренного запроса. В противном случае мы будет продолжаться загрузка файла, возвращаемого легитимным запросом (без учета инъекции).
Проверим, как работает инжектированный запрос (в случае, если мы угадали с колонкой, используемой для загрузки файла), и будет ли загружаться файл /etc/passwd.
Страница: Index.php
Параметры запроса: image=1337 union select 0x2f6574632f706173737764,2,3--&image_download=Download

Рисунок 9: Ошибка при загрузке файла с использованием колонки 1
При выполнении модифицированного запроса веб-приложение вернуло ошибку. Соответственно, мы должны поменять колонку.
Пробуем добавить местонахождение файла в колонку 2.
Страница: Index.php
Параметры запроса: image=1337 union select 1,0x2f6574632f706173737764,3--&image_download=Download

Рисунок 10: Ошибка при загрузке файла с использованием колонки 2
Опять неудача. Пробуем третью колонку.
Страница: Index.php
Параметры запроса: 1337 union select 1,2,0x2f6574632f706173737764--&image_download=Download

Рисунок 11: Успешная загрузка файла с использованием колонки 3
Наконец-то сработало. Веб-приложение загрузило файл при подстановке шестнадцатеричного значения строки ‘/etc/passwd’ в третью колонку.
Мы также можем загрузить исходный код веб-приложения, если знаем путь к соответствующим файлам.
Допустим, мы хотим найти полный путь к PHP-скриптам, посредством изменения типа параметра в запросе. Например, строковой параметр меняем на массив или наоборот. На моей Windows-машине по умолчанию включен отчет об ошибках, поэтому будем рассматривать этот случай в качестве тестового примера, имеющего отношение к обнаружению пути к файлам.
Первоначальный запрос выглядел так:
image=1&image_download=Download
после изменения типа данных со строки на массив запрос будет выглядеть так:
image[]=1&image_download=Download

Рисунок 12: Результат выполнения запроса с измененным типом данных
Теперь мы знаем полный путь к скрипту и также можем загрузить файл. Нужно лишь добавить шестнадцатеричное представление пути к файлу в колонку, которая используется функцией загрузки.
Благодарности
Выражаю особую благодарность IndiShell Crew и Myhackerhouse за помощь и мотивацию к написанию этой статьи.
Обо мне
Работаю инженером по безопасности приложений, а также интересуюсь разработкой эксплоитов.
Продолжаю изучать уязвимости, связанные с подменой неканоническими входными данными.
Мой блог: http://mannulinux.blogspot.in/
Мой аккаунт на github: https://github.com/incredibleindishell

Ваша приватность умирает красиво, но мы можем спасти её.

Присоединяйтесь к нам!