Back to blog
Best PracticesCloud SecurityGCPNetworkingOperations & Compliance

Firewall Allows Public NetBIOS: Why It's Risky and How to Fix It on GCP

Learn why a GCP firewall rule exposing NetBIOS ports 137-138 to the public internet is dangerous, and how to fix and prevent it with CLI, Terraform, and OPA.

TL;DR

This check flags GCP firewall rules that expose NetBIOS (UDP/TCP ports 137-138) to the public internet (0.0.0.0/0). NetBIOS is a legacy Windows protocol that leaks hostnames, shares, and session data, and has no business facing the internet. Fix it by scoping the source range to internal IPs or deleting the rule entirely.

NetBIOS is one of those protocols that should have stayed inside the corporate LAN where it was born in the late 1980s. It still ships enabled on Windows hosts, and when a GCP firewall rule opens ports 137 and 138 to the world, you hand attackers an easy way to enumerate your Windows machines before they have even authenticated. This check exists to catch exactly that mistake.

The vpc_opennetbios check inspects every firewall rule in your GCP VPCs and reports any rule that allows ingress on NetBIOS ports from a public source range.


What this check detects

Lensix evaluates your VPC firewall rules and flags a finding when all of the following are true for an ingress rule:

  • The rule direction is INGRESS and its action is allow.
  • The rule permits traffic on ports 137 or 138 over UDP or TCP.
  • The source range includes a public CIDR, most commonly 0.0.0.0/0 (any IPv4 address) or ::/0 (any IPv6 address).

NetBIOS spans three ports historically: 137 (name service), 138 (datagram service), and 139 (session service). This particular check focuses on the name and datagram services on 137-138, which are the ports most often exposed by overly broad rules and the ones that leak the most reconnaissance data.

Note: GCP firewall rules apply to instances based on target tags, service accounts, or the entire VPC. A rule with no target field applies to every instance in the network, so a single permissive NetBIOS rule can expose far more hosts than you expect.


Why it matters

NetBIOS name service (NBNS) was designed to share host information freely on a trusted local network. That openness is the problem once it crosses the public internet.

What an attacker gets

  • Host and domain enumeration. A single nbtstat or nmblookup query against port 137 can return the machine name, the Windows domain or workgroup, logged-in usernames, and the MAC address. That is free reconnaissance before any credential is needed.
  • Reflection and amplification DDoS. NetBIOS name service responds to spoofed UDP requests and has been used as an amplification vector. An exposed host can be conscripted into attacks against third parties, which can get your IP ranges blacklisted.
  • A stepping stone to SMB. NetBIOS enumeration is usually the first step in a chain that ends at SMB (port 445). Attackers map share names and usernames over 137-138, then pivot to credential attacks or known SMB exploits like EternalBlue.

Internet scanners such as Shodan and Censys catalog exposed NetBIOS endpoints continuously. If you open these ports to the public, assume you are indexed within hours, not weeks.

Warning: Even if no sensitive Windows hosts sit behind the rule today, leaving the port open invites future exposure. Someone deploys a Windows VM into that subnet six months from now, inherits the rule, and is exposed without ever touching the firewall.


How to fix it

The right fix depends on whether anything legitimately needs NetBIOS. In almost all cases the answer is no, and you should remove public access entirely.

Step 1: Find the offending rule

List firewall rules that allow ports 137 or 138 and inspect their source ranges:

gcloud compute firewall-rules list \
  --filter="ALLOWED.ports~'137' OR ALLOWED.ports~'138'" \
  --format="table(name, network, sourceRanges.list(), allowed[].map().firewall_rule().list(), targetTags.list())"

To see the full definition of a specific rule:

gcloud compute firewall-rules describe allow-netbios --format=json

Step 2: Decide on the remediation path

Pick one of three approaches in order of preference.

Option A — Delete the rule (preferred). If nothing legitimately needs inbound NetBIOS, remove it.

Danger: Deleting a firewall rule is immediate and affects all instances matched by its targets. Confirm no production service depends on it before running this. Check VPC Flow Logs for recent accepted traffic on 137-138 first.

gcloud compute firewall-rules delete allow-netbios --quiet

Option B — Restrict the source range. If internal systems use NetBIOS, scope the rule to your private RFC 1918 ranges or a specific bastion network instead of the public internet.

gcloud compute firewall-rules update allow-netbios \
  --source-ranges="10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"

Option C — Replace with a tightly scoped rule. Delete the broad rule and create a new one limited by target tag and a narrow source, so only tagged hosts accept the traffic.

