Содержание статьи
Всем привет. Как часто ты натыкаешься на скрипты администрирования MySQL? Лично я — постоянно. То Adminer проскочит, то старичок Sypex dumper промелькнет на периметре. Или, может быть, тебе попался скрипт, который может импортировать данные из сторонней БД? В общем, если у тебя есть возможность коннекта из приложения к своему серверу MySQL, то считай, что у тебя в кармане как минимум читалка файлов.
Intro
Техника, о которой я хочу сейчас рассказать, — это особенность протокола MySQL. Поэтому фиксить ее, сам понимаешь, никто не торопится. Шутка ли, эксплуатация этой фичи находится в паблике с 2013 года.
В MySQL есть конструкция LOAD DATA LOCAL INFILE 'filename'
, с помощью которой можно импортировать в таблицу локальные (клиентские) файлы. Нам нужно посмотреть, как же она работает на пакетном уровне. Для этого я вооружился tcpdump’ом, чтобы лучше их слышать, и Wireshark’ом, чтобы лучше их видеть. Присоединился к тестовому MySQL, выполнил команду LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE test FIELDS TERMINATED BY '\n';
, посмотрел результат импорта и вежливо попрощался с сервером.
Подробнее
Теперь все внимание на Wireshark. Авторизацию и прочие нежности опустим и сразу перейдем к пакету с запросом, точнее к ответу от сервера на него:
0000 0c 00 00 01 fb 2f 65 74 63 2f 70 61 73 73 77 64 ...../etc/passwd
Оп! А следующим пакетом клиент возвращает содержимое файла /etc/passwd
. Получается так, что сервер просто говорит клиенту: 0xFB Вообще-то я здесь для того, чтобы увидеть содержимое %filename%
. И клиент послушно выполняет команду.
В этом и есть суть «уязвимости». Нам нужно создать фейковый MySQL-сервер, который бы авторизовывал клиента, отправлял пакет FB с интересующим именем файла и выводил ответ от клиента.
Исходный код такого сервера можешь найти в моем GitHub. Это форк сервера от Gifts (за что ему огромное спасибо), заточенный под современные версии MySQL. В исходнике можно указать путь до файла, который нужно прочитать, и порт фейк-сервера.
Теперь несколько слов о PHP и драйвере для работы с MySQL — mysqlnd. Если используется такой драйвер, а он по умолчанию используется с PHP 5.4, то при чтении файлов можно применять врапперы PHP. И читалка медленно превращается в почти полноценную SSRF.
Ложка дёгтя
Если в приложении для работы с БД используется PDO, то фича, к сожалению, не работает. В нем по умолчанию отключено выполнение LOAD DATA LOCAL INFILE
.
Если приложение написано на Python и в качестве клиента выступает модуль MySQLdb, то чтение файлов также невозможно, если при соединении не указан аргумент local_infile
. Однако при использовании mysql.connector все работает прекрасно.
Стоит также отметить, что все сказанное актуально и для MariaDB, так как протоколы общения в данном случае идентичны.
Все на сегодня. Исследуй эту тему, возможно наткнешься на что-то интересное. Желаю удачных аудитов!