Injected Evil: обзор нескольких методик обхода файерволов — Архив WASM.RU

Все статьи

Injected Evil: обзор нескольких методик обхода файерволов — Архив WASM.RU

В этой статье я хочу описать техники, который позволят обойти большинство современных проактивных защит и фаерволов. Эта тема очень популярна и достаточно изъезжена, но что-то новое не появлялось уже достаточно большое количество времени. Помимо сугубо практического материала я представлю Вам необходимый теоретический минимум для того, чтобы въехать в тему самостоятельно, почувствовать вкус, который чувствует исследователь и взломщик. Конечно, все эти техники уже используются некоторое количество времени в виде приватных утилит. До этого времени подобная информация не появлялась в таком количестве, качестве и концентрате. Многое было сделано до меня другими исследователями. За это надо сказать им спасибо. За их смелость выложить на публичное мнение свои уникальные разработки. Обход описанный тут раннее нигде не появлялся, но базируется на некоторых известных техниках. Чтож приступим.

Теория

Что такое обход защиты? Это возможность выполнять действия, которые запрещает защита. Тут нет правил и законов, есть просто задача добиться результата. Существует негласная война между авторами фаерволов и проактивных защит с взломщиками. Конечно, само существование данной войны очевидно. Отмечу, что здесь рассматривается только материал применимый к ОС семейства Windows NT, хотя теоретические принципы могут быть использованы для обхода любой защиты, которая строится на основе модели архитектуры современных фаерволов. Кратко опишем эту модель.

На компьютере жертвы установлен фаервол, который имеет как модуль - проактивную защиту. Поведение (блокирование и разрешение) фаервола определяется набором правил. Правила бывают обычно двух видов - глобальные правила и правила для приложений. Если говорить в общем, то в основе обхода любой защиты лежат мысли о 1) допущениях, 2) исключениях или 3) ошибках, сделанных разработчиками защиты. Эти три вещи дают обход практически любой защиты. А как изввестно не было еще такой защиты, которую не сломали. Если рассматривать персональные фаерволы в этом разрезе, то получим такую схему действий исследования - искать и использовать три вышеперечисленных пункта. Если поиск успешен, то мы победили.

Из всего сказано следует слудующее - чтобы обойти персональный фаервол, необходимо либо использовать существующие правила, либо разрушить модель правил, либо использовать ошибку в ПО защиты. С ошибками в ПО сложнее всего. Во-первых они не универсальны, во-вторых их сложно найти и использовать. Наиболее лучший обход использует минимум недокументированных функций и структур и применим ко всем защитам, т.е. универсален. Я такого обхода не дам :) Поэтому будем использовать недокументированные техники, но при этом относительно универсальные. Вернемся к правилам фаервола - чтобы их нарушить достаточно попасть в ring0, на этом защита заканчивается. Чтобы использовать существующие правила в своих целях, мы используем общую технику известную давно называемую инжектом. Т.е. исполнение кода в контексте доверенного приложения. Так, если мы каким-либо образом заставим доверенное приложение Internet Explorer выполнить наш код, мы сможем отправить данные в обход защиты. Вообще универсально надо брать браузер по умолчанию и его использовать как жертву исполнения нашего кода. И конечно в своем инжектированном коде мы используем правила, которые имеются в фаерволе относительно Internet Explorer (например, исходящие на 80 порт). Теперь дело за малым - выполнить инжект, при этом оставшись незамеченным для проактивной защиты, которая стемится закрыть все методы, использующиеся для инжекта. Теперь опишем конкретные атаки, которые применимы для конкретных фаерволов.

Общий код инжекта

Инжект всегда состоит их двух этапов:

  1. Запись кода в адресное простанство процесса-жертвы
  2. Передача управления записанному коду

Этапы эти могут быть любого вида - явные или косвенные и использовать любые доступные технические приемы. Если каждый из этапов не обнаруживается защитой, то можно осуществить инжект. При этом каждый этап обязателен, без любого из них инжект не будет осуществлен.

CreateProcess

