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

Fix a crash with the admin/dcerpc/icpr_cert module and OpenSSL 3.4.0 #19624

Merged
merged 5 commits into from
Nov 19, 2024

Conversation

cdelafuente-r7
Copy link
Contributor

@cdelafuente-r7 cdelafuente-r7 commented Nov 8, 2024

This fixes an issue wit the ms_icpr library that crashes with OpenSSL 3.4.0. tis version introduced a change in the Certificate Signing Request library that reject setting versions on CSR's. See the related discussion in this issue and the related PR.

Verification

  • Start msfconsole
  • use admin/dcerpc/icpr_cert
  • run verbose=true CA=<target CA name> RHOSTS=<remote IP> username=<username> password=<password> CERT_TEMPLATE=User
  • Verify there is n crash anymore
  • Verify the expected certificate is retrieved

Scenarios

Without the fix

msf6 auxiliary(admin/dcerpc/icpr_cert) > run verbose=true CA=dc-mydomain-local-CA RHOSTS=192.168.3.44 username=msfuser password=123456 CERT_TEMPLATE=User
[*] Running module against 192.168.3.44

[*] 192.168.3.44:445 - Connecting to ICertPassage (ICPR) Remote Protocol
[*] 192.168.3.44:445 - Binding to \cert...
[+] 192.168.3.44:445 - Bound to \cert
[-] 192.168.3.44:445 - Auxiliary failed: OpenSSL::X509::RequestError X509_REQ_set_version: passed invalid argument
[-] 192.168.3.44:445 - Call stack:
[-] 192.168.3.44:445 -   /home/n00tmeg/dev/metasploit-framework/lib/msf/core/exploit/remote/ms_icpr.rb:259:in `version='
[-] 192.168.3.44:445 -   /home/n00tmeg/dev/metasploit-framework/lib/msf/core/exploit/remote/ms_icpr.rb:259:in `build_csr'
[-] 192.168.3.44:445 -   /home/n00tmeg/dev/metasploit-framework/lib/msf/core/exploit/remote/ms_icpr.rb:141:in `do_request_cert'
[-] 192.168.3.44:445 -   /home/n00tmeg/dev/metasploit-framework/lib/msf/core/exploit/remote/ms_icpr.rb:99:in `request_certificate'
[-] 192.168.3.44:445 -   /home/n00tmeg/dev/metasploit-framework/modules/auxiliary/admin/dcerpc/icpr_cert.rb:68:in `block in action_request_cert'
[-] 192.168.3.44:445 -   /home/n00tmeg/dev/metasploit-framework/modules/auxiliary/admin/dcerpc/icpr_cert.rb:83:in `with_ipc_tree'
[-] 192.168.3.44:445 -   /home/n00tmeg/dev/metasploit-framework/modules/auxiliary/admin/dcerpc/icpr_cert.rb:67:in `action_request_cert'
[-] 192.168.3.44:445 -   /home/n00tmeg/dev/metasploit-framework/modules/auxiliary/admin/dcerpc/icpr_cert.rb:53:in `run'
[*] Auxiliary module execution completed

With the fix

msf6 auxiliary(admin/dcerpc/icpr_cert) > run verbose=true CA=dc-mydomain-local-CA RHOSTS=192.168.3.44 username=msfuser password=123456 CERT_TEMPLATE=User
[*] Running module against 192.168.3.44

[*] 192.168.3.44:445 - Connecting to ICertPassage (ICPR) Remote Protocol
[*] 192.168.3.44:445 - Binding to \cert...
[+] 192.168.3.44:445 - Bound to \cert
[*] 192.168.3.44:445 - Requesting a certificate for user msfuser - digest algorithm: SHA256 - template: User
[+] 192.168.3.44:445 - The requested certificate was issued.
[*] 192.168.3.44:445 - Certificate UPN: [email protected]
[!] 192.168.3.44:445 - No active DB -- Credential data will not be saved!
[*] 192.168.3.44:445 - Certificate stored at: /home/n00tmeg/.msf4/loot/20241108161007_default_192.168.3.44_windows.ad.cs_031303.pfx
[*] Auxiliary module execution completed

TODO

  • [] Test if it is still working with older OpenSSL versions on Linux
  • [] Test if it is still working with older OpenSSL versions on Mac
  • [] Test if it is still working with older OpenSSL versions on Windows

@smcintyre-r7 smcintyre-r7 self-assigned this Nov 8, 2024
@@ -256,7 +256,6 @@ def do_request_cert(icpr, opts)
# @return [OpenSSL::X509::Request] The request object.
def build_csr(cn:, private_key:, dns: nil, msext_sid: nil, msext_upn: nil, algorithm: 'SHA256', application_policies: [])
request = OpenSSL::X509::Request.new
request.version = 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to confirm; is this version setting entirely wrong for newer versions of OpenSSL - or is the issue something else?

Just asking as it looks like we've got some other code similar to this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the discussion in the OpenSSL issue, setting the version on CSR's has always been wrong. RFC 2986 only defines a single version for CSRs: v1(0). The CSR could have an invalid version and they decided to totally remove the ability to set the version on CSR in OpenSSL 3.4.0. So, yes, this should be removed or it will throw an exception.

Copy link
Contributor

Choose a reason for hiding this comment

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

Awesome thanks; Some other places in framework:

$ rg -C 3 'OpenSSL::X509::Request'

