HackerOne allows bug bounty programs to integrate their reports queue with issue tracking tools such as Jira and Phabricator. By abusing a bug that I discovered in Ruby’s native resolver, I am able to bypass the SSRF filter and could potentially scan your internal network.
HackerOne uses the private_address_check gem to prevent SSRF on the “Integrations” panel: https://hackerone.com/{BBP}/integrations. The actual filtering takes place in lib/private_address_check.rb. The process starts by attempting to resolve the user-supplied URL with Resolv::getaddresses
and then compares the returned value with a the values in the blacklist. I discovered a bug in Resolv::getaddresses
that allows me to return an empty value, which is not included in the blacklist and therefore completely bypasses any checks.
def resolves_to_private_address?(hostname)
ips = Resolv.getaddresses(hostname)
ips.any? do |ip|
private_address?(ip)
end
end
The bypass consists of providing encoded IP addresses that when forwarded on to the operating system in lib/resolv.rb
return an empty value.
http://0177.1:22/
http://0x7f.1:22/
http://127.000.001:22/
I discovered the bug in Resolv::getaddresses
by running it on different Linux machines and noticing that the outputs vary. Until the Ruby Core come up with a better solution I suggest not relying on this library for any security-related features.
Machine 1 returned the following:
irb(main):001:0> require 'resolv'
irb(main):002:0> Resolv.getaddresses("127.000.000.1")
=> []
And Machine 2 returned this:
irb(main):001:0> require 'resolv'
irb(main):002:0> Resolv.getaddresses("127.000.000.1")
=> ["127.0.0.1"]
Admittedly, I was unable to actually exploit this issue and I am still playing around to see if I can exfiltrate valuable data. The current issue only consists of a filter bypass.
I would suggest using Socket.getaddrinfo()
as it is more reliable and is not affected by this bug. Something along the lines of this should work:
require "socket"
...
def resolves_to_private_address?(hostname)
ips = Socket.getaddrinfo(hostname, nil).sample[3]
ips.any? do |ip|
private_address?(ip)
end
end
My suggested patch can be found here: {F236338}.