Алгоритм следущий - хукаем в своем процессе из NTDLL - ZwCreateSection, ZwQuerySection и создаем процесс с помощью CreateProcess. До возврата из CreateProcess вызываются хукнутые ZwCreateSection/ZwQuerySection. Тут надо смотреть внутрь механима создания процесса - когда создается процесс, то один из этапов - открытие файла EXE-файла и проецирование его на адресное пространство создаваемого процесса. Создание проекции делается функцией ZwCreateSection. В этот момент в перехватчике функции ZwCreateSection можно писать любые данные с помощью той же KERNEL32!WriteProcessMemory и при этом проактивная защита будет молчать. Далее после создания проекции, код CreateProcess получает точку входа (Entry Point) нового процесса (т.е. его первичного потока) с помощью функции ZwQuerySection. Перехватчик ZwQuerySection возвращает точку входа, но не реальную - взятую из опционального заголовка - а лживую - а именно адрес нашего кода, который необходимо выполнить. Т.о. образом когда функция CreateProcess завершит работу будет исполнятся наш код, который отправит данные куда надо в контексте доверенного приложения.

Хорошо. Тут есть некоторые моменты, на которые я хотел бы обратить внимание. В обработчике ZwCreateSection не всегда можно писать с помощью WriteProcessMemory, некоторыми защитами это обнаруживается. В этом случае можно перехватить также и WriteProcessMemory, которая также вызывается внутри CreateProcess. Когда создается процесс в него пишутся переменные окружения родительского процесса, если не указаны явно переменные окружения. Вот в этом месте перехватывая WriteProcessMemory можно записать свой shellcode. Тоже самое с PEB. Еще проще просто указать параметром - lpEnvironment адрес нашего шелкода. Хорошо. Но это тоже не всегда работает. Существует способ записи, который пока работает всегда - если удается успешно создать процесс. Алгоритм записи следующий:

  1. Вызов CreateProcess с флагом CREATE_SUSPENDED
  2. Создание файла, запись shellcode в файл
  3. Создание проекции файла с шелкодом с помощью NtCreateSection
  4. Проецирование файла на адресное пространство приостановленного процесса с помощью NtCreateSection

Способ хоть и известный, но далеко не все авторы защит его пока прикрыли. Многие защиты блокируют создание сетевого процесса, а некоторые вообще создание процесса. Но не все. Например, известный Kaspersky Internet Security 7.0.0.125 поддается на эту атаку. Также на эту атаку поддаются ранние версии Agnitum Outpost (и Outpost 2008 если удастся успешно создать процесс). Ну и куча заморских фаерволов такие как Norton Internet Security 2008, Bit Defender Total Security 2008, Zone Alarm 7 на средних настройках (на максимуме блокирует), Agava Firewall, Ashampoo Firewall, AhnLab V3 Internet Security 2007, Black ICE PC Protection 3.6, Look'n'Stop 2.06, Normal Personal Firewall 1.4, Visnetic Firewall 3.0 и т.д. и т.д.

Перейдем к практической реализации данного метода. Браузер по умолчанию получаем с помощью доступа к ключу по умолчанию в разедел "http\shell\open\command":

      lea       ecx,[hKey]
      push      ecx
      push      KEY_READ
      push      0
      lea	ecx,[SubKey]
      push      ecx
      push      HKEY_CLASSES_ROOT
      call      RegOpenKeyEx

      lea       ecx,[cbData]
      push      ecx
      lea       ecx,[Buffer]
      push      ecx
      push      NULL
      push      NULL
      push      NULL
      push      [hKey]
      call      RegQueryValueEx

После исполнения данного кода в буффере будет путь к браузеру по умолчанию. Далее создаем процесс в приостановленом состоянии:

      lea       eax,[pi]
      push      eax
      lea       eax,[si]
      push      eax
      push      NULL
      push      NULL
      push      CREATE_SUSPENDED
      push      0
      push      NULL
      push      NULL
      lea       eax,[Buffer]
      push      eax
      push      NULL
      call      CreateProcess

