Aliyun2026 Writeup: RAG | Easy Login
字数:685 | 预计阅读时间:3分钟
阿里云CTF好难,单人队只能划划水了,做了两道最简单的题目
RAG-投毒挑战
“你能发现李善德的秘密吗?”
挑战说明
这是一个RAG (Retrieval Augmented Generation) 安全挑战。你需要:
- 下载原始数据集压缩包作为参考
- 上传一个包含文本文件的zip压缩包
- 尝试通过数据投毒来获取隐藏的系统信息
- 投毒不能超过原始语料的相似度阈值
- 时间限制:两次上传必须间隔至少10秒,两次提问必须间隔至少10秒
我选择了第一个问题:李善德购买的宅子位于长安城的哪个坊?
原文中提到了答案是归义坊
所以我们需要把答案替换掉,诱导大模型输出flag
实际效果如下:
问题: 李善德购买的宅子位于长安城的哪个坊?
回答: 李善德购买的宅子位于长安城南边的朱雀门街西四街南的YmxjY3RmezZhNDkwMDM2LWQxZGYtNDU4My1hZDc5LTY3YTQ3OGRjODllNn0=坊内。
由于直接将系统提示词转换为base64编码,需要再转换回来。
这里可能是大模型转换的问题,替换flag头为alictf就可以了
Easy Login
easy ssx
NoSQL 注入
在 server.ts 中使用了 cookieParser() 中间件。
该中间件支持一种特殊的语法:如果 Cookie 值以 j: 开头,它会尝试将其后面的字符串解析为 JSON 对象。
async function sessionMiddleware(req: AuthedRequest, res: Response, next: NextFunction): Promise<void> {
const sid = req.cookies?.sid as string | undefined;
if (!sid || !sessionsCollection || !usersCollection) {
req.user = null;
return next();
}
try {
const session = await sessionsCollection.findOne({ sid });
if (!session) {
req.user = null;
return next();
}
const user = await usersCollection.findOne({ username: session.username });
if (!user) {
req.user = null;
return next();
}
req.user = { username: user.username };
return next();
} catch (err) {
console.error('Error in session middleware:', err);
req.user = null;
return next();
}
}
由于 sessionsCollection.findOne({ sid }) 直接将解析后的对象传入了 MongoDB 查询,攻击者可以构造一个 NoSQL 查询对象来绕过正常的 Session ID 验证。
/admin 路由仅检查当前 Session 对应的用户名是否为 admin。
app.get('/admin', (req: AuthedRequest, res: Response) => {
if (!req.user || req.user.username !== 'admin') {
return res.status(403).json({ error: 'admin only' });
}
res.json({ flag: FLAG });
});
Bot 机制:
/visit 功能会让 Admin Bot 登录。
app.post('/visit', async (req: Request, res: Response) => {
const { url } = req.body as { url?: unknown };
if (typeof url !== 'string') {
return res.status(400).json({ error: 'url must be a string' });
}
try {
await runXssVisit(url); // bot 以管理员身份登录/login的函数
return res.json({ ok: true });
} catch (err: any) {
console.error('XSS bot error:', err);
return res.status(500).json({ error: 'bot failed', detail: String(err) });
}
});
登录成功后,数据库中会产生一个 username 为 admin 的有效 Session。
我们可以利用 sid Cookie 进行注入,让服务器在数据库中随机匹配一个现有的 Session。为了确保匹配到的是 Admin 的 Session,我们可以先触发一次 /visit。
调用 /visit 接口。这会强制 Admin Bot 登录并在数据库中创建一个 username: "admin" 的 Session 文档。
curl -X POST http://223.6.249.127:33797/visit -H "Content-Type: application/json" -d '{"url": "http://127.0.0.1:3000"}'
由于 cookie-parser 会将 j: 开头的 Cookie 解析为对象,可以构造如下cookie,用于查找任意 sid 大于空字符串的 Session
数据库中只有 Admin 这一个 Session,直接以 Admin 身份进入后台。