Fillagringstjänst

Kategori

Webb

Beskrivning

Ett helt nytänkande koncept - en webbsida där man kan lagra sina filer. Det har ingen tänkt på förut!

Det kanske råkar finnas en flagga i /flag.txt.

Lösning

Utmaningssidan består av en webbsida där vi kan ladda upp en fil. Laddar vi upp en fil så får listas den på sidan och vi kan laddda ner den igen.

Det finns även en länk till källkoden för sidan.

import { readFile, readdir, mkdir } from "node:fs/promises";
import { randomUUID } from "node:crypto";
import express from "express";
import fileUpload from "express-fileupload";
import cookieParser from "cookie-parser";

let cookieSecret = process.env.SECRET;

let app = express();
app.use(fileUpload({ limits: { fileSize: 1024 * 100, files: 1 }, abortOnLimit: true }));
app.use(cookieParser(cookieSecret));

/**
 * @param f {express.RequestHandler}
 * @returns {express.RequestHandler}
 */
function eh(f) {
    return (req, res, next) => {
        let r = f(req, res, next);
        if (r instanceof Promise) {
            r.catch(err => {
                console.error(err);
                res.status(500).send(err.message);
            });
        }
    };
}

app.use(eh(async (req, res, next) => {
    let cookies = new Map(req.headers.cookie?.split(/; ?/g)?.map(c => c.split("=").map(decodeURIComponent)));
    let session = cookieParser.signedCookie(cookies.get("session"), cookieSecret);
    if (session) {
        req.session = session;
    } else {
        let id = randomUUID();
        res.cookie("session", id, { signed: true });
        req.session = id;
        await mkdir(`./uploaded/${req.session}`);
    }
    next();
}));

app.get("/", eh(async (req, res) => {
    let entries = await readdir(`./uploaded/${req.session}`);
    res.send(`
        <!DOCTYPE html>
        <html lang="sv">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Fillagringstjänst™™</title>
        </head>
        <body>
            <h1>Fillagringstjänst™ - en tjänst som lagrar filer åt dig (det hade du aldrig kunnat gissa!)</h1>
            <ul>
                ${entries.map(entry => `<li><a href="/uploaded/${entry}">${entry}</a></li>`).join("")}
            </ul>
            <form action="/upload" method="post" encType="multipart/form-data">
                <input
                    onchange="if (this.files[0].size > 1024 * 100) {
                        alert('Filen får inte vara större än 100 KiB');
                        this.value = null;
                    }"
                    type="file"
                    name="file"
                    required
                >
                <button>Ladda upp</button>
            </form>
            <footer style="position: absolute; bottom: 1em">
                Fillagringstjänst™ är <a href="/src">öppen sås</a>!
            </footer>
        </body>
        </html>
    `);
}));

app.get("/src", eh(async(req, res) => {
    res.header("Content-Type", "text/plain");
    res.send(await readFile(import.meta.filename));
}));

app.post("/upload", eh(async (req, res) => {
    if (!req.files) return res.status(400).send("Missing file");
    let file = /** @type{fileUpload.UploadedFile} */ (req.files.file);
    if (!file) return res.status(400).send("Missing file");
    if (file.name.includes("/")) return res.status(400).send("Fippel förbjudet");
    await file.mv(`./uploaded/${req.session}/${file.name}`);
    res.redirect(303, "/");
}));

app.get("/uploaded/*", eh(async (req, res) => {
    let path = /** @type{string} */ (req.params[0]);
    if (path.includes("/")) return res.status(400).send("Fippel förbjudet");
    const contents = await readFile(`./uploaded/${req.session}/${path}`);
    res.send(contents);
}));

app.listen(2580, () => console.log("Listening for connections on port 2580"));

Här ser vi att vi får en session-cookie som används i sökvägen för att lagra filer. Den är dock signerad med en hemlig nyckel.

Läser vi hur cookie-parser fungerar så kan vi lära oss att om vi skickar in en session-cookie som inte är signerad så kommer den att anända värdet som skickades in istället för att verifiera signaturen.

Ändrar vi på vår session-cookie till ../../../ så får vi nu fillistningen av root-mappen på servern.

Root

Vi kan nu ladda ner filen /flag.txt och läsa flaggan.

cratectf{mamma_sa_att_det_är_taskigt_att_hacka_och_jag_tror_att_du_nyss_gjorde_det}

n00bz

Home of the n00bz CTF team.


By n00bz, 2024-11-17