bbot 2.4.1.6095rc0__py3-none-any.whl → 2.4.1.6107rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bbot might be problematic. Click here for more details.

bbot/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # version placeholder (replaced by poetry-dynamic-versioning)
2
- __version__ = "v2.4.1.6095rc"
2
+ __version__ = "v2.4.1.6107rc"
3
3
 
4
4
  from .scanner import Scanner, Preset
5
5
 
bbot/defaults.yml CHANGED
@@ -92,10 +92,18 @@ web:
92
92
  # These are attached to all in-scope HTTP requests
93
93
  # Note that some modules (e.g. github) may end up sending these to out-of-scope resources
94
94
  http_headers: {}
95
- # HTTP retries (for Python requests; API calls, etc.)
95
+ # How many times to retry API requests
96
+ # Note that this is a separate mechanism on top of HTTP retries
97
+ # which will retry API requests that don't return a successful status code
98
+ api_retries: 2
99
+ # HTTP retries - try again if the raw connection fails
96
100
  http_retries: 1
97
101
  # HTTP retries (for httpx)
98
102
  httpx_retries: 1
103
+ # Default sleep interval when rate limited by 429 (and retry-after isn't provided)
104
+ 429_sleep_interval: 30
105
+ # Maximum sleep interval when rate limited by 429 (and an excessive retry-after is provided)
106
+ 429_max_sleep_interval: 60
99
107
  # Enable/disable debug messages for web requests/responses
100
108
  debug: false
101
109
  # Maximum number of HTTP redirects to follow
bbot/modules/base.py CHANGED
@@ -104,12 +104,8 @@ class BaseModule:
104
104
  _batch_size = 1
105
105
  batch_wait = 10
106
106
 
107
- # API retries, etc.
108
- _api_retries = 2
109
107
  # disable the module after this many failed attempts in a row
110
108
  _api_failure_abort_threshold = 3
111
- # sleep for this many seconds after being rate limited
112
- _429_sleep_interval = 30
113
109
 
114
110
  default_discovery_context = "{module} discovered {event.type}: {event.data}"
115
111
 
@@ -159,12 +155,18 @@ class BaseModule:
159
155
  # track number of failures (for .api_request())
160
156
  self._api_request_failures = 0
161
157
 
158
+ self._default_api_retries = self.scan.config.get("web", {}).get("api_retries", 2)
159
+
162
160
  self._tasks = []
163
161
  self._event_received = None
164
162
 
165
163
  # used for optional "per host" tracking
166
164
  self._per_host_tracker = set()
167
165
 
166
+ # 429 rate limit handling
167
+ self._429_sleep_interval = self.scan.web_config.get("429_sleep_interval", 30)
168
+ self._429_max_sleep_interval = self.scan.web_config.get("429_max_sleep_interval", 60)
169
+
168
170
  async def setup(self):
169
171
  """
170
172
  Performs one-time setup tasks for the module.
@@ -338,7 +340,7 @@ class BaseModule:
338
340
 
339
341
  @property
340
342
  def api_retries(self):
341
- return max(self._api_retries + 1, len(self._api_keys))
343
+ return max(self._default_api_retries + 1, len(self._api_keys))
342
344
 
343
345
  @property
344
346
  def api_failure_abort_threshold(self):
@@ -1172,6 +1174,11 @@ class BaseModule:
1172
1174
  retry_after = self._get_retry_after(r)
1173
1175
  if retry_after or status_code == 429:
1174
1176
  sleep_interval = int(retry_after) if retry_after is not None else self._429_sleep_interval
1177
+ if retry_after and retry_after > self._429_max_sleep_interval:
1178
+ self.verbose(
1179
+ f"Got an excessive retry-after header of {retry_after} from {new_url}, using {self._429_max_sleep_interval} instead"
1180
+ )
1181
+ sleep_interval = self._429_max_sleep_interval
1175
1182
  self.verbose(
1176
1183
  f"Sleeping for {sleep_interval:,} seconds due to rate limit (HTTP status: {status_code})"
1177
1184
  )
@@ -1205,7 +1212,8 @@ class BaseModule:
1205
1212
  return url, requests_kwargs
1206
1213
 
1207
1214
  def _api_response_is_success(self, r):
1208
- return r.is_success
1215
+ # 404s typically indicate no data rather than an actual error with the API, so we don't want to retry them
1216
+ return getattr(r, "is_success", False) or getattr(r, "status_code", 0) == 404
1209
1217
 
1210
1218
  async def api_page_iter(self, url, page_size=100, _json=True, next_key=None, iter_key=None, **requests_kwargs):
1211
1219
  """
@@ -15,7 +15,7 @@ class certspotter(subdomain_enum):
15
15
 
