14/04/2025
Před lety jsem byl aktivní na scéně vývoje iOS tweaků. Vytvořil jsem mnoho produktů a nástrojů, distribuovaných na Cydii, které modifikovaly chování systému iOS a přidávaly nové funkce do SpringBoardu. Byla to opravdu zábavná doba, která mi poskytla cenné rané zkušenosti s reverzním inženýrstvím uzavřených binárních souborů, přímou interakcí s Objective-C runtime a podnikáním. Jsem za ty roky opravdu vděčný.

Jedním z aspektů scény jailbreaku, který mi vždy připadal jako černá magie, byl samotný proces jailbreaku. Vyhlídka je to docela pozoruhodná: vezměte jakýkoli standardní iPhone, pak provádějte obscénní rituály a recitujte prastará zaříkávání, dokud okovy nespadnou. Operační systém vám pak umožní spouštět jakýkoli kód, bez ohledu na to, zda prošel procesem podepisování od společnosti Apple, a dláždí tak cestu pro pracovité vývojáře tweaků, jako jsem já. Před několika týdny jsem dostal chuť odstranit tento závoj tajemství z jailbreaků napsáním vlastního. Jedna výjimka: skutečně šťavnatou práci zde odvedli mí předchůdci. Jsem obzvláště zavázán p0sixninjovi a axi0mx, kteří laskavě sdíleli své znalosti prostřednictvím open source.
Prvním krokem bylo získání zařízení. Nevěděl jsem nic o tom, jak napsat jailbreak nebo jak bude vypadat můj přístup, takže jsem začal něčím očividným. Pořídil jsem si iPhone 4 a 3GS z eBay. Starší zařízení se zdála být dobrým místem, kde začít, protože jejich zabezpečení je pravděpodobně horší, ale musíte najít zlatou střední cestu: opravdu stará zařízení jsou divoce cenná. Moje zařízení dorazila s vágními představami o tom, jak by se dala využít, založenými na fragmentech, které jsem četl v průběhu let: nějaké chybné parsování PDF zde, nějaký zranitelný kód framebufferu tam. Abych se mohl pokusit o využití některého z nich, musel bych být schopen spustit nějaký kód na zařízení. Představovaná cesta spočívala v tom, že se mi podaří nastavit nástroj, který dokáže produkovat a instalovat aplikace tak, jak se to dělalo v roce 2010. Pomocí toho bych pak napsal aplikaci a zkoumal zevnitř sandboxu, abych prozkoumal útočnou plochu.
Ukázalo se však, že nedávné verze Xcode neumožňují cílit na verze iOS starší než několik let. Možná bychom si mohli stáhnout starší verzi Xcode? Ale kašlu na to. Stáhl jsem si několik starších verzí Mac OS X, které jsem zamýšlel nastavit ve virtuálním stroji, abych mohl spustit starší verzi Xcode, pak jsem si uvědomil, že se nudím. I kdyby se mi podařilo nastavit starý nástroj, nebylo by jasné, zda by Apple binární soubor cílený na starší verzi iOS vůbec podepsal. Zkusme něco jiného. Nakonec jsem se rozhodl prozkoumat zranitelnost boot ROM. To má několik zábavných výhod, jako je nepotřebnost nastavování starého nástroje a práce ve virtuálním stroji, protože zranitelnost boot ROM je typicky využívána napsáním kódu na hostitelském počítači, který interaguje se zařízením přes USB.
Pochopení SecureROM: Základ Důvěry
Boot ROM, nebo jak ho Apple nazývá SecureROM, je první fází procesu spouštění iOS a spouští další části procesu spouštění. Je zodpovědností SecureROMu zajistit, aby cokoli, co se načte dál, bylo důvěryhodné – jinými slovy, aby se spustil pouze přesný obraz, který Apple dodal a podepsal. SecureROM s radostí načte jednu ze dvou komponent, v závislosti na tom, co se děje:
- Pokud zařízení provádí „normální“ spuštění ze souborového systému, SecureROM spustí komponentu zvanou Low Level Bootloader (LLB) z diskové partition na NOR paměti.
- Pokud je zařízení v režimu DFU a je připojeno k počítači přes USB, proces obnovy iPhonu může být zahájen odesláním bootloaderu iBSS (iBoot Single Stage).
Stejně jako SecureROM měl za úkol zkontrolovat, zda je LLB nebo iBSS důvěryhodný, tak i LLB a iBSS musí podobně zajistit, aby to, co načtou dál, bylo také důvěryhodné. Každá následující fáze se ujistí, že důvěřuje té, která následuje po ní. Proces obnovy vypadá zhruba takto:
SecureROM → iBSS → iBoot → Kernel → Launchd → System
Toto je náš řetězec důvěry: každá fáze načte pouze něco, čemu důvěřuje, a proto je konečný kód, se kterým uživatel pracuje, vždy důvěryhodný. To platí, pokud tento řetězec neporušíme! Všimněte si, že každá následující fáze je ověřena předchozí fází, kromě první fáze. Náš diagram ve skutečnosti vypadá takto:
(Implicitně Důvěryhodný) SecureROM → iBSS (ověřen SecureROMem) → iBoot (ověřen iBSS) → Kernel (ověřen iBootem) → Launchd (ověřen Kernelem) → System
SecureROM je implicitně důvěryhodný a je to těžké břemeno. Zatímco všechny ostatní fáze lze vyměnit, pokud se najdou nějaké zranitelnosti, vydáním aktualizované verze iOS, SecureROM je vyleptán do paměti pouze pro čtení (read-only memory) při výrobě zařízení. To znamená, že každé zařízení vyrobené s danou verzí SecureROMu bude trvale zranitelné vůči jakýmkoli problémům v této verzi. A jak se ukazuje, takové zranitelnosti skutečně existují a lze je zneužít! Pro srovnání, u novějších zařízení, jako je iPhone 5S a novější, Apple zavedl technologii Secure Enclave. Exploit, který by ovlivnil Secure Enclave, by se lišil od exploitů SecureROMu, protože Secure Enclave je navržena tak, aby chránila citlivá data a kryptografické operace, a její zranitelnosti jsou typicky odlišné a neznamenají, že by bylo možné spustit libovolný kód tak nízko v bootovacím procesu.
Vznešená Zranitelnost: Exploit limera1n
limera1n je název jednoho takového exploitu, který byl vydán geohotem a zabalen do stejnojmenného jailbreak nástroje v roce 2010. limera1n lze zneužít, když zařízení v DFU režimu čeká na odeslání iBSS hostitelem přes USB. SecureROM, který je dodáván s čipem A4 SoC (System on a Chip), je zranitelný, takže iPhone 4, který jsem si pořídil, by měl být skvělým cílem.
Jedna věc, která je na limera1nu opravdu fascinující, je, že nikdo přesně neví, jak funguje. Geohot řekl, že netuší, proč funguje, a p0sixninja spekuloval o teoriích. Nástroje k pochopení toho, co se děje, tam určitě jsou (zejména po úniku zdrojového kódu iBootu z iOS 9), ale podle mých znalostí nikdo netvrdí, že by dal dohromady všechny dílky. Pád, který vedl k limera1nu, byl nalezen fuzzingem USB kontrolních zpráv a zdá se, že jde o race condition, která vede k přetečení haldy (heap overflow), což útočníkovi umožňuje injektovat a spouštět shellcode. Fuzzing uzavřených binárních souborů nám dal dar mimozemské technologie: můžeme ji používat, je mocná, ale nevíme, co dělá.
Komunikace se Zařízením: Čtení Paměti přes USB
Začal jsem hledat implementace limera1nu, abych zjistil, jak ho replikovat. Rychle jsem narazil na SecureROM dumper od pod2g, který byl nesmírně užitečný. Jedním rázem mi ukázal:
- Jak implementovat limera1n.
- Jaký druh kódu by mohl být v payloadu.
- Jak číst paměť ze zařízení přes USB.
Poslední bod byl neuvěřitelný. SecureROM běží na zařízení, a pokud ho chcete analyzovat, budete ho muset nějak dostat ze zařízení. SecureROM dumper od pod2g kopíruje paměť, kde je SecureROM mapován (0x0), do přijímací oblasti USB. Pak, na straně hostitele, odesílá USB kontrolní zprávy pro čtení dat ze zařízení.

