🚩 HTB - Secret Alley
Executive Summary
- OS: Linux / Windows
- Key Technique:
views/package.jsonfile revealed that the website is usingnode-fetchversion2.6.6which is vulnerable to CVE-2022-0235,node-fetch()request method will send all request headers (including the sensitive authentication headers such as Authorization, Cookie, etc.) to along with a redirection request. Further enumeration revealed that the/thinkendpoint will display its request header to the body. Thedefault.conffile revealed that the/guardianendpoint is hosted on another virtual host that send requests to the URL inside itsquoteparameter. By pointing thequoteto the endpoint/thinkwe can get the flag inside the request header. - Status:
Completed
Reconnaissance
Configurations

Inspecting the Dockerfile we can see that there are two secret value inside that is the SECRET_ALLEY and the FLAG. The SECRET_ALLEY is used as the suffix for the virtual hosts’ names.
![[Pasted image 20260328043850.png#center]
Routers
The routes/guardian.js file introduces three endpoints for us.
/alley
router.get("/alley", async (_, res) => {
res.render("index");
});Return the index page, inside the index page there is a button that leads us to the /guardian endpoint

/think
router.get("/think", async (req, res) => {
return res.json(req.headers);
});Return the GET request headers
/guardian
router.get("/guardian", async (req, res) => {
const quote = req.query.quote;
if (!quote) return res.render("guardian");
try {
const location = new URL(quote);
const direction = location.hostname;
if (!direction.endsWith("localhost") && direction !== "localhost")
return res.send("guardian", {
error: "You are forbidden from talking with me.",
});
} catch (error) {
return res.render("guardian", { error: "My brain circuits are mad." });
}
try {
// uses the vulnerable node-fetch() to send request to the URL
let result = await node_fetch(quote, {
method: "GET",
headers: { Key: process.env.FLAG || "HTB{REDACTED}" },
}).then((res) => res.text());
res.set("Content-Type", "text/plain");
res.send(result);
} catch (e) {
console.error(e);
return res.render("guardian", {
error: "The words are lost in my circuits",
});
}
});The /guardian endpoint takes a parameter called quote and send the request to that URL, with the secret header called Key containing the flag.
Exploitation
The attack plan is quite clear:
- Send the request to
/guardian?quote=http://localhost:1337/think - Grab the flag.
However, the problem is that with going to the /guardian endpoint return a 404 not found.

Checking the request in Burpsuite:

The request is perfectly normal, nothing significan was observed. We go back to the default.conf file:

The alley.$SECRET_ALLEY virtual host is the default server, therefore when we send a standard request to it, the Host header is configured to this virtual host instead of the guardian.$SECRET_ALLEY virtual host that’s actually hosting the /guardian endpoint.
The command that we actually need to run:
curl -H 'Host: guardian.$SECRET_ALLEY' 'http://154.57.164.83:32372/guardian?quote=http://localhost:1337/think'However, this boils down to how can we find the value of $SECRET_ALLEY. This can be solved by using HTTP 1.0. In HTTP 1.0, the Host head is not mandatory, by adding an empty Host header, the server will automatically route that domain name to the default_server name (which is the alley.$SECRET_ALLEY in this case). We can then extract the $SECRET_ALLEY part.
curl --http1.0 -H 'Host:' http://154.57.164.83:32372/think
The secret alley is firstalleyontheleft.com, hence:
curl -H 'Host: guardian.firstalleyontheleft.com' 'http://154.57.164.83:32372/guardian?quote=http://localhost:1337/think'
Loot & Flags
Flag: HTB{DUsT_1n_my_3y3s_l33t}
Fix:
- Virtual Host Leakage: This happen because and active virtual host is binded to be the default server, normally, default server would just be a place where the server return an error page in order to prevent virtual host discovery.
# Add this block at the top to catch all undefined Host headers
server {
listen 80 default_server;
server_name _;
return 444;
}