dnsResolve result compared with a literal address
medium
if (dnsResolve(host) == "10.1.2.3") depends on the resolver answering, answering fast and answering with exactly that record. Any failure makes the condition false and the request takes the default route unnoticed.
Why it matters
dnsResolve returns the address as a string, or an empty value (null or empty string,
engine-dependent) when the name does not resolve. Comparing against a literal fails open:
a resolver outage, a timeout, a second A record or a round-robin answer all produce “not
equal”, and the request takes whatever the default route is. On a hostile network the
resolver answer is attacker-controlled, so the decision is too. isInNet(dnsResolve(host), ...)
shares the dependency but at least tolerates multiple addresses in the range; a hostname
rule (host == "app.corp.example") has no DNS dependency at all.
How to fix
Decide on the hostname instead; if an address check is required, use isInNet on the resolved address with the whole subnet and order it after the string checks.
Examples
Bad
function FindProxyForURL(url, host) {
if (dnsResolve(host) == "10.1.2.3") {
return "DIRECT";
}
return "PROXY proxy.corp.example:8080";
}
Open bad example in checkerGood
function FindProxyForURL(url, host) {
if (host == "app.corp.example" || dnsDomainIs(host, ".corp.example")) {
return "DIRECT";
}
return "PROXY proxy.corp.example:8080";
}
Open good example in checkerRelated rules
- DNS lookup on every request PAC-P001
- isInNet called with a hostname PAC-P002
- dnsResolve called repeatedly for the same host PAC-P004