-
Notifications
You must be signed in to change notification settings - Fork 13.9k
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
Add a command to customise DNS resolution (including through sessions) #18526
Conversation
… the OS's resources
end | ||
|
||
def sid | ||
'previous MSF session' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious - why was this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having the sink-holed DNS class was to satisfy the condition described above:
If a specific session was specified for a particular DNS rule, and then that session dies, DNS resolution will not fall back to custom or default nameservers, since we interpret the user's intentional use of a particular session to mean "I don't want DNS to fallback to other nameservers and potentially leak". Falling back upon a session closure would violate this.
This is also applicable when the application closes and re-opens. If the user's DNS configuration was saved, and that configuration had included a specific session to route certain DNS requests through, MSF will remember that there was a session associated with the rule, and that rule will be treated as a black hole: these DNS requests will deliberately fail, and not fall back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @smashery for these great additions! I just left a few minor comments. I also tested on different platforms (OSX, Windows, Linux) and it works great.
I noticed a few points that might be improved:
- Adding the same rule after loading it from the config file
When some saved rules with sessions are loaded while msfconsole
starts, these will deliberately fail, which is expected. However, when creating the same rule again with an up-to-date session ID, the previous one still applies and the module fails to resolve names matching this rule. For example:
msf6 auxiliary(scanner/http/http_header) > dns
Custom nameserver rules
=======================
ID Rule(s) DNS Server Comm channel
-- ------- ---------- ------------
0 *.myawesomesite.ch 9.9.9.9 Closed session (previous MSF session)
1 *.myawesomesite.fr 1.1.1.1
2 *.myawesomesite.com 9.9.9.9 Closed session (previous MSF session)
3 *.myawesomesite.com 9.9.9.9 Session 1
Here, rule #2 has been loaded from the config file and rule #3 has been created afterwards. Name resolution fails:
msf6 auxiliary(scanner/http/http_header) > run verbose=true rhosts=www.myawesomesite.com
[-] Msf::OptionValidateError The following options failed to validate: RHOSTS
Rule #2 needs to be specifically removed to make it work.
I'm wondering if there is a way to have some logic that detects the user is trying to add a rule that is the same as an existing rule with a "dead" session and simply update it instead of adding it? Would it make sense? At least, this could be optional, asking him if he wants to replace the existing rule.
- More descriptive error message
When a name resolution fails, whether it is because it doesn't exist or the dns route is deliberately blocked to avoid leak, the message is very generic:
[-] Msf::OptionValidateError The following options failed to validate: RHOSTS
I'm wondering if there is a way to display something more descriptive to help the user understand it is a DNS issue and why?
- Setting DNS feature off
I'm not sure if it is by design, but when you disable the DNS feature with features set dns_feature false
, the rules still apply. This means DNS requests are routed according to the rules even if the feature is off.
- DNS feature not loaded correctly at launch
When the configuration file contains the DNS feature enabled, Framework doesn't seem to enable it when it launches.
Config file:
…
[framework/features]
dns_feature=true
…
msf6 auxiliary(scanner/http/http_header) > features
Features table
==============
# Name Enabled Description
- ---- ------- -----------
…
6 dns_feature false When enabled, allows configuration of DNS resolution behaviour in Metasploit
msf6 auxiliary(scanner/http/http_header) > dns
[-] Unknown command: dns
That being said, rules still applies, as I noted in #3.
1b4157c
to
ef9a165
Compare
Thanks for the review @cdelafuente-r7. I believe I've addressed the issues. To the points you raised up top:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating this @smashery. These changes looks good to me and the issues I found are now fixed. I just left one minor comment for a typo.
I was not able to reproduce the issue #4 neither. Probably the last changes also fixed this one. But, I found another one: disabling the DNS feature makes DNS requests fail:
msf6 auxiliary(scanner/http/http_header) > sessions
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
1 meterpreter x64/windows DESKTOP-26CQRHP\n00tmeg @ DESKTOP-26CQRHP 192.168.100.77:4444 -> 192.168.1.50:64316 (192.168.100.50)
msf6 auxiliary(scanner/http/http_header) > dns
Custom nameserver rules
=======================
ID Rules(s) DNS Server Commm channel
-- -------- ---------- -------------
0 *.rapid7.com 9.9.9.9
2 *.github.com 1.1.1.1 Session 1
Default nameservers
===================
ID DNS Server Commm channel
-- ---------- -------------
1 8.8.4.4
msf6 auxiliary(scanner/http/http_header) > run verbose=true rhosts=www.github.com
[*] 140.82.113.4:80 : requesting / via HEAD
[*] 140.82.113.4:80 : deleted header Content-Length
[+] 140.82.113.4:80 : LOCATION: https://www.github.com/
[+] 140.82.113.4:80 : detected 1 headers
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/http_header) > features set dns_feature false
dns_feature => false
[*] Reloading module...
msf6 auxiliary(scanner/http/http_header) > dns
[-] Unknown command: dns
msf6 auxiliary(scanner/http/http_header) > run verbose=true rhosts=www.github.com
[-] Msf::OptionValidateError The following options failed to validate: RHOSTS
columns = ['ID', 'Rules(s)', 'DNS Server', 'Commm channel'] | ||
else | ||
columns = ['ID', 'DNS Server', 'Commm channel'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
columns = ['ID', 'Rules(s)', 'DNS Server', 'Commm channel'] | |
else | |
columns = ['ID', 'DNS Server', 'Commm channel'] | |
columns = ['ID', 'Rules(s)', 'DNS Server', 'Comm channel'] | |
else | |
columns = ['ID', 'DNS Server', 'Comm channel'] |
Ah, the fallback failed because it was passing an IpAddress, but expecting a string. That should be fixed now. I also realised that the zone transfer (AXFR) code was broken as a result of the changes, so I've fixed that up too. |
Thanks @smashery ! Everything looks good to me now. I tested on Mac OSX, Linux and Windows host, using both Meterpreter and SSH-based sessions. Routing of the DNS works as expected and no leaks were observed using Wireshark. I'll go ahead and land it.
|
Release notesThis adds a new |
This work implements the
dns
command in Metasploit, to allow the user to customise the behaviour of DNS resolution in the framework.The functionality, for now, is behind a feature flag. To use it, the feature needs to be enabled (
feature set dns_feature true
).I use the existing
CachedResolver
implementation, registering it withRex::Socket
as the DNS resolver. Any existing code that resolves addresses throughRex
will start using this functionality automatically.This work allows users to resolve domains through communication channels (i.e. sessions). There are two ways this occurs:
dns
command supports setting a session value (-s
or--session
) to set the comm channel through which the communication will be sent.route
has been set up for a particular CIDR, through a particular session, if the provided nameserver matches that CIDR, the DNS packet will be sent through it.The
save
command will save any DNS rules that have been configured by the user, which will be loaded upon application restart.An important aspect of this work is to ensure that DNS leaks don't happen: if someone sets a particular nameserver for a particular domain, DNS queries within that DNS zone should not go to the default nameservers. Thus, the resolution behaviour is as follows:
If a specific session was specified for a particular DNS rule, and then that session dies, DNS resolution will not fall back to custom or default nameservers, since we interpret the user's intentional use of a particular session to mean "I don't want DNS to fallback to other nameservers and potentially leak". Falling back upon a session closure would violate this.
This is also applicable when the application closes and re-opens. If the user's DNS configuration was saved, and that configuration had included a specific session to route certain DNS requests through, MSF will remember that there was a session associated with the rule, and that rule will be treated as a black hole: these DNS requests will deliberately fail, and not fall back.
If sending through a separate session was configured only using the
route
command and an application restarts,Verification
To test DNS resolution, a separate module could be created just to do that, or an existing basic module such as
auxiliary/scanner/http/http_header
could be used.msfconsole
.feature set dns_feature true
(enable the feature).dns
commands (add, remove/del, flush, print, help) behave as expected.save
command saves the DNS configuration (reloaded upon application restart)save
command is used to save an entry with a session associated with it, upon application restart (when the session will no longer exist), DNS does not fall back to nameservers that would not have been applicable prior to the application restart (DNS resolution should fail, unless other rules apply).