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.223
Client:
$ 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