Fix dropbear host keys on writable Data

This commit is contained in:
xiahouzhen
2026-07-05 13:36:24 +03:00
committed by zqxwce
parent c2e290e5a5
commit c6ed3a1732
6 changed files with 156 additions and 6 deletions
+13 -1
View File
@@ -411,6 +411,12 @@ ssh_cmd "/usr/bin/tar --preserve-permissions --no-overwrite-dir \
-xf /mnt1/iosbinpack64.tar -C /mnt1" -xf /mnt1/iosbinpack64.tar -C /mnt1"
ssh_cmd "/bin/rm -f /mnt1/iosbinpack64.tar" ssh_cmd "/bin/rm -f /mnt1/iosbinpack64.tar"
echo " Preparing dropbear host keys on Data volume..."
ssh_cmd "/bin/mkdir -p /mnt3/dropbear"
ssh_cmd "if [ ! -f /mnt3/dropbear/dropbear_rsa_host_key ]; then /mnt1/iosbinpack64/usr/local/bin/dropbearkey -t rsa -f /mnt3/dropbear/dropbear_rsa_host_key >/dev/null; fi"
ssh_cmd "if [ ! -f /mnt3/dropbear/dropbear_ecdsa_host_key ]; then /mnt1/iosbinpack64/usr/local/bin/dropbearkey -t ecdsa -f /mnt3/dropbear/dropbear_ecdsa_host_key >/dev/null; fi"
ssh_cmd "/bin/chmod 600 /mnt3/dropbear/dropbear_rsa_host_key /mnt3/dropbear/dropbear_ecdsa_host_key"
echo " [+] iosbinpack64 installed" echo " [+] iosbinpack64 installed"
# ═══════════ 5/7 PATCH LAUNCHD_CACHE_LOADER ══════════════════ # ═══════════ 5/7 PATCH LAUNCHD_CACHE_LOADER ══════════════════
@@ -493,7 +499,13 @@ echo " [+] vphoned installed (signed copy at .vphoned.signed)"
# Send daemon plists (overwrite on re-run) # Send daemon plists (overwrite on re-run)
for plist in bash.plist dropbear.plist trollvnc.plist rpcserver_ios.plist; do for plist in bash.plist dropbear.plist trollvnc.plist rpcserver_ios.plist; do
scp_to "$INPUT_DIR/jb/LaunchDaemons/$plist" "/mnt1/System/Library/LaunchDaemons/" plist_src="$INPUT_DIR/jb/LaunchDaemons/$plist"
if [[ "$plist" == "dropbear.plist" ]]; then
plist_src="$TEMP_DIR/dropbear.plist"
cp "$INPUT_DIR/jb/LaunchDaemons/dropbear.plist" "$plist_src"
"$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" patch-dropbear-plist "$plist_src"
fi
scp_to "$plist_src" "/mnt1/System/Library/LaunchDaemons/"
ssh_cmd "/bin/chmod 0644 /mnt1/System/Library/LaunchDaemons/$plist" ssh_cmd "/bin/chmod 0644 /mnt1/System/Library/LaunchDaemons/$plist"
done done
scp_to "$VPHONED_SRC/vphoned.plist" "/mnt1/System/Library/LaunchDaemons/" scp_to "$VPHONED_SRC/vphoned.plist" "/mnt1/System/Library/LaunchDaemons/"
+13 -1
View File
@@ -408,6 +408,12 @@ ssh_cmd "/usr/bin/tar --preserve-permissions --no-overwrite-dir \
-xf /mnt1/iosbinpack64.tar -C /mnt1" -xf /mnt1/iosbinpack64.tar -C /mnt1"
ssh_cmd "/bin/rm -f /mnt1/iosbinpack64.tar" ssh_cmd "/bin/rm -f /mnt1/iosbinpack64.tar"
echo " Preparing dropbear host keys on Data volume..."
ssh_cmd "/bin/mkdir -p /mnt3/dropbear"
ssh_cmd "if [ ! -f /mnt3/dropbear/dropbear_rsa_host_key ]; then /mnt1/iosbinpack64/usr/local/bin/dropbearkey -t rsa -f /mnt3/dropbear/dropbear_rsa_host_key >/dev/null; fi"
ssh_cmd "if [ ! -f /mnt3/dropbear/dropbear_ecdsa_host_key ]; then /mnt1/iosbinpack64/usr/local/bin/dropbearkey -t ecdsa -f /mnt3/dropbear/dropbear_ecdsa_host_key >/dev/null; fi"
ssh_cmd "/bin/chmod 600 /mnt3/dropbear/dropbear_rsa_host_key /mnt3/dropbear/dropbear_ecdsa_host_key"
echo " [+] iosbinpack64 installed" echo " [+] iosbinpack64 installed"
# ═══════════ 5/7 PATCH LAUNCHD_CACHE_LOADER ══════════════════ # ═══════════ 5/7 PATCH LAUNCHD_CACHE_LOADER ══════════════════
@@ -487,7 +493,13 @@ echo " [+] vphoned installed (signed copy at .vphoned.signed)"
# Send daemon plists (overwrite on re-run) # Send daemon plists (overwrite on re-run)
for plist in bash.plist dropbear.plist trollvnc.plist rpcserver_ios.plist; do for plist in bash.plist dropbear.plist trollvnc.plist rpcserver_ios.plist; do
scp_to "$INPUT_DIR/jb/LaunchDaemons/$plist" "/mnt1/System/Library/LaunchDaemons/" plist_src="$INPUT_DIR/jb/LaunchDaemons/$plist"
if [[ "$plist" == "dropbear.plist" ]]; then
plist_src="$TEMP_DIR/dropbear.plist"
cp "$INPUT_DIR/jb/LaunchDaemons/dropbear.plist" "$plist_src"
"$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" patch-dropbear-plist "$plist_src"
fi
scp_to "$plist_src" "/mnt1/System/Library/LaunchDaemons/"
ssh_cmd "/bin/chmod 0644 /mnt1/System/Library/LaunchDaemons/$plist" ssh_cmd "/bin/chmod 0644 /mnt1/System/Library/LaunchDaemons/$plist"
done done
scp_to "$VPHONED_SRC/vphoned.plist" "/mnt1/System/Library/LaunchDaemons/" scp_to "$VPHONED_SRC/vphoned.plist" "/mnt1/System/Library/LaunchDaemons/"
+12 -3
View File
@@ -52,6 +52,9 @@ Commands:
inject-daemons <launchd.plist> <daemon_dir> inject-daemons <launchd.plist> <daemon_dir>
Inject bash/dropbear/trollvnc into launchd.plist. Inject bash/dropbear/trollvnc into launchd.plist.
patch-dropbear-plist <dropbear.plist>
Rewrite dropbear ProgramArguments to use /var/dropbear host keys.
inject-dylib <binary> <dylib_path> inject-dylib <binary> <dylib_path>
Inject LC_LOAD_DYLIB into Mach-O binary (thin or universal). Inject LC_LOAD_DYLIB into Mach-O binary (thin or universal).
Equivalent to: optool install -c load -p <dylib_path> -t <binary> Equivalent to: optool install -c load -p <dylib_path> -t <binary>
@@ -76,7 +79,7 @@ if __name__ == "__main__":
from patchers.cfw_patch_hv_vmm_dsc import patch_hv_vmm_in_dsc from patchers.cfw_patch_hv_vmm_dsc import patch_hv_vmm_in_dsc
from patchers.cfw_patch_camera_dsc import apply_all_camera_patches from patchers.cfw_patch_camera_dsc import apply_all_camera_patches
from patchers.cfw_patch_watchdogd import patch_watchdogd from patchers.cfw_patch_watchdogd import patch_watchdogd
from patchers.cfw_daemons import parse_cryptex_paths, inject_daemons from patchers.cfw_daemons import parse_cryptex_paths, inject_daemons, patch_dropbear_plist
else: else:
from .cfw_patch_seputil import patch_seputil from .cfw_patch_seputil import patch_seputil
from .cfw_patch_cache_loader import patch_launchd_cache_loader from .cfw_patch_cache_loader import patch_launchd_cache_loader
@@ -85,7 +88,7 @@ else:
from .cfw_patch_hv_vmm_dsc import patch_hv_vmm_in_dsc from .cfw_patch_hv_vmm_dsc import patch_hv_vmm_in_dsc
from .cfw_patch_camera_dsc import apply_all_camera_patches from .cfw_patch_camera_dsc import apply_all_camera_patches
from .cfw_patch_watchdogd import patch_watchdogd from .cfw_patch_watchdogd import patch_watchdogd
from .cfw_daemons import parse_cryptex_paths, inject_daemons from .cfw_daemons import parse_cryptex_paths, inject_daemons, patch_dropbear_plist
def main(): def main():
@@ -169,6 +172,12 @@ def main():
sys.exit(1) sys.exit(1)
inject_daemons(sys.argv[2], sys.argv[3]) inject_daemons(sys.argv[2], sys.argv[3])
elif cmd == "patch-dropbear-plist":
if len(sys.argv) < 3:
print("Usage: patch_cfw.py patch-dropbear-plist <dropbear.plist>")
sys.exit(1)
patch_dropbear_plist(sys.argv[2])
elif cmd == "inject-dylib": elif cmd == "inject-dylib":
if len(sys.argv) < 4: if len(sys.argv) < 4:
print("Usage: patch_cfw.py inject-dylib <binary> <dylib_path>") print("Usage: patch_cfw.py inject-dylib <binary> <dylib_path>")
@@ -194,7 +203,7 @@ def main():
print(f"Unknown command: {cmd}") print(f"Unknown command: {cmd}")
print("Commands: cryptex-paths, patch-seputil, patch-launchd-cache-loader, patch-camera-dsc,") print("Commands: cryptex-paths, patch-seputil, patch-launchd-cache-loader, patch-camera-dsc,")
print(" patch-mobileactivationd, patch-launchd-jetsam,") print(" patch-mobileactivationd, patch-launchd-jetsam,")
print(" patch-hv-vmm-dsc, patch-watchdogd, inject-daemons, inject-dylib") print(" patch-hv-vmm-dsc, patch-watchdogd, inject-daemons, patch-dropbear-plist, inject-dylib")
sys.exit(1) sys.exit(1)
+43 -1
View File
@@ -4,6 +4,14 @@ from .cfw_asm import *
import os import os
import plistlib import plistlib
DROPBEAR_KEY_ARGS = [
"-r",
"/var/dropbear/dropbear_rsa_host_key",
"-r",
"/var/dropbear/dropbear_ecdsa_host_key",
]
def parse_cryptex_paths(manifest_path): def parse_cryptex_paths(manifest_path):
"""Extract Cryptex DMG paths from BuildManifest.plist. """Extract Cryptex DMG paths from BuildManifest.plist.
@@ -53,6 +61,9 @@ def inject_daemons(plist_path, daemon_dir):
with open(src, "rb") as f: with open(src, "rb") as f:
daemon = plistlib.load(f) daemon = plistlib.load(f)
if name == "dropbear":
patch_dropbear_daemon(daemon)
key = f"/System/Library/LaunchDaemons/{name}.plist" key = f"/System/Library/LaunchDaemons/{name}.plist"
target.setdefault("LaunchDaemons", {})[key] = daemon target.setdefault("LaunchDaemons", {})[key] = daemon
print(f" [+] Injected {name}") print(f" [+] Injected {name}")
@@ -61,7 +72,38 @@ def inject_daemons(plist_path, daemon_dir):
plistlib.dump(target, f, sort_keys=False) plistlib.dump(target, f, sort_keys=False)
def patch_dropbear_daemon(daemon):
"""Make dropbear use host keys on writable Data instead of read-only /etc."""
args = list(daemon.get("ProgramArguments", []))
if not args:
return
# -R generates default keys under /etc/dropbear, which is read-only during
# normal VM boot. Use explicit /var/dropbear keys seeded by cfw_install.
cleaned = []
idx = 0
while idx < len(args):
arg = args[idx]
if arg == "-R":
idx += 1
continue
if arg == "-r":
idx += 2
continue
cleaned.append(arg)
idx += 1
args = cleaned + DROPBEAR_KEY_ARGS
daemon["ProgramArguments"] = args
def patch_dropbear_plist(plist_path):
with open(plist_path, "rb") as f:
daemon = plistlib.load(f)
patch_dropbear_daemon(daemon)
with open(plist_path, "wb") as f:
plistlib.dump(daemon, f, sort_keys=False)
# ══════════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════════
# CLI # CLI
# ══════════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════════
+15
View File
@@ -98,6 +98,21 @@ chown -R 501:501 /var/jb/var/mobile/Library/Preferences
chmod 0755 /var/jb/var/mobile/Library/Preferences chmod 0755 /var/jb/var/mobile/Library/Preferences
log " Ownership set" log " Ownership set"
log "[2a/8] Preparing dropbear host keys..."
mkdir -p /var/dropbear
DROPBEARKEY=""
for p in /iosbinpack64/usr/local/bin/dropbearkey /iosbinpack64/usr/local/dropbearkey /var/jb/usr/bin/dropbearkey; do
[ -x "$p" ] && DROPBEARKEY="$p" && break
done
if [ -n "$DROPBEARKEY" ]; then
[ -f /var/dropbear/dropbear_rsa_host_key ] || "$DROPBEARKEY" -t rsa -f /var/dropbear/dropbear_rsa_host_key >/dev/null
[ -f /var/dropbear/dropbear_ecdsa_host_key ] || "$DROPBEARKEY" -t ecdsa -f /var/dropbear/dropbear_ecdsa_host_key >/dev/null
chmod 0600 /var/dropbear/dropbear_rsa_host_key /var/dropbear/dropbear_ecdsa_host_key
log " dropbear host keys ready"
else
log " WARNING: dropbearkey not found"
fi
# ═══════════ 3/7 RUN prep_bootstrap.sh ═══════════════════════ # ═══════════ 3/7 RUN prep_bootstrap.sh ═══════════════════════
log "[3/8] Running prep_bootstrap.sh..." log "[3/8] Running prep_bootstrap.sh..."
if [ -f /var/jb/prep_bootstrap.sh ]; then if [ -f /var/jb/prep_bootstrap.sh ]; then
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Unit tests for CFW LaunchDaemon plist rewrites."""
import os
import sys
import unittest
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(ROOT, "scripts"))
from patchers.cfw_daemons import DROPBEAR_KEY_ARGS, patch_dropbear_daemon # noqa: E402
class DropbearPlistTests(unittest.TestCase):
def test_rewrites_readonly_root_key_generation(self):
daemon = {
"ProgramArguments": [
"/iosbinpack64/usr/local/bin/dropbear",
"--shell",
"/iosbinpack64/bin/bash",
"-R",
"-E",
"-F",
"-p",
"22222",
"-a",
]
}
patch_dropbear_daemon(daemon)
args = daemon["ProgramArguments"]
self.assertNotIn("-R", args)
self.assertEqual(args[-len(DROPBEAR_KEY_ARGS):], DROPBEAR_KEY_ARGS)
def test_replaces_stale_explicit_key_paths(self):
daemon = {
"ProgramArguments": [
"dropbear",
"-r",
"/etc/dropbear/dropbear_rsa_host_key",
"-E",
"-r",
"/tmp/old_ecdsa_key",
"-p",
"22222",
]
}
patch_dropbear_daemon(daemon)
args = daemon["ProgramArguments"]
self.assertNotIn("/etc/dropbear/dropbear_rsa_host_key", args)
self.assertNotIn("/tmp/old_ecdsa_key", args)
self.assertEqual(args, ["dropbear", "-E", "-p", "22222", *DROPBEAR_KEY_ARGS])
if __name__ == "__main__":
unittest.main()