DNS server for the discovery and testing of SSRF DNS rebinding vulnerabilities.
Imagine web application code - such as the following - intended to prevent SSRF:
from flask import request
import ipaddress
import requests
import socket
import urllib.parse
attributes = urllib.parse.urlparse(request.form['url'])
host, port = socket.getaddrinfo(attributes.hostname, attributes.port, family=socket.AF_INET, type=socket.SOCK_STREAM)[0][-1]
ip = ipaddress.IPv4Address(host)
if ip.is_global:
requests.get(request.form['url'])Note that here there are 2 DNS lookups:
- DNS lookup to validate the URL
- DNS lookup as part of the HTTP request
The above logic creates a time-of-check to time-of-use (TOCTTOU) race condition which can potentially be exploited by a DNS server - such as this - which does the following:
- Returns a public A/AAAA/CNAME record with a low TTL (this is used in the URL validation)
- Returns a private/reserved/loopback/link_local A/AAAA/CNAME record (this is used in the actual HTTP request)
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .$ sudo make install-t: DNS TTL (default0)-c: number of legitimate responses for each reserved response (default1)-6(${HOST_IP}is an IPv6 address)-a: public A record target (default0.0.0.0)-A: public AAAA record target (default::)-C: public CNAME record target-p: alternative UDP port on which to listen for DNS requests (default53)-i: interface on which to listen (default is to listen on0.0.0.0)
- Create a CSV file of the form
qtype,subdomain,reservedIP. An example of such a file isexample.csv - Run the DNS server
$ rebind [-c ${VALID_RESPONSE_COUNT}] [-t ${TTL}] [-a ${PUBLIC_A}] [-A ${PUBLIC_AAAA}] [-C ${PUBLIC_CNAME}] [-6] [-p ${PORT}] ${DOMAIN_NAME} ${FILENAME} ${HOST_IP}Changes to the CSV file can be reloaded without restarting the server
$ kill -s SIGHUP ${PID}Server:
$ cat ./example.csv
A,one,127.0.0.1
A,two,192.168.0.1
A,three,169.254.169.254
AAAA,four,::1
A,five,127.0.0.2
CNAME,six,admin.internal.corp
$ rebind example.com ./example.csv 34.232.67.223Client:
$ dig +noall +answer one.example.com @127.0.0.1
one.example.com. 0 IN A 0.0.0.0
$ dig +noall +answer one.example.com @127.0.0.1
one.example.com. 0 IN A 127.0.0.1