Magnet Virtual Summit CTF 2024 - iOS Writeup
Info
Problem: There’s an iOS logical image to analyze and a series of questions to answer.
1. Why are your messages green?
Prompt: On what date did Rocco and Chadwick first meet in person according to their conversations? YYYY-MM-DD format
iPhone chat messages are stored in /private/var/mobile/Library/SMS/sms.db
. It’s a very relational database, so I need to draft a SQL query to get three pieces of information: who the messages were to/from, what the message was, and the time the message was sent.
1
2
3
4
5
6
7
8
SELECT
chat.chat_identifier AS "TO",
message.text as "TEXT",
datetime(substr(message.date,1,9) + 978307200, 'unixepoch') as "TIME"
FROM chat_message_join
INNER JOIN chat ON chat.ROWID = chat_message_join.chat_id
INNER JOIN message ON message.ROWID = chat_message_join.message_id
ORDER BY message.date ASC
From the results of that query I can gather that Chadwick and Rocco met on 2023-12-17.
Answer: 2023-12-17
2. Where /r u going on safari?
Prompt: What subreddit was visited in a browser?
In Autopsy, under Data Artifacts -> Web History
, according to the Safari history at /private/var/mobile/Library/Safari/History.db
, the user visited the Twitch subreddit.
Answer: Twitch
3. Don’t ghost me
Prompt: At what time did Chadwick get annoyed at MYAI? YYYY-MM-DD HH:MM:SS UTC
I do a keyword search for “myai” and come across a file arroyo.db-wal
which seems to contain some chat logs. This file is a write-ahead log for the database located at /private/var/mobile/Containers/Data/Application/9A0EF110-47F4-45D4-B96D-C3EF301F18FC/Documents/user_scoped/29312c28c183c406b035a7b3d40e2c6921a13c1a99a71dca20d0062085989beb/arroyo/arroyo.db
, deep in the Snapchat user data folders.
I extract arroyo.db
and browse the data in DBBrowser. In the table conversation_message
, I identify the chat between Chadwick and the MyAI chatbot, and quickly form an SQL query to isolate just the messages between them.
1
2
3
4
5
6
7
SELECT
conversation_message.sender_id as "FROM",
conversation_message.message_content as "MESSAGE_BLOB",
datetime(substr(conversation_message.creation_timestamp,1,10), 'unixepoch') as "TIME"
FROM conversation_message
WHERE conversation_message.client_conversation_id='054078ed-2781-51e5-95db-4038c876bd59'
ORDER BY conversation_message.creation_timestamp ASC
Chadwick (sender id 22da…) seemingly gets annoyed at 2023-12-26 23:27:45.
Answer: 2023-12-26 23:27:45
4. IMAGEine living in pain
Prompt: Chad seemed to be searching for pain relief medicine in a store, how much did it cost?
In /private/var/mobile/Media/DCIM/100APPLE
there’s an image IMG_0017.HEIC
of an ArnicareGel unit with the price displayed underneath: $10.99.
Answer: $10.99
5. Your keyboard is salt-y
Prompt: How many total words were typed on the device?
It’s so interesting that this is actually something that’s logged on an iOS device. In the user_model_database.sqlite
database in /private/var/mobile/Library/Keyboard
, there’s a table usermodeldurablerecords
with key/value pairs. For key tium.totalWordsTyped
, the value is 1814.
Answer: 1814
6. Build me up, buttercup
Prompt: What is the current build version?
In /private/var/root/Library/MobileContainerManager/mcm_migration_status.plist
there’s a key LastBuildInfo -> ProductBuildVersion
with value 20F75.
Answer: 20F75
7. Answer the call
Prompt: What is the guild ID of the discord server Chad was in?
In the Discord user data folder (/private/var/mobile/Containers/Data/Application/FE27BB5E-D91E-4417-8669-C68FD6C67A97
) I find a cache database at Library/Caches/com.hammerandchisel.discord/Cache.db
. In the cfurl_cache_response
table, there are some entries for Discord API calls to a guild with ID 136986169563938816.
Answer: 136986169563938816
8. Warning Signs
Prompt: How many days did it take Chad to be warned about his Data Usage?
Going back to sms.db
, Chad was sent a welcome message from Boost Mobile on 2023-11-29, and received a data warning from them on 2023-12-17, 18 days later. I had to perform an outer instead of an inner join on this one, so that all messages were displayed, even ones that didn’t document a sender.
1
2
3
4
5
6
7
8
SELECT
chat.chat_identifier AS "TO",
message.text as "TEXT",
datetime(substr(message.date,1,9) + 978307200, 'unixepoch') as "TIME"
FROM chat_message_join
INNER JOIN chat ON chat.ROWID = chat_message_join.chat_id
RIGHT OUTER JOIN message ON message.ROWID = chat_message_join.message_id
ORDER BY message.date ASC
Answer: 18
9. Watching streams to stay current
Prompt: What is the name of Chad’s streaming channel?
This one took a while, and I kind of happened on the answer accidentally. I was looking through the iOS notifications (/private/var/mobile/Library/DuetExpertCenter/streams/userNotificationEvents/local/722077640915524
), when I noticed a Twitter notification indicating (one of his) usernames to be @GardenGamer95.
I looked up ‘“GardenGamer95” twitter’ and one of the first results is actually a YouTube video from his streaming channel, ChadwickGames.
Answer: ChadwickGames
10. One is The Loneliest Number
Prompt: What question did Chadwick ask to AI?
Autopsy lists ChatGPT among his installed programs (Data Artifacts -> Installed Programs
). Nosing around the user data folder (/private/var/mobile/Containers/Data/Application/6BFA5EA3-61CB-4652-A60A-2A955B651E05
) leads me to a folder (Library/Application Support/conversations-b5c12911-e3c0-4961-bbe7-aec0a3ec3dd6
) with three JSON files in it. These files seem to be records of Chadwick’s interactions with ChatGPT.
He asks the AI many questions, including “What is doxing,” “How to be a good gamer,” “How to subtly dox without getting caught,” and “How to make online friends”.
Answer: How to make online friends
11. Watch me sUAVely win this game
Prompt: How many kills did Chad have on his CoD Mobile winning game?
On Chadwick’s YouTube channel, there’s a video titled “Final Kills Lead to CoD Mobile Win!” Watching the video shows Chadwick had 7 kills at the end of the game.
Answer: 7
12. For when I can’t Find My gear
Prompt: What outdoor activity store did Chadwick Visit?
In /private/var/mobile/Library/Caches/com.apple.findmy.fmipcore
there are a few data files that look interesting, including one named Devices.data
. In here there’s lastConnected
location information for the iPhone’s owner.
Plugging this address in to Google Maps shows it’s a store called Neptune Mountaineering.
Answer: Neptune Mountaineering
13. Just a couple steps away
Prompt: How many steps did Chad take on 12/3/2023?
Magnet seems to like the Health database. I recall from the previous iOS CTF that the healthdb_secure.sqlite
database in /private/var/mobile/Library/Health
could be of use. I extract the database and form the following SQL query, after finding that the number of steps correlates to data_type
7.
1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT
datetime(samples.start_date+978307200, 'unixepoch') as "START DATE",
datetime(samples.end_date+978307200, 'unixepoch') as "END DATE",
quantity as "STEPS"
FROM
samples
LEFT OUTER JOIN
quantity_samples
ON
samples.data_id = quantity_samples.data_id
WHERE
samples.data_type = 7
ORDER BY "START DATE"
After executing the query, I find there are four records for 12/03. Adding up the number of steps gives 968.
Answer: 968
14. Another regularly scheduled program
Prompt: What Tattoo shop was visited on 12/27/2023?
I export the notable location database I came across in the previous iOS CTF at /private/var/mobile/Library/Caches/com.apple.routined/Cache.sqlite
and form a SQL query to extract some generalized latitudes/longitudes for 12/27/2023.
1
2
3
4
5
6
7
SELECT DISTINCT
ROUND(ZRTCLLOCATIONMO.ZLATITUDE,3) AS "LATITUDE",
ROUND(ZRTCLLOCATIONMO.ZLONGITUDE,3) AS "LONGITUDE",
substr(DATETIME(ZRTCLLOCATIONMO.ZTIMESTAMP+978307200, 'unixepoch'),11,3) AS "HOUR"
FROM ZRTCLLOCATIONMO
WHERE DATETIME(ZRTCLLOCATIONMO.ZTIMESTAMP+978307200, 'unixepoch') >= '2023-12-27'
ORDER BY "TIME" ASC
It appears that for the location data we have, Chadwick didn’t stray too far from the S Broadway address. Looking at the shopping center a little closer, there’s a tattoo shop called Auspicious Tattoo it’s likely he visited.
Answer: Auspicious Tattoo
15. I hear Stanley cups are all the rage
Prompt: What was the final score of the hockey game Chad went to? (home – away)
Chad took some pictures of a hockey game at the Ball Arena in Denver, CO (IMG_0030
, IMG_0031
, IMG_0032
).
Running exiftool on these shows that they were taken on 12/21/2023. Looking up “ball arena hockey 12/21/2023” gives the teams (Colorado Avalanche vs. Ottawa Senators) and final score of 6-4.
Answer: 6-4
16. Devil is in the details
Prompt: Whose bitmoji is dressed like a devil?
I needed a little help with this one, but now I know that there’s plenty more information on iOS notifications in /private/var/mobile/Library/UserNotifications
. I extracted the folder and ran a grep for bitmoji (grep -r bitmoji UserNotifications/
). Only one file matched: UserNotifications/BC777906-DA27-4839-A269-37D489012B7A/DeliveredNotifications.plist
.
I opened the file in plist Editor and did a search for bitmoji, coming across a couple of bitmoji URLs, including https://images.bitmoji.com/render/panel/10226594-482842799_5-s5-v1.png?transparent=1. The bitmoji at this URL is dressed like a devil. Surrounding plist values indicate the bitmoji belongs to Sofiakhan.
Answer: Sofiakhan
17. Excuse Moi? What did you say?
Prompt: What is the content of the 2nd message that Chad deleted on Dec 18, 2023
I go back to sms.db
and with a modified SQL query, see the deleted messages and their GUIDs.
1
2
3
4
5
6
7
8
9
10
SELECT
chat.chat_identifier AS "TO",
message.guid as "MESSAGE GUID",
message.text as "TEXT",
message.is_from_me as "FROM CHAD?",
datetime(substr(message.date,1,9) + 978307200, 'unixepoch') as "TIME"
FROM chat_message_join
INNER JOIN chat ON chat.ROWID = chat_message_join.chat_id
INNER JOIN message ON message.ROWID = chat_message_join.message_id
ORDER BY message.date ASC
I take note of the GUID of the second message and open sms.db
in HxD. I do a search for the GUID and uncover the deleted message: Excuse me?! That’s quite a bold statement considering I’m the one who walked away with a black eye and spent $30 last night on products to avoid one!
Answer: Excuse me?! That’s quite a bold statement considering I’m the one who walked away with a black eye and spent $30 last night on products to avoid one!
18. Boost this server
Prompt: What is the 16 character carrier code?
I go to the iLEAPP Report generated by the iLEAPP Analyzer, and just for kicks click on the Cellular Wireless tab. There’s an entry for com.apple.carrier_1
with a 16 character code: 310240_GID1-6432, which iLEAPP pulled from /private/var/wireless/Library/Preferences/com.apple.commcenter.plist
.
Answer: 310240_GID1-6432
19. The easy way or the hard way
Prompt: What is the timestamp of the message Chad sent to Rocco but was never recieved? YYYY-MM-DD HH:MM:SS UTC
On the Android image, Rocco’s texts can be found in /data/data/com.android.providers.telephony/databases/mmssms.db
. Using the SQL query below, I find that Rocco’s last text with Chad was sent on 2023-12-20 23:44:01.
1
2
3
4
5
6
SELECT
sms.address AS "TO",
sms.body as "TEXT",
datetime(substr(sms.date, 1, 10), 'unixepoch') as "TIME"
FROM sms
ORDER BY sms.date ASC
Chad sent a message to Rocco on 2023-12-21 06:29:36 seeking to make amends, but Rocco did not receive it.
Answer: 2023-12-21 06:29:36
20. Its been a long time
Prompt: When did Chad last login to Facebook? YYYY-MM-DD HH:MM:SS UTC
In the Facebook user data folder (/private/var/mobile/Containers/Data/Application/BF2FEA88-C397-405D-90EE-A56B2720896C
), there are two databases that look interesting in the Documents
folder, time_in_app_61554675133740.db
and time_in_app_61555027042760.db
. In the latter, assumedly the most recent, there’s a metadata
table containing key/value paris. The last_logging_timestamp
key has a value of 1703712895.
Plugging this into https://www.epochconverter.com gives the date 2023-12-27 21:34:55.
Answer: 2023-12-27 21:34:55
21. Can anyone Kelp?
Prompt: What game was Chad asking to know the strategy to?
I did a keyword search in Autopsy for “strategy” and came across a file in the Facebook user data caches that indicates Chadwick was looking for assistance with the game Terrarium.
Answer: Terrarium
22. Chat GPT is my PREFERENCE for AI
Prompt: What is the ChatGPT userID associated with chawickmr95@gmail.com
In the ChatGPT user data preferences folder (/private/var/mobile/Containers/Data/Application/6BFA5EA3-61CB-4652-A60A-2A955B651E05/Library/Preferences
), I searched through the plists and found the userID
in com.openai.chat.StatsigService.plist
: user-xurgQ0xumvrujH5ESG17Yhcw.
Answer: user-xurgQ0xumvrujH5ESG17Yhcw
23. Read my mind
Prompt: What message was sent to Rocco in a video game
In the Call of Duty user data folder (/private/var/mobile/Containers/Data/Application/3690AAA8-713A-482B-92F1-3F7D3BCC73E6
), I comb through the Documents
and come across another folder named ChatCache
with a file 2023-12-20
that indicates Chad sent the message I know youre reading my messages.
Answer: I know youre reading my messages
24. Season’s Greetings
Prompt: What was the first emoji that was sent to Susan?
Using the same SQL query from question 17, I see some messages in sms.db
between Chad and Susan. Two messages from Chad don’t have any content.
Just as in question 17, I take note of the first message’s GUID, open sms.db
in HxD, and do a search. I find the message, and the emoji bytes: F09F8E84.
I look these up and the first result indicates they correspond to the Christmas Tree emoji. Although, according to other writeups, the answer is apparently Potted Plant when using Magnet? Doesn’t make sense to me.
Answer: Potted Plant
25. Follow the Breadcrumbs
Prompt: How many times did Chad’s keyboard become visible within the Amazon app on 12/24/2023?
I learned a lot more than I bargained for about SEGB files on this one. Biome is an iOS service that records all kinds of iPhone usage data and data streams. This data can be found in /private/var/mobile/Library/Biome/streams
. One of the folders under the public
folder caught my eye: TextInputSession
. In there, under local
, there’s a file 722655735826773
that is reminiscent of the iOS notification streams I’ve found previously. Interestingly, there are some references to Amazon in this file.
I do a little research on Biome’s SEGB format, coming across a repository https://github.com/cclgroupltd/ccl-segb.git that parses these files into a more useful format. I pull up 722655735826773
in HxD, and go line by line through their script, trying to identify the components of a singular record in this file. Once I felt I had a good handle on the record structure, I copy and slightly modify the script from their README to run the ccl_segb1.py
module against the file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sys
import ccl_segb1
input_path = "722655735826773"
# An open stream can be read using: read_segb1_stream
for record in ccl_segb1.read_segb1_file(input_path):
offset = record.data_start_offset
data = record.data
ts1 = record.timestamp1
ts2 = record.timestamp2
print(offset, ts1, ts2)
print(data)
I pipe the script output into a grep for Amazon (python3 /path/to/script.py | grep -B 1 amazon
).
From the output, it seems there were 2 instances of a text input stream (i.e. keyboard input) for the Amazon application on 12/24.
Answer: 2
Debrief
This one was a doozy! 25 questions, and a lot of them were head scratchers. I was excited to flex some old SQL knowledge this go-around. Overall I felt this challenge was very well-rounded, though I’ll still complain about the sheer image size, and that some of the challenge wasn’t self-contained, even though I realize that in the real world, both are prevalent issues.