Hello,
This library is fantastic. Thanks for that. I found an attack that is not being covered.
I setup a DNS record on my domain pointing to itself:
Nothing evil there. However, I added a redirection rule to redirect that URL into something evil:
So the library is preventing malicious attacks by checking for the hostname.
https://github.com/JaneJeon/got-ssrf/blob/master/index.js#L35
There the hostname is going to be 'local.urlint.co'
being resolved into a unicast IP address.
However, if you interact with the resource, that URL is going to redirect you into the evil URL
$ curl -I -s -X GET https://local.urlint.co/dns
HTTP/2 301
date: Fri, 24 Sep 2021 14:28:30 GMT
cache-control: max-age=3600
expires: Fri, 24 Sep 2021 15:28:30 GMT
location: http://192.168.0.1
That's because we checked the DNS over the hostname but not over the hostname over the location.
Plus, most HTTP clients follow redirects by default (that's the case of got
and that in fact a good expected behavior).
I wrote some code as suggestions to prevent the attack:
const { headers } = await got.head(url, { followRedirect: false })
const redirectHostname = new URL(headers.location).hostname
const { address } = await cacheableLookup.lookupAsync(url.hostname)
if (
ip.parse(address).range() !== 'unicast' ||
ip.parse(redirectHostname).range() !== 'unicast'
) {
throw new Error("You're evil")
}
However, I don't like it at all, since the got.head
extra network request needs to be performed. Maybe that could be handle at the DNS level?