Рады видеть вас на Ghost-Club

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

Rolllik

Местные
  • Content Count

    8
  • Joined

  • Last visited

  • Days Won

    1
1

Community Reputation

1 Обычный

About Rolllik

  • Birthday 06/17/1988

Информация

  • Пол
    Не определился

Контакты

  • Skype
    Ruiop51
  1. инвестиции тут большие нужны, а выхлопа не будет особого
  2. В рамках данной статьи мы достаточно подробно рассмотрим процесс написания эксплоита под уязвимость в Microsoft Edge, с последующим выходом из песочницы. Если вам интересно узнать, как выглядит этот процесс, то welcome под кат! Введение На последнем Pwn2Own 2019 в Монреале в категории браузеры был продемонстрирован эксплоит для взлома Microsoft Edge. Для этого было использовано две уязвимсоти: double free в ренедере и логическая уязвимость для выхода из песочницы. Эти две уязвимости были недавно закрыты и присвоены соотвествующие CVE: CVE-2019-0940 и CVE-2019-0938. Подробнее о уязвимостях можно прочить в блоге: Pwn2Own 2019: Microsoft Edge Renderer Exploitation (CVE-2019-0940). Part 1 и Pwn2Own 2019: Microsoft Eedge Sandbox Escape (CVE-2019-0938). Part 2. В рамках нашей статьи мы хотим показать процесс написания подобного эксплоита и то, сколько нужно времени и ресурсов на это на примере того же Microsoft Edge на Windows 10 с помощью CVE-2017-0240 и CVE-2016-3309. Одним из отличий будет то, что, если в эксплоите продемонстрированном на Pwn2Own, для выхода из песочницы использовалась логическая уязвимость, то в нашем сценарии для выхода из песочницы будет использована уязвимость в ядре Windows 10. Как показывают патчи от Microsoft, уязвимостей в ядре находится куда больше, чем уязвимостей в реализации песочницы. В итоге, такую цепочку уязвимостей встретить намного вероятнее, и её будет полезно знать сотрудникам ИБ в компаниях. Исходные данные В данной статье будет рассмотрен процесс написания 1-day эксплойта для браузера Microsoft Edge. Будет эксплуатироваться CVE-2017-0240. Первый этап эксплуатации будет производиться на основе материалов из источника [1], мы получим arbitrary address read/write примитив, а также познакомимся с различными техниками, которые могут быть полезны при эксплуатации подобных уязвимостей. Далее будет знакомство с инструментом pwn.js, который поможет получить вызов произвольных функций на основе произвольного чтения и записи, также будут рассмотрены различные mitigations и способы их обхода. На последнем этапе будет произведена эксплуатация уязвимости ядра Windows CVE-2016-3309 для повышения привилегий, обхода ограничений AppContainer и получения полного контроля над атакуемой машиной. Эксплуатация будет проводиться на стенде c ОС Microsoft Windows 10 Pro 1703 (10.0.15063) и браузером Microsoft Edge (40.15063.0.0). Шаг 1. Получение arbitrary address read/write примитива Описание уязвимости и получение OOB Уязвимость типа use-after-free присутствует в методе copyFromChannel объекта AudioBuffer. AudioBuffer — это интерфейс короткого звукового ресурса (audio asset), находящегося в памяти и созданного из аудиофайла методом AudioContext.decodeAudioData(), или из исходных данных с помощью метода AudioContext.createBuffer(). Помещенные в AudioBuffer звуковые данные могут быть воспроизведены в AudioBufferSourceNode. В презентации The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10 приведен подробный анализ уязвимости и патча. При вызове метода copyFromChannel происходит копирование содержимого канала аудио-буфера в буфер destination, указанный первым аргументом. Метод также принимает номер канала (channelNumber) и смещение в аудио-буфере (startInChannel), начиная с которого необходимо производить копирование. Перед непосредственным копированием данных в destination, в функции CDOMAudioBuffer::Var_copyFromChannel происходит кеширование буфера destination (адрес и размер буфера сохраняются в локальные переменные функции на стеке) и преобразование значений объектов channelNumber и startInChannel к типу Int, для чего происходит вызов метода valueOf преобразуемых объектов. Уязвимость заключается в том, что закешированный буфер может быть освобожден в момент преобразования типов в переопределенном методе valueOf объекта. Для проверки воспользуемся следующим кодом: буфер, который впоследствии будет освобожден var t2 = new Float32Array(0x20000); var ta = new Uint8Array(t2.buffer); for (i=0;i<t2.length;i++) t2 = 0x66; var myctx = new AudioContext(); var audioBuf = myctx.createBuffer(1, 0x25, 22050); // заполним содержимое аудио-буфера для наглядности var t = audioBuf.getChannelData(0); var ta2 = new Uint8Array(t.buffer); for(i=0;i<ta2.length;i++) ta2=0x55; // создаем объект с переопределенным valueOf var obj = { valueOf: function () { // освобождаем буфер var worker = new Worker('worker.js'); worker.postMessage(0, [t2.buffer]); worker.terminate(); worker = null; // ожидание освобождения буфера sleep(1000); return 0; } }; // триггерим уязвимость audioBuf.copyFromChannel(t2, obj, 0); ля освобождения буфера в данном коде используется технология Web Workers. Создав пустой Worker, мы можем отправить ему сообщение с помощью метода postMessage. Второй необязательный аргумент transfer данного метода принимает массив Transferable-объектов (ArrayBuffer, MessagePost или ImageBitmap), права на объект будут переданы в Worker и объект более не будет доступен в текущем контексте, благодаря чему может быть удален. После этого происходит вызов sleep — функция, обеспечивающая временную остановку выполнения программы (реализуется самостоятельно). Это необходимо для того, чтобы система сборки мусора (GC, Garbage Collector) успела освободить буфер, права на который были переданы. Web Worker-ы предоставляют простое средство для запуска скриптов в фоновом потоке. Поток Worker'а может выполнять задачи без вмешательства в пользовательский интерфейс. К тому же, они могут осуществлять ввод/вывод, используя XMLHttpRequest (хотя атрибуты responseXML и channel всегда будут равны null). Существующий Worker может отсылать сообщения JavaScript коду-создателю через обработчик событий, указанный этим кодом (и наоборот). Запустив данный код в Edge под отладчиком, можно получить следующее падение. В результате вызов copyFromChannel пытается копировать содержимое аудио-буфера в неалоцированную область памяти. Для эксплуатации уязвимости необходимо добиться выделения в этой области памяти каких-либо объектов. В данном случае отлично подойдет сегмент массива. Массивы в Chakra (JS-движок, используеммый в браузере Edge) устроены следующим образом: объект массива имеет фиксированный размер, сами указатели на объекты массива (или значения, в случае IntArray) хранятся в отдельной области памяти — сегменте, указатель на который содержится в объекте массива. Заголовок сегмента содержит различную информацию, в том числе размер сегмента, который соответствует размеру массива. Размер массива также присутствует и в самом объекте массива. Схематично это выглядит следующим образом: Таким образом, если нам удастся выделить сегмент массива в ранее освобожденном пространстве, то мы сможем перезаписать заголовок сегмента массива содержимым аудио-буфера. Для этого модифицируем код выше, добавив в следующие строки после sleep(1000);: ... /* Для повышения вероятности захватить интересующий нас сегмент массива, создаем большое их количество. Глобальный массив arr необходим для дальнейшего обращения созданным далее массивам */ arr = new Array(128); for(var i = 0; i < arr.length; i++) { arr = new Array(0x3ff0); for(var j = 0; j < arr.length; j++) arr[j] = 0x30303030; } .. Размер массивов подобран таким образом, чтобы размер сегмента массива занимал целый сегмент кучи (минимальный неделимый участок памяти кучи, размер которого — 0x10000 байт). Запустим данный код, указав в качестве точки остонова функцию memcpy (вызовов memcpy будет много, поэтому имеет смысл остановиться сначала на edgehtml!WebCore::AudioBufferData::copyBufferData), в которой происходило падение. Получим следующий результат: Отлично! Теперь мы можем перезаписать заголовок сегмента массива собственными значениями. Наиболее интересные значения в данном случае — размер массива, смещение которого мы можем увидеть на скриншоте выше. Изменим содержимое аудио-буфера следующим образом: ... var t = audioBuf.getChannelData(0); var ta2 = new Uint32Array(t.buffer); ta2[0] = 0; ta2[1] = 0; ta2[2] = 0xffe0; ta2[3] = 0; ta2[4] = 0; ta2[5] = 0; ta2[6] = 0xfba6; ta2[7] = 0; ta2[8] = 0; ta2[9] = 0x7fffffff - 2; ta2[10] = 0x7fffffff; ta2[11] = 0; ta2[12] = 0; ta2[13] = 0; ta2[14] = 0x40404040; ta2[15] = 0x50505050; ... Обратите внимание на значения ta2[14] и ta2[15] — они уже относятся не к заголовку сегмента, а к самим значениям массива. С помощью этого мы сможем определить нужный нам массив в глобальном массиве arr следующим образом: for(var i = 0; i < arr.length; i++) { if(arr[0] == 0x40404040 && arr[1] == 0x50505050) { alert('Target array idx: ' + i); target_idx = i; target_arr = arr; break; } } Если в результате был обнаружен массив, первые два элемента которого были изменены определенным образом, то всё отлично. Теперь мы имеем массив, размер сегмента которого больше, чем он есть на самом деле. Остальные массивы при этом можно освободить. Тут необходимо вспомнить о том, что размер массива существует в двух сущностях: в объекте массива, где он остался не измененным, и в сегменте массива, где мы его увеличили. Оказывается, что размер в объекте массива игнорируется, если код исполняется в JIT-режиме и был оптимизирован. Этого легко добиться, например, следующим образом: function arr_get(idx) { return target_arr[idx]; } function arr_set(idx, val) { target_arr[idx] = val; { for(var i = 0; i < 0x3ff0; i++) { arr_set(i, arr_get(i)); } После этого, с помощью функций arr_get и arr_set можно обращаться за границы массива (OOB, out-of-bound). Использование OOB для получения примитива чтения и записи по произвольному адресу В данном разделе рассмотрим технику, позволяющую добиться чтения и записи по произвольному адресу с помощью OOB. Метод, с помощью которого мы это получим, будет похож на тот, что используется в источнике [1], но будут также и значительные изменения. В используемой версии Edge блоки памяти для кучи выделяются последовательно, благодаря чему при выделении большого количества каких-либо объектов рано или поздно они окажутся после сегмента массива, за границы которого мы можем обращаться. Во-первых, это дает нам возможность считать указатель на виртуальную таблицу методов объектов (vftable), благодаря чему можно обойти рандомизацию адресного пространства процесса (ASLR). Но доступ к каким объектам поможет нам добиться произвольного чтения и записи? Для этого отлично подходит пара объектов DataView. Представление DataView предоставляет низкоуровневый интерфейс для чтения и записи многочисленных числовых типов в бинарном ArrayBuffer, независимо от порядка байтов платформы. Внутренняя структура DataView содержит указатель на буфер. Для чтения и записи по произвольному адресу мы, например, можем построить цепочку из двух DataView (dv1 и dv2) следующим образом: в качестве буфера dv1 указываем адрес dv2 с помощью обращения за пределы массива. Теперь с помощью dv1 мы можем изменять адрес буфера dv2, за счет чего и достигается произвольные чтение и запись. Схематично это можно изобразить следующим образом: Чтобы воспользоваться данным методом, необходимо научиться определять адреса объектов в памяти. Для этого существует следующая техника: необходимо создать новый Array, с помощью OOB сохранить его vftable и typeId (первые два 64-битных поля структуры) и присвоить первому элементу массива объект, адрес которого нас интересует. Затем, необходимо восстановить ранее сохраненные значения vftable и typeId. Теперь младшее и старшее двойное слово адреса объекта можно получить, обратившись к первому и второму элементу массива. Дело в том, что по умолчанию новый массив является IntArray, и в его сегменте хранятся 4-байтовые значения массива как они есть. При присвоении массиву объекта происходит преобразование массива в ObjectArray, и его сегмент используется для хранения адресов объектов. При преобразовании изменяются vftable и typeId. Соответственно, если мы восстановим исходные значения vftable и typeId, через элементы этого массива мы сможем обращаться напрямую к сегменту. Схематично описанный процесс можно изобразить следующим образом: Открытым вопросом остается создание необходимых объектов и поиск их с помощью OOB. Как было сказано ранее, при выделении большого количества объектов, рано или поздно они начнут выделяться после сегменты массива, за границы которого мы можем обращаться. Чтобы найти нужные объекты, необходимо просто пройтись по индексам за пределами массива в поиске нужных объектов. Т.к. все объекты одного типа располагаются в одном сегменте кучи, можно оптимизировать поиск и идти по сегментам кучи с шагом 0x10000, а проверять только несколько первых значений с начала каждого сегмента кучи. Чтобы идентифицировать объекты, можно установить им уникальные значения каких-либо параметров (например, для DataView это может быть byteOffset) или, используя уже известные константы в структуре объекта (например, в используемой версии Edge в IntArray по смещению 0x18 всегда находится значение 0x10005). Объединив все вышеописанные приемы, можно получить чтение и запись по произвольному адресу. Ниже представлен скриншот с чтением памяти объкетов DataView. Продолжение следует…
  3. у меня так знакомый на 20 к попал хотя мужик не глупый и не из доверчивых!
  4. Всем в полне доволен переводы приходят по разному но не разу не было что бы не дошло спасибо за твой качественный сервис😎