Tags: forensics 

Rating:

*This writeup is also readable on my [GitHub repository](https://github.com/shawnduong/zero-to-hero-hacking/blob/master/writeups/closed/2021-uiuctf.md) and [personal website](https://shawnd.xyz/blog/2021-08-05/Performing-Digital-Forensics-on-an-Apple-Tablet-to-Recover-Evidence).*

## forensics/Tablet 2

*Challenge written by WhiteHoodHacker.*

> Wait... there are TWO impostors?! Red must have been in contact with the other impostor. See if you can find out what they are plotting.

Files: [`tablet.tar.gz`](https://shawnd.xyz/blog/uploads/2021-08-05/tablet.tar.gz)

Checksum (SHA-1):

```
27dfb3448130b5e4f0f73a51d2a41b32fd81b284 tablet.tar.gz
```

Looks like this is a development to our initial investigation! Based on this description, let's get our situation oriented:

- There's another impostor that Red's been in contact with.
- Our objective is to find out what they're plotting.

Immediately, my mind jumps to SMS text messages. Let's find out where SMS text messages are located on the tablet:

```sh
[skat@anubis:~/work/UIUCTF/private/var] $ find . -name "SMS"
./mobile/Library/SMS
[skat@anubis:~/work/UIUCTF/private/var] $ cd mobile/Library/SMS/
[skat@anubis:~/work/UIUCTF/private/var/mobile/Library/SMS] $ ls
CloudKitMetaData PluginMetaDataCache transferInfo prewarm.db prewarm.db-wal sms.db-shm
Drafts StickerCache com.apple.messages.geometrycache_v5.plist prewarm.db-shm sms.db sms.db-wal
```

`sms.db` looks interesting. Let's access and dump that database, just like we dumped an application's database in **forensics/Tablet 1** earlier:

```sh
[skat@anubis:~/work/UIUCTF/private/var/mobile/Library/SMS] $ sqlite3 sms.db
```

```sql
SQLite version 3.36.0 2021-06-18 18:36:39
Enter ".help" for usage hints.
qlite> .dump
earlieAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE _SqliteDatabaseProperties (key TEXT, value TEXT, UNIQUE(key));
INSERT INTO _SqliteDatabaseProperties VALUES('counter_in_all','0');
INSERT INTO _SqliteDatabaseProperties VALUES('counter_out_all','0');
INSERT INTO _SqliteDatabaseProperties VALUES('counter_in_lifetime','0');
INSERT INTO _SqliteDatabaseProperties VALUES('counter_out_lifetime','0');
INSERT INTO _SqliteDatabaseProperties VALUES('counter_last_reset','0');
INSERT INTO _SqliteDatabaseProperties VALUES('_ClientVersion','14006');
INSERT INTO _SqliteDatabaseProperties VALUES('__CSDBRecordSequenceNumber','175');
CREATE TABLE deleted_messages (ROWID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, guid TEXT NOT NULL);
CREATE TABLE chat_handle_join (chat_id INTEGER REFERENCES chat (ROWID) ON DELETE CASCADE, handle_id INTEGER REFERENCES handle (ROWID) ON DELETE CASCADE, UNIQUE(chat_id, handle_id));
INSERT INTO chat_handle_join VALUES(2,2);
CREATE TABLE sync_deleted_messages (ROWID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, guid TEXT NOT NULL, recordID TEXT );
INSERT INTO sync_deleted_messages VALUES(1,'91ED3477-EDA9-48BF-8E87-873B37D804A8','');
INSERT INTO sync_deleted_messages VALUES(2,'D06D2D50-7C12-4114-B849-1A1D2146D306',NULL);
INSERT INTO sync_deleted_messages VALUES(3,'1EE3DB69-8FD1-478F-A7A7-D0C3D8716133',NULL);
INSERT INTO sync_deleted_messages VALUES(4,'096B56BF-5A85-4194-A5CA-4A9195AE0183',NULL);
INSERT INTO sync_deleted_messages VALUES(5,'D4A09E09-A6FE-40D6-B79C-B68A878B0568','');
INSERT INTO sync_deleted_messages VALUES(6,'0C78643A-0A67-4022-A3F2-301A9496F2AD','');
CREATE TABLE message_processing_task (ROWID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, guid TEXT NOT NULL, task_flags INTEGER NOT NULL );
CREATE TABLE handle (ROWID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, id TEXT NOT NULL, country TEXT, service TEXT NOT NULL, uncanonicalized_id TEXT, person_centric_id TEXT, UNIQUE (id, service) );
INSERT INTO handle VALUES(2,'[email protected]','us','iMessage',NULL,NULL);
CREATE TABLE sync_deleted_chats (ROWID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, guid TEXT NOT NULL, recordID TEXT,timestamp INTEGER);
CREATE TABLE message_attachment_join (message_id INTEGER REFERENCES message (ROWID) ON DELETE CASCADE, attachment_id INTEGER REFERENCES attachment (ROWID) ON DELETE CASCADE, UNIQUE(message_id, attachment_id));
CREATE TABLE sync_deleted_attachments (ROWID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, guid TEXT NOT NULL, recordID TEXT );
INSERT INTO sync_deleted_attachments VALUES(1,'F0DA17FE-0A7B-4B8D-BAA0-0CB898315AA4',NULL);
CREATE TABLE kvtable (ROWID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, key TEXT UNIQUE NOT NULL, value BLOB NOT NULL);
CREATE TABLE chat_message_join (chat_id INTEGER REFERENCES chat (ROWID) ON DELETE CASCADE, message_id INTEGER REFERENCES message (ROWID) ON DELETE CASCADE, message_date INTEGER DEFAULT 0, PRIMARY KEY (chat_id, message_id));
INSERT INTO chat_message_join VALUES(2,7,648927160045514368);
INSERT INTO chat_message_join VALUES(2,8,648927174089999872);
-- snip --
```

Alright, that's a lot of data to take in! Let's find out what tables are created and prepare our filters from there.

```sql
sqlite> .tables
_SqliteDatabaseProperties kvtable
attachment message
chat message_attachment_join
chat_handle_join message_processing_task
chat_message_join sync_deleted_attachments
deleted_messages sync_deleted_chats
handle sync_deleted_messages
```

The table "message" looks interesting. Let's have a look at that.

```sql
sqlite> .dump message
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE message (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, guid TEXT UNIQUE NOT NULL, text TEXT, replace INTEGER DEFAULT 0, service_center TEXT, handle_id INTEGER DEFAULT 0, subject TEXT, country TEXT, attributedBody BLOB, version INTEGER DEFAULT 0, type INTEGER DEFAULT 0, service TEXT, account TEXT, account_guid TEXT, error INTEGER DEFAULT 0, date INTEGER, date_read INTEGER, date_delivered INTEGER, is_delivered INTEGER DEFAULT 0, is_finished INTEGER DEFAULT 0, is_emote INTEGER DEFAULT 0, is_from_me INTEGER DEFAULT 0, is_empty INTEGER DEFAULT 0, is_delayed
INTEGER DEFAULT 0, is_auto_reply INTEGER DEFAULT 0, is_prepared INTEGER DEFAULT 0, is_read INTEGER DEFAULT 0, is_system_message INTEGER DEFAULT 0, is_sent INTEGER DEFAULT 0, has_dd_results
INTEGER DEFAULT 0, is_service_message INTEGER DEFAULT 0, is_forward INTEGER DEFAULT 0, was_downgraded INTEGER DEFAULT 0, is_archive INTEGER DEFAULT 0, cache_has_attachments INTEGER DEFAULT
0, cache_roomnames TEXT, was_data_detected INTEGER DEFAULT 0, was_deduplicated INTEGER DEFAULT 0, is_audio_message INTEGER DEFAULT 0, is_played INTEGER DEFAULT 0, date_played INTEGER, item_type INTEGER DEFAULT 0, other_handle INTEGER DEFAULT 0, group_title TEXT, group_action_type INTEGER DEFAULT 0, share_status INTEGER DEFAULT 0, share_direction INTEGER DEFAULT 0, is_expirable INTEGER DEFAULT 0, expire_state INTEGER DEFAULT 0, message_action_type INTEGER DEFAULT 0, message_source INTEGER DEFAULT 0, associated_message_guid TEXT, associated_message_type INTEGER DEFAULT 0, balloon_bundle_id TEXT, payload_data BLOB, expressive_send_style_id TEXT, associated_message_range_location INTEGER DEFAULT 0, associated_message_range_length INTEGER DEFAULT 0, time_expressive_send_played INTEGER, message_summary_info BLOB, ck_sync_state INTEGER DEFAULT 0, ck_record_id TEXT, ck_record_change_tag TEXT, destination_caller_id TEXT, sr_ck_sync_state INTEGER DEFAULT 0, sr_ck_record_id TEXT, sr_ck_record_change_tag TEXT, is_corrupt INTEGER DEFAULT 0, reply_to_guid TEXT, sort_id INTEGER, is_spam INTEGER DEFAULT 0, has_unseen_mention INTEGER DEFAULT 0, thread_originator_guid TEXT, thread_originator_part TEXT);
INSERT INTO message VALUES(7,'336CCB7F-3CB8-477A-AEED-8A4418EE7FFF','Hey what’s your Discord tag',0,NULL,2,NULL,NULL,X'040b73747265616d747970656481e803840140848484124e5341747472696275746564537472696e67008484084e534f626a656374008592848484084e53537472696e67019484012b1d4865792077686174e280997320796f757220446973636f7264207461678684026949011b928484840c4e5344696374696f6e617279009484016901928496961d5f5f6b494d4d657373616765506172744174747269627574654e616d658692848484084e534e756d626572008484074e5356616c7565009484012a84999900868686',10,0,'iMessage','E:[email protected]','73890C71-600D-4961-B1A7-687F2FAD3566',0,648927160045514368,648927163675579008,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,NULL,1,0,0,0,0,0,0,NULL,0,0,0,0,0,0,0,NULL,0,NULL,NULL,NULL,0,0,0,X'62706c6973743030d20102030453616d6353757374100009080d1115170000000000000101000000000000000500000000000000000000000000000018',0,NULL,NULL,'[email protected]',0,NULL,NULL,0,NULL,0,0,0,NULL,NULL);
INSERT INTO message VALUES(8,'0D123DA5-AB98-49EF-821A-5B9BC672E461','RedAmogus#8715',0,NULL,2,NULL,NULL,X'040b73747265616d747970656481e803840140848484194e534d757461626c6541747472696275746564537472696e67008484124e5341747472696275746564537472696e67008484084e534f626a6563740085928484840f4e534d757461626c65537472696e67018484084e53537472696e67019584012b0e526564416d6f67757323383731358684026949010e928484840c4e5344696374696f6e617279009584016901928498981d5f5f6b494d4d657373616765506172744174747269627574654e616d658692848484084e534e756d626572008484074e5356616c7565009584012a849b9b00868686',10,0,'iMessage','E:[email protected]','73890C71-600D-4961-B1A7-687F2FAD3566',0,648927174089999872,648927174257075840,648927174238451968,1,1,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,NULL,1,0,0,0,0,0,0,NULL,0,0,0,0,0,0,0,NULL,0,NULL,NULL,NULL,0,0,0,X'62706c6973743030d101025375737409080b0f0000000000000101000000000000000300000000000000000000000000000010',0,'','','[email protected]',0,'','',0,'336CCB7F-3CB8-477A-AEED-8A4418EE7FFF',0,0,0,NULL,NULL);
INSERT INTO message VALUES(9,'8F42373C-2567-4CCE-8D2A-6EBB95E5FD1D','Ok I sent you a friend request',0,NULL,2,NULL,NULL,X'040b73747265616d747970656481e803840140848484124e5341747472696275746564537472696e67008484084e534f626a656374008592848484084e53537472696e67019484012b1e4f6b20492073656e7420796f75206120667269656e6420726571756573748684026949011e928484840c4e5344696374696f6e617279009484016901928496961d5f5f6b494d4d657373616765506172744174747269627574654e616d658692848484084e534e756d626572008484074e5356616c7565009484012a84999900868686',10,0,'iMessage','E:[email protected]','73890C71-600D-4961-B1A7-687F2FAD3566',0,648927183589449856,648927183862630016,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,NULL,1,0,0,0,0,0,0,NULL,0,0,0,0,0,0,0,NULL,0,NULL,NULL,NULL,0,0,0,X'62706c6973743030d20102030453616d6353757374100009080d1115170000000000000101000000000000000500000000000000000000000000000018',0,NULL,NULL,'[email protected]',0,NULL,NULL,0,'0D123DA5-AB98-49EF-821A-5B9BC672E461',0,0,0,NULL,NULL);
INSERT INTO message VALUES(10,'55C30F22-0A25-4391-BC0B-FD706018D307','We should communicate on there instead of iMessage',0,NULL,2,NULL,NULL,X'040b73747265616d747970656481e803840140848484124e5341747472696275746564537472696e67008484084e534f626a656374008592848484084e53537472696e67019484012b3257652073686f756c6420636f6d6d756e6963617465206f6e20746865726520696e7374656164206f6620694d65737361676586840269490132928484840c4e5344696374696f6e617279009484016901928496961d5f5f6b494d4d657373616765506172744174747269627574654e616d658692848484084e534e756d626572008484074e5356616c7565009484012a84999900868686',10,0,'iMessage','E:[email protected]','73890C71-600D-4961-B1A7-687F2FAD3566',0,648927208709548672,648927208949269120,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,NULL,1,0,0,0,0,0,0,NULL,0,0,0,0,0,0,0,NULL,0,NULL,NULL,NULL,0,0,0,X'62706c6973743030d20102030453616d6353757374100009080d1115170000000000000101000000000000000500000000000000000000000000000018',0,NULL,NULL,'[email protected]',0,NULL,NULL,0,'8F42373C-2567-4CCE-8D2A-6EBB95E5FD1D',0,0,0,NULL,NULL);
INSERT INTO message VALUES(11,'A5BE983C-196B-48DC-B412-CE69A8B115FE','?',0,NULL,2,NULL,NULL,X'040b73747265616d747970656481e803840140848484124e5341747472696275746564537472696e67008484084e534f626a656374008592848484084e53537472696e67019484012b04f09fa4a286840269490102928484840c4e5344696374696f6e617279009484016901928496961d5f5f6b494d4d657373616765506172744174747269627574654e616d658692848484084e534e756d626572008484074e5356616c7565009484012a84999900868686',10,0,'iMessage','E:[email protected]','73890C71-600D-4961-B1A7-687F2FAD3566',0,648927219266664448,648927219586212096,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,NULL,1,0,0,0,0,0,0,NULL,0,0,0,0,0,0,0,NULL,0,NULL,NULL,NULL,0,0,0,X'62706c6973743030d20102030453616d6353757374100009080d1115170000000000000101000000000000000500000000000000000000000000000018',0,NULL,NULL,'[email protected]',0,NULL,NULL,0,'55C30F22-0A25-4391-BC0B-FD706018D307',0,0,0,NULL,NULL);
COMMIT;
```

That's still a lot to take in! It may be valuable to understand how this table is constructed:

```sql
CREATE TABLE message (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, guid TEXT UNIQUE NOT NULL, text TEXT, replace INTEGER DEFAULT 0, service_center TEXT, handle_id INTEGER DEFAULT 0, subject TEXT, country TEXT, attributedBody BLOB, version INTEGER DEFAULT 0, type INTEGER DEFAULT 0, service TEXT, account TEXT, account_guid TEXT, error INTEGER DEFAULT 0, date INTEGER, date_read INTEGER, date_delivered INTEGER, is_delivered INTEGER DEFAULT 0, is_finished INTEGER DEFAULT 0, is_emote INTEGER DEFAULT 0, is_from_me INTEGER DEFAULT 0, is_empty INTEGER DEFAULT 0, is_delayed
```

Based on this, we can prepare a SQL query to only return data from the column "text" in the table "message."

```sql
sqlite> SELECT text FROM message;
Hey what’s your Discord tag
RedAmogus#8715
Ok I sent you a friend request
We should communicate on there instead of iMessage
?
```

Just as a quick aside, I found this to be really funny while I was doing the challenge:

```sql
sqlite> SELECT account FROM message;
E:[email protected]
E:[email protected]
E:[email protected]
E:[email protected]
E:[email protected]
```

[poggers.university](https://poggers.university/) just redirects to [illinois.edu](https://illinois.edu/). That gave me a good laugh.

Back to the challenge, it looks like our next target is Discord. If you recall from earlier in **forensics/Tablet 1**, we found that Discord was one of the apps on the tablet:

```sh
[skat@anubis:~/.../Containers] $ find . -name "com.*" | awk -F '/' '{print $NF}' | sort | uniq -u | grep -v "com.apple"
com.crashlytics
com.crashlytics.data
com.firebase.FIRInstallations.plist
com.google.gmp.measurement.monitor.plist
com.google.gmp.measurement.plist
com.hackemist.SDImageCache
com.hammerandchisel.discord - {DEFAULT GROUP}
com.hammerandchisel.discord.plist
com.hammerandchisel.discord.savedState
com.innersloth.amongus - {DEFAULT GROUP}
com.innersloth.amongus.plist
com.innersloth.amongus.savedState
com.itimeteo.webssh - {DEFAULT GROUP}
com.itimeteo.webssh.plist
com.itimeteo.webssh.savedState
com.plausiblelabs.crashreporter.data
```

Let's go ahead and see what kind of data we can recover from the saved Discord application data on this tablet using the same method we used earlier for the SSH client:

```sh
[skat@anubis:~/work/UIUCTF/private/var] $ find . -name "com.hammerandchisel.discord - {DEFAULT GROUP}"
./mobile/Containers/Data/Application/0CE5D539-F72A-4C22-BADF-A02CE5A50D2E/Library/SplashBoard/Snapshots/com.hammerandchisel.discord - {DEFAULT GROUP}
[skat@anubis:~/work/UIUCTF/private/var] $ cd ./mobile/Containers/Data/Application/0CE5D539-F72A-4C22-BADF-A02CE5A50D2E/
[skat@anubis:~/.../0CE5D539-F72A-4C22-BADF-A02CE5A50D2E] $ tree
.
├── Documents
│   ├── mmkv
│   │   ├── mmkv.default
│   │   └── mmkv.default.crc
│   └── RCTAsyncLocalStorage_V1
├── Library
│   ├── Application Support
│   │   ├── Adjust
│   │   │   ├── AdjustIoActivityState
│   │   │   ├── AdjustIoAttribution
│   │   │   └── AdjustIoPackageQueue
│   │   ├── com.crashlytics
│   │   │   └── CLSUserDefaults.plist
│   │   └── Google
│   │   ├── FIRApp
│   │   │   ├── FIREBASE_DIAGNOSTICS_HEARTBEAT_DATE
│   │   │   └── HEARTBEAT_INFO_STORAGE
│   │   └── Measurement
│   │   ├── google-app-measurement.sql
│   │   └── google_experimentation_database.sql
│   ├── Caches
│   │   ├── assets
│   │   │   ├── components_ios
│   │   │   │   └── add_friend
│   │   │   │   └── images
│   │   │   │   ├── [email protected]
│   │   │   │   └── [email protected]
│   │   │   ├── data
│   │   │   │   ├── country-codes.json
│   │   │   │   ├── emoji-shortcuts.json
│   │   │   │   └── emojis.json
│   │   │   ├── i18n
│   │   │   │   ├── languages.json
│   │   │   │   └── messages
│   │   │   │   ├── bg.json
│   │   │   │   ├── cs.json
│   │   │   │   ├── da.json
│   │   │   │   ├── de.json
-- snip --
```

1,843 lines of output, and we don't really care about most of these files! Just like before when we found what we were looking for in the application's database file, let's apply the same idea here and see if we can find what we're looking for in the Discord app's database file(s):

```sh
[skat@anubis:~/.../0CE5D539-F72A-4C22-BADF-A02CE5A50D2E] $ find . -name "*.db"
./Library/Caches/com.hammerandchisel.discord/Cache.db
./Library/WebKit/WebsiteData/ResourceLoadStatistics/observations.db
```

`Cache.db` probably has some really interesting information valuable to us! Perhaps we can find cached messages or contacts. Let's load up the database and dig around:

```sh
[skat@anubis:~/.../0CE5D539-F72A-4C22-BADF-A02CE5A50D2E] $ cd ./Library/Caches/com.hammerandchisel.discord/
[skat@anubis:~/.../com.hammerandchisel.discord] $ sqlite3 Cache.db
```

```sql
SQLite version 3.36.0 2021-06-18 18:36:39
Enter ".help" for usage hints.
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE cfurl_cache_response(entry_ID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, version INTEGER, hash_value INTEGER, storage_policy INTEGER, request_key TEXT UNIQUE, time_stamp NOT NULL DEFAULT CURRENT_TIMESTAMP, partition TEXT);
INSERT INTO cfurl_cache_response VALUES(1,0,1139453470654270506,0,'https://discord.com/ios/83.0/manifest.json','2021-07-25 18:56:41',NULL);
INSERT INTO cfurl_cache_response VALUES(2,0,-1441044142,0,'https://discord.com/api/v9/gateway','2021-07-25 18:56:42',NULL);
INSERT INTO cfurl_cache_response VALUES(3,0,2017693604,0,'https://discord.com/api/v9/channels/868908952434384926/messages?limit=25','2021-07-25 18:56:43',NULL);
INSERT INTO cfurl_cache_response VALUES(4,0,-1637691448,0,'https://latency.discord.media/rtc','2021-07-25 18:57:06',NULL);
CREATE TABLE cfurl_cache_blob_data(entry_ID INTEGER PRIMARY KEY, response_object BLOB, request_object BLOB, proto_props BLOB, user_info BLOB);
INSERT INTO cfurl_cache_blob_data VALUES(1,X'62706c6973743030d2010203045756657273696f6e5541727261791001a7050a0b0c0d3839d2060708095f10105f434655524c537472696e67547970655c5f434655524c537472696e67100f5f102a68747470733a2f2f646973636f72642e636f6d2f696f732f38332e302f6d616e69666573742e6a736f6e2341c356f5b49c4a27100010c8df10150e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536375f1010436f6e74656e742d456e636f64696e675a782d6275696c642d69645663662d726179565365727665725d43616368652d436f6e74726f6c5f10195374726963742d5472616e73706f72742d5365637572697479585f5f686861615f5f5f10127065726d697373696f6e732d706f6c69637957416c742d537663597265706f72742d746f5f1010782d7873732d70726f74656374696f6e544461746554566172795f100f782d6672616d652d6f7074696f6e73536e656c596578706563742d63745c436f6e74656e742d5479706554457461675f100f63662d63616368652d7374617475735d4c6173742d4d6f6469666965645f1016782d636f6e74656e742d747970652d6f7074696f6e7354677a697057616534663662645f1014363734373963313133653630326137652d4f52445a636c6f7564666c617265586e6f2d63616368655f102c6d61782d6167653d33313533363030303b20696e636c756465537562446f6d61696e733b207072656c6f61645f11069c0d0a0d0a596e427361584e304d4444664542514241674d454251594843416b4b4377774e446738514552495446425558475273644879456a4a5363704b7930764d544d314e7a6b3758784151513239756447567564433146626d4e765a476c755a3170344c574a316157786b4c576c6b58557868633351745457396b61575a705a57525755325679646d567958554e685932686c4c554e76626e52796232786645426c5464484a705933517456484a68626e4e7762334a304c564e6c5933567961585235587841536347567962576c7a63326c76626e4d746347397361574e355630467364433154646d4e5a636d567762334a304c585276587841516543313463334d7463484a766447566a64476c76626c52455958526c56465a68636e6c66454139344c575a795957316c4c57397764476c76626e4e54626d5673575756346347566a6443316a64467844623235305a5735304c56523563475655525852685a31385144324e6d4c574e685932686c4c584e305958523163313851466e67745932397564475675644331306558426c4c57397764476c76626e4e5759325974636d46356f525a555a337070634b45595632466c4e475932596d5368476c38514856646c5a4377674d6a4567536e5673494449774d6a45674d5467364d5451364d544967523031556f527861593278766457526d624746795a614565574735764c574e685932686c6f53426645437874595867745957646c50544d784e544d324d4441774f794270626d4e736457526c553356695247397459576c75637a736763484a6c624739685a4b456958784153615735305a584a6c633351745932396f62334a30505367706f5352664546746f4d7930794e7a30694f6a51304d79493749473168505467324e4441774c43426f4d7930794f4430694f6a51304d79493749473168505467324e4441774c43426f4d7930794f5430694f6a51304d79493749473168505467324e4441774c43426f4d7a30694f6a51304d79493749473168505467324e4441776f535a66454f7437496d56755a48427661573530637949365733736964584a73496a6f696148523063484d36584339634c324575626d56734c6d4e736233566b5a6d7868636d557559323974584339795a584276636e52634c33597a50334d394f58566a52336c5661335a776348593261474e786258457862334e764a544a475448676c4d6b5a46636e685853323552556d78305a444d786358593554555a7455327733546c4931546e46745532706d534452764e6a6b6c4d6b4a6e56336c7653585a69596e4e685232566c51556f784e577333576b784552474e53576b566963304e3264335a524d4855784d554532636d704a613064505932684f544851306145394864466853626c553165534a39585377695a334a76645841694f694a6a5a6931755a5777694c434a74595868665957646c496a6f324d4451344d4442396f5368644d5473676257396b5a5431696247396a6136457158784164553356754c4341794e53424b645777674d6a41794d5341784f446f314e6a6f304d534248545653684c4638514430466a5932567764433146626d4e765a476c755a36457556455246546c6d684d4638514a337369636d567762334a3058335276496a6f6959325974626d567349697769625746345832466e5a5349364e6a41304f4441776661457958784258625746344c57466e5a5430324d4451344d44417349484a6c6347397964433131636d6b39496d68306448427a4f693876636d567762334a304c5856796153356a624739315a475a7359584a6c4c6d4e766253396a5a473474593264704c324a6c59574e766269396c6548426c59335174593351696f545266454242686348427361574e6864476c7662693971633239756f545a66454352584c7949774e3249314d6d466b4d4463305a446c6c4e5467314f4452694f5759784e6a4d304d4451354f575a6a5a434b684f464e49535653684f6c647562334e7561575a6d6f547866454251324e7a51334f574d784d544e6c4e6a41795954646c4c5539535241414941444d4152674252414638415a674230414a414170514374414c634179674450414e5141356744714150514241514547415267424d51453441546f425077464241556b4253774672415730426541463641594d4268514730416259427977484e416973434c514d62417830444b774d744130304454774e6841324d4461414e71413551446c675077412f494542515148424334454d4151304244594550675241414141414141414141674541414141414141414150514141414141414141414141414141414141414246633d5f1012696e7465726573742d636f686f72743d28295f105b68332d32373d223a343433223b206d613d38363430302c2068332d32383d223a343433223b206d613d38363430302c2068332d32393d223a343433223b206d613d38363430302c2068333d223a343433223b206d613d38363430305f10eb7b22656e64706f696e7473223a5b7b2275726c223a2268747470733a5c2f5c2f612e6e656c2e636c6f7564666c6172652e636f6d5c2f7265706f72745c2f76333f733d3975634779556b76707076366863716d71316f736f2532464c78253246457278574b6e51526c746433317176394d466d536c374e52354e716d536a6648346f36392532426757796f497662627361476565414a31356b375a4c444463525a4562734376777651307531314136726a496b474f63684e4c7434684f477458526e553579227d5d2c2267726f7570223a2263662d6e656c222c226d61785f616765223a3630343830307d5d313b206d6f64653d626c6f636b5f101d53756e2c203235204a756c20323032312031383a35363a343120474d545f100f4163636570742d456e636f64696e675444454e595f10277b227265706f72745f746f223a2263662d6e656c222c226d61785f616765223a3630343830307d5f10576d61782d6167653d3630343830302c207265706f72742d7572693d2268747470733a2f2f7265706f72742d7572692e636c6f7564666c6172652e636f6d2f63646e2d6367692f626561636f6e2f6578706563742d6374225f10106170706c69636174696f6e2f6a736f6e5f1024572f22303762353261643037346439653538353834623966313633343034393966636422534849545f101d5765642c203231204a756c20323032312031383a31343a313220474d54576e6f736e6966661200023b6e5f10106170706c69636174696f6e2f6a736f6e0008000d0015001b001d0025002a003d004a004c007900820084008600b300c600d100d800df00ed010901120127012f0139014c015101560168016c017601830188019a01a801c101c601ce01e501f001f9022808c808dd093b0a290a370a570a690a6e0a980af20b050b2c0b300b500b580b5d0000000000000201000000000000003a00000000000000000000000000000b70',X'62706c6973743030d2010203045756657273696f6e5541727261791009af101605060b0c0d0e0f0d0d0e0512131312140d1516170d0d08d20708090a5f10105f434655524c537472696e67547970655c5f434655524c537472696e67100f5f102a68747470733a2f2f646973636f72642e636f6d2f696f732f38332e302f6d616e69666573742e6a736f6e23404e00000000000010015f101f5f5f434655524c526571756573744e756c6c546f6b656e537472696e675f5f0910840908100023000000000000000013ffffffffffffffff100253474554d618191a1b1c1d1e1f202122235a557365722d4167656e745f100f4163636570742d4c616e6775616765564163636570745d49662d4e6f6e652d4d617463685f100f4163636570742d456e636f64696e67585f5f68686
-- snip --
```

There's a lot of data, so let's pipe everything to `grep` and find out how the tables are created:

```sh
[skat@anubis:~/.../com.hammerandchisel.discord] $ sqlite3 Cache.db ".dump" | grep "CREATE TABLE"
```

```sql
CREATE TABLE cfurl_cache_response(entry_ID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, version INTEGER, hash_value INTEGER, storage_policy INTEGER, request_key TEXT UNIQUE, time_stamp NOT NULL DEFAULT CURRENT_TIMESTAMP, partition TEXT);
CREATE TABLE cfurl_cache_blob_data(entry_ID INTEGER PRIMARY KEY, response_object BLOB, request_object BLOB, proto_props BLOB, user_info BLOB);
CREATE TABLE cfurl_cache_receiver_data(entry_ID INTEGER PRIMARY KEY, isDataOnFS INTEGER, receiver_data BLOB);
```

Given that most of what is presumably our mother lode data are data blobs, let's dump those blobs and see what they say. It takes a bit of trial and error until we find what we're looking for in the `receiver_data` blob column of the table `cfurl_cache_receiver_data`:

```sh
[skat@anubis:~/.../com.hammerandchisel.discord] $ sqlite3 Cache.db "SELECT receiver_data FROM cfurl_cache_receiver_data"
7EE02011-9C66-45A1-BFE4-CB18F2251F24
{"url": "wss://gateway.discord.gg"}
[{"id": "868914084370866187", "type": 0, "content": "", "channel_id": "868908952434384926", "author": {"id": "868302522304053248", "username": "RedAmogus", "avatar": "f15b13e77a7fe5ef2d4b4d13be65d1dd", "discriminator": "8715", "public_flags": 0}, "attachments": [{"id": "868914084639293490", "filename": "image0.jpg", "size": 13859, "url": "https://cdn.discordapp.com/attachments/868908952434384926/868914084639293490/image0.jpg", "proxy_url": "https://media.discordapp.net/attachments/868908952434384926/868914084639293490/image0.jpg", "width": 421, "height": 421, "content_type": "image/jpeg"}], "embeds": [], "mentions": [], "mention_roles": [], "pinned": false, "mention_everyone": false, "tts": false, "timestamp": "2021-07-25T17:54:21.357000+00:00",
"edited_timestamp": null, "flags": 0, "components": []}, {"id": "868913936542597140", "type": 0, "content": "Ok", "channel_id": "868908952434384926", "author": {"id": "868302522304053248",
"username": "RedAmogus", "avatar": "f15b13e77a7fe5ef2d4b4d13be65d1dd", "discriminator": "8715", "public_flags": 0}, "attachments": [], "embeds": [], "mentions": [], "mention_roles": [], "pinned": false, "mention_everyone": false, "tts": false, "timestamp": "2021-07-25T17:53:46.112000+00:00", "edited_timestamp": null, "flags": 0, "components": []}, {"id": "868913804002607114", "type": 0, "content": "The password is ||su5Syb@k4||", "channel_id": "868908952434384926", "author": {"id": "868907394569207858", "username": "BlueAmogus", "avatar": "92f083abd028e406866677d86f4ca3d4", "discriminator": "8346", "public_flags": 0}, "attachments": [], "embeds": [], "mentions": [], "mention_roles": [], "pinned": false, "mention_everyone": false, "tts": false, "timestamp": "2021-07-25T17:53:14.512000+00:00", "edited_timestamp": null, "flags": 0, "components": []}, {"id": "868913676176994324", "type": 0, "content": "I sent you an encrypted note with all the details", "channel_id": "868908952434384926", "author": {"id": "868907394569207858", "username": "BlueAmogus", "avatar": "92f083abd028e406866677d86f4ca3d4", "discriminator": "8346", "public_flags": 0}, "attachments": [], "embeds": [], "mentions": [], "mention_roles": [], "pinned": false, "mention_everyone": false, "tts": false, "timestamp": "2021-07-25T17:52:44.036000+00:00", "edited_timestamp": null, "flags": 0, "components": []}, {"id": "868913627615363103", "type": 0, "content": "I'll deal with them, you just make sure this next sabotage goes to plan", "channel_id": "868908952434384926", "author": {"id": "868907394569207858", "username": "BlueAmogus", "avatar": "92f083abd028e406866677d86f4ca3d4", "discriminator": "8346", "public_flags": 0}, "attachments": [], "embeds": [], "mentions": [], "mention_roles": [], "pinned": false, "mention_everyone": false, "tts": false, "timestamp": "2021-07-25T17:52:32.458000+00:00", "edited_timestamp": null, "flags": 0, "components": []}, {"id": "868913576629403659", "type": 0, "content": "White is onto me\u2026 they kept calling me out last meeting", "channel_id": "868908952434384926", "author": {"id": "868302522304053248", "username": "RedAmogus", "avatar": "f15b13e77a7fe5ef2d4b4d13be65d1dd", "discriminator": "8715", "public_flags": 0}, "attachments": [], "embeds": [], "mentions": [], "mention_roles": [], "pinned": false, "mention_everyone": false, "tts": false, "timestamp": "2021-07-25T17:52:20.302000+00:00", "edited_timestamp": null, "flags": 0, "components": []}, {"id": "868913513463181332", "type": 0, "content": "Yo", "channel_id": "868908952434384926", "author": {"id": "868302522304053248", "username": "RedAmogus", "avatar": "f15b13e77a7fe5ef2d4b4d13be65d1dd", "discriminator": "8715", "public_flags": 0}, "attachments": [], "embeds": [], "mentions": [], "mention_roles": [], "pinned": false, "mention_everyone": false, "tts": false, "timestamp": "2021-07-25T17:52:05.242000+00:00", "edited_timestamp": null, "flags": 0, "components": []}]
[{"region":"us-central","ips":["138.128.141.109","138.128.143.75","138.128.142.25","138.128.143.90","138.128.143.9"]},{"region":"us-east","ips":["162.244.55.137","35.212.111.96","35.212.103.235","35.212.77.192","35.212.111.72"]},{"region":"atlanta","ips":["31.204.134.61","31.204.134.50","31.204.133.74","31.204.134.36","31.204.133.27"]},{"region":"newark","ips":["109.200.210.38","109.200.210.45","109.200.210.27","109.200.210.51","109.200.210.42"]},{"region":"us-south","ips":["138.128.139.7","138.128.137.12","138.128.139.16","138.128.137.190","138.128.138.244"]}]
```

Great, some data! Let's toss this into a code prettifier for easier reading:

```json
[
{
"id": "868914084370866187",
"type": 0,
"content": "",
"channel_id": "868908952434384926",
"author": {
"id": "868302522304053248",
"username": "RedAmogus",
"avatar": "f15b13e77a7fe5ef2d4b4d13be65d1dd",
"discriminator": "8715",
"public_flags": 0
},
"attachments": [
{
"id": "868914084639293490",
"filename": "image0.jpg",
"size": 13859,
"url": "https://cdn.discordapp.com/attachments/868908952434384926/868914084639293490/image0.jpg",
"proxy_url": "https://media.discordapp.net/attachments/868908952434384926/868914084639293490/image0.jpg",
"width": 421,
"height": 421,
"content_type": "image/jpeg"
}
],
"embeds": [],
"mentions": [],
"mention_roles": [],
"pinned": false,
"mention_everyone": false,
"tts": false,
"timestamp": "2021-07-25T17:54:21.357000+00:00",
"edited_timestamp": null,
"flags": 0,
"components": []
},
{
"id": "868913936542597140",
"type": 0,
"content": "Ok",
"channel_id": "868908952434384926",
"author": {
"id": "868302522304053248",
"username": "RedAmogus",
"avatar": "f15b13e77a7fe5ef2d4b4d13be65d1dd",
"discriminator": "8715",
"public_flags": 0
},
"attachments": [],
"embeds": [],
"mentions": [],
"mention_roles": [],
"pinned": false,
"mention_everyone": false,
"tts": false,
"timestamp": "2021-07-25T17:53:46.112000+00:00",
"edited_timestamp": null,
"flags": 0,
"components": []
},
-- snip --
```

Based on this little snippet, it looks like messages are stored in a field called "content." We can throw the prettified code into a file and then `grep` it to quickly give us only the messages themselves without all the other data that comes along with them:

```sh
[skat@anubis:~/dl] $ grep "\"content\":" codebeautify.json
"content": "",
"content": "Ok",
"content": "The password is ||su5Syb@k4||",
"content": "I sent you an encrypted note with all the details",
"content": "I'll deal with them, you just make sure this next sabotage goes to plan",
"content": "White is onto me… they kept calling me out last meeting",
"content": "Yo",
```

Just like that, we have the messages from Red's conversation with... with whom? Let's try to get the usernames as well so that we can better understand the situation:

```sh
[skat@anubis:~/dl] $ grep "\"content\":\|\"username\":" codebeautify.json
"content": "",
"username": "RedAmogus",
"content": "Ok",
"username": "RedAmogus",
"content": "The password is ||su5Syb@k4||",
"username": "BlueAmogus",
"content": "I sent you an encrypted note with all the details",
"username": "BlueAmogus",
"content": "I'll deal with them, you just make sure this next sabotage goes to plan",
"username": "BlueAmogus",
"content": "White is onto me… they kept calling me out last meeting",
"username": "RedAmogus",
"content": "Yo",
"username": "RedAmogus",
```

It looks like Red is in contact with Blue, and Blue sent Red a note encrypted with the password `su5Syb@k4`. Given that Red is running an Apple system, it would be logical to entertain the hunch that this must be an Apple note. Let's try finding where these files are located on the iPad:

```sh
[skat@anubis:~/work/UIUCTF/private/var] $ find . -name "Note"
[skat@anubis:~/work/UIUCTF/private/var] $ find . -name "Note*"
./mobile/Containers/Shared/AppGroup/4DCEB2C7-5420-4446-9111-4091A929B4AC/NotesIndexerState-Modern
./mobile/Containers/Shared/AppGroup/4DCEB2C7-5420-4446-9111-4091A929B4AC/NotesIndexerState-Legacy
./mobile/Containers/Shared/AppGroup/4DCEB2C7-5420-4446-9111-4091A929B4AC/NoteStore.sqlite-shm
./mobile/Containers/Shared/AppGroup/4DCEB2C7-5420-4446-9111-4091A929B4AC/NoteStore.sqlite-wal
./mobile/Containers/Shared/AppGroup/4DCEB2C7-5420-4446-9111-4091A929B4AC/NoteStore.sqlite
./mobile/Library/Assistant/CustomVocabulary/com.apple.reminders/0000000000000000000000000000000000000000/NotebookItemTitleType
./mobile/Library/Notes
```

Great, more databases! We should be familiar with the process by now, but there's an added catch: Blue mentioned that the note is encrypted. If we try accessing the database as it currently is, then it's pretty useless to us. We need some kind of parser, such as [apple_cloud_notes_parser](https://github.com/threeplanetssoftware/apple_cloud_notes_parser), in order to decrypt the note using the `NoteStore.sqlite` file:

```sh
[skat@anubis:~/dl/apple_cloud_notes_parser] $ echo "su5Syb@k4" > password.txt
[skat@anubis:~/dl/apple_cloud_notes_parser] $ ruby notes_cloud_ripper.rb -f ~/work/UIUCTF/private/var/mobile/Containers/Shared/AppGroup/4DCEB2C7-5420-4446-9111-4091A929B4AC/NoteStore.sqlite -w password.txt

Starting Apple Notes Parser at Wed Aug 4 13:27:05 2021
Storing the results in ./output/2021_08_04-13_27_05

Created a new AppleBackup from single file: /home/skat/work/UIUCTF/private/var/mobile/Containers/Shared/AppGroup/4DCEB2C7-5420-4446-9111-4091A929B4AC/NoteStore.sqlite
Guessed Notes Version: 14
Added 1 passwords to the AppleDecrypter from password.txt
Updated AppleNoteStore object with 1 AppleNotes in 2 folders belonging to 1 accounts.
Adding the ZICNOTEDATA.ZPLAINTEXT and ZICNOTEDATA.ZDECOMPRESSEDDATA columns, this takes a few seconds

Successfully finished at Wed Aug 4 13:27:05 2021
```

Great, it looks like we were able to successfully decrypt the note! Let's check out the data:

```sh
[skat@anubis:~/dl/apple_cloud_notes_parser] $ cd output/2021_08_04-13_27_05/
[skat@anubis:~/dl/apple_cloud_notes_parser/output/2021_08_04-13_27_05] $ ls
csv html debug_log.txt NoteStore.sqlite
[skat@anubis:~/dl/apple_cloud_notes_parser/output/2021_08_04-13_27_05] $ tree
.
├── csv
│   ├── note_store_accounts_1.csv
│   ├── note_store_cloudkit_participants_1.csv
│   ├── note_store_embedded_objects_1.csv
│   ├── note_store_folders_1.csv
│   └── note_store_notes_1.csv
├── debug_log.txt
├── html
│   └── all_notes_1.html
├── NoteStore.sqlite
└── output.dat

2 directories, 9 files
```

It looks like `NoteStore.sqlite` is the original encrypted note store, and `csv` and `html` are directories containing the decrypted output. Let's have a look at `all_notes_1.html` by opening it up in a web browser:

![](https://irissec.xyz/uploads/2021-08-07/all_notes_1.png)

We've successfully uncovered what Red and Blue are plotting and in the process, we captured the flag!

### Debriefing

We were initially given new information and told that Red had been communicating with another impostor; our objective was to find out who Red and this other impostor were plotting. Looking through Red's SMS text messages on the iPad device, we learned that they agreed with this new party to communicate via Discord. Looking through the cached application data from the Discord app present on Red's device, we discover that the other impostor is Blue and they sent Red an encrypted note with the password given in the Discord chat. Upon locating and decrypting the encrypted note from the device's note store, we uncover what Red and Blue were plotting.

This was great and absolutely something that I can see mirroring a real-life scenario! I can say for a fact that I've asked people before to switch from SMS to another service such as Discord since it's easier for me to type from a keyboard. I can also say for a fact that I've discovered a mother lode of data in saved application data and cached application data, and that's something you can expect from real-life scenarios as well! By understanding our situation well and by orienting ourselves at every step, we were able to much more precisely and intently execute our investigation.

Original writeup (https://irissec.xyz/articles/categories/forensics/2021-08-07/Performing-Digital-Forensics-on-an-Apple-Tablet-to-Recover-Evidence).