bbot 2.5.0.6790rc0__py3-none-any.whl → 2.5.0.6803rc0__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 +1 -1
- bbot/modules/graphql_introspection.py +142 -0
- bbot/modules/trufflehog.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_graphql_introspection.py +34 -0
- {bbot-2.5.0.6790rc0.dist-info → bbot-2.5.0.6803rc0.dist-info}/METADATA +1 -1
- {bbot-2.5.0.6790rc0.dist-info → bbot-2.5.0.6803rc0.dist-info}/RECORD +9 -7
- {bbot-2.5.0.6790rc0.dist-info → bbot-2.5.0.6803rc0.dist-info}/LICENSE +0 -0
- {bbot-2.5.0.6790rc0.dist-info → bbot-2.5.0.6803rc0.dist-info}/WHEEL +0 -0
- {bbot-2.5.0.6790rc0.dist-info → bbot-2.5.0.6803rc0.dist-info}/entry_points.txt +0 -0
bbot/__init__.py
CHANGED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from bbot.modules.base import BaseModule
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class graphql_introspection(BaseModule):
|
|
7
|
+
watched_events = ["URL"]
|
|
8
|
+
produced_events = ["FINDING"]
|
|
9
|
+
flags = ["safe", "active", "web-basic"]
|
|
10
|
+
meta = {
|
|
11
|
+
"description": "Perform GraphQL introspection on a target",
|
|
12
|
+
"created_date": "2025-07-01",
|
|
13
|
+
"author": "@mukesh-dream11",
|
|
14
|
+
}
|
|
15
|
+
options = {
|
|
16
|
+
"graphql_endpoint_urls": ["/", "/graphql", "/v1/graphql"],
|
|
17
|
+
"output_folder": "",
|
|
18
|
+
}
|
|
19
|
+
options_desc = {
|
|
20
|
+
"graphql_endpoint_urls": "List of GraphQL endpoint to suffix to the target URL",
|
|
21
|
+
"output_folder": "Folder to save the GraphQL schemas to",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async def setup(self):
|
|
25
|
+
output_folder = self.config.get("output_folder", "")
|
|
26
|
+
if output_folder:
|
|
27
|
+
self.output_dir = Path(output_folder) / "graphql-schemas"
|
|
28
|
+
else:
|
|
29
|
+
self.output_dir = self.scan.home / "graphql-schemas"
|
|
30
|
+
self.helpers.mkdir(self.output_dir)
|
|
31
|
+
return True
|
|
32
|
+
|
|
33
|
+
async def filter_event(self, event):
|
|
34
|
+
# Dedup by the base URL
|
|
35
|
+
base_url = event.parsed_url._replace(path="/", query="", fragment="").geturl()
|
|
36
|
+
return hash(base_url)
|
|
37
|
+
|
|
38
|
+
async def handle_event(self, event):
|
|
39
|
+
base_url = event.parsed_url._replace(path="/", query="", fragment="").geturl().rstrip("/")
|
|
40
|
+
for endpoint_url in self.config.get("graphql_endpoint_urls", []):
|
|
41
|
+
url = f"{base_url}{endpoint_url}"
|
|
42
|
+
request_args = {
|
|
43
|
+
"url": url,
|
|
44
|
+
"method": "POST",
|
|
45
|
+
"json": {
|
|
46
|
+
"query": """\
|
|
47
|
+
query IntrospectionQuery {
|
|
48
|
+
__schema {
|
|
49
|
+
queryType {
|
|
50
|
+
name
|
|
51
|
+
}
|
|
52
|
+
mutationType {
|
|
53
|
+
name
|
|
54
|
+
}
|
|
55
|
+
types {
|
|
56
|
+
name
|
|
57
|
+
kind
|
|
58
|
+
description
|
|
59
|
+
fields(includeDeprecated: true) {
|
|
60
|
+
name
|
|
61
|
+
description
|
|
62
|
+
type {
|
|
63
|
+
... TypeRef
|
|
64
|
+
}
|
|
65
|
+
isDeprecated
|
|
66
|
+
deprecationReason
|
|
67
|
+
}
|
|
68
|
+
interfaces {
|
|
69
|
+
... TypeRef
|
|
70
|
+
}
|
|
71
|
+
possibleTypes {
|
|
72
|
+
... TypeRef
|
|
73
|
+
}
|
|
74
|
+
enumValues(includeDeprecated: true) {
|
|
75
|
+
name
|
|
76
|
+
description
|
|
77
|
+
isDeprecated
|
|
78
|
+
deprecationReason
|
|
79
|
+
}
|
|
80
|
+
ofType {
|
|
81
|
+
... TypeRef
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fragment TypeRef on __Type {
|
|
88
|
+
kind
|
|
89
|
+
name
|
|
90
|
+
ofType {
|
|
91
|
+
kind
|
|
92
|
+
name
|
|
93
|
+
ofType {
|
|
94
|
+
kind
|
|
95
|
+
name
|
|
96
|
+
ofType {
|
|
97
|
+
kind
|
|
98
|
+
name
|
|
99
|
+
ofType {
|
|
100
|
+
kind
|
|
101
|
+
name
|
|
102
|
+
ofType {
|
|
103
|
+
kind
|
|
104
|
+
name
|
|
105
|
+
ofType {
|
|
106
|
+
kind
|
|
107
|
+
name
|
|
108
|
+
ofType {
|
|
109
|
+
kind
|
|
110
|
+
name
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}"""
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
response = await self.helpers.request(**request_args)
|
|
122
|
+
if not response or response.status_code != 200:
|
|
123
|
+
self.debug(f"Failed to get GraphQL schema for {url} (status code {response.status_code})")
|
|
124
|
+
continue
|
|
125
|
+
try:
|
|
126
|
+
response_json = response.json()
|
|
127
|
+
except json.JSONDecodeError:
|
|
128
|
+
self.debug(f"Failed to parse JSON for {url}")
|
|
129
|
+
continue
|
|
130
|
+
if response_json.get("data", {}).get("__schema", {}).get("types", []):
|
|
131
|
+
filename = f"schema-{self.helpers.tagify(url)}.json"
|
|
132
|
+
filename = self.output_dir / filename
|
|
133
|
+
with open(filename, "w") as f:
|
|
134
|
+
json.dump(response_json, f)
|
|
135
|
+
await self.emit_event(
|
|
136
|
+
{"url": url, "description": "GraphQL schema", "path": str(filename.relative_to(self.scan.home))},
|
|
137
|
+
"FINDING",
|
|
138
|
+
event,
|
|
139
|
+
context=f"{{module}} found GraphQL schema at {url}",
|
|
140
|
+
)
|
|
141
|
+
# return, because we only want to find one schema per target
|
|
142
|
+
return
|
bbot/modules/trufflehog.py
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from .base import ModuleTestBase
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TestGraphQLIntrospectionNon200(ModuleTestBase):
|
|
5
|
+
targets = ["http://127.0.0.1:8888"]
|
|
6
|
+
modules_overrides = ["graphql_introspection"]
|
|
7
|
+
|
|
8
|
+
async def setup_after_prep(self, module_test):
|
|
9
|
+
module_test.set_expect_requests(
|
|
10
|
+
expect_args={"method": "POST", "uri": "/"},
|
|
11
|
+
respond_args={"response_data": "ok"},
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
def check(self, module_test, events):
|
|
15
|
+
assert all(e.type != "FINDING" for e in events), "should have raised 0 events"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestGraphQLIntrospection(ModuleTestBase):
|
|
19
|
+
targets = ["http://127.0.0.1:8888"]
|
|
20
|
+
modules_overrides = ["graphql_introspection"]
|
|
21
|
+
|
|
22
|
+
async def setup_after_prep(self, module_test):
|
|
23
|
+
module_test.set_expect_requests(
|
|
24
|
+
expect_args={"method": "POST", "uri": "/"},
|
|
25
|
+
respond_args={
|
|
26
|
+
"response_data": """{"data": {"__schema": {"types": ["dummy"]}}}""",
|
|
27
|
+
},
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def check(self, module_test, events):
|
|
31
|
+
finding = [e for e in events if e.type == "FINDING"]
|
|
32
|
+
assert finding, "should have raised 1 FINDING event"
|
|
33
|
+
assert finding[0].data["url"] == "http://127.0.0.1:8888/"
|
|
34
|
+
assert finding[0].data["description"] == "GraphQL schema"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: bbot
|
|
3
|
-
Version: 2.5.0.
|
|
3
|
+
Version: 2.5.0.6803rc0
|
|
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=
|
|
1
|
+
bbot/__init__.py,sha256=psvZSLfHy5t3AIe_f5WcJGV67hsBRzebgolsC2TQ89Y,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
|
|
@@ -114,6 +114,7 @@ bbot/modules/github_workflows.py,sha256=xKntAFDeGuE4MqbEmhJyYXKbzoSh9tWYlHNlnF37
|
|
|
114
114
|
bbot/modules/gitlab.py,sha256=9oWWpBijeHCjuFBfWW4HvNqt7bvJvrBgBjaaz_UPPnE,5964
|
|
115
115
|
bbot/modules/google_playstore.py,sha256=N4QjzQag_bgDXfX17rytBiiWA-SQtYI2N0J_ZNEOdv0,3701
|
|
116
116
|
bbot/modules/gowitness.py,sha256=o4nIBbHVX9tYVW9CpwTsaftWDZu41nhxS0KEp4FoiBs,13003
|
|
117
|
+
bbot/modules/graphql_introspection.py,sha256=-BAzNhBegup2sIYQdJ0jcafZFTGTZl3WoMygilyvfVA,4144
|
|
117
118
|
bbot/modules/hackertarget.py,sha256=IsKs9PtxUHdLJKZydlRdW_loBE2KphQYi3lKDAd4odc,1029
|
|
118
119
|
bbot/modules/host_header.py,sha256=uDjwidMdeNPMRfzQ2YW4REEGsZqnGOZHbOS6GgdNd9s,7686
|
|
119
120
|
bbot/modules/httpx.py,sha256=z0sgnfLqIXyiadM0rKQK2p86lka7rC4pGCfTVUqU5Lk,8118
|
|
@@ -207,7 +208,7 @@ bbot/modules/templates/sql.py,sha256=o-CdyyoJvHJdJBKkj3CIGXYxUta4w2AB_2Vr-k7cDDU
|
|
|
207
208
|
bbot/modules/templates/subdomain_enum.py,sha256=epyKSly08jqaINV_AMMWbNafIeQjJqvd3aj63KD0Mck,8402
|
|
208
209
|
bbot/modules/templates/webhook.py,sha256=uGFmcJ81GzGN1UI2k2O7nQF_fyh4ehLDEg2NSXaPnhk,3373
|
|
209
210
|
bbot/modules/trickest.py,sha256=MRgLW0YiDWzlWdAjyqfPPLFb-a51r-Ffn_dphiJI_gA,1550
|
|
210
|
-
bbot/modules/trufflehog.py,sha256=
|
|
211
|
+
bbot/modules/trufflehog.py,sha256=CqF9UAEzi0JzJKitXnBWFAVypCVuKKXWPW14Txw6GMs,8717
|
|
211
212
|
bbot/modules/url_manipulation.py,sha256=4J3oFkqTSJPPmbKEKAHJg2Q2w4QNKtQhiN03ZJq5VtI,4326
|
|
212
213
|
bbot/modules/urlscan.py,sha256=-w_3Bm6smyG2GLQyIbnMUkKmeQVauo-V6F4_kJDYG7s,3740
|
|
213
214
|
bbot/modules/vhost.py,sha256=cirOe0HR4M0TEBN8JdXo2l0s2flc8ZSdxggGm79blT8,5459
|
|
@@ -364,6 +365,7 @@ bbot/test/test_step_2/module_tests/test_module_github_workflows.py,sha256=o_teEa
|
|
|
364
365
|
bbot/test/test_step_2/module_tests/test_module_gitlab.py,sha256=fnwE7BWTU6EQquKdGLCiaX_LwVwvzOLev3Y9GheTLSY,11859
|
|
365
366
|
bbot/test/test_step_2/module_tests/test_module_google_playstore.py,sha256=uTRqpAGI9HI-rOk_6jdV44OoSqi0QQQ3aTVzvuV0dtc,3034
|
|
366
367
|
bbot/test/test_step_2/module_tests/test_module_gowitness.py,sha256=EH3NIMDA3XgZz1yffu-PnRCrlZJODakGPfzgnU7Ls_s,6501
|
|
368
|
+
bbot/test/test_step_2/module_tests/test_module_graphql_introspection.py,sha256=qac8DJ_exe6Ra4UgRvVMSdgBhLIZP9lmXyKhi9RPOK8,1241
|
|
367
369
|
bbot/test/test_step_2/module_tests/test_module_hackertarget.py,sha256=ldhNKxGk5fwq87zVptQDyfQ-cn3FzbWvpadKEO3h4ic,609
|
|
368
370
|
bbot/test/test_step_2/module_tests/test_module_host_header.py,sha256=w1x0MyKNiUol4hlw7CijhMwEMEL5aBddbZZjOcEgv_k,2672
|
|
369
371
|
bbot/test/test_step_2/module_tests/test_module_http.py,sha256=KhsQvqpVa6zmMa79jV4liv_NAv25wrSaO6h_x0AA12c,2127
|
|
@@ -451,8 +453,8 @@ bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt,sha256=ZSIVebs7ptMvHx
|
|
|
451
453
|
bbot/wordlists/top_open_ports_nmap.txt,sha256=LmdFYkfapSxn1pVuQC2LkOIY2hMLgG-Xts7DVtYzweM,42727
|
|
452
454
|
bbot/wordlists/valid_url_schemes.txt,sha256=0B_VAr9Dv7aYhwi6JSBDU-3M76vNtzN0qEC_RNLo7HE,3310
|
|
453
455
|
bbot/wordlists/wordninja_dns.txt.gz,sha256=DYHvvfW0TvzrVwyprqODAk4tGOxv5ezNmCPSdPuDUnQ,570241
|
|
454
|
-
bbot-2.5.0.
|
|
455
|
-
bbot-2.5.0.
|
|
456
|
-
bbot-2.5.0.
|
|
457
|
-
bbot-2.5.0.
|
|
458
|
-
bbot-2.5.0.
|
|
456
|
+
bbot-2.5.0.6803rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
|
|
457
|
+
bbot-2.5.0.6803rc0.dist-info/METADATA,sha256=RafoLsvxll-6fDNfVENz3dKWCZBy4nwrtx3qVZfkjw0,18308
|
|
458
|
+
bbot-2.5.0.6803rc0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
459
|
+
bbot-2.5.0.6803rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
|
|
460
|
+
bbot-2.5.0.6803rc0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|