Прокси-сервер является промежуточным звеном при общении двух узлов в сети. Обычно прокси используют для контроля исходящего и входящего трафика, скрытия белого адреса узла или получения доступа к закрытому сегменту сети.
Понятия Proxy-сервера и VPN-сервера часто путают. Их отличие заключается в предназначении. Если не учитывать дополнительные инструменты и надстройки при создании соединений:
Proxy используется для манипуляций над сетевым трафиком приложений;
с помощью VPN создается полноценная виртуальная сеть.
Ключевое различие между ними состоит в том, что соединение с помощью прокси-сервера является соединением L4, в то время как VPN заворачивает весь трафик на уровнях L2 и L3.
Чаще всего для проксирования трафика используются два протокола: SOCKS и HTTP. Для их реализации существует множество инструментов, а благодаря спецификациям их легко интегрировать с различными системами. Оба протокола имеют свои плюсы и минусы, поэтому для понимания принципов проксирования мы подробно разберем их работу. Хотя в статье мы местами рассматриваем прокси с точки зрения пентестера, она будет полезна всем, кто хочет разобраться в этой теме.
Теория
Вкратце о HTTP-прокси
HTTP CONNECT
Для реализации HTTP-прокси был разработан метод CONNECT. Он позволяет передавать данные через прокси-сервер с поддержкой сквозного шифрования между клиентом и ресурсом.
Принцип работы метода прост. На запрос такого типа:
CONNECT www.google.com:443 HTTP/1.1
Прокси-сервер ответит статусом 2xx (если сервер поддерживает CONNECT-метод) и перейдет на шифрованный канал.
Также метод CONNECT поддерживает авторизацию, с помощью специального заголовка:
CONNECT www.google.com:443 HTTP/1.1
Host:
www.google.com:443
Proxy-Authorization: basic aGVsbG86d29ybGQ=
Более подробно HTTP CONNECT метод описан в RFC 7231.
HTTP RELAY
Помимо метода CONNECT, можно использовать HTTP-прокси типа RELAY.
В отличие от CONNECT, который создает виртуальное соединение и прозрачно перенаправляет пакеты, RELAY-прокси подменяет отправителя и получателя сообщений на себя, выступая посредником между клиентом и ресурсом (аналогия с механизмом NAT). В этом случае сквозное шифрование не работает, а поддержка UDP отсутствует. Из-за этих недостатков RELAY-прокси не пользуются популярностью и встречаются реже.
Пример запроса к прокси-серверу в bash-консоли:
nc
proxy.com
8888
GET
http://google.com
HTTP 1.1
Host:
google.com
Получив запрос, прокси-сервер создаст новый GET-запрос от своего имени и отправит его на http://google.com.
Вкратце о SOCKS-прокси
Основное отличие SOCKS-протокола от HTTP заключается в том, что SOCKS — это бинарный протокол, работающий на пятом уровне модели OSI. Сам протокол не имеет шифрования, поэтому данные передаются в открытом виде.
Для старта процесса аутентификации клиент отправляет следующую последовательность байт:
05 02 00 02
Где:
05 — версия SOCKS,
02 — количество предложенных методов аутентификации,
00 и 02 — методы аутентификации.
Обозначения для методов аутентификации:
00 — АУТЕНТИФИКАЦИЯ НЕ ТРЕБУЕТСЯ,
01 — GSSAPI,
02 — ИМЯ ПОЛЬЗОВАТЕЛЯ/ПАРОЛЬ,
03 — 7F НАЗНАЧЕНО IANA,
80 — FE ЗАРЕЗЕРВИРОВАНЫ ДЛЯ ЧАСТНЫХ МЕТОДОВ,
FF — НЕТ ПРИЕМЛЕМЫХ МЕТОДОВ, СОЕДИНЕНИЕ ЗАКРЫВАЕТСЯ.
В ответ на запрос аутентификации сервер отправляет последовательность следующего типа:
05 02
Где:
05 — версия SOCKS,
02 — выбранный вариант аутентификации (имя и пароль).
После успешной аутентификации запросы клиента будут выглядеть следующим образом:
05 01 00 01 7f 00 00 01 00 16
Где:
05 — версия SOCKS,
01 — команда (есть 01 CONNECT, 02 BIND и 03 UDP ASSOCIATE),
00 — резерв,
01 — тип адреса (есть 01 IPv4, 02 DNS, 03 IPv6),
7f 00 00 01 — адрес назначения (127.0.0.1),
16 — порт назначения (22 порт).
SOCKS-протокол в пятой версии (SOCKS5) получил поддержку UDP через команду UDP ASSOCIATE в запросе клиента, которая переключает протокол с TCP на UDP. Получив такую команду, сервер открывает второй порт для общения по UDP и переходит на него. То есть обмен данными по UDP может начаться только после установки TCP-соединения.
Ответы сервера будут выглядеть следующим образом:
05 00 00 01 ac 10 00 02 82 fe
Где:
05 — версия SOCKS,
00 — ответ (00 успешно, 01 сбор сервера, 02 не разрешено ACL, 03 сеть недоступна, 04 хост недоступен, 05 соединение отклонено, 06 истек TTL, 07 команда не поддерживается, 08 типа адреса не поддерживается),
00 — резерв,
01 — тип следующего адреса (01 IPv4, 02 DNS, 03 IPv6),
ac 10 00 02 — адрес назначения (172.16.0.2),
82 fe — порт назначения (33406 порт).
Для более подробного изучения работы протокола SOCKS можно обратиться к следующим спецификациям:
SOCKS или HTTP?
SOCKS5 — протокол, специально разработанный для передачи данных через прокси-сервер. Он работает на уровне L5, что позволяет передавать информацию всех вышестоящих протоколов. Помогает установить стабильное соединение и поддерживает передачу данных по UDP.
HTTP — протокол, который поддерживает проксирование через метод CONNECT. Он также работает с множеством других протоколов и проксирует их, но все же менее гибок, чем SOCKS, и имеет ряд ограничений на передачу данных.
Для работы с прокси предпочтительнее использовать SOCKS-протокол, поэтому большинство средств для проксирования поддерживают его.
Поднимаем прокси
Теперь немного поговорим о способах поднятия своего прокси-сервера. В нашем случае мы будем рассматривать способы поднятия SOCKS5-прокси по причинам, описанным в предыдущем разделе.
Для поднятия прокси можно использовать, например, эти протоколы:
SSH (22 TCP-порт),
HTTP(S) (80 и 443 TCP-порты),
MSRPC (135 и 445 TCP-порты).
Они часто встречаются на практике, и для каждого из них есть довольно простой способ поднятия прокси-сервера. При анализе защищенности периодически возникает потребность в получении доступа к закрытым сегментам сети через промежуточные узлы. Для решения таких задач было написано много инструментов, в том числе и тех, что позволяют поднять свой прокси-сервер на промежуточном хосте и получить стабильное соединение.
SSH
Пожалуй, одно из самых популярных средств для удаленного администрирования Linux-систем. Удобство этой утилиты в том, что помимо удаленного доступа она также обладает богатой функциональностью для пивотинга — в том числе встроенными возможностями проброса портов (или port forwarding). Флаг -D, который позволяет сделать динамический проброс, также поднимает SOCKS-прокси, о чем написано на странице man.
Specifies a local “dynamic” application-level port forwarding. This works by allocating a socket to listen to port on the local side, optionally bound to the specified
bind_address. Whenever a connection is made to this port, the connection is forwarded over the secure channel, and the application protocol is then used to determine where
to connect to from the remote machine.Currently the SOCKS4 and SOCKS5 protocols are supported, and ssh will act as a SOCKS server. Only root can forward privileged ports.
Dynamic port forwardings can also be specified in the configuration file.
Отметим, что для использования несистемных портов в диапазоне 1024-65535 root-права не нужны. Команда для поднятия SOCKS-прокси приведена ниже:
client> ssh -D 4444 user@proxy
После выполнения команды на клиентской машине (localhost) будет поднят SOCKS5 на порту 4444. Схема соединения будет выглядеть примерно так:

