Статья Пентест iOS приложений: от установки Frida до обхода jailbreak detection

Руки оператора на тёмной механической клавиатуре, рядом взломанный iPhone на антистатическом коврике. Монитор с терминалом Frida отбрасывает зелёное свечение на клавиши и очки специалиста.


На аудите финтех-приложения под iOS мы упёрлись в трёхслойную jailbreak detection - файловые проверки, sandbox-запись в /private и анализ загруженных dylib на предмет FridaGadget. Стандартная команда objection explore приводила к крашу за 200 миллисекунд: приложение завершалось до того, как Frida успевала зацепиться за процесс. Liberty Lite тоже мимо - приложение использовало кастомные inline-проверки на C вместо стандартных ObjC-методов. На разбор механики и написание рабочего хука ушло четыре часа, но именно этот кейс показал пропасть между учебными стендами вроде DVIA-2 и тем, что встречается в продакшне. Ниже - пошаговый разбор от подготовки устройства до работающих Frida-скриптов, обходящих конкретные механизмы защиты.

Jailbreak bypass в цепочке мобильного пентеста​

Обход jailbreak detection - не финальная цель, а входной барьер. Без него динамический анализ iOS приложений мёртв: приложение либо крашится при запуске, либо урезает функциональность до бесполезного состояния, блокируя доступ к критичным API-вызовам.

Типичная цепочка пентеста iOS приложений:
  1. Jailbreak + SSH-доступ - root на устройстве через Palera1n или checkra1n
  2. Jailbreak detection bypass - обход проверок, чтобы приложение нормально запустилось
  3. SSL pinning bypass - снятие TLS-пиннинга для перехвата трафика в Burp Suite
  4. Runtime manipulation - Frida-хуки для перехвата API-вызовов, дамп Keychain, модификация логики
  5. Статический анализ - декомпиляция бинаря в Hopper или Ghidra, поиск hardcoded secrets
  6. Эксплуатация - IDOR, business logic flaws, API abuse на бэкенде
Без прохождения шага 2 всё остальное - не более, чем теория.

По MITRE ATT&CK jailbreak bypass - аналог Virtualization/Sandbox Evasion (T1497): приложение пытается определить модификацию среды выполнения, а атакующий маскирует этот факт. Frida-хуки реализуют Hooking (T0874) для перехвата и подмены возвращаемых значений в runtime. Сам механизм внедрения Frida в процесс - Process Injection (T1055).

По классификации OWASP MASVS, jailbreak detection относится к категории MASVS-RESILIENCE - устойчивость к реверс-инжинирингу. Стандарт описывает root/jailbreak detection, anti-debug и runtime integrity checks как обязательные для приложений с высоким уровнем риска. Задача пентестера - проверить, насколько эти меры устойчивы к реальному атакующему, а не к автоматизированному сканеру.

Подготовка окружения: от jailbreak до Frida​

Требования к окружению​

КомпонентМинимумРекомендация
iPhone/iPadЧип A11 или старше с поддержкой checkra1n/Palera1niPhone 8/X на iOS 15.x - 16.x
JailbreakPalera1n rootfulPalera1n с rootful режимом, Sileo как менеджер пакетов
macOS хостmacOS 12+, HomebrewmacOS 13+
RAM хоста8 ГБ16 ГБ при параллельной работе Burp Suite + Ghidra
Frida клиент16.7.19 для совместимости с objectionВерсия, совпадающая с сервером
Frida сервер (iOS)Та же мажорная версия, что и клиент16.7.19 rootful build
Python3.9+3.11+
USB-кабельОригинальный Lightning или USB-C-
СетьOnline для установки пакетов через SileoOffline после настройки для OPSEC

Альтернатива физическому устройству - Corellium: облачная платформа виртуализации iOS с пре-джейлбрейкнутыми устройствами и встроенной Frida-консолью. Не нужно возиться с физическим jailbreak. Минус - стоимость подписки и невозможность тестирования hardware-специфичных защит (Secure Enclave, биометрия через SEP). На реальных проектах я предпочитаю физику - Corellium хорош для быстрой проверки гипотез, но не заменяет живое устройство.

Установка Frida и проблема совместимости версий​

После джейлбрейка первым делом - SSH. В Sileo ставите пакет OpenSSH, порт 22 на устройстве открывается автоматически. Дефолтные креды: mobile:alpine и root:alpine - смените их командой passwd через SSH до подключения к Wi-Fi. Серьёзно, до подключения. Я видел кейсы, когда на устройство заходили извне в первые секунды после подключения к сети.

Для подключения через USB (без Wi-Fi, что безопаснее) на macOS нужен usbmuxd: ставится через brew install usbmuxd, затем проброс порта командой iproxy 2222 22 & и подключение через ssh -p 2222 mobile@localhost.

Дальше - Frida-сервер. В Sileo добавляете репозиторий https://build.frida.re, ставите пакет Frida - сервер запускается автоматически. Проверка: ps aux | grep frida должен показать процесс /usr/sbin/frida-server.

И вот тут начинается боль. Текущая версия Frida в Sileo - 17.5.2 (на момент публикации), а objection совместим только с Frida 16.7.19. Несовпадение мажорных версий клиента и сервера приводит к ошибке подключения - молча, без внятного сообщения. Решение - даунгрейд Frida-сервера на устройстве до 16.7.19 через скрипт обновления с GitHub-репозитория Frida (скрипт скачивает нужную версию .deb-пакета и устанавливает через dpkg). После даунгрейда обязательна перезагрузка устройства и повторная активация jailbreak командой palera1n -f.

На хосте macOS клиент ставится через pip install frida==16.7.19 frida-tools==12.3.0 (версия frida-tools должна соответствовать версии Frida - сверяйте в release notes). Проверка: frida --version должна вернуть 16.7.19. Если версии разъехались хотя бы на минорку - готовьтесь к невнятным ошибкам при подключении.

Анатомия jailbreak detection в iOS приложениях​

1780317186069.webp

Чтобы обойти защиту, нужно понять, что конкретно она проверяет. По результатам анализа исходного кода iOS Security Suite (разбор опубликован исследователями Appknox) и реального поведения банковских приложений, проверки делятся на три категории.

Файловые проверки и URL-схемы​

Самый распространённый и самый слабый метод. Приложение вызывает NSFileManager.fileExistsAtPath: или C-функции fopen()/access() для путей, характерных для джейлбрейкнутого устройства:
  • /Applications/Cydia.app - классический маркер
  • /usr/sbin/sshd - OpenSSH-сервер
  • /bin/bash - нестандартный shell
  • /usr/bin/ssh - SSH-клиент
  • /etc/apt и /private/var/lib/apt/ - следы APT-менеджера пакетов
Параллельно проверяются URL-схемы: UIApplication.canOpenURL: тестирует доступность sileo://, cydia://, undecimus://. Хотя бы одна доступна - маркер джейлбрейка.

По исходному коду iOS Security Suite, метод performChecks() реализует восемь отдельных проверок:
  1. Check URL Schemes - доступность sileo:// и аналогов
  2. Check Existence Of Suspicious Files - fileExists(atPath:), fopen, access в read-only
  3. Check Suspicious Files Can Be Opened - isReadableFile(atPath:) и write-режим
  4. Check Restricted Directories Writable - попытка записи в /, /root, /private
  5. Check Fork - sandbox violation через fork()
  6. Check Symbolic Links - нестандартные symlink'и в системных директориях
  7. Check DYLD - загруженные библиотеки: FridaGadget, CydiaSubstrate
  8. Check Suspicious ObjC Classes - обнаружение Shadow (bypass-твик)
Метод amIJailbroken() агрегирует результаты: хотя бы одна проверка не пройдена - возвращается true, устройство считается скомпрометированным. Звучит грозно, но большинство приложений используют от силы три-четыре проверки из восьми.

Sandbox-тесты, fork() и системные вызовы​

Файловые проверки обходятся просто - хуком на fileExistsAtPath: и подменой ответа. Серьёзнее - sandbox-тесты: приложение пытается создать и удалить файл в директории, которая на стоковом iOS доступна только для чтения (/private, /root). Запись удалась - sandbox нарушен.

Вызов fork() - ещё один индикатор. На стоковом iOS sandbox запрещает создание дочерних процессов. Если fork() возвращает валидный PID вместо ошибки - устройство джейлбрейкнуто. Эту проверку сложнее обойти: fork() - системный вызов, а не ObjC-метод, и для перехвата нужен хук на уровне C-функций.

DYLD-анализ: обнаружение Frida и Substrate

Продвинутые приложения лезут в список загруженных динамических библиотек через _dyld_image_count() и _dyld_get_image_name(). В списке ищут строки:
  • FridaGadget и frida-agent - Frida
  • CydiaSubstrate и SubstrateLoader - Cydia Substrate
  • libhooker - LibHooker (альтернатива Substrate)
Если Frida внедрена через frida-server, её агент появляется в списке dylib - приложение это видит и крашится. Некоторые дополнительно сканируют TCP-порт 27042 (дефолтный порт Frida) или проверяют наличие ObjC-классов, ассоциированных с bypass-твиками.