modules/auxiliary/server/relay/esc8.rb
128-
129-  def create_csr(private_key, cert_template)
130-    vprint_status('Generating CSR...')
131:    request = OpenSSL::X509::Request.new
132-    request.version = 1 <---------------------------
133-    request.subject = OpenSSL::X509::Name.new([
134-      ['CN', cert_template, OpenSSL::ASN1::UTF8STRING]

modules/exploits/windows/backupexec/ssl_uaf.rb
586-  def handle_a_csr(s, ca_cert, ca_key)
587-    resp = do_simple_ssl_request(s, SSLRequest::Opcode.get_csr_req, 0)
588-    return nil if resp.nil?
589:    request = OpenSSL::X509::Request.new(resp.unknown2)
590-
591-    agent_cert = OpenSSL::X509::Certificate.new
592-    agent_cert.version    = 3 <---------------------------

modules/exploits/multi/veritas/beagent_sha_auth_rce.rb
364-    end
365-
366-    def sign_agent_csr(csr)
367:      o_csr = OpenSSL::X509::Request.new(csr)
368-      @be_agent_cert = OpenSSL::X509::Certificate.new
369-      @be_agent_cert.version = 2 <---------------------------
370-      @be_agent_cert.serial = rand(2**32..2**64 - 1)

There might be more 🕵️

Copy link
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

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

This code was copied into the new ESC8 module which will also need to be updated.

def create_csr(private_key, cert_template)
vprint_status('Generating CSR...')
request = OpenSSL::X509::Request.new
request.version = 1
request.subject = OpenSSL::X509::Name.new([
['CN', cert_template, OpenSSL::ASN1::UTF8STRING]
])
request.public_key = private_key.public_key
request.sign(private_key, OpenSSL::Digest.new('SHA256'))
vprint_status('CSR Generated')
request
end

I think at this point, it would make sense to make a create_csr method probably in Rex::Proto::CryptAsn1::X509. Placing it in Rex would put us in a position to test it more easily in the future too.

Also update `ms_icpr.rb` to use it
It also fixes some unrelated minor issues found in the module and the documentation
@cdelafuente-r7
Copy link
Contributor Author

cdelafuente-r7 commented Nov 12, 2024

Thank you @smcintyre-r7 and @adfoster-r7 for the review. I added a create_csr helper as suggested and updated both the icpr_cert and esc8 modules.

The windows/backupexec/ssl_uaf.rb and multi/veritas/beagent_sha_auth_rce.rb modules don't set the version of the CSR but the Certificate itself, which is allowed. Also, it is not worth using the new helper for these because it only creates a CSR from an existing CSR in PEM format, without changing any value. I didn't want to update them since I won't be able to find a vulnerable target to test.

Also, I fixed some unrelated minor issues in the esc8 module and documentation.

Copy link
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

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

It looks like the test failure needs some kind of change to address. I'm not sure why it's popping up now, it probably has something to do with the require order but it should probably be addressed.

https://github.com/rapid7/metasploit-framework/actions/runs/11802525455/job/32878382550?pr=19624#step:7:655

Once that's done, I'm happy to land this. I tested both the esc8 relay module as well as the icpr_cert module. In both cases, I was able to successfully issue a certificate and then use the certificate to issue a TGT, thus proving it's validity.

@cdelafuente-r7
Copy link
Contributor Author

Thank you @smcintyre-r7 for your review. I fixed the require issue and also updated the specs since the signature of the CSR changed.
Finally, I added the algorithm argument to create_csr, it was missing.

@@ -5,7 +5,8 @@
#
# -*- coding: binary -*-

require 'windows_error/h_result'
require 'windows_error'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure why it breaks now since it is unrelated to the changes in this PR. Probably some lib autoloading that used to be in place and that are not anymore.

@smcintyre-r7
Copy link
Contributor

It looks like we still have a related failure:

https://github.com/rapid7/metasploit-framework/actions/runs/11896585776/job/33148841662#step:7:591

  1) dcerpc icpr_cert #build_csr when building should be correct
     Failure/Error: expect(result.to_der).to eq(x509_csr.to_der)

       expected: "0\x82\x02W0\x82\x01?\x02\x01\x010\x121\x100\x0E\x06\x03U\x04\x03\f\aaliddle0\x82\x01\"0\r\x06\t*\x86...xC5K\x8F\xA45V\xE6-E\xA5[\xF3s\xF6#|\x9DM\xFC\x1D\xF4\xCB\\\xC0*\x8Al\xB77\xAB\x18\xDD\x1F\xC8\x87`"
            got: "0\x82\x02W0\x82\x01?\x02\x01\x000\x121\x100\x0E\x06\x03U\x04\x03\f\aaliddle0\x82\x01\"0\r\x06\t*\x86...x93;.\xFF\xF2\x8B\x9D:\xBC\xAF\"\xA1\xF0\xC3b\x81\xF1\xCF\xC60h\xF55\f\xE0\x9F\xBB\xE4\xFC\xDEs\xE0"

       (compared using ==)

@cdelafuente-r7
Copy link
Contributor Author

It looks like I missed this one. It should be good now.

@smcintyre-r7 smcintyre-r7 merged commit f7e210d into rapid7:master Nov 19, 2024
71 checks passed
@smcintyre-r7
Copy link
Contributor

Release Notes

This fixes a bug that would occur when generating CSRs for AD CS with OpenSSL 3.4.0. The bug was related to an attribute in the request that can no longer be explicitly set.

@smcintyre-r7 smcintyre-r7 added library rn-fix release notes fix labels Nov 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug library rn-fix release notes fix
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants