Advisory: Support Portal Maintenance. Login is currently unavailable, more info available here.


Detecting RED TEAM Activity

I suspect for most of us reading these posts, we have had the experience of a RedTeam test.  This is where you as a business hire an outside party to perform a penetration test of your organization. They can use lots of different tactics from phishing to exploits, to vulnerabilities. The way these test normally run is you only tell a few folks in the IT department that the test is underway.  The challenge is to see if the defenses hold or the mock adversary breaches the infrastructure.

Now having worked with a number of RedTeams before they tend to use the same types of noisy attack methods.  And as these are often the same attack methods used by an advanced threat actors and even by run of the mill criminal groups I thought it would be nice to detect their activity quickly.

Instead of writing hundreds of rules mapping them to MITRE ATT&CK (Ive been doing that as well) I wanted a SINGLE, SMALL, and fairly FAST query that could detect likely red team activity.

Knowing that RedTeams often avoid the use of malware and instead try and use available system tools for their actions, I thought it would be nice to look for rare use of system tools.  We already have such a query in the system, but it is too FP prone for my needs.  I wanted something that could look over weeks of time, and identify the rare or unique use of a system tool run from a powershell or command line...

Seeing that some powershell directly ran the MS Dll injector (mavinject.exe) for the first time in 30 days on some help desk admins machine sure is a tell tale sign that your systems are under attack either by the un-announced red team or some real threat.

Here is what I came up with.

-- VARIABLE: $$Days to Check$$                 STRING
-- VARIABLE: $$Having Run N or Fewer Times$$   STRING
-- VARIABLE: $$Parent Process Name$$           STRING

-- QUERY NAME: SUSPECT System tools run  X or fewer times in last Y days by process name Z
-- DESCRIPTION: Lists occasions when rarely-run system tools were used in the last 30 days
--              EXAMPLE: Parent Process name   
--              - cmd.exe
--              - powershell.exe
--              - %

-- First we need to get a list of SophosPIDs for rarely run executables in the C:\Windows directory tree
-- We will count the number of instances seen and ensure it is below the threashold using a GROUP BY and HAVING statement.
-- For each processname we will collect the SPIDs for each execution using a jsongroup.
WITH systemToolsRun AS (
   SELECT json_group_array(json_object('SophosPID', spj.sophosPID)) SPIDS, COUNT(spj.pathname) runCount
   FROM sophos_process_journal spj
   WHERE spj.pathname LIKE 'C:\Windows\%.exe' AND spj.pathname NOT LIKE 'C:\Windows\Temp\%' AND spj.pathname NOT LIKE 'C:\Windows\SoftwareDistribution\%' AND 
      spj.time >= strftime('%s','now', '-$$Days to Check$$ days') AND spj.eventType = 0
   GROUP BY spj.pathname HAVING (runCount <= $$Having Run N or Fewer Times$$)
-- Now that we have a list of rare SPIDs we need to split them up as a list of individual SPIDs
SPID_List AS (SELECT CAST(json_extract(sys_details.value, '$.SophosPID') AS TEXT) SophosPID FROM systemToolsRun sys, json_each(sys.SPIDS) sys_details )

-- Given our list of sophos PIDs now collect additional useful information on the process, like its name, who ran it, the cmdline and its parent.
-- We use that Parent name information to cull the list from just rarely run proceses to rare processess with a specific ancestor
    CAST(DateTime(spj.time,'unixepoch') AS TEXT) Date_Time, 
    CAST( (SELECT username FROM users WHERE uid = replace(spj.sid, rtrim(spj.sid, replace(spj.sid, '-', '')), '')) AS TEXT) User_Name,
    CAST(spj.processName AS TEXT) Process_Name,
    CAST(spj.pathname AS TEXT) Path_Name,
    CAST(spj.cmdline AS TEXT) Cmd_Line,
    CAST(spj.sophosPID AS TEXT) SophosPID,
    CAST( (SELECT processname FROM sophos_process_journal spj2 WHERE spj2.sophosPID = spj.parentSophosPID AND spj2.eventType = 0) AS TEXT) Parent_Process_Name,
    CAST(spj.parentSophosPID AS TEXT) Parent_SophosPID
   LEFT JOIN sophos_process_journal spj ON spj.eventtype = 0 AND spj.sophosPID = SPID.SophosPID AND spj.time = replace(SPID.SophosPID, rtrim(SPID.SophosPID, replace(SPID.SophosPID,':','')),'')/10000000-11644473600
WHERE Parent_Process_Name LIKE '$$Parent Process Name$$'

It does appear to work  Have fun.