Это прямое обнаружение инструментария пентестера, и стандартные bypass-твики (Liberty Lite, Shadow) его не всегда закрывают: iOS Security Suite в проверке №8 сама детектирует Shadow по имени ObjC-класса. Классическая гонка вооружений.

Обход jailbreak detection: практика с Frida​

1780317529367.webp

Objection для быстрого старта в iOS pentest

Objection - фреймворк динамического анализа на базе Frida, который автоматизирует мобильный пентест: обход SSL-пиннинга, дамп Keychain, стандартный jailbreak bypass. Подключение к приложению через USB (после настройки iproxy):
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

Результат: метод всегда возвращает false (0x0) независимо от реального состояния устройства. Приложение продолжает работу как на стоковом iOS.

Нюанс, на котором я сам спотыкался: используйте + isJailbroken (с плюсом) для class methods и - isJailbroken (с минусом) для instance methods. Перепутаете - хук молча не сработает, Frida не выбросит ошибку, а приложение продолжит крашиться. Сидишь и думаешь, что скрипт не работает, а на деле - не тот префикс. Проверить тип метода можно через перечисление ObjC.classes.JailbreakDetection.$ownMethods.

Для хуков C-функций (fopen, access, fork) синтаксис другой - вместо ObjC-класса используется Module.findExportByName(null, "fork") и аналогичный Interceptor.attach с подменой return value.

Работа со Swift-модулями и iOS Security Suite​

С Swift ситуация принципиально другая. Swift-методы не экспортируются в ObjC runtime - их имена подвергаются name mangling, и прямой доступ через ObjC.classes невозможен. Для хука нужен точный offset метода в бинаре.

Workflow для обхода iOS Security Suite в Swift-приложении (по методологии Appknox):
  1. Распаковать IPA: unzip target.ipa -d Payload/
  2. Найти фреймворк: Payload/<name>.app/Frameworks/IOSSecuritySuite.framework
  3. Открыть бинарь в radare2: r2 IOSSecuritySuite, выполнить aaa для полного анализа
  4. Найти целевой метод: afl | grep amIJail - получаем offset метода amIJailbroken
  5. Вычислить runtime-адрес: Module.getBaseAddress("IOSSecuritySuite") + offset
  6. Поставить Interceptor.attach по вычисленному адресу
Для Flutter-приложений, использующих flutter_jailbreak_detection (обёртка над iOS Security Suite), достаточно обойти один метод - amIJailbroken(). Flutter-плагин внутри вызывает IOSSecuritySuite.amIJailbroken() и транслирует результат в Dart-слой. Bypass на уровне нативного фреймворка разрывает всю цепочку. Один хук - и Flutter-часть приложения даже не узнает, что устройство джейлбрейкнуто.

Отдельная головная боль - приложения, где iOS Security Suite скомпилирована статически (вкомпилирована в основной бинарь, а не как отдельный .framework). Метод amIJailbroken находится не в отдельном модуле, а в основном исполняемом файле. Поиск offset через radare2 остаётся тем же, но Module.getBaseAddress нужно вызывать для основного бинаря приложения. Мелочь, но при первом столкновении отнимает час на отладку.

Когда что применять: decision tree для iOS pentest​

СитуацияТехникаИнструментВремя
Стандартные ObjC-проверкиАвтоматический bypassobjection ios jailbreak disable2-5 мин
Кастомный ObjC-класс с известным именемРучной хук методаFrida + Interceptor.attach15-30 мин
Swift-модуль (iOS Security Suite)Offset-based хукradare2 + Frida1-2 часа
Inline C-функции (fork, fopen)Хук C-экспортовFrida + Module.findExportByName30-60 мин
Anti-tampering блокирует runtime хукиСтатический патч бинаряHopper/Ghidra + resign через ldid2-4 часа
Многослойная защита (всё вместе)Послойный обходFrida + radare2 + binary patching4-8 часов

Алгоритм выбора:
  1. Запустите objection explore - приложение работает? Да - переходите к SSL pinning bypass, jailbreak detection решён.
  2. Нет (краш при запуске) - запустите через spawn: frida -U -f com.target.app -l bypass.js --no-pause. Скрипт bypass.js содержит хуки из предыдущего раздела.
  3. Frida детектируется - проверьте, не сканирует ли приложение порт 27042. Смените порт: на устройстве остановите frida-server и перезапустите с флагом frida-server -l 0.0.0.0:1337. Альтернатива - внедрение через Frida Gadget (dylib, вкомпилированный в IPA) вместо frida-server.
  4. Всё ещё крашится - статический анализ бинаря. В дизассемблере найдите условный переход (CBZ/CBNZ в ARM64), отвечающий за ветвление «jailbroken / not jailbroken», и замените на NOP. Resign бинарь через ldid -S или codesign.

