Skip to content

Commit f9ef981

Browse files
committed
feat: implement dune sand walking
1 parent 7443cb7 commit f9ef981

File tree

2 files changed

+81
-10
lines changed

2 files changed

+81
-10
lines changed

extensions/business/cybersec/red_mesh/pentester_api_01.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@
6060
"MONITOR_INTERVAL": 60, # seconds between passes in continuous mode
6161
"MONITOR_JITTER": 5, # random jitter to avoid simultaneous CStore writes
6262

63+
# Dune sand walking - random delays between operations to evade IDS detection
64+
"SCAN_MIN_RND_DELAY": 0.0, # minimum delay in seconds (0 = disabled)
65+
"SCAN_MAX_RND_DELAY": 0.0, # maximum delay in seconds (0 = disabled)
66+
6367
'VALIDATION_RULES': {
6468
**BasePlugin.CONFIG['VALIDATION_RULES'],
6569
},
@@ -301,16 +305,18 @@ def _ensure_worker_entry(self, job_id, job_spec):
301305

302306

303307
def _launch_job(
304-
self,
308+
self,
305309
job_id,
306310
target,
307-
start_port,
308-
end_port,
311+
start_port,
312+
end_port,
309313
network_worker_address,
310314
nr_local_workers=4,
311315
exceptions=None,
312316
port_order=None,
313317
excluded_features=None,
318+
scan_min_delay=0.0,
319+
scan_max_delay=0.0,
314320
):
315321
"""
316322
Launch local worker threads for a job by splitting the port range.
@@ -335,6 +341,10 @@ def _launch_job(
335341
Port scanning order: "SHUFFLE" or "SEQUENTIAL".
336342
excluded_features : list[str], optional
337343
List of feature names to exclude from scanning.
344+
scan_min_delay: float, optional
345+
Minimum random delay between scan operations.
346+
scan_max_delay: float, optional
347+
Maximum random delay between scan operations.
338348
339349
Returns
340350
-------
@@ -387,12 +397,14 @@ def _launch_job(
387397
batch_job = PentestLocalWorker(
388398
owner=self,
389399
local_id_prefix=str(i + 1),
390-
target=target,
400+
target=target,
391401
job_id=job_id,
392402
initiator=network_worker_address,
393403
exceptions=exceptions,
394404
worker_target_ports=batch,
395405
excluded_features=excluded_features,
406+
scan_min_delay=scan_min_delay,
407+
scan_max_delay=scan_max_delay,
396408
)
397409
batch_job.start()
398410
local_jobs[batch_job.local_worker_id] = batch_job
@@ -476,6 +488,8 @@ def _maybe_launch_jobs(self, nr_local_workers=None):
476488
self.P("No end port specified, defaulting to 65535.")
477489
end_port = 65535
478490
exceptions = job_specs.get("exceptions", [])
491+
scan_min_delay = job_specs.get("scan_min_delay", self.cfg_scan_min_rnd_delay)
492+
scan_max_delay = job_specs.get("scan_max_delay", self.cfg_scan_max_rnd_delay)
479493
workers_requested = nr_local_workers if nr_local_workers is not None else self.cfg_nr_local_workers
480494
self.P("Using {} local workers for job {}".format(workers_requested, job_id))
481495
try:
@@ -489,6 +503,8 @@ def _maybe_launch_jobs(self, nr_local_workers=None):
489503
exceptions=exceptions,
490504
port_order=port_order,
491505
excluded_features=excluded_features,
506+
scan_min_delay=scan_min_delay,
507+
scan_max_delay=scan_max_delay,
492508
)
493509
except ValueError as exc:
494510
self.P(f"Skipping job {job_id}: {exc}", color='r')
@@ -908,6 +924,8 @@ def launch_test(
908924
excluded_features: list[str] = None,
909925
run_mode: str = "",
910926
monitor_interval: int = 0,
927+
scan_min_delay: float = 0.0,
928+
scan_max_delay: float = 0.0,
911929
):
912930
"""
913931
Start a pentest on the specified target.
@@ -937,6 +955,10 @@ def launch_test(
937955
repeated scans at monitor_interval.
938956
monitor_interval: int, optional
939957
Seconds between passes in CONTINUOUS_MONITORING mode (0 = use config).
958+
scan_min_delay: float, optional
959+
Minimum random delay between scan operations (Dune sand walking).
960+
scan_max_delay: float, optional
961+
Maximum random delay between scan operations (Dune sand walking).
940962
941963
Returns
942964
-------
@@ -996,6 +1018,15 @@ def launch_test(
9961018
if monitor_interval <= 0:
9971019
monitor_interval = self.cfg_monitor_interval
9981020

1021+
# Validate scan delays (Dune sand walking)
1022+
if scan_min_delay <= 0:
1023+
scan_min_delay = self.cfg_scan_min_rnd_delay
1024+
if scan_max_delay <= 0:
1025+
scan_max_delay = self.cfg_scan_max_rnd_delay
1026+
# Ensure min <= max
1027+
if scan_min_delay > scan_max_delay:
1028+
scan_min_delay, scan_max_delay = scan_max_delay, scan_min_delay
1029+
9991030
chainstore_peers = self.cfg_chainstore_peers
10001031
num_workers = len(chainstore_peers)
10011032

@@ -1062,6 +1093,9 @@ def launch_test(
10621093
"job_pass": 1,
10631094
"next_pass_at": None,
10641095
"pass_history": [],
1096+
# Dune sand walking
1097+
"scan_min_delay": scan_min_delay,
1098+
"scan_max_delay": scan_max_delay,
10651099
}
10661100
self.chainstore_hset(
10671101
hkey=self.cfg_instance_id,

extensions/business/cybersec/red_mesh/redmesh_utils.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import uuid
2+
import random
23
import threading
34
import socket
45
import json
@@ -47,15 +48,17 @@ class PentestLocalWorker(
4748
"""
4849

4950
def __init__(
50-
self,
51-
owner,
52-
target,
51+
self,
52+
owner,
53+
target,
5354
job_id : str,
54-
initiator : str,
55+
initiator : str,
5556
local_id_prefix : str,
5657
worker_target_ports=COMMON_PORTS,
5758
exceptions=None,
5859
excluded_features=None,
60+
scan_min_delay: float = 0.0,
61+
scan_max_delay: float = 0.0,
5962
):
6063
"""
6164
Initialize a pentest worker with target ports and exclusions.
@@ -78,6 +81,10 @@ def __init__(
7881
Ports to exclude from scanning.
7982
excluded_features: list[str], optional
8083
List of feature method names to exclude.
84+
scan_min_delay : float, optional
85+
Minimum random delay (seconds) between operations (Dune sand walking).
86+
scan_max_delay : float, optional
87+
Maximum random delay (seconds) between operations (Dune sand walking).
8188
8289
Raises
8390
------
@@ -89,6 +96,8 @@ def __init__(
8996
if exceptions is None:
9097
exceptions = []
9198
self.target = target
99+
self.scan_min_delay = scan_min_delay
100+
self.scan_max_delay = scan_max_delay
92101
self.job_id = job_id
93102
self.initiator = initiator
94103
self.local_worker_id = "RM-{}-{}".format(
@@ -305,6 +314,25 @@ def _check_stopped(self):
305314
return self.state["done"] or self.stop_event.is_set()
306315

307316

317+
def _interruptible_sleep(self):
318+
"""
319+
Sleep for a random interval (Dune sand walking), interruptible by stop_event.
320+
321+
Uses stop_event.wait(timeout) instead of time.sleep() so that stop requests
322+
are handled immediately rather than waiting for the sleep to complete.
323+
324+
Returns
325+
-------
326+
bool
327+
True if stop was requested during sleep (should exit), False otherwise.
328+
"""
329+
if self.scan_max_delay <= 0:
330+
return False # Delays disabled
331+
delay = random.uniform(self.scan_min_delay, self.scan_max_delay)
332+
# wait() returns True if event is set (stop requested), False on timeout
333+
return self.stop_event.wait(timeout=delay)
334+
335+
308336
def execute_job(self):
309337
"""
310338
Run the full pentesting workflow: port scanning, service info gathering,
@@ -382,6 +410,9 @@ def _scan_ports_step(self, batch_size=None, batch_nr=1):
382410
for i, port in enumerate(ports_batch):
383411
if self.stop_event.is_set():
384412
return
413+
# Dune sand walking - random delay before each port scan
414+
if self._interruptible_sleep():
415+
return # Stop was requested during sleep
385416
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
386417
sock.settimeout(0.3)
387418
try:
@@ -436,7 +467,10 @@ def _gather_service_info(self):
436467
method_info = []
437468
for port in open_ports:
438469
if self.stop_event.is_set():
439-
continue
470+
return
471+
# Dune sand walking - random delay before each service probe
472+
if self._interruptible_sleep():
473+
return # Stop was requested during sleep
440474
info = func(target, port)
441475
if port not in self.state["service_info"]:
442476
self.state["service_info"][port] = {}
@@ -481,7 +515,10 @@ def _run_web_tests(self):
481515
func = getattr(self, method)
482516
for port in ports_to_test:
483517
if self.stop_event.is_set():
484-
return
518+
return
519+
# Dune sand walking - random delay before each web test
520+
if self._interruptible_sleep():
521+
return # Stop was requested during sleep
485522
iter_result = func(target, port)
486523
if iter_result:
487524
result.append(f"{method}:{port} {iter_result}")

0 commit comments

Comments
 (0)