Podle mých znalostí nikdo explicitně nenapsal o druhé polovině toho: nejenže můžete zapisovat data do zařízení iOS přes USB, zařízení bude také reagovat na požadavky na čtení. To mě docela překvapilo, protože jsem si představoval zařízení jako černou díru, která by nasávala bity a nikdy nic neodhalila o svém vlastním stavu. Tento mechanismus není nikde online vysvětlen, co jsem viděl. Zde je mé porozumění:
- MMU (Memory Management Unit) čipu A4 mapuje základ SRAM (Static Random-Access Memory) na adresu 0x84000000.
- Hostitelé komunikující se zařízením v DFU režimu (jako software Apple běžící na Macu pro obnovu iPhonu) mohou odesílat obraz iBSS po částech odesíláním USB kontrolních paketů s typem požadavku 0x21 a ID požadavku 1. Data odeslaná v kontrolních paketech budou zkopírována do SRAM, počínaje 0x84000000 a posouvající se na vyšší adresy, jak hostitel odesílá více paketů (aby nepřepsal předchozí data). SecureROM udržuje některé interní čítače sledující, kam by měl být zkopírován další paket, a tyto čítače lze vymazat (pravděpodobně pokud hostitel chce zrušit přenos a začít znovu).
- Zařízení bude také reagovat, pokud hostitel odešle kontrolní paket s typem požadavku 0xA1, ID požadavku 2. Zařízení přečte obsah paměti na 0x84000000 a odešle ho hostiteli. To se zdá pochybně užitečné, pokud tato paměť zdánlivě obsahuje pouze data, která hostitel sám již odeslal, ale stává se to opravdu užitečné, když máme možnost spouštět kód na zařízení a můžeme zkopírovat cokoli se nám líbí na 0x84000000.
Výše uvedený dumper proto používá limera1n k provedení payloadu, který kopíruje paměť na 0x0 (která obsahuje SecureROM) na 0x84000000, a poté se vrací zpět do původní smyčky SecureROM DFU. Hostitel pak odešle několik požadavků na čtení A1:2, v podstatě vytáhne dump SecureROMu ze zařízení. Napsal jsem vlastní implementaci limera1nu na základě SecureROM dumperu od pod2g a také jsem se pokusil dumpovat SecureROM. Byl jsem nadšený, když to fungovalo!
Od Sestavovacího Jazyka k Rustu: Tvorba Payloada
Nyní mám na tomto iPhonu 4 spuštěný kód a jsem připraven jít svým vlastním směrem. Pro začátek mohu spouštět assembly, ale není jasné, kde se tato assembly spouští. Kde je můj zásobník? Jakou paměť můj shellcode přepisuje? Jaké jsou limity, jak velký může být můj program shellcode, než začnu přepisovat něco důležitého v paměti? Než budeme moci odpovědět na kteroukoli z těchto otázek, budeme potřebovat nějaký způsob, jak získat ladicí data ze zařízení. Tok „čtení paměti na 0x84000000“, použitý v SecureROM dumperu, se zdá být pro to opravdu užitečným nástrojem! Napsal jsem nějaký shellcode, který kopíruje hodnoty ukazatele instrukcí a ukazatele zásobníku na 0x84000000, a pak jsem použil stejný kód na straně hostitele k přečtení hodnot zpět. Tímto způsobem jsem vytvořil „print()“ pro chudé, který mi umožňuje komunikovat informace, které shromažďuji na zařízení, pomocí paměťových dumpů, které přijímám na hostiteli.
Vytvořil jsem skripty pro automatické spuštění mého exploitu a dumpování prvních několika slov z 0x84000000 do výstupního okna, abych mohl kontrolovat zkopírované hodnoty ukazatele instrukcí a ukazatele zásobníku. Tyto skripty mi umožnily rychle iterovat po provedení změn v mém shellcodu. Při pohledu na první dvě slova dumpované paměti vidíme, že náš shellcode běží kolem 0x8402b048 a ukazatel zásobníku je na 0x8403bfa0. To dává smysl! Ukazatel zásobníku je v rámci normální oblasti zásobníku, kterou nastavuje sám SecureROM, a ukazatel instrukcí je v oblasti přijímacího obrazu. Jelikož jsme využili přetečení k získání spuštění kódu, vidět náš kód běžící zevnitř doručovacího bufferu není překvapivé.
Zatímco jsem si zlepšoval život, usnadnil jsem také vývoj logiky payloadu. Psaní softwaru přímo v assembly je v některých případech užitečné, ale zde je to jen překážka. Nastavil jsem build systém, který mi umožnil psát payload v Rustu, který by pak byl převeden na shellcode a odeslán do zařízení. Vybral jsem Rust, protože jsem věděl, že jednoho dne tuto práci sepíšu do blogového příspěvku, a tato volba by si získala nejvíce pozdvižených obočí. Navíc, jak cool je spouštět Rust exploit payload na zařízení, které bylo vyrobeno předtím, než Rust vůbec existoval?!
Překážky při Vývoji Payloada: Linker a Statická Data
Psaní shellcode v jakémkoli vyšším jazyce však přináší některé další obtíže, které nejsou přítomny při dodávání assembly. Je důležité si uvědomit, že když kompilujete kód ve vyšším jazyce, nedostanete ven surový strojový kód: místo toho nástroje zkompilují váš kód do binárního souboru, který obsahuje tuny metadat, včetně (mimo jiné) instrukcí pro operační systém o tom, jak nastavit virtuální adresní prostor způsobem, který program očekává, tabulky symbolů pro ladění a informace o linkeru. Nic z toho zde nechceme! Náš exploit nám dává možnost vložit některé bajty do paměti a skočit na ně, a nechceme všechny ty doplňky, které normálně přicházejí s kompilací binárního souboru v kontrolovaném prostředí. Tohle je divoký západ, a my programujeme podivný stroj.
Na macOS mají binární soubory obvykle následující rozložení:
Mach-O Binární Soubor → Segmenty (např. __TEXT, __DATA) → Sekce (např. __text, __const)
Jinými slovy, binární soubor (rozložený ve formátu Mach-O) je sbírka segmentů, z nichž každý obsahuje sekci představující nějaká data nebo něco jiného. Jedna sekce může být pro ukládání Objective-C metadat, zatímco jiná může být pro ukládání staticky vložených C řetězců. Pouze jedna z těchto sekcí obsahuje surový strojový kód, který budeme chtít nahrát do iPhonu: sekce __text v segmentu __TEXT. Jak se ukázalo, napsal jsem strongarm, rozsáhlou knihovnu pro analýzu Mach-O, takže jsem do build systému přidal rychlý skript: zkompiluje payload a nalinkuje ho do Mach-O, pak pomocí strongarmu extrahuje obsah sekce __TEXT,__text a uloží ho do souboru. Obsah tohoto souboru pak používáme limera1n k provedení na zařízení.
Nekompilujeme zde typický binární soubor a typické binární soubory mají s infrastrukturou operačního systému dohodu o tom, jak se bude jmenovat jejich vstupní bod. Ve výchozím nastavení linker očekává, že náš binární soubor definuje symboly start nebo _main, které pro náš případ použití nejsou nutné. Pokud linkeru neřekneme, že děláme něco neortodoxního, zastaví nás v našich stopách a bude si stěžovat, že chybí standardní symboly. Proto jsme museli použít specifické parametry linkeru, jako je -U _main -U start -static. Parametr -static je klíčový, protože říká linkeru, že nevytváříme dynamickou knihovnu ani spustitelný soubor, který by se spoléhal na dynamické linkování (dyld). To je přesně to, co potřebujeme pro náš samostatný shellcode, který poběží v prostředí bez operačního systému.
Nicméně, to vedlo k dalšímu problému. Můj nástroj strongarm, který analyzuje Mach-O soubory, havaroval. Důvodem bylo, že během své počáteční analýzy binárního souboru strongarm očekává, že najde příkaz pro načtení LC_DYLD_INFO. Jelikož jsme vytvořili samostatný binární soubor, který vůbec nepoužívá dyld, tento příkaz pro načtení není přítomen. Nebyl ošetřen, protože jsem se s takovým binárním souborem nikdy předtím nesetkal: většina binárních souborů používá dyld! Přidal jsem rychlou záplatu do strongarmu, aby to ošetřil, a nyní je vše v pořádku.
Během rozšiřování payloadu jsem začal do paměťového dumpu zahrnovat poměrně dost hodnot. Bylo trochu obtížné si pamatovat „slovo 3 je návratová hodnota tohoto volání, slovo 7 je adresa této funkce“, a tak jsem provedl v podstatě benigní změnu, abych zahrnul některé řetězce do dat, která jsem umístil do komunikačního prostoru. Kód vypadal nějak takto:
let communication_area_base = unsafe { slice :: from_raw_parts_mut ( 0x84000000 as * mut _, 2048 ) }; let mut cursor = 0 ; write_str ( communication_area_base, & mut cursor, "Output from image3_decrypt_payload: ", ); write_u32 ( communication_area_base, & mut cursor, ret ); Zkusil jsem to a něco se rozbilo. Důvod je zcela jasný, když se blíže podíváme na náš binární soubor payloadu, před extrahováním jeho obsahu __TEXT,__text: statický řetězec v našem zdrojovém kódu byl umístěn v __const sekci, a náš zkompilovaný Rust kód se snaží přistupovat k řetězci načítáním paměti na adrese, kde binární soubor požaduje, aby byl řetězec umístěn ve virtuálním adresním prostoru. Jelikož kompletně zahazujeme vše kromě __TEXT,__text, tato mapování virtuálního adresního prostoru jsou impotentním požadavkem binárního souboru a data v __const se nikdy nenačtou do paměti. Proto náš kód žádá o načtení řetězce z kompletně nemapované adresy a náš binární soubor havaruje. Oprava je poměrně jednoduchá a poskytuje jeden dobrý důvod, proč používat assembly k psaní našich payloadů: assembly dává programátorovi explicitní a přímou kontrolu nad rozložením statických dat, zatímco kompilované jazyky se to snaží řešit za programátora. Abychom to opravili, budeme muset zajistit, aby jakákoli statická data, která definujeme, byla vložena do __TEXT,__text, abychom se ujistili, že se neztratí, když extrahujeme shellcode. Budeme také muset být opatrní, aby jakékoli přístupy ke statickým datům používaly adresování relativní k ukazateli instrukcí, spíše než absolutní adresy, protože se nemůžeme spoléhat na to, že budou načteny na jakékoli stabilní paměťové adrese. Prozatím definuji všechny řetězce, které chci použít v assembly, a předávám jejich adresy do vstupního bodu Rust payloadu.

