@@ -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 :
0 commit comments