Consolidate the attribute creation

This commit is contained in:
Spencer McIntyre
2026-04-06 19:17:38 -04:00
parent dac67e6ee6
commit 4d02f92fab
6 changed files with 117 additions and 84 deletions
@@ -362,5 +362,79 @@ RSpec.describe Msf::Exploit::Remote::CertRequest do
subject.create_csr(username: 'alice', private_key: rsa_key)
end
end
context 'returned attributes' do
context 'with cert_template supplied via opts' do
it 'sets CertificateTemplate in the returned attributes' do
_csr, _key, attrs = subject.create_csr(username: 'alice', private_key: rsa_key, cert_template: 'AdminTemplate')
expect(attrs['CertificateTemplate']).to eq('AdminTemplate')
end
end
context 'with cert_template supplied via datastore' do
before { subject.datastore['CERT_TEMPLATE'] = 'UserTemplate' }
it 'sets CertificateTemplate from the datastore' do
_csr, _key, attrs = subject.create_csr(username: 'alice', private_key: rsa_key)
expect(attrs['CertificateTemplate']).to eq('UserTemplate')
end
end
context 'when no cert_template is supplied' do
it 'does not include CertificateTemplate in the returned attributes' do
_csr, _key, attrs = subject.create_csr(username: 'alice', private_key: rsa_key)
expect(attrs).not_to have_key('CertificateTemplate')
end
end
context 'with alt_dns supplied via opts' do
it 'includes dns in the SAN attribute' do
_csr, _key, attrs = subject.create_csr(username: 'alice', private_key: rsa_key, alt_dns: 'host.example.com')
expect(attrs['SAN']).to include('dns=host.example.com')
end
end
context 'with alt_upn supplied via opts' do
it 'includes upn in the SAN attribute' do
_csr, _key, attrs = subject.create_csr(username: 'alice', private_key: rsa_key, alt_upn: 'alice@example.com')
expect(attrs['SAN']).to include('upn=alice@example.com')
end
end
context 'with alt_sid supplied via opts' do
let(:sid) { 'S-1-5-21-1234567890-1234567890-1234567890-500' }
before { allow(Rex::Proto::X509::Request).to receive(:build_csr).and_return(double('csr', to_der: '')) }
it 'includes the prefixed URL SAN entry in the returned attributes' do
_csr, _key, attrs = subject.create_csr(username: 'alice', private_key: rsa_key, alt_sid: sid)
expect(attrs['SAN']).to include("url=#{Rex::Proto::X509::SAN_URL_PREFIX}#{sid}")
end
it 'includes the bare URL SAN entry in the returned attributes' do
_csr, _key, attrs = subject.create_csr(username: 'alice', private_key: rsa_key, alt_sid: sid)
expect(attrs['SAN']).to include("url=#{sid}")
end
end
context 'with alt_dns and alt_upn supplied' do
it 'joins both SAN entries with &' do
_csr, _key, attrs = subject.create_csr(
username: 'alice', private_key: rsa_key,
alt_dns: 'host.example.com', alt_upn: 'alice@example.com'
)
expect(attrs['SAN']).to include('dns=host.example.com')
expect(attrs['SAN']).to include('upn=alice@example.com')
expect(attrs['SAN']).to include('&')
end
end
context 'when no SAN values are set' do
it 'does not include SAN in the returned attributes' do
_csr, _key, attrs = subject.create_csr(username: 'alice', private_key: rsa_key)
expect(attrs).not_to have_key('SAN')
end
end
end
end
end
@@ -211,7 +211,7 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do
let(:res) { double('response', code: 200, body: 'Certificate request was denied by policy') }
before do
allow(subject).to receive(:create_csr).and_return(csr)
allow(subject).to receive(:create_csr).and_return([csr, nil, {}])
allow(subject).to receive(:send_request_raw).and_return(res)
end
@@ -225,7 +225,7 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do
let(:res) { double('response', code: 200, body: 'Certificate request failed due to an error') }
before do
allow(subject).to receive(:create_csr).and_return(csr)
allow(subject).to receive(:create_csr).and_return([csr, nil, {}])
allow(subject).to receive(:send_request_raw).and_return(res)
end
@@ -239,7 +239,7 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do
let(:res) { double('response', code: 401, body: 'Error: invalid credentials provided') }
before do
allow(subject).to receive(:create_csr).and_return(csr)
allow(subject).to receive(:create_csr).and_return([csr, nil, {}])
allow(subject).to receive(:send_request_raw).and_return(res)
end
@@ -253,7 +253,7 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do
let(:res) { double('response', code: 200, body: 'Certificate generated successfully, no location here') }
before do
allow(subject).to receive(:create_csr).and_return(csr)
allow(subject).to receive(:create_csr).and_return([csr, nil, {}])
allow(subject).to receive(:send_request_raw).and_return(res)
end
@@ -287,7 +287,7 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do
let(:get_res) { double('response', code: 200, body: certificate.to_der) }
before do
allow(subject).to receive(:create_csr).and_return([csr, rsa_key])
allow(subject).to receive(:create_csr).and_return([csr, rsa_key, { 'CertificateTemplate' => template }])
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
@@ -313,6 +313,14 @@ RSpec.describe Msf::Exploit::Remote::HTTP::WebEnrollment do
)
subject.retrieve_cert(target_ip, authenticated_client, identity, template)
end
it 'posts CertAttrib with key:value format' do
expect(subject).to receive(:send_request_raw).with(
hash_including('vars_post' => hash_including('CertAttrib' => "CertificateTemplate:#{template}"))
).and_return(post_res)
allow(subject).to receive(:send_request_raw).and_return(get_res)
subject.retrieve_cert(target_ip, authenticated_client, identity, template)
end
end
end
end
@@ -284,7 +284,7 @@ RSpec.describe Msf::Exploit::Remote::MsIcpr do
let(:csr_double) { double('csr', to_der: 'der') }
before do
allow(subject).to receive(:create_csr).and_return(csr_double)
allow(subject).to receive(:create_csr).and_return([csr_double, rsa_key, {}])
allow(icpr).to receive(:cert_server_request).and_return({ status: :issued })
end
@@ -309,57 +309,12 @@ RSpec.describe Msf::Exploit::Remote::MsIcpr do
end
end
context 'certificate template' do
it 'uses cert_template from opts' do
expect(icpr).to receive(:cert_server_request).with(hash_including(
attributes: hash_including('CertificateTemplate' => 'AdminTemplate')
))
subject.send(:do_request_cert, icpr, { username: 'alice', private_key: rsa_key, cert_template: 'AdminTemplate' })
end
it 'falls back to datastore CERT_TEMPLATE' do
subject.datastore['CERT_TEMPLATE'] = 'UserTemplate'
expect(icpr).to receive(:cert_server_request).with(hash_including(
attributes: hash_including('CertificateTemplate' => 'UserTemplate')
))
subject.send(:do_request_cert, icpr, { username: 'alice', private_key: rsa_key })
end
end
context 'SAN attributes' do
it 'includes dns SAN when alt_dns is in opts' do
expect(icpr).to receive(:cert_server_request).with(hash_including(
attributes: hash_including('SAN' => 'dns=host.example.com')
))
subject.send(:do_request_cert, icpr, { username: 'alice', private_key: rsa_key, alt_dns: 'host.example.com' })
end
it 'includes upn SAN when alt_upn is in opts' do
expect(icpr).to receive(:cert_server_request).with(hash_including(
attributes: hash_including('SAN' => 'upn=alice@example.com')
))
subject.send(:do_request_cert, icpr, { username: 'alice', private_key: rsa_key, alt_upn: 'alice@example.com' })
end
it 'includes both dns and upn SANs when both are set' do
expect(icpr).to receive(:cert_server_request) do |args|
san = args[:attributes]['SAN']
expect(san).to include('dns=host.example.com')
expect(san).to include('upn=alice@example.com')
{ status: :issued }
end
subject.send(:do_request_cert, icpr, {
username: 'alice', private_key: rsa_key,
alt_dns: 'host.example.com', alt_upn: 'alice@example.com'
})
end
it 'omits SAN attribute entirely when no alt values are set' do
expect(icpr).to receive(:cert_server_request) do |args|
expect(args[:attributes]).not_to have_key('SAN')
{ status: :issued }
end
subject.send(:do_request_cert, icpr, { username: 'alice', private_key: rsa_key })
context 'attributes pass-through' do
it 'passes the attributes hash from create_csr directly to cert_server_request' do
attrs = { 'CertificateTemplate' => 'TestTemplate', 'SAN' => 'upn=alice@example.com' }
allow(subject).to receive(:create_csr).and_return([csr_double, rsa_key, attrs])
expect(icpr).to receive(:cert_server_request).with(hash_including(attributes: attrs))
subject.send(:do_request_cert, icpr, { username: 'alice' })
end
end
end