Вопрос или проблема
У меня есть этот (упрощенный) запрос к базе данных на Perl, и мне интересно, можно ли его как-то использовать в злоумышленных целях. Это часть задания, поэтому я знаю, что можно сделать иначе, задача заключается в том, чтобы эксплуатировать это.
Насколько мне известно, он использует подготовленные выражения и, следовательно, считается достаточно безопасным. Однако я нашел это относительно проблем с quote и param.
if ('POST' eq request_method && param('username') && param('password')){
my $dbh = DBI->connect( "DBI:mysql:database_name","database_name", "<censored>", {'RaiseError' => 1});
my $query="Select * FROM users where username =".$dbh->quote(param('username')) . " and password =".$dbh->quote(param('password'));
my $sth = $dbh->prepare($query);
$sth->execute();
my $ver = $sth->fetch();
if ($ver){
print "win!<br>";
print "here is your result:<br>";
print @$ver;
}
else{
print "fail";
}
$sth->finish();
$dbh->disconnect();
}
Это небезопасно, но я не думаю, что это можно эксплуатировать.
Суть в том, что param
может возвращать скаляр или список. В контексте списка, если вы передадите username=a&username=b
на эту страницу, список будет (“a”, “b”).
В Perl, если вы передаете список в функцию, он интерпретируется как отдельные аргументы.
quote("a", "b")
Это эквивалентно
@list = ("a", "b")
quote(@list)
Таким образом, если вы вызовете quote(param("username"))
, пользователь сможет предоставить второй параметр для quote
, передав два значения username
. Поскольку второй параметр определяет, как выполнять экранирование, это может привести к уязвимости SQL-инъекции.
Однако то, как обрабатывается второй параметр, зависит от драйвера DBD, который различен для каждого типа базы данных. Более того, это должен быть хеш-ссылка, но мы можем передавать только строку. Поэтому может быть так, что существует драйвер, в котором передача чего-то изменяет способ выполнения экранирования, но я не смог его найти. Драйвер Postgres, например, явно указывает: “Второй аргумент для quote должен быть ссылкой на хеш”.
После воспроизведения сайта локально и ознакомления с документацией
Да
Эта программа на Perl уязвима к SQL-инъекции.
- Однако это зависит от драйвера DBI, и я смог воспроизвести это только с MySQL.
Существует 2 недостатка в $dbh->quote(param('paramater'))
-
Видите ли, param чувствителен к контексту. В скалярном контексте, если параметр имеет одно значение
(name=foo)
, он возвращает это значение, а если параметр имеет несколько значений(name=foo&name=bar)
, он возвращаетarrayref
.Согласно ссылке на SO, проблема с непосредственным вызовом
param()
заключается в том, что он может вернутьarray
-
Как особый случай, стандартные числовые типы оптимизированы для возврата
$value
без вызова type_info.Согласно документации DBI. Вызов quote как
list
сSQL_INTEGER
в качестве второго параметра вернет неэкранированное значение.
Поскольку SQL_INTEGER == 4
Все, что потребовалось, – это этот скрипт на Python:
def vuln(url):
params={"username": "valid_username", "password": ["'lol' or 1", 4]}
print(requests.post(url, data=params).text)
В общем, это плохая идея использовать переменные POST напрямую (в конце концов, они контролируются клиентом). Лучше сделать следующее:
- Ограничить размер ввода, чтобы ограничить переполнения.
- Разобрать значение, чтобы убедиться, что оно того типа, которого вы ожидаете.
- Очистить параметр, чтобы он не содержал никаких (неэкранированных) управляющих инструкций; quote делает большинство из этого, но не все (проверьте инструкции для вашей версии Perl)
- Использовать правильные подготовленные выражения (как предлагает @Marc)
В остальном это выглядит как хороший подход, но убедитесь, что вы используете актуальное программное обеспечение и библиотеки. (это относится к любому приложению)
Я знаю, что эта тема довольно старая, но я только сегодня столкнулся с этой задачей, когда играл в уровень 30 вызова Natas в OverTheWire.
Читая ответ от @Ludisposed, я смог решить проблему, как он отметил, что SQL_INTEGER == 4
, я попытался заменить число 4 на другие числа и, похоже, только от 2
до 8
работает.
Мне интересно, почему только числа от 2
до 8
работают в этом случае? Поскольку я пытался поискать, я лишь вижу, что люди упоминают, что SQL_INTEGER это просто целое число, например 1, 2, 3, 4 и т.д.
Я не знаю, не упускаю ли я что-то. Если у кого-то есть ответ, пожалуйста, дайте знать, мне интересно понять это больше. Спасибо.
Ответ или решение
Вопрос, связанный с уязвимостью соединения с базой данных в Perl к SQL-инъекциям, требует внимательного анализа. Статья, на которую вы ссылаетесь, рассматривает несколько аспектов, касающихся использования метода quote
из Perl DBI, и поясняет возможные уязвимости.
Обзор уязвимости
Сначала стоит отметить, что SQL-инъекции представляют собой одну из самых распространенных и опасных уязвимостей для веб-приложений. Они возникают, когда пользователь может несанкционированно манипулировать SQL-запросами, передавая специально сконструированные данные.
Подробный разбор кода
В приведенном вами коде основным местом, где потенциально может возникнуть SQL-инъекция, является конструкция:
$query = "SELECT * FROM users WHERE username =" . $dbh->quote(param('username')) . " AND password =" . $dbh->quote(param('password'));
Здесь метод quote
используется для экранирования пользовательских вводов. Однако важно помнить, что метод param
может возвращать как скалярные значения, так и массивы, что усложняет работу с его результатом.
-
Контекст вызова:
param
может возвращать массив, если передано несколько значений для одного и того же ключа. В случае, если запрос будет выглядеть так:username=foo&username=bar
Метод
param("username")
будет возвращать массив с двумя элементами. Это может привести к следующей проблеме: еслиquote
получает массив с двумя элементами, он может не обрабатывать их должным образом и передавать их в SQL-запрос как отдельные параметры, что может существенно повлиять на безопасность. -
Неправильное использование
quote
: В случае, еслиparam
возвращает массив, это может привести к вызову методаquote
с несколькими аргументами, что может вызвать неожиданное поведение, в зависимости от реализации драйвера базы данных. Это, в свою очередь, может создать уязвимость для SQL-инъекций, особенно если передается неправильный тип данных. -
Специфика драйвера базы данных: Как указывают исследования, конкретные детали реализации метода
quote
могут варьироваться в зависимости от используемого драйвера (например, MySQL или PostgreSQL). В вашем случае, тестировалось с MySQL. Если такое поведение будет несанкционировано и неправильно обработано, это может помочь злоумышленникам создать SQL-инъекции.
Выводы и рекомендации
На основании предоставленного анализа видно, что данный код действительно уязвим к SQL-инъекциям. Чтобы устранить эту уязвимость, рекомендуется следующее:
- Используйте подготовленные выражения (Prepared Statements): Вместо динамической подстановки значений в SQL-запросы, используйте подготовленные выражения, которые позволяют безопасно передавать параметры в запросы.
my $sth = $dbh->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$sth->execute(param('username'), param('password'));
-
Валидация и фильтрация ввода: Применяйте строгую валидацию входных данных. Проверяйте, что пользователь передает ожидаемые типы данных и не содержит незаконных символов.
-
Ограничение параметров: Установите ограничения на допустимые размеры входящих данных, чтобы предотвратить возможные атаки.
-
Обновление библиотек и систем: Регулярно обновляйте используемые библиотеки и системы для обеспечения их безопасности.
Следуя этим рекомендациям, вы сможете существенно повысить безопасность вашего веб-приложения и снизить риск SQL-инъекций.