In this section

Volatility windows.netscan: Reading State, Owner and What No Firewall Records

Module 0

Introduction

Every sub so far has examined this machine from the inside, and none of them has connected it to anything beyond it. This one produces the finding that does, and the column that does it exists nowhere else: not in a firewall, not in a proxy, not in a flow record.

windows.netscan reports the network structures the kernel maintains for its own use, and unlike every network source it names the process responsible for each connection. That single column is why this plugin belongs in the first ten minutes of any investigation.

Scenario

The alert that started all of this said NE-ENG-031 was making anomalous outbound connections. Five subs later you have a process worth suspecting and no link at all between it and the traffic somebody reported. The firewall knows the machine talked to an address it did not like. It does not know, and cannot know, which of the sixty processes on that host was responsible. This is the command that closes that gap, and it is the reason memory evidence is worth capturing at all on a network alert.

01

Running windows.netscan Across the Image

No --pid, because it scans rather than enumerates

This plugin takes no process filter, unlike almost everything else in this module, and the reason is worth understanding before you go looking for an option that does not exist.

Network structures are not hung off the process that owns them in a way the plugin can walk from a process identifier. netscan works the way psscan does: it sweeps the image for structures matching what a network object looks like, and reports everything it finds. So you run it against the whole image every time and do the narrowing afterwards, on the output.

$ vol -f NE-ENG-031-1431.raw windows.netscan
Proto  LocalAddr  LPort  ForeignAddr   FPort  State        PID   Owner
TCPv4  10.5.0.31  52014  10.5.0.20     4444   ESTABLISHED  1428  explorer.exe
TCPv4  10.5.0.31  52008  13.107.42.16  443    ESTABLISHED  2016  msedge.exe
TCPv4  0.0.0.0    445    0.0.0.0       0      LISTENING    4     System
TCPv4  10.5.0.31  51990  10.5.0.10     389    ESTABLISHED  672   lsass.exe
(LocalPort and ForeignPort abbreviated here so the rows fit the page)

Four rows from several dozen, chosen because between them they show every state you need to recognize. The first one is the answer to the alert that started this whole module. Process 1428, which four subs have now found separate reasons to distrust, holds an established connection to 10.5.0.20 on port 4444. That is the attacker VM's address on the lab network, and this is the first evidence tying the process to anything outside the host.

Read State before anything else, because it decides whether a row is worth reading at all. ESTABLISHED means a connection that was open and in use at capture time; LISTENING means a socket waiting for inbound connections and is what every server process on the machine shows; CLOSED and the various waiting states are connections on their way out. Rows in those final states are common and are usually residue rather than findings.

Owner and PID are the pair that make this plugin what it is. PID identifies the process the kernel recorded as owning the structure, and Owner is the process name that goes with it, which is why row one ties the network alert to a specific program rather than to a machine.

Proto distinguishes TCP from UDP and IPv4 from IPv6, and it is worth glancing at because a machine with unexpected IPv6 traffic is a real finding in an estate that does not use it. LocalAddr and LocalPort are this machine's end; ForeignAddr and ForeignPort are the other end, which for an outbound connection is where it went.

Note also what the third row shows. System listening on a well-known service port is ordinary Windows behavior, and an analyst who flags every listening socket will spend their day explaining file sharing to people.

02

The Column No Other Source Has

Why the network cannot answer this

It is worth being precise about why the Owner column matters so much, because it is the single strongest argument for capturing memory on a network alert.

A firewall sees packets. It records that one address spoke to another, on which ports, for how long, and how much data moved. It has no way to know which program on the sending machine produced those packets, because that information never leaves the host: by the time traffic is on the wire, the process that created it is not represented in it at all.

The five also fit together in a way that matters more than their individual weight. They describe one process, at one time, doing things that are each individually explicable and collectively hard to explain any other way, and that coherence is what a reader of a report actually responds to.

Your firewall logged the connection to 10.5.0.20. What can it not tell you?pick one, then the tell

The same applies to every other network source. A proxy log records a request and the account behind it, an intrusion detection system records a pattern it matched, and a flow record records volumes. None of them carries the process, because none of them is on the host.

