CTF

Cellebrite CTF 2020: Juan Mortyme

 · 14 mins read

TL;DR: Breakdown of our answers to Juan Mortyme’s questions from the Cellebrite 2020 CTF using only free, open source tools.

Background

Cellebrite just finished up its first Capture the Flag (CTF) event, running from October 26, 2020 through October 29, 2020. The introductory information about our team’s participation in that event can be found here, specifically it links to many of the free, open-source tools we used, which is worth a read to understand the commands you may see below. This post focuses on the questions relating to Juan Mortyme.

Phone Information - 20 points

What is the owner’s mobile phone number (10 or 11 digits only)

There are a few ways to answer this question, but because we were already reviewing Juan’s texts due to a question in Rene’s section, we got it from there. I used sqlitebrowser to open private/var/mobile/Library/SMS/sms.db where iOS stores your text messages and simply eyeballed1 the chat table becuase I was reading them. In that table, the last_address_handle is always +16095299858 and the rows where account_login is filled in for iMessage have P:+16095299858. Both of those facts indicate his phone number was “16095299858”, which is the correct answer.

If you weren’t already poking in that database visually, or happened to be SSH’d into your storage server and didn’t want to copy 30GB over the network, you could use sqlite3 to snag it directly from the command line:

[notta@cuppa jm]$ sqlite3 private/var/mobile/Library/SMS/sms.db \
'SELECT DISTINCT account_login FROM chat WHERE account_login LIKE "P:%"'

P:+16095299858

Location Address - 10 points

What is the owner’s home street name (just the street name, NO home address number, NO city, NO state, just street name)

Most users tell their smart device everything, so we opted for the easy answer and checked Apple Maps. Sure enough, Juan told Apple Maps where both his home and work were. You find this information in the NSKeyedArchive2 located at /private/var/mobile/Library/Caches/com.apple.Maps.Suggestions/MapsSuggestionsManager_Maps.storage.

[notta@cuppa mobile]$ ruby ~/ruby/bplister/nskeyed_archive_list.rb \
Library/Caches/com.apple.Maps.Suggestions/MapsSuggestionsManager_Maps.storage

