Вразливість CLR: як затягнути об'єкт в пісочницю без маршаллинга і викликати Callback

Добрий день! Сподіваюся я вже завоював на Хабре досягнення «дізнався автора по заголовку» -) Проте сьогодні мова піде про свіжою, ще не закритій уразливості .Net, на яку мене навів своєю думкою одна людина (хто підкине йому інвайт?), який написав мені на пошту:

Ви намагалися IL кодом приводити об'єкти до строковому типу і передавати в інші домени?


Спочатку я його не зрозумів, але потім народився приклад коду, який прокидає будь-який об'єкт, що знаходиться в SharedDomain в пісочницю і дозволяє використовувати його методи БЕЗ маршаллинга.

Дірою, з одного боку це назвати досить складно, тому що грунт для цього повинен підготувати хост. І не самим звичайним способом, треба помітити. Але з іншого боку… Так, це баг.

Перше, що нам знадобиться — це стали буденністю, методи EntityPtr.ToPointer ()*.ToInstance() з DotNetEx. Їх комбінація змушує приводити об'єкт до несумісного типу. Тобто до того типу, яким він не є:
string str = EntityPtr.ToInstance<string>(EntityPtr.ToPointer(new List<int>()));

Природно, якщо викликати будь-який метод у такий «рядки», нічого не вийде: вилетить Exception (крім віртуальних методів від object — їх позиції в таблиці вірт методів співпадуть з переопределенными в нашому тип і виклик відбудеться коректно)

Однак, оскільки рядок є вже сериализованным об'єктом, то при маршаллинге вона передається за посиланням, без копіювання. Це означає, що ми можемо прокинути її в метод, код якого виконується в «пісочниці» і там, всередині, зробити кастинг назад в тип.

Почну відразу з смачного:

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<Action> expression = () => Console.WriteLine("Surprise, motherf*ckers!");

AppDomainRunner.Go(EntityPtr.ToInstance<string>(EntityPtr.ToPointer(expression.Compile ())));

Console.ReadKey();
}
}

Виведе вітання на екран

Отже, що буде працювати:
  1. Проведення об'єктів будь-якого типу з кастингом всередині sandbox — тип з Shared Domain.
  2. Якщо викликати віртуальні методи базового класу, який знаходиться в Shared Domain і які ви переопределили на свої — вызовутся ваші
  3. Якщо ви передасте в якості Action — скомпільований Expression — також викличеться. Але при цьому викликається він також в sandbox, оскільки не відбудеться перемикання домену, який йде при маршаллинге.


не можна:
  1. Призводити до свого типу у sandbox, оскільки в пісочниці тип і все що з ним пов'язано прогружено вдруге і привести до такого ж типу не вийде: фізично описатели типів в різних доменах будуть за різними адресами, а значить вийде InvalidCastException.
  2. Action віддавати звичайні делегати на свій код. Делегати домен перевіряють, або виклик всередині домену йде по near jmp, а в доменів — різні селектори коду… загалом, виклик падає. Потрібно робити Expression.
  3. Навіть якщо приводити тип не треба (віддаємо List<CustomType>, Отримуємо list.First().DoSomething()), все одно нічого не працює. Треба працювати через базовий тип


На що впливає:
  • Власне кажучи, оскільки таке має підлаштувати хост, а в sandbox цього не зробити, цінність уразливості падає
  • Не викликати зовнішній код, щоб відбулося перемикання в хостирующий AppDomain. Прокинутый всередину делегат буде викликаний під тими ж правами. Хоча можна через ланцюжок зробити метод RunAsHost(() => ...), але про це — пізніше =)
  • Можна досить безпечно, без маршаллинга організувати швидкий виклик методів хоста з пісочниці. Іноді таке буває критичним. Наприклад, для перекидання котирувань


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.