gcloud compute firewall-rules create allow-netbios-internal \
  --network=my-vpc \
  --direction=INGRESS \
  --action=ALLOW \
  --rules=udp:137-138,tcp:137-138 \
  --source-ranges="10.0.0.0/8" \
  --target-tags=windows-internal

Step 3: Verify the change

gcloud compute firewall-rules describe allow-netbios-internal \
  --format="value(sourceRanges.list())"

Confirm the output no longer contains 0.0.0.0/0.

Tip: If you manage hosts that genuinely need to resolve NetBIOS names across networks, retire NetBIOS in favor of DNS. Modern Windows environments work fine with DNS-based name resolution, and you can disable NetBIOS over TCP/IP on the NIC entirely, removing the need for any open ports.


Fixing it with Infrastructure as Code

If your firewall rules live in Terraform, the fix should happen in code so it does not drift back. A correctly scoped rule looks like this:

resource "google_compute_firewall" "netbios_internal" {
  name      = "allow-netbios-internal"
  network   = google_compute_network.my_vpc.name
  direction = "INGRESS"

  allow {
    protocol = "udp"
    ports    = ["137", "138"]
  }

  allow {
    protocol = "tcp"
    ports    = ["137", "138"]
  }

  # Internal sources only, never 0.0.0.0/0
  source_ranges = ["10.0.0.0/8"]
  target_tags   = ["windows-internal"]
}

If a public NetBIOS rule already exists in state, change source_ranges and apply, or remove the resource block entirely and run terraform apply to destroy it.


How to prevent it from happening again

One-time fixes do not stick. Stop the misconfiguration at the source by gating it in your pipeline and policy layer.

Block it in CI with policy-as-code

Use Open Policy Agent (OPA) with Conftest to fail any Terraform plan that opens NetBIOS to the world. Example Rego:

package main

deny[msg] {
  resource := input.resource.google_compute_firewall[name]
  resource.source_ranges[_] == "0.0.0.0/0"
  rule := resource.allow[_]
  port := rule.ports[_]
  port == "137"
  msg := sprintf("Firewall '%s' exposes NetBIOS port 137 to the public internet", [name])
}

deny[msg] {
  resource := input.resource.google_compute_firewall[name]
  resource.source_ranges[_] == "0.0.0.0/0"
  rule := resource.allow[_]
  port := rule.ports[_]
  port == "138"
  msg := sprintf("Firewall '%s' exposes NetBIOS port 138 to the public internet", [name])
}

Wire it into your pipeline so the build fails before anything reaches GCP:

terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test tfplan.json

Enforce org-wide guardrails

For defense in depth, set a VPC Firewall Policy at the organization or folder level that denies ingress from 0.0.0.0/0 on legacy ports. Hierarchical firewall policies take precedence over project-level rules, so an engineer cannot override them from inside a project.

Tip: Pair policy-as-code in CI with continuous monitoring in Lensix. The CI gate stops new bad rules, and Lensix catches anything created out-of-band through the console, the API, or a forgotten script. The combination closes the gap that either control leaves on its own.


Best practices

  • Default to deny. Build firewall rules from a baseline that blocks everything, then open only what a documented service requires. Never start from a permissive rule and trim later.
  • Never use 0.0.0.0/0 for management or legacy protocols. NetBIOS, SMB (445), RDP (3389), and SSH (22) should be reachable only from known internal ranges, a bastion host, or an identity-aware proxy.
  • Prefer Identity-Aware Proxy (IAP) for admin access. Instead of opening ports to networks, use Cloud IAP TCP forwarding so access is tied to authenticated identity, not IP source.
  • Scope rules with target tags or service accounts. An untargeted rule applies to the whole VPC. Tagging limits blast radius to the hosts that genuinely need the traffic.
  • Retire NetBIOS where you can. Modern Windows networks resolve names over DNS. Disabling NetBIOS over TCP/IP removes the attack surface completely rather than just firewalling it.
  • Turn on VPC Flow Logs. Before you delete a rule, flow logs tell you whether anything is actually using it. After you delete it, they confirm nothing broke.

NetBIOS exposure is a small misconfiguration with an outsized payoff for attackers: free reconnaissance, an amplification vector, and a path toward SMB. Closing it is usually a one-line change, and keeping it closed is a matter of putting the right guardrail in your pipeline. Fix it once, gate it in CI, and let continuous monitoring watch the rest.