16
16
  def request_url(self, query):
17
17
  url = f"{self.base_url}/issuances?domain={self.helpers.quote(query)}&include_subdomains=true&expand=dns_names"
18
- return self.api_request(url, timeout=self.http_timeout + 30)
18
+ return self.api_request(url)
19
19
 
20
20
  async def parse_results(self, r, query):
21
21
  results = set()
@@ -1,4 +1,5 @@
1
1
  from bbot.modules.base import BaseModule
2
+ import time
2
3
 
3
4
 
4
5
  class shodan_idb(BaseModule):
@@ -46,23 +47,48 @@ class shodan_idb(BaseModule):
46
47
  "created_date": "2023-12-22",
47
48
  "author": "@TheTechromancer",
48
49
  }
50
+ options = {"retries": None}
51
+ options_desc = {
52
+ "retries": "How many times to retry API requests (e.g. after a 429 error). Overrides the global web.api_retries setting."
53
+ }
49
54
 
50
- # we get lots of 404s, that's normal
55
+ # we typically don't want to abort this module
51
56
  _api_failure_abort_threshold = 9999999999
52
57
 
53
- # there aren't any rate limits to speak of, so our outgoing queue can be pretty big
54
- _qsize = 500
58
+ # since there are rate limits, we set a lower qsize
59
+ # this way when our queue is full, we can give the API a break
60
+ _qsize = 100
55
61
 
56
62
  base_url = "https://internetdb.shodan.io"
57
63
 
64
+ async def setup(self):
65
+ await super().setup()
66
+ self.last_request_time = 0
67
+ return True
68
+
58
69
  def _incoming_dedup_hash(self, event):
59
70
  return hash(self.get_ip(event))
60
71
 
72
+ @property
73
+ def api_retries(self):
74
+ # allow the module to override global retry setting
75
+ return self.config.get("retries", None) or super().api_retries
76
+
61
77
  async def handle_event(self, event):
62
78
  ip = self.get_ip(event)
63
79
  if ip is None:
64
80
  return
65
81
  url = f"{self.base_url}/{ip}"
82
+
83
+ # Rate limiting: ensure at least 1 second between requests
84
+ current_time = time.time()
85
+ time_since_last = current_time - self.last_request_time
86
+ if time_since_last < 1:
87
+ await self.helpers.sleep(1 - time_since_last)
88
+
89
+ # Update the last request time
90
+ self.last_request_time = time.time()
91
+
66
92
  r = await self.api_request(url)
67
93
  if r is None:
68
94
  self.debug(f"No response for {event.data}")
@@ -16,10 +16,9 @@ class WebhookOutputModule(BaseOutputModule):
16
16
  # abort module after 10 failed requests (not including retries)
17
17
  _api_failure_abort_threshold = 10
18
18
  # retry each request up to 10 times, respecting the Retry-After header
19
- _api_retries = 10
19
+ _default_api_retries = 10
20
20
 
21
21
  async def setup(self):
22
- self._api_retries = self.config.get("retries", 10)
23
22
  self.webhook_url = self.config.get("webhook_url", "")
24
23
  self.min_severity = self.config.get("min_severity", "LOW").strip().upper()
