Edge
Node
Node
Node
Edge
Manage from reality because that's the prepared Defenders Mindset
https://github.com/BloodHoundAD/BloodHound
[
{
'ppid': 100,
'pid': 250,
'action': 'started',
'time': '2019-02-24 18:21:25'
},
{
'ppid': 250,
'pid': 350,
'action': 'started',
'time': '2019-02-24 18:22:31'
}
]
[
{
'pid': 350,
'action': 'connected_to',
'domain': 'evil.com',
'time': '2019-02-24 18:24:27'
},
{
'pid': 350,
'action': 'created_file',
'path': 'downloads/evil.exe',
'time': '2019-02-24 18:28:31'
}
]
seen_at: '2019-02-24 18:21:25'
created_at: '2019-02-24 18:21:25'
created_at: '2019-02-24 18:22:31'
seen_at: '2019-02-24 18:24:27'
seen_at: '2019-02-24 18:28:31'
seen_at: '2019-02-24 18:22:31'
seen_at: '2019-02-24 18:24:27'
seen_at: '2019-02-24 18:28:31'
{
"host_id": "cobrien-mac",
"parent_pid": 3,
"pid": 4,
"image_name": "word.exe",
"create_time": 600,
}
{
"host_id": "cobrien-mac",
"parent_pid": 4,
"pid": 5,
"image_name": "payload.exe",
"create_time": 650,
}
explorer.exe
payload.exe
word.exe
word.exe
payload.exe
word.exe
word.exe
payload.exe
ssh.exe
/secret/file
11.22.34.55
mal.doc
Grapl
Graph Analytics Platform
for Detection, and Incident Response
Parsing
Subgraph Generation
Identification
Merging
Analysis
Engagements
Process {
node_key: string ,
asset_id: string ,
process_id: int ,
process_guid: string ,
created_timestamp: int ,
terminated_timestamp: int ,
last_seen_timestamp: int ,
process_name: string ,
process_command_line: string ,
process_integrity_level: string ,
operating_system: string ,
process_path: File ,
children: [Process] ,
created_files: [File] ,
deleted_files: [File] ,
read_files: [File] ,
wrote_files: [File] ,
}
File {
node_key: string ,
asset_id: string ,
created_timestamp: int ,
deleted_timestamp: int ,
last_seen_timestamp: int ,
file_name: string ,
file_path: string ,
file_extension: string ,
file_mime_type: string ,
file_size: int ,
file_version: string ,
file_description: string ,
file_product: string ,
file_company: string ,
file_directory: string ,
file_inode: int ,
file_hard_links: int ,
md5_hash: string ,
sha1_hash: string ,
sha256_hash: string ,
}
OutboundConnection {
port: int,
created: int,
ended: int,
external_connections: ExternalIp
}
InboundConnection {
port: int,
created: int,
ended: int,
}
Asset {
operating_system: string,
processes: [Process],
}
ExternalIp {
port: int,
address: str,
}
Unstable
Unstable
pid: 1200,
timestamp: 1551754000
pid: 1200,
timestamp: 1551755000
pid: 1200,
timestamp: 1551766000
834c1419-7987-41d6-a3c4-1c811e00fecb
981f30b9-e076-4713-9f83-999e40695dd0
Identity
{
'pid': 100,
'type': 'process_start',
'timestamp': 1551753726,
'image': '/usr/bin/program',
}
{
'pid': 100,
'type': 'file_read',
'path': '/home/.cache/file',
'timestamp': 1551754726
}
{
'pid': 100,
'type': 'process_terminate',
'timestamp': 1551755726,
'image': '/usr/bin/program',
}
pid: 100
created: 1551753726
terminated: 1551755726
image: '/usr/bin/program'
node_key: <uuid>
{
'ppid': 100,
'pid': 120,
'type': 'process_start',
'timestamp': 1551753726,
'image_name': 'program.exe',
}
executed
pid: 100
pid: 120
node_key:<GUID>
node_key:<GUID>
image: 'program.exe'
{
'pid': 120,
'type': 'process_create_file',
'timestamp': 1551754726,
'image_name': 'program.exe',
'file_path': 'path/to/file.doc'
}
node_key:<GUID>
path: 'path/to/file.doc'
create
1551754726
{
'pid': 120,
'type': 'process_read_file',
'timestamp': 1551757726,
'image_name': 'program.exe',
'file_path': 'path/to/file.doc'
}
read
1551757726
ppid, type
pid, image_name, type
Redundant:
pid, file_path,
image_name, type
1551753726
Process Execution
File Creation
File Read
{
'type': 'Process Create',
'image': 'C:\Program Files\Microsoft Office\winword.exe',
'pid': 1200,
'ppid': 1080
}
{
'type': 'Process Create',
'image': 'C:\Windows\WindowsPowershell\v1.0\powershell.exe',
'pid': 2590,
'ppid': 1200,
}
word.exe
powershell.exe
unique parent child
<any process>
<any file>
<external ip>
Process with external network access creates file, executes child from it
<any browser>
Browser Executing Child Process
<any process>
<winrar/7zip/zip>
<any file>
Process Executed From Unpacked Binary
<any process>
created file
executed as
<any process>
Rare Parent Child Process
<any process>
executed
executed
created file
executed as
connected to
executed
<any process>
Rare Parent LOLBAS Process
<any process>
executed
<binary>
<lolbas path>
<word/reader/etc>
Commonly Target Application with Non Whitelisted Child Process
<non whitelisted process>
executed
Risk: 5
Risk: 70
Risk: 5
Risk: 20
Risk: 10
Risk: 15
chrome.exe
<cmd.exe>
executed
<zip.exe>
<flash_installer.exe>
<flash_installer.exe>
created file
executed as
<52.217.32.54>
connected to
<svchost.exe>
created file
executed
executed
<svchost.exe>
name: 'Browser Executing Child Process'
score: 5
name: Process with external network access creates file, executes child from it
score: 25
name:Unpacked process execution
score: 10
Risk Node
Risk Node
Risk Node
Asset Lens
score: 80
<winrar/7zip/zip>
<any file>
Process Executed From Unpacked Binary
<any process>
created file
executed as
ProcessQuery()
.with_bin_file(
FileQuery()
.with_creator(
ProcessQuery()
.with_process_name(eq=Or('zip.exe', '7zip.exe', 'winrar.exe'))
)
)
class CommonTargetWithChildProcess(Analyzer):
def get_queries(self) -> OneOrMany[ProcessQuery]:
return (
ProcessQuery()
.with_process_name(eq="winword.exe")
.with_process_name(eq="excel.exe")
.with_process_name(eq="reader.exe")
.with_children(ProcessQuery())
)
def on_response(self, response: ProcessView, output: Any):
output.send(
ExecutionHit(
analyzer_name="Common Target Application With Child Process",
node_view=response,
risk_score=75,
)
)
Any process
With one of these process names
With any children
Executes for every change to the master graph
class UniqueWindowsBuiltinExecution(Analyzer):
def __init__(self, dgraph_client: DgraphClient, counter: ParentChildCounter):
super(UniqueWindowsBuiltinExecution, self).__init__(dgraph_client)
self.counter = counter
@classmethod
def build(cls: Type[A], dgraph_client: DgraphClient) -> A:
counter = ParentChildCounter(dgraph_client)
return UniqueWindowsBuiltinExecution(dgraph_client, counter)
def get_queries(self) -> OneOrMany[Queryable]:
return (
ProcessQuery().with_process_name()
.with_parent(
ProcessQuery().with_process_name()
.with_bin_file(FileQuery())
)
.with_bin_file(
FileQuery()
.with_file_path(contains='Windows\\\\System32\\')
.with_file_path(contains='Windows\\\\SysWow64\\')
)
)
def on_response(self, response: ProcessView, output: Any):
count = self.counter.get_count_for(
parent_process_name=output.get_parent().get_process_name(),
child_process_name=output.get_process_name(),
)
if count <= 2:
output.send(
ExecutionHit(
analyzer_name="Unique Windows Builtin Execution",
node_view=output,
risk_score=15,
)
)
from analyzers.suspicious_svchost.main import SuspiciousSvchostAnalyzer
class TestSuspiciousSvchost(unittest.TestCase):
def setUp(self) -> None:
self.local_mg = init_local_dgraph()
self.node_view = populate_signature('hardcoded-node-key')
def test_suspicious_svchost_hit(self):
query = SuspiciousSvchostAnalyzer.build(self.local_mg).get_queries()
result = query.query_first(self.local_mg, contains_node_key=self.node_view.node_key)
assert isinstance(result, ExecutionHit)
def test_suspicious_svchost_miss(self):
benign_view = deepcopy(self.node_view)
benign_view.node_key = "some-other-key"
query = SuspiciousSvchostAnalyzer.build(self.local_mg).get_queries()
result = query.query_first(self.local_mg, contains_node_key=benign_view.node_key)
assert result is None
if __name__ == "__main__":
unittest.main()
{
'pid': 250,
'ppid': 150,
'created_at': 1551565127,
'hash': 'acbd18db4cc2f85cedef654fccc4a4d8',
'image_name': '/home/user/downloads/evil.sh'
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
{
~~: ~~
~~: ~~
}
Search Window
pid collision
{
~~: ~~
~~: ~~
}
Alert
engagement = EngagementView.get_or_create('Demo', cclient)
svchost = engagement.get_process('df41941e-1b20-4e61-9a99-34e4e4a56211')
svc_parent = svchost.get_parent()
Suspicious IPC via Plugin
Tracking SSH Hijacking, via Process Tree Analysis
Tracking SSH Hijacking through Process Tree + User Id Analysis
$ git clone git@github.com:insanitybit/grapl.git
$ cd ./grapl/grapl-cdk/
$ npm install -g
$ <your editor> ./.env
BUCKET_PREFIX="<unique identifier>"
$ ./deploy_all.sh
cd /path/to/grapl/
python ./gen-raw-logs.py <bucket prefix>
Questions