|
| 1 | +--- |
| 2 | +description: How to parse unencrypted iTunes backups! |
| 3 | +--- |
| 4 | + |
| 5 | +# iTunes Backup Extraction |
| 6 | + |
| 7 | +You can use the artemis API to parse and extract information from unencrypted |
| 8 | +iTunes backups! Creating an iOS backup is a common way to collect data from an |
| 9 | +iOS device for forensic analysis. |
| 10 | + |
| 11 | +:::info |
| 12 | + |
| 13 | +Support for iOS/iTunes is extremely new and limited. Artemis only supports a few |
| 14 | +apps and iOS features. A list of support artifacts/apps can be found |
| 15 | +[here](../../Artifacts/ios.md) |
| 16 | + |
| 17 | +If you are looking for a more mature tool, checkout |
| 18 | +[iLeapp](https://github.com/abrignoni/iLEAPP) |
| 19 | + |
| 20 | +::: |
| 21 | + |
| 22 | +## Creating the script |
| 23 | + |
| 24 | +Its really easy to start reviewing iTunes backups using the API. |
| 25 | + |
| 26 | +```typescript |
| 27 | +import { extractBackup } from "./artemis-api/src/ios/itunes/backup.ts"; |
| 28 | +import { Format, Output, OutputType } from "./artemis-api/src/system/output.ts"; |
| 29 | + |
| 30 | +function main() { |
| 31 | + const out: Output = { |
| 32 | + name: "iOS_deviceName", |
| 33 | + directory: "./tmp", |
| 34 | + // JSON format is **strongly** recommended for now |
| 35 | + format: Format.JSON, |
| 36 | + compress: false, |
| 37 | + endpoint_id: "", |
| 38 | + collection_id: 0, |
| 39 | + output: OutputType.LOCAL, |
| 40 | + }; |
| 41 | + const result = extractBackup( |
| 42 | + "./iTunesBackup/00008112-000429AE0C07401E", |
| 43 | + out, |
| 44 | + ); |
| 45 | +} |
| 46 | + |
| 47 | +main(); |
| 48 | +``` |
| 49 | + |
| 50 | +The script above will read the iTunes backup directory and parse all supported |
| 51 | +apps and backup artifacts in the backup collection. |
| 52 | + |
| 53 | +:::info |
| 54 | + |
| 55 | +Do not forget to transpile the script above to JavaScript as mentioned in |
| 56 | +[API](../../Intro/Scripting/bundling.md) section! |
| 57 | + |
| 58 | +**Strongly** suggest you `minify` the transpiled script.\ |
| 59 | +It will make it alot smaller! |
| 60 | + |
| 61 | +::: |
| 62 | + |
| 63 | +## Script Output |
| 64 | + |
| 65 | +The way artemis outputs data is unique for the iTunes API. Normally, artemis |
| 66 | +will output all data to single folder provided in the `Output` object. |
| 67 | + |
| 68 | +However, since artemis is parsing multiple apps and other artifacts, parsed data |
| 69 | +will placed in its own sub-directory. |
| 70 | + |
| 71 | +If you run the script above the output structure will look like the following: |
| 72 | + |
| 73 | +- Root folder - **./tmp** |
| 74 | +- Subfolders: |
| 75 | + - ./tmp/iOS_deviceName: Contains metadata associated with iTunes backup |
| 76 | + - ./tmp/apps/iOS_deviceName_app_or_artifact_name. Ex: |
| 77 | + `iOS_deviceName_com.zhiliaoapp.musically` |
| 78 | + |
| 79 | +Each subfolder will contain you JSON output |
| 80 | + |
| 81 | +## Adding new apps or artifacts |
| 82 | + |
| 83 | +Adding support for new apps or artifacts is not too challenging :) |
| 84 | + |
| 85 | +1. Make sure you have the tool prerequisites installed (Deno, VSCodium (or |
| 86 | + VSCode)) |
| 87 | +2. Clone the artemis-api repo (https://github.com/puffyCid/artemis-api.git) |
| 88 | +3. To add support for a new app, create a folder under |
| 89 | + `./src/ios/apps/<app name>`\ |
| 90 | + To add support for a new artifact, create a folder under |
| 91 | + `./src/ios/domains/<artifact domain name>` |
| 92 | + |
| 93 | +## App or artifact code structure |
| 94 | + |
| 95 | +Once you have identified an app you want to parse you will need to write some |
| 96 | +TypeScript code in order to plug it in to the artemis API. |
| 97 | + |
| 98 | +Lets say we want to add support for the DuckDuckGo browser app. We would create |
| 99 | +a typescript (ts) file under `./src/ios/apps/duckduckgo/duck.ts`. |
| 100 | + |
| 101 | +:::note |
| 102 | + |
| 103 | +You can name you folder and ts file anything you want |
| 104 | + |
| 105 | +::: |
| 106 | + |
| 107 | +In our new file we would add the following code |
| 108 | + |
| 109 | +```typescript |
| 110 | +import { |
| 111 | + FileType, |
| 112 | + ManifestApp, |
| 113 | +} from "../../../../types/ios/itunes/manifest.ts"; |
| 114 | +import { Output, outputResults } from "../../../system/output.ts"; |
| 115 | +import { IosError } from "../../error.ts"; |
| 116 | +import { parseManifestAppPlist } from "../../itunes/apps.ts"; |
| 117 | + |
| 118 | +/** |
| 119 | + * Function to extract DuckDuckGo browser info |
| 120 | + * @param app_paths Array of `ManifestApp` |
| 121 | + * @param db_path iTunes backup directory |
| 122 | + * @param output `Output` configuration object |
| 123 | + */ |
| 124 | +export function extractDuckDuckGo( |
| 125 | + app_paths: ManifestApp[], |
| 126 | + db_path: string, |
| 127 | + output: Output, |
| 128 | +) { |
| 129 | + for (const path of app_paths) { |
| 130 | + if (path.file_type != FileType.IsFile) { |
| 131 | + continue; |
| 132 | + } |
| 133 | + // Function to parse the binary plist in the Manifest.db file. Contains FileMetadata |
| 134 | + const info = parseManifestAppPlist(path.file); |
| 135 | + if (info instanceof IosError) { |
| 136 | + continue; |
| 137 | + } |
| 138 | + |
| 139 | + const target = `${db_path}/${path.directory}/${path.fileID}`; |
| 140 | + console.log(info.path); |
| 141 | + console.log(target); |
| 142 | + } |
| 143 | +} |
| 144 | +``` |
| 145 | + |
| 146 | +The code above registers a function called `extractDuckDuckGo` and pass several |
| 147 | +parameters to the function. We will review the parameters later, but you do not |
| 148 | +need to know how to obtain them. |
| 149 | + |
| 150 | +Afte register our function, we start to loop through our array of app_paths. |
| 151 | +These paths are the directories associated withe our app. Each directory should |
| 152 | +have an associated binary plist that we need to parse using |
| 153 | +`parseManifestAppPlist`. |
| 154 | + |
| 155 | +:::note |
| 156 | + |
| 157 | +We only want to parse files! So we skip non-file related entries such as |
| 158 | +directories |
| 159 | + |
| 160 | +::: |
| 161 | + |
| 162 | +:::info |
| 163 | + |
| 164 | +iTunes backup files and directories names are hashed! In order to determine the |
| 165 | +original name we need to call `parseManifestAppPlist`! |
| 166 | + |
| 167 | +::: |
| 168 | + |
| 169 | +Next we specify the target iTunes backup file: |
| 170 | +``const target = `${db_path}/${path.directory}/${path.fileID}`;`` |
| 171 | + |
| 172 | +Then we print the target file (path of hashed names and directories) and the |
| 173 | +un-hashed names |
| 174 | + |
| 175 | +If you transpile and run the code above you should see something like below: |
| 176 | + |
| 177 | +``` |
| 178 | +[runtime]: "Library/Preferences/com.duckduckgo.blocker-list.etags.plist" |
| 179 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/7e/7e65b6f36849e8431a36186ddf98019af8487f1d" |
| 180 | +[runtime]: "Library/Preferences/com.apple.EmojiCache.plist" |
| 181 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/96/962a40518bc47a929fa53a208b2250e4937add6a" |
| 182 | +[runtime]: "Library/Preferences/com.duckduckgo.app.adClickAttribution.plist" |
| 183 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/0a/0a54b7c56e482059cfb55968b1305a15e948453a" |
| 184 | +[runtime]: "Library/WebKit/WebsiteData/Default/QkkulCe8Q-EdXJ986IlKrwKGIychU0X2UP4JUpfNScs/QkkulCe8Q-EdXJ986IlKrwKGIychU0X2UP4JUpfNScs/origin" |
| 185 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/cd/cd330b4833dbc9f7630c774f45ea1ec746eaec01" |
| 186 | +[runtime]: "Library/WebKit/WebsiteData/Default/salt" |
| 187 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/05/05fd1262114b7103c4a5fbb1b6e08e52d40d546f" |
| 188 | +[runtime]: "Library/WebKit/GeolocationSites.plist" |
| 189 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/88/8888b54a27c0f79e99567e6329b95378f8901a0c" |
| 190 | +[runtime]: "Library/WebKit/WebsiteData/SearchHistory/RecentSearches.plist" |
| 191 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/c7/c77e5f0c616494ccfee9c7fa381646c54bfb13b8" |
| 192 | +[runtime]: "Library/WebKit/ContentRuleLists/ContentRuleList-blockImageRules" |
| 193 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/a0/a08cfdf4b2d3cdce9fbf6bc7afbcf7565b65492a" |
| 194 | +[runtime]: "Library/Preferences/group.com.duckduckgo.app.plist" |
| 195 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/d9/d956029361b6990e109d1d8cc47c8b0780fa0758" |
| 196 | +[runtime]: "Library/Application Support/.tipkit/tips-store.db" |
| 197 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/2b/2bb3cd6079dac3dc4fbd757e1a9730458056f6a3" |
| 198 | +[runtime]: "Library/WebKit/ContentRuleLists/ContentRuleList-%22TrackerDataSet%22%22A_%2275cff4e36f76d9a81af0643d775008c9%22%22cbc7891482cdc5656356758fbf4df7d672ce2933%22%2265d5e346d5f4b32b8b5f813d753c8718%22%22%22" |
| 199 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/27/272f7a61c6b687508c7a9c30f91cc955a1d418cd" |
| 200 | +[runtime]: "Library/Preferences/HKE973VLUW.com.duckduckgo.subscriptions.plist" |
| 201 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/34/346893e9861467096e4bd75a103ec913e000a9c8" |
| 202 | +[runtime]: "Library/Application Support/atb-present.marker" |
| 203 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/95/95d9c13c5075b50fe82e77a63dc33a49edd9698d" |
| 204 | +[runtime]: "Library/WebKit/WebsiteData/Default/QkkulCe8Q-EdXJ986IlKrwKGIychU0X2UP4JUpfNScs/QkkulCe8Q-EdXJ986IlKrwKGIychU0X2UP4JUpfNScs/CacheStorage/salt" |
| 205 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/28/284d57dd57fbb5b2ba47df75061d54b029d349cd" |
| 206 | +[runtime]: "Library/Preferences/com.duckduckgo.mobile.ios.plist" |
| 207 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/f8/f881608e2754551e1695a1bab51b8b11c8b5a3bc" |
| 208 | +[runtime]: "Library/Preferences/com.duckduckgo.unique.pixel.storage.plist" |
| 209 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/7e/7ee08df288b84e89aba7e642620731b032be42d5" |
| 210 | +[runtime]: "Library/Preferences/com.duckduckgo.pixel.storage.plist" |
| 211 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/68/68a54dda6529c4364087fa47776d8bf312cb6a71" |
| 212 | +[runtime]: "Library/WebKit/ContentRuleLists/ContentRuleList-%22Attribution_TrackerDataSet%22%22A_%2275cff4e36f76d9a81af0643d775008c9%22%22cbc7891482cdc5656356758fbf4df7d672ce2933%22%2265d5e346d5f4b32b8b5f813d753c8718%22%22%22" |
| 213 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/b6/b641cda8a7bb7d542d604e2441ca05b0a7b3cad9" |
| 214 | +[runtime]: "Library/WebKit/WebsiteData/ResourceLoadStatistics/observations.db" |
| 215 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/0e/0eeba21e901d1c692c861fa478d3b9021352f5be" |
| 216 | +[runtime]: "Library/Application Support/ad-attribution-successful.marker" |
| 217 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/3c/3c73a053786b0cfbddb873791a5af1ff575686a8" |
| 218 | +[runtime]: "Library/Preferences/com.duckduckgo.daily.pixel.storage.plist" |
| 219 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/0e/0e7c2c955a0e18fe284fa6a96787d9db7e2257d7" |
| 220 | +[runtime]: "Library/Cookies/Cookies.binarycookies" |
| 221 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/1c/1c2937886859a739a9746411d9a25d3907de4ac1" |
| 222 | +[runtime]: "Library/Application Support/History.sqlite" |
| 223 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/2a/2a984202263e5aedd67a20d6233f115783cddd65" |
| 224 | +[runtime]: "Library/Preferences/com.duckduckgo.app.toggleProtectionsCounter.plist" |
| 225 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/f1/f1b0e539a2f0d8a3897fe0331882bc07f3ef82ed" |
| 226 | +[runtime]: "Library/WebKit/WebsiteData/MediaKeys/v1/salt" |
| 227 | +[runtime]: "./itunesBackupDirectory/00008112-000429AE0C07401E/ca/caa12f6b21d5beb6938d79a9ea8d4b2b503d0223" |
| 228 | +``` |
| 229 | + |
| 230 | +## Research! |
| 231 | + |
| 232 | +Now is the challenging part! We need to research what file we may want to parse! |
| 233 | +The most common types of files you will encouter are JSON, plist, and sqlite |
| 234 | +databases. |
| 235 | + |
| 236 | +Lets look at the `GeolocationSites.plist` file. It looks interesting. Since its |
| 237 | +a plist file we will need to import the plist parsing function to our code |
| 238 | + |
| 239 | +```typescript |
| 240 | +for (const path of app_paths) { |
| 241 | + if (path.file_type != FileType.IsFile) { |
| 242 | + continue; |
| 243 | + } |
| 244 | + // Function to parse the binary plist in the Manifest.db file. Contains FileMetadata |
| 245 | + const info = parseManifestAppPlist(path.file); |
| 246 | + if (info instanceof IosError) { |
| 247 | + continue; |
| 248 | + } |
| 249 | + |
| 250 | + const target = `${db_path}/${path.directory}/${path.fileID}`; |
| 251 | + if (info.path.includes("GeolocationSites.plist")) { |
| 252 | + // Make sure to provide the target variable! This the full path the to iTunes backup hashed filename |
| 253 | + const result = getPlist(target); |
| 254 | + outputResults(JSON.stringify(result), "duckduckgo_geosites", output); |
| 255 | + } |
| 256 | + console.log(info.path); |
| 257 | + console.log(target); |
| 258 | +} |
| 259 | +``` |
| 260 | + |
| 261 | +The additional code we added will parse the `GeolocationSites.plist` file and |
| 262 | +output the results to a JSON file! |
| 263 | + |
| 264 | +We would repeat this process for each file we are interested for the DuckDuckGo |
| 265 | +app. |
| 266 | + |
| 267 | +## Conclusion |
| 268 | + |
| 269 | +The final step is to make artemis aware of the `extractDuckDuckGo` function.\ |
| 270 | +Navigate to the file `./src/ios/itunes/apps.ts` and to `extractAppInfo`. |
| 271 | + |
| 272 | +You should see something like below: |
| 273 | + |
| 274 | +```typescript |
| 275 | +/** |
| 276 | + * Function to parse supported apps and domains |
| 277 | + * @param paths Array of `ManifestApp` |
| 278 | + * @param namespace App or domain name |
| 279 | + * @param db_path iTunes backup directory |
| 280 | + * @param output `Output` object |
| 281 | + */ |
| 282 | +export function extractAppInfo( |
| 283 | + paths: ManifestApp[], |
| 284 | + namespace: string, |
| 285 | + db_path: string, |
| 286 | + output: Output, |
| 287 | +) { |
| 288 | + switch (namespace) { |
| 289 | + case "com.amazon.echo": |
| 290 | + extractAmazonEcho(paths, db_path, output); |
| 291 | + break; |
| 292 | + case "us.zoom.videomeetings": |
| 293 | + extractZoom(paths, db_path, output); |
| 294 | + break; |
| 295 | + case "co.hinge.mobile.ios": |
| 296 | + extractHingeInfo(paths, db_path, output); |
| 297 | + break; |
| 298 | + case "HomeDomain": |
| 299 | + extractHomeDomain(paths, db_path, output); |
| 300 | + break; |
| 301 | + case "RootDomain": |
| 302 | + extractRootDomain(paths, db_path, output); |
| 303 | + break; |
| 304 | + } |
| 305 | +} |
| 306 | +``` |
| 307 | + |
| 308 | +We just need to insert domain name for the DuckDuckGo app and add our function! |
| 309 | + |
| 310 | +```typescript |
| 311 | +switch (namespace) { |
| 312 | + case "com.amazon.echo": |
| 313 | + extractAmazonEcho(paths, db_path, output); |
| 314 | + break; |
| 315 | + case "us.zoom.videomeetings": |
| 316 | + extractZoom(paths, db_path, output); |
| 317 | + break; |
| 318 | + case "com.duckduckgo.mobile.ios": // <-------------- Our function is registered! |
| 319 | + extractDuckDuckGo(paths, db_path, output); |
| 320 | + break; |
| 321 | + case "co.hinge.mobile.ios": |
| 322 | + extractHingeInfo(paths, db_path, output); |
| 323 | + break; |
| 324 | + case "HomeDomain": |
| 325 | + extractHomeDomain(paths, db_path, output); |
| 326 | + break; |
| 327 | + case "RootDomain": |
| 328 | + extractRootDomain(paths, db_path, output); |
| 329 | + break; |
| 330 | +} |
| 331 | +``` |
| 332 | + |
| 333 | +:::info |
| 334 | + |
| 335 | +If you do not know the domain name of the app. You can find it in the output |
| 336 | +after running your script and calling `extractBackup` once. |
| 337 | + |
| 338 | +::: |
0 commit comments