-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
103 changed files
with
13,117 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
*.zip | ||
*.class | ||
.vagrant/ | ||
*.swp | ||
*.log | ||
*.pcap | ||
*.jar | ||
node/target/ | ||
j60870/target/ | ||
Vagrantfile | ||
*.retry | ||
*.iml | ||
*.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[submodule "UA-Java"] | ||
path = UA-Java | ||
url = https://github.com/OPCFoundation/UA-Java.git | ||
[submodule "attacks/PoC2013"] | ||
path = attacks/PoC2013 | ||
url = https://github.com/atimorin/PoC2013/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Development Notes | ||
|
||
## Creating Vagrant boxes. | ||
|
||
Requirements for vagrant box (performed by the Ansible role): | ||
- vagrant user | ||
- password vagrant | ||
- added vagrant ssh key | ||
- password-less sudo access | ||
|
||
Create a box by running: | ||
|
||
vagrant package --base <VBox VM name> | ||
vagrant box add testbed-node package.box | ||
|
||
Remove: | ||
|
||
vagrant box remove testbed-node |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,62 @@ | ||
# ICS TestBed Framework | ||
Place holder for ICS-TestBed-Framework. | ||
# ICS TestBed Framework | ||
|
||
A scalable framework for automatically deploying locally (or remotely) a number of virtual machines that replicate a Supervisory Control And Data Acquisition (SCADA) network is proposed. This includes multiple virtual hosts emulating sensors and actuators, with a Human Machine Interface (HMI) controlling the hosts. | ||
The presented framework contains a collection of automation scripts which build and deploy a variable number of virtual machines, pre-configured to act as either a Remote Terminal Unit (RTU), HMI or Data Historian. The presented work includes a standards compliant implementation of IEC 60870-5-104 (IEC104) and OPC Unified Architecture (OPC-UA), with the capability to support other protocols such as Modbus-TCP (Modbus) and IEC61850. | ||
|
||
This allows researchers to build testbeds that can be configured to replicate real-world deployments of SCADA networks. The framework builds upon open source libraries and is released under the Free Software Foundation approved licence, GNU General Public License version 3. | ||
|
||
# Example Operation | ||
|
||
[![asciicast](https://asciinema.org/a/clpPltx8oGGQBxCKsz0FzIfqg.png)](https://asciinema.org/a/clpPltx8oGGQBxCKsz0FzIfqg) | ||
|
||
# Potential Use Cases | ||
|
||
- **Packet Generation**: The objective of the first use case is to generate network traffic from a large number of devices, with a high level of domain fidelity, where ideally process information would be included in the traffic. As highlighted earlier, creating datasets from a live system is not always possible: it is labour intensive, one needs to locate a suitable tap point and gain approval from the plant operators. This may be restricted by the plant operation and policies, as interference with a working network may lead to unforeseen circumstances and leak identifiable information. Packet generation can be used to test proposed changes to the SCADA network before being deployed into the live system. It can also perform stress testing of devices using legitimate looking packets. Interesting research use-cases include experimentation with different networking paradigms, such as ICN or IPv6, that have not been applied to ICS networks. | ||
|
||
- **Attack Simulations**: This case considers simulation of complex attacks on SCADA systems, and aims to perform risk analysis of a replicated ICS network, without adversely affecting the live system. The testbed may be used by red teams in an attempt to compromise testbed nodes, whilst analysing the consequence and getting full packet capture analysis. If they are successful, the process can be re-performed with additional countermeasures in place, allowing testing of the new countermeasures in the context of security and how it may affect the site processes. The network captures of the read team exercise can be published as an open datasets for verifying IDS which can be reproduced and confirm the results by other researchers. | ||
|
||
- **Agent Benchmarking**: The objective of this use case is to support the benchmarking of agent host based systems, which are typically not performed on live systems due to vendor restrictions. Unless the agent is trusted by vendors, it is often prohibited from being deployed, with a risk of breach of contract. By using a testbed that accurately represents the real industrial site, it is possible to monitor the use of agent based software without causing disruptions. Provided the testbed is freely modifiable, functionally accurate and can integrate with physical hardware, it would be possible to perform a benchmark of the agent. | ||
|
||
- **Extending Limited Hardware**: The final case is to extend an existing physical testbed to include communication protocols and configurations which were not possible with the existing hardware. This could require the use of network emulators, to extend the networking equipment, and process simulators, to extend the process control equipment. By coupling virtual and physical hardware together, it is possible to create a complex and highly realistic testbed, allowing for large deployments which combine multiple protocols and devices to be created and analysed. This use case could be used alongside the others to enhance their results. | ||
|
||
# Build and run locally | ||
|
||
Clone the repository and install the required dependencies: | ||
|
||
git clone --recurse-submodules [email protected]:PMaynard/ICS-TestBed-Framework.git | ||
sudo apt install openjdk-8-jdk maven | ||
|
||
Build: | ||
|
||
mvn clean package | ||
mvn package -Dmaven.test.skip=true # Skip tests. | ||
|
||
Start up a RTU: | ||
|
||
java -jar node/target/node-1.0.jar | ||
script --file scripts/rtu.cmd | ||
|
||
Start up a HMI: | ||
|
||
java -jar node/target/node-1.0.jar | ||
script --file scripts/hmi.cmd | ||
|
||
# Auto Deploy in VMs | ||
|
||
The default configuration profile will deploy 1 HMI and 4 RTUs. The HMI will integrate the RTUs using the IEC104 and OPC-UA. The RTUs are configured to return random process data. | ||
|
||
## Prerequisites | ||
|
||
Use the latest version of Vagrant over the pre-built/distribution packages as these scripts use features from the latest versions of Vagrant. *Should be fine if using Ubuntu 18.04.1 LTS*. | ||
|
||
git clone https://github.com/mitchellh/vagrant.git /opt/vagrant | ||
cd /opt/vagrant | ||
bundle install | ||
bundle --binstubs exec | ||
ln -sf /opt/vagrant/exec/vagrant /usr/local/bin/vagrant | ||
|
||
## Deploy | ||
|
||
cp Vagrantfile.default Vagrantfile | ||
vagrant up | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# -*- mode: ruby -*- | ||
# vi: set ft=ruby : | ||
|
||
Vagrant.configure("2") do |config| | ||
config.vm.provision "ansible_local" do |ansible| | ||
ansible.playbook = "playbook.yml" | ||
end | ||
|
||
config.vm.define "buildImage" do |buildImage| | ||
buildImage.vm.hostname = "buildImage" | ||
buildImage.vm.box = "ubuntu/xenial64" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# -*- mode: ruby -*- | ||
# vi: set ft=ruby : | ||
|
||
Vagrant.configure("2") do |config| | ||
config.vm.box_check_update = false | ||
|
||
# TODO: Hack because the packaged vm is messy. | ||
config.vm.provider "virtualbox" do |vb| | ||
vb.customize [ "modifyvm", :id, "--uartmode1", "disconnected" ] | ||
vb.memory = 512 | ||
end | ||
|
||
config.vm.provision "ansible_local" do |ansible| | ||
ansible.playbook = "playbook.yml" | ||
end | ||
|
||
(1..5).each do |i| | ||
config.vm.define "rtu-#{i}" do |node| | ||
node.vm.hostname = "rtu" | ||
node.vm.box = "pmaynard/testbed-node" | ||
node.vm.network "public_network", ip: "10.50.50.10#{i}", bridge: "br0" | ||
end | ||
end | ||
|
||
config.vm.define "hmi" do |hmi| | ||
hmi.vm.hostname = "hmi" | ||
hmi.vm.box = "pmaynard/testbed-node" | ||
hmi.vm.network "public_network", ip: "10.50.50.200", bridge: "br0" | ||
end | ||
end |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<parent> | ||
<groupId>xyz.scada.testbed</groupId> | ||
<artifactId>testbed</artifactId> | ||
<version>1.0</version> | ||
</parent> | ||
|
||
<properties> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
</properties> | ||
|
||
<groupId>org.openmuc</groupId> | ||
<artifactId>j60870</artifactId> | ||
<packaging>jar</packaging> | ||
<version>1.2.0</version> | ||
<name>IEC104 Lib</name> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<version>3.3</version> | ||
<configuration> | ||
<source>1.8</source> | ||
<target>1.8</target> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
<dependencies> | ||
|
||
<dependency> | ||
<groupId>junit</groupId> | ||
<artifactId>junit</artifactId> | ||
<version>4.12</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.slf4j</groupId> | ||
<artifactId>slf4j-simple</artifactId> | ||
<version>1.6.1</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/* | ||
* Copyright 2014-17 Fraunhofer ISE | ||
* | ||
* This file is part of j60870. | ||
* For more information visit http://www.openmuc.org | ||
* | ||
* j60870 is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* j60870 is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with j60870. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
package org.openmuc.j60870; | ||
|
||
import java.io.DataInputStream; | ||
import java.io.IOException; | ||
|
||
import org.openmuc.j60870.internal.ConnectionSettings; | ||
|
||
final class APdu { | ||
|
||
public enum APCI_TYPE { | ||
I_FORMAT, | ||
S_FORMAT, | ||
TESTFR_CON, | ||
TESTFR_ACT, | ||
STOPDT_CON, | ||
STOPDT_ACT, | ||
STARTDT_CON, | ||
STARTDT_ACT; | ||
} | ||
|
||
private int sendSeqNum = 0; | ||
|
||
private int receiveSeqNum = 0; | ||
|
||
private APCI_TYPE apciType; | ||
|
||
private ASdu aSdu = null; | ||
|
||
APdu(int sendSeqNum, int receiveSeqNum, APCI_TYPE apciType, ASdu aSdu) { | ||
this.sendSeqNum = sendSeqNum; | ||
this.receiveSeqNum = receiveSeqNum; | ||
this.apciType = apciType; | ||
this.aSdu = aSdu; | ||
} | ||
|
||
APdu(DataInputStream is, ConnectionSettings settings) throws IOException { | ||
|
||
int length = is.readByte() & 0xff; | ||
|
||
if (length < 4 || length > 253) { | ||
throw new IOException("APDU contain invalid length: " + length); | ||
} | ||
|
||
byte[] aPduHeader = new byte[4]; | ||
is.readFully(aPduHeader); | ||
|
||
if ((aPduHeader[0] & 0x01) == 0) { | ||
apciType = APCI_TYPE.I_FORMAT; | ||
sendSeqNum = ((aPduHeader[0] & 0xfe) >> 1) + ((aPduHeader[1] & 0xff) << 7); | ||
receiveSeqNum = ((aPduHeader[2] & 0xfe) >> 1) + ((aPduHeader[3] & 0xff) << 7); | ||
|
||
aSdu = new ASdu(is, settings, length - 4); | ||
} | ||
else if ((aPduHeader[0] & 0x02) == 0) { | ||
apciType = APCI_TYPE.S_FORMAT; | ||
receiveSeqNum = ((aPduHeader[2] & 0xfe) >> 1) + ((aPduHeader[3] & 0xff) << 7); | ||
} | ||
else { | ||
if (aPduHeader[0] == (byte) 0x83) { | ||
apciType = APCI_TYPE.TESTFR_CON; | ||
} | ||
else if (aPduHeader[0] == 0x43) { | ||
apciType = APCI_TYPE.TESTFR_ACT; | ||
} | ||
else if (aPduHeader[0] == 0x23) { | ||
apciType = APCI_TYPE.STOPDT_CON; | ||
} | ||
else if (aPduHeader[0] == 0x13) { | ||
apciType = APCI_TYPE.STOPDT_ACT; | ||
} | ||
else if (aPduHeader[0] == 0x0B) { | ||
apciType = APCI_TYPE.STARTDT_CON; | ||
} | ||
else { | ||
apciType = APCI_TYPE.STARTDT_ACT; | ||
} | ||
} | ||
|
||
} | ||
|
||
int encode(byte[] buffer, ConnectionSettings settings) throws IOException { | ||
|
||
buffer[0] = 0x68; | ||
|
||
int length = 4; | ||
|
||
if (apciType == APCI_TYPE.I_FORMAT) { | ||
buffer[2] = (byte) (sendSeqNum << 1); | ||
buffer[3] = (byte) (sendSeqNum >> 7); | ||
buffer[4] = (byte) (receiveSeqNum << 1); | ||
buffer[5] = (byte) (receiveSeqNum >> 7); | ||
length += aSdu.encode(buffer, 6, settings); | ||
} | ||
else if (apciType == APCI_TYPE.STARTDT_ACT) { | ||
buffer[2] = 0x07; | ||
buffer[3] = 0x00; | ||
buffer[4] = 0x00; | ||
buffer[5] = 0x00; | ||
} | ||
else if (apciType == APCI_TYPE.STARTDT_CON) { | ||
buffer[2] = 0x0b; | ||
buffer[3] = 0x00; | ||
buffer[4] = 0x00; | ||
buffer[5] = 0x00; | ||
} | ||
else if (apciType == APCI_TYPE.S_FORMAT) { | ||
buffer[2] = 0x01; | ||
buffer[3] = 0x00; | ||
buffer[4] = (byte) (receiveSeqNum << 1); | ||
buffer[5] = (byte) (receiveSeqNum >> 7); | ||
} | ||
|
||
buffer[1] = (byte) length; | ||
|
||
return length + 2; | ||
|
||
} | ||
|
||
APCI_TYPE getApciType() { | ||
return apciType; | ||
} | ||
|
||
int getSendSeqNumber() { | ||
return sendSeqNum; | ||
} | ||
|
||
int getReceiveSeqNumber() { | ||
return receiveSeqNum; | ||
} | ||
|
||
ASdu getASdu() { | ||
return aSdu; | ||
} | ||
|
||
} |
Oops, something went wrong.