Update the specs

This commit is contained in:
Spencer McIntyre
2026-04-06 18:50:30 -04:00
parent f177b98bfb
commit dac67e6ee6
3 changed files with 33 additions and 93 deletions
@@ -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
@@ -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
@@ -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