https://course.secsem.ru/w/index.php?title=%D0%92%D0%B5%D0%B1-%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C/%D0%A3%D1%8F%D0%B7%D0%B2%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8_SQLi&feed=atom&action=historyВеб-безопасность/Уязвимости SQLi - История изменений2024-03-29T12:55:56ZИстория изменений этой страницы в викиMediaWiki 1.32.0https://course.secsem.ru/w/index.php?title=%D0%92%D0%B5%D0%B1-%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C/%D0%A3%D1%8F%D0%B7%D0%B2%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8_SQLi&diff=310&oldid=prevDzeni: /* Ссылки */2020-10-12T12:02:06Z<p><span dir="auto"><span class="autocomment">Ссылки</span></span></p>
<table class="diff diff-contentalign-left" data-mw="interface">
<col class="diff-marker" />
<col class="diff-content" />
<col class="diff-marker" />
<col class="diff-content" />
<tr class="diff-title" lang="ru">
<td colspan="2" style="background-color: #fff; color: #222; text-align: center;">← Предыдущая</td>
<td colspan="2" style="background-color: #fff; color: #222; text-align: center;">Версия 12:02, 12 октября 2020</td>
</tr><tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l49" >Строка 49:</td>
<td colspan="2" class="diff-lineno">Строка 49:</td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div>==Ссылки==</div></td><td class='diff-marker'> </td><td style="background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div>==Ссылки==</div></td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div>* [https://drive.google.com/file/d/1dekLK3xKPO_zQ31W5aZ-FdpGF3pnhvjh/view?usp=sharing презентация "SQL injection"]</div></td><td class='diff-marker'> </td><td style="background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div>* [https://drive.google.com/file/d/1dekLK3xKPO_zQ31W5aZ-FdpGF3pnhvjh/view?usp=sharing презентация "SQL injection"]</div></td></tr>
<tr><td class='diff-marker'>−</td><td style="color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;"><div>* [https://yadi.sk/d/o4cFtxls-WlUAw?w=1 видео "SQL injection"]</div></td><td class='diff-marker'>+</td><td style="color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div>* [https://yadi.sk/d/o4cFtxls-WlUAw<ins class="diffchange diffchange-inline">/%D0%A1%D0%B5%D0%BC%D0%B8%D0%BD%D0%B0%D1%80%203.%2001%20%D0%BE%D0%BA%D1%82%D1%8F%D0%B1%D1%80%D1%8F.mp4</ins>?w=1 видео "SQL injection"]</div></td></tr>
</table>Dzenihttps://course.secsem.ru/w/index.php?title=%D0%92%D0%B5%D0%B1-%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C/%D0%A3%D1%8F%D0%B7%D0%B2%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8_SQLi&diff=302&oldid=prevDzeni: Новая страница: «== SQL Injection == Веб-приложения нередко используют SQL-базы данных, т. е. такие БД, взаимодейств…»2020-10-12T09:55:19Z<p>Новая страница: «== SQL Injection == Веб-приложения нередко используют SQL-базы данных, т. е. такие БД, взаимодейств…»</p>
<p><b>Новая страница</b></p><div>== SQL Injection ==<br />
<br />
Веб-приложения нередко используют SQL-базы данных, т. е. такие БД, взаимодействие с которыми осуществляется через запросы на языке SQL ([https://ru.wikipedia.org/wiki/SQL Википедия]). Поиграться с SQL можно просто в [http://sqlfiddle.postgrespro.ru/#!17//5814 SQL Fiddle] или с помощью [https://linux.die.net/man/1/sqlite3 sqlite3].<br />
<br />
Веб-приложение формирует и отправляет базе SQL-запросы, которые могут зависеть от параметров, пришедших от клиента (взятых из HTTP-запроса). SQL-запросы текстовые и простой способ сделать запрос, зависящий от параметра - просто подставить этот параметр в текст запроса. Пример кода (на языке [https://www.python.org/ Python]), подставляющего параметр из HTTP-запроса в SQL-запрос к базе:<br />
[[Файл:Sqli-code.png|650px]]<br />
<br clear=all> <br />
Здесь "request.args.get('login')" считывает значение параметра "login" query string запроса. Вот какой вид примет запрос, если параметр login будет иметь значение "testuser".<br />
<br />
[[File:Sql-req.png|650px]]<br />
<br clear=all> <br />
Значение "testuser" подставилось в строковый литерал в одинарных кавычках. Говорят, что значение попало в контекст строкового литерала. После такой подстановки может оказаться, что смысл SQL-запроса изменился и данные, подставленые в запрос стали не просто значением параметра запроса (скажем, числовым или стокововым литералом), а какими-то еще конструкциями языка SQL. Вот какой запрос будет сформирован, если параметр "login" будет иметь значение <code>' or 1=1 --</code>:<br />
[[File:Sqli-annot.png|650px]]<br />
<br clear=all><br />
В результате выражение в <code>WHERE</code>-части приняло вид <code>login=<nowiki>''</nowiki> or 1=1</code>, оно будет истинным для любой строки таблицы, в результате чего в ответ на этот запрос будут возвращены все строки таблицы. Так же можно изменить смысл запроса и передав специфическое значение параметра id для этого кода (для этого даже не придётся добавлять в значение параметра кавычку):<br />
[[File:Sql-id.png|650px]]<br />
<br clear=all><br />
Возможность передать такое значение параметра HTTP-запроса, которое подставится в текст SQL-запроса и при подстановке изменит смысл этого запроса называется '''SQL injection''' ([https://www.owasp.org/index.php/SQL_Injection OWASP]). Хорошие статьи про SQL injection можно найти [https://rdot.org/forum/showthread.php?s=f4292383c9896b773bd98d827eb9411f&t=124 тут] и еще [http://pentestmonkey.net/cheat-sheet/sql-injection/mysql-sql-injection-cheat-sheet тут].<br />
<br />
Фактически, SQLi это один из представителей класса уязвимостей injection, которые получаются, если в какие-то управляющие команды подставляются параметры-данные от пользователя и эти данные могут выйти из контекста, куда подставляются, перестав быть просто данными, и поменять смысл команды.<br />
<br />
== Полезные трюки ==<br />
<br />
=== SQL-комментарий ===<br />
Супер простой трюк. В конце своей инъекции можно добавить символы однострочного SQL-комментария, тогда всё что идет дальше (как правило, остаток исходного SQL-запроса) будет считаться комментарием и проигнорируется. В разных БД символы комментария могут разными, см. документацию, чаще всего это двойной минус <code>--</code> и/или решетка <code>#</code>, также бывает классический двойной слеш <code>//</code>. Фактически, этот трюк уже был использован в примере в предыдущем разделе, двойной минус в конце там заставил базу данных проигнорировать оставшуюся от исходного запроса одинарную кавычку в конце.<br />
<br />
=== UNION SELECT ===<br />
Очень полезным может быть <code>UNION</code> ([https://ru.wikipedia.org/wiki/Union_(SQL) вики],[https://www.postgresql.org/docs/current/typeconv-union-case.html доки postgres]), который позволяет присоединить к результату одного <code>SELECT</code> запроса результат еще одного. Если инъекция в операторе <code>SELECT</code> (что в реальности довольно часто), с помощью <code>UNION</code> можно добавить в его результат вывод нового, полностью написанного атакующим <code>SELECT</code> - запроса из другой таблицы, с другими колонками, условиями и т. д.<br />
Чтобы <code>UNION</code> сработал, требуется чтобы количество и типы колонок совпадали. Помочь с этим может то что в SQL можно писать на месте колонок просто константы (и тогда они просто проставятся на эти места в каждой из строк результата запроса) и то что чаще всего можно использовать вместо любого значения <code>NULL</code>. Если количество колонок в исходном запросе неизвестно, можно просто подбирать его.<br />
<br />
Еще при использовании <code>UNION</code> бывает проблема что код приложения отдаёт не все строки результата, а только часть, например только первую (так обычно бывает когда разработчик предполагал что запрос вернет только одну строку). В этом случае можно просто сделать невыполнимое условие в <code>WHERE</code> исходного запроса - тогда он вернёт 0 строк и в выдачу попадёт строка из присоединенного <code>SELECT</code>'а.<br />
<br />
=== Метаданные ===<br />
Как правило, при эксплуатации SQL injection мешает то что неизвестны имена таблиц, имена и типы колонок. Может помочь то что в современных базах данных как правило есть специальные таблицы с метаданными, которые содержат всю схему данных базы. У разных СУБД эти таблицы называются и устроены по-разному, про их устройство можно почитать в документации. Например, у MySQL таблицы с метаданными хранятся в базе <code>INFORMATION_SCHEMA</code>([https://dev.mysql.com/doc/refman/8.0/en/information-schema.html документация]) - информация о существующих базах данных лежит в таблице <code>SCHEMATA</code>, о таблицах - в <code>TABLES</code>, о колонках - в <code>COLUMNS</code> и так далее.<br />
<br />
=== Fingerprinting ===<br />
Для атаки бывает необходимо понимать, какая именно СУБД используется - чтобы понимать, как называются таблицы с метаданными какие функции/механизмы/особенности БД можно использовать и т. д. Если из других источников (скажем, доступных исходных кодов приложения или вывода приложением подробной информации о себе) узнать, какая СУБД, не удаётся, это можно узнать от неё самой. Во-первых, если выдаются SQL-ошибки, часто по виду ошибки, который обычно содержит числовой идентификатор, можно однозначно определить базу. Если даже ошибки не выводятся, зафингерпринтить базу можно по её реакции на конструкции SQL, которые работают только в некоторых базах или работают в разных БД по-разному. Яркими примерами является то что в MySQL можно в качестве строковой колонки указать переменную <code>@@version</code> и функцию <code>version()</code> и на её место в обоих случаях подставится версия базы, в PostgreSQL и SQLite оператор <code>||</code> работает как оператор конкатенации строк, при этом в PostgreSQL поддерживается база <code>information_schema</code>, а в SQLite - нет. Почитать про database fingerprinting можно на [https://www.owasp.org/index.php/OWASP_Backend_Security_Project_DBMS_Fingerprint OWASP], [https://null-byte.wonderhowto.com/how-to/sql-injection-101-fingerprint-databases-perform-general-reconnaissance-for-more-successful-attack-0184562/ здесь], и еще много где, это довольно легко гуглится.<br />
<br />
=== Error-based вывод ===<br />
В случае, если мы в ответ сервера не выводятся данные, полученные из SQL-запроса, но выводятся ошибки, вытаскивать информацию бывает всё равно можно - через сообщения об ошибке. Про это написано в [https://intsystem.org/security/error-based-sql-injection-in-mysql/ этой статье], [https://xakep.ru/2010/05/27/52222/ ещё этой], [https://habr.com/ru/post/235287/ этой] и еще много где.<br />
Кстати, если даже не выводится ошибка, а есть только бинарный признак (приложение даёт в зависимости от полученных из SQL-запроса данных ответ да/нет) и даже если нет и его вытягивать данные всё равно можно. Как именно - задача на подумать или нагуглить -).<br />
<br />
=== Stacked queries ===<br />
Иногда бывает можно в одном запросе от приложения к базе данных передать несколько SQL-запросов через <code>;</code>. В этом случае SQL injection позволяет сделать уже вообще любой SQL-запрос. Доступность stacked queries зависит от БД и используемого драйвера, к примеру c базами sqlite3 и MySQL обычно нельзя, с базой PostgreSQL и стандартным питоновским драйвером '''psycopg2''' - можно.<br />
<br />
=== Изменения в БД ===<br />
Часто клиентские библиотеки для баз данных (драйверы) при выполнении SQL-запроса автоматически начинают новую транзакцию (выполняя оператор BEGIN). Любые изменения, сделанные SQL-запросом в транзакции, не будут видны за пределами текущей транзакции, пока она не будет успешно завершена (оператором <code>COMMIT</code>). Однако, если программист делал запрос, достающий что-то из базы, то есть <code>SELECT</code>, ему не нужно применять никакие изменения и вполне возможно он не делал COMMIT после своего запроса - поэтому, даже если вставить какой то изменяющий базу запрос после <code>SELECT</code>'а, он не повлияет на ее состояние, так как не будет подтверждена транзакция. (Про то как это работает в [http://initd.org/psycopg/docs/connection.html#connection.commit доках psycopg2], см. про функции commit и close). Однако, эту проблему можно (по крайней мере в некоторых случаях) победить - если работают stacked queries, можно просто вставить после своего запроса оператор <code>COMMIT</code>, и это приведет к подтверждению транзакции (<code>SELECT ... WHERE ... ; INSERT INTO ... ; COMMIT; --</code>).<br />
<br />
==Ссылки==<br />
* [https://drive.google.com/file/d/1dekLK3xKPO_zQ31W5aZ-FdpGF3pnhvjh/view?usp=sharing презентация "SQL injection"]<br />
* [https://yadi.sk/d/o4cFtxls-WlUAw?w=1 видео "SQL injection"]</div>Dzeni