Ограничения техник обхода jailbreak detection​

ТехникаРаботает противНе работает против
objection ios jailbreak disableСтандартные ObjC-проверки, NSFileManagerКастомные C-функции, Swift, обфускация
Frida ObjC hookЛюбые ObjC-методы с известным именем классаInline C, обфусцированные имена, anti-Frida
Frida C-function hookfopen, access, fork, canOpenURLServer-side validation, timing-based detection
Binary patching (NOP/CBZ)Любые клиентские проверкиServer-side validation, integrity checks с HMAC
Liberty Lite / Shadow / A-BypassМассовые файловые проверкиiOS Security Suite (детектирует Shadow), кастомные SDK

Конкретные сценарии, где стандартные подходы ломаются:

Server-side validation. Приложение отправляет результат jailbreak-проверки на бэкенд, и уже сервер принимает решение о блокировке. Клиентский bypass бесполезен - нужно перехватывать HTTP-запрос через Burp Suite (после обхода SSL pinning) и подменять значение isJailbroken: true на false в теле запроса. По сути, вся клиентская защита сводится к одному параметру в JSON.

Обфускация имён. Коммерческие SDK обфусцируют имена классов и методов. Вместо isJailbroken - a3x_k9m. Поиск через ObjC.classes по имени не даст результата. Решение - статический анализ в Hopper или Ghidra с трассировкой ветвлений от точки краша назад по call graph. Муторно, но работает.

Anti-Frida. Приложения, целенаправленно детектирующие Frida (DYLD-проверка на frida-agent, сканирование порта 27042, обнаружение Interceptor через integrity checks), требуют модификации самой Frida - пересборки из исходников с изменёнными строковыми литералами и дефолтным портом. Занятие не для слабонервных, но иногда это единственный путь.

Timing-based detection. Ряд защитных SDK замеряет время выполнения проверки: если jailbreak-функция отработала аномально быстро (хук мгновенно вернул false без реального выполнения проверок), приложение трактует это как bypass. Обход - добавление задержки в хук через Thread.sleep(0.1) или выполнение части оригинальной логики перед подменой return value. Хитро, но предсказуемо.

Integrity checks. Приложение вычисляет хеш собственного бинаря при запуске и сравнивает с эталоном. Пропатчен бинарь (NOP вместо CBZ) - хеш не совпадает - приложение отказывается работать. Для обхода нужен либо runtime-хук функции проверки хеша, либо пересчёт эталонного значения после патча.

Массовые bypass-твики (Liberty Lite, Shadow, A-Bypass) перехватывают системные API: когда приложение спрашивает «существует ли /Applications/Cydia.app?», твик подменяет ответ на «нет». Но обновления отстают от защитных SDK. Shadow детектируется самим iOS Security Suite в проверке №8 (Check Suspicious ObjC Classes) - классический пример гонки вооружений, которую исследование jsmon.sh метко назвало «Cat and Mouse game».


Заключение.
За последние пару лет jailbreak detection из формальной галочки в чек-листе разработчика превратился в первый серьёзный барьер мобильного пентеста. Банковские и финтех-приложения комбинируют файловый анализ, DYLD-инспекцию, sandbox-тесты и server-side валидацию. По MASVS-RESILIENCE - так и должно быть.

Но вот что я наблюдаю в реальных аудитах: подавляющее большинство приложений по-прежнему полагаются на один-два метода из iOS Security Suite без кастомизации. Bypass занимает 15 минут и десять строк JavaScript. Реально устойчивые реализации - с inline C, обфускацией имён, anti-Frida и server-side подтверждением - встречались мне примерно в трёх из двадцати аудитов за последний год. Остальные семнадцать ломаются стандартным Frida-скриптом с retval.replace(0x0).

Это не значит, что jailbreak detection бесполезен. Это значит, что клиентская защита без серверной валидации - декорация. Пока результат amIJailbroken() не подтверждается на бэкенде, один Interceptor.attach сводит всю защиту к нулю. Пентестеры, которые понимают эту архитектурную слабость, тратят время не на очередной вариант обхода fileExistsAtPath, а на анализ серверной логики - и именно там находят уязвимости, которые действительно стоят отчёта.
 
Последнее редактирование модератором:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab