SQL-инъекции. Разбираем на пальцах одну из самых популярных хакерских техник

Содержание статьи

  • Что такое SQL?
  • Как выглядит запрос к приложению
  • Проход в админку
  • UNION-based SQLi
  • Определяем число столбцов в таблице
  • Узнаём версию БД
  • Выводим список баз данных
  • Выводим список таблиц в базе данных
  • Выводим список столбцов в таблице
  • Выводим информацию из нужных столбцов
  • Sqlmap
  • SQLi в дикой природе
  • Где практиковаться?

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

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

От редакции

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

 

Что такое SQL?

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

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

  • Ва­сёк вво­дит в стро­ку поис­ка сло­во «ган­тели» и нажима­ет «Искать».
  • Ког­да кноп­ка нажата, бра­узер зап­рашива­ет стра­ницу поис­ка и переда­ет на сер­вер стро­ку «ган­тели».
  • Ра­бота­ющее на сер­вере веб‑при­ложе­ние, что­бы поис­кать в базе дан­ных, фор­миру­ет зап­рос к ней. Для это­го в при­ложе­ние прог­раммис­том заложе­на коман­да на SQL, зада­ющая усло­вия поис­ка. В нее и под­став­ляет­ся получен­ное от поль­зовате­ля сло­во «ган­тели».
  • По­лучив зап­рос, база дан­ных ищет под­ходящие под усло­вия стро­ки в таб­лице товаров и выда­ет их веб‑при­ложе­нию.
  • Веб‑при­ложе­ние объ­еди­няет резуль­тат поис­ка с шаб­лоном стра­ницы и выда­ет резуль­тат бра­узе­ру Вась­ка. Васёк видит кра­сивую стра­нич­ку с изоб­ражени­ем ган­телей, ценами и про­чей инфой.
  • Что же такое база дан­ных? Так час­то называ­ют, во‑пер­вых, саму прог­рамму, работа­ющую на сер­вере (точ­нее, она называ­ется СУБД — сис­тема управле­ния базами дан­ных), а во‑вто­рых, файл или нес­коль­ко фай­лов, в которых в спе­циаль­ном фор­мате хра­нят­ся дан­ные.

    Дан­ные записы­вают­ся в виде таб­лиц, а в таб­лицах — стол­бцы и стро­ки, как в докумен­те 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#%') orord(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

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

    Источник: xakep.ru

    Ответить

    Ваш адрес email не будет опубликован. Обязательные поля помечены *