Skip to content

Commit b15ad08

Browse files
committed
Alessandro's Concealment module now works in persistent mode
1 parent 6ac3c1e commit b15ad08

File tree

4 files changed

+62
-104
lines changed

4 files changed

+62
-104
lines changed

dhalsim/network_attacks/concealment_netfilter_queue.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,10 @@ def handle_enip_response(self, ip_payload):
244244
modified = False
245245
return ip_payload, modified
246246

247-
def handle_enip_request(self, ip_payload, offset):
247+
def handle_enip_request(self, ip_payload):
248248

249249
this_session = int.from_bytes(ip_payload[Raw].load[4:8], sys.byteorder)
250-
tag_name = ip_payload[Raw].load.decode(encoding='latin-1')[54:offset]
250+
tag_name = ip_payload[Raw].load.decode(encoding='latin-1')[54:60].split(':')[0]
251251
context = int.from_bytes(ip_payload[Raw].load[12:20], sys.byteorder)
252252

253253
#self.logger.debug('this tag is: ' + str(tag_name))
@@ -289,13 +289,9 @@ def capture(self, packet):
289289
return
290290

291291
else:
292-
if len(p) == 118:
292+
if len(p) == 118 or len(p) == 116 or len(p) == 120:
293293
#self.logger.debug('handling request 57')
294-
self.handle_enip_request(p, 57)
295-
#self.logger.debug('handled request')
296-
elif len(p) == 116:
297-
#self.logger.debug('handling request 56')
298-
self.handle_enip_request(p, 56)
294+
self.handle_enip_request(p)
299295
#self.logger.debug('handled request')
300296
else:
301297
packet.accept()

dhalsim/network_attacks/synced_attack.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,8 @@ def main_loop(self, start_now=False):
292292
while not self.get_sync(0):
293293
pass
294294

295-
# Modified for the Alessandro's concealment to work properly
296-
if start_now:
295+
# Modified for the Alessandro's concealment to work properly.
296+
if start_now and int(self.get_master_clock()) > 10:
297297
run = True
298298
else:
299299
run = self.check_trigger()

dhalsim/network_attacks/unconstrained_blackbox_netfilter_queue.py