HTTP(S)
Как уже говорилось выше, для реализации HTTP-прокси можно воспользоваться методом CONNECT. Например, популярный инструмент Burp Suite по умолчанию применяет HTTP-прокси с этим методом.
Однако, используя протокол HTTP, можно поднять и SOCKS-прокси. Инструмент chisel позволяет реализовать это, переводя соединение на канал для передачи данных WebSockets.
server> chisel server --socks5 --port 54545
client> chisel client 192.168.56.104:5465 50080:socks
При этом поднимается локальный порт, соединение HTTP переходит на WebSockets, и данные передаются через него. Выглядит это следующим образом:

По словам одного из авторов этого инструмента, переход на WebSockets нужен для ускорения работы инструмента, так как работа по HTTP будет требовать отправки большого количества запросов. При работе с WebSockets стоит помнить, что в некоторых ситуациях этот протокол может блокироваться файрволом, из-за чего установить соединение через прокси не получится.
MSRPC (RPC + SMB)
В Windows часто используется SMB на 445 порту. Этот протокол тоже можно использовать для поднятия SOCKS-прокси. Для этой цели подойдет rpc2socks.
Инструмент записывает исполняемый файл в папку $ADMIN
, поэтому для работы требуется учетная запись с правами администратора на прокси-сервере. На стороне клиента достаточно подключиться к диску SMB и установить SOCKS-сервер:
client> rpc2socks BLOCKCHAINDOMAIN/Carlos@10.1.2.3 #подключение к серверу по SMB
client> inst #установка SOCKS сервера
Используем прокси
После того, как прокси поднят, настроен и готов к работе, следующим этапом будет проксирование — перенаправление трафика с клиентского узла на прокси-сервер.
Способы проксирования можно разделить на три типа:
Клиентский — приложение само поддерживает прокси.
Проксификация — перехват библиотечных вызовов.
Прозрачный прокси — перенаправление трафика без изменений в приложениях.
Клиентский
Здесь все просто. Приложение предоставляет возможность по заворачиванию своего трафика в прокси. Если мы говорим про веб-трафик, то здесь можно воспользоваться плагином Foxy Proxy. Клиентские прокси часто поддерживают описанные выше SOCKS и HTTP.
Проксификация
Проксификация — способ проксирования приложения, при котором происходит перехват библиотечных вызовов и перенаправление на прокси. Один из популярных инструментов для проксификации на Linux — proxychains.
Позволяет настроить прокси SOCKS4/5 и HTTP. Для начала нужно открыть конфиг /etc/proxychains4.conf
и добавить список прокси-серверов
[ProxyList]
# add proxy here ...
# meanwile
# defaults set to "tor"
socks4 127.0.0.1 9050
http 127.0.0.1 8080
socks5 127.0.0.1 8989
После этого можно проксифицировать нужное приложение с помощью proxychains4
client> proxychains4 -q curl ipinfo.io
При использовании proxychains4 стоит помнить, что утилита использует LD_PRELOAD trick, смысл которого в перехвате C-вызовов. Если приложение статически скомпилированное или вызывает свои библиотеки (например Go приложения), то proxychains4 работать не будет.
В таких случаях можно воспользоваться другими утилитами, например, graftcp или использовать прозрачный (transparentproxy) прокси.
Прозрачный прокси
Проксирование без вмешательства в работу приложений. Для такого вида прокси может использоваться отдельный сервер или пересылка пакетов на уровне ядра.
Настроить прозрачное проксирование с поддержкой SOCKS-прокси можно с помощью redsocks. Для его настройки прописываем в /etc/redsocks.conf
следующее:
local_ip = 127.0.0.1; — локальный адрес, который прослушивается,
local_port = 12345; — локальный порт,
ip = 127.0.0.1; — адрес, куда перенаправлять,
port = 3128; — порт, куда перенаправлять.
Принимая на локальный порт 12345, redsocks будет перенаправлять соединения на указанный прокси. При этом заворачивать весь трафик на SOCKS-порт можно с помощью iptables.
Пример настройки iptables, где 192.15.2.0/8 внутренняя сеть, которую надо перенаправлять на порт redsocks 12345
sudo iptables -t nat -A OUTPUT -p tcp -d 192.15.2.0/8 -j REDIRECT --to-ports 12345
redsocks -c /etc/redsocks.conf
Ротация
C прокси-серверами можно провернуть один очень удобный трюк под названием «Ротация». Суть его в том, что для каждого пакета по отдельности будет выбираться прокси из списка (случайным образом или по порядку). Это может быть полезно в случаях, когда необходимо отправить множество запросов к одному или нескольким узлам, и при этом не получить блокировку IP-адреса. Часто такие ситуации возникают в проектах red team или при задачах веб-парсинга.
С ротацией IP-адреса подойдет инструмент mubeng.
Для утилиты нужно создать текстовый файл со списком используемых прокси, выглядеть это должно примерно так:
http://127.0.0.1:8080
https://127.0.0.1:443
socks4://127.0.0.1:4145
socks5://127.0.0.1:2121
Простой старт утилиты командой:
mubeng [-c|-a :8080] -f proxies.txt
Флаг -a
указывает на порт, который будет прослушивать mubeng
и перенаправлять на случайную прокси из списка.
Про использование этого инструмента для тестирования веб-приложений можно почитать в статье — https://habr.com/ru/articles/796969/.
Проблемные места
В конце статьи хотелось бы рассказать про ситуации, в которых работа с прокси может быть затрудненной. Обычно, к таким случаям относится работа с UDP-протоколом и проксирование сырых сокетов.
Проксирование UDP
SOCKS5 и HTTP-протоколы, которые чаще всего встречаются в задачах пивотинга, не умеют работать с чистым UDP (даже для SOCKS5 требуется сначала реализовать TCP-соединение). Основная причина — отсутствие поддержки соединения в UDP-протоколе, что усложняет задачу проксирования. В кейсах, где в среде разрешен только UDP-трафик, реализовать прокси данными протоколами не получится.
Однако можно прибегнуть к сырым сокетам, которые позволяют пересобирать TCP-пакеты в UDP. Данную технику я подсмотрел в этой статье.
proxy> socat -v tcp-listen:8080 udp-connect:attacker:53
client> socat -v udp-listen:53,fork tcp-connect:127.0.0.1:22
Так мы сможем поднять прокси в UDP-среде, воспользовавшись локальными TCP сокетами.
proxy> ssh -p8080 proxy@localhost -R 2222:127.0.0.1:22
client> sudo proxychains ssh -p2222 -N root@localhost -w 0:0
К этой же проблеме можно отнести задачу проксирования DNS, однако в этом случае решение может быть намного проще. Реализация DNS через TCP-протокол (DNS Over TCP) описана в RFC 7766 и упрощает работу с DNS через прокси. Например, популярные инструменты для анализа защищенности внутренней инфраструктуры (netexec, bloodhound-python, certipy) имеют флаг --dns-tcp
, который переводит протокол DNS на TCP и позволяет без проблем работать через прокси-сервер.
Сырые сокеты (raw sockets)
Сырые сокеты вызывают много проблем при проксировании. Чтобы понять почему — нужно немного погрузиться в их строение и принцип работы.
Ключевая особенность сырых сокетов в том, что они позволяют создавать кастомные (уникальные) сетевые пакеты, предоставляя максимально возможную свободу в реализации их структуры и наполнения. Такие пакеты могут иметь любые заголовки, любые данные и могут передаваться в обход стека TCP/IP и правил брандмауэра. Для лучшего понимания стоит взглянуть на схему, взятую из этого обсуждения.

