*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).