Lines changed: 56 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,8 @@ def __init__(self, intermediate_yaml_path: Path, yaml_index: int, queue_number:
5151

5252
if self.intermediate_attack['persistent'] == 'True':
5353
self.persistent = True
54-
self.first_time = True
5554
else:
5655
self.persistent = False
57-
self.first_time = False
5856

5957
if self.persistent:
6058
self.window_size = 1
@@ -79,13 +77,11 @@ def __init__(self, intermediate_yaml_path: Path, yaml_index: int, queue_number:
7977

8078
self.scada_values = []
8179
self.scada_session_ids = []
82-
self.initialized = False
80+
self.ok_to_conceal = False
8381

8482
# This flaf ensures that the prediction is called only once per iteration
8583
self.predicted_for_iteration = False
8684

87-
88-
8985
#toDo: This will be something configured in the YAML file
9086
file_expr = 'training_data/ctown/'
9187

@@ -131,14 +127,8 @@ def handle_sync(self):
131127
while (not self.get_sync(0)) and self.sync_flag:
132128
pass
133129

134-
self.logger.debug('Handle sync. Sync 0 is: ' + str(self.get_sync(0)))
135-
self.logger.debug('Handle sync. Sync 1 is: ' + str(self.get_sync(1)))
136-
self.logger.debug('Handle sync. Sync 2 is: ' + str(self.get_sync(2)))
137-
self.logger.debug('Handle sync. Sync 3 is: ' + str(self.get_sync(3)))
138-
139130
# We have to keep the same state machine as PLCs
140131
self.set_sync(1)
141-
self.logger.debug('Sync flat set in 1')
142132

143133
# 2 is when the PLCs exchange locally their information
144134
while not self.get_sync(2):
@@ -148,19 +138,12 @@ def handle_sync(self):
148138

149139
# We stay in 2, to conceal the values exchanged remotely from the PLCs, until we make a prediction
150140
while self.missing_scada_tags and self.sync_flag:
151-
152-
#todo: This is really horrible, but launching this module in persistent mode is breaking our sync state machine
153-
if self.first_time:
154-
self.first_time = True
155-
break
156-
#self.logger.debug('Waiting for all tags and sync_flag')
157141
pass
158142

159143
self.logger.debug('Setting attack sync in 3')
160144
self.set_sync(3)
161145

162146
self.logger.debug('Netfilter sync thread while finished')
163-
#self.set_sync(3)
164147

165148
def set_initial_conditions_of_scada_values(self):
166149
df = pd.DataFrame(columns=self.scada_tags)
@@ -179,92 +162,72 @@ def get_scada_tags(self):
179162

180163
# Delivers a pandas dataframe with ALL SCADA tags
181164
def predict_concealment_values(self):
182-
183165
# This returns 1 row
184166
self.calculated_concealment_values_df = self.advAE.predict(self.received_scada_tags_df)
167+
self.logger.debug('predicting')
185168

186-
# Collect samples, don't conceal
169+
def process_tag_in_missing(self, session, ip_payload):
170+
current_clock = int(self.get_master_clock())
171+
# We store the value, this df is an input for the concealment ML model
172+
self.received_scada_tags_df.loc[current_clock, session['tag']] = translate_payload_to_float(
173+
ip_payload[Raw].load)
174+
self.missing_scada_tags.remove(session['tag'])
175+
self.logger.debug('Missing tags len after removing: ' + str(len(self.missing_scada_tags)))
187176

188-
self.logger.debug('predicting')
177+
def scada_tag_list_empty(self):
178+
self.logger.debug('SCADA set empty')
189179

190-
def handle_concealment(self, session, ip_payload):
180+
# Wait for sync to take place
181+
while not self.get_sync(3) and self.sync_flag:
182+
self.logger.debug('Waiting for flag 3')
183+
pass
191184

192-
self.logger.debug('Concealing method for session: ' + str(session))
185+
self.missing_scada_tags = list(self.scada_tags)
193186

187+
if self.persistent:
188+
if self.intermediate_attack['trigger']['start'] <= int(self.get_master_clock()) < \
189+
self.intermediate_attack['trigger']['end']:
190+
self.logger.debug('Adversarial Model initialized and ready to conceal')
191+
self.ok_to_conceal = True
192+
if not self.predicted_for_iteration:
193+
self.predict_concealment_values()
194+
self.logger.debug('Concealment values predicted')
195+
self.predicted_for_iteration = True
196+
else:
197+
self.ok_to_conceal = False
198+
else:
199+
if not self.ok_to_conceal:
200+
self.received_window_size = self.received_window_size + 1
201+
if self.received_window_size >= self.window_size - 1:
202+
self.ok_to_conceal = True
203+
self.logger.debug('Adversarial Model initialized')
204+
205+
elif not self.predicted_for_iteration:
206+
self.predict_concealment_values()
207+
self.logger.debug('Concealment values predicted')
208+
self.predicted_for_iteration = True
209+
210+
def handle_concealment(self, session, ip_payload):
194211
if len(self.missing_scada_tags) == len(self.scada_tags):
195212
# We miss all the tags. Start of a new prediction cycle
196213
self.logger.debug('We miss all the tags. Start of a new prediction cycle')
197214
self.predicted_for_iteration = False
198215

199-
#aux_tags = list(self.scada_tags)
200-
#self.logger.debug('Missing tags are: ' + str(self.missing_scada_tags))
201-
#self.logger.debug('SCADA tags are: ' + str(self.scada_tags))
202-
#self.logger.debug('Missing tags len: ' + str(len(self.missing_scada_tags)))
203-
#self.logger.debug('SCADA tags len: ' + str(len(self.scada_tags)))
204-
205216
if session['tag'] in self.missing_scada_tags:
217+
self.process_tag_in_missing(session, ip_payload)
206218

207-
current_clock = int(self.get_master_clock())
208-
# We store the value, this df is an input for the concealment ML model
209-
self.received_scada_tags_df.loc[current_clock, session['tag']] = translate_payload_to_float(ip_payload[Raw].load)
210-
211-
#self.logger.debug('Received tag ' + str(session['tag']) + ' with value: ' +
212-
str(self.received_scada_tags_df[session['tag']].iloc[-1])
213-
self.missing_scada_tags.remove(session['tag'])
214-
self.logger.debug('Missing tags len after removing: ' + str(len(self.missing_scada_tags)))
215-
216-
# Missing set is empty, increase the window count or predict
217-
if not self.missing_scada_tags:
218-
219-
# Wait for sync to take place
220-
while not self.get_sync(3) and self.sync_flag:
221-
self.logger.debug('Waiting for flag 3')
222-
pass
223-
224-
self.missing_scada_tags = list(self.scada_tags)
225-
226-
if self.persistent:
227-
if self.intermediate_attack['trigger']['start'] <= int(self.get_master_clock()) <\
228-
self.intermediate_attack['trigger']['end']:
229-
# We predict only during the trigger duration
230-
# todo: This means we could not launch persistent with sensor triggers
231-
if not self.predicted_for_iteration:
232-
self.predict_concealment_values()
233-
self.logger.debug('Concealment values predicted')
234-
self.predicted_for_iteration = True
235-
else:
236-
if not self.initialized:
237-
self.received_window_size = self.received_window_size + 1
238-
if self.received_window_size >= self.window_size - 1:
239-
self.initialized = True
240-
self.logger.debug('Adversarial Model initialized')
241-
242-
elif not self.predicted_for_iteration:
243-
self.predict_concealment_values()
244-
self.logger.debug('Concealment values predicted')
245-
self.predicted_for_iteration = True
246-
247-
# We are still missing some scada tags
248-
else:
249-
if self.persistent:
250-
if self.intermediate_attack['trigger']['start'] <= int(self.get_master_clock()) <\
251-
self.intermediate_attack['trigger']['end']:
252-
modified = True
253-
return translate_float_to_payload(self.calculated_concealment_values_df.loc[-1, session['tag']],
254-
ip_payload[Raw].load), modified
255-
else:
256-
modified = False
257-
# We don't conceal outside of trigger
258-
return ip_payload[Raw].load, modified
259-
else:
260-
if self.initialized:
261-
modified = True
262-
return translate_float_to_payload(self.calculated_concealment_values_df.loc[-1, session['tag']],
263-
ip_payload[Raw].load), modified
264-
else:
265-
modified = False
266-
# We don't conceal before initialization
267-
return ip_payload[Raw].load, modified
219+
# Missing set is empty, increase the window count or predict
220+
if not self.missing_scada_tags:
221+
self.scada_tag_list_empty()
222+
223+
if self.ok_to_conceal:
224+
modified = True
225+
return translate_float_to_payload(self.calculated_concealment_values_df.loc[-1, session['tag']],
226+
ip_payload[Raw].load), modified
227+
else:
228+
modified = False
229+
# We don't conceal before initialization
230+
return ip_payload[Raw].load, modified
268231

269232
def handle_enip_response(self, ip_payload):
270233
this_session = int.from_bytes(ip_payload[Raw].load[4:8], sys.byteorder)
@@ -273,7 +236,7 @@ def handle_enip_response(self, ip_payload):
273236
#self.logger.debug('ENIP response session: ' + str(this_session))
274237
#self.logger.debug('ENIP response context: ' + str(this_context))
275238

276-
self.logger.debug('ENIP Response for: ' + str(ip_payload[IP].dst))
239+
#self.logger.debug('ENIP Response for: ' + str(ip_payload[IP].dst))
277240

278241
try:
279242
# Concealment values to SCADA
@@ -316,7 +279,7 @@ def capture(self, packet):
316279

317280
try:
318281
p = IP(packet.get_payload())
319-
self.logger.debug('packet')
282+
#self.logger.debug('packet')
320283
if 'TCP' in p:
321284
if len(p) == 102:
322285
p[Raw].load, modified = self.handle_enip_response(p)
@@ -325,6 +288,7 @@ def capture(self, packet):
325288
del p[TCP].chksum
326289
packet.set_payload(bytes(p))
327290
packet.accept()
291+
self.logger.debug('Packet modified and accepted')
328292
return
329293

330294
else:

examples/ctown_topology/ctown_unconstrained_blackbox_concealment_mitm.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ network_attacks:
22
- name: plc3attack
33
type: unconstrained_blackbox_concealment_mitm
44
trigger:
5-
#start: 3
6-
#end: 20
75
start: 648
86
end: 792
97
type: time

0 commit comments

Comments
 (0)