Далее создаем проекцию файла и проецируем ее на адресное пространство созданного процесса:

      push      [hFile]
      push      0x8000000;SEC_COMMIT
      push      PAGE_EXECUTE_READWRITE
      push      NULL
      push      NULL
      push      SECTION_ALL_ACCESS
      lea       eax,[hSection]
      push      eax
      call      NtCreateSection

      push      PAGE_EXECUTE_READWRITE
      push      0
      push      1;ViewShare
      mov       [ViewSize],0
      lea       ecx,[ViewSize]
      push      ecx
      push      NULL
      push      0
      push      0
      push      [ebx+FreeBaseAddress-NormalCodeStart]
      pop       [BaseAddress]
      lea       ecx,[BaseAddress]
      push      ecx
      push      [pi + PROCESS_INFORMATION.hProcess]
      push      [hSection]
      call      NtMapViewOfSection

Вот здесь выявляется проблема: по какому адресу спроецировать shellcode. Ведь уже в этот момент мы должны были изменить точку входа на адрес нашего шелкода (до возврата из CreateProcess в перехватчике NtQuerySection). Пусть это будет предварительно выбранный адрес - константа, который по тестам не конфликтует с имеющимися проекциями. Но это будет явно не стабильно, т.к. конфликты могут быть и необходимо предусмотреть их отсутствие. Поэтому мы делаем следующим образом. Первый раз вызываем CreateProcess с флагом CREATE_SUSPENDED без хука NtCreateSecion/NtQuerySection, потом делаем проекцию по адресу, который выберет система и запоминаем его. После этого уничтожаем процесс. Далее создаем процесс уже с хуками нужных функций, но уже зная адрес проекции. После создания процесса делаем проекцию по уже известному адресу. Т.о. мы делаем тест конфликта для данной системы. Это должно минимизировать совпадения адресов проекции нашего шелкода и другой выделенной на данной момент памяти. После этог обход можно считать оконченным. Дальше остается только возобновить выполнение первичного потока (т.к. мы создавали процесс у которого первичный поток находится в приостановленном состоянии):

      push      [pi + PROCESS_INFORMATION.hThread]
      call      ResumeThread

NtQueueApcThread

На вызове этой функции основан обход почти всех существующий сейчас проактивных защит и фаерволов, какими бы мощными они не казались. Вот алгоритм обхода:

  1. Создаем DLL с именем совпадающим последними буквами с DLL, которая есть в импорте SVCHOST.EXE, например "ERNEL32.DLL" . В DLL должен быть shellcode для исполнения в контексте SVCHOST.EXE (это приложение в правилах всех фаерволов может также проявлять активность как и Internet Explorer).
  2. Ищем в адресном пространстве процесса SVCHOST.EXE строку "ERNEL32.DLL". Можно делать это сканированием вызовом функции ReadProcessMemory (для этого предварительно надо открыть SVCHOST - OpenProcess(PROCESS_VM_READ)). Другой и более качественный способ поиска строки - спроецировать файл SVCHOST.EXE себе в адресное пространство - и найти в таблице импорта строку "ERNEL32.32". Складывая относительный адрес строки с базовым адресом модуля по умолчанию (ImageBase в опциональном заголовке) получаем адрес нужной строки.
  3. Перебираем потоки процесса SVCHOST.EXE и делаем вызов для каждого из них NtQueueApcThread. Эта функция добавляет потоку APC - вызов асинхронной процедуры. Но у функции есть ограничение - процедура выполняется только в случае если поток перешел в режим Alartable. Этот факт обуславливает выбор процесса SVCHOST.EXE в качестве жертвы исполнения shellcode. При исследовании стало понятно, что во всех версиях Windows 2000/XP процесс SVCHOST.EXE имеет Alertable-потоки и при этом SVCHOST.EXE является доверенным приложением с возможной сетевой активностью, что явно дает право выбрать его в качестве жертвы. Также неободимо сказать, что процесс SVCHOST.EXE существует в системе всегда, в противном случае ОС попадает в BSOD (если убить один из SVCHOST.EXE). Вот прототип функции NtQueueApcThread:
  4. proc NtQueueApcThread hThread, FunctionAddress, Par1, Par2, Par3

    Функция добавляет в очередь асинхронных процедур новую асинхронную процедуру с конвенцией вызова STDCALL и с передачей трех параметров Par1, Par2, Par3. Интересно, но есть функция, которая очень точно нам подоходит и также принимает три параметра - LoadLibraryEx. Т.о. мы просто загружаем DLL в адресное пространство процесса SVCHOST.EXE.

  5. Из самой DLL нельзя что-либо делать, т.к. это обнаружение контролем компонентов. Поэтому что все мы делаем в DLLMain это - копируем shellcode из своего тела, и сохраняем в файл с известным именем TID и адрес shellcode. Далее после проделанных действий DLL нужно удалить.
  6. Открываем файл созданный внутри DllMain - и ставим в очередь известного потока (его TID записан в файле) адрес процедуры, который также известен. После этого обход закончен.
