✨ Welcome in AirDrop Round 2! ✨
After I published the AirDrop Forensics post my dear friend Mikey got in touch to let me know about other ways of obtaining AirDrop artefacts. Those are looking into the quarantine database and the com.apple.quarantine
extended attribute attached to downloaded files, including those transferred using AirDrop. As it turned out, it is possible to link entries in that database to actual files in the file system by the value of a specific extended attribute of AirDropped and downloaded files. To make that process easier, I’ve written a Python tool – more on later though, let’s start from the beginning.
Extended attributes
What are extended attributes? They are key value pairs holding additional metadata about a file. One of the extended attributes is com.apple.quarantine
, which is added to files downloaded from the Internet or AirDropped (coming from a foreign source) for enhanced security. This is the attribute holding flags which may lead to you seeing a popup like so:
Extended attributes are not exclusive to macOS, other operating systems have them too, and – as you can imagine – their functionality is not limited to detecting downloaded and AirDropped files. I, however, am going to focus solely on the com.apple.quarantine
attribute, but for a full overview please head on over to Mikey’s write up and Tuxera’s post as those are brilliant resources on the topic. I’d actually recommend you read Mikey’s post first before diving into this; I’m only glossing over things he explains in detail.
Let’s dive right in with a TL;DR:
Extended attributes can be viewed, edited and deleted using the xattr
command. It’s easy to spot files which contain extended attributes by running ls -la
:
Kingas-MacBook-Pro:files kingakieczkowska$ ls -la total 10992 drwxr-xr-x 6 kingakieczkowska staff 192 14 Jun 18:12 . drwx------+ 142 kingakieczkowska staff 4544 16 Jun 16:17 .. -rw-r--r--@ 1 kingakieczkowska staff 328217 12 May 11:02 BIAŁKA.pptx -rw-r--r--@ 1 kingakieczkowska staff 1033732 9 May 23:58 Collage_Fotor1.jpg -rw-r--r--@ 1 kingakieczkowska access_bpf 2298741 24 May 18:27 IMG_0631.JPG -rw-r--r--@ 1 kingakieczkowska access_bpf 1954141 27 May 16:37 IMG_6944.jpg
Every file with an @ at the end of the permissions will have extended attributes. We can check them by running the xattr
command against any given file (full demo in Mikey’s post). We’re interested in what the com.apple.quarantine
attribute looks like, so let’s view it for IMG_0631.JPG
. We can do that by adding a -p
(print) flag to the xattr
command and specifying the attribute to be printed like so:
Kingas-MacBook-Pro:files kingakieczkowska$ xattr -p com.apple.quarantine IMG_0631.JPG 0081;5ecaae8a;sharingd;29B870EA-BB4A-41EB-B014-F722B33D1C26
The output we get can be decoded as follows:
flags;downloaded / received timestamp in hex;agent;unique file identifier
The flags in the beginning are Gatekeeper scores (via here and here), determining what kind of security checks must be performed when the user tries to open the file. The timestamp in hex is pretty self explanatory, although it might be confusing how to turn it into a human-readable format – for this I recommend using an online tool such as this one, and if you’re interested in a programmatic solution bear with me to see in my script how it can be done in Python. The next value is the agent from which the file originates; that’s sharingd
for AirDrop and for browsers it usually uses their names like so:
Kingas-MacBook-Pro:Downloads kingakieczkowska$ xattr -p com.apple.quarantine Spotify-0.8.5.dmg 0081;5ef7b1c6;Chrome;47E57525-8A80-43BC-8193-DB61878C1C75 Kingas-MacBook-Pro:Downloads kingakieczkowska$ xattr -p com.apple.quarantine braveimg.jpg 0081;5ef780e5;Brave;35CBF646-4602-4789-95F9-487DE097BE69
The unique file identifier happens to also be logged in the quarantine database in the LSQuarantineEventIdenfier
field and is the value which will allow us to link the database entry to a specific file.
Quarantine database
The database is located in ~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2
. It only contains one table, LSQuarantineEvents
, which consists of the following fields:
From the perspective of investigating AirDrop transfers, we are interested in the following fields:
-
LSQuarantineEventIdentifier
– which is equivalent to the value revealed by thexattr
commandLSQuarantineTimeStamp
– the date the file was received / downloadedLSQuarantineSenderName
– the name associated with the Apple ID of the sending deviceLSQuarantineAgentName
– which we need to filter out AirDropped files from other quarantined files; if a file has been AirDropped, the agent name will besharingd
If we’re interested in investigating browser downloads, the below fields come in handy as well:
-
LSQuarantineOriginURLString
– the website containing the link to the downloaded fileLSQuarantineDataURLString
– the actual link where the file residesLSQuarantineAgentName
– if a file has been downloaded using a browser, usually the browsers name will be displayed here.
The differences between OriginURLString
and DataURLString
are portrayed by these two examples of downloading files from Gmail and Blogspot:
File photo.JPG Path: /Users/kingakieczkowska/Desktop/photo.JPG Browser name: Chrome Time downloaded: 2019-10-16 23:54:40 Origin URL: https://mail.google.com/mail/u/0/ Data URL: https://mail-attachment.googleusercontent.com/attachment/u/0/...
File tlo.JPG Path: /Users/kingakieczkowska/Desktop/tlo.JPG Browser name: Chrome Time downloaded: 2020-04-24 17:54:30 Origin URL: http://simpleintricacy.blogspot.com/search?updated-max=2012-12-18T13:37:00-08:00&max-results=3&start=33&by-date=false Data URL: http://1.bp.blogspot.com/-eTPfRtUK2HY/UKlcoL_23zI/AAAAAAAACo8/DWvPRx7ecuM/s1600/DSC_0883.JPG
The same information can also be retrieved using the mdls
command I talked about in my previous post. Running mdls
on the file tlo.jpg
yields a long list of metadata including the lines:
kMDItemWhereFroms = ( "http://1.bp.blogspot.com/-eTPfRtUK2HY/UKlcoL_23zI/AAAAAAAACo8/DWvPRx7ecuM/s1600/DSC_0883.JPG", "http://simpleintricacy.blogspot.com/search?updated-max=2012-12-18T13:37:00-08:00&max-results=3&start=33&by-date=false" )
However, it is incredibly easy to get rid of this metadata. A simple command presented below will delete this information immediately.
xattr -d com.apple.metadata:kMDItemWhereFroms tlo.jpg
That’s why when automating the extended attribute retrieval I decided to go with the contents of the quarantine database, which does not (at least not immediately) delete entries or specific fields’ contents for files which metadata has been manually removed. So while after running the above command I would not be able to see the kMDItemWhereFroms
metadata using mdls
anymore, the information is still be available in the database.
So, how can we put the intel we get from xattr
together with what we found in the quarantine database?
You guessed it – with Python!
Collating extended attributes info with Python

