From dac67e6ee62ac1e939fa02bf1fc1a416ea5cf37a Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Mon, 6 Apr 2026 18:50:30 -0400 Subject: [PATCH] Update the specs --- .../core/exploit/remote/cert_request_spec.rb | 32 +++++----- .../remote/http/web_enrollment_spec.rb | 58 ++++++------------- .../msf/core/exploit/remote/ms_icpr_spec.rb | 36 ------------ 3 files changed, 33 insertions(+), 93 deletions(-) diff --git a/spec/lib/msf/core/exploit/remote/cert_request_spec.rb b/spec/lib/msf/core/exploit/remote/cert_request_spec.rb index 83058be3c5..7c67f8cda1 100644 --- a/spec/lib/msf/core/exploit/remote/cert_request_spec.rb +++ b/spec/lib/msf/core/exploit/remote/cert_request_spec.rb @@ -96,18 +96,18 @@ RSpec.describe Msf::Exploit::Remote::CertRequest do describe '#create_csr' do context 'with a private key supplied via opts' do it 'returns an OpenSSL::X509::Request' do - result = subject.create_csr(username: 'alice', private_key: rsa_key) - expect(result).to be_a(OpenSSL::X509::Request) + csr, _key = subject.create_csr(username: 'alice', private_key: rsa_key) + expect(csr).to be_a(OpenSSL::X509::Request) end it 'signs the CSR with the provided key' do - result = subject.create_csr(username: 'alice', private_key: rsa_key) - expect(result.verify(rsa_key.public_key)).to be true + csr, _key = subject.create_csr(username: 'alice', private_key: rsa_key) + expect(csr.verify(rsa_key.public_key)).to be true end it 'sets the CN to the provided username' do - result = subject.create_csr(username: 'alice', private_key: rsa_key) - expect(result.subject.to_s).to include('alice') + csr, _key = subject.create_csr(username: 'alice', private_key: rsa_key) + expect(csr.subject.to_s).to include('alice') end context 'when the key size matches the expected rsa_key_size' do @@ -117,8 +117,8 @@ RSpec.describe Msf::Exploit::Remote::CertRequest do end it 'returns a CSR' do - result = subject.create_csr(username: 'alice', private_key: rsa_key, rsa_key_size: 2048) - expect(result).to be_a(OpenSSL::X509::Request) + csr, _key = subject.create_csr(username: 'alice', private_key: rsa_key, rsa_key_size: 2048) + expect(csr).to be_a(OpenSSL::X509::Request) end end @@ -154,8 +154,8 @@ RSpec.describe Msf::Exploit::Remote::CertRequest do end it 'returns a valid OpenSSL::X509::Request' do - result = subject.create_csr(username: 'alice') - expect(result).to be_a(OpenSSL::X509::Request) + csr, _key = subject.create_csr(username: 'alice') + expect(csr).to be_a(OpenSSL::X509::Request) end end @@ -282,13 +282,13 @@ RSpec.describe Msf::Exploit::Remote::CertRequest do end it 'calls build_on_behalf_of and returns a ContentInfo object' do - result = subject.create_csr( + csr, _key = subject.create_csr( username: 'alice', private_key: rsa_key, pkcs12: pkcs12, on_behalf_of: 'DOMAIN\\administrator' ) - expect(result).to be_a(Rex::Proto::CryptoAsn1::Cms::ContentInfo) + expect(csr).to be_a(Rex::Proto::CryptoAsn1::Cms::ContentInfo) end it 'passes on_behalf_of to build_on_behalf_of' do @@ -314,20 +314,20 @@ RSpec.describe Msf::Exploit::Remote::CertRequest do it 'skips build_on_behalf_of and returns a plain CSR' do expect(Rex::Proto::X509::Request).not_to receive(:build_on_behalf_of) - result = subject.create_csr(username: 'alice', private_key: rsa_key, pkcs12: pkcs12) - expect(result).to be_a(OpenSSL::X509::Request) + csr, _key = subject.create_csr(username: 'alice', private_key: rsa_key, pkcs12: pkcs12) + expect(csr).to be_a(OpenSSL::X509::Request) end end context 'with on_behalf_of but no pkcs12' do it 'skips build_on_behalf_of and returns a plain CSR' do expect(Rex::Proto::X509::Request).not_to receive(:build_on_behalf_of) - result = subject.create_csr( + csr, _key = subject.create_csr( username: 'alice', private_key: rsa_key, on_behalf_of: 'DOMAIN\\administrator' ) - expect(result).to be_a(OpenSSL::X509::Request) + expect(csr).to be_a(OpenSSL::X509::Request) end end diff --git a/spec/lib/msf/core/exploit/remote/http/web_enrollment_spec.rb b/spec/lib/msf/core/exploit/remote/http/web_enrollment_spec.rb index bca917c821..b4e8a5cdab 100644 --- a/spec/lib/msf/core/exploit/remote/http/web_enrollment_spec.rb +++ b/spec/lib/msf/core/exploit/remote/http/web_enrollment_spec.rb @@ -167,6 +167,8 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do describe '#retrieve_certs' do let(:authenticated_client) { double('authenticated_client') } + before { subject.instance_variable_set(:@issued_certs, {}) } + it 'calls retrieve_cert for each template' do templates = %w[UserTemplate AdminTemplate] templates.each do |t| @@ -179,6 +181,20 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do expect(subject).not_to receive(:retrieve_cert) subject.retrieve_certs('192.168.1.1', authenticated_client, 'DOMAIN\\user', []) end + + context 'when a cert has already been issued' do + before { subject.add_cert_entry('DOMAIN\\user', 'UserTemplate') } + + it 'skips retrieve_cert for the already-issued template' do + expect(subject).not_to receive(:retrieve_cert).with('192.168.1.1', authenticated_client, 'DOMAIN\\user', 'UserTemplate') + subject.retrieve_certs('192.168.1.1', authenticated_client, 'DOMAIN\\user', ['UserTemplate']) + end + + it 'still calls retrieve_cert for other templates' do + expect(subject).to receive(:retrieve_cert).with('192.168.1.1', authenticated_client, 'DOMAIN\\user', 'AdminTemplate') + subject.retrieve_certs('192.168.1.1', authenticated_client, 'DOMAIN\\user', %w[UserTemplate AdminTemplate]) + end + end end # ------------------------------------------------------------------------- @@ -190,43 +206,6 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do before { subject.instance_variable_set(:@issued_certs, {}) } - context 'when generating the RSA key' do - let(:csr_double) { double('csr', to_der: 'der_bytes') } - let(:denied_res) { double('response', code: 200, body: 'request was denied') } - - it 'uses the default key size of 2048 when RSAKeySize is blank' do - allow(subject).to receive(:create_csr).and_return(csr_double) - allow(subject).to receive(:send_request_raw).and_return(denied_res) - expect(OpenSSL::PKey::RSA).to receive(:new).with(2048).and_call_original - subject.retrieve_cert(target_ip, authenticated_client, identity, template) - end - - it 'uses the RSAKeySize from the datastore when set' do - subject.datastore['RSAKeySize'] = '4096' - allow(subject).to receive(:create_csr).and_return(csr_double) - allow(subject).to receive(:send_request_raw).and_return(denied_res) - expect(OpenSSL::PKey::RSA).to receive(:new).with(4096).and_call_original - subject.retrieve_cert(target_ip, authenticated_client, identity, template) - end - - it 'passes the generated key to create_csr' do - generated_key = OpenSSL::PKey::RSA.new(2048) - allow(OpenSSL::PKey::RSA).to receive(:new).and_return(generated_key) - allow(subject).to receive(:send_request_raw).and_return(denied_res) - expect(subject).to receive(:create_csr).with(hash_including(private_key: generated_key)).and_return(csr_double) - subject.retrieve_cert(target_ip, authenticated_client, identity, template) - end - end - - context 'when the cert has already been issued' do - before { subject.add_cert_entry(identity, template) } - - it 'returns nil without making an HTTP request' do - expect(subject).not_to receive(:send_request_raw) - expect(subject.retrieve_cert(target_ip, authenticated_client, identity, template)).to be_nil - end - end - context 'when the POST response is 200 with body containing "request was denied"' do let(:csr) { double('csr', to_der: 'der_bytes') } let(:res) { double('response', code: 200, body: 'Certificate request was denied by policy') } @@ -308,10 +287,7 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do let(:get_res) { double('response', code: 200, body: certificate.to_der) } before do - # Force retrieve_cert to use rsa_key so the generated key matches the certificate - rsa_key - allow(OpenSSL::PKey::RSA).to receive(:new).and_return(rsa_key) - allow(subject).to receive(:create_csr).and_return(csr) + allow(subject).to receive(:create_csr).and_return([csr, rsa_key]) allow(subject).to receive(:send_request_raw).and_return(post_res, get_res) allow(subject).to receive(:store_loot).and_return('/tmp/cert.pfx') end diff --git a/spec/lib/msf/core/exploit/remote/ms_icpr_spec.rb b/spec/lib/msf/core/exploit/remote/ms_icpr_spec.rb index 4c24490995..9c1b90e0ec 100644 --- a/spec/lib/msf/core/exploit/remote/ms_icpr_spec.rb +++ b/spec/lib/msf/core/exploit/remote/ms_icpr_spec.rb @@ -302,47 +302,11 @@ RSpec.describe Msf::Exploit::Remote::MsIcpr do end context 'RSA key generation' do - it 'generates a 2048-bit key by default' do - expect(OpenSSL::PKey::RSA).to receive(:new).with(2048).and_return(rsa_key) - subject.send(:do_request_cert, icpr, { username: 'alice' }) - end - - it 'uses the RSAKeySize from the datastore' do - subject.datastore['RSAKeySize'] = '4096' - allow(rsa_key).to receive(:n).and_return(double('OpenSSL::BN', num_bits: 4096)) - expect(OpenSSL::PKey::RSA).to receive(:new).with(4096).and_return(rsa_key) - subject.send(:do_request_cert, icpr, { username: 'alice' }) - end - it 'uses a private key supplied in opts without generating one' do rsa_key # force let evaluation before stubbing expect(OpenSSL::PKey::RSA).not_to receive(:new) subject.send(:do_request_cert, icpr, { username: 'alice', private_key: rsa_key }) end - - it 'passes the key to create_csr via opts' do - allow(OpenSSL::PKey::RSA).to receive(:new).and_return(rsa_key) - expect(subject).to receive(:create_csr).with(hash_including(private_key: rsa_key)) - subject.send(:do_request_cert, icpr, { username: 'alice' }) - end - end - - context 'key size mismatch' do - let(:wrong_size_key) { OpenSSL::PKey::RSA.new(1024) } - - it 'raises ArgumentError' do - expect { subject.send(:do_request_cert, icpr, { username: 'alice', private_key: wrong_size_key }) }.to raise_error(ArgumentError, /RSA key size mismatch/) - end - - it 'logs an RSA key size mismatch error' do - expect(subject).to receive(:elog).with('RSA key size mismatch') - expect { subject.send(:do_request_cert, icpr, { username: 'alice', private_key: wrong_size_key }) }.to raise_error(ArgumentError) - end - - it 'does not call create_csr' do - expect(subject).not_to receive(:create_csr) - expect { subject.send(:do_request_cert, icpr, { username: 'alice', private_key: wrong_size_key }) }.to raise_error(ArgumentError) - end end context 'certificate template' do