BACnet

December 17, 2025 · View on GitHub

BACnet (Building and Control Network) Protocol Threat Emulation Tooling

Mapped to MITRE ATT&CK® for ICS v16

Overview

The BACnet plugin provides 14 adversary emulation abilities specific to the BACnet protocol. The BACnet standard, ANSI/ASHRAE 135, for the BACnet protocol is available for purchase from ASHRAE as described on the BACnet Committee website.

The following table outlines MITRE ATT&CK for ICS Tactic coverage provided by the BACnet plugin:

DiscoveryCollectionImpactInhibit Response Function
Remote System DiscoveryAutomated CollectionManipulation of ControlDevice Restart/Shutdown
Remote System Information DiscoveryMonitor Process State
Point & Tag Identification

Ability Overview Tables

The following tables list each plugin ability by their corresponding tactic. A heatmap of plugin abilities is available to view here.

Discovery Abilities

NameTacticTechniqueTechnique ID
BACnet Who-IsDiscoveryRemote System DiscoveryT0846
BACnet Who-HasDiscoveryRemote System Information DiscoveryT0888

Collection Abilities

NameTacticTechniqueTechnique ID
BACnet Atomic Read FileCollectionMonitor Process StateT0801
BACnet EPICS ReportCollectionAutomated CollectionT0802
BACnet Get Event InformationCollectionMonitor Process StateT0801
BACnet Read PropertyCollectionPoint & Tag IdentificationT0861
BACnet Read Property MultipleCollectionPoint & Tag IdentificationT0861
BACnet Device Collection - BasicCollectionPoint & Tag IdentificationT0861
BACnet Object Collection - BasicCollectionPoint & Tag IdentificationT0861
BACnet Subscribe COVCollectionAutomated CollectionT0802

Impact Abilities

NameTacticTechniqueTechnique ID
BACnet Atomic Write FileImpactManipulation of ControlT0831
BACnet Write PropertyImpactManipulation of ControlT0831

Inhibit Response Function Abilities

NameTacticTechniqueTechnique ID
BACnet Reinitialize Device - Warm StartInhibit Response FunctionDevice Restart/ShutdownT0816
BACnet Reinitialize Device - Cold StartInhibit Response FunctionDevice Restart/ShutdownT0816

Architecture

This section describes the main components of the plugin and how they interface.

Block Diagram

BACnet Plugin Block Diagram

The BACnet Plugin exposes several new protocol specific abilities to your Caldera instance. The abilities are executed from a host running a Caldera agent via the corresponding payload. Abilities must target devices that support the BACnet protocol to achieve the described effects.

Payloads

The BACnet plugin leverages several payloads that are part of the BACnet Stack detailed in the libraries section. Each payload is an individual BACnet service, with a name combining bac + service_name. The payloads have been compiled for 2 different architectures.

Example payloads for BACnet Read Property service:

  • bacrp.exe - Windows executable Read Property protocol service
  • bacrp - Linux executable for the Read Property protocol service

Compatibility

The plugin's payloads currently support deployment to Caldera Agents of the following computer architectures:

WindowsLinuxMacintosh
32-bit
64-bitXX
  • The Linux payload was compiled on Ubuntu 22.04.2 LTS with GNU Make 4.4.
  • The Windows payload was compiled on Windows 10 v21H2 with GNU Make 4.4.1 (part of Mingw64 tools downloaded through MSYS2).

Libraries

The following libraries were used to build the BACnet payloads:

LibraryVersionLicense
bacnet-stack1.0GPL

Usage

This section describes how to initially deploy and execute the abilities present within the BACnet plugin.

Deployment

  1. Identify the target system you would like to communicate with via the BACnet protocol.
  2. Identify a viable host for the Caldera agent that will be sending BACnet messages to the target system. A viable host will have a network connect to the target system and be compatible with the payloads in this plugin.
  3. Deploy the Caldera agent to the viable host.
  4. Run a combination of the BACnet plugin abilities to achieve the desired effect.

If you don't know where to begin, try using Who-Is to discover BACnet devices on the network.

Reference the Caldera training plugin for a step-by-step tutorial on how to deploy an agent and run abilities via an operation.

Abilities

Who-Is

The Who-Is service is used by a sending BACnet-user to determine the Device object identifier, the network address, or both, of other BACnet devices that share the same internetwork.

Ability Command:

./bacwi

Who-Has

The Who-Has service is used by a sending BACnet-user to determine the Device object identifier, the network address, or both, of BACnet devices that contain a given object name or object identifier.

If a device in the instance range has that object, an "I-Have" response will be reported, stating the object-type and object-instance

Ability Command:

./bacwh #{bacnet.device.min_instance} #{bacnet.device.max_instance} #{bacnet.obj.name}

Facts:

NameDescriptionType
bacnet.device.min_instanceMinimum device instance to checkint
bacnet.device.max_instanceMaximum device instance to checkint
bacnet.obj.nameobject-name, object-type, or object-instance to check forstring

Examples:

./bacwh 100200 100300 "Timer"
./bacwh 100200 100300 binary-value 7

EPICS Report

Generates an EPICS report - which provides services supported, the object list, and properties of those objects.

Ability Command:

./bacepics #{bacnet.device.instance}

Facts:

NameDescriptionType
bacnet.device.instanceDevice instance to poll (also accepts IP:Port address of device)int

Examples:

./bacepics 100101

Get Event Information

The GetEventInformation service is used by a client BACnet-user to get a summary of all active state events on the target device.

Ability Command:

./bacge #{bacnet.device.instance}

Facts:

NameDescriptionType
bacnet.device.instanceDevice instance to poll (also accepts IP:Port address of device)int

Examples:

./bacge 100101

Read Property

The ReadProperty service is used by a client BACnet-user to request the value of one property of one BACnet Object.

Ability Command:

./bacrp #{bacnet.device.instance} #{bacnet.obj.type} #{bacnet.obj.instance} #{bacnet.obj.property} #{bacnet.read.index}

Facts:

NameDescriptionType
bacnet.device.instanceDevice instance (also accepts IP:Port address of device)int
bacnet.obj.typeType of the object to be readint or string
bacnet.obj.instanceThe instance number of the object-type specifiedint
bacnet.obj.propertyThe property of the object to be readint or string
bacnet.read.indexHow to handle reading arraysint