25
24
  assert self.min_severity in self.vuln_severities, (
@@ -31,6 +30,10 @@ class WebhookOutputModule(BaseOutputModule):
31
30
  return False
32
31
  return await super().setup()
33
32
 
33
+ @property
34
+ def api_retries(self):
35
+ return self.config.get("retries", self._default_api_retries)
36
+
34
37
  async def handle_event(self, event):
35
38
  message = self.format_message(event)
36
39
  data = {self.content_key: message}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bbot
3
- Version: 2.4.1.6095rc0
3
+ Version: 2.4.1.6107rc0
4
4
  Summary: OSINT automation for hackers.
5
5
  License: GPL-3.0
6
6
  Keywords: python,cli,automation,osint,threat-intel,intelligence,neo4j,scanner,python-library,hacking,recursion,pentesting,recon,command-line-tool,bugbounty,subdomains,security-tools,subdomain-scanner,osint-framework,attack-surface,subdomain-enumeration,osint-tool
@@ -1,4 +1,4 @@
1
- bbot/__init__.py,sha256=IWliuFZfjWjwG33exEqeUFSip4K0QyS7Ah6FYizaqJI,163
1
+ bbot/__init__.py,sha256=sam4KorFe9w2RyVwBP9vdpFRehUOzVwRYur0Gh67BqY,163
2
2
  bbot/cli.py,sha256=1QJbANVw9Q3GFM92H2QRV2ds5756ulm08CDZwzwPpeI,11888
3
3
  bbot/core/__init__.py,sha256=l255GJE_DvUnWvrRb0J5lG-iMztJ8zVvoweDOfegGtI,46
4
4
  bbot/core/config/__init__.py,sha256=zYNw2Me6tsEr8hOOkLb4BQ97GB7Kis2k--G81S8vofU,342
@@ -48,7 +48,7 @@ bbot/core/modules.py,sha256=U0Z2UoZAOPG9lLvR9Juc3UwdWCc_xbktF4t_NoiKPrY,31385
48
48
  bbot/core/multiprocess.py,sha256=ocQHanskJ09gHwe7RZmwNdZyCOQyeyUoIHCtLbtvXUk,1771
49
49
  bbot/core/shared_deps.py,sha256=mCMZeKSt46trzVqQDPGfXfEWg0Zw5YjiJx4BnsIRgHM,7640
50
50
  bbot/db/sql/models.py,sha256=SrUdDOBCICzXJBY29p0VvILhMQ1JCuh725bqvIYogX0,4884
51
- bbot/defaults.yml,sha256=XPaGfTKWFjKV-lLIJy-Qs7X0d-8X8EYM0QELEu8KjBw,6670
51
+ bbot/defaults.yml,sha256=wIiOAEvsnJi_Q7qXC1Pn1TCSWiSh8CagpwVvVIGefLs,7106
52
52
  bbot/errors.py,sha256=xwQcD26nU9oc7-o0kv5jmEDTInmi8_W8eKAgQZZxdVM,953
53
53
  bbot/logger.py,sha256=wE-532v5FyKuSSoTdyW1xSfaOnLZB1axAJnB-uW2xrI,2745
54
54
  bbot/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -61,7 +61,7 @@ bbot/modules/baddns.py,sha256=ubO3KDfcIMJnMjyZX5FWZ4GWxLSekV_JQV7QvsPjtD0,6693
61
61
  bbot/modules/baddns_direct.py,sha256=hWThpkXP87nnCRTlUh5qBJ1t4eo4l9kUmKNNxVNJI8A,3819
62
62
  bbot/modules/baddns_zone.py,sha256=y1XaBUfFPnRbR2qaTqRyUsPgEL73722v2B8aS5YoGN4,1035
63
63
  bbot/modules/badsecrets.py,sha256=LG37p48Rlxsfc3BmACMpkypsbuFTVvXqNhlP1IEsx0k,5109
64
- bbot/modules/base.py,sha256=SCIeaLX1d5VMUl2nC3IyqPdzBF8mr_7__JB3855q5cc,74529
64
+ bbot/modules/base.py,sha256=_RFTPNZ6X7mD7jBOTYZmjgzTSKWT9U_eYxL4gXIh8TE,75265
65
65
  bbot/modules/bevigil.py,sha256=0VLIxmeXRUI2-EoR6IzuHJMcX8KCHNNta-WYa3gVlDg,2862
66
66
  bbot/modules/binaryedge.py,sha256=5F9LnZwRM_rZnzTv29hLJLI2GEQdzOwSpahPFC1kJC0,1397
67
67
  bbot/modules/bucket_amazon.py,sha256=mwjYeEAcdfOpjbOa1sD8U9KBMMVY_c8FoHjSGR9GQbg,730
@@ -75,7 +75,7 @@ bbot/modules/builtwith.py,sha256=6ZQOc6vmSVc8LsdgsiuMWfDquGm5K0jxwsnL8MsKNWw,538
75
75
  bbot/modules/bypass403.py,sha256=HyONgOYlZUET61FZ0QWE7zPGG-N6n0x_j9KUGw8kVxQ,6855
76
76
  bbot/modules/c99.py,sha256=yHT9-eyqRODISV5eTi11uh-xwqX0JG7zey5AgcDYCdI,1448
77
77
  bbot/modules/censys.py,sha256=J7NhPnSeoCuG_9FkLjFBIg-tqHAB21HjvQUw_6OQNZo,3311
78
- bbot/modules/certspotter.py,sha256=AtL5BiOuDp4vu1-5fct4aQAGZM2qiODYsbgBsw0phoU,937
78
+ bbot/modules/certspotter.py,sha256=qdRGCkGyP07_cP9h2o_AEZwoiQPXtrC-Bel3vgh24x8,905
79
79
  bbot/modules/chaos.py,sha256=JyuwytwE3IRmNbw-uyJ0gCaTnywhhsHzTiZ3OJ15PAw,1573
80
80
  bbot/modules/code_repository.py,sha256=x70Z45VnNNMF8BPkHfGWZXsZXw_fStGB3y0-8jbP1Ns,2078
81
81
  bbot/modules/credshed.py,sha256=HAF5wgRGKIIpdMAe4mIAtkZRLmFYjMFyXtjjst6RJ20,4203
@@ -177,7 +177,7 @@ bbot/modules/robots.py,sha256=LGG6ixsxrlaCk-mi4Lp6kB2RB1v-25NhTAQxdQEtH8s,2172
177
177
  bbot/modules/securitytrails.py,sha256=5Jk_HTQP8FRq6A30sN19FU79uLJt7aiOsI2dxNkLDcM,1148
178
178
  bbot/modules/securitytxt.py,sha256=nwaTOnRAl2NWcEc3i_I9agB56QjqK8dHqiKRHPPkCPE,4558
179
179
  bbot/modules/shodan_dns.py,sha256=ETOyUhCiAETlGUAQhvAP47oEEPYss7fm_F_CAeCQyoI,842
180
- bbot/modules/shodan_idb.py,sha256=kyPbGI68BrLgCab_T5emKXBoyD2OVn59K0J1V6CNR4s,5213
180
+ bbot/modules/shodan_idb.py,sha256=PB5vplwpjPjbfoMeez333rWYqFY5RuMp40ZN9hNCJAg,6088
181
181
  bbot/modules/sitedossier.py,sha256=MR9fSkgE_3YGsUe7M-TyJ2GdpBtF3oLKDl9agAwzw5U,2284
182
182
  bbot/modules/skymem.py,sha256=ZrxWcePFTCiDkFeAc3YLegFG-Tgw4C9af_JHiVonk84,1930
183
183
  bbot/modules/smuggler.py,sha256=v8NCRgzd7wpEFZJUTAArG04bN8nNTGiHxYpGBapzi14,1580
@@ -192,7 +192,7 @@ bbot/modules/templates/postman.py,sha256=MIpz2q_r6LP0kIEgByo7oX5qHhMZLOhr7oKzJI9
192
192
  bbot/modules/templates/shodan.py,sha256=MXBvlmfw3jZFqT47v10UkqMSyQR-zBIxMJmK7PWw6uw,1174
193
193
  bbot/modules/templates/sql.py,sha256=o-CdyyoJvHJdJBKkj3CIGXYxUta4w2AB_2Vr-k7cDDU,3553
194
194
  bbot/modules/templates/subdomain_enum.py,sha256=epyKSly08jqaINV_AMMWbNafIeQjJqvd3aj63KD0Mck,8402
195
- bbot/modules/templates/webhook.py,sha256=0N7LorCxT0sVmT2XgrV81ubdcdPVW8s2X__BrKC2AcM,3313
195
+ bbot/modules/templates/webhook.py,sha256=uGFmcJ81GzGN1UI2k2O7nQF_fyh4ehLDEg2NSXaPnhk,3373
196
196
  bbot/modules/trickest.py,sha256=MRgLW0YiDWzlWdAjyqfPPLFb-a51r-Ffn_dphiJI_gA,1550
197
197
  bbot/modules/trufflehog.py,sha256=KEObs5z0ce0Ck7gA3t3l66MJKUo6P_9TIfVOwFO28t4,8743
198
198
  bbot/modules/url_manipulation.py,sha256=4J3oFkqTSJPPmbKEKAHJg2Q2w4QNKtQhiN03ZJq5VtI,4326
@@ -428,8 +428,8 @@ bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt,sha256=ZSIVebs7ptMvHx
428
428
  bbot/wordlists/top_open_ports_nmap.txt,sha256=LmdFYkfapSxn1pVuQC2LkOIY2hMLgG-Xts7DVtYzweM,42727
429
429
  bbot/wordlists/valid_url_schemes.txt,sha256=0B_VAr9Dv7aYhwi6JSBDU-3M76vNtzN0qEC_RNLo7HE,3310
430
430
  bbot/wordlists/wordninja_dns.txt.gz,sha256=DYHvvfW0TvzrVwyprqODAk4tGOxv5ezNmCPSdPuDUnQ,570241
431
- bbot-2.4.1.6095rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
432
- bbot-2.4.1.6095rc0.dist-info/METADATA,sha256=s1SYf_crJYiFaXoX6ZM7VtItSyo5A6n2TY5XOp68Mdc,18218
433
- bbot-2.4.1.6095rc0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
434
- bbot-2.4.1.6095rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
435
- bbot-2.4.1.6095rc0.dist-info/RECORD,,
431
+ bbot-2.4.1.6107rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
432
+ bbot-2.4.1.6107rc0.dist-info/METADATA,sha256=Tbmi58DKjLEf9JRM2BKNULln0TwoEqeiPdGOMn3kQdU,18218
433
+ bbot-2.4.1.6107rc0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
434
+ bbot-2.4.1.6107rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
435
+ bbot-2.4.1.6107rc0.dist-info/RECORD,,