BambooCTF - SSRFrog

Right off the bat the name "SSRFrog" gives a substantial hint. Searching for SSRF gives an explanation of what an SSRF vulnerability is, a Server Side Request Forgery is being able to make a server send HTTP requests from the server itself. A typical way to use an SSRF vulnerability is to access local resources like another web server that is only available on the local network the open server resides in. Now to actually look at the challenge site itself.

Visiting the site shows an input that will make an HTTP request from the server itself to an address supplied by the user. To test that this works as expected the url "x.com" was entered. This website just literally is the letter x, which is what was displayed by the SSRFrog once the request was made.

A link to the websites source code is given. This source code will be crucial later but my team and I didn't spend too much time investigating the code before digging through the website a bit further.

    
    const express = require("express");
    const http = require("http");

    const app = express();

    app.get("/source", (req, res) => {
        return res.sendFile(__filename);
    })
    app.get('/', (req, res) => {
        const { url } = req.query;
        if (!url || typeof url !== 'string') return res.sendFile(__dirname + "/index.html");

        // no duplicate characters in `url`
        if (url.length !== new Set(url).size) return res.sendFile(__dirname + "/frog.png");

        try {
            http.get(url, resp => {
                resp.setEncoding("utf-8");
                resp.statusCode === 200 ? resp.on('data', data => res.send(data)) : res.send(":(");
            }).on('error', () => res.send("WTF?"));
        } catch (error) {
            res.send("WTF?");
        }
    });

    app.listen(3000, '0.0.0.0');
    
  

For all web challenges it is pretty common practice to take a look at the webpage's html code. Usually the code isn't too long and hints may be given in the form of developer comments. Inside SSRFrog's html code we are given the location of the flag,
the.c0o0o0l-fl444g.server.internal
But attempting to submit http:the.c0o0o0l-fl444g.server.internal results in a fail state. Referring to the code we see the following snippet

      
          // no duplicate characters in `url`
          if (url.length !== new Set(url).size) return res.sendFile(__dirname + "/frog.png");
      
   

We see it is checking that there are no duplicate characters in the supplied address. The address for the flag clearly has tons of repeating characters. How do we work around this? My team member 0x3c3e took note of the input's placeholder message as shown below

The idea was now to try different unicode versions of characters. Some unicode characters will be interpreted as the same character for address indexing. For example if there was a site called "444.com" then requesting "4₄⁴.com" would return the same site! Even though none of those characters in the second address were the same ascii value.

The first couple attempts at encoding the address failed. To get more insight into what could be happening my team set up some of the source code locally.

    
    const http = require("http");
      
    function foo(url) {
        http.get(url, resp => {
            resp.setEncoding("utf-8");
            console.log(resp.statusCode);
            resp.on('data', data => console.log(data))
        }).on('error', (e) => console.log(e));
    };
    foo( "url to test goes here!" )
    
 

Next we passed our encoded input htTp:ⓣHe.c0o⓪O⓿l-fL4⓸④g。sErvⒺR.inⓉⓔⓡNaⓛ into this test function to hopefully see where it was breaking. The output received was

Taking a look at the first line of the error we see the problem. Some characters of our input are being transcribed as gibberish. Upon further inspection it is apparent that the '.' encodings are working fine but the other encodings are broken.

The true solution to find the correct encodings was a lucky google search. I was able to find this site: , which shows related characters for any given character. Heres an example of characters related to '4'.

Using that website we came up with a correct address pretty quickly:
http:ⓣHe。c0o⓪O₀l-fL4₄⁴g。sErvⒺR.inⓉⓔⓡNaⓛ
Plugging this new address into the local code produces

Which shows our malicious address is being translated correctly to the target address where the flag lives! Submitting this address gives us the flag!
flag{C0o0o0oL_baby_ssrf_trick}