exploits-review-0x0A

exploits review\\ 0Ah выпуск

крис касперски ака мыщъх, no-email

brief:дополнительные защитные средства (такие, как антивирусы, персональные брандмауэры, etc) зачастую _сами_ становятся объектом атаки и вместо обещанного рекламой усиления защиты мы получаем новые дыры, одна из которых была обнаружена David'ом Matousek'ом (основателем и руководителем одноименной исследовательской компании — Matousec - Transparent security, www.matousec.com).

15 сентября 2006 года, экспериментируя с версий 9.1.0.33, он посылал псевдоустройству '\Device\SymEvent' (созданному брандмауэром) различные IOCTL-запросы, которых оно, признаться, не ожидало и от удивления высадило систему на полный BSOD. David уведомил производителя об ошибке, которая была исправлена в следующей версии, и зафиксирована в базе Security Focus под номером BID 20051 (множественные локальные отказы в обслуживании в драйвере SymEvent) но! в версии 9.1.1.7 разработчики вернули ошибку на место, что привело к возможности обрушения системы древним exploit'ом!подробности об этом инциденте можно найти на: www.matousec.com/info/advisories/Norton-Insufficient-validation-of-SymEvent-driver-input-buffer.phpи http://www.securityfocus.com/bid/22961/info;

targets:уязвимость впервые обнаружена в версии 9.1.0.33 и «реикцинирована» в версии 9.1.1.7, промежуточные версии выпущены без этой ошибки;

exploit:исходный текст exploit'а на языке Си лежит на сервере компании Matousec: www.matousec.com/downloads/windows-personal-firewall-analysis/BTP00011P002NF.zip, а ниже приведен его ключевой фрагмент:

HANDLE file=CreateFile(«\\\\.\\Global\\SymEvent»,GENERIC_READ |

GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,

NULL,OPEN_EXISTING,0,NULL);

srand(GetTickCount());

char bufout[4],bufin[20]=«\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1»;

DeviceIoControl(file,0x00220404,(PVOID)bufin,20,(PVOID)bufout,4,&retlen,NULL);

Листинг 1 ключевой фрагмент exploit'а David'а Matousek'а

solutionиспользовать стабильные версии между 9.1.0.33 и 9.1.1.7;

Рисунок 1 страничка компании Matousec - Transparent security, обнаружившей уязвимость в Norton Personal Firewall