That is the gap memory closes, and it explains a pattern you will see repeatedly in real incidents. A network alert identifies a machine; only host evidence identifies a program; and the window in which that host evidence exists is the window the earliest sub in this module was about.

The consequence for how an incident is contained is direct and it is worth making to whoever decides. Containment based on a firewall alert can block an address, and an attacker changes an address in minutes at no cost to themselves. Containment based on a named process can remove the thing producing the traffic, and the difference between those two is entirely this column.

It also cuts the other way, and that is worth stating in the same breath. The process name in this output is the program the kernel recorded, not a judgment about whether that program was acting for itself. explorer.exe owning a connection means the connection was created from within that process, which is exactly what code injected into it would produce, so the owner names where rather than who.

03

Filtering Several Dozen Rows to One

Three questions worth asking of the output

Reading network output row by row does not scale and there is no reason to. A workstation image produces more rows than you want to read that way, and a server produces far more. Three filters handle almost all of it.

$ vol -q -r csv -f NE-ENG-031-1431.raw windows.netscan > net.csv
$ grep ESTABLISHED net.csv | grep -v "10\.5\.0\." 
$ grep ESTABLISHED net.csv | cut -d, -f8,9 | sort | uniq -c | sort -rn
$ grep -E "explorer|cmd|update" net.csv

The second command shows established connections leaving the local network, which is where exfiltration and command traffic live. On this image it returns nothing, which is itself informative: the connection at issue is to another machine inside the lab network rather than to the internet.

The third counts connections by owning process, which turns a long listing into a short summary of which programs are doing the talking. A browser with many connections is expected; a shell process with any is not.

PS> Import-Csv .\net.csv |
>>   Where-Object State -eq 'ESTABLISHED' |
>>   Group-Object Owner | Sort-Object Count -Descending |
>>   Format-Table Count, Name

Count Name
----- ----
   14 msedge.exe
    6 Teams.exe
    3 svchost.exe
    2 lsass.exe
    1 explorer.exe

Nothing in that pipeline knows anything about attackers. It counted, and the row that matters is at the bottom of the list rather than the top.

A browser holding fourteen connections is what a browser is for. The shell holding one is the question, and it became visible because of the company it keeps rather than because anything scored it.

Counting like that is worth adopting as a habit rather than a one-off. It reduces an unreadable listing to a dozen lines, it ranks nothing by suspicion so it cannot mislead you, and the row that matters is visible because of the company it keeps rather than because anything flagged it.

That reveal is the same judgment the last three subs have asked for, applied to a fourth structure. Knowing what each program legitimately does is the skill; the plugins only tell you what happened.

A note on the second command's exclusion pattern, because it is worth adapting rather than copying. It removes the lab's own network so that what remains is traffic leaving it, and on your estate the ranges will be different. Building that filter once for your own address space makes every future run of this plugin considerably faster to read.

The fourth command is the targeted one, and it is what you run once you have a suspect rather than while looking for one. Searching for the names already under suspicion turns a survey into a check.

04

What Created Does and Does Not Date

A timestamp with a narrower meaning than it looks

One more column deserves care, and it is the one most likely to end up in a timeline. The plugin reports a creation time for structures where one is recorded, and it is worth being careful about what that time refers to.

It dates the network structure, which is to say when that particular socket or connection object was created. It does not date the conversation, because a program can open a connection, use it for hours and close it, and the structure's creation time will still say when it was opened.

The value dates the structure's creation, which is roughly when the connection was opened. It does not date when data last moved across it, how long the connection lasted, or what crossed it at any point. Useful for ordering; useless for duration.

The distinction sounds pedantic until somebody asks how long an attacker was in contact with a machine.

Two connections, both created at 14:08. What can you say about their durations?pick one, then the tell

Two connections make the point faster than any description of the field.

both structures created at 14:08 14:08 four hours four seconds identical Created values. nothing updates the field as data moves.

Good for placing a connection in a timeline. Useless for how long it lasted.

That makes it good for one job and bad for another. Ordering is where it earns its place: a connection structure created at 14:08 sits neatly between the command prompt at 14:07 and the short-lived process at 14:09, and that ordering is a real contribution to a timeline.

Duration is where the column actively misleads, and it does so quietly. A connection open for four hours and one open for four seconds look identical in this output, so a report claiming sustained communication on the strength of a creation time is claiming something the evidence does not carry.

