Уязвимость CLR: как затащить объект в песочницу без маршаллинга и вызвать Callback
Добрый день! Надеюсь, я уже завоевал на Хабре достижение «узнал автора по заголовку» -) Однако сегодня речь пойдет о свежей, еще не закрытой уязвимости в .Net, на которую меня навел своей мыслью один человек (кто подкинет ему инвайт?), который написал мне на почту: Вы пытались IL кодом приводить объекты к строковому типу и передавать в другие домены?
Сначала я его не понял, но потом родился пример кода, который пробрасывает любой объект типа, находящегося в SharedDomain в песочницу и позволяет использовать его методы БЕЗ маршаллинга.
Дырой, с одной стороны это назвать достаточно сложно, т.к. почву для этого должен подготовить хост. И не самым обычным способом, надо заметить. Но с другой стороны… Да, это баг.
Первое, что нам понадобится — это ставшие обыденностью, методы EntityPtr.ToPointer () и *.ToInstance () из DotNetEx. Их комбинация заставляет приводить объект к несовместимому типу. Т.е. к тому типу, которым он не является:
string str = EntityPtr.ToInstance
private void methodInsideAppDomain (string str) { object tmp = str; var act = (Action)tmp; act (); }
public static void Go (string startingIntPtr) { // make appdomain var permissions = new PermissionSet (PermissionState.None); permissions.AddPermission (new SecurityPermission (SecurityPermissionFlag.Execution)); var dom = AppDomain.CreateDomain («PseudoIsolated», null, new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory }, permissions);
// create object instance var asmname = typeof (AppDomainRunner).Assembly.FullName; var typename = typeof (AppDomainRunner).FullName; var instance = (AppDomainRunner)dom.CreateInstanceAndUnwrap (asmname, typename);
// enumerate objects from outside area to our appdomain area instance.methodInsideAppDomain (startingIntPtr); } }
class Program
{
static void Main (string[] args)
{
Expression
AppDomainRunner.Go (EntityPtr.ToInstance
Console.ReadKey (); } } Выведет приветствие на экранИтак, что будет работать:
Протаскивание объектов любого типа с кастингом внутри sandbox — в тип из Shared Domain. Если вызывать виртуальные методы базового класса, который находится в Shared Domain и которые вы переопределили на свои — вызовутся ваши Если вы передадите в качестве Action — скомпилированный Expression — также вызовется. Но при этом вызовется он также в sandbox, поскольку не произойдет переключения домена, который идет при маршаллинге. Что нельзя:
Приводить к своему типу в sandbox, поскольку в песочнице тип и все что с ним связано прогружено во второй раз и привести к такому же типу не получится: физически описатели типов в разных доменах будут по разным адресам, а значит получится InvalidCastException. В качестве Action отдавать обычные делегаты на свой код. Делегаты либо домен проверяют, либо вызов внутри домена идет по near jmp, а у доменов — разные селекторы кода… В общем, вызов падает. Нужно делать Expression. Даже если приводить тип не надо (отдаем List<CustomType>, Получаем list.First ().DoSomething ()), все равно ничего не работает. Надо работать через базовый тип На что влияет:
Собственно говоря, поскольку такое должен подстроить хост, а в sandbox этого не сделать, ценность уязвимости падает Не вызвать внешний код, чтобы произошло переключение в хостирующий AppDomain. Прокинутый вовнутрь делегат будет вызван под теми же правами. Хотя можно через цепочку сделать метод RunAsHost (() => …), но об этом — позже =) Можно довольно безопасно, без маршаллинга организовать быстрый вызов методов хоста из песочницы. Иногда такое бывает критичным. Например, для проброса котировок