brief:Unique RAR File Library – представляет собой бесплатную кросс платформенную библиотеку, распространяемую в исходных текстах (http://www.unrarlib.org/) и, как легко догадаться из ее названия, позволяющую сторонним программистам создавать независимые утилиты для распаковки RAR-архивов или интегрировать библиотечный код в свои собственные продукты, обеспечивая его «прозрачную» поддержку. К сожалению, библиотека не свободна от ошибок и некоторые из которых носят характер критических, как например, ошибка переполнения в имени файла, обнаруженная хакером по кличке starcadi (starcadi@autistici.org) и описанная на http://securityvulns.com/news/Unrarlib/BO.html.

Суть ошибки состоит в том, что имя распаковываемого файла копируется в локальный буфер фиксированного размера длинной в 255 байт (см. листинг 2), что (вместе с путем) является пределом для Windows и файл с более длинным именем ни создать, ни открыть не удаться, но в _архиве_ длина имени файла ограничена только длинной самого архива (то есть не ограничена вообще!). Естественно, такой архив нельзя создать легальным путем с помощью самого RAR'а, но что мешает хакеру «смастерить» архив самостоятельно (формат-то известен!) или «надругаться» над уже существующим?! при этом мы получаем классическое переполнение стека с возможностью передачи управления на shell-код (содержащийся в имени файла) и захвату управления машиной (естественно, на процессорах с поддержкой NX/XD флагов и задействованным аппаратным DEP на Windows или аналогичными защитными средствами из мира UNIX, делающих стек неисполняемым и использующих рандомизацию адресного пространства, атака окажется чрезвычайно затруднена, но все-таки возможна! впрочем, это тема для совсем другого разговора).

Передав такой архив по сети, мы одним взмахом руки превращаем локальную уязвимость в удаленную, причем, если антивирус или другой программное обеспечение использует библиотеку unrarlib для автоматической проверки «пролетающих» архивов (а оно ее использует), то жертве вообще не требуется совершать никаких действий!!!

char ArcName[255];/* RAR archive

urarlib_get(void *output,ulong*size,char *filename, void *rarfile, char *psswd)

{

strcpy(ArgName, filename);/* set file(s) to extract

strcpy(ArcName, rarfile);/* set RAR file name

}

Листинг 2 фрагмент библиотеки unrarlib, содержащий уязвимость

targets:на данный момент уязвимость подтверждена в версии 0.4.0, про другие версии пока ничего не известно, так же отсутствует перечень продуктов сторонних разработчиков, использующих библиотеку unrarlib в своих проектах;

exploit:готовые exploit'ов в дикой природе еще не обнаружены, но используя «легальный» rar архив, длину файла можно легко увеличить с помощью hiew'а (или любого другого hex-редактора) и с его же помощью вбить туда боевой shell-код;

solutionда поможет нам Аллах!

Рисунок 2 здесь раздают unrarlib

brief:small http (smallsrv.com) надежный, проворный и чрезвычайно компактный http.ftp/proxy/pop3/smtp/dns/dhcp-сервер в одном флаконе (а для граждан бывшего СНГ к тому же еще и бесплатный). сейчас он стоит в норе у мыщъх'а, сменив War FTP-сервер, и мыщъх, естественно, следит за его безопасностью, шурша логами и добавляя в Black-List все новые IP-адреса, обладатели которых конкретные крысы или просто всякие там поисковые машины, качающие все без разбору и напрягающие канал, досаждая не только мыщъху, но и всем остальным посетителям. так вот, после добавления нового IP в «Deny-IP» сервер через некоторое время вылетел в soft-ice, который мыщъх держит всегда запущенным, демонстрируя ситуацию типичного переполнения.

анализ показал, что последний внесенный в Black-ListIP (87.250.254.249), принадлежащий yandex'у, был усечен сервером до 87.250.254, и при попытке подключения с адреса 87.250.254.xxx, у small http что-то «перемкнуло» внутри парсера IP-адресов, и возникло необработанное исключение, отловленное айсом.дело кончилось тем, что мыщъх, отправив разработчику уведомление об ошибке, перенес Black-List на персональный брандмауэр (ну типа workaround такой).

эксперименты со списком блокируемых адресов быстро опровергли первоначальную гипотезу об фиксированной длине поля Deny-IP, и не позволили установить условия, при которых происходит «усечение» последнего введенного адреса. судя по всему, помимо количества IP-адресов, тут присутствуют еще и другие факторы.

target:уязвимость обнаружена в версии 3.05.64, о других мыщъх'у ничего не известно;

exploit:фрагмент конфигурационного файла с «черным списком» IP-адресов, на которых наблюдается _устойчивое_ воспроизведение ошибки (вместе с кратким описанием ситуации) лежит в мыщъхиной норе по адресу http://nezumi.org.ru/souriz/hack/http.cf_

solutionблокировать IP-адреса на брандмауэре;

Рисунок 3 поле Deny-IP со списком блокируемых адресов, подверженное переполнению

brief:20 февраля 2007 года сотрудники лаборатории CoreLabs Advisory (подразделение компании Core Security Technologies), обнаружили, что при получении фрагментированного IPv6 пакета, OpenBSD, воздвигнутая в конфигурации по умолчанию, выпадает в kernel panic. на следующий день разработчикам системы быв выслан proof-of-concept exploit, демонстрирующий удаленный отказ в обслуживании, успешно подтвержденный и довольно оперативно исправленый заплаткой, выпущенной 26 февраля, однако, статуса уязвимости («vulnerability») ошибке так и не присвоили, поскольку по мнению OpenBSD-team'а, отказ в обслуживании это не дыра, а так… просто мелкая неприятность, о которой пользователям знать совершенно необязательно.

Рисунок 4 «только одна удаленная дыра в конфигурации по умолчанию, более чем за 8 лет эксплуатации»

такое положение дел разозлило парней из CoreLabs и они, ценой недели беспощадных исследований (пиво, сигареты, трава, чай, кофе и свечи от геморроя в придачу) доказали возможность удаленного захвата управления, выпустив 5 марта «боевую» версию explout'а с shell-кодом на борту, от которого разработчикам OpenBSD было уже не отвертеться, и вся последующая неделя ушла на переписку с CoreLabs, подготовившей за это время развернутый отчет по безопасности, опубликованный ими на собственном сайте http://www.coresecurity.com/?action=item&id=1703. 13 марта координатор проекта Theo de Raadt переслал его на Bugtraq (http://archives.neohapsis.com/archives/openbsd/2007-03/0417.html), откуда он разошелся по другим сайтам, прямо или косвенно связанных с безопасностью.

это вторая дыра в OpenBSD, обнаруженнаяза последние 10 лет «промышленной» эксплуатации (предыдущая сидела в демоне SSH), так что ее открытие можно назвать весьма эпохальным событием, привлекающим к себе внимание и вызывающим желание как следует во всем разобраться;

Рисунок 5 «всего лишь все удаленных дыры в конфигурации по умолчанию, более чем за 10 лет эксплуатации»

targets:уязвимости подвержены следующие версии: OpenBSD 4.1, OpenBSD 4.0 Current, OpenBSD 4.0 Stable, OpenBSD 3.9, OpenBSD 3.8, OpenBSD 3.6 и OpenBSD 3.1;

exploit:исходный текст exploit'а можно найти в отчете CoreLabs, доступном по адресу http://www.coresecurity.com/?action=item&id=1703. он написан на Питоне и требует библиотеки Impacket, используемой для создания сырых (raw) сокетов и доступной для бесплатного скачивания по адресу: http://oss.coresecurity.com/projects/impacket.html. shell-код состоит из одной-единственной инструкции INT 03h (точка останова, вызывающая всплытие отладчика) и последующей за ней командами балансировки ESP и возврата внутрь ядра. фрагментированный IPv6 пакет, засовывается внутрь ICMP-пакета с полем type равным 128 (ICMP echo request), который должен быть послан злоумышленником непосредственно по локальной сети, либо через какой-нибудь тоннель IPv6 over IPv4, в противном случае, удаленная атака не состоится и хакер склеит ласты, а он их непременно склеит, т. к. популярность протокола IPv6 еще долгое время будет оставаться на уровне чуть выше абсолютного нуля, во всяком случае в глобальном масштабе.

solutionsзаплатки для OpenBSD 4.0 и 3.9 доступны по следующим адресам: ftp://ftp.openbsd.org/pub/OpenBSD/patches/4.0/common/010_m_dup1.patch, ftp://ftp.openbsd.org/pub/OpenBSD/patches/3.9/common/020_m_dup1.patch.

версия 4.1 залатана непосредственно в исходном коде и отдельной заплатки для нее нет.

как вариант, можно не устанавливать заплатку, а заблокировать весь IPv6 трафик на встроенном в OpenBSD брандмауэре (естественно, при условии, что он не нужен), для этого необходимо выполнить следующую последовательность действий.

# добавить следующую строку в файл /etc/pf.conf:

block in quick inet6 all

# загрузить обновленный pf.conf

# внутрь запущенного PFпосредством утилиты pfctl

pfctl -f /etc/pf.conf

# разрешить его использование

pfctl -e -f /etc/pf.conf

# посмотреть текущий статус

# на предмет проверки успешности принятия нового правила

pfctl -s rules

Листинг 3 блокирование всего IPv6 трафика на встроенном брандмауэре

Рисунок 6 патч для OpenBSD, исправляющий ошибку

fulldisclose:

Чтобы разобраться в дыре основательно и (самое главное!) _самостоятельно_ необходимо иметь установленную OpenBSD, а поскольку такой внутри мыщъхиной норы и не обозначилось, пришлось курить исходные тексты. Но исходные тесты OpenBSD – это же лчень сильно до хрена и курить ее можно целый сезон, а она даже не убавится. Нееет… Тут нужно мыслить стратегически и действовать по плану!

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

— sys/kern/uipc_mbuf2.c17 Mar 2006 04:15:51 -00001.24

+++ sys/kern/uipc_mbuf2.c7 Mar 2007 19:21:48 -00001.24.2.1

@@ -226,16 +226,14 @@ m_dup1(struct mbuf *m, int off, int len,

{

struct mbuf *n;

int l;

-int copyhdr;

if (len > MCLBYTES)

return (NULL);

if (off == 0 && (m→m_flags & M_PKTHDR) != 0) {

copyhdr = 1;

MGETHDR(n, wait, m→m_type);

+M_DUP_PKTHDR(n, m);

l = MHLEN;

} else {

-copyhdr = 0;

MGET(n, wait, m→m_type);

l = MLEN;

}

@@ -249,8 +247,6 @@ m_dup1(struct mbuf *m, int off, int len,

if (!n)

return (NULL);

-if (copyhdr)

-M_DUP_PKTHDR(n, m);

m_copydata(m, off, len, mtod(n, caddr_t));

n→m_len = len;

Листинг 4 патч для OpenBSD, приведенный с незначительными сокращениями

С первого взгляда суть изменений совершенна неясна, можно даже сказать, мистически непонятна. Разработчики просто переместили макрос M_DUP_PKTHDR из конца функции m_dup1() внутрь ветки «if (off == 0 &&…», попутно избавившись от переменой-флага copyhdr. Но ведь алгоритм функции m_dup1() остался прежним и при этом совершенно непостижимым…

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

Идем на http://fxr.watson.org/fxr/source/kern/uipc_mbuf2.c?v=OPENBSD и смотрим на _полный_ код функции m_dup1(), критические фрагменты которого в патче отсутствуют.

static struct mbuf *

m_dup1(struct mbuf *m, int off, int len, int wait)

{

struct mbuf *n;int l;int copyhdr;

if (len > MCLBYTES)return (NULL);

if (off == 0 && (m→m_flags & M_PKTHDR) != 0)

{

copyhdr = 1;

MGETHDR(n, wait, m→m_type);

l = MHLEN;

}

else

{

copyhdr = 0;

MGET(n, wait, m→m_type);

l = MLEN;

}

if (n && len > l)

{

MCLGET(n, wait);

if 1)

1)
n→m_flags & M_EXT) == 0) { m_free(n); n = NULL; } } if (!n)return (NULL); if (copyhdr)M_DUP_PKTHDR(n, m); m_copydata(m, off, len, mtod(n, caddr_t));n→m_len = len; return (n); } Листинг 5 полный исходный текст функции m_dup1() Злобный diff покоцал ветвь «if (n && len > l)», сбив нас с толку и завязав наш хвост двойным морским узлом, но теперь мы вникли в тему: в исправленной версии макрос M_DUP_PKTHDR вызывается _до _ MCLGET , а в старой — после. Осталось только узнать, чем все эти макросы занимаются. Нет ничего проще — на fxr.watson.org все они представлены ссылками, щелкнув по которым мы переходим к месту их определения, снабженного комментариями. Точно таким же путем разбирается с M_PKTHDR и m→m_flags, проясняющих смысл конструкции: «if (off == 0 && (m→m_flags & M_PKTHDR) != 0)», который в переводе на русский язык звучит приблизительно так: если смещение (off) пакета равно нулю, но оно не совпадает с началом пакета, то мы имеем дело с фрагментом пакета для обработки которого входим внутрь ветки if. Макрос MGETHDR выделяет память под специальную структуру mbuf (в данном случае указатель на нее помещается в переменную n) и тут же инициализирует ее для хранения пакетов типа m→m_type. Макрос MCLGET «заглатывает» заполненные структуры mbuf и объединяет их в кластер, осуществляя сборку пакетов, но!!! в нефикисенной версии объединение пакетов происходит _до_ вызова макроса M_DUP_PKTHDR, копирующая переданный функции указатель m в выделенную и проинициализированную переменную n. Вот где собака зарыта!!! Поскольку, выделение памяти под фрагменты осуществляется сразу же после инициализации mbuf и _до_ заполнения ее полей реальными значениями, то попытка копирования всех фрагментов функцией m_copydata() в переменную m приводит к переполнению. А все потому, что макрос M_DUP_PKTHDR стоит не на месте! Вроде бы мелочь, а какие последствия она вызывает… Кстати говоря, парни из CoreLabs этот момент никак не объясняют, заставляя нас гадать как связана фрагментация с переполнением и на сколько фрагментов пакет необходимо разбить для успешной атаки. Забавно, но некоторые ресурсы по безопасности (особенно русские), передирая (и конечно же, перевирая) письмо Theo de Raadt'а, к прилагательному «фрагментированный» добавляют эпитет «сильно». Дескать, шлите ребята сильно фрагментированные IPv6 пакеты и валите OpenBSD косяками. На самом деле, в оригинале слова «сильно» отсутствует и прилагаемый к письму exploit разбивает IP-пакет всего на _два_ фрагмента, так что называть его сильно фрагментированным можно только накурившись дряни, пополам смешанной с удобрениями. Но это все лирика, пора переходить к технике передачи управления на shell-код, поскольку вгонять ядро в панику как-то неинтересно. Поскольку, это не совсем обычное переполнение, то традиционные приемы здесь не подходят и начинать приходится с изучения полей структуры mbuf.h, описанной в файле /sys/mbuf.h: This is the definition (/sys/mbuf.h): struct mbuf { struct m_hdr m_hdr; { union { struct { structpkthdr MH_pkthdr;/* M_PKTHDR set */ union { structm_ext MH_ext;/* M_EXT set */** charMH_databuf[MHLEN]; } MH_dat; } MH; charM_databuf[MLEN]; /* !M_PKTHDR, !M_EXT */ } M_dat; }; Листинг 6 устройство структуры mbuf Парни из CoreLabs верно подметили, что одним из элементов структуры mbuf является структура m_ext, описанная в файле /sys/mbuf.h: struct m_ext { caddr_t ext_buf;/* start of buffer */ /* free routine if not the usual */ void(*ext_free)(caddr_t, u_int, void *); void*ext_arg;/* argument for ext_free */ u_intext_size;/* size of buffer, for ext_free */ intext_type; structmbuf *ext_nextref; structmbuf *ext_prevref; #ifdefDEBUG const char *ext_ofile; const char *ext_nfile; int ext_oline; int ext_nline; #endif }; Листинг 7 устройство структуры m_ext Среди множества разных типов данных, в структуру m_ext входит указатель на функцию ext_free, которая вызывается из функции m_free(), когда приходит последний фрагмент пакета (см. листинг 5). А это значит, что подменив ext_arg указателем на shell-код мы вместо банального краха системы добьемся перехвата управления. Вся сложность в том, что мы не знаем где именно размещается переполняемая структура mbuf в памяти, поэтому возникает задача определения ее дислокации. Парни из CoreLabs называют ее поиском правильного «трамплина» (right trampoline), и делают они это следующим образом… но прежде небольшое лирическое отступление. Словарь «Мультилекс» переводит «trampoline» как «батут» и по этому поводу вспоминается следующая невероятно правдоподобная история из жизни. В одном из небольших городов театр проездом давал «Грозу» Островского, в которой по сценарию Катерина бросалась в реку (и, как любил поговаривать мой папа, если бы она жила в наше время, то не утопилась, а бросилась бы под поезд). Естественно, для смягчения последствий падения использовались маты. С собой их не возили, перекладывая задачу возведенная декораций на местных и вот местные, покурив хорошей травы, вместо мата по ошибке положили батут. Короче, бросается значит, Катерина в «реку» и… тут же с криком вылетает обратно. И так несколько раз. Актеры с трудом сдерживаются (сцена-то трагическая), зрители в трансе и в этот момент один из стоящих на сцене произносит: «Да… Не принимает матушка-Волга…» С трамплином возникает та же ситуация, только вместо Волги у нас BSD, а вылетает не Катерина, а исключение. И продолжает вылетать до тех пор, пока мы не угадаем точную локацию переполняемой структуры в памяти, которую парни из CoreLabs добывают довольно варварским путем: «objdump -d /bsd | grep esi | grep jmp», то есть дизассемблируют конкретную версию OpenBSD и ищут в ней инструкцию jmp esi. При чем тут esi? По чистой случайности компилятор разместил в нем указатель на переполняемую структуру, но… где гарантия, что при малейшем изменении исходного кода или компиляции с другими ключами, компилятор не выберет иную стратегию поведения и не засунет указатель совсем в другой регистр?! Увы, такой гарантии у нас нет, а потому proof-of-concept exploit _крайне_ не универсален и ненадежен. Атаковать произвольную систему с его помощью не получится и он годится только для взлома систем, установленных «из коробки» (т. е. поставляемых в уже откомпилированном виде), да и то, в разных версиях, положение трамплина будет различным, так что опасность грозящая пользователям OpenBSD _очень_ сильно преувеличена.