almasumdev/awesome-mobile-security-agent-skills

root-jailbreak-detection

Detecting rooted Android and jailbroken iOS devices as a risk signal — not a silver bullet.

Voir la source

Mode d’emploi

  1. Copiez la commande et examinez le paquet avant de l’installer dans votre environnement agent.
  2. Consultez le document original pour les outils, identifiants et environnements requis.
  3. Décrivez les entrées et le livrable souhaité ; comparez la sortie aux sources.
npx skills add https://github.com/almasumdev/awesome-mobile-security-agent-skills --skill root-jailbreak-detection

Avant de commencer

  • L’installation n’inclut ni abonnement au modèle, ni quota API, ni accès aux fichiers.
  • Étoiles et installations reflètent l’activité, pas un test indépendant de qualité ou de sécurité.
Document Skill original

Rendu depuis le dépôt source en conservant titres, exemples, code, tableaux, liens et images.

Root / Jailbreak Detection

Instructions

Root and jailbreak detection are signals, not security controls. A rooted device can hide itself from any detection you write. Design for that reality.

1. What a Rooted / Jailbroken Device Actually Means

  • Platform sandbox is no longer a trust boundary for your app.
  • Keystore / Keychain material is still protected if it is hardware-backed — root can't extract from StrongBox / Secure Enclave.
  • TLS pinning can be bypassed (user CA installed into system store, Frida unpinning, LSPosed modules).
  • Screen recorders, input injectors, and clipboard snoopers are trivial.

You may still want to run on these devices (your users include pentesters, tinkerers, and kids with unlocked bootloaders) — but with a higher risk score.

2. Android Signals (Combine Several)

kotlin
fun rootSignals(ctx: Context): List<String> {
    val signals = mutableListOf<String>()

    // 1. Common su binaries
    val suPaths = listOf(
        "/system/bin/su", "/system/xbin/su", "/sbin/su",
        "/system/app/Superuser.apk", "/vendor/bin/su",
    )
    if (suPaths.any { File(it).exists() }) signals += "su_binary"

    // 2. Magisk / SuperSU package names
    val rootPackages = listOf(
        "com.topjohnwu.magisk", "eu.chainfire.supersu",
        "com.koushikdutta.rommanager", "com.noshufou.android.su",
    )
    if (rootPackages.any { isInstalled(ctx, it) }) signals += "root_package"

    // 3. Test-keys build
    if (Build.TAGS?.contains("test-keys") == true) signals += "test_keys"

    // 4. Writable system partitions
    val rwDirs = listOf("/system", "/system/bin", "/system/xbin")
    if (rwDirs.any { File(it).canWrite() }) signals += "system_rw"

    return signals
}

Each signal is independently bypassable. Report the set of signals, not a boolean.

3. iOS Signals

swift
func jailbreakSignals() -> [String] {
    var signals: [String] = []

    // 1. Known jailbreak files
    let paths = [
        "/Applications/Cydia.app",
        "/Library/MobileSubstrate/MobileSubstrate.dylib",
        "/bin/bash", "/usr/sbin/sshd", "/etc/apt",
        "/private/var/lib/apt/",
    ]
    if paths.contains(where: { FileManager.default.fileExists(atPath: $0) }) {
        signals.append("jb_file")
    }

    // 2. Sandbox escape: can we write outside our container?
    let probe = "/private/jb_probe_\(UUID().uuidString)"
    if (try? "x".write(toFile: probe, atomically: true, encoding: .utf8)) != nil {
        signals.append("sandbox_rw")
        try? FileManager.default.removeItem(atPath: probe)
    }

    // 3. Fork allowed (only jailbroken iOS allows fork)
    if fork() >= 0 { signals.append("fork_allowed") }

    // 4. Known URL schemes
    if let url = URL(string: "cydia://package/com.example"),
       UIApplication.shared.canOpenURL(url) { signals.append("cydia_scheme") }

    return signals
}

The fork() and sandbox-write checks are the hardest to bypass without kernel patches — the file-path checks are trivial to hide.

4. Library Options

  • Android: RootBeer, dtx-free style checks. Read their source, fork, and rename before shipping — stock names are what jailbreak-hiders target.
  • iOS: IOSSecuritySuite, DTTJailbreakDetection. Same caveat.
  • Don't trust a single "is rooted?" function. Combine N signals with weights.

5. Treat as Risk Score, Not Verdict

Bad pattern:

kotlin
if (isRooted) { finish(); return } // attacker patches the method, done.

Better pattern:

kotlin
val signals = rootSignals(ctx)
riskScore += signals.size * 10
riskScore += if (attestationVerdict != STRONG) 20 else 0
// Send score + signals to server; let the server decide.

The server may:

  • Allow read-only usage.
  • Block money movement / key operations / document export.
  • Require step-up MFA.
  • Silently flag for fraud review.

6. Business Policy First

Before writing a single check, get written agreement on policy:

  • "We allow login on rooted devices, but not transactions over $X."
  • "We allow all operations with warning, with extra logging."
  • "We refuse to run at all on jailbroken devices."

Each of these is defensible; each has different UX and legal implications. Don't decide this in code review.

7. UX

  • If you block, tell the user why in plain language ("This app requires an unmodified device for security reasons"). Don't hide behind a generic error.
  • Provide a support channel for false positives.
  • Never ship a hard block without a feature flag to roll back.

Checklist

  • [ ] Root / jailbreak detection combines multiple independent signals.
  • [ ] Results are reported as a score + signal list, not a boolean.
  • [ ] Server makes the final decision for sensitive operations.
  • [ ] Business policy for rooted/jailbroken devices is documented and agreed in advance.
  • [ ] Detection libraries are forked / renamed, not used stock.
  • [ ] Blocked users get a clear explanation and a support path.
  • [ ] A feature flag exists to disable enforcement remotely.

Tâches connexes

du même dépôt

Autres Skills

Tous les Skills