def make_file(self, event: dict) -> Optional[Tuple[File, Process, File]]: """Converts a fileWriteEvent to two nodes, a file and the process manipulated the file. Generates a process - (Wrote) -> File edge. Parameters ---------- event : dict The fileWriteEvent event. Returns ------- Optional[Tuple[File, Process, File]] Returns a tuple contaning the File that this event is focused on, and the process which manipulated the file. The process has a Wrote edge to the file. Also contains the file that the process belongs to. """ if "filePath" not in event: return None # Add the drive where possible. if event.get("drive"): file_path = f"{event['drive']}:\\{event['filePath']}" else: file_path = event["filePath"] hashes: Dict[str, str] = {} if event.get("md5"): hashes = {"md5": event["md5"]} # Create the file node. file_node = File(file_path=file_path, file_name=event["fileName"], hashes=hashes) # Set the extension file_node.set_extension() # Set the process node process = Process( process_id=int(event["pid"]), process_image=event["process"], process_image_path=event["processPath"], user=event.get("username"), ) # Add a wrote edge with the contents of the file write. process.wrote[file_node].append( timestamp=int(event["event_time"]), contents=event.get("textAtLowestOffset") ) # Pull out the image of the process proc_file_node = process.get_file_node() # File - (File Of) -> Process proc_file_node.file_of[process] return (file_node, process, proc_file_node)
def make_basic_file(self, event: dict) -> Tuple[Process, File, File]: """Transforms a file based event. Support events: 1. EventTypes.FILE_DELETED 2. EventTypes.FILE_OPENED 3. EventTypes.FILE_WRITTEN 4. EventTypes.LOADED_MODULE Parameters ---------- event : dict [description] Returns ------- Tuple[Process, File, File] [description] """ process = Process( process_image=event[FieldNames.PROCESS_IMAGE], process_image_path=event[FieldNames.PROCESS_IMAGE_PATH], process_id=int(event[FieldNames.PROCESS_ID]), command_line=event[FieldNames.COMMAND_LINE], ) proc_file = process.get_file_node() proc_file.file_of[process] file_node = File( file_path=event[FieldNames.FILE_PATH], file_name=event[FieldNames.FILE_NAME], hashes=event.get(FieldNames.HASHES), ) file_node.set_extension() # Switch based on the event type event_type = event[FieldNames.EVENT_TYPE] if event_type == EventTypes.FILE_OPENED: process.accessed[file_node] elif event_type == EventTypes.FILE_WRITTEN: process.wrote[file_node] elif event_type == EventTypes.LOADED_MODULE: process.loaded[file_node] else: process.deleted[file_node] return (process, proc_file, file_node)
def testEquals(): file_node = File(file_path="c:/windows", file_name="test.exe", extension=".exe") file_node2 = File(file_path="c:/windows", file_name="test.exe", extension=".exe") assert file_node == file_node2 assert hash(file_node) == hash(file_node2)
def make_file_copy(self, event: dict) -> Tuple[Process, File, File, File]: process = Process( process_image=event[FieldNames.PROCESS_IMAGE], process_image_path=event[FieldNames.PROCESS_IMAGE_PATH], process_id=int(event[FieldNames.PROCESS_ID]), command_line=event[FieldNames.COMMAND_LINE], ) proc_file = process.get_file_node() proc_file.file_of[process] # Source file src_file = File( file_path=event[FieldNames.SRC_FILE][FieldNames.FILE_PATH], file_name=event[FieldNames.SRC_FILE][FieldNames.FILE_NAME], hashes=event[FieldNames.SRC_FILE].get(FieldNames.HASHES), ) # Dest file src_file.set_extension() dest_file = File( file_path=event[FieldNames.DEST_FILE][FieldNames.FILE_PATH], file_name=event[FieldNames.DEST_FILE][FieldNames.FILE_NAME], hashes=event[FieldNames.DEST_FILE].get(FieldNames.HASHES), ) dest_file.set_extension() src_file.copied_to[dest_file] process.copied[src_file] return (process, proc_file, src_file, dest_file)
def test_add_nodes_no_overlap(nx): proc = Process(process_id=10, process_image="test.exe", command_line="test.exe /c foobar") other_proc = Process(process_id=12, process_image="best.exe", command_line="best.exe /c 123456") proc.launched[other_proc].append(timestamp=1) backend = NetworkX(consolidate_edges=True, nodes=[proc, other_proc]) G = backend.graph() assert len(G.nodes()) == 2 assert len(G.edges()) == 1 # Add in a new pair of nodes. proc2 = Process(process_id=4, process_image="malware.exe", command_line="malware.exe /c foobar") f = File(file_name="foo", file_path="bar") proc2.wrote[f] G = backend.add_nodes([proc2, f]) # Graph grew assert len(G.nodes()) == 4 assert len(G.edges()) == 2
def test_file_of(): file_node = File(file_path="c:/windows", file_name="test.exe", extension=".exe") proc = Process(process_id=0, process_image="test.exe", process_image_path="c:/windows/test.exe") file_node.file_of[proc] assert proc in file_node.file_of
def testNotFileOf(): file_node = File(file_path="c:/windows", file_name="test.exe", extension=".exe") proc = Process(process_id=0, process_image="best.exe", process_image_path="c:/windows/best.exe") assert proc not in file_node.file_of
def access_file(self, event) -> Tuple[Process, File]: proc = Process(process_id=int(event["process_id"]), process_image=event["process_name"]) file_name, file_path = split_path(event["path"]) target_file = File(file_name=file_name, file_path=file_path) proc.accessed[target_file].append(timestamp=event["event_time"]) return (proc, target_file)
def file_created(self, event: dict) -> Tuple[Process, File, File]: process_image, process_path = split_path(event["EventData_Image"]) proc = SysMonProc( host=event["Computer"], user=event.get("EventData_User"), process_guid=event["EventData_ProcessGuid"], process_id=int(event["EventData_ProcessId"]), process_image=process_image, process_image_path=process_path, ) proc_file = proc.get_file_node() proc_file.file_of[proc] file_image, file_path = split_path(event["EventData_TargetFilename"]) file_node = File(file_name=file_image, file_path=file_path) proc.accessed[file_node].append(timestamp=event["EventData_UtcTime"]) return (proc, proc_file, file_node)
def test_add_node_overlaps_existing(nx): proc = Process(process_id=10, process_image="test.exe", command_line="test.exe /c foobar") other_proc = Process(process_id=12, process_image="best.exe", command_line="best.exe /c 123456") proc.launched[other_proc].append(timestamp=1) backend = NetworkX(consolidate_edges=True, nodes=[proc, other_proc]) G = backend.graph() assert len(G.nodes()) == 2 assert len(G.edges()) == 1 # Add a new node that *overlaps* an existing node (note - not the same node object.) proc2 = Process(process_id=10, process_image="test.exe", command_line="test.exe /c foobar") f = File(file_name="foo", file_path="bar") proc2.wrote[f] G = backend.add_nodes([proc2, f]) # Graph grew, but only 3 nodes. assert len(G.nodes()) == 3 assert len(G.edges()) == 2 # Process should have both write and launched edges. u = hash(proc2) v = hash(other_proc) v2 = hash(f) assert networkx.has_path(G, u, v) assert networkx.has_path(G, u, v2) assert "Launched" in G[u][v] assert "Wrote" in G[u][v2]
def file_events( self, event: dict ) -> Union[Tuple[Process, File, File], Tuple[Process, File, File, File]]: """Transforms a file event Example file event:: { "mode": "created", "fid": { "ads": "", "content": 2533274790555891 }, "processinfo": { "imagepath": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", "md5sum": "eb32c070e658937aa9fa9f3ae629b2b8", "pid": 2956 }, "ntstatus": "0x0", "value": "C:\\Users\\admin\\AppData\\Local\\Temp\\sy24ttkc.k25.ps1", "CreateOptions": "0x400064", "timestamp": 9494 } In 8.2.0 the `value` field became a dictionary when the mode is `failed`:: "values": { "value": "C:\\Users\\admin\\AppData\\Local\\Temp\\sy24ttkc.k25.ps1"" } Parameters ---------- event : dict The source event Returns ------- Tuple[Process, File, File] The process, the process' image, and the file written. """ proc_info = event["processinfo"] process_image, process_image_path = split_path(proc_info["imagepath"]) proc = Process( process_id=int(proc_info["pid"]), process_image=process_image, process_image_path=process_image_path, ) proc_file = proc.get_file_node() proc_file.file_of[proc] # 8.2.0 changes. if "values" in event: full_path = event["values"]["value"] else: full_path = event["value"] file_name, file_path = split_path(full_path) file_node = File(file_name=file_name, file_path=file_path) file_node.set_extension() if event["mode"] == "created": proc.wrote[file_node].append(timestamp=event["timestamp"]) elif event["mode"] == "deleted": proc.deleted[file_node].append(timestamp=event["timestamp"]) elif event["mode"] == "CopyFile": src_name, src_path = split_path(event["source"]) src_file = File(file_name=src_name, file_path=src_path) src_file.set_extension() src_file.copied_to[file_node].append(timestamp=event["timestamp"]) proc.copied[src_file].append(timestamp=event["timestamp"]) return (proc, proc_file, file_node, src_file) else: proc.accessed[file_node].append(timestamp=event["timestamp"]) return (proc, proc_file, file_node)
def test_explicit_full_path_overrides(): file_node = File(file_path="c:\\windows", file_name="test.exe", full_path="foobar") assert file_node.full_path == "foobar"
def test_create(): file_node = File(file_path="c:/windows", file_name="test.exe", extension=".exe") assert type(file_node) == File
def test_set_full_path(): file_node = File(file_path="c:\\windows", file_name="test.exe") assert file_node.full_path == "c:\\windows\\test.exe"
def test_set_extension(): file_node = File(file_path="c:/windows", file_name="test.exe") file_node.set_extension() assert file_node.extension == "exe"