metasploit-framework
metasploit-framework copied to clipboard
Issue interpreting 'localhost' as '127.0.0.1'
Steps to reproduce
Run the wp_bookingpress_category_services_sqli
module and set RHOSTS
to localhost
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > run
[*] Running module against 0.0.0.1
[*] Running automatic check ("set AutoCheck false" to disable)
[-] Auxiliary aborted due to failure: unknown: Cannot reliably check exploitability. Unable to get wp-nonce as an unauthenticated user "set ForceExploit true" to override check result.
[*] Running module against 127.0.0.1
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable.
[*] Extracting credential information
Wordpress User Credentials
==========================
Username Email Hash
-------- ----- ----
admin [email protected] $P$BfxUckldN6AiHPD0BK6jg58se2b.aL.
hackerman [email protected] $P$BESfz7bqSOY8VkUfuYXAZ/bT5E36ww/
mr_metasploit [email protected] $P$BDb8pIfym5dS6WTnNU8vU5Uk6i89fk.
msfuser [email protected] $P$BpITVDPiqOZ7fyQbI5g9rsgUvZQFBd1
todd [email protected] $P$BnlpkVgxGFWnmvdDQ3JStgpIx8LMFj0
[*] Auxiliary module execution completed
Notice it says it's running the module against: 0.0.0.1
and then Auxiliary aborted due to failure: unknown: Cannot reliably check exploitability
Now this isn't a solution but is interesting behaviour, if you: include Msf::Auxiliary::Scanner
in the module and reload it, the module has no issue interpreting localhost
as 127.0.0.1
I had wrongfully included Msf::Auxiliary::Scanner
when I first wrote the module, it was removed during code review. I noticed this when I went back to record a demo for this module, when all of a sudden setting RHOSTS to localhost
started causing this problem.
Looks like this gets caused by the Rex socket RangeWalker implementation which the rhost walker calls under the hood:
Resolving 127.0.0.1:
>> Rex::Socket::RangeWalker.new('127.0.0.1').to_enum(:each_host).to_a
=> [{:address=>"127.0.0.1", :hostname=>nil}]
Resolving localhost:
>> Rex::Socket::RangeWalker.new('localhost').to_enum(:each_host).to_a
=> [{:address=>"0.0.0.1", :hostname=>"localhost"}, {:address=>"127.0.0.1", :hostname=>"localhost"}]
If we dig a bit deeper, under the covers the Rex::Socket.addreses
function returns the ipv4 and ipv6 address for localhost
which the range walker doesn't seem to handle correctly as part of the next_host logic:
=> [{:address=>"0.0.0.1", :hostname=>"localhost"}, {:address=>"127.0.0.1", :hostname=>"localhost"}]
>> Rex::Socket.getaddresses('127.0.0.1')
=> ["127.0.0.1"]
>> Rex::Socket.getaddresses('localhost')
=> ["::1", "127.0.0.1"]
A quick sanity check of Metasploit 5.0.101 shows this isn't a regression in the Rex::Socket resolving logic:
>> Rex::Socket::RangeWalker.new('127.0.0.1').to_enum(:each).to_a
=> ["127.0.0.1"]
>> Rex::Socket::RangeWalker.new('localhost').to_enum(:each).to_a
=> ["0.0.0.1", "127.0.0.1"]
This discussion might be relevant to rex-socket #43 and @gwillcox-r7 as the behavior you're currently observing and upon which this output relies ends up falling-through Rex Socket to the OS resolver running Ruby (and leaking DNS requests through the Framework host...).
On order to achieve those localhost
lookups using the Rex based Resolver used to plug the DNS leak and pivot our lookups, we need to do the following:
Rex::Socket.getaddresses('localhost')
SocketError: getaddrinfo: Name or service not known
resolver = Rex::Socket.class_variable_get(:@@resolver)
resolver.cache.add_static('localhost', '127.0.0.1')
=> 0
resolver.cache.add_static('localhost', '::1', 'AAAA')
=> 0
Rex::Socket.getaddresses('localhost')
=> ["127.0.0.1", "::1"]
alternatively, i can change how the CachedResolver reads-in the system's local hostsfile to handle localhost
in 4 and 6 modalities (its explicitly excluded in the init method).
I believe this bug fix is unrelated/separate to https://github.com/rapid7/rex-socket/pull/43
We're already resolving localhost
to an ipv4 and ipv6 address ["::1", "127.0.0.1"]
- it's just a bug in the Rex::Socket::RangeWalker
implementation which causes the result to be two ipv4 addresses that are yielded ["0.0.0.1", "127.0.0.1"]
Ah, thanks for the correction. Interesting problem, reproducible from my version as well:
Rex::Socket::RangeWalker.new('localhost').to_enum(:each).to_a
=> ["127.0.0.1", "0.0.0.1"]
Probably a rex-socket issue in that case, which i'm happy to triage and try to fix in there since i dig around those pieces quite often.
Got it:
> before.to_enum.to_a
=> ["127.0.0.1", "0.0.0.1"]
> after.to_enum.to_a
=> ["127.0.0.1", "::1"]
Rex::Socket::Host
needed to "figure out" whether it's IPv6 or not during its init.
PR incoming on that side.
Worth mentioning that not all systems resolve dual-stack for localhost
:
$ grep localhost /etc/hosts
127.0.0.1 localhost
::1 ip6-localhost ip6-loopback
and when using the native resolver:
> Rex::Socket.class_variable_get(:@@resolver)
=> nil
> Rex::Socket::RangeWalker.new('localhost').to_enum.to_a
=> ["127.0.0.1"]