endpointscanner 7.2.2__tar.gz → 7.2.4__tar.gz
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.
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/PKG-INFO +13 -3
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/README.md +11 -2
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/endpointscanner.egg-info/PKG-INFO +13 -3
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/endpointscanner.egg-info/requires.txt +1 -0
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/enumerateendpoint.py +86 -25
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/pyproject.toml +3 -2
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/LICENSE +0 -0
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/endpointscanner.egg-info/SOURCES.txt +0 -0
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/endpointscanner.egg-info/dependency_links.txt +0 -0
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/endpointscanner.egg-info/entry_points.txt +0 -0
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/endpointscanner.egg-info/top_level.txt +0 -0
- {endpointscanner-7.2.2 → endpointscanner-7.2.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: endpointscanner
|
|
3
|
-
Version: 7.2.
|
|
3
|
+
Version: 7.2.4
|
|
4
4
|
Summary: Website endpoint reconnaissance tool and rate limit tester that can bypass simple captchas and WAFs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/SphericalFlower52811/endpointscanner
|
|
6
6
|
Project-URL: Issues, https://github.com/SphericalFlower52811/endpointscanner/issues
|
|
@@ -14,9 +14,10 @@ Requires-Dist: beautifulsoup4
|
|
|
14
14
|
Requires-Dist: playwright
|
|
15
15
|
Requires-Dist: playwright-stealth
|
|
16
16
|
Requires-Dist: httpx[http2]>=0.27.0
|
|
17
|
+
Requires-Dist: colorama>=0.4.6
|
|
17
18
|
Dynamic: license-file
|
|
18
19
|
|
|
19
|
-
# Website Endpoint Scanner and Rate Limit Tester For Websites (Version 7.2.
|
|
20
|
+
# Website Endpoint Scanner and Rate Limit Tester For Websites (Version 7.2.4)
|
|
20
21
|
|
|
21
22
|
A fast automated website reconnaissance tool that extracts endpoints, files, and even external links from websites. Automates IDOR and broken access control vulnerability testing through replacing variables with 1 in endpoints. Has a built in rate limit tester that can test on any endpoint, and can bypass simple WAFs/captchas and client-side SPAs.
|
|
22
23
|
|
|
@@ -189,11 +190,20 @@ Version 7.2.1 (patch update) added:
|
|
|
189
190
|
|
|
190
191
|
Version 7.2.2 just changed wording and description of the tool to be more clear.
|
|
191
192
|
|
|
193
|
+
Version 7.2.3 added one more sensitive endpoint and fixed bug where some paths would be / from extra files.
|
|
194
|
+
|
|
195
|
+
Version 7.2.4 added:
|
|
196
|
+
|
|
197
|
+
- fixing pdf keys like /Btn and /Widget used in jsPDF, as they were previously detected as endpoints
|
|
198
|
+
- better SPA separation
|
|
199
|
+
- added more asset formats (.avif, .mp3, .mp4, .webm, .wav)
|
|
200
|
+
- Added text at start saying Endpointscanner 7.2.4 (i wanted it to have the vibe of most open source tools but the 3d text was just too much)
|
|
201
|
+
|
|
192
202
|
## Plans for next version and the future
|
|
193
203
|
|
|
194
204
|
Version 7.3 is planned to have:
|
|
195
205
|
|
|
196
|
-
- More JS Stacks to detect
|
|
206
|
+
- More JS Stacks to detect (and more accurate)
|
|
197
207
|
- Detecting what type of captcha was used if the script is blocked.
|
|
198
208
|
|
|
199
209
|
Future plans (May be added in the next version):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Website Endpoint Scanner and Rate Limit Tester For Websites (Version 7.2.
|
|
1
|
+
# Website Endpoint Scanner and Rate Limit Tester For Websites (Version 7.2.4)
|
|
2
2
|
|
|
3
3
|
A fast automated website reconnaissance tool that extracts endpoints, files, and even external links from websites. Automates IDOR and broken access control vulnerability testing through replacing variables with 1 in endpoints. Has a built in rate limit tester that can test on any endpoint, and can bypass simple WAFs/captchas and client-side SPAs.
|
|
4
4
|
|
|
@@ -171,11 +171,20 @@ Version 7.2.1 (patch update) added:
|
|
|
171
171
|
|
|
172
172
|
Version 7.2.2 just changed wording and description of the tool to be more clear.
|
|
173
173
|
|
|
174
|
+
Version 7.2.3 added one more sensitive endpoint and fixed bug where some paths would be / from extra files.
|
|
175
|
+
|
|
176
|
+
Version 7.2.4 added:
|
|
177
|
+
|
|
178
|
+
- fixing pdf keys like /Btn and /Widget used in jsPDF, as they were previously detected as endpoints
|
|
179
|
+
- better SPA separation
|
|
180
|
+
- added more asset formats (.avif, .mp3, .mp4, .webm, .wav)
|
|
181
|
+
- Added text at start saying Endpointscanner 7.2.4 (i wanted it to have the vibe of most open source tools but the 3d text was just too much)
|
|
182
|
+
|
|
174
183
|
## Plans for next version and the future
|
|
175
184
|
|
|
176
185
|
Version 7.3 is planned to have:
|
|
177
186
|
|
|
178
|
-
- More JS Stacks to detect
|
|
187
|
+
- More JS Stacks to detect (and more accurate)
|
|
179
188
|
- Detecting what type of captcha was used if the script is blocked.
|
|
180
189
|
|
|
181
190
|
Future plans (May be added in the next version):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: endpointscanner
|
|
3
|
-
Version: 7.2.
|
|
3
|
+
Version: 7.2.4
|
|
4
4
|
Summary: Website endpoint reconnaissance tool and rate limit tester that can bypass simple captchas and WAFs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/SphericalFlower52811/endpointscanner
|
|
6
6
|
Project-URL: Issues, https://github.com/SphericalFlower52811/endpointscanner/issues
|
|
@@ -14,9 +14,10 @@ Requires-Dist: beautifulsoup4
|
|
|
14
14
|
Requires-Dist: playwright
|
|
15
15
|
Requires-Dist: playwright-stealth
|
|
16
16
|
Requires-Dist: httpx[http2]>=0.27.0
|
|
17
|
+
Requires-Dist: colorama>=0.4.6
|
|
17
18
|
Dynamic: license-file
|
|
18
19
|
|
|
19
|
-
# Website Endpoint Scanner and Rate Limit Tester For Websites (Version 7.2.
|
|
20
|
+
# Website Endpoint Scanner and Rate Limit Tester For Websites (Version 7.2.4)
|
|
20
21
|
|
|
21
22
|
A fast automated website reconnaissance tool that extracts endpoints, files, and even external links from websites. Automates IDOR and broken access control vulnerability testing through replacing variables with 1 in endpoints. Has a built in rate limit tester that can test on any endpoint, and can bypass simple WAFs/captchas and client-side SPAs.
|
|
22
23
|
|
|
@@ -189,11 +190,20 @@ Version 7.2.1 (patch update) added:
|
|
|
189
190
|
|
|
190
191
|
Version 7.2.2 just changed wording and description of the tool to be more clear.
|
|
191
192
|
|
|
193
|
+
Version 7.2.3 added one more sensitive endpoint and fixed bug where some paths would be / from extra files.
|
|
194
|
+
|
|
195
|
+
Version 7.2.4 added:
|
|
196
|
+
|
|
197
|
+
- fixing pdf keys like /Btn and /Widget used in jsPDF, as they were previously detected as endpoints
|
|
198
|
+
- better SPA separation
|
|
199
|
+
- added more asset formats (.avif, .mp3, .mp4, .webm, .wav)
|
|
200
|
+
- Added text at start saying Endpointscanner 7.2.4 (i wanted it to have the vibe of most open source tools but the 3d text was just too much)
|
|
201
|
+
|
|
192
202
|
## Plans for next version and the future
|
|
193
203
|
|
|
194
204
|
Version 7.3 is planned to have:
|
|
195
205
|
|
|
196
|
-
- More JS Stacks to detect
|
|
206
|
+
- More JS Stacks to detect (and more accurate)
|
|
197
207
|
- Detecting what type of captcha was used if the script is blocked.
|
|
198
208
|
|
|
199
209
|
Future plans (May be added in the next version):
|
|
@@ -8,6 +8,7 @@ import time
|
|
|
8
8
|
import json
|
|
9
9
|
from playwright.sync_api import sync_playwright #headless browser to solve captcha
|
|
10
10
|
from playwright_stealth import Stealth #ensure strict firewalls do not block the playwright browser
|
|
11
|
+
from colorama import Fore, Style, init
|
|
11
12
|
|
|
12
13
|
HEADER = {
|
|
13
14
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
@@ -18,6 +19,21 @@ USELESSSTUFF = {
|
|
|
18
19
|
"localhost", "127.0.0.1", "0.0.0.0",
|
|
19
20
|
"w3.org", "schema.org", "xml.org", "://microsoft.com", "/../"
|
|
20
21
|
}
|
|
22
|
+
#jspdf makes all those for no reason
|
|
23
|
+
JSPDF_SIGNATURE_KEYS = {
|
|
24
|
+
"/ASCII85Decode", "/ASCII85Encode", "/ASCIIHexDecode", "/ASCIIHexEncode",
|
|
25
|
+
"/Annot", "/Btn", "/CIDSystemInfo", "/Ch", "/FlateDecode", "/FlateEncode",
|
|
26
|
+
"/Form", "/I", "/Image", "/Outlines", "/Pattern", "/Sig", "/Tx", "/Widget", "/XObject"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
S_HEADER = {
|
|
30
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...",
|
|
31
|
+
"Cache-Control": "no-store",
|
|
32
|
+
"Pragma": "no-cache",
|
|
33
|
+
"If-None-Match": "",
|
|
34
|
+
"If-Modified-Since": ""
|
|
35
|
+
}
|
|
36
|
+
|
|
21
37
|
unsorted_paths = []
|
|
22
38
|
e_files = []
|
|
23
39
|
start_test_time = None
|
|
@@ -225,9 +241,7 @@ async def async_rate_test(url, num_reqs=100, method="GET", rb=None, rv=None, coo
|
|
|
225
241
|
print("[+] Streaming requests into the background network pipeline...")
|
|
226
242
|
await asyncio.sleep(45.0)
|
|
227
243
|
|
|
228
|
-
#
|
|
229
|
-
#Fancy label below
|
|
230
|
-
#----AFTER NETWORK IS GONE----
|
|
244
|
+
#label every single request using enumerate() to find out exactly when the first request timed out, or hit a non-200.
|
|
231
245
|
status_counts = {}
|
|
232
246
|
first_limit_at = None
|
|
233
247
|
|
|
@@ -349,6 +363,13 @@ def main():
|
|
|
349
363
|
parser.add_argument("-ro", "--raw-output", action="store_true", help="Do not sort out endpoints after finding them. Will leave out sensitive endpoints whether they are exposed or not.")
|
|
350
364
|
parser.add_argument("-rh", "--ratelimit-header", type=str, default=None, help="Custom headers. Must be seperated by a pipe(|), or newlines. Example use: Cookies: {ExampleCookie: example} | Accept: application/json, text/plain, */*. If the custom header contains double quotes, please use single quotes instead of double quotes to pass this flag.")
|
|
351
365
|
args = parser.parse_args()
|
|
366
|
+
if not args.only_res:
|
|
367
|
+
init(autoreset=True)
|
|
368
|
+
print()
|
|
369
|
+
print("-" * 40)
|
|
370
|
+
print(f"{Style.BRIGHT}Endpointscanner {Fore.LIGHTMAGENTA_EX}v7.2.4")
|
|
371
|
+
print("-" * 40)
|
|
372
|
+
print()
|
|
352
373
|
#ratelimit type always defaults to get
|
|
353
374
|
if (args.ratelimit_type != 'GET' or args.ratelimit_var or args.ratelimit_body or args.ratelimit_header) and args.ratelimit is None:
|
|
354
375
|
passedrateargs = []
|
|
@@ -404,7 +425,7 @@ def main():
|
|
|
404
425
|
exit(1)
|
|
405
426
|
|
|
406
427
|
if args.ratelimit_body and args.ratelimit_var:
|
|
407
|
-
# bracket escaping
|
|
428
|
+
# bracket escaping, so if someone puts {{X}} or X, it will end up as {X}.
|
|
408
429
|
expected_bracket_token = f"{{{args.ratelimit_var.strip('{}')}}}"
|
|
409
430
|
|
|
410
431
|
if expected_bracket_token not in args.ratelimit_body:
|
|
@@ -473,7 +494,7 @@ def main():
|
|
|
473
494
|
start_test_time = time.perf_counter()
|
|
474
495
|
#hardcoded dangerous endpoints to test
|
|
475
496
|
SENSITIVE_ENDPOINT = {
|
|
476
|
-
"/.env", "/.env.local", "/.env.production", "/.env.development",
|
|
497
|
+
"/.env", "/.env.local", "/.env.production", "/.env.development", ".env.dev",
|
|
477
498
|
"/.git/config", "/.git/HEAD", "/package.json", "/package-lock.json", "/.npmrc", "/.dockerenv",
|
|
478
499
|
"/.gitignore", "/api/health", "/admin", "/login", "/config",
|
|
479
500
|
"/.env.example", "/docker-compose.yml", "/.babelrc", "/.eslintrc.json",
|
|
@@ -504,6 +525,8 @@ def main():
|
|
|
504
525
|
for rule in rules:
|
|
505
526
|
clean_rule = rule.strip()
|
|
506
527
|
if clean_rule and clean_rule not in ["/", "/*"] and clean_rule not in found_paths:
|
|
528
|
+
if clean_rule.strip() in ["/", "/*"]:
|
|
529
|
+
continue
|
|
507
530
|
found_paths.add(clean_rule)
|
|
508
531
|
results_fromotherfiles.append(f"{clean_rule} [Source: robots.txt]")
|
|
509
532
|
if args.show_prog:
|
|
@@ -519,6 +542,8 @@ def main():
|
|
|
519
542
|
for loc in locs:
|
|
520
543
|
clean_path = loc.strip()
|
|
521
544
|
if clean_path and clean_path != "/" and clean_path not in found_paths:
|
|
545
|
+
if clean_path.strip() == "/":
|
|
546
|
+
continue
|
|
522
547
|
found_paths.add(clean_path)
|
|
523
548
|
if not args.tidy:
|
|
524
549
|
results_fromotherfiles.append(f"{clean_path} [Source: sitemap.xml]")
|
|
@@ -536,6 +561,8 @@ def main():
|
|
|
536
561
|
paths = re.findall(r'["\'](/[a-zA-Z0-9_\-\./]+)["\']', m_res.text)
|
|
537
562
|
for path in paths:
|
|
538
563
|
if path not in found_paths:
|
|
564
|
+
if path.strip() == "/":
|
|
565
|
+
continue
|
|
539
566
|
found_paths.add(path)
|
|
540
567
|
if not args.tidy:
|
|
541
568
|
results_fromotherfiles.append(f"{path} [Source: asset-manifest.json]")
|
|
@@ -553,6 +580,8 @@ def main():
|
|
|
553
580
|
paths = re.findall(r'["\'](/[a-zA-Z0-9_\-\./]+)["\']', m_res.text)
|
|
554
581
|
for path in paths:
|
|
555
582
|
if path not in found_paths:
|
|
583
|
+
if path.strip() == "/":
|
|
584
|
+
continue
|
|
556
585
|
found_paths.add(path)
|
|
557
586
|
if not args.tidy:
|
|
558
587
|
results_fromotherfiles.append(f"{path} [Source: {manifest_path.lstrip('/')}]")
|
|
@@ -572,6 +601,8 @@ def main():
|
|
|
572
601
|
paths = re.findall(r'["\'`](/[a-zA-Z0-9_\-\./{}:]+)["\'`]', sw_res.text)
|
|
573
602
|
for path in paths:
|
|
574
603
|
if path not in found_paths and not any(path.endswith(ext) for ext in ['.js', '.css']):
|
|
604
|
+
if path.strip() == "/":
|
|
605
|
+
continue
|
|
575
606
|
found_paths.add(path)
|
|
576
607
|
if not args.tidy:
|
|
577
608
|
results_fromotherfiles.append(f"{path} [Source: {sw_path}]")
|
|
@@ -584,11 +615,13 @@ def main():
|
|
|
584
615
|
#openid
|
|
585
616
|
try:
|
|
586
617
|
oidc_res = requests.get(urljoin(target, "/.well-known/openid-configuration"), headers=E_HEADER, impersonate="chrome120", timeout=4)
|
|
587
|
-
if oidc_res.status_code == 200 and "
|
|
618
|
+
if oidc_res.status_code == 200 and "authorization_endpoint" in oidc_res.text and "issuer" in oidc_res.text:
|
|
588
619
|
e_files.append("/.well-known/openid-configuration")
|
|
589
620
|
paths = re.findall(r'https?://[^/]+(/[^"\']*)', oidc_res.text)
|
|
590
621
|
for path in paths:
|
|
591
622
|
if path not in found_paths:
|
|
623
|
+
if path.strip() == "/":
|
|
624
|
+
continue
|
|
592
625
|
found_paths.add(path)
|
|
593
626
|
if not args.tidy:
|
|
594
627
|
results_fromotherfiles.append(f"{path} [Source: openid-configuration]")
|
|
@@ -651,6 +684,10 @@ def main():
|
|
|
651
684
|
start_test_time = time.perf_counter()
|
|
652
685
|
args.scan_timeout = 5.0 #5min more
|
|
653
686
|
matches = re.findall(p, respo.text)
|
|
687
|
+
detected_library_keys = set(matches).intersection(JSPDF_SIGNATURE_KEYS)
|
|
688
|
+
if len(detected_library_keys) >= 3:
|
|
689
|
+
matches = [m for m in matches if m not in JSPDF_SIGNATURE_KEYS]
|
|
690
|
+
|
|
654
691
|
for m in matches:
|
|
655
692
|
m_clean = re.sub(r'(\$\{.*?\}|:[a-zA-Z0-9]+)', '1', m)
|
|
656
693
|
m_clean = m_clean.strip()
|
|
@@ -772,6 +809,9 @@ def main():
|
|
|
772
809
|
current_filename = js_url
|
|
773
810
|
for p in patterns:
|
|
774
811
|
matches = re.findall(p, js_res.text)
|
|
812
|
+
detected_library_keys = set(matches).intersection(JSPDF_SIGNATURE_KEYS)
|
|
813
|
+
if len(detected_library_keys) >= 3:
|
|
814
|
+
matches = [m for m in matches if m not in JSPDF_SIGNATURE_KEYS]
|
|
775
815
|
for m in matches:
|
|
776
816
|
m_clean = re.sub(r'(\$\{.*?\}|:[a-zA-Z0-9]+)', '1', m)
|
|
777
817
|
m_clean = m_clean.strip()
|
|
@@ -821,8 +861,13 @@ def main():
|
|
|
821
861
|
start_test_time = time.perf_counter()
|
|
822
862
|
args.scan_timeout = 5.0 #5min more
|
|
823
863
|
try:
|
|
864
|
+
DOWNLOAD_XML_HEADERS = HEADER.copy()
|
|
865
|
+
DOWNLOAD_XML_HEADERS["Cache-Control"] = "no-cache"
|
|
866
|
+
DOWNLOAD_XML_HEADERS["Pragma"] = "no-cache"
|
|
867
|
+
DOWNLOAD_XML_HEADERS["If-None-Match"] = ""
|
|
868
|
+
DOWNLOAD_XML_HEADERS["If-Modified-Since"] = ""
|
|
824
869
|
target_xml_url = urljoin(target, xmlfile)
|
|
825
|
-
x_res = requests.get(target_xml_url, headers=
|
|
870
|
+
x_res = requests.get(target_xml_url, headers=DOWNLOAD_XML_HEADERS, impersonate="chrome120", timeout=4)
|
|
826
871
|
|
|
827
872
|
if x_res.status_code == 200 and "<loc" in x_res.text.lower():
|
|
828
873
|
locs = re.findall(r'<loc>https?://[^/]+(/[^<]+)</loc>', x_res.text, re.IGNORECASE)
|
|
@@ -852,9 +897,11 @@ def main():
|
|
|
852
897
|
results_services, results_ext, results_subd = [], [], []
|
|
853
898
|
results_frameworks, results_assets = [], []
|
|
854
899
|
unsorted = []
|
|
900
|
+
assets_suffix = "" if args.show_assets else " (Hidden, use --show-assets to show)"
|
|
901
|
+
dead_suffix = "" if args.show_404s else " (Hidden, use --show-404s to show)"
|
|
855
902
|
if not args.raw_output:
|
|
856
903
|
if not args.only_res:
|
|
857
|
-
print(f"Total paths to test: {len(found_paths)}")
|
|
904
|
+
print(f"Total paths to test: {len(found_paths)} (Scraped: {len(found_paths) - len(SENSITIVE_ENDPOINT)} | Built-in: {len(SENSITIVE_ENDPOINT)})")
|
|
858
905
|
print("Testing endpoints...")
|
|
859
906
|
for path in sorted(found_paths):
|
|
860
907
|
display_path = discovered_in_js.get(path, path)
|
|
@@ -900,7 +947,7 @@ def main():
|
|
|
900
947
|
|
|
901
948
|
r = requests.get(
|
|
902
949
|
urljoin(target, path),
|
|
903
|
-
headers=
|
|
950
|
+
headers=S_HEADER,
|
|
904
951
|
cookies=session_cookies,
|
|
905
952
|
timeout=5,
|
|
906
953
|
allow_redirects=False,
|
|
@@ -915,8 +962,8 @@ def main():
|
|
|
915
962
|
service_markers = ["/api", "/v1", "/v2", "socket.io", "engine.io", "/graphql", "/webhook", "/rpc", "/actuator", "/swagger", "/v3/api-docs", "/rest/", "/ws", "/metrics"]
|
|
916
963
|
is_machine_path = any(marker in path.lower() for marker in service_markers)
|
|
917
964
|
|
|
918
|
-
media_extensions = ('.png', '.jpg', '.jpeg', '.svg', '.webp', '.gif', '.ico', '.woff', '.woff2', '.ttf', '.swf')
|
|
919
|
-
framework_extensions = ('.js', '.css', '.json', '.txt', '.xml', '.map')
|
|
965
|
+
media_extensions = ('.png', '.jpg', '.jpeg', '.svg', '.webp', '.gif', '.ico', '.woff', '.woff2', '.ttf', '.swf', '.mp4', '.mp3', '.avif', '.webm', '.wav')
|
|
966
|
+
framework_extensions = ('.js', '.css', '.json', '.txt', '.xml', '.map', '.rels', '.md')
|
|
920
967
|
|
|
921
968
|
is_media_asset = path.lower().endswith(media_extensions)
|
|
922
969
|
is_framework_asset = any(path.lower().endswith(ext) for ext in framework_extensions)
|
|
@@ -927,7 +974,13 @@ def main():
|
|
|
927
974
|
if not args.tidy and r.status_code == 405:
|
|
928
975
|
statag = ' [405]'
|
|
929
976
|
is_transport = any(p in path.lower() for p in ["socket.io", "engine.io", "/rpc", "/webhook", "/graphql"])
|
|
930
|
-
if (is_shell or is_home_redirect) and not is_transport:
|
|
977
|
+
if (is_shell or is_home_redirect) and (is_framework_asset or is_media_asset or "." in path) and not is_transport:
|
|
978
|
+
if not args.tidy:
|
|
979
|
+
results_dead.append(f"404 Not Found (React Shell): {path}{statag}")
|
|
980
|
+
else:
|
|
981
|
+
results_dead.append(f"404 Not Found: {path}{statag}")
|
|
982
|
+
continue
|
|
983
|
+
if (is_shell or is_home_redirect) and not is_transport and not is_framework_asset and not is_media_asset:
|
|
931
984
|
if path in discovered_in_js:
|
|
932
985
|
if not args.tidy:
|
|
933
986
|
results_200.append(f"{display_path}{statag} [Client-Side Route, Requires Login]")
|
|
@@ -950,7 +1003,7 @@ def main():
|
|
|
950
1003
|
results_services.append(f"{display_path}{statag}")
|
|
951
1004
|
|
|
952
1005
|
else:
|
|
953
|
-
if "text/html" in content_type:
|
|
1006
|
+
if "text/html" in content_type and not ((is_shell or is_home_redirect) and (is_framework_asset or "." in path)):
|
|
954
1007
|
if not args.tidy:
|
|
955
1008
|
results_200.append(f"{display_path}{statag} [Access no matter what]")
|
|
956
1009
|
else:
|
|
@@ -958,7 +1011,13 @@ def main():
|
|
|
958
1011
|
elif is_media_asset:
|
|
959
1012
|
results_assets.append(f"{display_path}{statag}")
|
|
960
1013
|
elif is_framework_asset:
|
|
961
|
-
|
|
1014
|
+
if is_shell or is_home_redirect:
|
|
1015
|
+
if not args.tidy:
|
|
1016
|
+
results_dead.append(f"404 Not Found (React Shell Fake File): {path}{statag}")
|
|
1017
|
+
else:
|
|
1018
|
+
results_dead.append(f"404 Not Found: {path}{statag}")
|
|
1019
|
+
else:
|
|
1020
|
+
results_frameworks.append(f"{display_path}{statag}")
|
|
962
1021
|
else:
|
|
963
1022
|
if not args.tidy:
|
|
964
1023
|
results_frameworks.append(f"{display_path}{statag} [Non-Standard File]")
|
|
@@ -1005,6 +1064,7 @@ def main():
|
|
|
1005
1064
|
start_test_time = time.perf_counter()
|
|
1006
1065
|
args.scan_timeout = 5.0 #5min more
|
|
1007
1066
|
else:
|
|
1067
|
+
#UNSORTED_PATHS IS FOR RAW OUTPUT FLAG. FOR UNSORTED AFTER SCAN TIMEOUT IT IS THE 'UNSORTED' LIST.
|
|
1008
1068
|
for path in sorted(found_paths):
|
|
1009
1069
|
display_path = discovered_in_js.get(path, path)
|
|
1010
1070
|
pure_path = display_path.split(" [Original:")[0]
|
|
@@ -1075,17 +1135,23 @@ def main():
|
|
|
1075
1135
|
print("WARNING: There should never be no inaccessble paths on a website.\nThis is most likely a false positive a fault on the script's end.\nReport this to the owner of the script immediately, whether it has found endpoints or not, and what site the script has been tested on.")
|
|
1076
1136
|
if unsorted:
|
|
1077
1137
|
print("\n----UNSORTED (Scan timed out)----")
|
|
1078
|
-
for p in
|
|
1138
|
+
for p in unsorted: print(f" {p}")
|
|
1079
1139
|
else:
|
|
1080
1140
|
if args.scan_timeout:
|
|
1081
1141
|
if not args.only_res:
|
|
1082
1142
|
print("\nAll paths sorted out.")
|
|
1083
1143
|
|
|
1084
1144
|
print(f"\n--- Scan Summary ---")
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1145
|
+
summary_report = (
|
|
1146
|
+
f"Total Accessible Pages: {len(results_200)}\n"
|
|
1147
|
+
f"Total Services: {len(results_services)}\n"
|
|
1148
|
+
f"Total External References: {len(results_ext)}\n"
|
|
1149
|
+
f"Total Source Code/Files: {len(results_frameworks)}\n"
|
|
1150
|
+
f"Total Redirects: {len(results_30x)}\n"
|
|
1151
|
+
f"Total Assets: {len(results_assets)}{assets_suffix}\n"
|
|
1152
|
+
f"Total Inaccessible: {len(results_dead)}{dead_suffix}"
|
|
1153
|
+
)
|
|
1154
|
+
print(summary_report)
|
|
1089
1155
|
if unsorted:
|
|
1090
1156
|
print(f"Total Unsorted: {len(unsorted)}")
|
|
1091
1157
|
if args.show_source:
|
|
@@ -1158,12 +1224,7 @@ def main():
|
|
|
1158
1224
|
f.write("All paths sorted out.\n")
|
|
1159
1225
|
|
|
1160
1226
|
f.write(f"\n--- Scan Summary ---\n")
|
|
1161
|
-
f.write(
|
|
1162
|
-
f.write(f"Total Services: {len(results_services)}\n")
|
|
1163
|
-
f.write(f"Total External References: {len(results_ext)}\n")
|
|
1164
|
-
f.write(f"Total Source Code/Files: {len(results_frameworks)}\n")
|
|
1165
|
-
f.write(f"Total Redirects: {len(results_30x)}\n")
|
|
1166
|
-
f.write(f"Total Inaccessible: {len(results_dead)}\n")
|
|
1227
|
+
f.write(summary_report)
|
|
1167
1228
|
if unsorted:
|
|
1168
1229
|
f.write(f"Total Unsorted: {len(unsorted)}")
|
|
1169
1230
|
if args.show_source:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "endpointscanner"
|
|
7
|
-
version = "7.2.
|
|
7
|
+
version = "7.2.4"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
description = "Website endpoint reconnaissance tool and rate limit tester that can bypass simple captchas and WAFs."
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -33,7 +33,8 @@ dependencies = [
|
|
|
33
33
|
"beautifulsoup4",
|
|
34
34
|
"playwright",
|
|
35
35
|
"playwright-stealth",
|
|
36
|
-
"httpx[http2]>=0.27.0"
|
|
36
|
+
"httpx[http2]>=0.27.0",
|
|
37
|
+
"colorama>=0.4.6"
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
[project.scripts]
|
|
File without changes
|
|
File without changes
|
{endpointscanner-7.2.2 → endpointscanner-7.2.4}/endpointscanner.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|