rapid7 / metasploit-framework

Metasploit Framework
https://www.metasploit.com/
Other
34.25k stars 14k forks source link

Fix failure in secrets dump edge case #19665

Closed smashery closed 4 days ago

smashery commented 6 days ago

This fixes an issue in the windows_secrets_dump module, wherein it fails after certain password change APIs.

To reproduce this issue (and verify the fix):

changepasswd.py -reset -newpass Pass123123$ domain/user@192.168.1.1 -altuser administrator -altpass Password1!

msf6 auxiliary(gather/windows_secrets_dump) > run
[*] Running module against 192.168.1.1

[*] 192.168.1.1:445 - Service RemoteRegistry is already running
[*] 192.168.1.1:445 - Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] 192.168.1.1:445 - Using the DRSUAPI method to get NTDS.DIT secrets
[*] 192.168.1.1:445 - SID enumeration progress - 0 / 9 ( 0.00%)
[*] 192.168.1.1:445 - SID enumeration progress - 9 / 9 (  100%)
[*] 192.168.1.1:445 - Cleaning up...
[-] 192.168.1.1:445 - Auxiliary failed: IOError data truncated
[-] 192.168.1.1:445 - Call stack:
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/io.rb:317:in `read'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/io.rb:278:in `readbytes'
[-] 192.168.1.1:445 -   (eval):23:in `read_and_return_value'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/base_primitive.rb:129:in `do_read'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/struct.rb:140:in `block in do_read'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/struct.rb:140:in `each'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/struct.rb:140:in `do_read'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/base.rb:147:in `block in read'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/base.rb:253:in `start_read'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/base.rb:145:in `read'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/base.rb:21:in `read'
[-] 192.168.1.1:445 -   /opt/shared/git/metasploit-framework/modules/auxiliary/gather/windows_secrets_dump.rb:723:in `decrypt_supplemental_info'
[-] 192.168.1.1:445 -   /opt/shared/git/metasploit-framework/modules/auxiliary/gather/windows_secrets_dump.rb:837:in `block in parse_user_record'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/array.rb:220:in `block in each'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/array.rb:220:in `each'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/gems/3.2.0/gems/bindata-2.4.15/lib/bindata/array.rb:220:in `each'
[-] 192.168.1.1:445 -   /opt/shared/git/metasploit-framework/modules/auxiliary/gather/windows_secrets_dump.rb:782:in `parse_user_record'
[-] 192.168.1.1:445 -   /opt/shared/git/metasploit-framework/modules/auxiliary/gather/windows_secrets_dump.rb:928:in `block (2 levels) in dump_ntds_hashes'
[-] 192.168.1.1:445 -   /opt/shared/git/metasploit-framework/modules/auxiliary/gather/windows_secrets_dump.rb:922:in `each'
[-] 192.168.1.1:445 -   /opt/shared/git/metasploit-framework/modules/auxiliary/gather/windows_secrets_dump.rb:922:in `block in dump_ntds_hashes'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/3.2.0/set.rb:511:in `each_key'
[-] 192.168.1.1:445 -   /home/smash/.rbenv/versions/3.2.5/lib/ruby/3.2.0/set.rb:511:in `each'
[-] 192.168.1.1:445 -   /opt/shared/git/metasploit-framework/modules/auxiliary/gather/windows_secrets_dump.rb:920:in `dump_ntds_hashes'
[-] 192.168.1.1:445 -   /opt/shared/git/metasploit-framework/modules/auxiliary/gather/windows_secrets_dump.rb:1269:in `run'
[*] Auxiliary module execution completed

This occurs because the particular API being used there clears the Kerberos keys. Then, when it tries to parse the UserProperties object, it's not of the right structure. The value we receive in the supplementalCredentials lookup is something like: b'\x00\x00\x00\x00b\x00\x00\x00\x00\x00\x00\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00P\x00\x00'. I have no idea what this is. MSDN doesn't seem to help us either - the docs (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/0705f888-62e1-4a4c-bac0-b4d427f396f8) don't mention anything about alternative structures.

Impacket's secretsdump.py just catches a parse exception and moves on with its life: https://github.com/fortra/impacket/blob/835e17550b57606ee3c681ae1c3f0edea096ec19/impacket/examples/secretsdump.py#L2275-L2278

With this fix, I do similar - catch the parse error, add a verbose warning message.

smcintyre-r7 commented 4 days ago

So I looked into this and I think the root cause is actually that our definition for the structure is incorrect because it doesn't take the following into account:

When there are zero USER_PROPERTY elements in the UserProperties field, this field MUST be omitted; the resultant USER_PROPERTIES structure has a constant size of 0x6F bytes.

I proposed a fix for this in rapid7/ruby_smb#280. I think with that in place, the array will just be empty and the IOError should no longer be raised.

smashery commented 4 days ago

Yeah, that's a much cleaner solution I think - closing this one.