Pull to refresh

Подписанные сборки .Net

Задумался я тут над смыслом подписания компоновочных блоков .Net. Наверняка Вы тоже подписывали свои библиотеки, что бы установить их в GAC.
В ходе расследования мы научимся изменять подписанные сборки, не обладая исходниками и секретными ключами.


Приватные сборки


При подписании библиотеки (назначении строгого имени) открытый ключ записывается в манифест. Таким образом, что бы внести изменения в чужую подписанную библиотеку, нужно просто заменить публичный ключ на свой. Или, еще проще, сделать новую сборку с таким же именем и подписать на своем ключе.
При использовании библиотеки любой публичный ключ будет принят как доверенный.

Проведем следующий эксперимент. Создадим небольшую библиотечку.

namespace signedLib
{
    public class sLib
    {
        public static int GetNumber() { return 1; }
    }
}
Листинг 1. Библиотека signedLib.dll


Подпишем её и добавим к проекту консольного приложения:

namespace changeKey
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(signedLib.sLib.GetNumber());
            Console.ReadLine();
        }
    }
}
Листинг 2. Консольное приложение changeKey.exe


Затем скомпилируем релиз проекта.
С помощью .NET Reflector и плагина Reflexil отредактируем IL код подписанной библиотеки (signedLib.dll), так что GetNumber() будет возвращать не «1», а «2». Консольное приложение не заметило подмены и вывело «2».

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

Обращаю внимание что речь идет именно о приватных сборках, со сборками в GAC дело обстоит иначе.
Приватные это те которые не в GAC.

Сборки в GAC


Как мы только что убедились подписанные приватные компоновочные блоки можно легко модифицировать. При этом не обязательно обладать ни исходниками, ни секретным ключом, ни правами администратора.
Вносить изменения в сборки установленные в GAC не многим сложнее.

В случаи с приватными сборками не играет ни какой роли подписаны они или нет. Подпись не проверяется, а «полный идентификатор приватного компоновочного блока состоит из имени компоновочного блока и числового номера его версии» (из книги Э. Троелсена).

Сборки устанавливаемые в GAC должны иметь так называемое строгое имя. Сборка получает строгое имя как только вы её подписываете. Идентификаторы сборок в GAC дополняются параметрами публичного ключа, подписи проверяются.

Окружили демоны:
  • незаметно внести изменения не получится — подпись проверку не пройдет
  • свой публичный ключ не подсунешь — идентификатор сборки изменится


Но не нужно быть криптографом что бы все же изменить библиотеку в GAC, а нужно обладать правами администратора и знать параметры утилиты sn.exe.
Страдальцы не имеющие студии вручную используют стандартную утилиту sn.exe для подписания компоновочных блоков.

Итак, возьмем проект уже знакомой библиотеки signedLib.dll (см. листинг 1). подпишем её и установим в GAC.

gacutil.exe /i D:\projects\changeKey\signedLib\bin\Release\signedLib.dll

Добавим референс к консольному приложению changeKey.exe (см. листинг 2). Компилируем релиз, убеждаемся что в папке с программой нет файла signedLib.dll (значит сборка будет загружена из GAC). Запускаем changeKey.exe — приложение показывает «1».

С этого момента воображаем себя атакующими — у нас нет исходников, нет секретного ключа. Но нам надо что бы метод GetNumber() возвращал не 1 а 2.

Структуру файлов ниже C:\Windows\assembly проводник windows не показывает. Создадим псевдодиск на который будет проецироваться нужный каталог:

subst b: C:\Windows\assembly

В проводнике появился диск B.

Папки ниже C:\Windows\assembly
рис. 1. Папки ниже C:\Windows\assembly


.Net сборки попадают в папку GAC_MSIL, находим нужную папку (её название совпадает с названием .dll файла). Внутри будет еще одна папка, а в ней наконец signedLib.dll. Копируем signedLib.dll на рабочий стол.

С помощью замечательной программы .NET Reflector и не менее замечательного плагина Reflexil будем редактировать библиотеку. Предварительно перепишем токен публичного ключа и его значение в блокнот (они нам пригодятся позже). Как мы уже знаем публичный ключ записан в самой сборке, теперь в этом можно окончательно убедиться.

Параметры публичного ключа
рис. 2. Параметры публичного ключа


После правки IL кода и сохранения изменений, программа сообщит о том что цифровая подпись нарушена и предложит варианты дальнейших действий:

image

Нажимаем «Remove Strong Name» — удалить цифровую подпись. Закрываем сборку.
Теоретически закрывать сборку нет необходимости и нам должен подойти вариант «Register it for verification skipping». Однако у меня эта операция заканчивается ошибкой. К тому же в обучающих целях лучше проделать все операции вручную.

Теперь у нас есть:
  • измененная, не подписанная dll библиотека;
  • публичный ключ оригинальной библиотеки.


Осталось установить её в GAC. Для этого воспользуемся механизмом отложенной подписи. Если сборка содержит информацию о публичном ключе, но не имеет цифровой подписи — говорят что она имеет отложенную подпись. Придумал это какой-то извращенный мозг из микрософт «для тестирования».

Сделать такую сборку с помощью .NET Reflector не составляет никакой сложности — нужно заполнить соответствующие поля, они выделены желтым на рис.2. (Помните мы копировали их значения в блокнот?). И не забудьте поставить галочку «HasPublicKey».
В теории публичный ключ нужно извлекать из секретного с помощью утилиты sn.exe, и потом с помощью неё же создавать отложенную подпись.

Мы получили сборку которая называется так же как оригинальная, имеет такую же версию и такой же публичный ключ. Т.е. если, её установить в GAC она получит точно такой же идентификатор как и оригинальная (см. начало начало статьи). Как я писал выше, по умолчанию у сборок в GAC проверяется подпись, однако проверку подписи можно отключить — опять же «для тестирования».

Что бы отключить проверку подписи dll на данном компьютере нужно воспользоваться sn.exe

sn -Vr C:\Users\Alex\Desktop\signedLib.dll

Удаляем оригинальную сборку из GAC:

gacutil /u signedLib,Version=1.0.0.0,Culture=neutral,PublicKeyToken=2b1b71846e76146e

устанавливаем измененную:

gacutil /i C:\Users\Alex\Desktop\signedLib.dll

Радуемся, глядя на выведенную gacutil.exe надпись:

Assembly successfully added to the cache

Вот мы и добились желаемого — изменили библиотеку установленную в GAC. Что бы еще раз порадоваться (и проверить результат) запускаем наше приложение changeKey.exe, которое в начале статьи выводило 1, теперь он покажет 2.

Подведем итог


Публичный ключ записан в самой сборке (точнее в манифесте);
В случаи с приватными сборками подписи не проверяются.

Что бы изменить сборку в CAG нужно:
  • Сделать копию нужного dll файла из C:\Windows\assembly (воспользовавшись командой subst)
  • Извлечь из сборки публичный ключ
  • Модифицировать IL код сборки и удалить цифровую подпись
  • Добавить к измененному файлу публичный ключ, полученный на шаге 2 (создадим отложенную подпись)
  • Отменить проверку цифровой подписи для модифицированной сборки на данном компьютере
  • Удалить оригинальную сборку из GAC
  • Установить модифицированную сборку.


Для шагов 5, 6, 7 нужно обладать правами администратора.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.