PDF をダウンロード: 2022_C00C00R00CON_jailbreak.pdf
ジェイルブレイクなし
ptrace (lldb / frida) の場合 → アプリには get-task-allow 資格が必要です
frida コードを挿入する → アプリを再パッケージ化する必要がある
どちらの場合も、申請を辞退する必要があります
しかし、それには多くの副作用があります。
異なるチームID
ファイルが変更されました
脱獄あり
ジェイルブレイク検出メカニズムがリバース エンジニアリング防御に追加され、ジェイルブレイクされたデバイスでのアプリの実行がより困難になります。これにより、リバース エンジニアが好んで使用するツールやテクニックの一部がブロックされます。他のほとんどの種類の防御と同様、ジェイルブレイク検出はそれ自体ではあまり効果的ではありませんが、アプリのソース コード全体にチェックを分散させることで、全体的な改ざん防止スキームの有効性を向上させることができます。
ユーザーが一般的なジェイルブレイク検出方法を簡単に回避できるツールに直面して、ジェイルブレイクによってもたらされる企業のセキュリティ リスクが複合化されます。ユーザーは、Spotify++ などのアプリ調整をサードパーティ アプリ ストア Cydia から直接ダウンロードできます。多くのアプリケーションはジェイルブレイク検出を提供しており、コンテンツの著作権侵害とアカウント侵害をそれぞれ制限したい多くのメディア アプリや金融サービス アプリも同様です。残念ながら、これらの脱獄検出は、次のような比較的単純で回避可能なテストの組み合わせに依存しています。
これらの検出テストと同等の検出テストの基本的な制限は、クライアント側のテストとして、攻撃者によってアクセス、リバース エンジニアリング、回避される可能性があることです。さらに、これらの脱獄検出テストを実行するアプリ (MDM アプリなど) は Apple のアプリレビュープロセスを通過する必要があり、デバイスの脱獄ステータスを分析するために収集できるデータの範囲が制限されます。それらを簡単に見てみましょう。
通常、ジェイルブレイクに関連付けられている次のようなファイルとディレクトリを確認します。
//suspicious system and app paths to check
private static var suspicousAppandSystemPaths : [ String ] {
return [
" /usr/sbin/frida-server " ,
" /etc/apt/sources.list.d/electra.list " ,
" /etc/apt/sources.list.d/sileo.sources " ,
" /.bootstrapped_electra " ,
" /usr/lib/libjailbreak.dylib " ,
" /jb/lzma " ,
" /.cydia_no_stash " ,
" /.installed_unc0ver " ,
" /jb/offsets.plist " ,
" /usr/share/jailbreak/injectme.plist " ,
" /etc/apt/undecimus/undecimus.list " ,
" /var/lib/dpkg/info/mobilesubstrate.md5sums " ,
" /Library/MobileSubstrate/MobileSubstrate.dylib " ,
" /jb/jailbreakd.plist " ,
" /jb/amfid_payload.dylib " ,
" /jb/libjailbreak.dylib " ,
" /usr/libexec/cydia/firmware.sh " ,
" /var/lib/cydia " ,
" /etc/apt " ,
" /private/var/lib/apt " ,
" /private/var/Users/ " ,
" /var/log/apt " ,
" /Applications/Cydia.app " ,
" /private/var/stash " ,
" /private/var/lib/apt/ " ,
" /private/var/lib/cydia " ,
" /private/var/cache/apt/ " ,
" /private/var/log/syslog " ,
" /private/var/tmp/cydia.log " ,
" /Applications/Icy.app " ,
" /Applications/MxTube.app " ,
" /Applications/RockApp.app " ,
" /Applications/blackra1n.app " ,
" /Applications/SBSettings.app " ,
" /Applications/FakeCarrier.app " ,
" /Applications/WinterBoard.app " ,
" /Applications/IntelliScreen.app " ,
" /private/var/mobile/Library/SBSettings/Themes " ,
" /Library/MobileSubstrate/CydiaSubstrate.dylib " ,
" /System/Library/LaunchDaemons/com.ikey.bbot.plist " ,
" /Library/MobileSubstrate/DynamicLibraries/Veency.plist " ,
" /Library/MobileSubstrate/DynamicLibraries/LiveClock.plist " ,
" /System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist " ,
" /Applications/Cydia.app " ,
" /Applications/blackra1n.app " ,
" /Applications/FakeCarrier.app " ,
" /Applications/Icy.app " ,
" /Applications/IntelliScreen.app " ,
" /Applications/MxTube.app " ,
" /Applications/RockApp.app " ,
" /Applications/SBSettings.app " ,
" /Applications/WinterBoard.app "
]
}
ほとんどの場合、これらは次のコマンドを使用してチェックされます。
-(BOOL)fileExistsAtPath:(NSString*)
NSFileManager またはFileManager.default.fileExists(atPath: path)
のパス メソッド。ただし、fopen()、stat()、access() などの下位レベルの C 関数を使用するアプリケーションもあります。
脱獄メカニズムを確認するもう 1 つの方法は、アプリケーションのサンドボックスの外側にある場所への書き込みを試みることです。これを行うには、アプリケーションに/private
ディレクトリなどにファイルを作成させます。ファイルが正常に作成された場合、デバイスはジェイルブレイクされています。
この方法は、システム上の特定のファイルとディレクトリのアクセス許可を確認することで構成されます。ジェイルブレイクされたデバイスでは、侵害されていないデバイスよりも多くのディレクトリが書き込みアクセス権を持っています。この一例は、元々は読み取り権限しか持たないルート パーティションです。読み取りおよび書き込み権限があることが判明した場合、デバイスがジェイルブレイクされていることを意味します。これらのチェックを実行するには、NSFileManager や statfs() などの C 関数を使用するなど、さまざまな方法があります。
迅速:
do {
let pathToFileInRestrictedDirectory = " /private/jailbreak.txt "
try " This is a test. " . write ( toFile : pathToFileInRestrictedDirectory , atomically : true , encoding : String . Encoding . utf8 )
try FileManager . default . removeItem ( atPath : pathToFileInRestrictedDirectory )
// Device is jailbroken
} catch {
// Device is not jailbroken
}
目標-C:
NSError *error;
NSString *stringToBeWritten = @" This is a test. " ;
[stringToBeWritten writeToFile: @" /private/jailbreak.txt " atomically: YES
encoding: NSUTF8StringEncoding error: &error];
if (error == nil ) {
// Device is jailbroken
return YES ;
} else {
// Device is not jailbroken
[[ NSFileManager defaultManager ] removeItemAtPath: @" /private/jailbreak.txt " error: nil ];
}
これらのチェックを実行するには、NSFileManager や statfs()、open()、utimes()、stat()、pathconf()、stat64()、fopen() などの C 関数を使用するなど、さまざまな方法があります。
Cydia URL を開いてプロトコル ハンドラーを確認できます。 Cydia アプリ ストアは、事実上すべての脱獄ツールがデフォルトでインストールしますが、cydia:// プロトコル ハンドラーをインストールします。
迅速:
if let url = URL ( string : " cydia://package/com.example.package " ) , UIApplication . shared . canOpenURL ( url ) {
// Device is jailbroken
}
目標-C:
if ([[UIApplication sharedApplication ] canOpenURL: [ NSURL URLWithString: @" cydia://package/com.example.package " ]]){
// Device is jailbroken
}
この単純なチェックは、攻撃者によってアクセスされ、リバース エンジニアリングされ、回避される可能性があります。
ptrace ( PT_DENY_ATTACH );
void try_kill () {
const int pid = getpid ();
int ret = kill (pid, 0 );
}
kill の man ページ ( man 2 kill
) によると、最初のパラメータで指定されたpid
が実際に存在するかどうかを確認するためにシグナル0
が使用されます。
[…] ただし、値を 0 にすると、(信号は送信されずに) エラー チェックが実行されます。これは、pid の有効性をチェックするために使用できます。
この強制終了操作の後には、 PTRACE
チェックが行われます。
inline bool ptrace_detect () {
int32_t opt[ 4 ] = {
CTL_KERN,
KERN_PROC,
KERN_PROC_PID,
getpid (),
};
kinfo_proc info;
sysctl (opt, 4 , &info, sizeof (kinfo_proc), nullptr , 0 );
return info. kp_proc . p_flag & P_TRACED;
}
getppid() == 1
getfsstat64(), statvfs()
fcntl(F_ADDSIGS)
csops(CS_OPS_MARKKILL)
バイナリの署名の整合性をチェックできます。このチェックは、ディスクからメイン アプリのバイナリを開き、 kSecCodeMagicEmbeddedSignature シーケンス0xfade0cc0
までシークし、資格を読み取り、チェックサムを計算することから始まります。
コード署名セグメントにはスーパーブロブ構造が含まれており、この構造自体にさまざまな種類の他のブロブが含まれています。両方の構造は、よりオープンソースの Apple コードで定義されています。
/*
* Structure of an embedded-signature SuperBlob
*/
typedef struct __BlobIndex {
uint32_t type; /* type of entry */
uint32_t offset; /* offset of entry */
} CS_BlobIndex;
typedef struct __SuperBlob {
uint32_t magic; /* magic number */
uint32_t length; /* total length of SuperBlob */
uint32_t count; /* number of index entries following */
CS_BlobIndex index[]; /* (count) entries */
/* followed by Blobs in no particular order as indicated by offsets in index */
} CS_SuperBlob;
スーパー BLOB には、コード署名セクション全体の長さ、BLOB の数、およびそれらの BLOB へのインデックスの配列という魔法が含まれています。さまざまな識別ブロブ マジックがこのファイルで定義されています。
/*
* Magic numbers used by Code Signing
*/
enum {
kSecCodeMagicRequirement = 0xfade0c00 , /* single requirement */
kSecCodeMagicRequirementSet = 0xfade0c01 , /* requirement set */
kSecCodeMagicCodeDirectory = 0xfade0c02 , /* CodeDirectory */
kSecCodeMagicEmbeddedSignature = 0xfade0cc0 , /* single-architecture embedded signature */
kSecCodeMagicDetachedSignature = 0xfade0cc1 , /* detached multi-architecture signature */
kSecCodeMagicEntitlement = 0xfade7171 , /* entitlement blob */
};
たとえば、iOS ps
バイナリには、最初のスーパーブロブに続いてコンテンツの 4 つのブロブがあります。
これらはそれぞれ次のとおりです。
0xfade0c02
)。修飾バイナリ名com.apple.ps
が含まれていることがわかります。0xfade0c01
)。0xfade7171
) を含む資格 BLOB。0xfade0b01
)。大きなタイムアウトの後にアプリを停止し、ジェイルブレイク検出場所から直接終了しないようにしてください。
fork()
プロセスのフォーク。 Sandboxd は、アプリケーションが fork()、popen()、またはその他の C 関数を使用してジェイルブレイクされたデバイス上に子プロセスを作成する機能を拒否しません。ただし、sandboxd は、ジェイルブレイクされていないデバイスでのプロセスのフォークを明示的に拒否します。したがって、fork() で返された pid をチェックすることで、アプリケーションはデバイスが侵害されているかどうかを知ることができます。フォークが成功すると、アプリはジェイルブレイクされたデバイス上で実行されていると推測できます。
system()
ジェイルブレイクされていないデバイスで NULL 引数を指定して system() 関数を呼び出すと、0 が返されます。ジェイルブレイクされたデバイスで同じことを実行すると、1 が返されます。これは、この関数が /bin/sh が存在するかどうかをチェックするためであり、この関数は /bin/sh が存在するかどうかを確認するためです。ジェイルブレイクされたデバイス。
SSH ループバック接続。ジェイルブレイクされたデバイスの大部分には OpenSSH がインストールされているため、一部のアプリケーションはポート 22 で 127.0.0.1 への接続を試みます。接続が成功した場合は、OpenSSH がインストールされ実行されていることを意味し、デバイスがジェイルブレイクされていることを証明します。
22 (OpenSSH) および 44 (checkra1n) の開いているポートのループバックを確認します。
この検出方法は、 _dyld_image_count()
や_dyld_get_image_name()
などの関数を呼び出して、現在ロードされている dylib を確認することから始まります。パッチ自体が dylib の一部であるため、この方法では動的にパッチを適用するのが非常に困難です。
/usr/lib/substitute-inserter.dylib
など
dlopen / メモリスキャン / dyld 内部構造などを使用できます。
private static func checkDYLD ( ) -> Bool {
let suspiciousLibraries = [
" FridaGadget " ,
" frida " ,
" cynject " ,
" libcycript "
]
for libraryIndex in 0 ..< _dyld_image_count ( ) {
guard let loadedLibrary = String ( validatingUTF8 : _dyld_get_image_name ( libraryIndex ) ) else { continue }
for suspiciousLibrary in suspiciousLibraries {
if loadedLibrary . lowercased ( ) . contains ( suspiciousLibrary . lowercased ( ) ) {
return true
}
}
}
return false
}
コードの整合性をチェックする
フリーダを検出してみます
private static func isFridaRunning ( ) -> Bool {
func swapBytesIfNeeded ( port : in_port_t ) -> in_port_t {
let littleEndian = Int ( OSHostByteOrder ( ) ) == OSLittleEndian
return littleEndian ? _OSSwapInt16 ( port ) : port
}
var serverAddress = sockaddr_in ( )
serverAddress . sin_family = sa_family_t ( AF_INET )
serverAddress . sin_addr . s_addr = inet_addr ( " 127.0.0.1 " )
serverAddress . sin_port = swapBytesIfNeeded ( port : in_port_t ( 27042 ) )
let sock = socket ( AF_INET , SOCK_STREAM , 0 )
let result = withUnsafePointer ( to : & serverAddress ) {
$0 . withMemoryRebound ( to : sockaddr . self , capacity : 1 ) {
connect ( sock , $0 , socklen_t ( MemoryLayout < sockaddr_in > . stride ) )
}
}
if result != - 1 {
return true
}
return false
}
一般に、ジェイルブレイクの検出が複雑になればなるほど、検出とバイパスが難しくなります。ジェイルブレイク検出を実装する際の最も一般的な間違いは、実装自体にあることがよくあります。
優れたジェイルブレイク検出機能を備えたアプリをよく見かけますが、その実装は、デバイスがジェイルブレイクされているかどうかに応じて true または false を返す 1 つの関数内にあります。このような場合、Cycript または同様のツールを使用して検出関数からの戻り値を反転することで、ジェイルブレイク検出をバイパスします。
実際には、複数のテクニックを組み合わせ、それらを他の機能に統合することで簡単にバイパスできないようにすることが最善のジェイルブレイク検出になります。
https://github.com/mehrankmlf/SecurityKit
https://www.synacktiv.com/sites/default/files/2021-10/2021_sthack_jailbreak.pdf
https://www.romainthomas.fr/post/21-07-pokemongo-anti-frida-jailbreak-bypass/
https://aeonlucid.com/Snapchat-detection-on-iOS/
https://github.com/AeonLucid/SnapHide
https://redmaple.tech/blogs/macho-files/