Как стать автором
Обновить

Создание прокси-dll для проверок эксплуатации dll hijack

Время на прочтение7 мин
Количество просмотров9.6K
Всего голосов 18: ↑18 и ↓0+18
Комментарии21

Комментарии 21

Знание порядка загрузки помогло мне однажды, когда надо было подсунуть к SFTP плагину для Total Commander правильную версию libssh библиотеки.
Собственно, это и есть правильное применение технологии — подложить конкретную библиотеку для конкретного приложения.
НЛО прилетело и опубликовало эту надпись здесь
С одной стороны метод хороший, но довольно сложно его реализовать правильно. Дело в том, что если загрузка идет через секцию импорта, то проверять и считать что-либо уже поздно — библиотека уже загружена и выгружать ее бесполезно — код уже выполнен. Если загрузка происходит ручками, то у нас появлются варианты: можно намеренно испортить dll и тем самым сломать логику приложения, но что хуже, потенциально можно поставить OpLock на файл и подменить его после доступа на чтение. Получится, что сначала у файла посчитают контрольную сумму и она будет корректной, а загрузит приложение уже подмененный файл.
А не поделитесь идеями, как правильно реализовать данный метод защиты?
Интересует не намеренное ломание логики приложения, т.к. если есть возможность подменить DLL, то просто сломать можно простым ее переименованием. Интересует защита именно проверкой загружаемой библиотеки.
К слову у DLL прокси, после реализации всех экспортных методов, с помощью мусорного кода, можно и MD5 нужный подобрать. Тут лучше 2 хеша разных сразу проверить.
Я бы решал эту задачу используя механизмы безопасности Windows — загрузка библиотек только из папки на которой явно прописаны допустимые права и вместо проверки хэшей, сразу бы сделал проверку подписи PE-файла, благо к этому довольно внятное АПИ, хоть и не без подводных камней. Если запускать сервис, то я вижу лучшим решением скопировать его в C:\Windows\system32 — стандартные библиотеки защищены от переписывания механизмами защиты ОС и если там можно будет подменять библиотеки, то у нас проблемы посерьезнее, чем внедрении dll в сервис.

Вы между тем забыли упомянуть о KnownDLLs в который внесены основные системные dll-ки. Так что трюк с version.dll не пройдёт. А ещё есть CWDIllegalInDllSearch который тоже меняет порядок поиска dll-ок… Это правда редко кем-либо настраивается, скорее в корпоративных окружениях.

Да, спасибо, что упомянули список KnownDLLs и ключ CWDIllegalInDllSearch. Они слишком редко встречаются в боевых условиях, поэтому не стал заострять на них внимание.
Но все же, version.dll не входит в список KnownDLLs (по крайней мере на ближайших доступных мне машинах). И инъекция отлично работает (на гитхабе выложены билды dll, опыт ставится легко).



Странно, инжектился в один процесс, как раз хотел через version.dll, но что-то пошло не так, и не получилось, и почему-то отложилось, что он по умолчанию в KnownDLLs… Странно. Ну да ладно. Проверил, вроде действительно нету.
Жесть. Process Monitor справляется с задачей гораздо проще и быстрее.
Process Monitor только найдет возможность загрузки, а dll он за нас не сделает.

Как уже написали выше, про KnownDLLs хорошо бы указать в самой статье, а это важное место с точки зрения защиты.


WinSxS redirect (aka “DotLocal”).

Это не "aka", а лишь один из способов. Не освещен другой механизм: ApiSetSchema — набор маршрутов загрузки dll по спец именам (типа api-ms-win-core-errorhandling-l1-1-0.dll), присутствующий в Win7 и выше. Тут хайджекинг не сработает.

Добавил небольшой апдейт в статью.

До сих пор встречаю инсталляторы, которые распаковываются в Temp и запускаются оттуда.


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


P.S.

И то, даже в этом случае будет состояние гонки, так что по-хорошему на эту папку нужно ещё и правильный дескриптор безопасности навесить. Естественно, это относится к ситуации, когда пользователь работает со включённым UAC в Admin Approval Mode, ведь в этом случае папка Temp является общей для программ, работающих с разными уровнями привилегий.

Да, вы правы. Из недавнего — такие уязвимости были у NVIDIA и, вроде, у какого-то продукта Cisco.
А про состояние гонки — TOCTOU часто можно выиграть используя OpLock.
Ещё немного занудства:
BOOL WINAPI SetDllDirectory(LPCTSTR lpPathName);

Добавляет произвольный путь к местам поиска DLL и меняет порядок (после папки с приложением поиск будет в папке, указанной в lpPathName); это влияет на все последующие вызовы LoadLibrary[Ex].
Опять-таки, на импортируемые библиотеки это не повлияет.
Как же не повлияет, если библиотека (или не она, а подставная) загрузится с другого места?

upd: Когда-то я так делал для BDE (IDAPI32.dll), теперь — для sqlite. В моих заголовочниках импорт статический, переписывать на динамический лень. Потому я просто заменил импорт на отложенный импорт, чтоб виндовый загрузчик не ругался, а при старте приложения читаю путь к DLL из реестра и передаю в SetDllDirectory. Можно, конечно, положить DLL в папку к каждому приложению (и не забывать их обновлять) или же нагадить в System32, но мне так больше нравится.
Библиотеки из таблицы импорта грузятся до того, как будет выполнен любой код программы, в том числе до того, как произойдёт вызов указанной выше функции.
Если это не отложенный импорт, тогда библиотека загрузится при первом обращении к любой из её функций. Механизмы импорта разные бывают, помимо стандартного есть ещё delayed (о котором я писал) и bound (при котором адреса в таблице импорта уже вычислены на этапе компиляции, применяется, когда вы поставляете весь набор приложений и библиотек одной кучей (операционка, например)).
Зарегистрируйтесь на Хабре, чтобы оставить комментарий