detection-hackathon-apt29
detection-hackathon-apt29 copied to clipboard
4.C) File and Directory Discovery, System Owner/User Discovery, System Information Discovery, System Network Configuration Discovery, Process Discovery, Security Software Discovery, Permission Groups Discovery, Execution through API
Description
Finally, the attacker launches a PowerShell script that performs a wide variety of reconnaissance commands (T1083, T1033, T1082, T1016, T1057, T1063, T1069), some of which are done by accessing the Windows API (T1106).
Invoke-Discovery
Maybe?: https://github.com/mitre-attack/attack-arsenal/blob/master/adversary_emulation/APT29/Emulation_Plan/Day%201/payloads/SysinternalsSuite/readme.txt#L591
Basic Query to find for handles requested to AD objects over the network
SELECT o.`@timestamp`, o.TargetUserName, o.TargetLogonId,
a.EventID, a.ObjectName, a.ObjectType, a.ObjectServer, a.Hostname
FROM apt29Table o
INNER JOIN (
SELECT EventID, SubjectLogonId, ObjectName, ObjectType, ObjectServer, Hostname
FROM apt29Table
WHERE lower(Channel) = "security"
AND EventID = 4661
AND ObjectType = "SAM_DOMAIN"
AND ObjectServer = "Security Account Manager"
) a
ON o.TargetLogonId = a.SubjectLogonId
WHERE lower(Channel) = "security"
AND o.EventID = 4624
AND o.LogonType = 3
AND NOT o.TargetUserName LIKE "%$"
Results:
-RECORD 0----------------------------------
@timestamp | 2020-05-02T03:04:05.749Z
TargetUserName | pbeesly
TargetLogonId | 0x5dd594
EventID | 4661
ObjectName | DC=dmevals,DC=local
ObjectType | SAM_DOMAIN
ObjectServer | Security Account Manager
Hostname | NEWYORK.dmevals.local
4.C.12 Execution through API
Detection Gategory - Telemetry
Procedure: Executed API call by reflectively loading Netapi32.dll Criteria: The NetUserGetLocalGroups API function loaded into powershelle.exe from Netapi32.dll
Query needs some validation and filtering
netapi = spark.sql(
'''
SELECT Image, count(*) as count
FROM apt29Table
WHERE Channel = "Microsoft-Windows-Sysmon/Operational"
AND EventID = 7 AND LOWER(ImageLoaded) LIKE "%netapi32.dll%"
GROUP BY Image
ORDER BY count DESC
''')
netapi.show(100,truncate = False, vertical = False)
results
+------------------------------------------------------------------------+-----+
|Image |count|
+------------------------------------------------------------------------+-----+
|C:\Windows\System32\wbem\WmiPrvSE.exe |6 |
|C:\Windows\System32\sppsvc.exe |5 |
|C:\Windows\System32\svchost.exe |5 |
|C:\Windows\PSEXESVC.exe |4 |
|C:\Program Files\SysinternalsSuite\PsExec64.exe |4 |
|C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe |3 |
|C:\Windows\System32\WerFault.exe |2 |
|C:\Program Files (x86)\Google\Update\1.3.35.452\GoogleCrashHandler.exe |2 |
|C:\Program Files (x86)\Google\Update\1.3.35.452\GoogleCrashHandler64.exe|2 |
|C:\ProgramData\victim\‮cod.3aka3.scr |1 |
|C:\Windows\System32\rundll32.exe |1 |
|C:\Windows\System32\wermgr.exe |1 |
|C:\Windows\Temp\python.exe |1 |
+------------------------------------------------------------------------+-----+
4.C.1 File and Directory Discovery
Procedure: Enumerated user's temporary directory path using PowerShell Criteria: powershell.exe executing $env:TEMP
Sysmon + PowerShell Logs
SELECT Message
FROM apt29Host f
INNER JOIN (
SELECT d.ProcessId
FROM apt29Host d
INNER JOIN (
SELECT a.ProcessGuid, a.ParentProcessGuid
FROM apt29Host a
INNER JOIN (
SELECT ProcessGuid
FROM apt29Host
WHERE Channel = "Microsoft-Windows-Sysmon/Operational"
AND EventID = 1
AND LOWER(Image) LIKE "%control.exe"
AND LOWER(ParentImage) LIKE "%sdclt.exe"
) b
ON a.ParentProcessGuid = b.ProcessGuid
WHERE a.Channel = "Microsoft-Windows-Sysmon/Operational"
AND a.EventID = 1
AND a.IntegrityLevel = "High"
) c
ON d.ParentProcessGuid= c.ProcessGuid
WHERE d.Channel = "Microsoft-Windows-Sysmon/Operational"
AND d.EventID = 1
AND d.Image LIKE '%powershell.exe'
) e
ON f.ExecutionProcessID = e.ProcessId
WHERE f.Channel = "Microsoft-Windows-PowerShell/Operational"
AND f.EventID = 4104
AND LOWER(f.ScriptBlockText) LIKE "%$env:temp%"
Results
Creating Scriptblock text (1 of 1):
function Invoke-Discovery {
$DiscoveryInfo =@()
$CurrentDir = Get-Location
$DiscoveryInfo += [PSCustomObject]@{
CurrentDirectory = $CurrentDir
TempDirectory = $env:TEMP
UserName = $env:USERNAME
ComputerName = $env:COMPUTERNAME
UserDomain = $env:USERDOMAIN
CurrentPID = $PID
}
$DiscoveryInfo | Format-List
$NameSpace = Get-WmiObject -Namespace "root" -Class "__Namespace" | Select Name | Out-String -Stream | Select-String "SecurityCenter"
foreach ($SecurityCenter in $NameSpace) {
Get-WmiObject -Namespace "root\$SecurityCenter" -Class AntiVirusProduct -ErrorAction SilentlyContinue | Select DisplayName, InstanceGuid, PathToSignedProductExe, PathToSignedReportingExe, ProductState, Timestamp | Format-List
WmiObject -Namespace "root\$SecurityCenter" -Class FireWallProduct -ErrorAction SilentlyContinue | Select DisplayName, InstanceGuid, PathToSignedProductExe, PathToSignedReportingExe, ProductState, Timestamp | Format-List
}
Gwmi Win32_OperatingSystem | Select Name, OSArchitecture, CSName, BuildNumber, Version | Format-List
Invoke-NetUserGetGroups
Invoke-NetUserGetLocalGroups
}
ScriptBlock ID: 70878299-2ee1-4a5d-869f-124b349aee1d
Path: C:\Program Files\SysinternalsSuite\readme.ps1
Security Logs + PowerShell Logs
SELECT Message
FROM apt29Host f
INNER JOIN (
SELECT split(d.NewProcessId, '0x')[1] as NewProcessId
FROM apt29Host d
INNER JOIN(
SELECT a.ProcessId, a.NewProcessId
FROM apt29Host a
INNER JOIN (
SELECT NewProcessId
FROM apt29Host
WHERE LOWER(Channel) = "security"
AND EventID = 4688
AND LOWER(NewProcessName) LIKE "%control.exe"
AND LOWER(ParentProcessName) LIKE "%sdclt.exe"
) b
ON a.ProcessId = b.NewProcessId
WHERE LOWER(a.Channel) = "security"
AND a.EventID = 4688
AND a.MandatoryLabel = "S-1-16-12288"
AND a.TokenElevationType = "%%1937"
) c
ON d.ProcessId = c.NewProcessId
WHERE LOWER(d.Channel) = "security"
AND d.EventID = 4688
AND d.NewProcessName LIKE '%powershell.exe'
) e
ON LOWER(hex(f.ExecutionProcessID)) = e.NewProcessId
WHERE f.Channel = "Microsoft-Windows-PowerShell/Operational"
AND f.EventID = 4104
AND LOWER(f.ScriptBlockText) LIKE "%$env:temp%"
Results
Creating Scriptblock text (1 of 1):
function Invoke-Discovery {
$DiscoveryInfo =@()
$CurrentDir = Get-Location
$DiscoveryInfo += [PSCustomObject]@{
CurrentDirectory = $CurrentDir
TempDirectory = $env:TEMP
UserName = $env:USERNAME
ComputerName = $env:COMPUTERNAME
UserDomain = $env:USERDOMAIN
CurrentPID = $PID
}
$DiscoveryInfo | Format-List
$NameSpace = Get-WmiObject -Namespace "root" -Class "__Namespace" | Select Name | Out-String -Stream | Select-String "SecurityCenter"
foreach ($SecurityCenter in $NameSpace) {
Get-WmiObject -Namespace "root\$SecurityCenter" -Class AntiVirusProduct -ErrorAction SilentlyContinue | Select DisplayName, InstanceGuid, PathToSignedProductExe, PathToSignedReportingExe, ProductState, Timestamp | Format-List
WmiObject -Namespace "root\$SecurityCenter" -Class FireWallProduct -ErrorAction SilentlyContinue | Select DisplayName, InstanceGuid, PathToSignedProductExe, PathToSignedReportingExe, ProductState, Timestamp | Format-List
}
Gwmi Win32_OperatingSystem | Select Name, OSArchitecture, CSName, BuildNumber, Version | Format-List
Invoke-NetUserGetGroups
Invoke-NetUserGetLocalGroups
}
ScriptBlock ID: 70878299-2ee1-4a5d-869f-124b349aee1d
Path: C:\Program Files\SysinternalsSuite\readme.ps1
4.C.2 System Owner/User Discovery
Procedure: Enumerated the current username using PowerShell Criteria: powershell.exe executing $env:USERNAME
Same as before but looking for
LIKE "%$env:username%"
4.C.3 System Information Discovery
Procedure: Enumerated the computer hostname using PowerShell
Criteria: powershell.exe executing $env:COMPUTERNAME
Same as before but looking for
LIKE "%$env:computername%"
4.C.4 System Network Configuration Discovery
Procedure: Enumerated the current domain name using PowerShell Criteria: powershell.exe executing $env:USERDOMAIN
Same as before but looking for
LIKE "%$env:userdomain%"
4.C.5 Process Discovery
Procedure: Enumerated the current process ID using PowerShell Criteria: powershell.exe executing $PID
Same as before but looking for LIKE "%$pid%"
4.C.6 System Information Discovery
Procedure: Enumerated the OS version using PowerShell Criteria: powershell.exe executing Gwmi Win32_OperatingSystem
Same as before but looking for
"%gwmi win32_operatingsystem%"
4.C.7 Security Software Discovery
Procedure: Enumerated anti-virus software using PowerShell Criteria: powershell.exe executing Get-WmiObject ... -Class AntiVirusProduct
Same as before but looking for
"%-class antivirusproduct%"
4.C.8 Security Software Discovery
Procedure: Enumerated firewall software using PowerShell Criteria: powershell.exe executing Get-WmiObject ... -Class FireWallProduct
Same as before but looking for
"%-class firewallproduct%"
4.C.9 Permission Groups Discovery
Procedure: Enumerated user's domain group membership via the NetUserGetGroups API Criteria: powershell.exe executing the NetUserGetGroups API
One could look for the Invoke-NetUserGetGroups
script name in PowerShell Logs but that can be bypassed by simply renaming the function/script :/
Another option (I like the most) is to look for Domain users requesting handles to SAM_DOMAIN objects with access rights 'getlocalgroupmembership'
Security Event Logs
SELECT a.EventTime, o.TargetUserName, o.IpAddress, a.Message
FROM apt29Table o
INNER JOIN (
SELECT Message, EventTime, SubjectLogonId
FROM apt29Table
WHERE lower(Channel) = "security"
AND EventID = 4661
AND ObjectType = "SAM_DOMAIN"
AND SubjectUserName NOT LIKE '%$'
AND AccessMask = '0x20094'
AND LOWER(Message) LIKE '%getlocalgroupmembership%'
) a
ON o.TargetLogonId = a.SubjectLogonId
WHERE lower(Channel) = "security"
AND o.EventID = 4624
AND o.LogonType = 3
Results
EventTime | 2020-05-01 23:04:04
TargetUserName | pbeesly
IpAddress | 10.0.1.4
Message | A handle to an object was requested.
Subject :
Security ID: S-1-5-21-1830255721-3727074217-2423397540-1107
Account Name: pbeesly
Account Domain: DMEVALS
Logon ID: 0x5DD594
Object:
Object Server: Security Account Manager
Object Type: SAM_DOMAIN
Object Name: DC=dmevals,DC=local
Handle ID: 0x1fccecf7240
Process Information:
Process ID: 0x2c0
Process Name: C:\Windows\System32\lsass.exe
Access Request Information:
Transaction ID: {00000000-0000-0000-0000-000000000000}
Accesses: READ_CONTROL
ReadOtherParameters
CreateUser
GetLocalGroupMembership
Access Reasons: -
Access Mask: 0x20094
Privileges Used for Access Check: -
Properties: ---
{19195a5a-6da0-11d0-afd3-00c04fd930c9}
READ_CONTROL
ReadOtherParameters
CreateUser
GetLocalGroupMembership
{c7407360-20bf-11d0-a768-00aa006e0529}
{bf9679a4-0de6-11d0-a285-00aa003049e2}
{bf9679a5-0de6-11d0-a285-00aa003049e2}
{bf9679a6-0de6-11d0-a285-00aa003049e2}
{bf9679bb-0de6-11d0-a285-00aa003049e2}
{bf9679c2-0de6-11d0-a285-00aa003049e2}
{bf9679c3-0de6-11d0-a285-00aa003049e2}
{bf967a09-0de6-11d0-a285-00aa003049e2}
{bf967a0b-0de6-11d0-a285-00aa003049e2}
{b8119fd0-04f6-4762-ab7a-4986c76b3f9a}
{bf967a34-0de6-11d0-a285-00aa003049e2}
{bf967a33-0de6-11d0-a285-00aa003049e2}
{bf9679c5-0de6-11d0-a285-00aa003049e2}
{bf967a61-0de6-11d0-a285-00aa003049e2}
{bf967977-0de6-11d0-a285-00aa003049e2}
{bf96795e-0de6-11d0-a285-00aa003049e2}
{bf9679ea-0de6-11d0-a285-00aa003049e2}
{ab721a52-1e2f-11d0-9819-00aa0040529b}
Restricted SID Count: 0
4.C.10 Execution through API
Procedure: Executed API call by reflectively loading Netapi32.dll Criteria: The NetUserGetGroups API function loaded into powershell.exe from Netapi32.dll
Correlating the netapi32.dll load with the previous Bypassuac deff helps to add contexts since that DLL is loaded by several other process.
Sysmon
SELECT Message
FROM apt29Host f
INNER JOIN (
SELECT d.ProcessGuid
FROM apt29Host d
INNER JOIN (
SELECT a.ProcessGuid, a.ParentProcessGuid
FROM apt29Host a
INNER JOIN (
SELECT ProcessGuid
FROM apt29Host
WHERE Channel = "Microsoft-Windows-Sysmon/Operational"
AND EventID = 1
AND LOWER(Image) LIKE "%control.exe"
AND LOWER(ParentImage) LIKE "%sdclt.exe"
) b
ON a.ParentProcessGuid = b.ProcessGuid
WHERE a.Channel = "Microsoft-Windows-Sysmon/Operational"
AND a.EventID = 1
AND a.IntegrityLevel = "High"
) c
ON d.ParentProcessGuid= c.ProcessGuid
WHERE d.Channel = "Microsoft-Windows-Sysmon/Operational"
AND d.EventID = 1
AND d.Image LIKE '%powershell.exe'
) e
ON f.ProcessGuid = e.ProcessGuid
WHERE f.Channel = "Microsoft-Windows-Sysmon/Operational"
AND f.EventID = 7
AND LOWER(f.ImageLoaded) LIKE "%netapi32.dll"
Results
Image loaded:
RuleName: -
UtcTime: 2020-05-02 03:04:04.361
ProcessGuid: {47ab858c-e23d-5eac-c603-000000000400}
ProcessId: 3876
Image: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
ImageLoaded: C:\Windows\System32\netapi32.dll
FileVersion: 10.0.18362.1 (WinBuild.160101.0800)
Description: Net Win32 API DLL
Product: Microsoft® Windows® Operating System
Company: Microsoft Corporation
OriginalFileName: NetApi32.DLL
Hashes: SHA1=7DFDD188B30162DAA87FDD3E5B7A55C80CD839F1,MD5=86A9DF3FA9D5FCAC8EF57601FCCD78F9,SHA256=F8CDA38FD3FF371E772875EA657A37662321EBB7AD8D6978DBCCCA7FC6DB64F1,IMPHASH=7D4E1695394CF47BD397AFD45A40E55D
Signed: true
Signature: Microsoft Windows
SignatureStatus: Valid
4.C.11 Permission Groups Discovery
Procedure: Enumerated user's local group membership via the NetUserGetLocalGroups API Criteria: powershell.exe executing the NetUserGetLocalGroups API
One could look for the Invoke-NetUserGetLocalGroups script name in PowerShell Logs but that can be bypassed by simply renaming the function/script :/
4.C.12 Execution through API
Procedure: Executed API call by reflectively loading Netapi32.dll Criteria: The NetUserGetLocalGroups API function loaded into powershelle.exe from Netapi32.dll
Correlating the netapi32.dll load with the previous Bypassuac deff helps to add contexts since that DLL is loaded by several other process. Same as query above 4.C.10