AirDrop Forensics | by Kinga Kięczkowska

AirDrop Forensics

✨ Welcome to AirDrop forensics! ✨

Let’s start with the basics: what is AirDrop? It’s a file-sharing service in macOS and iOS which uses both Bluetooth and WiFi to transfer files from one Apple-made device to another. The nearby devices discovery is done using Bluetooth, then the file transfer itself is completed over WiFi. AirDrop is part of Apple’s Continuity service – a group of functionalities designed to provide a seamless user experience between multiple Mac devices. Continuity allows you to answer phone calls and SMS on devices different than your phone, easily share websites between browsers on different devices, use your iPad as a monitor for your Apple computer, use your iPhone as a camera for your laptop, send and receive files through AirDrop… This list is not exhaustive – if you’re interested in the full range of Apple’s Continuity capabilities make sure to check their website.

A great rundown of forensics artefacts produced by AirDrop in iPhones was presented by Heather Mahalik and Sarah Edwards in their ‘The Cider Press: Extracting Forensic Artifacts from Apple Continuity’ presentation. I’ve only seen the slides but they are very informative on their own so make sure to check that out. Seeing that analysis inspired me to take a look at what these artefacts will look like in macOS.

Setup

To create a semi-controlled environment, I have purposefully sent some images from both my own device and someone else’s device to my MacBook (iOS -> macOS). When running the log show commands, I therefore put time constraints to make sure I don’t include heaps of old transfers I do not remember the details of. Therefore, where you see some weird-looking time constraints like last x minutes ( --last Xm), please disregard the value itself and if you’re following the investigation steps on your own machine feel free to amend those time restrictions accordingly.

Apple unified logs

First destination to check out for anything regarding a macOS system are the unified logs. The number one source of accessible knowledge on the topic is Sarah Edward’s unified logs blog series, especially the one about AirDrop logs. Unfortunately, the macOS logs aren’t identical to the iOS ones so it took me a bit of observing and playing around to find the right combination of predicate search terms to find what I needed.

I have found the only transferrable (between iOS and macOS) log search mentioned in Sarah’s blog post was checking what kind of AirDrop scans were performed by the machine:Screenshot 2020-06-13 at 22.45.22

As you can clearly see above, I only performed Contacts Only scans, looking for devices of people in my contacts to AirDrop with – the other option would be Everyone, where I would be looking for any nearby device with AirDrop switched on.

Back to the macOS logs, I’m going to shed some light on my thought process behind creating the queries. All I knew in the beginning was that I’m looking for entries related to AirDrop, so in the early stages I was experimenting with simple singular predicates like eventMessage contains "AirDrop". With time I have noticed now AirDrop wasn’t actually mentioned that often in the eventMessage; it was visible in the pattern clause before eventMessage which I later came to understand to be the category.  At the beginning though I was interested to see all logs connected to sharingd as I wasn’t sure if eg. the Bluetooth discovery stage would show as part of category AirDrop, or as a separate process. Why sharingdSharingd is macOS’s service powering “AirDrop, Shared Computers, and Remote Disc in the Finder”, and I wanted a wider set of logs to look at first. So, I’ve decided to first go with the following command:

log show --predicate "processImagePath contains 'sharingd' && subsystem contains 'com.apple.sharing' --last 30m"

Some explanation about the command: I am specifying what exactly the specific pattern clauses (processImagePath and subsystem) need to contain. If you’re wondering what kind of pattern clauses you can use, I described the full list of elements you can include in your predicate in my USB Forensics blog post (Ctrl+F “a predicate” to go straight to it). For simplicity, check out my cheatsheet schematic below:

Screenshot 2020-06-14 at 14.37.05

The output was as follows:

Screenshot 2020-05-24 at 18.59.11
Note: if the image is too small to read the text, please right click and open the image in new tab.

First thing I see in the output logs is the BTLE device discovery. BTLE is a version of Bluetooth Low Energy protocol, which is a Bluetooth implementation for devices where power consumption is an important consideration (eg, IOT devices). As I mentioned before, AirDrop uses both Bluetooth and WiFi, and the nearby device discovery is what Bluetooth is used for. We can see above the first entry reads BTLE discovered hashes .... These has actually been found to be problematic and a huge security issue. According to some articles (see here and here) the BLE discovery process reveals phone numbers and email addresses associated with the discovered device in a form of unsalted SHA256 hashes, truncated to 16 or 24 most significant bytes. This has of course been automated already (yay), and you can find it in the Apple Bleee’s airdrop_leak.py module. A TL;DR on the method is that the only way to link these parts of hashes to actual phone numbers is to brute force it. You need a lengthy list of all possible phone numbers, hash them and compare the hashes to each of the discovered values. I decided to have a go at finding the connection between the data and the hashes advertised myself. To do that I’ve compared hashes shown in the entry logs for transfers between my laptop and two iPhones – one associated with the same iCloud account as my laptop (device name KingasiPhone), and one not (device name GeorgeClooney) – with the information I have about the discovered devices. I used a Python script based on the following:

import hashlib


# a list of items to be hashed, probably your iCloud ID and email address
items = [] 