...

         "mseSSI"=>
          {"NS.keys"=>
            ["MapsSuggestionsShortcutContactsKey",
             "MapsSuggestionsLongitudeKey",
             "MapsSuggestionsGEOMapItemOriginKey",
             "MapsSuggestionsOriginalPositionKey",
             "MapsSuggestionsPrimaryKey",
             "MapsSuggestionsShortcutOnlyKey",
             "MapsSuggestionsRemovalBehaviorKey",
             "MapsSuggestionsAlreadyLocalizedKey",
             "MapsSuggestionsShortcutSetupKey",
             "MapsSuggestionsNeedsDistanceTrackingKey",
             "MapsSuggestionsSinkRankKey",
             "MapsSuggestionsNeedsETATrackingKey",
             "MapsSuggestionsLatitudeKey",
             "MapsSuggestionsUserPlacePK",
             "MapsSuggestionsEntryTitleNameKey",
             "MapsSuggestionsAlreadyThereKey",
             "MapsSuggestionsShortcutPositionKey"],
           "NS.objects"=>
            [{"NS.objects"=>[],
              "$class"=>
               {"$classname"=>"NSArray", "$classes"=>["NSArray", "NSObject"]}},
             -122.1249197,
             6,
             0,
             "MapsSuggestionsUserPlacePK",
             true,
             0,
             true,
             false,
             false,
             0,
             true,
             47.6489619,
             "16133 NE 44th Ct, Redmond",
             "Home",
             true,
             0],
...

Note that the address is in the object corresponding to the MapsSuggestionsUserPlacePK key3 when the MapsSuggestionsEntryTitleNameKey key is Home. In this case, we wanted just the street name, so our answer was “NE 44th Ct”.

Activation - 20 points

When was the phone first activated (after a wipe) format: MM-DD-YYYY

This is a really easy one if you only remember that the /private/var/root/.obliterated file is the answer. This file exists when the phone is wiped and tells you when.

[notta@cuppa jm]$ ls -al private/var/root/.obliterated

-rw-r--r-- 1 notta notta 0 Apr 23  2020 private/var/root/.obliterated

As shown above, the answer was “04-23-2020”.

Vehicle - 20 points

Name a vehicle make of which the device was connected to

I actually did some research into cars, bluetooth, and the “security” therein a decade and a half ago, so this one was near and dear to my heart. We found the answer by looking at com.apple.MobileBluetooth.devices.plist to see what cars it had connected to. But since this is iOS, we don’t have the full path right off the bat, right? Do not fear, find is here! With no other arguments, find will spit out a recursive listing of all files beneath your current location which, when paired with grep can let you bring specific files of interest to the top, even if you only know part of the name. Once we use that, we’ll have the exact path, even if we haven’t bothered to look at application container names.

We then used BPlister again to parse that binary plist and paired it with grep to find just the names. Why the names? Because most devices default to naming themselves what they are and we might as well try for an easy answer.

[notta@cuppa jm]$ find | grep com.apple.MobileBluetooth.devices.plist

./private/var/containers/Shared/SystemGroup/F710384D-B640-420A-AA08-B55E0BBF3EE9/Library/Preferences/com.apple.MobileBluetooth.devices.plist

[notta@cuppa jm]$ cd ./private/var/containers/Shared/SystemGroup/

[notta@cuppa SystemGroup]$ ruby ~/data/programming/ruby/bplister/plist_parse.rb \
F710384D-B640-420A-AA08-B55E0BBF3EE9/Library/Preferences/com.apple.MobileBluetooth.devices.plist \
| grep Name

   "DefaultName"=>"Handsfree",
   "Name"=>"MY LEAF",
   "Name"=>"MyPad",
   "DefaultName"=>"PDA",
   "DefaultName"=>"Handsfree",
   "Name"=>"MB Bluetooth",
   "DeviceCarVendorName"=>"Mercedes-Benz",
   "Name"=>"PLT V5200 Series",
   "DefaultName"=>"Headset",
   "Name"=>"PLT V5200 Series",
   "DefaultName"=>"Headset",
   "UserNameKey"=>"Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah ",
   "Name"=>"Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah ",
   "DefaultName"=>"Headphones"

In the above names, MY LEAF stands out, as the Leaf is made by Nissan. Submitting “Nissan” confirmed that is an answer (although there may be others).

Location Details - 20 points

In which city is the favorite starbucks located?

I very quickly found the Starbucks application folder with two grep commands, the first being a recursive (-r) grep looking for the word “starbucks” and that was piped into the second looking explicitly for the container manager files. This is the way I find any specific application I am looking for on iOS, as it ends up being quicker than opening the relevant SQLite database (/private/var/mobile/Library/FrontBoard/applicationState.db), querying it across two tables, reading a binary plist and spitting back an answer.

Could I automate it? Sure. Do I want to recreate what grep can do so very well? Not a chance.

[notta@cuppa Applications]$ grep -r starbucks \
| grep .com.apple.mobile_container_manager.metadata.plist 

Binary file 753DD1A6-A70A-48D9-85F1-F9B56DE7CBDA/.com.apple.mobile_container_manager.metadata.plist matches

However, I went through a lot of that application and didn’t find a correct location. Naturally, that led me to back up a level and try harder4. Once I tried a larger recursive grep, I found a file I hadn’t been expecting to pop up, /private/var/mobile/Library/Passes/passes23.sqlite which is related to Apple Pay. As I eyeballed that database in SQLite Browser, the location table listed an address in Baltimore under relevant_text: 100 E. Pratt Street. One Google Search later showed that had a Starbucks cafe, so we tried “Baltimore” as our answer and boom goes the dynamite.

Daytrip - 20 points

What did I pick up from Montana

This answer just goes into the “know the dataset” bucket. After pawing through Juan for some time looking for other answers, I knew he had pictures of a boat, I knew he’d been searching for boat-related items, and I thought he had visited a boat dealer. While working on other things, I simply tried the word “boat” and it was correct. There is likely a more technical answer, but it wasn’t needed if you got to know Juan a bit.

Printing - 20 points

On a document printed from this device, what is the 2nd word on the 3rd line.

When iOS devices print, they keep a copy in /private/var/mobile/Library/com.apple.printd/. For this answer, you just have to go into that folder and open the only PDF, which happened to be an Apple Note. The second word on the third line was “delete”, so we entered that as the flag and capped. Note, I’m not giving logs of things like this, because you can do it on the command line with ls or you could open a file browser, either way works just as well.

Photo analysis - 20 points

Find the following photo: [snip] Analyze and determine the offset from UTC, enter numerics only (without UTC and no +/- for example: 2)

We initially saw this file on Rene’s phone having been passed over WhatsApp (/data/data/media/0/WhatsApp/Media/WhatsApp Images/IMG-20200515-WA0003.jpg) so we knew the date it had to have been created by. It may have been created much earlier, but it certainly had been created on or before May 15, 2020. This let us go into Juan’s camera folder (/private/var/mobile/Media/DCIM/100APPLE) to better search for the image. We could scroll through, but wanted a quicker answer, so we again started with a few commands to give a smaller pool.

Specifically, we used stat with a custom output format (-c '%n %y' *) to display on each line the image’s name and its modify time, which we piped to grep to look for the date in question.

[notta@cuppa 100APPLE]$ stat -c '%n %y' * | grep 2020-05-15

IMG_0364.PNG 2020-05-15 13:03:24.000000000 -0400
IMG_0365.PNG 2020-05-15 13:04:21.000000000 -0400
IMG_0366.JPG 2020-05-15 13:05:13.000000000 -0400
IMG_0375.MP4 2020-05-15 16:44:12.000000000 -0400
IMG_0401.MP4 2020-05-15 16:44:12.000000000 -0400

In reviewing these five images, IMG_0366.JPG was the image in question and the one immediately preceeding it was a screenshot which had the current time (10:04) in it. Because only a minute separated the two image times, we thought that previous image was a safe representation of the offset from that image. From that point, it is basic math to say 13 - 10 is 3. You then add 4 to the answer for the offset from my local machine (-0400) to get an offset of “7”. This was the correct answer.

(Audio) Recording Location - 50 points

There are multiple (Audio) Recordings, created by the user - on the device, a few of them are associated with different airports locations. Name the ICAO code of either one of the airports (format has 4 characters for example CYYZ for Toronto Pearson airport)

I started by doing this the dumb way and listened to the recordings. They are found in /private/var/mobile/Media/Recordings/ and I should have known better than to think they’d accidentally leave the location in. Next I eyeballed the CloudRecordings.db SQLite database in the same folder. I already knew a sample file which had airport-sounding stuff in it (20200705 134412.m4a) and when I looked at the ZCLOUDRECORDING table I found this filename in the ZPATH column and a ZCUSTOMLABEL column of “Seattle-Tacoma International Airport 2”. Looking up the ICAO code of that airport gave the result “KSEA”. This was a valid answer.

IP Address - 50 points

What is the IP Address the device was associated with - while connected to the WiFi network on August 14, 2020? (Standard IP Address format for example: 10.1.123.11)

The right answer from this question would be to check /private/var/preferences/SystemConfiguration/com.apple.wifi.plist to determine which network the user was on and make sure you’re matching up the right things. However, there’s only one possible Dynamic Host Configuration Protocol (DHCP)5 lease in /private/var/db/dhcpclient/leases/ to match it up to and according to stat it was on 14 August, so why don’t we just try that directly?

[notta@cuppa jm]$ ls -al private/var/db/dhcpclient/leases/

total 12
drwx------ 2 notta notta 4096 Aug 14 17:47 .
drwx------ 3 notta notta 4096 Jul 20 18:51 ..
-rw-r--r-- 1 notta notta  969 Aug 14 17:47 en0-1,f0:98:9d:8:6b:6

[notta@cuppa jm]$ cat private/var/db/dhcpclient/leases/en0-1\,f0\:98\:9d\:8\:6b\:6

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>IPAddress</key>
  <string>192.168.1.98</string>
  <key>LeaseLength</key>
  <integer>86400</integer>
  <key>LeaseStartDate</key>
  <date>2020-08-14T21:47:03Z</date>
  <key>PacketData</key>
  <data>
  AgEGAJMtYaUAAgAAAAAAAMCoAWIAAAAAAAAAAPCYnQhrBgAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABjglNjNQEFNgTAqAABMwQAAVGA
  AQT///wAAwTAqAABBgTAqAABDwtsb2NhbGRvbWFpbncNC2xvY2FsZG9tYWluAP8=
  </data>
  <key>RouterHardwareAddress</key>
  <data>
  gCqoTLHY
  </data>
  <key>RouterIPAddress</key>
  <string>192.168.0.1</string>
  <key>SSID</key>
  <string>UniFi</string>
</dict>
</plist>

In this output you can see the device’s IP address 192.168.1.98 and the Router’s IP address for the UniFi network. These would be good values to match to the Wi-Fi plist if you had a lot of networks and interfaces going on. But in this case, “192.168.1.98” is the only answer.

Financial Situation - 100 points

In a financial app there is still a $ balance - what is that amount? (full amount with pennies for example: 12.34)

Having looked at other devices already, I suspected it was the cash app, but did not know what the application folder was. As we saw earlier, two greps very easily gets us that answer (64F77221-3E98-46DF-A9FA-2B8229652965). Part of poking around in any application is looking at the screenshots it creates when you navigate to another app in Library/SplashBoard/Snapshots/. After I had spent a lot of time looking elsewhere, I took a peek at the two screenshots in this application and saw the answer.

[notta@cuppa Application]$ python ~/MacForensics/IOS_KTX_TO_PNG/ios_ktx2png.py \
64F77221-3E98-46DF-A9FA-2B8229652965/Library/SplashBoard/Snapshots/sceneID:com.squareup.cash-default/6C4FD907-F6BC-4272-98FF-1C5CF458DFE1@3x.ktx

Conversion successful!
Output is at 6C4FD907-F6BC-4272-98FF-1C5CF458DFE1@3x.ktx.png

Viewing that file in your favorite image viewer shows a balance of $2.29, making the answer “2.29”.

Conclusion

All of Juan’s questions were very doable with free, open-source tools. You’ll notice I didn’t list iLEAPP in any of the above, but the truth is it helps on many of them. However, since iLEAPP is presenting analyzed information to you and you still need to understand the guts of what is going on, I opted to show answers more the command line itself6.

Footnotes

  1. My meaning of eyeballed isn’t too sophisticated, I start with the first table and go through to the last, getting a quick idea of what each has in it. Depending on your knowledge of the application, you might be able to do this purely from the schema of the table, but I like seeing the actual contents (“BLOB” can mean so very much). 

  2. If you’d like to know more about NSKeyedArchives and how to read them, check out the second section in this post

  3. I’ve been meaning to make BPlister a bit more recursive, it still needs some work. 

  4. Anytime I feel I’m not trying hard enough, it likely stems from not trying enough answers quickly enough. 

  5. Most users connect to most wireless networks using a DHCP-set IP address, so this seems like the most likely answer, even if the user might have set their own IP address. 

  6. Plus I’m sure many others will be showing how and where iLEAPP can be useful for these questions.