Этот обход преодолевает уже и Comodo Firewall 2.4 и Agnitum Outpost 2008 и все названные в предыдущем разделе защиты. Остались при этом живы Jetico и Tiny - об обходе этих защит здесь говорить ничего не буду, т.к. это отдельный разговор. Zone Alarm 7 в параноидальном режиме ловит этот вид обхода - а именно не дает открыть поток для посылки в него APC, но для него был сделан отдельных обход, который я опишу ниже.

Zone Alarm

У ZoneAlarm особая модель - Trusted Application. Если исполнять что-либо в контексте Super Trusted приложений, то ZA не обратит на это внимание. Одно из таких Super Trusted приложений - это EXPLORER.EXE. У ZA особая модель защиты - она не мучается перехватывать кучу функций в SDT относительно потоков и процессов - она просто не дает открывать процесс и поток с видами доступа, которые могут привести к нарушению безопасности. Т.е. если открывать процесс или поток, то ZA будет ругатся. Но была найдена ошибка, которая позволяет все таки открыть процесс. Вот алгоритм:

  1. Открываем процесс с правами DUP_HANDLE - это ZA позволяет.
  2. Перечисляем описатели EXPLORER.EXE в поисках описателя одного из потоков EXPLORER.EXE в нем. Смотрим права для описателя - если они максимальны, то делаем вызов DuplicateHandle. Т.о. мы получаем описатель потока с максимальными привилегиями.
  3. Теперь можно сделать инжект. Передать управление мы можем с помощью функции SetThreadContext, описатель потока у нас есть. Но чтобы в процесс записать данные надо исопльзовать отдельную технику, которая общедоступна. О ней в следующем пункте.
  4. При перечислении описателей получаем описатель порта. У EXPLORER.EXE во всех версиях имеется порт. После получения описателя порта получаем его имя. После этого вызовом NtConnectPort и созданием секции в которой находится наш shellcode проекция файла с shellcode волшебным образом окажется в адресном пространстве процесса EXPLORER.EXE.
  5. Используем SetThreadContext для передачи управления нашему shellcode.
  6. Т.к. от имени EXPLORER.EXE можно делать разные системные вещи (но нельзя проявлять сетевую активность) без опасения тревоги ZA, то используем метод с названием CreateProcess описанный выше для окончательного обхода ZA на параноидальных настройках.

Видно, что обход основан на том, что возможно все таки получить описатель потока без обнаружения ZA. При этом нам повезло, что процесс EXPLORER имеет в себе свои же описатели потоков с максимальными привилениями, а также имеет порт, подключение к которому позволяет записать данные shellcode в адресное пространство процесса-жертвы используя ранее публично доступный метод. При исследонии ZA было также обнаружено, что ZA позволяет открыть процесс СTFMON.EXE с максимальными привилегиями без предупреждения пользователя. Это факт также легко может использоваться для обхода ZA. Но CTFMON.ExE не всегда запущен (хоть и очень часто - сама Windows прописывает его в автозагрузку при установке), и появился только в Windows XP, поэтому этот способ не очень универсален(не может быть использован на Windows 2000), хотя и может применятся с успехом в Windows XP. Также на Windows XP SP2 было обнаружено, что процесс EXPLORER.EXE имеет свой же описатель с максимальными привилегиями, после его получения возможно делать с процессом любые манипуляции. Но после дальнейших тестов было обнаружено, что это ошибка именно Service Pack 2, и на Service Pack 1 этот недочет не обнаружен так же как и на обычной Windows XP RTM.

POC не будет - сгорите в аду SCRIPT KIDDIES!

Greets to Ms-Rem, EP_XOFF, Tyler Durden, deadbody

FEEBACK

· mail: evil_phreak mail.ru
· web: evilphreak.blog.ru

2002-2013 (c) wasm.ru