Skip to content
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

NTLM support for the LDAP capture capabilities #18376

Conversation

JustAnda7
Copy link
Contributor

This PR is a follow up to #18125

This PR aims to increase the scope of capturing authentication details from LDAP clients by supporting NTLM Authentication.

Verification

List the steps needed to make sure this thing works

  • Start msfconsole
  • use auxiliary/server/capture/ldap
  • Set the SRVHOST,SRVPORT if required.
  • Change the CHALLENGE to another value if necessary
  • Verify the working of the feature using NTLM Authentication.
  • Verify that the credentials are stored in the database appropriately

Note Points

  • This was observed on Metasploit v6.2.
  • Additional testing is required to ensure the handling of various cases of Authentication. (Would like some suggestions on testing)

modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
@jmartin-tech jmartin-tech changed the base branch from master to GSoC/LDAP-Capture-Capabilities September 18, 2023 22:40
@jmartin-tech jmartin-tech self-assigned this Sep 18, 2023
@jmartin-tech
Copy link
Contributor

Some notes from GSoC sync conversations:

Desired adjustments:

Shift Auth code into Rex::Proto::LDAP::Auth
* Add specs for PDU processing and return class with a type that offers context of auth type

Server class refactor to allow registration/filter of callback based on PDU type
* This could be expanded to allow for multiple modules to listen on LDAP service
* This could enable adding filtering criteria for when the callback should be passed to capture
or some other module that is listening for other specific reasons.

Idea for future processing, allow capture module to be configured for specific response patterns for bind (not required)

Possible configurations to support:
* ResultCodeSuccess to all cases
* ResultCodeInvalidCredentials - No Anonymous Auth
* Mixed ResultCodeSuccess to Anonymous and ResultCodeInvalidCredentials all others
* Random ResultCodeSuccess to Anonymous and random success/failure reasons for others

@jmartin-tech jmartin-tech added the GSoC Google Summer of Code project PRs label Sep 19, 2023
@JustAnda7 JustAnda7 force-pushed the GSoC/LDAP-capture-capabilities-ntlm branch from a562033 to 795e622 Compare September 20, 2023 15:28
@JustAnda7 JustAnda7 force-pushed the GSoC/LDAP-capture-capabilities-ntlm branch from 795e622 to 6972a91 Compare September 20, 2023 16:49
Copy link
Contributor

@jmartin-tech jmartin-tech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed previously, the implementation here for #on_dispatch_request() can be pushed down into the LDAP server as default functionality and the authentication processing can then become shared capability provided by the LDAP server class.

Authentication processing looks to be directly related to a BindRequest PDU and a parser and processor for these request can return an object that the service would use to determine the "credential" data to be stored.

Consider this means that each method in the server class need to be scoped to be independent of Framework output helpers like print_status or those helpers need to be injected into the server object.

modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
Copy link
Contributor

@jmartin-tech jmartin-tech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updated revisions, I added some comments about possible utilization of Net::NTLM::Message::* classes for parsing the messages instead of implementing parser methods in Auth. (This is optional but I think will reduce the code required significantly.)

This PR will need rspec for Rex::Proto::LDAP::Server and Rex::Proto::LDAP::Auth at a minimum.

lib/rex/proto/ldap/auth.rb Outdated Show resolved Hide resolved
lib/rex/proto/ldap/auth.rb Outdated Show resolved Hide resolved
lib/rex/proto/ldap/auth.rb Outdated Show resolved Hide resolved
lib/rex/proto/ldap/auth.rb Outdated Show resolved Hide resolved
lib/rex/proto/ldap/auth.rb Outdated Show resolved Hide resolved
@@ -74,6 +75,7 @@ def initialize(lhost = '0.0.0.0', lport = 389, udp = true, tcp = true, ldif = ni
self.listener_thread = nil
self.dispatch_request_proc = dblock
self.send_response_proc = sblock
@auth_provider = auth_provider
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the type on auth_provider, might be even better to raise an ArgumentError when value is not nil and not of type Rex::Proto::LDAP::Auth.

lib/rex/proto/ldap/server.rb Outdated Show resolved Hide resolved
lib/rex/proto/ldap/auth.rb Outdated Show resolved Hide resolved
lib/rex/proto/ldap/auth.rb Outdated Show resolved Hide resolved
lib/rex/proto/ldap/auth.rb Outdated Show resolved Hide resolved
@JustAnda7
Copy link
Contributor Author

Any further changes required ?

Copy link
Contributor

@jmartin-tech jmartin-tech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing shows this is working well for simple auth with the revision to capture/ldap.rb regarding :post_pdu , however NTLM auth does not succeed.

With all the changes I am suggesting to evaluate mechanism during SASL auth attempts ldapsearch expectations still result in a decode error:

$ ldapsearch -H ldap://server.example.com -Y ntlm -U admin -b 'dc=server,dc=example,dc=com'
SASL/NTLM authentication started
ldap_sasl_interactive_bind: Decoding error (-4)
        additional info: SASL(-5): bad protocol / cancel: server didn't issue valid NTLM challenge

Digging into the type2 message generated in Ruby seems to be valid so I suspect something is not quite right in the serialization for the Net::LDAP::PDU::BindResult being returned to ldapsearch.

lib/rex/proto/ldap/auth.rb Outdated Show resolved Hide resolved
lib/rex/proto/ldap/auth.rb Show resolved Hide resolved
lib/rex/proto/ldap/server.rb Show resolved Hide resolved
spec/lib/rex/proto/ldap/auth_spec.rb Outdated Show resolved Hide resolved
modules/auxiliary/server/capture/ldap.rb Outdated Show resolved Hide resolved
* invert storage test for callback
* do not override service instance domain and hostname
* remove wrapping `Array` on `context_data` in response
* generate NTLM Type1 message instead of hardcoded blob
@jmartin-tech
Copy link
Contributor

@JustAnda7 I have posted a PR that resolves the issues I had in testing. I will hold off a couple days for you to test if you would like to. After that if not already merged, I will merge directly here so I can land this release the new functionality for upstream.

@JustAnda7
Copy link
Contributor Author

I have merged the PR. You can go ahead and land the branch on upstream. So far I think it works well and good.

OptString.new('Server', [ false, 'The default server to use for NTLM authentication', 'SERVER']),
OptString.new('DnsName', [ false, 'The default DNS server name to use for NTLM authentication', 'SERVER']),
OptString.new('DnsDomain', [ false, 'The default DNS domain name to use for NTLM authentication', 'example.com']),
OptBool.new('ForceDefault', [ false, 'Force the default settings', false]),
Copy link
Contributor

@jmartin-tech jmartin-tech Jan 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note, this option is not used by the implementation.

Looking at where it came from, I suspect this is from intention to mimic http_nltm behavior. For now I will just remove this options during land of this PR.

@jmartin-tech jmartin-tech merged commit a4e8714 into rapid7:GSoC/LDAP-Capture-Capabilities Jan 7, 2024
34 checks passed
@jmartin-tech
Copy link
Contributor

Release Notes

This PR adds support for LDAP capture of NTLM authentication and adds a default implementation for LDAP BindRequest, SearchRequest, UnbindRequest, as well as a default action for unsupported requests.

@ekalinichev-r7 ekalinichev-r7 added the rn-enhancement release notes enhancement label Feb 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
GSoC Google Summer of Code project PRs rn-enhancement release notes enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants