diff --git a/.gitignore b/.gitignore index 6a93e4e..2e400b4 100644 --- a/.gitignore +++ b/.gitignore @@ -162,4 +162,8 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ -attachments/ \ No newline at end of file +attachments/ + +proxy/hosts.proxy +proxy/lldb_commands.txt +proxy/imessage_proxy.pac \ No newline at end of file diff --git a/albert.py b/albert.py index a4f39f2..e1b9e97 100644 --- a/albert.py +++ b/albert.py @@ -1,7 +1,7 @@ import plistlib import re import uuid -from base64 import b64decode, b64encode +from base64 import b64decode import requests from cryptography import x509 diff --git a/apns.py b/apns.py index 4ee9c38..0d58806 100644 --- a/apns.py +++ b/apns.py @@ -21,7 +21,10 @@ import bags logger = logging.getLogger("apns") # Pick a random courier server from 01 to APNSCourierHostcount -COURIER_HOST = f"{random.randint(1, bags.apns_init_bag()['APNSCourierHostcount'])}-{bags.apns_init_bag()['APNSCourierHostname']}" +try: + COURIER_HOST = f"{random.randint(1, bags.apns_init_bag()['APNSCourierHostcount'])}-{bags.apns_init_bag()['APNSCourierHostname']}" +except: + COURIER_HOST = "01-courier.push.apple.com" COURIER_PORT = 5223 ALPN = [b"apns-security-v3"] diff --git a/ids/_helpers.py b/ids/_helpers.py index a10fd79..7a905b6 100644 --- a/ids/_helpers.py +++ b/ids/_helpers.py @@ -24,7 +24,7 @@ def parse_key(key: str): else: return serialization.load_pem_private_key(key.encode(), None) -def serialize_key(key): +def serialize_key(key) -> str: if isinstance(key, ec.EllipticCurvePrivateKey) or isinstance(key, rsa.RSAPrivateKey): return key.private_bytes( encoding=serialization.Encoding.PEM, diff --git a/proxy/proxy.py b/proxy/proxy.py index 317e939..e425115 100644 --- a/proxy/proxy.py +++ b/proxy/proxy.py @@ -1,17 +1,21 @@ +import os import sys -sys.path.append("../") -sys.path.append("../../") -import apns -import trio -import ssl +# setting path so we can import the needed packages +sys.path.append(os.path.join(sys.path[0], "../")) +sys.path.append(os.path.join(sys.path[0], "../../")) -import logging -from rich.logging import RichHandler -from hashlib import sha1 -import plistlib import gzip +import logging +import plistlib +import ssl +from hashlib import sha1 +import trio +from rich.logging import RichHandler + +import printer +import apns logging.basicConfig( level=logging.NOTSET, @@ -26,7 +30,8 @@ async def main(): context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH) context.set_alpn_protocols(["apns-security-v3"]) # Set the certificate and private key - context.load_cert_chain("push_certificate_chain.pem", "push_key.pem") + parent_dir: str = os.path.dirname(os.path.realpath(__file__)) + context.load_cert_chain(os.path.join(parent_dir, "push_certificate_chain.pem"), os.path.join(parent_dir, "push_key.pem")) await trio.serve_ssl_over_tcp(handle_proxy, 5223, context) @@ -43,13 +48,22 @@ class APNSProxy: self.client = client async def start(self): + logging.info("Starting proxy...") async with trio.open_nursery() as nursery: - apns_server = apns.APNSConnection(nursery) - await apns_server._connect_socket() - self.server = apns_server.sock + while True: + try: + apns_server = apns.APNSConnection(nursery) + await apns_server._connect_socket() + self.server = apns_server.sock + + nursery.start_soon(self.proxy, True) + nursery.start_soon(self.proxy, False) + + break # Will only happen if there is no exception + except Exception: + logging.error("Unable to start proxy, trying again...") + await trio.sleep(1) - nursery.start_soon(self.proxy, True) - nursery.start_soon(self.proxy, False) async def proxy(self, to_server: bool): @@ -66,7 +80,6 @@ class APNSProxy: await payload.write_to_stream(to_stream) def log(self, payload: apns.APNSPayload, to_server: bool): - import printer printer.print_payload(payload, to_server) # if to_server: # logging.info(f"-> {payload}") diff --git a/proxy/start_proxy.sh b/proxy/start_proxy.sh new file mode 100755 index 0000000..558357c --- /dev/null +++ b/proxy/start_proxy.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +set -euo pipefail + +err() { + echo -e "\e[31m[!]\e[0m ${1}" 1>&2 +} + +inf() { + echo -e "\e[34m[*]\e[0m ${1}" +} + +leave() { + [ -f /etc/hosts.bak ] && { inf "Fixing /etc/hosts..." && sudo mv /etc/hosts.bak /etc/hosts; } + + [ -z ${lldb_pid+x} ] || { inf "Killing attached lldb..." && { sudo kill "$lldb_pid" 2>/dev/null || :; }; } + [ -z ${mitm_pid+x} ] || { inf "Killing mitmweb..." && { kill "$mitm_pid" 2>/dev/null || :; }; } + [ -z ${proxy_pid+x} ] || { inf "Killing proxy..." && { kill "$proxy_pid" 2>/dev/null || :; }; } + + cd "$old_dir" || : + + exit 0 +} + +[[ "$(uname)" != "Darwin" ]] && { err "This can only be run on macOS" && exit 1; } + +old_dir="$(pwd)" +root_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +full_dir="$root_dir" +[[ "$old_dir" == "$root_dir" ]] && root_dir="." + +trap 'leave' INT ERR + +proxy_dir="${root_dir}" + +inf "Setting up \e[1mhosts.proxy\e[0;34m..." + +hosts_proxy="${proxy_dir}/hosts.proxy" +cat /etc/hosts > "$hosts_proxy" +python3 "${proxy_dir}/hosts.py" >> "$hosts_proxy" + +echo -e "\e[32m[?]\e[0;1m ${hosts_proxy}\e[0m must be copied over to /etc/hosts. Would you like us to do that for you? [y/n]" +read -rn1 answer +if [[ "${answer,,}" == "y" ]] +then + inf "Backing up /etc/hosts to /etc/hosts.bak and copying ${hosts_proxy} to /etc/hosts" + sudo cp /etc/hosts /etc/hosts.bak + sudo cp "$hosts_proxy" /etc/hosts +fi + +lldb_commands="${proxy_dir}/lldb_commands.txt" +cat << EOF > "$lldb_commands" +breakpoint set -n "SecTrustEvaluateWithError" -C "thread return 1" -C "c" +c +EOF + +inf "Attaching to lldb..." +env TERM=xterm-256color sudo lldb -p $(pgrep apsd) -s "$lldb_commands" >/dev/null & +lldb_pid=$! + +cat << EOF > "${proxy_dir}/imessage_proxy.pac" +// https://en.wikipedia.org/wiki/Proxy_auto-config for reference +function FindProxyForURL(url, host) { + // to redirect apns tcp traffic + if (shExpMatch(host, '*-courier.push.apple.com')) { + // this should redirect it to mitmproxy if it's running. + // should 127.0.0.1:8080 fail to respond, it should just forward it + return 'PROXY 127.0.0.1:8080; DIRECT'; + } + + // to redirect ids stuff + if (shExpMatch(host, '*ess.apple.com')) { + return 'PROXY 127.0.0.1:8080; DIRECT'; + } + + // for everything else, just forward it + return 'DIRECT' +} +EOF + +inf "Setting up proxy auto-config..." +networksetup -setautoproxyurl Wi-Fi "file://${full_dir}/imessage_proxy.pac" + +inf "Starting up mitmweb..." +mitmweb & +mitm_pid=$! + +inf "Running apns proxy..." +python3 "${proxy_dir}/proxy.py" & +proxy_pid=$! + +# need to give the proxy a second to start up or it yells at us +sleep 1 + +inf "Restarting wifi to force apsd to reconnect..." +networksetup -setairportpower en0 off +networksetup -setairportpower en0 on + +while true; do read -rn1 _; done \ No newline at end of file