There is a second limitation on the column that is easy to miss. Not every structure the plugin finds carries a creation time at all; some rows come back with nothing in that field, and that absence is a property of the structure rather than a sign that anything was concealed.

There is also the familiar asymmetry to apply. A connection opened, used and closed well before the capture may have had its structure reclaimed entirely, so absence here is weak evidence. The earlier traffic that triggered the alert may leave nothing at all in this output, and that is not a contradiction.

05

Scanning Finds Stale Structures Too

The same trade-off as psscan, in a different plugin

It is worth saying plainly how this plugin finds anything, because it explains the rows that follow. Because netscan scans rather than walks, it inherits exactly the property MF0.2 described: it finds structures that are present, including ones that no longer describe anything live.

That is a strength and a cost in the same mechanism, exactly as it was for process structures. A connection that closed some time ago leaves its structure in memory until that memory is needed for something else. The plugin sweeps past, finds the header, and reports what it reads, often with a state field indicating the connection is closed or in one of the waiting states, and sometimes with fields that have been partly overwritten by whatever claimed the memory next, which is where the implausible values come from.

Sort each netscan row by what it most likely meanstap a row, then a bucket
Ordinary, move onWorth the next hour

Work the sort and notice that three of the four are ordinary, which is the usual proportion. The rule separating them is the same one MF0.2 gave for process structures: internal implausibility means the reading, not the machine. A port of zero or an address that cannot exist is a stale structure rather than a strange connection.

There is a second kind of noise particular to this plugin. A machine that has been browsing produces a large number of short-lived connections, all legitimate, and their structures accumulate. So the volume of closed rows tracks how much the machine has been used rather than anything about an intrusion, and a busy host looks alarming until you have seen a few.

There is one more reason the residue is worth tolerating rather than wishing away. A closed connection structure is a record of something the machine did earlier, and on an investigation where the interesting traffic finished before the capture, those rows may be the only host evidence that it happened at all. They are noise when you are asking what is happening now and evidence when you are asking what happened.

So the practical routine is to filter to ESTABLISHED first, which discards most of the residue in one step, and only widen to closed states when you are specifically looking for what a machine did earlier rather than what it was doing at capture.

06

What This Row Establishes

And the question it does not answer

This is the row the alert was about and the row five subs have been working toward, so it deserves the same discipline as every other finding rather than less. Write out precisely what it supports, because this is the finding the whole investigation has been building toward and it is worth not spoiling.

It establishes that at capture time a network structure existed recording an established TCP connection from this host to 10.5.0.20 on port 4444, and that the kernel recorded process 1428 as its owner. Every clause is checkable by anybody with the image.

The row establishes that a connection existed, to a specific address, owned by a specific process, and open at capture time. It establishes nothing about what crossed that connection, how much data moved, whether anything was exfiltrated, or who was operating the far end. Every item in that second list is a network question being asked of a host source.

Notice how much narrower that is than what the row feels like it says.

You need to know how much data crossed that connection. Where do you go?pick one, then the tell

Side by side, it is clear which column a host source can fill.

the host, this row a connection existed to a specific address owned by a named process open at capture time address, port, time window network logs what crossed it how much data moved whether it was exfiltration who operated the far end neither source answers this the owning process exists only on the host; the volume exists only on the network

The left box is what you take to the middle one. Each source answers what the other cannot.

The right-hand column is the one a reader will fill in for themselves if you do not address it. A connection to an attacker's machine reads as data leaving, and this evidence says nothing whatsoever about content or volume. Memory records that a conversation existed; it does not record the conversation.

Being disciplined about that is what makes the finding survive. A sentence that stops at what the structure records is one nobody can attack; a sentence that adds an inference about data leaving is one somebody will ask you to support, and you cannot.

The honest next step is therefore to go back to the network evidence with something it can use. You now have a source address, a destination, a port and a time window, which is precisely the query that makes firewall or proxy logs answer the volume question they are good at.

That round trip is worth noticing because it is how real investigations work. Network evidence identified a machine, host evidence identified the process, and host evidence now sends you back to the network with a specific question. Neither source answers the whole thing and neither is redundant.

07

Five Plugins, One Coherent Account

