You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Express/MongoDB项目的CTF场景中用Axios传递会话Cookie

CTF CSRF漏洞项目:Cookie携带失败问题

需求背景

正在做一个包含CSRF漏洞的CTF项目,核心逻辑是:用户向后端提交指定URL后,后端要以support用户的身份(携带其会话Cookie)向该URL发起请求,以此模拟CSRF攻击场景。

现有代码(已移除无关部分)

const express = require('express')
const session = require('express-session')
const MongoDBSession = require('connect-mongodb-session')(session)
const axios = require('axios')
const mongoose = require('mongoose')
const app = express()

const CreateDefaultUsers = () => {
    try {
        User.create({
            username: "guest",
            password: "guest"
        })
        User.create({
            username: "support",
            password: "639Mydd&ou6Y"
        })
        console.log("User Created...")
    } catch (err) {
        console.log("Error creating users")
    }
}

const db = mongoose.connection
db.on('error', (error) => console.log(error))
db.once('open', () => {
    console.log('Connected to Database')
    CreateDefaultUsers()
})

const store = new MongoDBSession({
    uri: mongoURI,
    collection: 'csrf-sessions'
})

app.use(session({
    name: "SESS_COOKIE",
    secret: 'secret',
    resave: false,
    saveUninitialized: false,
    store: store,
    cookie: {
        maxAge: 1000 * 60 * 60 * 24, 
        secure: false, 
        httpOnly: false, 
        sameSite: false
    }
}))

app.post('/api/login-support', async (req, res) => {
    console.log(req.session)
    const user = await User.findOne({
        username: req.body.username,
        password: req.body.password
    })
    if (user) {
        req.session.userId = user._id.toString();
        res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000")
        res.send(req.session)
    } else {
        res.status(403).json({ status: "Invalid Login" });
    }
})

app.post('/api/support', async (req, res) => {
    axios.post('http://localhost:5000/api/login-support', { username: "support", password: "639Mydd&ou6Y" }, { withCredentials: true })
        .then((data) => axios.get(req.body.url, { headers: { Cookie: data } }, { withCredentials: true })
            .then((data) => console.log(data)))
})

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

问题分析

当前请求无法携带support用户Cookie的原因:

  • Cookie获取错误:axios.post返回的data是接口返回的session对象,不是响应头里的Set-Cookie字段,直接把它作为Cookie值完全无效。
  • Axios配置错误:第二个请求的withCredentials和headers分属不同配置对象,Axios不会识别,应该合并到同一个配置里。
  • 会话管理冗余:每次收到/api/support请求都重新登录support用户,没必要且容易出现会话不一致问题。

修复方案(修正Cookie携带逻辑)

修改/api/support接口代码,正确提取并携带Cookie:

app.post('/api/support', async (req, res) => {
    try {
        // 登录support用户,获取会话Cookie
        const loginRes = await axios.post(
            'http://localhost:5000/api/login-support', 
            { username: "support", password: "639Mydd&ou6Y" },
            { withCredentials: true }
        )
        // 从响应头提取会话Cookie(只取键值对部分)
        const sessionCookie = loginRes.headers['set-cookie']?.find(c => c.startsWith('SESS_COOKIE='))?.split(';')[0]
        if (!sessionCookie) {
            console.log("Failed to get support session cookie")
            return res.status(500).send("Internal error")
        }
        // 携带Cookie请求用户指定URL
        const targetRes = await axios.get(req.body.url, {
            headers: { Cookie: sessionCookie },
            withCredentials: true
        })
        console.log(targetRes.data)
        res.send("Request sent successfully")
    } catch (err) {
        console.error(err)
        res.status(500).send("Request failed")
    }
})

更优实现思路

如果想要更贴近真实CSRF场景(模拟用户浏览器行为),推荐使用Puppeteer(无头浏览器)替代Axios:

  • 无头浏览器会自动处理会话Cookie,无需手动提取设置,完全符合真实用户的浏览器行为。
  • 可模拟点击、表单提交等操作,适合构造复杂CSRF payload场景。

示例代码片段:

const puppeteer = require('puppeteer')

app.post('/api/support', async (req, res) => {
    try {
        const browser = await puppeteer.launch({ headless: true })
        const page = await browser.newPage()
        // 登录support用户,浏览器自动保存Cookie
        await page.goto('http://localhost:5000/login') // 假设存在登录页面
        await page.type('#username', 'support')
        await page.type('#password', '639Mydd&ou6Y')
        await page.click('#login-btn')
        await page.waitForNavigation()
        // 访问用户指定URL,自动携带会话Cookie
        await page.goto(req.body.url)
        const content = await page.content()
        console.log(content)
        await browser.close()
        res.send("Request sent successfully")
    } catch (err) {
        console.error(err)
        res.status(500).send("Request failed")
    }
})

另外可优化会话管理:服务器启动时提前登录support用户并保存其会话Cookie,后续请求直接复用,避免重复登录提升效率。

内容的提问来源于stack exchange,提问作者Josh Morrison

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 14:46:02