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
{
  '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=output,
                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()$ git clone git@github.com:insanitybit/grapl.git
$ cd ./grapl/grapl-cdk/
$ npm install -g
$ <your editor> ./.env
BUCKET_PREFIX="<unique identifier>"$ ./deploy_all.shcd /path/to/grapl/
python ./gen-raw-logs.py <bucket prefix>Questions