skip to content

What does an EDR stream show when an intruder runs discovery inside a long-lived allow-listed process?

level: middleimportance: should knowfreq 46%

answer

  1. nothing on disk is not nothing recorded
  2. what got mapped into the process
  3. children that lived under a second
  4. lineage was written down at exec time

basics

~20 s

Quite a lot, none of it on disk. Expect shared-object load events into the running process, a burst of sub-second child processes carrying full command lines, and outbound connection events attributed to that process — with no new binary and no file write anywhere.

solid answer

~50 s

Three surfaces survive. Module or shared-object loads show objects mapped into the daemon's address space that it never normally maps. Process executions show the children it spawned — often sub-second, so they are long gone from any live view — each with its full command line and the recorded parent identity, which the sensor captured at creation and therefore keeps even after the parent exits. Network connection events tie outbound sockets to that same process. What you will not have is the artefact people reach for first: no new binary, no file write, nothing to hash or submit. Be honest about the blind area too — work done entirely inside the process that never execs, never maps an object and never opens a socket leaves no event at all, so what you are reading is the parts that had to touch the kernel.

code

json · 7 lines
json
[
  {"event": "ModuleLoad",    "ts": "02:14:03.112Z", "process": "httpd", "process_uid": "a1f7c2", "module": "/dev/shm/.cache/libgs.so"},
  {"event": "ProcessCreate", "ts": "02:14:03.902Z", "process": "sh",    "process_uid": "b3e011", "parent_process_uid": "a1f7c2", "cmdline": "sh -c id; hostname; cat /etc/passwd", "lifetime_ms": 41},
  {"event": "ProcessCreate", "ts": "02:14:04.030Z", "process": "sh",    "process_uid": "b3e0a4", "parent_process_uid": "a1f7c2", "cmdline": "sh -c getent group sudo; mount",     "lifetime_ms": 28},
  {"event": "NetworkConnect","ts": "02:14:07.551Z", "process": "httpd", "process_uid": "a1f7c2", "direction": "outbound", "remote": "203.0.113.44:443"}
  ...
]

go deeper

for a junior

Be ready to name the three surfaces that survive when nothing is written to disk: shared objects mapped into the process, short-lived child processes with their command lines, and outbound connections tied to that process.

for a middle

Explain why sub-second children are still in the stream when they are gone from the host, and how the sensor's own process identifiers keep lineage intact after a parent exits and PIDs are recycled.

for a senior

Demonstrate the reading: build a timed chain across the three event classes, state each link's limit, and name the blind area — in-process work with no exec, no new mapping and no socket leaves nothing behind.

for a principal

Own the consequence for coverage: if a fleet's entire detection posture assumes files land on disk, you are buying alerts for a technique class you have already been bypassed on, and the fix is behavioural content over lineage rather than more hashes.

## The scenario A long-lived, allow-listed service process on a Linux server fleet — a web daemon, an application server, a job runner — is where an intruder is operating. They are not dropping tooling. They are enumerating the host, reading what the process can already reach, and reaching out. Nothing lands on disk. A candidate who has only ever triaged malware alerts assumes there is nothing to look at. There is. ## Surface 1 — module and shared-object loads The sensor records objects mapped into a process's address space. On Linux that means shared objects: the ones loaded at start-up, and any loaded later. The signal is not the load itself — every process maps libraries — it is a **long-lived process mapping something it has never mapped before**, especially from a world-writable path, and especially at a time nothing was deployed. What a load event proves is narrow and you must say so: the sensor observed that object mapped into that process. It does not prove what the code did, and legitimate loaders map objects for entirely ordinary reasons. On its own it is an artefact. Next to a burst of children and three connections in the same second, it is the start of a chain. ## Surface 2 — sub-second children with their command lines Discovery has to ask the kernel questions, and the easy way is to spawn something. Those children may live for forty milliseconds. They are invisible to anyone looking at the box afterwards, and they never appear in a process listing that a human runs. They are in the EDR stream anyway, because execution events are recorded at exec, with the argument vector as it was at exec. The part people get wrong: the children's parent has often already exited by the time you look, and they assume that breaks the tree. It does not. The sensor records the parent's identity **at the child's creation** and stores the edge. The graph is written down, not derived later from live process state. On the host itself the same orphaned child would show a parent of `1` after reparenting, and PIDs get recycled; the sensor's own process identifiers do not. Read the command lines as a set, not one at a time. Account, group and sudo enumeration, host and network identity, mounted filesystems, credential file reads — several of those inside two seconds, from a service account that has never spawned a shell in the retained window, is a far stronger statement than any single line. ## Surface 3 — outbound connection events The sensor ties sockets to the process that opened them. That gives you something a passive network view cannot: **process attribution**. You know it was that daemon, not merely that host. And the direction of the claim matters. A connection event proves a socket was opened to that endpoint at that time. It does not prove what was sent — the event carries no payload — so what you actually reason over is destination, timing, regularity and, where the sensor records it, volume. "Bytes moved" and "data was stolen" are different claims and only the first is in the record. ## What is missing, and saying so No file write. No new executable. No hash to look up, no sample to submit, no persistence artefact yet. If your instinct is to conclude "nothing happened" from that absence, you have inverted the evidence: the absence of a disk artefact is evidence about **how** they worked, not about **whether** they worked. The genuine blind area is different and worth naming aloud: activity that stays inside the process — reading memory it already holds, using a connection it already owns, without exec, without a new mapping — produces no sensor event. The stream shows you the boundary crossings. It does not show you the inside of a process. ## Putting it together A defensible reading is a chain with times: object mapped at T; four short-lived children at T+0.8s with enumeration command lines; outbound connection from the same process at T+4s. Each link individually is weak; the sequence, the process, and the fact that this daemon has done none of it before are the argument. State each link's limit as you go, and mark the ones you could not establish rather than filling them in.

  • The console draws a tree whose parent had already exited before you looked. How is that possible?
    Lineage is recorded, not reconstructed. When the child is created the sensor captures its own unique process identifier alongside the parent's, and stores the edge as an event. Nothing about it depends on the parent still running. On the host itself that same orphan would show a parent of `1` after reparenting, and recycled PIDs would make the link ambiguous anyway — which is exactly why the sensor uses its own identifiers.
  • A shared object loaded into the daemon from a world-writable path — does that prove code execution inside the process?
    It proves the sensor observed that object mapped into that process's address space. Mapping is not a transcript of what ran, and legitimate loaders map objects all the time. Alone it is an artefact worth pivoting on; combined with the timing of the child processes and the outbound connection from the same process, it becomes a chain you can argue.
  • You have three outbound connections from that process — what can you conclude from the connection events alone?
    That the process opened sockets to those endpoints at those times, and, where the sensor records it, roughly how much moved. Not what was sent: the event carries no payload. Destination, timing and regularity are the reasoning surface. 'Bytes left the host' is supportable from this; 'the customer table was exfiltrated' is not, and needs a different source.

A burglar who only uses tools already in the house leaves no receipts. But the drawer opening, the forty-second trips through each room, and the calls made from the hall phone are all still on the record.

saying these in an interview costs you the question

  • Assumes nothing was recorded because nothing was written to disk
  • Treats a module load as proof of what the code did
  • Believes an exited parent breaks the recorded process tree
  • Reads a connection event as evidence of what was sent
  • Expects every intrusion to leave a new binary to hash

context