for item in items:
    i = str(item).encode('utf-8')
    hash = hashlib.sha256(i).hexdigest()
    strhash = str(hash).encode()
    print("Hash for {} is {}".format(item, hash))

I added some print statements to display the hashes found in the logs for convenience. The results were as shown:

Screenshot 2020-06-14 at 18.32.30

My findings were the following:

  • In both cases the first byte of discovered hashes was 01.
  • In both cases the discovered hashes ended with 00.
  • In both cases the next two bytes after 01 were identical to the first two bytes of the phone number associated with the sender’s device, confirming statements from here and here.
  • The first 2 (in case of the GeorgeClooney device) and 3 (in case of KingasiPhone) bytes of the sender’s Apple ID appeared in the discovered hash. This was inconsistent both in terms of the number of bytes and the location within discovered hashes. This seems too much of a correlation for a simple coincidence, it definitely needs more testing. When lockdown is over I’ll be sure to test with other devices more and update this post with new findings. In the meantime please feel free to reach out if you have some knowledge on this that I’m missing – I’d love to crack this one!

In the same log entry, we have a mention of RSSI. The RSSI stands for Received Signal Strength Indicator. The maximum value of RSSI is dependent on the chip manufacturer and therefore not universal. I performed some experiments on my machine and I found that the RSSI value was -38 when the other device was placed just near my laptop,  -62 from across the room and -71 from around 4 meters & a bit of the wall away. More on estimating proximity based on RSSI here.

Scrolling further down the logs you will be able to spot a log entry similar to this, including the key words New incoming transfer:

Screenshot 2020-06-13 at 23.37.51
Note: if the image is too small to read the text, please right click and open the image in new tab.

There are a couple of important pieces of information here, including:

    • the transfer’s identifier – in this case ​E6A9D66F-2637-49B3-AF9A-C91B04ABD12; this can be useful if we’re particularly interested in a specific transfer, as we can use it as a search criterion for the predicate to narrow down the logs to this transfer only.
    • senderIsMe flag – which simply tells us if the sender is us, which means if the device the transfer originates from uses the same iCloud account as the recipient device. In my experiments this was yes for KingasiPhone and no for GeorgeClooney.
    • sender – the given name of the sending device, in the case above -​KingasiPhone.

Let’s refine our search with the transfer identifier I found above. We can now drop the time flag in the command as it is now narrowed down to a specific transfer. The output is still quite large but not even close to what we dealt with before. You will see a number of log entries concerning the user’s (yours) actions in the UI in response to the transfer, such as creating and posting a notification, receiving the decision from the notification (save in Downloads or open in Photos), as well some transfer progress entries. Only one of the entries – which was, surprisingly, of type Error – featured the actual file name of the transferred file – IMG_7596.png.

Screenshot 2020-06-13 at 23.52.46
Note: if the image is too small to read the text, please right click and open the image in new tab.

mdls

Another tool useful in the forensic analysis of AirDrop transfers is mdls. The man page for mdls states it “lists the metadata attributes for the specified file”. Let’s try it out on the file we’ve managed to locate by parsing the unified logs in the section above.

Kingas-MacBook-Pro:Downloads kingakieczkowska$ mdls IMG_7596.PNG
kMDItemBitsPerSample                     = 32
kMDItemColorSpace                        = "RGB"
kMDItemContentCreationDate               = 2020-06-13 20:34:07 +0000
kMDItemContentCreationDate_Ranking       = 2020-06-13 00:00:00 +0000
kMDItemContentModificationDate           = 2020-06-13 20:34:07 +0000
kMDItemContentType                       = "public.png"
kMDItemContentTypeTree                   = (
    "public.png",
    "public.item",
    "public.png",
    "public.data",
    "public.image",
    "public.content"
)
kMDItemDateAdded                         = 2020-06-13 22:18:46 +0000
kMDItemDateAdded_Ranking                 = 2020-06-13 00:00:00 +0000
kMDItemDisplayName                       = "IMG_7596.PNG"
kMDItemFSContentChangeDate               = 2020-06-13 22:18:44 +0000
kMDItemFSCreationDate                    = 2020-06-13 22:18:44 +0000
kMDItemFSCreatorCode                     = ""
kMDItemFSFinderFlags                     = 0
kMDItemFSHasCustomIcon                   = (null)
kMDItemFSInvisible                       = 0
kMDItemFSIsExtensionHidden               = 0
kMDItemFSIsStationery                    = (null)
kMDItemFSLabel                           = 0
kMDItemFSName                            = "IMG_7596.PNG"
kMDItemFSNodeCount                       = (null)
kMDItemFSOwnerGroupID                    = 501
kMDItemFSOwnerUserID                     = 501
kMDItemFSSize                            = 1860602
kMDItemFSTypeCode                        = ""
kMDItemHasAlphaChannel                   = 0
kMDItemInterestingDate_Ranking           = 2020-06-13 00:00:00 +0000
kMDItemKind                              = "PNG image"
kMDItemLogicalSize                       = 1860602
kMDItemOrientation                       = 0
kMDItemPhysicalSize                      = 1863680
kMDItemPixelCount                        = 2156160
kMDItemPixelHeight                       = 960
kMDItemPixelWidth                        = 2246
kMDItemProfileName                       = "sRGB IEC61966-2.1"
...