Jsme na tom docela dobře! Nyní máme tento pipeline:
- Provedení modifikace Rust payloadu.
- Stisknutí tlačítka.
- Payload bude zkompilován.
- Shellcode bude extrahován z binárního souboru.
- Runner použije limera1n na připojeném DFU iPhonu k provedení payloadu.
- Runner automaticky přečte data z 0x84000000, které používáme jako komunikační prostor, a prezentuje je v hexdumpu.
Co Dál? Budoucnost Arbitrární Exekuce Kódu
Co dál? Odtud můžeme dělat prakticky cokoli, protože můžeme spouštět libovolný kód na zařízení. Z jednoho úhlu pohledu je to „konec hry“. Z jiného pohledu však zábava teprve začala. Jedna věc je být schopen dělat cokoli teoreticky. Je to zcela jiné zvíře, když skutečně přimějete zařízení dělat něco zajímavého. Přečtěte si více v části 2: Obcházení bootchainu.
Často Kladené Otázky (FAQ)
Co je SecureROM a proč je tak důležitý?
SecureROM, známý také jako Boot ROM, je první a nejdůležitější fází procesu spouštění operačního systému iOS. Jedná se o kód vyleptaný přímo do paměti pouze pro čtení (ROM) zařízení během výroby. Jeho klíčovou úlohou je zajistit, aby každý další krok v bootovacím řetězci (například LLB a iBSS) byl důvěryhodný a podepsaný společností Apple. SecureROM je základem celého bezpečnostního modelu iPhonu, protože je to první kód, který se spustí, a jako jediný není možné ho aktualizovat.
Co znamená „nepatchovatelná“ zranitelnost?
Nepatchovatelná zranitelnost znamená, že chyba nebo slabina v kódu je umístěna v paměti ROM (read-only memory), kterou nelze po výrobě zařízení změnit ani aktualizovat. Na rozdíl od softwarových chyb, které Apple může opravit vydáním aktualizace iOS, zranitelnosti v SecureROMu zůstávají trvale přítomny na všech zařízeních, která byla vyrobena s touto konkrétní verzí ROM. To je důvod, proč jsou exploity SecureROMu tak ceněné v komunitě jailbreaku.
Co je limera1n a na které iPhony se vztahuje?
Limera1n je název exploitu pro SecureROM, který byl veřejně vydán známým hackerem geohotem v roce 2010. Je to bootrom exploit, což znamená, že zneužívá zranitelnost v SecureROMu, aby umožnil spuštění nepodepsaného kódu. Tento exploit se specificky zaměřuje na zařízení s čipem Apple A4 SoC, což zahrnuje iPhone 4, iPad 1 a iPod Touch 4. generace.
Jaký je rozdíl mezi SecureROM a Secure Enclave?
SecureROM je počáteční kód, který ověřuje integritu celého bootovacího procesu a nemůže být aktualizován. Secure Enclave je naopak samostatný, izolovaný koprocesor, který byl zaveden společností Apple s čipem A7 (poprvé použitým v iPhone 5S) a novějšími. Jeho účelem je chránit citlivá uživatelská data, jako jsou otisky prstů (Touch ID) nebo data Face ID, a provádět kryptografické operace v bezpečném prostředí. Zranitelnosti Secure Enclave se liší od SecureROM, protože se zaměřují na ochranu dat a kryptografii, nikoli na základní bootovací proces, a i když jsou vážné, nevedou nutně k možnosti spuštění libovolného kódu tak nízko v systému jako SecureROM exploity.
Co je to DFU režim?
DFU (Device Firmware Update) režim je speciální stav, do kterého lze iPhone uvést. V tomto režimu zařízení komunikuje s počítačem, ale nespouští iOS ani iBoot. Namísto toho čeká na firmware, který má být odeslán přes USB. Je to klíčový režim pro obnovu zařízení nebo pro instalaci upraveného firmwaru, a je to právě DFU režim, který exploit limera1n využívá k interakci se SecureROMem.
Proč je složité psát exploit payload ve vyšším programovacím jazyce?
Psaní exploit payloadu ve vyšším jazyce, jako je Rust, je složité, protože payload běží v „holém“ prostředí, bez operačního systému a jeho služeb (jako je dynamické linkování nebo správa paměti). Kompilátory vyšších jazyků obvykle generují binární soubory, které očekávají tyto služby a obsahují metadata, která nejsou pro shellcode relevantní. Je nutné ručně extrahovat pouze čistý strojový kód (např. ze sekce __TEXT,__text) a zajistit, aby veškerá statická data byla buď vložena přímo do kódu, nebo adresována relativně k ukazateli instrukcí, aby se předešlo pádům.
Chceš-li si přečíst další články podobné jako iPhone 4 a SecureROM: Hluboký Ponor do Jailbreaku, navštiv kategorii Technologie.
