Skip to content

url: private_resolution/dns_resolution useless #2284

@half-duplex

Description

@half-duplex

Description

The url.enable_private_resolution and url.enable_dns_resolution settings do not work as advertised, and the concept of the latter is fatally flawed.

The current url.py private-address protection logic is as follows:

if not enable_private_resolution:
    if host is ip and ip is private:
        return E_NAUGHTY
    if enable_dns_resolution and resolve(host) is private:
        return E_NAUGHTY
post_title(url)

This has many problems, as demonstrated below.

Reproduction steps

Oh lordy.

Scenario 1: Attacker has $2

  1. Buy a domain, e.g. haha.bad
  2. Create a DNS record: haha.bad A 127.0.0.1
  3. Post a link in IRC: http://haha.bad/csrf/me/daddy

If enable_dns_resolution is disabled, this bypasses all private-address protection.

Scenario 2: Attacker doesn't have $2

  1. Configure webserver to respond to requests with 302 http://127.0.0.1/csrf/me/daddy
  2. Post a link to http://$webserver_ip/

The posted link is a public IP, so Sopel will happily load it and follow the redirect to the private address.

Scenario 3: Attacker has $2, but the above is fixed

  1. Buy a domain, e.g. haha.bad
  2. Set the nameservers for haha.bad to an IP you control
  3. Run a script on that IP with the following behavior:
attack = False
if attack := not attack:
    return dns_record(type="A", address="1.2.3.4", ttl=0)
return dns_record(type="A", address="127.0.0.1", ttl=0)

In the checking stage, Sopel will see the address 1.2.3.4. When performing the request, 127.0.0.1 will be used without validation. (TOCTOU)

Scenario 4: Attacker has $2 and target is IPv6

  1. Buy a domain, e.g. haha.bad
  2. Point haha.bad to the target IPv6 address
  3. Post a link to http://haha.bad/csrf/me/daddy

dns.resolver.resolve() does not request AAAA records, so any combination of DNS and IPv6 passes validation.

Expected behavior

As explained on IRC, this behavior (especially the first two parts) is objectively broken and gives a false sense of security. We should either remove those config options and not pretend, or we should make url.py unable to talk to private IPs.

Environment

  • Sopel .version: Since before the name "Sopel" to present.
  • Relevant plugins: url.py

Notes

Ping @dgw and @Exirel, who requested this be an issue instead of IRC comments and a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugThings to squish; generally used for issuesSecurityIssues or PRs with security or privacy implications

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions