lots and lots of squashed stuff!!

This commit is contained in:
JJTech0130 2023-05-02 08:39:11 -04:00
parent 8b5bcf2461
commit d66e3e0adf
No known key found for this signature in database
GPG key ID: 23C92EBCCF8F93D6
3 changed files with 150 additions and 156 deletions

95
demo.py Normal file
View file

@ -0,0 +1,95 @@
from ids import *
import ids
import getpass
import json
# Open config
try:
with open("config.json", "r") as f:
config = json.load(f)
except FileNotFoundError:
config = {}
# If no username is set, prompt for it
if "username" not in config:
config["username"] = input("Enter iCloud username: ")
# If no password is set, prompt for it
if "password" not in config:
config["password"] = getpass.getpass("Enter iCloud password: ")
# If grandslam authentication is not set, prompt for it
if "use_gsa" not in config:
config["use_gsa"] = input("Use grandslam authentication? [y/N] ").lower() == "y"
def factor_gen():
return input("Enter iCloud 2FA code: ")
user_id, token = ids._get_auth_token(
config["username"], config["password"], config["use_gsa"], factor_gen=factor_gen
)
config["user_id"] = user_id
config["token"] = token
key, cert = ids._get_auth_cert(user_id, token)
config["key"] = key
config["cert"] = cert
conn1 = apns.APNSConnection()
conn1.connect()
conn1.filter(["com.apple.madrid"])
info = {
"uri": "mailto:" + config["username"],
"user_id": user_id,
}
resp = None
try:
if "validation_data" in config:
resp = ids._register_request(
b64encode(conn1.token),
info,
cert,
key,
conn1.cert,
conn1.private_key,
config["validation_data"],
)
except Exception as e:
print(e)
resp = None
if resp is None:
print(
"Note: Validation data can be obtained from @JJTech, or intercepted using a HTTP proxy."
)
validation_data = (
input_multiline("Enter validation data: ")
.replace("\n", "")
.replace(" ", "")
)
resp = ids._register_request(
b64encode(conn1.token),
info,
cert,
key,
conn1.cert,
conn1.private_key,
validation_data,
)
config["validation_data"] = validation_data
madrid_cert = x509.load_der_x509_certificate(
resp["services"][0]["users"][0]["cert"]
)
madrid_cert = (
madrid_cert.public_bytes(serialization.Encoding.PEM).decode("utf-8").strip()
)
config["madrid_cert"] = madrid_cert
# Save config
with open("config.json", "w") as f:
json.dump(config, f, indent=4)

30
gsa.py
View file

@ -18,7 +18,8 @@ import getpass
DEBUG = True # Allows using a proxy for debugging (disables SSL verification) DEBUG = True # Allows using a proxy for debugging (disables SSL verification)
# Server to use for anisette generation # Server to use for anisette generation
#ANISETTE = "https://sign.rheaa.xyz/" #ANISETTE = "https://sign.rheaa.xyz/"
ANISETTE = 'http://45.132.246.138:6969/' #ANISETTE = 'http://45.132.246.138:6969/'
ANISETTE = False
# ANISETTE = 'https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx' # ANISETTE = 'https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx'
# ANISETTE = "http://jkcoxson.com:2052/" # ANISETTE = "http://jkcoxson.com:2052/"
@ -33,15 +34,34 @@ urllib3.disable_warnings()
def generate_anisette() -> dict: def generate_anisette() -> dict:
r = requests.get(ANISETTE, verify=False if DEBUG else True, timeout=5) import objc
r = json.loads(r.text) from Foundation import NSBundle, NSClassFromString #type: ignore
return r
AOSKitBundle = NSBundle.bundleWithPath_('/System/Library/PrivateFrameworks/AOSKit.framework')
objc.loadBundleFunctions(AOSKitBundle, globals(), [("retrieveOTPHeadersForDSID", b'')]) #type: ignore
util = NSClassFromString('AOSUtilities')
h = util.retrieveOTPHeadersForDSID_("-2")
o = {
"X-Apple-I-MD": str(h["X-Apple-MD"]),
"X-Apple-I-MD-M": str(h["X-Apple-MD-M"]),
}
#h["X-Apple-I-MD"] = str(h["X-Apple-MD"])
#h["X-Apple-I-MD-M"] = str(h["X-Apple-MD-M"])
#print(o)
return o
#r = requests.get(ANISETTE, verify=False if DEBUG else True, timeout=5)
#r = json.loads(r.text)
#return r
class Anisette: class Anisette:
@staticmethod @staticmethod
def _fetch(url: str) -> dict: def _fetch(url: str) -> dict:
"""Fetches anisette data that we cannot calculate from a remote server""" """Fetches anisette data that we cannot calculate from a remote server"""
if url == False:
return generate_anisette()
r = requests.get(url, verify=False if DEBUG else True, timeout=5) r = requests.get(url, verify=False if DEBUG else True, timeout=5)
r = json.loads(r.text) r = json.loads(r.text)
return r return r
@ -507,5 +527,5 @@ def authenticate(username, password, anisette: Anisette):
print(f"Unknown auth value {r['Status']['au']}") print(f"Unknown auth value {r['Status']['au']}")
return return
else: else:
print("Assuming 2FA is not required") #print("Assuming 2FA is not required")
return spd return spd

181
ids.py
View file

@ -6,19 +6,21 @@ from base64 import b64decode, b64encode
from datetime import datetime from datetime import datetime
import requests import requests
from cryptography import x509
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.x509.oid import NameOID
import apns import apns
import bags import bags
import gsa import gsa
USER_AGENT = "com.apple.madrid-lookup [macOS,13.2.1,22D68,MacBookPro18,3]" #USER_AGENT = "com.apple.madrid-lookup [macOS,13.2.1,22D68,MacBookPro18,3]"
# NOTE: The push token MUST be registered with the account for self-uri! # NOTE: The push token MUST be registered with the account for self-uri!
# This is an actual valid one for my account, since you can look it up anyway. # This is an actual valid one for my account, since you can look it up anyway.
PUSH_TOKEN = "5V7AY+ikHr4DiSfq1W2UBa71G3FLGkpUSKTrOLg81yk=" #PUSH_TOKEN = "5V7AY+ikHr4DiSfq1W2UBa71G3FLGkpUSKTrOLg81yk="
SELF_URI = "mailto:jjtech@jjtech.dev" #SELF_URI = "mailto:jjtech@jjtech.dev"
# Nonce Format: # Nonce Format:
@ -189,13 +191,6 @@ def _get_auth_token(
return realm_user_id, auth_token return realm_user_id, auth_token
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.x509.oid import NameOID
def _generate_csr(private_key: rsa.RSAPrivateKey) -> str: def _generate_csr(private_key: rsa.RSAPrivateKey) -> str:
csr = ( csr = (
x509.CertificateSigningRequestBuilder() x509.CertificateSigningRequestBuilder()
@ -229,8 +224,6 @@ def _get_auth_cert(user_id, token) -> tuple[str, str]:
"realm-user-id": user_id, "realm-user-id": user_id,
} }
# print(body["csr"])
body = plistlib.dumps(body) body = plistlib.dumps(body)
r = requests.post( r = requests.post(
@ -242,10 +235,7 @@ def _get_auth_cert(user_id, token) -> tuple[str, str]:
r = plistlib.loads(r.content) r = plistlib.loads(r.content)
if r["status"] != 0: if r["status"] != 0:
raise (Exception(f"Failed to get auth cert: {r}")) raise (Exception(f"Failed to get auth cert: {r}"))
# return b64encode(r["cert"]).decode()
# cert = x509.load_pem_x509_certificate(b64encode(r["cert"]).decode())
cert = x509.load_der_x509_certificate(r["cert"]) cert = x509.load_der_x509_certificate(r["cert"])
# cert = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
return ( return (
private_key.private_bytes( private_key.private_bytes(
encoding=serialization.Encoding.PEM, encoding=serialization.Encoding.PEM,
@ -258,95 +248,27 @@ def _get_auth_cert(user_id, token) -> tuple[str, str]:
) )
def _register_request(push_token, user_id, auth_cert, auth_key, push_cert, push_key): def _register_request(
# body = {'device-name': 'Test'} push_token, info, auth_cert, auth_key, push_cert, push_key, validation_data
):
body = { body = {
"device-name": "Jamess Laptop",
"hardware-version": "MacBookPro18,3", "hardware-version": "MacBookPro18,3",
"language": "en-US", "language": "en-US",
"os-version": "macOS,13.2.1,22D68", "os-version": "macOS,13.2.1,22D68",
# "private-device-data": { "software-version": "22D68",
# "ap": "0",
# "d": "703987510.082306",
# "dt": 1,
# "gt": "0",
# "h": "1",
# "ktf": "0",
# "ktv": 54,
# "m": "0",
# "p": "0",
# "pb": "22D68",
# "pn": "macOS",
# "pv": "13.2.1",
# "s": "0",
# "t": "0",
# "u": "E451BD65-51B0-44F3-805A-A92BDD8A5000",
# "v": "1",
# },
"retry-count": 0,
"services": [ "services": [
{ {
"capabilities": [{"flags": 1, "name": "Messenger", "version": 1}], "capabilities": [{"flags": 1, "name": "Messenger", "version": 1}],
"service": "com.apple.madrid", "service": "com.apple.madrid",
# "sub-services": [
# "com.apple.private.alloy.gelato",
# "com.apple.private.alloy.gamecenter.imessage",
# "com.apple.private.alloy.sms",
# "com.apple.private.alloy.biz",
# ],
"users": [ "users": [
# { {
# "client-data": { "uris": [{"uri": info["uri"]}],
# "ec-version": 1, "user-id": info["user_id"],
# "kt-version": 5, }
# "nicknames-version": 1,
# "optionally-receive-typing-indicators": True,
# "prefers-sdr": False,
# "public-message-identity-key": b"0\x81\xf6\x81C\x00A\x04\x87\x1e\xeb\xe4u\x0b\xa3\x9e\x9c\xbc\xf8rK\x1e\xfe44%f$\x1d\xe8\xbb\xc6\xbdCD\x9ckv K\xc1\x1e\xb1\xdf4\xc8S6\x0f\x92\xd0=\x1e\x84\x9c\xc5\xa5\xb6\xb7}\xdd\xec\x1e\x1e\xd8Q\xd8\xca\xdb\x07'\xc7\x82\x81\xae\x00\xac0\x81\xa9\x02\x81\xa1\x00\xa8 \xfc\x9f\xa6\xb0V2\xce\x1c\xa7\x13\x9e\x03\xd1\xd8\x97a\xbb\xdd\xac\x86\xb8\x10(\x89\x13QP\x8f\xf0+EP\xd1\xb06\xee\x94\xcd\xa8\x9e\xf1\xedp\xa4\x9726\x1e\xe9\xab\xd4\xcb\xac\x05\xd7\x8c?\xbb\xa2\xde,\xfe\r\x1a\xb9\x88W@\x99\xec\xa0]\r\x1a>dV\xb2@\xc5P\xf3m\x80y\xf5\xa0G\xae\xd8h\x92\xef\xca\x85\xcbB\xed\xa9W\x8c\x13\xd4O\xdbYI2\xdcM\x1f\xf6c\x17\x1c\xd1v\xdd\xbcc\xac,&V\xfd\x07\xa0\xc3\x9f\x00\x1f\xc6\xe4\x02u\x12p\x8f\xe2\xb0\x14\xfai\x12\xbb\xa6\x9a6Q\xa5\xde+\x9e{\xcf\xc8\x1b}\x02\x03\x01\x00\x01",
# "public-message-identity-ngm-version": 12,
# "public-message-identity-version": 2,
# "public-message-ngm-device-prekey-data-key": b"\n v\xd6=#\xb7\xde\xe9~n\xdd\x94|xw\x1c#W\xa5\xe0\xf3\x15\xed\xd1\xa0\xab\xa8\xfd\xa9\x8c\xdd\x16\xb0\x12@x\xb4\xafE\xc4\x1b\xcd\xe9\xb1\x8f\x8aI\x03\xd2&F\x95\x9b\x99R\x92\x07/\x12\xaeM\x10\xf3\xa2u\x7f5]\xd9\x19\xc3\x91\xb5\xb4\xbdO\x9c\x1f\r\xae\xa9\xf3+\x9c\x00M2\x83\x147\xb3X\xa11\x00\xaeV\xbe_\x19\x10\xafg\x19\xbf\x10\xd9A",
# "supports-ack-v1": True,
# "supports-animoji-v2": True,
# "supports-audio-messaging-v2": True,
# "supports-autoloopvideo-v1": True,
# "supports-be-v1": True,
# "supports-ca-v1": True,
# "supports-certified-delivery-v1": True,
# "supports-cross-platform-sharing": True,
# "supports-dq-nr": True,
# "supports-fsm-v1": True,
# "supports-fsm-v2": True,
# "supports-fsm-v3": True,
# "supports-hdr": True,
# "supports-heif": True,
# "supports-ii-v1": True,
# "supports-impact-v1": True,
# "supports-inline-attachments": True,
# "supports-keep-receipts": True,
# "supports-location-sharing": True,
# "supports-media-v2": True,
# "supports-original-timestamp-v1": True,
# "supports-people-request-messages": True,
# "supports-people-request-messages-v2": True,
# "supports-photos-extension-v1": True,
# "supports-photos-extension-v2": True,
# "supports-protobuf-payload-data-v2": True,
# "supports-rem": True,
# "supports-sa-v1": True,
# "supports-st-v1": True,
# "supports-update-attachments-v1": True,
# },
# #"kt-loggable-data": b'\n"\n \rl\xbe\xca\xf7\xe8\xb2\x89k\x18\x1e\xb9,d\xf8\xe2\n\xbf\x8d\xe1E\xd6\xf3T\xcb\xd9\x99d\xd1mk\xeb\x10\x0c\x18\x05"E\x08\x01\x12A\x04\x99\x16\xc3\xd8\x85\x80qPr\xbf\x0c\xdb\x9f\x1bHK\xb2:)\x01\x88\x91\xb1\x08do\xf3\x16\xc7\xaa\xd3nb\xddQF\x8f\xb2a\xb1\xbbK\xdf\xd0\xfa\x95\xa29XZ\xcaRh\xbex\xc4f\xe6G`\x1f\xf2\xf3[',
# "uris": [{"uri": "mailto:user_test2@icloud.com"}],
# "user-id": "D:20994360971",
# }
], ],
} }
], ],
"software-version": "22D68", "validation-data": b64decode(validation_data),
# "validation-data": b"v\x02V`\xd8N\xf3V\xb0\xc4'=\x137+\x16-o\x1d\x00\x1c\xf53\xe0g\xd9\x83x\xe0\xderh\xa0\xc7\x00\x00\x01\xe0\x07\x00\x00\x00\x01\x00\x00\x01\x806\x81<\xb9G\xf0,(CQX\xc5\x1e\xbc\xfe}a\xf7y\xfcg\xa1j/B\xb6k\xf4\xeey\x7f=e\xe6\xea\xa3\xfd\xb2\x18\x8e.\x19\x9e'\x9eO]\xca\xe3{\x9f\x10I\x94\x1a\xe0\xef>\xce\x9dl\xb0\xb2u\x88KT\x0b\xcc\x915\x92\xd3\x86\x1b\xb3\xe5\x04\x9f\x8d\x8a\x82$\x11\xfb\xf2t\xda&\x96@U<lP\"/\xf6->\xec\x84\x13\xe7p\xffS\x02\xde\x8c\xdd\xfcqA\x14!\xa4\x07\x82\xd0\x9fm\xe9~\xc4\xcf\x96\xd6D\xa3\xf0\xb9\xa7\xa2}\xc5\x0e.\x0fvYz\x07\xc2\x9f$s:\xd4<\x13u]\x06f[\xcd\x95\xd1\xad\xe9\xb3\xb3\x9f|\nh-\xa2\xa6\xb9c\xa1\x8d\xf2gx\x84\xbe\x1d\xc4\x03}^\xbf\x9ck#\xa8\xad\xa5\x87\x04\x88D\xd0\xee>\x9f\x0f\xa63;\x7fE\x14\x89\x1c]\x8b\x13o\xbd\xf6\x84`R\xa2\xb7Z\xcc\xdf+\xc5\xe5>\xf73?\x84\xe2d\x97\xd3\x07\x10V\xb6\xb4\nB7\xfc\x8bReeA\x15t\x94\xcf\xa8\x957\x1f\x1d>\xe1\xa4\xc5X\xb0!\x81\xcah\x11.'$\xf6\x12\xb3`\xe9\xa93\x07\xe4}\x02\xee\x95hW\xb7\xfb '_\xafC.\xdd\x13\x8df\xcf(H\x06\x18\xe2\xe7\xce\x93+\xf9\xe0\xf5\x17r\xb5.g2M\x8a\xb7\x80j\xb3\x00*\xa6Z\x1f\x07\xf3\x82\xcfj\xd2R\xc1%\xcc\xbe\x10\x9c\xb4qp\x1f\xd7W\x8c\x1aL2\xfa\xeb\x01s\x01m\xa3$\x9cJ\xf8X?\x99r`pT\xa7\xa6\x80\xcc\xc0\x97\xb3|[%\xc8Usj\x1d\x00\x00\x00\x00\x00\x00\x00O\x01P\xdc\"\x98\xc3\xd9\xb5\xbaK\xdc.\xeb\x81\x87r\x8d\xa4q^\xcb\x00\x00\x006\t\x01\x92\x91\x8fI\xff\xff\xd5g\xf2\x1d\x04G\xf4_\xe4\x90dE\xc7\xb7\xcaO~V\x1e[\x7ff?e|d\xae\xb8\x92(F\xd1_\xe9\xb2\x9e\xb0\xf1\x99\x92\x07\xf8\xb2&I\xed",
"validation-data": random.randbytes(10),
} }
body = plistlib.dumps(body) body = plistlib.dumps(body)
@ -363,7 +285,7 @@ def _register_request(push_token, user_id, auth_cert, auth_key, push_cert, push_
"x-auth-cert-0": auth_cert.replace("\n", "") "x-auth-cert-0": auth_cert.replace("\n", "")
.replace("-----BEGIN CERTIFICATE-----", "") .replace("-----BEGIN CERTIFICATE-----", "")
.replace("-----END CERTIFICATE-----", ""), .replace("-----END CERTIFICATE-----", ""),
"x-auth-user-id-0": user_id, "x-auth-user-id-0": info["user_id"],
"x-auth-nonce-0": b64encode(auth_nonce), "x-auth-nonce-0": b64encode(auth_nonce),
"x-pr-nonce": b64encode(auth_nonce), "x-pr-nonce": b64encode(auth_nonce),
"x-push-token": push_token, "x-push-token": push_token,
@ -374,68 +296,25 @@ def _register_request(push_token, user_id, auth_cert, auth_key, push_cert, push_
"x-push-nonce": b64encode(push_nonce), "x-push-nonce": b64encode(push_nonce),
} }
# headers.update(gsa.Anisette().generate_headers())
r = requests.post( r = requests.post(
"https://identity.ess.apple.com/WebObjects/TDIdentityService.woa/wa/register", "https://identity.ess.apple.com/WebObjects/TDIdentityService.woa/wa/register",
headers=headers, headers=headers,
data=body, data=body,
verify=False, verify=False,
) )
print(r.text) r = plistlib.loads(r.content)
print(f'Response code: {r["status"]}')
if "status" in r and r["status"] == 6004:
raise Exception("Validation data expired!")
return r
def test(): def input_multiline(prompt):
import getpass print(prompt)
import json lines = []
while True:
# Open config as read and write line = input()
if line == "":
try: break
with open("config.json", "r") as f: lines.append(line)
config = json.load(f) return "\n".join(lines)
except FileNotFoundError:
config = {}
# If no username is set, prompt for it
if "username" not in config:
config["username"] = input("Enter iCloud username: ")
# If no password is set, prompt for it
if "password" not in config:
config["password"] = getpass.getpass("Enter iCloud password: ")
# If grandslam authentication is not set, prompt for it
if "use_gsa" not in config:
config["use_gsa"] = input("Use grandslam authentication? [y/N] ").lower() == "y"
def factor_gen():
return input("Enter iCloud 2FA code: ")
user_id, token = _get_auth_token(
config["username"], config["password"], config["use_gsa"], factor_gen=factor_gen
)
config["user_id"] = user_id
config["token"] = token
key, cert = _get_auth_cert(user_id, token)
config["key"] = key
config["cert"] = cert
# print(key, cert)
conn1 = apns.APNSConnection()
conn1.connect()
conn1.filter(["com.apple.madrid"])
_register_request(
b64encode(conn1.token), user_id, cert, key, conn1.cert, conn1.private_key
)
# Save config
with open("config.json", "w") as f:
json.dump(config, f, indent=4)
if __name__ == "__main__":
test()