Skip to content

Instantly share code, notes, and snippets.

@TempAccountNull
Forked from MCJack123/ipsw_keys.py
Created October 5, 2019 02:30
Show Gist options
  • Save TempAccountNull/1c5bf5a7b324eae13b89cfd9024bc562 to your computer and use it in GitHub Desktop.
Save TempAccountNull/1c5bf5a7b324eae13b89cfd9024bc562 to your computer and use it in GitHub Desktop.
Fetch iOS firmware keys using on-device AES engine
#!/usr/bin/env python
# pip install future
from sys import argv, stdout
from os import system, remove
from urlparse import urlparse
import re
import dfu
import ssl
import asn1
import math
import json
import dfuexec
import httplib
import usbexec
import zipfile
import plistlib
dev = dfu.acquire_device()
serial_number = dev.serial_number
print("Found:" + serial_number)
if not "PWND:[" in serial_number:
print "Please enable pwned DFU Mode first."
exit(4)
cpid_m = re.search("CPID:([0-9A-F]{4})", serial_number)
if cpid_m == None:
print("Could not find CPID in serial")
exit(4)
cpid = cpid_m.group(1)
bdid_m = re.search("BDID:([0-9A-F][0-9A-F])", serial_number)
if bdid_m == None:
print("Could not find BDID in serial")
exit(4)
bdid = bdid_m.group(1)
dfu.release_device(dev)
def extractKeys(infile, outfile, delete=False):
print("Reading manifest...")
zip = zipfile.ZipFile(infile)
manifest = plistlib.readPlistFromString(zip.read("BuildManifest.plist"))
print("Reading keys...")
output = {}
try:
identity = next(item for item in manifest["BuildIdentities"] if item["ApChipID"] == "0x" + cpid and item["ApBoardID"] == "0x" + bdid)
except StopIteration:
print("Could not find identity for CPID " + cpid + " and BDID " + bdid + " in manifest")
exit(5)
if identity == None:
print("Could not find identity for CPID " + cpid + " and BDID " + bdid + " in manifest")
exit(5)
for k,v in identity["Manifest"].items():
if not "Path" in v["Info"].keys(): continue
if not (v["Info"]["Path"].endswith("im4p") or k == "RestoreRamDisk"): continue
dec = asn1.Decoder()
dec.start(zip.read(v["Info"]["Path"]))
#pretty_print(dec, stdout)
dec.enter()
if dec.read() == None:
print("Missing id 0")
continue
if dec.read() == None:
print("Missing id 1")
continue
if dec.read() == None:
print("Missing id 2")
continue
if dec.read() == None:
print("Missing id 3")
continue
if dec.eof():
output[k] = {"Path": v["Info"]["Path"], "Encrypted": False}
continue
tag, value = dec.read()
dec.start(value)
dec.enter()
dec.enter()
dec.read()
tag, ivenc = dec.read()
tag, keyenc = dec.read()
keys = None
if 'PWND:[checkm8]' in serial_number:
pwned = usbexec.PwnedUSBDevice()
keys = pwned.aes((ivenc + keyenc), usbexec.AES_DECRYPT, usbexec.AES_GID_KEY).encode('hex')
else:
device = dfuexec.PwnedDFUDevice()
keys = device.aes_hex((ivenc + keyenc), dfuexec.AES_DECRYPT, dfuexec.AES_GID_KEY)
output[k] = {"Path": v["Info"]["Path"], "Encrypted": True, "IV": keys[:32], "Key": keys[32:]}
file = open(outfile, "w")
json.dump(output, file)
file.close()
if delete:
print("Cleaning up...")
remove(infile)
print("Keys saved to " + outfile)
if __name__ == "__main__":
if len(argv) < 4:
print("Usage: " + argv[0] + " <device ID> <version> <output.json> [skip download]")
exit(1)
if len(argv) < 5:
def getFile(url):
uri = urlparse(url)
con = httplib.HTTPSConnection(uri.netloc, context=ssl.SSLContext(ssl.PROTOCOL_SSLv23))
con.request("GET", uri.path)
res = con.getresponse()
if math.floor(res.status / 100) != 2:
print(res.status)
return None
retval = res.read()
con.close()
return retval
firmwares_json = getFile("https://api.ipsw.me/v4/device/" + argv[1])
if firmwares_json == None:
print("Unknown device type " + argv[1])
exit(2)
firmwares = json.loads(firmwares_json)
if firmwares == None:
print("Error decoding firmwares JSON")
exit(2)
if argv[2] == "all":
# could this be a mistake to run?
for firm in firmwares["firmwares"]:
print("Downloading iOS " + firm["version"] + " (" + firm["buildid"] + ") for device " + firm["identifier"] + "...")
system("curl -o firmware.ipsw -L --progress-bar " + firm["url"])
extractKeys("firmware.ipsw", argv[3] + "/" + firm["identifier"] + "_" + firm["version"] + "_" + firm["buildid"] + "_Keys.json", True)
exit(0)
try:
firm = next(item for item in firmwares["firmwares"] if item["version"] == argv[2])
except StopIteration:
print("Unknown version " + argv[2] + " for device " + argv[1])
exit(3)
if firm == None:
print("Unknown version " + argv[2] + " for device " + argv[1])
exit(3)
print("Downloading iOS " + firm["version"] + " (" + firm["buildid"] + ") for device " + firm["identifier"] + "...")
system("curl -o firmware.ipsw -L --progress-bar " + firm["url"])
extractKeys("firmware.ipsw", argv[3], len(argv) < 5)

How to use this

  1. pip install asn1 future
  2. Download the script to the same folder as ipwndfu
  3. Boot the device in DFU mode and run checkm8 exploit
  4. Run the script with the device identifier, iOS version, and output JSON file

Example

./ipsw_keys.py iPad4,5 12.4.2 iPad4,5_12.4.2_Keys.json

Notes

  • Only IMG4 files are supported at the moment. This means only A7+ IPSWs can be used until I add IMG3 support.
  • The rootfs key is not extracted because the image is too big to use with the IMG4 parser. If you need the rootfs key, you will have to extract it yourself.
  • Using all for the iOS version will extract the keys from every available version for the device, and saves them at <output>/<device>_<version>_<build>_Keys.json. THIS WILL TAKE A LONG TIME!
  • If you add anything after the output file, the script will skip downloading and will instead look inside firmware.ipsw in the current directory.
  • The outputted JSON file will have this basic format:
{
    "BatteryLow0": {
        "Path": "Firmware/all_flash/batterylow0@2x~ipad.im4p",
        "Encrypted": false
    },
    [...]
    "iBSS": {
        "Path": "Firmware/dfu/iBSS.ipad4b.RELEASE.im4p",
        "Encrypted": true,
        "IV": "00112233445566778899aabbccddeeff",
        "Key": "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment