Friday, May 5, 2017

BACnet - Bro - Spicy Part 3

So, in BACnet - Bro -Spicy Part 2, I went through setting up a VM with Docker and the rsmmr-hilti docker image that had Bro and Spicy installed. I then described the modifications to the bacnet.spicy and bacnet.evt files that would allow my bacnetctf.bro script to run and detect when a specific bacnet command packet was seen.

The Plan:

  1. Set up some virtual machines in my VMware environment to experiment. 
    1. Install the BACnet-stack on two: server and client. (see BACnet - Bro - Spicy Part 1)
    2. Install Docker and the rsmmr-hilti docker image on the bacnet server machine. (see BACnet - Bro - Spicy Part 2)
  2. Write a Bro script that uses the events created by the BACnet parser to log the source IP address from packets with the correct BACnet command.
  3. Install the php-apache docker image on the bacnet server machine and run it with the same shared volume as the rsmmr-hilti container where the log file is saved.
  4. Write a PHP webpage/script that shows the flag only to the IPs in the log file and a different page to all other IPs

3) Install the php-apache docker image on the third machine and run it with the same shared volume as the rsmmr-hilti container where the log file is saved.

Now that we have the spicy, evt, and bro files working in the rsmmr-hilti docker container, we need to build our own docker image that will incorporate our new files every time we run a new container. Then we can set up our web server using the php-apache docker image and server our the simulated Human Machine Interface (HMI) that will indicate when a successful write-property packet is detected.  
  1. In order to build a new image, I needed to create a DockerFile that defined how to get from the base image of rsmmr/hilti to my bacnet-hilti image.  This is basically a file that has its own syntax where you can do things like copy a local file into the container as it starts up, or run specific commands. This is the DockerFile for by bacnet-hilti image:
  2. FROM rsmmr/hilti
    MAINTAINER jenngates2004@gmail.com 
    COPY ./node.cfg /usr/local/bro/etc/node.cfg
    COPY ./bacnet.evt /opt/hilti/bro/spicy/bacnet.evt
    COPY ./bacnet.spicy /opt/hilti/bro/spicy/bacnet.spicy
    COPY ./properties.bro  /opt/hilti/bro/tests/spicy/bacnet/properties.bro
    COPY ./myBACnet.bro /opt/hilti/bro/tests/spicy/bacnet/myBACnet.bro
    COPY ./bacnetctf.bro /opt/hilti/bro/tests/spicy/bacnet/bacnetctf.bro
    COPY ./*.pcap /opt/hilti/bro/tests/Traces/bacnet/
    COPY ./CriticalRoom55-1.cap    /opt/hilti/bro/tests/Traces/bacnet/CriticalRoom55-1.cap
    RUN mkdir /opt/bacnet-stack-0.8.3/
    COPY ./bacnet-stack /opt/bacnet-stack-0.8.3/
    RUN apt-get update
    RUN cd /opt/bacnet-stack-0.8.3 &&  make clean all
    RUN mkdir /root/brologs

  3. Next I use this file to create the bacnet-hilti Docker image.
    1. Made a new dir 
      1. mkdir ~/bacnet-hilti
      2. cd bacnet-hilti/
    2. Copied .bro/.evt/.spicy/DockerFile files to it
    3. Build the new image with the files inserted
      1. NOTE: The space and then period at the end of the command are important!
      2.  sudo docker build -t bacnet-hilti .
  4. Now I run a container named bacnet-hilti1 in interactive mode (-ti) using the host VM's networking (--network=host) maps port 47808 on the host VM to forward packets to port 47808 in the bacnet-hilti1 container (-p 47808:47808) since bacnet listens on that port inside my container. I add a volume (--volume) to the container that maps a directory on the host VM (/home/bacnetchall/sharedvolume) to the /root/brologs directory in the container and allows read/write access (:rw) and tells the container it is sharing that volume with other containers (, z). The following command should be entered as one line.
    1. sudo docker run --network=host --name bacnet-hilti1 -ti -p 47808:47808  --volume=/home/bacnetchall/sharedvolume:/root/brologs:rw,z bacnet-hilti
  5. This starts the container and puts me at the root command prompt inside the container. Since I'm just going to run a specific Bro command so that it runs my bro script, I cd into the proper folder for the log file to be written. This is the folder the web server of the HMI will be monitoring.
    1. cd brologs   
    2. I start Bro listening on interface ens160 (the Docker image doesn't have the interfaces named eth0, eth1, etc). I tell Bro to use the bacnet.evt file which the new bacnet-hilti image made sure the running container has my modified version. I run the traffic through my bacnetctf.bro script, which, again, the container has the new version.  The -C tells it to ignore checksums and the & runs it in the background
      1. bro -i ens160 bacnet.evt /opt/hilti/bro/tests/spicy/bacnet/bacnetctf.bro -C & 
        1. NOTE!! wait for "listening on ens160". It can take a long time.
      2. Once bro is running and listening for traffic to parse with the bacnetctf.bro script, I start the bacnet server installed in Step 1 of this blog series. It has the device ID of 422 and runs in the background.
        1. /opt/bacnet-stack-0.8.3/demo/server/bacserv 422 &
      3. To break out of the running container and back to the host VM without stopping bro, bacnet, or the container:
        1. Ctrl-p Ctrl-q 
  6. From the same host VM I'm also going to run a Docker container that will run a web server with the HMI webpage. I'll talk more about the webpage and php script in the next blog post but here I'm just going to get the server piece working.  Again, there is a Docker container that does 98% of what I want already called php:7.0-apache, I just need to build it to copy the webpage files into the container's /var/www/html/ directory when it runs.
    1. I make a new dir called php-apache and cd into it
      1. mkdir php-apache
      2. cd php-apache
    2. I create a new file here named DockerFile and add the following lines inside it:
      1. FROM php:7.0-apache
      2. COPY ./bacnet_monitor/ /var/www/html/
    3. Here I make another new dir for the website files called bacnet_monitor.
      1. mkdir bacnet_monitor
    4. I copy over the website files into the bacnet_monitor directory
    5. I build a new Docker image file called php-apache
      1. sudo docker build -t php-apache .
      2. NOTE: remember the space and period at the end
    6. Now I run a new container of the new php-apache image file using the host VMs networking (--network=host) and name it php-apache1. It maps the host's port 80 with the container's port 80 and adds the same volume as the bacnet-hilti container, but with read-only access (:ro). This container doesn't require me to run any commands in it interactively, so we run it detached (-d).
      1. sudo docker run --network=host --name php-apache1 -d -p 80:80 --volume=/home/bacnetchall/sharedvolume/:/tmp/:ro,z php-apache


Part 4 will finish up with the PHP script for the HMI and wrap up this series.

4 comments:

  1. Hi,

    I am trying to run a bacnet client as a container and access the demo server (bacserv) running on the host machine or any other bacnet server running in the same subnet as the host.

    I have tried almost all the possible solutions but I am unable to establish a communication or let’s say failed to discover my server using Who-Is (bacwi).

    I am using the open stack by Steve https://sourceforge.net/projects/bacnet/
    The docker command I am running is:

    docker run -it --network=host --rm -p 47808:47808/udp --name=bacnetclient bacnetclient:14Sept /bin/bash.

    I also specify the interface address in bvlc.sh file and perform the discovery, but it still doesn’t work.
    Any help would be really appreciated as I am stuck on this for a long time.

    ReplyDelete
    Replies
    1. Are you able to see the UDP packets reaching the bacserv interface? Can you run tcpdump or wireshark on it and verify the packets are getting to the server?

      Delete
    2. No, wireshark doesn't capture any packets on my docker network interface. Btw, my demo server (bacserv), runs on DockerNAT (10.0.75.1).

      Did you try accessing the demo server/any bacnet server from inside the docker network, i.e. containerize the client? If so, then how? I believe my docker command is correct.

      Delete
  2. No, sorry. I only used Docker for the rsmmr/hilti container so I could use Bro with Spicy. My client was on a separate virtual machine and I did not use any NATing.

    I'm sure you've already verified no firewalls are interfering on the host or the containers. Maybe set the iptables rules to log all udp as a makeshift packet capture. Just to verify the packets are making it to the container.

    I'm not familiar with DockerNAT. I'd say focus on the fact that the Who-is/who-has query is a UDP packet sent to the subnet broadcast address and verify DockerNAT will do that.

    I wish I could be more help but this was my first time using Docker.

    ReplyDelete