Инжект кода средствами CSRSS: Сказ о том, как Windows 7 помогает современным хакерам старыми средствамиСуществует такая аксиома: чем больше недокументирована какая-нибудь часть Windows, тем больше разработчики Microsoft не хотят, чтобы ее использовали разработчики программного обеспечения. Причина как всегда банальна – недокументированные возможности позволяют вытворять с системой жуткие вещи! Тем не менее в среде программистов бытует мнение, что, несмотря на полную недокументированность подсистемы ввода-вывода CSRSS, ничего интересного она из себя не представляет, как с точки зрения прикладного кодинга, так и с точки зрения разработки малвари. Да, приходится признать, что даже вирусам и руткитам она неинтересна. За исключением, пожалуй, знаменитого червя Nimda. Хотя, постой, еще существует способ детекта скрытых процессов возможностями CSRSS… В общем, CRRSS не так прост и бесполезен, как кажется некоторым несознательным гражданам. Положим его на наш операционный стол и узнаем, чем же он может быть полезен настоящему хакеру. ВведениеПодсистема CSRSS – client/server run-time subsystem (клиент-серверная подсистема) – это часть исполнительной подсистемы Windows, которая отвечает за консольные приложения, создание/удаление потоков и за 16-битную виртуальную среду MS-DOS. Это мы знаем, но, к сожалению, на этом вся документированность CSRSS заканчивается. Наверное, это один из самых загадочных кирпичиков Windows. И это не только загадочный, но и чувствительный кирпичик, так как процесс CSRSS.EXE – единственный, которому присваивается эпитет «критичный», и вмешательство в его нормальное исполнение грозит крахом всей системы. Упомяну такую деталь – на весь (!) зоопарк BSOD’ов, который только может сгенерировать ядро Windows, только два багчека: 0x0000004C (FATAL_UNHANDLED_HARD_ERROR) и 0xC000021A (STATUS_SYSTEM_PROCESS_TERMINATED) происходят при крахе его юзермодных процессов – это процессы winlogon.exe и csrss.exe. Без этих двух процессов Windows существовать не может. И самое печальное – во втором случае, если будет установлено, что причиной «синего экрана» стал отказ csrss.exe (как правило, в результате действия малвари), то это будет фатальный случай, приводящий к переустановке системы (или ее аварийному восстановлению). CSRSS берет свои настройки не из реестра, как может показаться на первый взгляд, а из командной строки, которая выглядит примерно так:
В общем, на мой взгляд, основная задача CSRSS все-таки заключается в своеобразном контроле над созданием и уничтожением процессов и потоков. А раз так, то нельзя недооценивать те возможности, которые в нем скрыты. LPC, или Local Procedure CallsПеред тем, как углубиться в дебри CsrApi, давай посмотрим в сторону механизма LPC, созданного в Windows для реализации межпроцессного взаимодействия. Почему именно на него? Все дело в том, что CSRSS реализует свой собственный протокол поверх LPC. LPC часто называют Local InterProcess Communication. Я, к сожалению, не знаю, как на самом деле расшифровывается LPC, но раз на MSDN-блогах LPC зовут Local Procedure Calls, значит, так тому и быть. Как уже было сказано выше, LPC – это недокументированный механизм Windows, предназначенный для взаимодействия между процессами и потоками, основанный на приеме/передаче пакетов. Это взаимодействие может происходить как целиком в ядре, так и между юзермодными компонентами. Суть LPC предельно проста – коммуникация между участниками взаимодействия (клиентом и сервером) осуществляется путем передачи блоков данных (так называемых сообщений LPC) . Клиентом может быть поток или процесс, и исполняться они могут на разных ring-уровнях, как в ядре (r0), так и простым пользовательским приложением (r3). Основывается он на двух вещах – портах и внутренних LPC-структурах, таких как PORT_MESSAGE. Логически LPC состоит из двух действий – коннекта к определенному порту (NtCreatePort, NtConnectPort, NtListenPort и т.д.) и передаче данных (NtRequestWaitReplyPort и др.). CsrClientCallServer – вот где собака порылась!Среди всех используемых CsrApi-функций наиболее часто можно встретить CsrClientCallServer. Перекрестные ссылки на нее можно увидеть в таких функциях, как kernel32!CreateProcess, kernel32!AllocConsole, kernel32!FreeConsole, user32!EndTask и десятках других. Если мы взглянем на нее под микроскопом IDA, то увидим, что каждый раз, когда вызывается CsrClientCallServer, в стек заталкивается какое-то уникальное число, меняющееся от функции к функции:
Это загадочное число – не что иное, как индекс указателя специальной функции, определенной в библиотеках, используемых подсистемой CSRSS. То есть специальная процедура, называемая CsrApiRequestThread, исполняется в контексте отдельного потока в csrss.exe, ответственного за прием запросов от пользовательской подсистемы. Она обрабатывает его через соответствующие диспетчерские таблицы CSRSS и возвращает результат. Что интересного может дать использование функций CsrApi в интересах программиста? Много чего, например, можно организовать прямой распил консоли, так как подсистема CSRSS напрямую отвечает за консоль в Windows.
К сожалению, рамки статьи не позволяют описать все аспекты использования CsrApi-функций в повседневной жизни программиста, поэтому сейчас мы перейдем непосредственно к теме сегодняшней статьи. Что дальше?Итак, что же мы нашли при распиле CSRSS? В смысле, с хакерской точки зрения. Ни много, ни мало – инжект кода в Windows 7. Как ты знаешь, разработчики Windows 7 сильно потрудились над безопасностью процессов в системе – теперь просто так выполнить CreateRemoteThread в чужой процесс не удастся. Да, приходится признать, что дяденьки из Microsoft постарались на славу, отрубив мегакулхацкерам любимый способ инжекта кода. При попытке вызова CreateRemoteThread с хэндлом процесса другого пользователя, мы обламываемся, и функция возвращает нам NULL с кодом ошибки ERROR_NOT_ENOUGH_MEMORY. Конечно, существуют в природе способы инжекта с использованием RtlCreateUserThread (подробнее об этом можно прочитать здесь), но мы не будем его рассматривать; желающие могут поэкспериментировать сами. Не зря мы сегодня завели разговор про CSRSS, ведь именно с помощью ее возможностей можно очень даже неплохо вернуть утраченный status quo и получить возможность инжекта кода в чужие процессы. Концепция нашего PoC проста. Дело в том, что успешность вызова CreateRemoteThread зависит от системной функции CsrClientCallServer, которая фактически обрабатывает этот запрос. Она следит за выполнением, но сама удаленный поток не создает. Вызов CreateRemoteThread сводится к системной функции NtCreateThreadEx, которая создаст поток с флагом CREATE_SUSPENDED, однако дальнейшее развитие ситуации будет зависеть от успешности вызова функции подсистемы CSRSS – CsrClientCallServer. Ничего интересного в голову не приходит? :) Все, что нам нужно – это сделать так, чтобы при создании удаленного потока CsrClientCallServer всегда возвращала успешное значение. И будет тебе счастье. Смотрим на дизассемблированную kernelbase.dll (это аналог kernel32.dll в Windows 7, если ты не знал):
Нам нужно найти в памяти kernelbase.dll, просканировать таблицу импорта, найти адрес импортируемой функции CsrClientCallServer и подменить его новым указателем на заранее подготовленную функцию CsrClientCallServer, которая всегда будет возвращать «успех». Сделать это легко. Смотрим:
И все! Раз, раз, раз – и… мы в дамках! Теперь наша новая функция CsrClientCallServer будет возвращать «success», что, собственно, и нужно для успешного запуска CreateRemoteThread. Кстати, такой подход довольно оригинален: вместо того, чтобы искать способы выполнения своего кода, писать (или покупать) 0day-эксплойты, повышающие права, искать новые уязвимости в системе и т.д., иногда бывает просто нужно переписать один байт. Самое главное – знать, где :) В заключение только добавлю, что для успешного выполнения такого кода нужны дебаг-привилегии, которые подрубаются примерно таким образом:
Вот и все, что я хотел донести до тебя. Не так страшна Windows 7, как ее раскрашивают. Код, который делает все вышеизложенное, как всегда, ищи на диске. Удачного компилирования, и да пребудет с тобой Сила! P.S. Осторожнее в экспериментах с CSRSS! А то систему жалко :). CDНа компакт-диске ты найдешь реализацию описанных в статье приемов на С. WWWНе ленись читать MSDN – несмотря на бытующее в определенных кругах пренебрежительное отношение к данному сайту, чтение его статей позволяет устранить до 99% ошибок, возникающих при использовании WinAPI. |