Read Index Values:

  • -2: An improved -1 for arrays. Iteratively reads each index of the array-type property
  • -1: Reads the whole property (this can fail for arrays if the device doesn't support segmentation)
  • 0: Gets the length of the property if it is an array type
  • 1..N: Gets the property value at index N if it is an array type

Examples:
Read the PRESENT VALUE (property 85) of instance 7 of the ANALOG INPUTS (object type 0) on device instance 100101. Read the full property (read index -2).

./bacrp 100101 0 7 85 -2

Read the OBJECT LIST (property 76) of instance 100101 of the DEVICES (object type 8) on device instance 100101. Read the full property (read index -2).

./bacrp 100101 device 100101 76 -2

Read Property Multiple

The ReadPropertyMultiple service is used by a client BACnet-user to request the values of one or more specified properties of one ore more BACnet Objects. For example, a single property for a single object, a list of properties for a single object, or even any number of properties for any number of objects.

Read Property Multiple is similar to Read Property, but allows multiple properties to be provided, in the form of a comma separated list (e.g., "1,2,3").

Ability Command:

./bacrpm #{bacnet.device.instance} #{bacnet.obj.type} #{bacnet.obj.instance} #{bacnet.obj.property}

Facts:

NameDescriptionType
bacnet.device.instanceDevice instance (also accepts IP:Port address of device)int
bacnet.obj.typeType of the object to be readint or string
bacnet.obj.instanceThe instance number of the object-type specifiedint
bacnet.obj.propertyThe properties of the object to be readcomma separated list of int

Examples:

./bacrpm 100101 8 100101 77,28

BACnet Device Collection Example:

Read the OBJECT NAME (77), VENDOR NAME (121), MODEL NAME (70), DESCRIPTION (28), and OBJECT LIST (76) of instance 100101 of the DEVICES for Device Instance 100101.

./bacrpm 100101 device 100101 77,121,70,28,76

Subscribe COV

The SubscribeCOV service is used by a client to subscribe to Change Of Value (COV) notifications for the properties of a given object.

Ability Command:

./bacscov #{bacnet.device.instance} #{bacnet.obj.type} #{bacnet.obj.instance} #{bacnet.scov.process_id}  #{bacnet.scov.confirm_status}

Facts:

NameDescriptionType
bacnet.device.instanceDevice instance (also accepts IP:Port address of device)int
bacnet.obj.typeType of the object to subscribe toint or string
bacnet.obj.instanceThe instance number of the object-type specifiedint
bacnet.scov.process_idA process identifier for the COV subscriptionint
bacnet.scov.confirm_statusA flag to subscribe with confirmed notifications"confirmed" or "unconfirmed"

Examples:

./bacscov 100101 binary-output 13 1234 unconfirmed

Write Property

The WriteProperty service is used by a client BACnet-user to modify the value of a single specified property of a BACnet object.

Ability Command:

./bacwp #{bacnet.device.instance} #{bacnet.obj.type} #{bacnet.obj.instance} #{bacnet.obj.property} #{bacnet.write.priority} #{bacnet.write.index} #{bacnet.write.tag} #{bacnet.write.value}

Facts:

NameDescriptionType
bacnet.device.instanceDevice instance (also accepts IP:Port address of device)int
bacnet.obj.typeType of the object to be written toint or string
bacnet.obj.instanceThe instance number of the object-type specifiedint
bacnet.obj.propertyThe property of the object to be written toint or string
bacnet.write.priorityThe precedence of the write (lower is higher)int
bacnet.write.index-1 write the entire property, 1..N write to the specified indexint
bacnet.write.tagThe type of value to be writtenint or string
bacnet.write.valueThe write value itselfstring

The value (#{bacnet.write.value}) is an ASCII representation of some type of data that you wish to write. It is encoded using the tag information provided (#{bacnet.write.tag}). For example, if you were writing a REAL value of 100.0, you would use a #{bacnet.write.value} of '100.0' and a #{bacnet.write.tag} of 4.

Examples:

Write a BOOLEAN value of FALSE to property 85 (PRESENT_VALUE) of instance 7 of object 4 (BINARY_OUTPUT) on device instance 100101. Write the full property with a precedence of 5.

./bacwp 100101 4 7 85 5 -1 1 0

Atomic Read File

The AtomicReadFile Service is used by a client BACnet-user to perform an open-read-close operation on the contents of the specified file. The file is saved locally.

Ability Command:

./bacarf #{bacnet.device.instance} #{bacnet.file.instance} #{bacnet.read.local_name}

Facts:

NameDescriptionType
bacnet.device.instanceDevice instance (also accepts IP:Port address of device)int
bacnet.file.instanceSpecifies which file on the device to readint
bacnet.read.local_nameThe name of the local file in which to store the read datastring

Examples:

./bacarf 100101 3 file_dump

Atomic Write File

The AtomicWriteFile Service is used by a client BACnet-user to perform an open-write-close operation of an OCTET STRING into a specified position or a list of OCTET STRINGs into a specified group of records in a file.

Ability Command:

./bacawf #{bacnet.device.instance} #{bacnet.file.instance} #{bacnet.write.local_name} #{bacnet.write.octet_offset}

Facts:

NameDescriptionType
bacnet.device.instanceDevice instance (also accepts IP:Port address of device)int
bacnet.file.instanceSpecifies which file on the device to write toint
bacnet.write.local_nameThe name of the local file containing content that will be written to the BACnet device filestring
bacnet.write.octal_offsetThe byte offset in the BACnet device file at which the content will be written toint

Examples:

./bacawf 100101 5 ieee_754_payload 31

Reinitialize Device

The ReinitializeDevice service is used by a client BACnet-user to instruct a remote device to do one of the following behaviors: reboot (cold start), reset to another predefined initial state (warm start), activate network port object changes, or to control the backup or restore procedures.

Ability Command:

 ./bacrd #{bacnet.device.instance} 1

Facts:

NameDescriptionType
bacnet.device.instanceDevice instance (also accepts IP:Port address of device)int

Examples:

./bacrd 100101 1

Protocol Details

BACnet models a device as a collection of objects. Each object represents something on the device: a sensor, an output, a schedule, the device itself, etc.

Objects

An object is identified by its object type plus an object instance:

  • Object type: the kind of thing it is
    Examples: analog-input, analog-output, binary-input, device, schedule, etc.
    For a list of object types see the BACnet Fact Values section.

  • Object instance: which one of that type it is
    Each object type on a device has instance numbers (0, 1, 2, …) that uniquely identify individual objects of that type on that device. Note that the object instance is always an integer value.

Together, (object type, object instance) uniquely identify an object within a device.

Properties

Each object has properties, which are named pieces of data that describe the object or its current state. For example:

  • object-identifier – the object’s type and instance
  • object-name – a human-readable label
  • present-value – the current value (e.g., temperature, setpoint)
  • units – engineering units (e.g., degrees Celsius)
  • status-flags – alarm/fault/overridden indicators

For a list of property types see the BACnet Fact Values section.

Some property values are stored as arrays. In these cases, the #{bacnet.read.index} or #{bacnet.write.index} facts are used to identify the index in that array to read or write.

BACnet Fact Values

As described above, uniquely specifying a data object in BACnet requires specifying:

  • the Object Type
  • the Instance of that Object
  • the Property within that Object
  • and, if the property contains an array, the Index within the Property

Additionally, for a Write Property operation, a write tag (#{bacnet.write.tag}) must be supplied to indicate how to interpret the value passed to the ability (#{bacnet.write.value}).

The object instance (#{bacnet.obj.instance}) is always an integer. The other facts can be provided either as a numeric code or as a string. The numeric codes are defined by the BACnet specification and are consistent across BACnet implementations. BACnet Stack (the library underlying this plugin) also defines human-readable string equivalents for these codes, which may be more convenient in many cases.

You can find the full list of codes and their string equivalents in the BACnet Stack source code:

Additional Resources:

Here is a small selection of resources to aid in understanding of the BACnet protocol. You may already have more specific specification documents, vendor manuals, and other resources to assist with understanding the state of your target environment.

Source Code

The binaries for this plugin are produced by compiling BACnet Stack 1.0. The functionality of readprop and writeprop have been modified by the Caldera for OT team. The source code for these binaries can can be found inside this plugin's src/ directory.

ASHRAE® and BACnet® are registered trademarks of the American Society of Heating, Refrigerating and Air-Conditioning Engineers, Inc. 180 Technology Parkway NW, Peachtree Corners, Georgia 30092 US.

This Caldera plugin is named "BACnet" as that is a short identifier of its purpose / scope. This plugin is not produced-by ASHRAE.