What the module has established about process 1428

Five plugins have now been pointed at one process across five subs, and it is worth assembling what they collectively support before the module turns to what happens when none of them work at all.

A command prompt was created at 14:07 with explorer.exe as its parent, and nothing else on the machine was created for five hours on either side of it. A short-lived update.exe ran two minutes later at 14:09, and its own parent cannot be found anywhere in the image. explorer.exe holds a single unbacked executable page containing a file header, at an address you can quote. The same process holds a handle into the credential process, with the rights recorded alongside it. And it owns an established outbound connection to another machine on port 4444, which is the row that answers the alert everything started from.

pslist a child process at an isolated time psscan a second, short-lived process nearby malfind code that came from no file handles access to the credential process netscan an outbound connection it should not have PID 1428 explorer.exe

Five structures, five plugins, one process. The three in orange each carry weight alone.

Read the diagram as a whole rather than as five separate results. What it shows is convergence: five different kernel structures, read by five different plugins, each of which knew nothing about the others, all pointing at the same process. No single one of them was designed to corroborate any of the rest.

That independence is what gives the set its weight. Five findings drawn from one source could all be wrong for one reason; five drawn from unrelated structures cannot be, unless the reading itself is broken, and MF0.6 gave the check for that.

Read that list as a whole rather than as five results. None of the five is conclusive alone and the last three each carry real weight independently. Together they describe a process doing four things the Windows shell has no reason to do, which is a considerably stronger position than any single observation.

Which two of the five would you not report on their own?pick one, then the tell

It is still not a conclusion, and the remaining gaps are worth naming rather than glossing. Nothing establishes that code ran from that page, that the handle was used, or what crossed the connection. Those are the questions the rest of the course answers, and the point of this module is that you can now ask them precisely: about one named process, at one known time, with an address and an offset attached to each. A question in that form is answerable. A general worry about a machine is not.

08

Practice

Learn which of your programs talk
hands on
Do this Build the owner baseline before you need it
  1. Run windows.netscan against an image with -q -r csv and count the rows. Then count only those in an established state. The ratio shows how much of the output is residue.
  2. Group established connections by owning process and write the list down. That list is your baseline for what talks on a machine of this kind, and it is the thing that makes an unexpected owner obvious.
  3. Find every row whose foreign address is outside your own networks. On a workstation this is mostly browsers and update services; anything else on that list deserves a name you recognize.
  4. Find a row with an implausible port or a blank owner and satisfy yourself that it is a stale structure rather than something strange. Being able to dismiss these quickly is what keeps the output usable.
  5. Take the most interesting row and write the finding sentence: protocol, both endpoints, state, owning process, at capture time. Then write the sentence saying what it does not establish about content or volume.
What you should end up with: a count of how much netscan output is residue, a written baseline of which programs legitimately hold connections on your machines, and one finding written with its limits attached. The baseline is the part that makes the next incident faster.

Extend it

These connect the host evidence to the network evidence, which is where this plugin earns its keep.

  1. Open a connection from an unusual program, capture memory, and find your own row. Seeing a connection you created with your own name against it makes the Owner column concrete.
  2. Take a connection you find in netscan and look for it in your firewall or proxy logs using the address, port and time. Doing the round trip once teaches what each source contributes.
  3. Capture the same machine twice a few minutes apart and compare the netscan output. Which rows persist and which vanish tells you how long closed structures survive on that host.
  4. Count the listening sockets on a server image and account for each one. Unexplained listeners are a genuinely useful thing to hunt for and this is how you learn what explained looks like.

Drill one is the quickest and the one that makes the Owner column stop being abstract. Opening a connection from a program that has no business holding one, capturing, and finding your own row with your own process name against it takes ten minutes and permanently changes how you read this output.

Drill two is the one worth doing properly. An analyst who has taken a connection from memory into network logs and back understands, in a way no explanation conveys, why neither source is sufficient and why capturing memory on a network alert is worth the argument.

You can run windows.netscan, explain why it takes no process filter, read State and Owner, say precisely why no firewall can supply that owner, filter several dozen rows to the one that matters, treat Created as ordering rather than duration, and recognize stale structures. MF0.6 turns to what happens when the tooling cannot read the image at all.