NSG and VNet flow logs analysis for exfiltration
NSG and VNet flow logs analysis for incident response: tuple formats, bytes and flow states, spotting exfiltration, mining and attacker IPs, blind spots.
TL;DR. Flow logs are Layer-4 records of every flow through your NSGs (NSG flow logs) or VNets (VNet flow logs), written to storage every minute as tuples: timestamp, source, destination, ports, protocol, direction, decision or state, and — for NSG v2 and VNet flow logs — packets and bytes each way. For incident response they answer three questions: did a compromised VM talk to the attacker's IP, did a lot of data leave to one address, and is anything reaching mining pools or management ports. NSG flow logs retire on September 30, 2027; VNet flow logs replace them. Flow logs cannot see Internet clients downloading straight from a storage account.
Flow logs are the least glamorous Azure log and often the one that closes the case. The Activity Log says "Run Command at 02:19"; the flow log says "at 02:20 the VM fetched 46 KB from the attacker's IP on port 80". That is the difference between suspicion and confirmation.
Two formats, one idea
| NSG flow logs | VNet flow logs | |
|---|---|---|
| Scope | One network security group | A whole virtual network (subnets, NICs) |
| Status | Retiring 2027-09-30; no new ones can be created | Current |
| Storage container | insights-logs-networksecuritygroupflowevent | insights-logs-flowlogflowevent |
| Record category | NetworkSecurityGroupFlowEvent | FlowLogFlowEvent |
| Timestamp in tuple | Unix seconds | Unix milliseconds |
| Protocol | T / U | IANA number (6 = TCP, 17 = UDP) |
| Decision | Separate field A / D | Flow state D for denied |
| Bytes / packets | Version 2 only | Yes, plus encryption state |
Both are written at one-minute intervals (NSG flow logs, VNet flow logs). A version 2 NSG tuple:
1542110379,10.5.16.4,203.0.113.117,59932,443,T,O,A,E,1,66,1,66
reads: time, source IP, destination IP, source port, destination port, TCP, outbound, allowed, flow ended, 1 packet / 66 bytes sent, 1 packet / 66 bytes received. A VNet tuple:
1663146003606,10.0.0.6,192.0.2.180,23956,443,6,O,E,NX,3,767,2,1580
adds the encryption status (NX = not encrypted) and uses milliseconds and IANA protocol numbers.
Counting bytes correctly
Flow states are Begin (no counters), Continuing (counters every five minutes for long flows) and End (final counters); VNet flow logs add Deny. Microsoft specifies that C and E counters are "aggregate counts from the time of the previous flow's tuple record" — so the conversation total is the sum of all C and E tuples, not the last one. Double-counting and under-counting both happen when this is ignored.
Two further traps from Microsoft's documentation:
- For NSG flow logs, flows hitting non-default inbound TCP rules are handled statelessly and byte and packet counts are not recorded; VNet flow logs support bytes and packets for stateless flows.
- VMs without a public IP can still show inbound tuples from Internet addresses on SNAT ports; the attempt is logged although Azure does not deliver it.
What to look for
| Pattern | Why it matters | Analyzer rule |
|---|---|---|
| Any flow between a VM and an IP already seen in other findings (the IP that ran Run Command, read the vault…) | Links the control-plane attacker to on-VM activity: second-stage download, C2, exfiltration | AZ-NET-004, high |
| More than 500 MB outbound from the network to one public IP | Exfiltration candidate (T1048) | AZ-NET-003, medium |
| Outbound to ports typical of mining pools (3333, 4444, 5555, 7777, 14433, 14444, 45560, 45700) | Crypto-mining (T1496) | AZ-NET-002, high |
| Allowed inbound from public IPs to 22, 3389, 5985, 5986 | Management ports exposed (T1133) | AZ-NET-001, medium |
| Denied inbound scans | Background noise on any public IP | — (context only) |
AZ-NET-004 is the most valuable because it needs no threshold: once the analyzer has an attacker IP from the Activity Log, Key Vault or storage logs, every flow to or from that IP is evidence. The ports list for mining is a heuristic; services legitimately using those ports will match.
How the analyzer handles volume
A busy subnet writes millions of tuples per day. The Azure Forensics analyzer never keeps them one by one: it aggregates per conversation (source, destination, destination port, protocol, direction, decision) with first and last seen, tuple count and summed packets and bytes. The Entities tab then shows, for each IP, flow totals sent and received — the fastest way to find "the one address that received 40 GB".
Still, be selective when exporting: download only the incident hours and the affected VNets or NSGs. The export guide has the paths.
What flow logs cannot see
- Direct access to PaaS endpoints from outside. An attacker downloading blobs over the Internet with a SAS never touches your VNet. Use the storage logs.
- Payloads. Flow logs are metadata only: no URLs, no TLS server names, no content.
- Traffic at the private endpoint itself. Microsoft notes it can only be captured at the source VM.
- Unsupported services. Microsoft lists services without flow log support, including App Service, Azure Functions, Logic Apps and Container Instances.
- Anything before flow logs were enabled, and anything after the storage account's retention deleted it.
A worked example
In the site's fictional sample, a Run Command starts on vm-app-01 at 02:19:40 UTC. The VNet flow log shows the VM (10.10.1.4) opening a connection to the attacker's IP on port 80 at 02:20:05 — 812 bytes sent, 46,338 received, the size of a small script — and on port 443 four seconds later. Neither flow would raise an eyebrow alone; both become high-severity findings because the IP already appears in the Run Command event. The walkthrough shows the full chain.
FAQ
Are NSG flow logs being retired?
Yes. Microsoft states that NSG flow logs retire on September 30, 2027 and that creating new NSG flow logs is no longer supported. Virtual network flow logs are the replacement.
Do flow logs show how much data left a VM?
NSG flow logs version 2 and VNet flow logs carry packets and bytes in each direction on continuation (C) and end (E) tuples. Summing them per conversation gives the volume. Version 1 NSG flow logs have no byte counts.
Can flow logs show downloads from a storage account by an outside attacker?
No. Flow logs record traffic of network interfaces in your virtual networks. An attacker downloading blobs directly from the storage endpoint over the Internet never crosses your VNet; use the storage resource logs for that.