Если опустить детали, в сетевом программировании AF_INET используется для работы с адресами, в то время как AF_PACKET — для работы с сокетами. Если в случае с первым работа идет в рамках стека TCP/IP, и там применяются классические процессы инкапсуляции/декапсуляции данных, то AF_PACKET позволяет создать сырые сокеты, которые будут работать с пакетами прямо на уровне сетевой карты (OSI Layer 2).
Такой механизм значительно усложняет манипуляции сторонними инструментами над сетевым трафиком (в том числе и ограничения брандмауэра), из-за чего проксирование вышеперечисленными способами становится невозможным. В таком случае можно воспользоваться промежуточным узлом между клиентом и прокси (например, маршрутизатором) и реализовать проксирование через него. По сути, это будет прозрачным проксированием, но с использованием физического файрволла.
Подробнее про AF_INET можно узнать тут, а по AF_PACKET информацию стоит посмотреть по этой ссылке.
Заключение
Прокси-серверы остаются незаменимым инструментом для управления трафиком и тестирования сетевой инфраструктуры. В статье были рассмотрены ключевые протоколы (SOCKS и HTTP), способы поднятия прокси через SSH, HTTP(S) и SMB, а также методы проксификации трафика. Также мы постарались уделить особое внимание проблемным местам, таким как работа с UDP и сырыми сокетами, и предложить решения для сложных сценариев.
Несмотря на технические ограничения, прокси продолжают развиваться, адаптируясь к новым вызовам. Выбор между SOCKS и HTTP, клиентским или прозрачным проксированием зависит от конкретной задачи, но в любом случае понимание принципов их работы поможет вам использовать их максимально эффективно.
Автор: @n3xani