The first part of the output states some standard data as the date and time the photo was taken and the file type. Interestingly, the timestamp was an hour early compared to the both the phone’s (with which it was taken) and laptop’s clocks (see the comparison of properties view in the OS) which corresponds to UTC (as I’m currently in a BST zone). Towards the end, we can observe the information we’re after: the date the photo was received, the recipient’s handle (I assume this is the Apple ID), the sender’s name and handle, and most importantly – the transport type which reveals the file has been received via Airdrop, and in the last value we can see the name of the device which sent the file.

Screenshot 2020-06-14 at 00.13.02

I wanted to check if this metadata persists when the file is moved – but also did not want to erase the metadata by transferring it using AirDrop again, so I copied the file IMG_7596.png onto a USB stick and viewed its contents in a separate Mojave VM. The only information that persisted was the kMDItemWhereFroms value revealing the name and device name of the sender.

Screenshot 2020-06-14 at 17.52.55

Next I compared the md5 digests of both the images.

Kingas-MacBook-Pro:Downloads kingakieczkowska$ md5 IMG_7596.png
MD5 (IMG_7596.png) = 338b61e282bde46b5b7a93a3078fd427
Admins-Mac:USB_STORAGE adminadmin$ md5 IMG_7596.PNG
MD5 (IMG_7596.PNG) = 338b61e282bde46b5b7a93a3078fd427

Both hashes were the same, which means the file stored on my laptop is identical to that on USB_STORAGE. This implies that the extended metadata containing the transport type and details of the sender such as Apple ID is stored within the file system and not embedded in the file, as lack of those annotations were shown not to influence the md5 hash of the file.

mdfind

The mdls command explained above allows for investigation of a specific file. What if you want to find all AirDropped files within a given directory? That’s where mdfind comes in. A simple command mdfind -onlyin . "com.apple.AirDrop" returns a list of AirDropped files in the current working directory (the -onlyin flag makes sure of that – if you don’t add it, all of the user’s files will be searched).

Kingas-MacBook-Pro:files kingakieczkowska$ pwd
/Users/kingakieczkowska/Downloads/files
Kingas-MacBook-Pro:files kingakieczkowska$ ls
BIAŁKA.pptx Collage_Fotor1.jpg IMG_0631.JPG IMG_6944.jpg
Kingas-MacBook-Pro:files kingakieczkowska$ mdfind -onlyin . com.apple.AirDrop
/Users/kingakieczkowska/Downloads/files/IMG_0631.JPG
/Users/kingakieczkowska/Downloads/files/IMG_6944.jpg

It is also possible to narrow down your search to different metadata fields we saw in mdls. For instance, to find all files sent from the GeorgeClooney device in the current working directory, I ran the following command:

Kingas-MacBook-Pro:files kingakieczkowska$ mdfind -onlyin . kMDItemWhereFroms == "*GeorgeClooney*"
/Users/kingakieczkowska/Downloads/files/IMG_0631.JPG

This can be verified by checking the item’s metadata with mdls.

Wireshark & PacketLogger

As during an AirDrop transfer the actual data travels over WiFi, it is encrypted and eavesdropping that phase using Wireshark does not give us much more than a bunch of scrambled unreadable data. What we can sniff, as numerous articles I cited above (and in Sources below) pointed out, is the Bluetooth stage of an AirDrop transfer as that is said to be propagated around in plaintext. Bluetooth sniffing is unsupported in Wireshark for macOS so it is needed to install XCode and Additional Tools for XCode in order to gain access to a PacketLogger tool. I was unfortunately unable to find anything more in PacketLogger than what unified logs already revealed. Interestingly, I also was only able to see data regarding connections to my iPhone (KingasiPhone), but not the other (GeorgeClooney) device (despite transferring files between both of them and my laptop).

🔮🔮🔮

Thanks for reading the post, I hope you learnt a thing or two on the way. AirDrop is something I’ve only recently started to look into so this will very probably be updated and built upon in the future. See you in the next one!

SOURCES

Originally published: https://kieczkowska.com/2020/06/15/airdrop-forensics/

July 9, 2020
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
© HAKIN9 MEDIA SP. Z O.O. SP. K. 2013

Privacy Preference Center

Necessary

Cookies that are necessary for the site to function properly. This includes, storing the user's cookie consent state for the current domain, managing users carts to using the content network, Cloudflare, to identify trusted web traffic. See full Cookies declaration

gdpr, PYPF, woocommerce_cart_hash, woocommerce_items_in_cart, _wp_wocommerce_session, __cfduid [x2]

Performance

These are used to track user interaction and detect potential problems. These help us improve our services by providing analytical data on how users use this site.

_global_lucky_opt_out, _lo_np_, _lo_cid, _lo_uid, _lo_rid, _lo_v, __lotr
_ga, _gid, _gat, __utma, __utmt, __utmb, __utmc, __utmz
vuid

Marketing


tr, fr
ads/ga-audiences