The script, setup and usage instructions and examples are all available on the project’s Github. Here I will just provide the usage instructions and go into more detail on how it works. If you want the code and usage examples, go to Github.
Usage
python3 xattribs.py /path/to/directory/ [--db /path/to/db] [--json]
Use the --db
flag to specify the QuarantineEvents
database location, if different than the default (/Users/<current logged in user name>/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2
).
Use --json
to print output in JSON format, for instance to pipe output to another command or tool. By default the output will be presented in a human-friendly way, like so:
Kingas-MacBook-Pro:Desktop kinga$ python3 xattribs.py /Users/kinga/Downloads/files Files in /Users/kinga/Downloads/files without the com.apple.quarantine attribute: ['.DS_Store'] 💨💨💨 AirDropped files: File IMG_6944.jpg Path: /Users/kinga/Downloads/files/IMG_6944.jpg Time received: 2020-05-27 16:37:51 Sender name: Kinga Kieczkowska File IMG_0631.JPG Path: /Users/kinga/Downloads/files/IMG_0631.JPG Time received: 2020-05-24 18:27:38 Sender name: Brad Pitt 🔥🔥🔥 Downloaded files: File BIAŁKA.pptx Path: /Users/kinga/Downloads/files/BIAŁKA.pptx Browser name: Chrome Time downloaded: 2020-05-12 11:02:25 Origin URL: https://l.facebook.com/ Data URL: https://cdn.fbsbx.com/v/....
How does it work?
First of all, the script parses positional arguments (including the path to the folder with files to be inspected) and flags.
Then the function get_files
returns two dictionaries: airdropped_files
and downloaded_files
. It goes through all files in the specified directory and chooses those which were AirDropped or downloaded via a browser by detecting and then inspecting their com.apple.quarantine
extended attribute. Subsequently, it saves some basic metadata about these files in a dictionary which will then be appended to airdropped_files
and downloaded_files
. Both the airdropped_files
and downloaded_files
dictionaries are of the the same structure at this point:
{ "<file1 UID>": { "file_name": "", "file_path": "", "download_time": "", }, "<file2 UID>": { "file_name": "", "file_path": "", "download_time": "", }, ... }
Next a connection to the database is made and the following query is executed:
SELECT LSQuarantineEventIdentifier as EventID, datetime(LSQuarantineTimeStamp + strftime('%s','2001-01-01'), 'unixepoch') as Timestamp, LSQuarantineSenderName as SenderName, LSQuarantineOriginURLString as OriginURL, LSQuarantineDataURLString as DataURL, LSQuarantineAgentName as Agent FROM LSQuarantineEvent WHERE LSQuarantineAgentName IN ('sharingd', 'Chrome', 'Safari', 'Opera', 'Brave', 'Firefox');
The query simply pulls the information from following fields: LSQuarantineEventIdentifier
, LSQuarantineTimeStamp
, LSQuarantineSenderName
, LSQuarantineOriginURLString
, LSQuarantineDataURLString
, LSQuarantineAgentName
from the LSQuarantineEvent
table in the database. I give each of the field names as XYZ
aliases for clarity. It only pulls rows which also satisfy the requirement of the LSAgentName
being sharingd
, Chrome
, Safari
, Opera
, Firefox
or Brave
.
The query’s results then are iterated over and matched against the dictionary of AirDropped files (airdropped_files
) and downloaded files (downloaded_files
) by comparing the LSQuarantineEventIdentifier
to the file UID we have extracted using xattr
.
If there is a match for an AirDropped file, an additional key (sender_name
) value pair is added to file_list
so it becomes the following format:
{
"<file UID>":
{
"file_name": "",
"file_path": "",
"download_time": "",
"sender_name": ""
},
...
}
If there is a match for a downloaded file, additional keys of origin_url
, data_url
and browser_name
are added and populated with data creating a dictionary like so:
{ "<file UID>": { "file_name": "", "file_path": "", "download_time": "", "origin_url": "", "data_url": "", "browser_name": "" }, ... }
Next, if the --json
flag is present, the output is created using the json.dumps()
function on the two dictionaries – airdropped_files
and downloaded_files
and encapsulating it in yet another dictionary:
{
"aidropped_files":
{
"<file UID>":
{
"file_name": "",
"file_path": "",
"download_time": "",
"sender_name": ""
},
...
},
"downloaded_files":
{
"<file UID>":
{
"file_name": "",
"file_path": "",
"download_time": "",
"origin_url": "",
"data_url": "",
"browser_name": ""
},
...
}
}
This is so that it is straightforward to pipe the output of this script to other tools or commands.
If the --json
flag is not used, the pretty_print()
function is called for both of the dictionaries. pretty_print
provides an easily readable layout of the output, like we saw at the beginning of this section.
🔮🔮🔮
That’s it for this one, I hope you learned something new just like I did. Feel free to fork my project and add to it, I’d love to see what further work could be done. See you in the next one!
Sources
-
- https://0xmachos.com/2019-02-01-Quarantine-Intro/
- https://tuxera.com/community/ntfs-3g-advanced/extended-attributes/
- https://eclecticlight.co/2017/08/14/show-me-your-metadata-extended-attributes-in-macos-sierra/
- https://osxdaily.com/2018/05/03/view-remove-extended-attributes-file-mac/
- https://eclecticlight.co/2017/08/17/xattred-a-tool-for-setting-the-quarantine-xattr/