http://root-access.info/index.php?showtopic=1785&pid=9149&st=0&#entry9149 http://root-access.info/index.php?showtopic=2804&mode=linear
http://root-access.info/index.php?showtopic=1785&pid=9149&st=0&#entry9149 [quote name='Jinn' date='Apr 26 2007, 02:37 PM' post='9146'] ==================================================== Организация работы php скрипта через socks5 сервер ==================================================== ===== Вступление ===== Я думаю у многих возникало желание организовать работу php скрипта через socks, этим повысить безопасность, обойти ограничения (не буду уточнять какие;-) У меня тоже возникала подобная проблема, поэтому я попробовал ее решить. Порыл в рунете, ничего подобного не нашел ;-0. Это нужно исправлять! В данной статье я попытаюсь изложить методы работы php-скрипта через socks5-сервер. Спецификация протокола socks5 дана в RFC1928. Все что здесь написано, тестировалось мной на домашнем компе: * Win XP SP2 * Frenzy-lite 1.0 установленая на VMware 4.5.1 =) * Opera 8.52 * Денвер Для организации socks5 сервера использовалась тулза boucer, которая меня устраивает по всем параметрам: win : bouncer-1.0.rc6-win32 frenzy : bouncer-1.0.rc6-freebsd-intel Для понимания статьи вы должны иметь начальное знание пхп. ===== Описание протокола ===== Те кто знаком с протоколом могут не читать данное описание,оно является перессказом RFC. Координальным отличием от протоколов HTTP, POP3, SMTP является что данные socks-серверу должны передаваться в бинарном виде,а не в текстовом. Т.е строка типа GET <!--HideBegin--><div class='hidetop'></div><div class='hidemain'><!--HideEBegin-->http://www.lamer.ru/r57shell.php<!--HideEnd--></div><!--HideEEnd--> HTTP/1.0 как в HTTP не покатит :-( Для работы через сокс для начала нам нужно к нему подключиться(верх логики=)) После чего отправить пакет: Code: * VER версия протокола,для socks5 == 05, socks4 == 04 * NMETHODS содержит число октетов в идентификаторах методов авторизации * METHODS метод авторизации(см.дальше) На что сокс должен вам ответить: Code: * VER версия протокола,для socks5 == 05 * METHODS метод авторизации: * 00 аутентификация не требуется * 01 GSS-API (см. RFC1961) * 02 USERNAME/PASSWORD (см. RFC1929) * 03 до 7F зарезервировано IANA * 80 до FE предназначено для частных методов * FF нет применимых методов Если в поле метода выставлено FF, то соединение должно быть разорвано, ни один из методов не удовлетворяет сервер (вот такой он привередливый=)). Т.к сокс отправляет свою версию, это можно использовать в чекерах проксей, на определение соксовых. Далее следуют запросы к серверу. Они бывают трех видов: * Connect * Bind * Udp associate Мы рассмотрим Connect, как требующийся наиболее часто. ---- Запросы && Ответы ---- ЛЮБОЙ запрос должен состоять из данных полей: Code: * VER версия протокола: 05 * CMD * 01 CONNECT - он нам и нужен * 02 BIND * 03 UDP ASSOCIATE * RSV зарезервировано * ATYP тип адреса, следующего вида: * 01 IP v4 адрес * 03 имя домена, мы будем использовать именно этот метод, т.к это более универсально, можно передавать и IPv4 (как выяснилось) и имя домена. При передаче пакета с полем ATYP равном 01 у меня возникали проблемы. * 04 IP v6 адрес * DST.ADDR требуемый адрес * DST.PORT требуемый порт (в сетевом порядке октетов) Значения зарезервированных (RSV) полей должны быть установлены в 00. На что socks-сервер должен бут ответить: Code: * VER версия протокола: 05 * REP код ответа: * 00 успешный * 01 ошибка SOCKS-сервера * 02 соединение запрещено набором правил * 03 сеть недоступна * 04 хост недоступен * 05 отказ в соединении * 06 истечение TTL * 07 команда не поддерживается * 08 тип адреса не поддерживается * 09 - FF не определены * RSV зарезервирован * ATYP тип последующего адреса * 01 IP v4 адрес * 03 имя домена * 04 IP v6 адрес * BND.ADDR выданный сервером адрес * BND.PORT выданный сервером порт (в сетевом порядке октетов) При получении ответа с сообщением об удаче, клиент может начинать передавать данные. Если выбраная схема аутентификации требует особое формирование пакетов, то данные должны инкапсулироваться в пакет, формат которого определяется данным методом аутентификации. Socks-сервер, тоже, должен инкапсулировать данные для клиента согласно согласно тому, как этого требует выбранная схема аутентификации. ===== Кодим ===== Я ставил перед собой задачу не написать какой-то полезный всем скрипт, а просто рассмотреть работу с соксом, без авторизации (пока) Вот алгоритм нашего скрипта: * подключиемся к соксу * проверяем версию сокса (05 в нашем случае) и метод авторизации(без нее - 00) * отправляем пакет для коннекта, я пробовал сначала все на локалхосте, после на www.yandex.ru * проверяем поле REP,что все прошло успешно.... * начинаем передавать данные..произвольные.Т.к в этой статье мы рассматриваем схему без аутентификации, то мы будем передавать данные в том формате, в котором они должны доставлены нашему HTTP-серверу. * получаем ответ сервера (www.yandex.ru) * закрываем соединение Как сказал наш первый космонавт.....Поехали ;-) Т.к данные соксу должны передаваться в бинарном виде, то напишем небольшую функцию, котороя берет по 2 символа и переводит из hex'а в бинарный вид.Hex можно передавать с пробелами, они вырезаются: Code: <? function hex2bin($dump) { $dump=str_replace(' ', '', $dump); $res=''; for ($i=0; $i<=strlen($dump); $i+=2) { $bt=$dump[$i].$dump[$i+1]; $res=$res.chr(hexdec($bt)); } return $res; } ?> И функцию для перевода в hex. Для удобства я делаю с пробелами,но они все равно вырезаются при переводе в бинарный режим, но с ними приятнее глазу и удобней при отладке. Code: <? function hex($dump) { $res=''; for ($i=0; $i<strlen($dump); $i++) { if ($i+1==strlen($dump)){$res=$res.dechex(ord($dump[$i]));} else {$res=$res.dechex(ord($dump[$i]))." ";} } return $res; } ?> Мы должны передовать длину хоста, которому мы в последствии будем передавать данные, в шестнадцатиричной системе счисления. Переводить в шестнадцатиричный режим мы будем с помощью dechex(). Но как известно число 4 в десятиричной - 4 в шестнадцатиричной, но нам то нужно чтобы оно было 04, а dechex возвращает 4. Поэтому я написал небольшую функцию для исправления таких случаев: Code: <? function len_test($dump) { if (strlen($dump)==1) {$dump="0".$dump;} return $dump; } ?> Она проверяет длину переменной на кол-во символов, если она равно 1, то добавляется впереди 0. В принципе можно было не выносить это в функцию, но при постоянных экспериментах она сильно пригодится. Хотя может кто знает как вернуть 04 Далее будет идти код с подробными коментариями: Code: <? error_reporting(1); echo '<meta http-equiv="Content-Language" content="ru"><meta http-equiv=Content-Type content="text/html; charset=windows-1251">'; $ip="192.168.177.1"; // адрес scoks 5 прокси $port=1080; // порт сокса $connect_host='192.168.177.1'; // адрес или ip которому мы будем посылать данные через сокс-сервер =) $connect_port=80; // порт $socks=fsockopen($ip,$port); if ($socks) { $h=hex2bin('05 01 00'); // передаем версию сокса , кол-во методов аутентификации и методы аутентификации fwrite($socks,$h); $list=bin2hex(fread($socks,2));// переводим ответ сокса в нормальный hex'овый вид // проверяем на версию и метод аутентификации, я не стал заморачивться,но можно сделать проверку без перевода в hex а примерно вот так: /* пример проверки на соотвествие версии и методу авт-ции. $bb=fread($socks,2); if ( $bb==hex2bin( '0500' ) ) { ....................... } вариантов много :) */ // проверяем метод аутентификации if ($list == '0500') { $list=""; $len=dechex(strlen($connect_host)); // длина адреса,переводится в шестнадцатиричный режим $len=len_test($len); $h=hex2bin("05 01 00 03 $len ".hex($connect_host)." 00 ".dechex($connect_port).""); //формируем запрос ,в принципе нужно было бы и длину портав шестнадцатиричной сисетме счисления проверить, но это нужно только от 1 до 9 порта, но на таких я думаю поднимать не будут сокс :-) fwrite($socks,$h); $l=bin2hex(fread($socks,1024)); if ($l[3] == '0') { // формируем запрос ,который будет отправлен, через сокс, серверу ( в нашем случае http - www.yandex.ru) $head = "GET / HTTP/1.0\r\n"; $head .= "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.52\r\n"; $head .= "Host: www.yandex.ru\r\n"; $head .= "Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1\r\n"; $head .= "Accept-Language: ru,en;q=0.9\r\n"; $head .= "Accept-Charset: windows-1252, utf-8, utf-16, iso-8859-1;q=0.6, *;q=0.1\r\n"; $head .= "Proxy-Connection: close\r\n\r\n"; fwrite($socks,$head); // считываем ответ HTTP-сервера через SOCKS-сервер до конца =) while(!feof($socks)) { $l=fread($socks,1024); echo $l; } } // определяем ошибку! данные коды ответа взяты из RFC elseif ($l[3] == '1') {echo "<center><font color=red>Ошибка: SOCKS-сервера</font></center>";} elseif ($l[3] == '2') {echo "<center><font color=red>Ошибка: соединение запрещено набором правил</font></center>";} elseif ($l[3] == '3') {echo "<center><font color=red>Ошибка: сеть недоступна</font></center>";} elseif ($l[3] == '4') {echo "<center><font color=red>Ошибка: хост недоступен</font></center>";} elseif ($l[3] == '5') {echo "<center><font color=red>Ошибка: отказ в соединении</font></center>";} elseif ($l[3] == '6') {echo "<center><font color=red>Ошибка: истечение TTL</font></center>";} elseif ($l[3] == '7') {echo "<center><font color=red>Ошибка: команда не поддерживается</font></center>";} elseif ($l[3] == '8') {echo "<center><font color=red>Ошибка: тип адреса не поддерживается</font></center>";} else {echo "<center><font color=red>Ошибка: не определено!</font></center>";} } else { echo "<center><font color=red>Ошибка:возможно это не socks5 или он не поддерживает метод без аутентификации(!</font></center>"; } fclose($socks); } else { echo "<center><font color=red>SOCKS сервер недоступен!</font></center>"; } echo "<br><br><center><font size=1 color='#292929'>c0ded by <b>Jinn</b> (#304227033)</font></center>"; //автора не забудьте указать =) ?> В данном примере ответ HTTP-сервера (www.yandex.ru) выводится в браузер и не обрабатывается им. При желании написать разделение ответа и контента не сложно, нужно разделять по конструкции:"\r\n\r\n". После чего отправлять браузеру ответ HTTP сервера с помощью header(). Но все это я не стал делать, потому что данный скрипт написан только для демострации работы с соксами. И делать это смысла нет, потому что мало кто будет использовать этот скрипт(если вообще кто бут=)) все зависит от данных которые вы обрабатывате Возможное расширения скрипта: - работа не только с 5 версией, но и с 4, но для этого нужно изучить этот протокол. RFC я думаю рулит=) - работа не только без авторизации, но и с поддержкой ее, для этого нужно изучить эти методы, они описаны в RFC. Надеюсь в скором времени я этим займусь=) напишу статью, если все получится;-) ===== Заключение ===== Нельзя сказать что мы изучили протокол, до этого еще долго. Но во всяком случае теперь мы(я точно) представляю себе как идет взаимодействие с соксами. Надеюсь, что не один я узнал что-то новое, кому-то может даже помог З.Ы. Думаю что скрипт не идеален, приму любые замечания Вообще можно попробовать написать скрипт без dechex() и передавать данные не в виде 05, а в виде 5.Но мне так пробовать не захотелось [ Jinn | 304227033 | ZaeB.uS | _http://zaeb.us ][/quote] _________________________________ а вторая ссылка здохла