Performing Digital Forensics on an Apple Tablet to Recover Evidence
Categories: forensics
2021-08-07
by skat
This writeup is also readable on my GitHub repository and personal website.
Last weekend, I had the pleasure of competing at UIUCTF 2021 hosted by the University of Illinois at Urbana-Champaign. This was my team’s second time competing at UIUCTF and we scored 3rd out of 658 teams this year. It was a great experience and I learned a lot while also having a lot of fun hacking with my team! I think we really pulled it together and everyone gave 110% this event, and I’m of course always grateful to be surrounded by such talented individuals that I can learn from.
This writeup is for a pair of challenges that I did called forensics/Tablet 1 and forensics/Tablet 2 authored by WhiteHoodHacker of UIUC’s SIGPwny. For someone who specializes in network security and digital forensics, I think that this is actually the first time I’ve ever written a forensics writeup! These challenges involved performing digital forensics on an Apple tablet to recover evidence and investigate an individual.
Contents
forensics/Tablet 1
Challenge written by WhiteHoodHacker.
Red has been acting very sus lately… so I took a backup of their tablet to see if they are hiding something!
It looks like Red has been exfiltrating sensitive data bound for Mira HQ to their own private server. We need to access that server and contain the leak.
Files: tablet.tar.gz
Checksum (SHA-1):
To preface, I just want to say that this was a really fun challenge! It involves performing digital forensics on an Apple tablet using a given filesystem backup, investigating a (fictional) target, interacting with SQLite databases used by some common applications, and using evidence discovered through the forensics process to hack into another server. The ideas and procedures explored are pretty neat and are definitely something that would be encountered in real-life operations!
Let’s first start off the challenge by getting oriented. Based off of the challenge description, we have a few key pieces of information:
- Our target is “Red.”
- We have a backup of Red’s tablet.
- Red exfiltrated data to a private server.
- Our objective is to gain control of this server and contain the leak.
The file that we’re given is a .tar.gz
file, meaning that it’s a tarball that’s been gzipped; a tarball is a file format that combines multiple files into a single file, and gzip is a file compression format. We can decompress the gzip and extract the files from the tarball using the gunzip
and tar
utilities, additionally passing xf
(extract file) to tar
as a command line argument:
We can see that we’ve extracted a new directory: private/
. We can get oriented by exploring the directory; a great utility is tree
, which will display a tree structure of the filesystem starting from your current active directory if no additional arguments are supplied.
[skat@anubis:~/work/UIUCTF] $ cd private/
[skat@anubis:~/work/UIUCTF/private] $ ls
var
[skat@anubis:~/work/UIUCTF/private] $ cd var/
[skat@anubis:~/work/UIUCTF/private/var] $ ls
buddy empty hardware iomfb_bics_daemon Keychains logs mobile MobileSoftwareUpdate networkd protected run tmp
containers folders installd keybags log 'Managed Preferences' MobileDevice msgs preferences root staged_system_apps wireless
[skat@anubis:~/work/UIUCTF/private/var] $ tree
.
├── buddy
├── containers
│ ├── Data
│ │ └── System
│ │ ├── 0484B045-1EFD-4EC1-9B74-3E7665974A42
│ │ │ ├── Documents [error opening dir]
│ │ │ ├── Library [error opening dir]
│ │ │ └── tmp [error opening dir]
│ │ ├── 1CB5E5B6-3849-4CA0-8DCD-BF5A521286B9
│ │ │ ├── Documents [error opening dir]
│ │ │ ├── Library [error opening dir]
│ │ │ └── tmp [error opening dir]
│ │ ├── 28841A8D-11F8-4013-8D5D-B02B63B944F4
│ │ │ ├── Documents [error opening dir]
│ │ │ ├── Library [error opening dir]
│ │ │ └── tmp [error opening dir]
│ │ ├── 2FD33CE1-DDAD-4FEC-A4F5-55144CBA75EB
│ │ │ ├── Documents [error opening dir]
│ │ │ ├── Library [error opening dir]
│ │ │ └── tmp [error opening dir]
│ │ ├── 416AB7BD-5EC1-4075-9704-44048CF01074
│ │ │ ├── Documents [error opening dir]
│ │ │ ├── Library [error opening dir]
│ │ │ └── tmp [error opening dir]
│ │ ├── 4BA18D97-352A-475E-9C22-36315FF4C868
│ │ │ ├── Documents [error opening dir]
│ │ │ ├── Library [error opening dir]
-- snip --
Whoa, that’s a lot of information to take in! In fact, tree
’s output goes on for 12,541 lines in total. This is common in any digital forensics investigation: you’re given such a great quantity of information that it would be impractical to go through every single one individually, so you must be smart and precise about how you select certain pieces of evidence such that you make good use of your time while also not compromising the value of the information that you do uncover. We’ll get back to this in a moment.
Before we dig too deep into the investigation itself, we can see that there are a bunch of errors in opening up directories according to the output of tree
. Let’s select a sample one, ./containers/Data/System/0484B045-1EFD-4EC1-9B74-3E7665974A42/Documents/
, and find out why we’re having trouble opening up these directories:
Of course we can’t access anything – the modes are insufficient for read access! In fact, we have neither read, write, nor execution permissions. We can solve all of these problems at once by giving ourselves all three permissions for all files and directories in the backup, which can easily be done recursively with chmod -R
:
[skat@anubis:~/work/UIUCTF/private/var] $ chmod -R 700 *
[skat@anubis:~/work/UIUCTF/private/var] $ tree
.
├── buddy
├── containers
│ ├── Data
│ │ └── System
│ │ ├── 0484B045-1EFD-4EC1-9B74-3E7665974A42
│ │ │ ├── Documents
│ │ │ ├── Library
│ │ │ │ ├── Caches
│ │ │ │ └── Preferences
│ │ │ └── tmp
│ │ ├── 1CB5E5B6-3849-4CA0-8DCD-BF5A521286B9
│ │ │ ├── Documents
│ │ │ ├── Library
│ │ │ │ ├── Caches
│ │ │ │ └── Preferences
│ │ │ └── tmp
│ │ ├── 28841A8D-11F8-4013-8D5D-B02B63B944F4
│ │ │ ├── Documents
│ │ │ ├── Library
│ │ │ │ ├── Caches
│ │ │ │ │ ├── functions.data
│ │ │ │ │ ├── functions.list
│ │ │ │ │ ├── libraries.data
│ │ │ │ │ └── libraries.list
│ │ │ │ └── Preferences
│ │ │ └── tmp
│ │ ├── 2FD33CE1-DDAD-4FEC-A4F5-55144CBA75EB
│ │ │ ├── Documents
│ │ │ ├── Library
│ │ │ │ ├── Caches
│ │ │ │ └── Preferences
│ │ │ └── tmp
│ │ ├── 416AB7BD-5EC1-4075-9704-44048CF01074
-- snip --
Great, no more errors! Based on the briefing earlier, we know that we currently have a backup of Red’s tablet. Before we start combing through the files, let’s see if we can first find out what type of tablet Red has. Knowing what type of device we’re investigating may help us locate things and orient ourselves much more effectively.
hardware/
looks interesting.
Just like that, we now know that we’re on an Apple system. Apple’s line of tablets are iPads, so we can safely assume that this is an Apple iPad. This gives us a starting point from which we can do our research from.
It’s always important to get oriented when dealing with a new set of data. By first understanding that we’re dealing with an iPad device that’s running iPadOS, we can better and more precisely inspect the system while keeping in mind that its behavior will be that of an iPadOS; this removes an element of unpredictability from the equation.
According to Wikipedia’s article on iPadOS, iPadOS is a rebranded variant of iOS. iPadOS 14, the current major release of iPadOS, seems to at least partially mirror the features of iOS 14. Although we’re not sure if this specific backup is of an iPadOS 14 device, we can at least make the educated assumption that vital features and internal workings may stay consistent; our research on iPadOS 14 and iOS 14 may still be relevant to whatever specific operating system is on the device being investigated. Doing more research on what type of filesystem is utilized by iOS 14 brings us to an article from Apple’s own documentation: “File System Basics.”
Because we know that Red had exfiltrated data to a private server, knowing the tools that Red may have used may reveal where this private server is, how Red communicated with this server, and how Red may have connected to this server. What we’re looking for is a host, either in the form of a domain or an IP address, within the data of some application.
The aforementioned official Apple documentation tells us something important:
For security purposes, an iOS app’s interactions with the file system are limited to the directories inside the app’s sandbox directory. During installation of a new app, the installer creates a number of container directories for the app inside the sandbox directory.
We’re targeting applications that Red may have used to exfiltrate data since we’re searching for the potential host that Red may have connected to. Applications on an iOS device – and by extension, most likely on an iPadOS device – have their files confined to containers for security reasons. Let’s find where these containers are located using find
:
Let’s navigate to the containers and get oriented using tree
:
[skat@anubis:~/work/UIUCTF/private/var] $ cd ./mobile/Containers/
[skat@anubis:~/work/UIUCTF/private/var/mobile/Containers] $ tree
.
├── Data
│ ├── Application
│ │ ├── 0086F008-29FB-4F0A-AEF7-2EA84DBCE5BD
│ │ │ ├── Documents
│ │ │ ├── Library
│ │ │ │ ├── Caches
│ │ │ │ └── Preferences
│ │ │ ├── SystemData
│ │ │ └── tmp
│ │ ├── 009B0AA6-834F-433E-A1CC-D573DE8ADF6F
│ │ │ ├── Documents
│ │ │ ├── Library
│ │ │ │ ├── Caches
│ │ │ │ ├── Preferences
│ │ │ │ └── SplashBoard
│ │ │ │ └── Snapshots
│ │ │ │ └── com.apple.dt.XcodePreviews - {DEFAULT GROUP}
│ │ │ │ ├── 080464E1-D42F-4D8E-9D2D-8C18BE42C874@2x.ktx
│ │ │ │ ├── 194C4475-1EA1-495D-8B00-A06FA13EB212@2x.ktx
│ │ │ │ ├── 1D2FF3B4-E656-4976-A8B8-C3EAB7BF29AE@2x.ktx
│ │ │ │ ├── 38B8C5FD-456B-4972-AE04-CCEFFB738EE8@2x.ktx
│ │ │ │ ├── 5F090246-FD8E-4477-8834-4F094A0CE129@2x.ktx
│ │ │ │ ├── 90CFFFF1-DB89-447A-98B7-3190F4DCC26F@2x.ktx
│ │ │ │ ├── 9144A369-E7C7-4CF2-8B73-9A7ECEBFBBB6@2x.ktx
│ │ │ │ └── D43458C6-E992-4780-A5E3-F68F8B25EC5A@2x.ktx
│ │ │ ├── SystemData
│ │ │ └── tmp
│ │ ├── 05AB72AC-91C4-4A63-9116-D5C43068EA5F
│ │ │ ├── Documents
│ │ │ ├── Library
│ │ │ │ ├── Caches
│ │ │ │ ├── Preferences
│ │ │ │ └── SplashBoard
│ │ │ │ └── Snapshots
│ │ │ │ └── com.apple.WebSheet - {DEFAULT GROUP}
-- snip --
7,474 lines of output! We don’t really care about most of these files, but we do know now where the apps are located. Let’s try to get a list of all apps that are available on this system. Something that you may have noticed is that apps have names such as com.apple.dt.XcodePreviews
and com.apple.WebSheet
, something known was reverse domain name notation. We can use this to our advantage to create an expression that will only give us directories or files with an app name since we know that it will most likely start with some sort of common top-level domain like “.com” or “.org.” Let’s find
these:
[skat@anubis:~/work/UIUCTF/private/var/mobile/Containers] $ find . -name "com.*"
./Data/PluginKitPlugin/6C4CE4A9-900B-4177-B7EA-770F4DCE7C57/SystemData/com.apple.chrono
./Data/PluginKitPlugin/6C4CE4A9-900B-4177-B7EA-770F4DCE7C57/SystemData/com.apple.chrono/placeholders/com.apple.Maps
./Data/PluginKitPlugin/6C4CE4A9-900B-4177-B7EA-770F4DCE7C57/Library/Caches/com.apple.dyld
./Data/PluginKitPlugin/9CEFB254-EACD-409A-ADD6-F526CDE241DD/Library/Caches/com.apple.Animoji.StickersApp.MessagesExtension
./Data/PluginKitPlugin/9CEFB254-EACD-409A-ADD6-F526CDE241DD/Library/Caches/com.apple.Animoji.StickersApp.MessagesExtension/com.apple.metalfe
./Data/PluginKitPlugin/9CEFB254-EACD-409A-ADD6-F526CDE241DD/Library/Caches/com.apple.Animoji.StickersApp.MessagesExtension/com.apple.metal
./Data/PluginKitPlugin/26BC37DE-ECA3-4DE2-83FD-BB19CC960116/Library/SyncedPreferences/com.apple.kvs
./Data/PluginKitPlugin/26BC37DE-ECA3-4DE2-83FD-BB19CC960116/Library/SyncedPreferences/com.apple.kvs/ChangeTokens/EndToEndEncryption/WeatherIntents/com.apple.weather
./Data/PluginKitPlugin/26BC37DE-ECA3-4DE2-83FD-BB19CC960116/Library/SyncedPreferences/com.apple.weather.WeatherIntents.plist
./Data/PluginKitPlugin/E00F3026-1873-4BF6-BE3E-1E10F75FEB71/Library/SyncedPreferences/com.apple.mobilenotes.SharingExtension-com.apple.notes.analytics.plist
./Data/PluginKitPlugin/E00F3026-1873-4BF6-BE3E-1E10F75FEB71/Library/SyncedPreferences/com.apple.mobilenotes.SharingExtension.plist
./Data/PluginKitPlugin/4DCDF246-E707-4F52-B616-39B0BB323238/SystemData/com.apple.chrono
./Data/PluginKitPlugin/4DCDF246-E707-4F52-B616-39B0BB323238/SystemData/com.apple.chrono/placeholders/com.apple.tips
./Data/PluginKitPlugin/4DCDF246-E707-4F52-B616-39B0BB323238/Library/Caches/com.apple.dyld
./Data/PluginKitPlugin/B3D150FB-FD7E-4A6D-A4B0-C8277CADF6DF/SystemData/com.apple.chrono
./Data/PluginKitPlugin/B3D150FB-FD7E-4A6D-A4B0-C8277CADF6DF/SystemData/com.apple.chrono/placeholders/com.apple.mobilenotes.FolderWidget
./Data/PluginKitPlugin/B3D150FB-FD7E-4A6D-A4B0-C8277CADF6DF/SystemData/com.apple.chrono/placeholders/com.apple.mobilenotes.NoteWidget
./Data/PluginKitPlugin/B3D150FB-FD7E-4A6D-A4B0-C8277CADF6DF/Library/Caches/com.apple.dyld
./Data/PluginKitPlugin/B3D150FB-FD7E-4A6D-A4B0-C8277CADF6DF/Library/Caches/com.apple.dyld/com.apple.mobilenotes.WidgetExtension.closure
./Data/PluginKitPlugin/B3D150FB-FD7E-4A6D-A4B0-C8277CADF6DF/Library/SyncedPreferences/com.apple.mobilenotes.WidgetExtension-com.apple.notes.analytics.plist
./Data/PluginKitPlugin/B3D150FB-FD7E-4A6D-A4B0-C8277CADF6DF/Library/SyncedPreferences/com.apple.mobilenotes.WidgetExtension.plist
./Data/PluginKitPlugin/09D88789-E428-4922-B8D9-802A7399F256/Library/Preferences/com.apple.FileProvider
./Data/PluginKitPlugin/09D88789-E428-4922-B8D9-802A7399F256/Library/Preferences/com.apple.FileProvider/com.apple.CloudDocs.MobileDocumentsFileProvider
./Data/PluginKitPlugin/D02660B1-0F69-424C-B13C-AB0D17D1D979/SystemData/com.apple.chrono
./Data/PluginKitPlugin/D02660B1-0F69-424C-B13C-AB0D17D1D979/SystemData/com.apple.chrono/placeholders/com.apple.tv
-- snip --
Alright, that’s still a lot to take in! Let’s use awk
to split each line by the /
delimiter and print only the final token containing the name, and then let’s sort
it and make a unique list out of it with uniq -u
. Let’s additionally filter out all the built-in Apple stuff by performing a reverse grep
on the “com.apple” string:
Awesome! Right away, I notice com.itimeteo.webssh
. Could Red have been using SSH to exfiltrate data? Let’s find out by continuing to explore this application and its associated saved data:
[skat@anubis:~/work/UIUCTF/private/var/mobile/Containers] $ find . -name "com.itimeteo.webssh - {DEFAULT GROUP}"
./Data/Application/AA7DB282-D12B-4FB1-8DD2-F5FEF3E3198B/Library/SplashBoard/Snapshots/com.itimeteo.webssh - {DEFAULT GROUP}
[skat@anubis:~/work/UIUCTF/private/var/mobile/Containers] $ cd ./Data/Application/AA7DB282-D12B-4FB1-8DD2-F5FEF3E3198B/
[skat@anubis:~/work/UIUCTF/private/var/mobile/Containers/Data/Application/AA7DB282-D12B-4FB1-8DD2-F5FEF3E3198B] $ tree
.
├── Documents
├── Library
│ ├── Application Support
│ │ └── webssh.db
│ ├── Caches
│ │ └── com.apple.dyld
│ │ └── WebSSH.closure
│ ├── Preferences
│ │ └── com.itimeteo.webssh.plist
│ ├── Saved Application State
│ │ └── com.itimeteo.webssh.savedState
│ │ └── KnownSceneSessions
│ │ └── data.data
│ └── SplashBoard
│ └── Snapshots
│ ├── com.itimeteo.webssh - {DEFAULT GROUP}
│ │ ├── 14838935-9A83-4B7A-A340-E662593C666E@2x.ktx
│ │ ├── 3D6E2704-7354-4728-9686-67B626541EFE@2x.ktx
│ │ ├── 68B31944-676B-47CF-B1FC-C5C0B190AC16@2x.ktx
│ │ ├── 89B3C356-395B-4D8F-AFB4-05D32CB5409A@2x.ktx
│ │ ├── 9F8D3DA2-FDA7-4EB3-981D-514D277F1E85@2x.ktx
│ │ ├── A06B3B88-1384-41D6-8C49-AC4A1C9964B2@2x.ktx
│ │ ├── A1F7241A-C71F-430B-969E-7E627FC22F53@2x.ktx
│ │ ├── B3AA04CC-C35A-420E-9F83-ACA0EA306207@2x.ktx
│ │ └── downscaled
│ │ └── 788735BD-3AA3-481E-A60B-F318C5C5C678@2x.ktx
│ └── sceneID:com.itimeteo.webssh-default
│ └── downscaled
├── StoreKit
│ └── receipt
├── SystemData
└── tmp
18 directories, 14 files
I see a webssh.db
file, which seems interesting. Perhaps this file will contain some saved data regarding Red’s data exfiltration?
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 NSFValues(ROWID INTEGER PRIMARY KEY, NSFKey TEXT, NSFAttribute TEXT, NSFValue NONE, NSFDatatype TEXT);
INSERT INTO NSFValues VALUES(1,'4F479229-163D-469B-AD21-E23CDFDAFBBC','privatePart',replace('-----BEGIN OPENSSH PRIVATE KEY-----\n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\n9HmS+uFZxtacdqj4KdrHPcagm3Q=\n-----END OPENSSH PRIVATE KEY-----\n','\n',char(10)),'TEXT');
INSERT INTO NSFValues VALUES(2,'4F479229-163D-469B-AD21-E23CDFDAFBBC','objectIsDeleted',0,'REAL');
INSERT INTO NSFValues VALUES(3,'4F479229-163D-469B-AD21-E23CDFDAFBBC','objectCreation','2021-07-25 14:12:28:733','TEXT');
INSERT INTO NSFValues VALUES(4,'4F479229-163D-469B-AD21-E23CDFDAFBBC','objectType','privateKey','TEXT');
INSERT INTO NSFValues VALUES(5,'4F479229-163D-469B-AD21-E23CDFDAFBBC','objectEdition','2021-07-25 14:12:28:739','TEXT');
INSERT INTO NSFValues VALUES(6,'4F479229-163D-469B-AD21-E23CDFDAFBBC','isEncrypted',1,'REAL');
INSERT INTO NSFValues VALUES(7,'4F479229-163D-469B-AD21-E23CDFDAFBBC','name','private_key','TEXT');
INSERT INTO NSFValues VALUES(8,'4F479229-163D-469B-AD21-E23CDFDAFBBC','decryptPassword','********','TEXT');
INSERT INTO NSFValues VALUES(9,'69933883-557F-4A3D-94ED-F38CEE706B57','objectCreation','2021-07-25 14:11:08:530','TEXT');
INSERT INTO NSFValues VALUES(10,'69933883-557F-4A3D-94ED-F38CEE706B57','port_knocking','','TEXT');
INSERT INTO NSFValues VALUES(11,'69933883-557F-4A3D-94ED-F38CEE706B57','objectType','connection','TEXT');
INSERT INTO NSFValues VALUES(12,'69933883-557F-4A3D-94ED-F38CEE706B57','port_forwarding','','TEXT');
INSERT INTO NSFValues VALUES(13,'69933883-557F-4A3D-94ED-F38CEE706B57','type','SSH','TEXT');
INSERT INTO NSFValues VALUES(14,'69933883-557F-4A3D-94ED-F38CEE706B57','host','red.chal.uiuc.tf','TEXT');
INSERT INTO NSFValues VALUES(15,'69933883-557F-4A3D-94ED-F38CEE706B57','objectIsDeleted',0,'REAL');
INSERT INTO NSFValues VALUES(16,'69933883-557F-4A3D-94ED-F38CEE706B57','authentication.privateKeyID','4F479229-163D-469B-AD21-E23CDFDAFBBC','TEXT');
INSERT INTO NSFValues VALUES(17,'69933883-557F-4A3D-94ED-F38CEE706B57','authentication.password','','TEXT');
INSERT INTO NSFValues VALUES(18,'69933883-557F-4A3D-94ED-F38CEE706B57','authentication.2fa','false','TEXT');
INSERT INTO NSFValues VALUES(19,'69933883-557F-4A3D-94ED-F38CEE706B57','authentication.user','red','TEXT');
INSERT INTO NSFValues VALUES(20,'69933883-557F-4A3D-94ED-F38CEE706B57','groupName','','TEXT');
INSERT INTO NSFValues VALUES(21,'69933883-557F-4A3D-94ED-F38CEE706B57','objectEdition','2021-07-25 14:12:31:621','TEXT');
INSERT INTO NSFValues VALUES(22,'69933883-557F-4A3D-94ED-F38CEE706B57','name','Red’s Server','TEXT');
INSERT INTO NSFValues VALUES(23,'69933883-557F-4A3D-94ED-F38CEE706B57','port',42069,'REAL');
CREATE TABLE NSFKeys(ROWID INTEGER PRIMARY KEY, NSFKey TEXT, NSFKeyedArchive BLOB, NSFCalendarDate TEXT, NSFObjectClass TEXT);
INSERT INTO NSFKeys VALUES(1,'4F479229-163D-469B-AD21-E23CDFDAFBBC',X'62706c6973743030d4010203040506070a582476657273696f6e592461726368697665725424746f7058246f626a6563747312000186a05f100f4e534b657965644172636869766572d1080954726f6f748001af10150b0c232425262728292a2b2f37383c3f404344454655246e756c6cd30d0e0f101922574e532e6b6579735a4e532e6f626a656374735624636c617373a8111213141516171880028003800480058006800780088009a81a1b1c1d1e1f2021800a800c800d800f801080118012801380145b70726976617465506172745f100f6f626a656374497344656c657465645e6f626a6563744372656174696f6e5a6f626a656374547970655d6f626a65637445646974696f6e5b6973456e63727970746564546e616d655f100f6465637279707450617373776f7264d20f2c2d2e594e532e737472696e67800b5f110a5f2d2d2d2d2d424547494e204f50454e5353482050524956415445204b45592d2d2d2d2d0a6233426c626e4e7a614331725a586b74646a454141414141436d466c637a49314e69316a6448494141414147596d4e796558423041414141474141414142417471656d704e770a54757772456d706c2b4379325178414141414541414141414541414147584141414142334e7a614331796332454141414144415141424141414267514443347576474b72314d0a3335695a4a6b4255356b4b64754b74424d45467569344a61726b567575444d79307337676b554e353443517a522b70544735755050422f32416f4770413942452f35585258690a654f624b4a71656d784250646655412f5a6c436b66377543736d59354262424253633764504e566741556843654249324a59482f72434b4b51346870574854595242695537590a6b3454394e736b367273495a4b76546e7652462b355a5371466a5941776c7a66662b4567627342675163306b37356a4869456f697564696e596d376b7771454d6e7a585961380a7065725074756b4a3351614f6a67505036744f53443450305837417863732b5537704261505a734e47445931352f517a49523466533479523332696d596a4a314831376e31550a4577344b787146696e70742f71475a54476a6e68454866486641575276776e51346e566972624953556c69757872704d654533766b776c4d63767735554a567a45445a4239380a5852785a577a76647359526a6c577571686150743559674a6858312b334e45306f47484443564d6d6478506453734c38736b6b654d6d4b4f62356b3530455a3165413558462f0a592f782f72434d657171563375773661574e65383876694d67336941542f4234644e3763495330574b582b3267637642506e395a787934587532705341714b6375524a2b544c0a4c76695232386879336c7a4c73414141575141585355506d665777426a45637a4e642f68504765374f376a387a392f676153744d42672b724a57745632446d313836304162680a4e2f756a3244533354486d324f444c6c676b4561634943596733506c776676794b734a447738364c63376b4f552b6a2b774d514c6f536a3545396c59716d4e636f426c5472750a43695577396f53656f65562f527a5175594a3277354447382f5636554669586e5944755457304b6b38792f6c34556a2b397a5367556f68336b666b52484f69763167456a46580a694b78522b2f516b51316f414a6b73343944445833373375746d62685a362f67474762476b46796c4d44654d6b6458773061655449365558306842354479477a31535172476b0a62545a7a5057674d6a7456574a593077764d36617277735431637131736745596a706f57574f66663042514f425578564157333336593248414559702f7a734d434c4c335a6f0a772f36664f6a2b65705272713030525037496c6e556a51433242333871674362496c6c61664f7455525659427835776a4e444e557038644474325852455130687338792b55790a2f3936414b524f53412f506a317a2f36786a774c4171576a67537879694d5770524a504b645371664e444c716d7779753845662f456e743053574c58594b38475043567236580a746a487447522b7376716c3879553034526f4f476949564733517a545a345777444f764568525741307a414841754f756c6371776d3843776c76385a5053303555716f4750780a5634756e3367324454735766614d6536744846715654624e756d756d55615a4c336b47705649634e4a4b6d436e70324650716f3336644b76473256684f4c45714d61476c50760a464158747678714c697a394d54314b72345758795263706d3473316f7934633572685371326f77445a4377516e744d423034724878596b47642b5278616a6f56466473352b630a555a38376f7578322f30623052572f6357423273427a7673545a4d6938573935344b6f4a39644e614957394b2f3461314e4c6a354a594146354a6832513950566369464f78700a6f6953534f41664d71544f5267357a7a2f435337624f35496a59344c63576b31394a4d396d344f32394f6976424f6c5269426a366d42506e66576e6f327668554a6565624c480a3376496a5767654c45564c7639666b4f5a39727332652b5266444d4f5734474154674f68566275436a747143684348585247416c69784c2b4a653147785142673578712f414e0a67383965777a4d324f7539352b4c7745414d7578664c666a33306468417634374c416c4f59762f7a36686d4a41634842654f417568744177744277564832415333586d42717a0a74774668433362575a374f4150625777426a557a63736a326e3776486c72515274426a71302b7a2f4b4a334d442b4574504a362f706f647a343579507173794e364a6b4c63410a4e45562b4a4b6434706b515a6143336d524648416b47334b56344b355753434246546436434c724461557a5357306c31485a4d704a665267623348445642496f7337676e64570a316b416b794b41463672643744717175334a555a37654768777a453138425a71473051507846392f3132326369743376476a4a434f56653865384936446a5a6d5179773867610a4859463634465a61696b7853316333586464686a3857485253776650522b384e437243386457423632427a32394a6f51584c6a393847564738484376347749743145734159330a726669726832364151456c326d506d72776554793152766c75514c4f4b46594a546864713464726e4b6434575150734c4b37756d6833697a616861777631633466374f6c4b480a4d6371766a50667768337165674870704c5577464a5747772f6350314c45332f6a6a6d504a61727071612f376d306437344d33434164596f6f37704d516954634973625634640a2b672b30722f44673269554d6e2f7a45746b653055774d4147326347794e53472b47534243374556547248394332747a55372f6a5046595538753459314934414846617168520a744f72716d2f5541454e64666e427167376b6f6458312f68326c457573546939686b715a5a454d616f4b71456a574d596943764a382b744c707678304f7373314a77514536560a4c335175752b7659635173397843766f4e77304e41716f453262704970464a6530524a6c332b362b47654a437574384830396d366862477a46777157736f564b304c685435410a4b344344774b4933706f426c4b59574758567344426557496439724f69367248706c5965772b502b7773344d6c6453626732516e59626e2f67644c51334a643549494a38782b0a524c6a454b62524a39622b7243544f6951355254577034354b2f71327134753650326b6c51775232455537425633324e6c2b5a6576445a556e56516c4d4937735769747a47460a4f6563346b332f5649633642516338755a34744148304d50696645566f7847316d5a7832767466456351546a4b4c4b627773574b6c4c4d374c41674665335a444a38616965620a7176627843376e6b76694d534c4955775a517a76575243542b7766662f2f776767594942722f4579746346714a633346306539715455763372336168614777493857326732790a39486d532b75465a7874616364716a344b647248506361676d33513d0a2d2d2d2d2d454e44204f50454e5353482050524956415445204b45592d2d2d2d2d0ad2303132335a24636c6173736e616d655824636c61737365735f100f4e534d757461626c65537472696e67a33435365f100f4e534d757461626c65537472696e67584e53537472696e67584e534f626a65637408d2390f3a3b574e532e74696d652341c356f78e5dcc53800ed230313d3e564e5344617465a23d365a707269766174654b6579d2390f413b2341c356f78e5e9b63800e095b707269766174655f6b6579582a2a2a2a2a2a2a2ad2303147485f10134e534d757461626c6544696374696f6e617279a34749365c4e5344696374696f6e61727900080011001a00240029003200370049004c00510053006b007100780080008b0092009b009d009f00a100a300a500a700a900ab00b400b600b800ba00bc00be00c000c200c400c600d200e400f300fe010c0118011d012f0134013e01400ba30ba80bb30bbc0bce0bd20be40bed0bf60bf70bfc0c040c0d0c0f0c140c1b0c1e0c290c2e0c370c390c3a0c460c4f0c540c6a0c6e0000000000000201000000000000004a00000000000000000000000000000c7b','2021-07-25 14:12:28:750','BO');
INSERT INTO NSFKeys VALUES(2,'69933883-557F-4A3D-94ED-F38CEE706B57',X'62706c6973743030d4010203040506070a582476657273696f6e592461726368697665725424746f7058246f626a6563747312000186a05f100f4e534b657965644172636869766572d1080954726f6f748001af10220b0c2b2c2d2e2f30313233343536373b41424344454652535455565758595a5e616255246e756c6cd30d0e0f101d2a574e532e6b6579735a4e532e6f626a656374735624636c617373ac1112131415161718191a1b1c80028003800480058006800780088009800a800b800c800dac1e1f201f222324251f272829800e80108011801080128013801480158010801f80208021801e5e6f626a6563744372656174696f6e5d706f72745f6b6e6f636b696e675a6f626a656374547970655f100f706f72745f666f7277617264696e67547479706554686f73745f100f6f626a656374497344656c657465645e61757468656e7469636174696f6e5967726f75704e616d655d6f626a65637445646974696f6e546e616d6554706f7274d2380f393a574e532e74696d652341c356f76643dfb1800fd23c3d3e3f5a24636c6173736e616d655824636c6173736573564e5344617465a23e40584e534f626a656374505a636f6e6e656374696f6e535353485f10107265642e6368616c2e756975632e746608d30d0e0f474c2aa448494a4b8016801780188019a44d4e4f50801a801b801c801d801e5c707269766174654b657949445870617373776f72645332666154757365725f102434463437393232392d313633442d343639422d414432312d453233434446444146424243505566616c736553726564d23c3d5b5c5f10134e534d757461626c6544696374696f6e617279a35b5d405c4e5344696374696f6e617279d2380f5f3a2341c356f78fcf8898800f6c00520065006420190073002000530065007200760065007211a45500080011001a00240029003200370049004c005100530078007e0085008d0098009f00ac00ae00b000b200b400b600b800ba00bc00be00c000c200c400d100d300d500d700d900db00dd00df00e100e300e500e700e900eb00fa010801130125012a012f01410150015a0168016d01720177017f0188018a018f019a01a301aa01ad01b601b701c201c601d901da01e101e601e801ea01ec01ee01f301f501f701f901fb01fd020a02130217021c02430244024a024e02530269026d027a027f0288028a02a300000000000002010000000000000063000000000000000000000000000002a6','2021-07-25 14:12:31:629','ConnectionBO');
COMMIT;
Well, would you look at that!
We have a host, port, username, SSH private key, and the decryption password – five ingredients for an SSH connection! I must admit that I found the decryption password to be quite humorous; I initially thought that the password must have been censored, but it is actually, literally ********
.
Let’s go ahead and copy that SSH private key into a file, give it the appropriate permissions, and connect to the server that we just uncovered. This is an exciting development!
[skat@anubis:~/work/UIUCTF] $ cat key
-----BEGIN OPENSSH PRIVATE KEY-----
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
9HmS+uFZxtacdqj4KdrHPcagm3Q=
-----END OPENSSH PRIVATE KEY-----
[skat@anubis:~/work/UIUCTF] $ chmod 600 key
[skat@anubis:~/work/UIUCTF] $ ssh -i key -p 42069 red@red.chal.uiuc.tf
Enter passphrase for key 'key':
This service allows sftp connections only.
Connection to red.chal.uiuc.tf closed.
Oh, it looks like the server only allows SFTP connections. Given the context of the scenario, that makes sense. No worries, we can just connect using SFTP instead of SSH:
Alright, we’re in! Let’s have a look at the Bash history to see if there’s anything interesting that Red may have done on this system. We can get files using the get
command in SFTP:
It looks like the file /srv/exfiltrated
was renamed to /srv/...
. Let’s have a look at that file. We can again get the file using get
:
Oh, it’s a directory; the file was actually being moved to a directory.
Awesome, some important data! Let’s have a look at it:
Just like that, we have a flag!
Debriefing
We were initially given a backup of Red’s tablet and told that Red had been exfiltrating data to a private server; our objective was to access the server and contain the leak. We first got oriented and discovered what type of device it was: an iPad. From here, we began doing research and discovered that iPads use iPadOS, a variant of iOS, whose filesystem structure is documented by Apple. Learning that applications on an iOS device run inside of containers, we discover the location of the containers and generate a list of applications present on the device. We found that there was an SSH client and looked through the stored application data to discover a host, port, username, SSH private key, and the decryption password, allowing us to then connect to the server via SFTP, look through the Bash history, and retrieve the exfiltrated data.
Although this was just a CTF challenge, the entire process very closely resembles an operation that could very well happen in real-life operations! Digital forensics is simply just the branch of forensic science involved with digital devices. It lends itself to criminal investigations by focusing on the investigation of digital devices, allowing evidence to be attributed to suspects, incriminating data to be recovered, and more. I oftentimes playfully refer to digital forensics as being like a “hacker detective.” This challenge was a great exercise of the digital forensics process: given data, assess the situation and look for evidence of criminal activity on the digital device. Being just a CTF challenge, we could have some fun and also additionally hack into the remote server while avoiding the legal and bureaucratic process that would have otherwise been required as a prerequisite to such an act in a real-life investigation.
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
Checksum (SHA-1):
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:
sms.db
looks interesting. Let’s access and dump that database, just like we dumped an application’s database in forensics/Tablet 1 earlier:
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,'blue@poggers.university','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.
The table “message” looks interesting. Let’s have a look at that.
That’s still a lot to take in! It may be valuable to understand how this table is constructed:
Based on this, we can prepare a SQL query to only return data from the column “text” in the table “message.”
Just as a quick aside, I found this to be really funny while I was doing the challenge:
poggers.university just redirects to 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:
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:
[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
│ │ │ │ ├── nearby_scan@2x.png
│ │ │ │ └── nearby_scan@3x.png
│ │ │ ├── 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):
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:
There’s a lot of data, so let’s pipe everything to grep
and find out how the tables are created:
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
:
Great, some data! Let’s toss this into a code prettifier for easier reading:
[
{
"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:
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:
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:
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, in order to decrypt the note using the NoteStore.sqlite
file:
Great, it looks like we were able to successfully decrypt the note! Let’s check out the data:
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:
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.
Conclusion
These were a great set of challenges and definitely some of the best forensics challenges I’ve ever done at a CTF event! At no point did I feel that these challenges were guessy, and I was appreciative at every step of the way of how closely this challenge could mirror a real-life investigation – something that many CTFs shy away from. For anyone looking to get an idea of what a digital forensics specialist may encounter in their investigations and operations, this challenge gives a great idea!
That’s all I have to offer for these two challenges. However, I do have two additional writeups from this CTF authored available immediately if you’d like to read them now. These were a lot of fun to author and took me nearly a week of writing, editing, proofreading, and revising until I finally decided to publish them.