Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

url: private_resolution/dns_resolution useless #2284

Closed
half-duplex opened this issue May 25, 2022 · 0 comments · Fixed by #2285
Closed

url: private_resolution/dns_resolution useless #2284

half-duplex opened this issue May 25, 2022 · 0 comments · Fixed by #2285
Labels
Bug Things to squish; generally used for issues Security Issues or PRs with security or privacy implications
Milestone

Comments

@half-duplex
Copy link
Member

half-duplex commented May 25, 2022

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.

@half-duplex half-duplex added the Bug Things to squish; generally used for issues label May 25, 2022
@half-duplex half-duplex added this to the 8.0.0 milestone May 25, 2022
@dgw dgw modified the milestones: 8.0.0, 8.1.0 Jun 15, 2023
@half-duplex half-duplex added the Security Issues or PRs with security or privacy implications label Nov 12, 2023
@dgw dgw closed this as completed in #2285 Oct 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Things to squish; generally used for issues Security Issues or PRs with security or privacy implications
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants