We continue to make excellent progress to the intended May release of the Data Lake version of the product.
This week I wanted to demonstrate some of the capabilities we have just added around Pivots and the Depth of information available for admins that need to perform a rapid investigation.
We cover the breadth of capabilities by showing a data lake query looking for hundreds of indicators of compromise across all devices. In the next few weeks we are going to be adding the ability to schedule queries to be run automatically for report generation. Starting from here I will pivot do some interesting information available on the device then drill down to the specific actions a process took and then even deeper down to the contents of a file left behind by the adversary. Below is the 8 min video and the the Extended Process Tree I used to get forensic information off of the device.
Link to Video:
Extended Process Tree Query
-- Extended Process Tree for a SophosPID -- VARIABLE $$SophosPID$$ sophosPID -- NOTE THE PROCESS OR ANCESTORS MAY STILL BE RUNNING SO HANDLE ENDTIME CORRECTLY by changing endtime for running processes to now + 10 min WITH RECURSIVE -- GET A LIST OF ALL ANCESTORS OF A SOPHOS PID Ancestors(SophosPID, Level, parent, processname, pathname, cmdline, sha256, sid, start, end) AS ( -- Define the SEED Row as the information on the SophosPID provided SELECT sophosPID, 0, ParentSophosPID, processname, pathname, cmdline, sha256, sid, time, CASE WHEN (SELECT endtime FROM sophos_process_Journal WHERE sophos_process_journal.SophosPID = '$$SophosPID$$' AND CAST(endtime AS INT) > 0) > 0 THEN (SELECT endtime FROM sophos_process_Journal WHERE sophos_process_journal.SophosPID = '$$SophosPID$$' AND CAST(endtime AS INT) > 0) ELSE strftime('%s','now','+10 minutes') END FROM sophos_process_Journal WHERE sophos_process_journal.SophosPID = '$$SophosPID$$' AND CAST(endtime AS INT) = 0 UNION ALL -- Recursvly identify all decendents SELECT spj.SophosPID, Level - 1, spj.ParentSophosPID, spj.processname, spj.pathname, spj.cmdline, spj.sha256, spj.sid, spj.time, CASE WHEN (SELECT spj2.endtime FROM sophos_process_Journal spj2 WHERE spj2.SophosPID = spj.SophosPID AND CAST(endtime AS INT) > 0) > 0 THEN (SELECT spj2.endtime FROM sophos_process_Journal spj2 WHERE spj2.SophosPID = spj.SophosPID AND CAST(endtime AS INT) > 0) ELSE strftime('%s','now','+10 minutes') END FROM Ancestors JOIN Sophos_Process_Journal spj ON spj.SophosPID = Ancestors.parent AND CAST(spj.endtime AS INT) = 0 -- Perform a Depth First Search ASC would perform a Breadth First Search ORDER BY 2 DESC ), -- Add Row Numbers to the Ancestor List so we order the tree corretly -- EXCLUDE the line for the specified SophosPID so that when we show the tree we do not have a duplicate row Orderd_Ancestors AS (SELECT SophosPID, Level, parent, processname, pathname, cmdline, sha256, sid, start, end, -1 * ROW_Number() OVER () Row FROM Ancestors WHERE SophosPID NOT IN ('$$SophosPID$$') ), -- GET A LIST OF ALL DECENDENTS OF A SOPHOS PID -- NOTE THE PROCESS OR CHILDREN MAY STILL BE RUNNING SO HANDLE ENDTIME CORRECTLY by changing endtime for running processes to now + 10 min Children(SophosPID, Level, parent, processname, pathname, cmdline, sha256, sid, start, end) AS ( -- Define the SEED Row as the information on the SophosPID provided SELECT sophosPID, 0, ParentSophosPID, processname, pathname, cmdline, sha256, sid, time, CASE WHEN (SELECT endtime FROM sophos_process_Journal WHERE sophos_process_journal.SophosPID = '$$SophosPID$$' AND CAST(endtime AS INT) > 0) > 0 THEN (SELECT endtime FROM sophos_process_Journal WHERE sophos_process_journal.SophosPID = '$$SophosPID$$' AND CAST(endtime AS INT) > 0) ELSE strftime('%s','now','+10 minutes') END FROM sophos_process_Journal WHERE sophos_process_journal.SophosPID = '$$SophosPID$$' AND CAST(endtime AS INT) = 0 UNION ALL -- Recursvly identify all decendents SELECT spj.SophosPID, Level +1, spj.ParentSophosPID, spj.processname, spj.pathname, spj.cmdline, spj.sha256, spj.sid, spj.time, CASE WHEN (SELECT spj2.endtime FROM sophos_process_Journal spj2 WHERE spj2.SophosPID = spj.SophosPID AND CAST(endtime AS INT) > 0) > 0 THEN (SELECT spj2.endtime FROM sophos_process_Journal spj2 WHERE spj2.SophosPID = spj.SophosPID AND CAST(endtime AS INT) > 0) ELSE strftime('%s','now','+10 minutes') END FROM Children JOIN Sophos_Process_Journal spj ON spj.ParentSophosPID = Children.SophosPID AND CAST(spj.endtime AS INT) = 0 AND spj.time > Children.start and spj.time < Children.end -- Perform a Depth First Search ASC would perform a Breadth First Search ORDER BY 2 DESC ), -- Add Row Numbers to the Decendent List so we order the tree corretly Orderd_Descendants AS (SELECT SophosPID, Level, parent, processname, pathname, cmdline, sha256, sid, start, end, ROW_Number() OVER () Row FROM Children), -- Now collect the activity for all descendents and the selected sophosPID using a UNION to list the decendent then the file activity it had File_Activity AS ( -- FOR ANCESTORS WE WILL ONLY SHOW THE PROCESS TREE INFO (No activity will be collected) SELECT REPLACE(DATETIME(A.start,'unixepoch'), ' ','T') Date_Time, CASE A.SophosPID WHEN '$$SophosPID$$' THEN A.ProcessName ELSE substr('< < < < < < < < < < < < < < < < < < < < ', 1, A.Level * -2) || A.processName END Process_Tree, '-----------' Subject, '-----------' Action, '-----------' Object, CAST(A.cmdline AS TEXT) Cmd_Line, A.SophosPID SophosPID, A.pathname Process_Pathname, A.sha256 Process_SHA256, A.SID Process_SID, A.Level Level, A.Row Row, 0 Sub_Row, a.start time FROM Orderd_Ancestors A UNION ALL -- SHOW THE PROCESS TREE INFO FOR DESCENDENTS SELECT REPLACE(DATETIME(D.start,'unixepoch'), ' ','T') Date_Time, CASE D.SophosPID WHEN '$$SophosPID$$' THEN D.ProcessName ELSE substr('> > > > > > > > > > > > > > > > > > > > > > ', 1, D.Level * 2) || D.processName END Process_Tree, '-----------' Subject, '-----------' Action, '-----------' Object, CAST(D.cmdline AS TEXT) Cmd_Line, D.SophosPID SophosPID, D.pathname Process_Pathname, D.sha256 Process_SHA256, D.SID Process_SID, D.Level Level, D.Row Row, 0 Sub_Row, D.start time FROM Orderd_Descendants D UNION ALL -- ADD THE PROCESS ACTIVITY FOR EACH DESCENDENT SELECT REPLACE(DATETIME(MIN(spa.time),'unixepoch'),' ','T') Date_Time, CASE D.SophosPID WHEN '$$SophosPID$$' THEN '( '||D.processname||' ) ACTIVITY' ELSE substr('~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~', 1, D.Level * 2) || '( '||D.processname||' ) ACTIVITY' END Process_Tree, spa.subject Subject, CAST(GROUP_CONCAT(DISTINCT spa.action)||' ('||COUNT(spa.action)||')' AS TEXT) Action, spa.object, CAST(D.cmdline AS TEXT) Cmd_Line, D.SophosPID SophosPID, D.pathname Process_Pathname, D.sha256 Process_SHA256, D.SID Process_SID, '' Level, D.Row Row, 1 Sub_Row, spa.time time -- NOTE The details for each process does not include 'FileDataReads', 'FileOtherReads', 'FileBinaryReads', 'Image', 'Thread' FROM Orderd_Descendants D LEFT JOIN Sophos_Process_Activity spa ON spa.subject IN ('DirectoryChanges','Dns','FileBinaryChanges','FileDataChanges','FileOtherChanges','Http','Ip','Network','Url','Registry') AND spa.SophosPID = D.SophosPID AND spa.time > D.start-1 AND spa.time < D.end+1 WHERE spa.subject > '' GROUP BY spa.subject, spa.action, spa.object, spa.SophosPID, D.processname, D.SophosPID, D.pathname, D.sha256, D.SID ) -- Now that we have all activity for each descendent, we need to provide the pretty list showing the Process Tree and File activity for each process in the tree SELECT Date_Time, Process_Tree, Subject, Action, Object, Cmd_Line, SophosPID, Process_Pathname, Process_SHA256, Process_SID, row, sub_row, time,ROW_NUMBER() OVER( ORDER BY Row, Sub_Row, Time) SORT_ORDER_FOR_EXCEL FROM File_Activity ORDER By SORT_ORDER_FOR_EXCEL
ASCII File viewer
-- Perform an ASCII DUMP for a file -- VARIABLE $$File Path$$ STRING SELECT CAST(line AS TEXT) '$$File Path$$' FROM grep WHERE pattern IN (CHAR(0),CHAR(10),CHAR(32)) AND path = '$$File Path$$'