В данном разделе я бы хотел познакомить начинающих (опытные давно в курсе) программистов (или кого еще сюда занесло) с таким важным понятием, в плане безопасности (в том числе и вэб приложений), как SQL инъекции (SQL Injection или SQLi). SQL инъекции становятся доступными тогда когда пользовательский ввод (без экранирования спецсимволов) вставляется напрямую в код SQL запроса, например:
$query="SELECT * FROM users WHERE login='".$_POST['login']."' AND password='". $_POST['password'] ."'"
Это пример в лоб, но наверняка новоиспеченные программисты до сих пор используют подобные запросы для проверки наличия пользователя в БД. Так вот из-за того что данные из запроса попадают сюда без какой либо обработки то мы можем передать определенное значение login для того чтобы результаты по данному запросу нашлись, а следовательно и авторизация успешно прошла. Например, если в качестве логина мы будем использовать значение (а пароль значения не имеет, пусть будет пустым)
admin' -- '
Почему это возможно? Потому что данные интерпретируются дважды, в первый раз данные интерпретируются в PHP и получается готовая строка вида:
SELECT * FROM users WHERE login='admin' -- ' ' AND password=' '
При передаче запроса для БД это готовая строка (наверно это было слишком очевидно) и для БД эта строка интерпретируется не так как задумывал ее автор. Если вы хоть немного знакомы с SQL (например с MySQL), то знаете что все что после " -- " включая эти символы будет интерпретироваться БД как комментарий. Следовательно запрос который будет обрабатывать БД это лишь
SELECT * FROM users WHERE login='admin'
Соответственно пользователя авторизуют как админа, или необходимого пользователя остается только узнать его логин.
Вернемся к самому заданию.
Здесь мы видим форму авторизации и судя по названию самого задания Most basic SQLi pattern ("Самый базовый шаблон SQL инъекций" приблизительно дословно) все будет очень просто.
Попробуем в качестве логина (первое что мне пришло на ум при подготовке скриншотов)
admin' or '
Этого оказалось достаточно, можно только догадываться о примитивности запроса и естественно ни о каком экранировании речи и быть не может.
Получаем заветный флаг! Какой урок мы можем извлечь из данного задания? Нужно всегда экранировать данные пользовательского ввода (для PHP):
если пользователь должен ввести строку (наиболее частый случай) используйте $mysqli->real_escape_string($string);, где $mysqli это объект класса Mysqli (не забудьте установить charset после создания объекта, без этого не работает), а $string это данные пользовательского ввода который должны быть строкой (имя, фамилия, адрес, email... список почти бесконечен).
если пользователь вводит целое число (например выбирает количество комнат в отеле), используйте $integer=intval($integer);, почти аналогично для чисел с двойной точностью $float=floatval($floatval);
That's all, folks!