87
88
89
90
@@ -976,7 +981,13 @@
334
335
336
-337 class EngineClient (EngineBase):
+337
+338
+339
+340
+341
+342
+343
class EngineClient (EngineBase):
"""
The client portion of BBOT's RPC Engine.
@@ -1011,9 +1022,9 @@
SERVER_CLASS = None
- def __init__ (self, ** kwargs):
- super() . __init__ ()
+ def __init__ (self, debug = False , ** kwargs):
self . name = f "EngineClient { self . __class__ . __name__ }"
+ super() . __init__ (debug = debug)
self . process = None
if self . SERVER_CLASS is None :
raise ValueError ( f "Must set EngineClient SERVER_CLASS, { self . SERVER_CLASS }" )
@@ -1038,7 +1049,7 @@
async def run_and_return (self, command, * args, ** kwargs):
fn_str = f "{ command }({ args }, { kwargs })"
- self . log . debug( f "{ self . name }: executing run-and-return { fn_str }" )
+ self . debug( f "{ self . name }: executing run-and-return { fn_str }" )
if self . _shutdown_status and not command == "_shutdown" :
self . log . verbose( f "{ self . name } has been shut down and is not accepting new tasks" )
return
@@ -1047,7 +1058,7 @@
message = self . make_message(command, args = args, kwargs = kwargs)
if message is error_sentinel:
return
- await self . _infinite_retry(socket . send, message)
+ await socket . send(message)
binary = await self . _infinite_retry(socket . recv, _context = f "waiting for return value from { fn_str }" )
except BaseException :
try :
@@ -1058,7 +1069,7 @@
raise
# self.log.debug(f"{self.name}.{command}({kwargs}) got binary: {binary}")
message = self . unpickle(binary)
- self . log . debug( f "{ self . name }: { fn_str } got return value: { message }" )
+ self . debug( f "{ self . name }: { fn_str } got return value: { message }" )
# error handling
if self . check_error(message):
return
@@ -1066,7 +1077,7 @@
async def run_and_yield (self, command, * args, ** kwargs):
fn_str = f "{ command }({ args }, { kwargs })"
- self . log . debug( f "{ self . name }: executing run-and-yield { fn_str }" )
+ self . debug( f "{ self . name }: executing run-and-yield { fn_str }" )
if self . _shutdown_status:
self . log . verbose( "Engine has been shut down and is not accepting new tasks" )
return
@@ -1085,18 +1096,18 @@
)
# self.log.debug(f"{self.name}.{command}({kwargs}) got binary: {binary}")
message = self . unpickle(binary)
- self . log . debug( f "{ self . name } { command } got iteration: { message }" )
+ self . debug( f "{ self . name }: { fn_str } got iteration: { message }" )
# error handling
if self . check_error(message) or self . check_stop(message):
break
yield message
except ( StopAsyncIteration , GeneratorExit ) as e:
exc_name = e . __class__ . __name__
- self . log . debug( f "{ self . name }.{ command } got { exc_name }" )
+ self . debug( f "{ self . name }.{ command } got { exc_name }" )
try :
await self . send_cancel_message(socket, fn_str)
except Exception :
- self . log . debug( f "{ self . name }.{ command } failed to send cancel message after { exc_name }" )
+ self . debug( f "{ self . name }.{ command } failed to send cancel message after { exc_name }" )
self . log . trace(traceback . format_exc())
break
@@ -1163,6 +1174,7 @@
# this allows us to more easily mock http, etc.
if os . environ . get( "BBOT_TESTING" , "" ) == "True" :
kwargs[ "_loop" ] = get_event_loop()
+ kwargs[ "debug" ] = self . _debug
self . process = CORE . create_process(
target = self . server_process,
args = (
@@ -1202,7 +1214,7 @@
if self . _server_process is None :
self . _server_process = self . start_server()
while not self . socket_path . exists():
- self . log . debug( f "{ self . name }: waiting for server process to start..." )
+ self . debug( f "{ self . name }: waiting for server process to start..." )
await asyncio . sleep( 0.1 )
socket = self . context . socket(zmq . DEALER)
socket . setsockopt(zmq . LINGER, 0 )
@@ -1248,12 +1260,7 @@
Source code in bbot/core/engine.py
-206
-207
-208
-209
-210
-211
+211
212
213
214
@@ -1263,7 +1270,12 @@ 218
219
220
-221 async def send_cancel_message (self, socket, context):
+221
+222
+223
+224
+225
+226
async def send_cancel_message (self, socket, context):
"""
Send a cancel message and wait for confirmation from the server
"""
@@ -1316,13 +1328,7 @@
Source code in bbot/core/engine.py
-340
-341
-342
-343
-344
-345
-346
+346
347
348
349
@@ -1580,7 +1586,13 @@
601
602
603
-604 class EngineServer (EngineBase):
+604
+605
+606
+607
+608
+609
+610
class EngineServer (EngineBase):
"""
The server portion of BBOT's RPC Engine.
@@ -1609,9 +1621,9 @@
CMDS = {}
- def __init__ (self, socket_path):
- super() . __init__ ()
+ def __init__ (self, socket_path, debug = False ):
self . name = f "EngineServer { self . __class__ . __name__ }"
+ super() . __init__ (debug = debug)
self . socket_path = socket_path
self . client_id_var = contextvars . ContextVar( "client_id" , default = None )
# task <--> client id mapping
@@ -1640,21 +1652,21 @@
fn_str = f "{ command_fn . __name__ }({ args }, { kwargs })"
with self . client_id_context(client_id):
try :
- self . log . debug( f "{ self . name } run-and-return { fn_str }" )
+ self . debug( f "{ self . name }: run-and-return { fn_str }" )
result = error_sentinel
try :
result = await command_fn( * args, ** kwargs)
except BaseException as e:
if not in_exception_chain(e, ( KeyboardInterrupt , asyncio . CancelledError)):
error = f "Error in { self . name }.{ fn_str }: { e }"
- self . log . debug(error)
+ self . debug(error)
trace = traceback . format_exc()
- self . log . debug(trace)
+ self . debug(trace)
result = { "_e" : (error, trace)}
finally :
self . tasks . pop(client_id, None )
if result is not error_sentinel:
- self . log . debug( f "{ self . name }: Sending response to { fn_str }: { result }" )
+ self . debug( f "{ self . name }: Sending response to { fn_str }: { result }" )
await self . send_socket_multipart(client_id, result)
except BaseException as e:
self . log . critical(
@@ -1662,27 +1674,27 @@
)
self . log . critical(traceback . format_exc())
finally :
- self . log . debug( f "{ self . name } finished run-and-return { command_fn . __name__ }({ args }, { kwargs })" )
+ self . debug( f "{ self . name } finished run-and-return { command_fn . __name__ }({ args }, { kwargs })" )
async def run_and_yield (self, client_id, command_fn, * args, ** kwargs):
fn_str = f "{ command_fn . __name__ }({ args }, { kwargs })"
with self . client_id_context(client_id):
try :
- self . log . debug( f "{ self . name } run-and-yield { fn_str }" )
+ self . debug( f "{ self . name }: run-and-yield { fn_str }" )
try :
async for _ in command_fn( * args, ** kwargs):
- self . log . debug( f "{ self . name }: sending iteration for { command_fn . __name__ }(): { _ }" )
+ self . debug( f "{ self . name }: sending iteration for { command_fn . __name__ }(): { _ }" )
await self . send_socket_multipart(client_id, _)
except BaseException as e:
if not in_exception_chain(e, ( KeyboardInterrupt , asyncio . CancelledError)):
error = f "Error in { self . name }.{ fn_str }: { e }"
trace = traceback . format_exc()
- self . log . debug(error)
- self . log . debug(trace)
+ self . debug(error)
+ self . debug(trace)
result = { "_e" : (error, trace)}
await self . send_socket_multipart(client_id, result)
finally :
- self . log . debug( f "{ self . name } reached end of run-and-yield iteration for { command_fn . __name__ }()" )
+ self . debug( f "{ self . name } reached end of run-and-yield iteration for { command_fn . __name__ }()" )
# _s == special signal that means StopIteration
await self . send_socket_multipart(client_id, { "_s" : None })
self . tasks . pop(client_id, None )
@@ -1692,7 +1704,7 @@
)
self . log . critical(traceback . format_exc())
finally :
- self . log . debug( f "{ self . name } finished run-and-yield { command_fn . __name__ }()" )
+ self . debug( f "{ self . name } finished run-and-yield { command_fn . __name__ }()" )
async def send_socket_multipart (self, client_id, message):
try :
@@ -1707,7 +1719,7 @@
return True
async def worker (self):
- self . log . debug( f "{ self . name }: starting worker" )
+ self . debug( f "{ self . name }: starting worker" )
try :
while 1 :
client_id, binary = await self . socket . recv_multipart()
@@ -1723,14 +1735,14 @@
# -1 == cancel task
if cmd == - 1 :
- self . log . debug( f "{ self . name } got cancel signal" )
+ self . debug( f "{ self . name } got cancel signal" )
await self . send_socket_multipart(client_id, { "m" : "CANCEL_OK" })
await self . cancel_task(client_id)
continue
# -99 == shutdown task
if cmd == - 99 :
- self . log . debug( f "{ self . name } got shutdown signal" )
+ self . debug( f "{ self . name } got shutdown signal" )
await self . send_socket_multipart(client_id, { "m" : "SHUTDOWN_OK" })
await self . _shutdown()
return
@@ -1768,7 +1780,7 @@
self . log . error( f "{ self . name }: error in EngineServer worker: { e }" )
self . log . trace(traceback . format_exc())
finally :
- self . log . debug( f "{ self . name }: finished worker()" )
+ self . debug( f "{ self . name }: finished worker()" )
async def _shutdown (self):
if not self . _shutdown_status:
@@ -1783,7 +1795,7 @@
self . context . term()
except Exception :
self . log . trace(traceback . format_exc())
- self . log . debug( f "{ self . name }: finished shutting down" )
+ self . log . verbose( f "{ self . name }: finished shutting down" )
def new_child_task (self, client_id, coro):
task = asyncio . create_task(coro)
@@ -1816,11 +1828,11 @@
if parent_task is None :
return
parent_task, _cmd, _args, _kwargs = parent_task
- self . log . debug( f "{ self . name }: Cancelling client id { client_id } (task: { parent_task })" )
+ self . debug( f "{ self . name }: Cancelling client id { client_id } (task: { parent_task })" )
parent_task . cancel()
child_tasks = self . child_tasks . pop(client_id, set())
if child_tasks:
- self . log . debug( f "{ self . name }: Cancelling { len(child_tasks) :,} child tasks for client id { client_id }" )
+ self . debug( f "{ self . name }: Cancelling { len(child_tasks) :,} child tasks for client id { client_id }" )
for child_task in child_tasks:
child_task . cancel()
@@ -1831,7 +1843,7 @@
try :
await asyncio . wait_for(task, timeout = 10 )
except ( TimeoutError , asyncio . exceptions . TimeoutError):
- self . log . debug( f "{ self . name }: Timeout cancelling task" )
+ self . log . trace( f "{ self . name }: Timeout cancelling task: { task }" )
return
except ( KeyboardInterrupt , asyncio . CancelledError):
return
diff --git a/Dev/dev/event/index.html b/Dev/dev/event/index.html
index 5a200be618..86b9d18f4d 100644
--- a/Dev/dev/event/index.html
+++ b/Dev/dev/event/index.html
@@ -20,7 +20,7 @@
-
+
@@ -726,30 +726,7 @@
Source code in bbot/core/event/base.py
-1458
-1459
-1460
-1461
-1462
-1463
-1464
-1465
-1466
-1467
-1468
-1469
-1470
-1471
-1472
-1473
-1474
-1475
-1476
-1477
-1478
-1479
-1480
-1481
+1481
1482
1483
1484
@@ -853,7 +830,30 @@
1582
1583
1584
-1585 def make_event (
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
def make_event (
data,
event_type = None ,
parent = None ,
@@ -1034,30 +1034,7 @@
Source code in bbot/core/event/base.py
-1588
-1589
-1590
-1591
-1592
-1593
-1594
-1595
-1596
-1597
-1598
-1599
-1600
-1601
-1602
-1603
-1604
-1605
-1606
-1607
-1608
-1609
-1610
-1611
+1611
1612
1613
1614
@@ -1083,7 +1060,30 @@
1634
1635
1636
-1637 def event_from_json (j, siem_friendly = False ):
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
def event_from_json (j, siem_friendly = False ):
"""
Creates an event object from a JSON dictionary.
@@ -2169,7 +2169,16 @@
873
874
875
-876
class BaseEvent :
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
class BaseEvent :
"""
Represents a piece of data discovered during a BBOT scan.
@@ -2440,6 +2449,15 @@
return self . host
return self . _host_original
+ @property
+ def closest_host (self):
+ """
+ Walk up the chain of parents events until we hit the first one with a host
+ """
+ if self . host is not None or self . parent is None or self . parent is self:
+ return self . host
+ return self . parent . closest_host
+
@property
def port (self):
self . host
@@ -2698,7 +2716,7 @@
return parents
def _host (self):
- return ""
+ return None
def _sanitize_data (self, data):
"""
@@ -3441,16 +3459,7 @@
Source code in bbot/core/event/base.py
-686
-687
-688
-689
-690
-691
-692
-693
-694
-695
+695
696
697
698
@@ -3513,7 +3522,16 @@
755
756
757
-758 def json (self, mode = "json" , siem_friendly = False ):
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
def json (self, mode = "json" , siem_friendly = False ):
"""
Serializes the event object to a JSON-compatible dictionary.
@@ -3624,20 +3642,20 @@
Source code in bbot/core/event/base.py
-760
-761
-762
-763
-764
-765
-766
-767
-768
-769
+@staticmethod
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
class DNSHelper (EngineClient):
+198
+199
class DNSHelper (EngineClient):
SERVER_CLASS = DNSEngine
ERROR_CLASS = DNSError
@@ -798,7 +799,8 @@
self . parent_helper = parent_helper
self . config = self . parent_helper . config
self . dns_config = self . config . get( "dns" , {})
- super() . __init__ (server_kwargs = { "config" : self . config})
+ engine_debug = self . config . get( "engine" , {}) . get( "debug" , False )
+ super() . __init__ (server_kwargs = { "config" : self . config}, debug = engine_debug)
# resolver
self . timeout = self . dns_config . get( "timeout" , 5 )
@@ -953,8 +955,8 @@
Source code in bbot/core/helpers/dns/dns.py
-async def resolve (self, query, ** kwargs):
+async def resolve (self, query, ** kwargs):
return await self . run_and_return( "resolve" , query = query, ** kwargs)
@@ -972,14 +974,14 @@
Source code in bbot/core/helpers/dns/dns.py
- 93
- 94
+async def resolve_batch (self, queries, ** kwargs):
+100
+101
async def resolve_batch (self, queries, ** kwargs):
agen = self . run_and_yield( "resolve_batch" , queries = queries, ** kwargs)
while 1 :
try :
@@ -1003,8 +1005,8 @@
Source code in bbot/core/helpers/dns/dns.py
-async def resolve_raw (self, query, ** kwargs):
+async def resolve_raw (self, query, ** kwargs):
return await self . run_and_return( "resolve_raw" , query = query, ** kwargs)
@@ -1089,8 +1091,7 @@
Source code in bbot/core/helpers/dns/dns.py
-119
-120
+120
121
122
123
@@ -1133,7 +1134,8 @@
160
161
162
-163 @async_cachedmethod ( lambda self: self . _is_wildcard_cache)
+163
+164
@async_cachedmethod ( lambda self: self . _is_wildcard_cache)
async def is_wildcard (self, query, ips = None , rdtype = None ):
"""
Use this method to check whether a *host* is a wildcard entry
@@ -1194,13 +1196,13 @@
Source code in bbot/core/helpers/dns/dns.py
-165
-166
+@async_cachedmethod ( lambda self: self . _is_wildcard_domain_cache)
+171
+172
@async_cachedmethod ( lambda self: self . _is_wildcard_domain_cache)
async def is_wildcard_domain (self, domain, log_info = False ):
domain = self . _wildcard_prevalidation(domain)
if not domain:
diff --git a/Dev/dev/helpers/index.html b/Dev/dev/helpers/index.html
index ab3052b8ce..3893e307a1 100644
--- a/Dev/dev/helpers/index.html
+++ b/Dev/dev/helpers/index.html
@@ -20,7 +20,7 @@
-
+
diff --git a/Dev/dev/helpers/interactsh/index.html b/Dev/dev/helpers/interactsh/index.html
index e72d20162b..345de25dfd 100644
--- a/Dev/dev/helpers/interactsh/index.html
+++ b/Dev/dev/helpers/interactsh/index.html
@@ -20,7 +20,7 @@
-
+
diff --git a/Dev/dev/helpers/misc/index.html b/Dev/dev/helpers/misc/index.html
index 2c2f87657b..8375585b59 100644
--- a/Dev/dev/helpers/misc/index.html
+++ b/Dev/dev/helpers/misc/index.html
@@ -20,7 +20,7 @@
-
+
diff --git a/Dev/dev/helpers/web/index.html b/Dev/dev/helpers/web/index.html
index cb3fe901d8..e03bc6c21c 100644
--- a/Dev/dev/helpers/web/index.html
+++ b/Dev/dev/helpers/web/index.html
@@ -20,7 +20,7 @@
-
+
@@ -1151,7 +1151,11 @@
591
592
593
-594
class WebHelper (EngineClient):
+594
+595
+596
+597
+598
class WebHelper (EngineClient):
SERVER_CLASS = HTTPEngine
ERROR_CLASS = WebError
@@ -1188,7 +1192,11 @@
self . web_spider_distance = self . web_config . get( "spider_distance" , 0 )
self . target = self . preset . target
self . ssl_verify = self . config . get( "ssl_verify" , False )
- super() . __init__ (server_kwargs = { "config" : self . config, "target" : self . parent_helper . preset . target . radix_only})
+ engine_debug = self . config . get( "engine" , {}) . get( "debug" , False )
+ super() . __init__ (
+ server_kwargs = { "config" : self . config, "target" : self . parent_helper . preset . target . radix_only},
+ debug = engine_debug,
+ )
def AsyncClient (self, * args, ** kwargs):
from .client import BBOTAsyncClient
@@ -1883,11 +1891,7 @@
Source code in bbot/core/helpers/web/web.py
-259
-260
-261
-262
-263
+263
264
265
266
@@ -1941,7 +1945,11 @@
314
315
316
-317 async def api_page_iter (self, url, page_size = 100 , json = True , next_key = None , ** requests_kwargs):
+317
+318
+319
+320
+321
async def api_page_iter (self, url, page_size = 100 , json = True , next_key = None , ** requests_kwargs):
"""
An asynchronous generator function for iterating through paginated API data.
@@ -2082,11 +2090,7 @@
Source code in bbot/core/helpers/web/web.py
-438
-439
-440
-441
-442
+442
443
444
445
@@ -2138,7 +2142,11 @@
491
492
493
-494 def beautifulsoup (
+494
+495
+496
+497
+498
def beautifulsoup (
self,
markup,
features = "html.parser" ,
@@ -2334,11 +2342,7 @@
Source code in bbot/core/helpers/web/web.py
-319
-320
-321
-322
-323
+323
324
325
326
@@ -2451,7 +2455,11 @@
433
434
435
-436 async def curl (self, * args, ** kwargs):
+436
+437
+438
+439
+440
async def curl (self, * args, ** kwargs):
"""
An asynchronous function that runs a cURL command with specified arguments and options.
@@ -2660,11 +2668,7 @@
Source code in bbot/core/helpers/web/web.py
-165
-166
-167
-168
-169
+169
170
171
172
@@ -2699,7 +2703,11 @@
201
202
203
-204 async def download (self, url, ** kwargs):
+204
+205
+206
+207
+208
async def download (self, url, ** kwargs):
"""
Asynchronous function for downloading files from a given URL. Supports caching with an optional
time period in hours via the "cache_hrs" keyword argument. In case of successful download,
@@ -2783,11 +2791,7 @@
Source code in bbot/core/helpers/web/web.py
-499
-500
-501
-502
-503
+503
504
505
506
@@ -2823,7 +2827,11 @@
536
537
538
-539 def is_login_page (self, html):
+539
+540
+541
+542
+543
def is_login_page (self, html):
"""
Determines if the provided HTML content contains a login page.
@@ -3059,11 +3067,7 @@
Source code in bbot/core/helpers/web/web.py
-