Skip to content

Commit

Permalink
rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
Lrdsnow committed Sep 2, 2024
1 parent 8a33c17 commit 46fd732
Show file tree
Hide file tree
Showing 10 changed files with 464 additions and 7 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
how to use:
- install `pymobiledevice3`
- run `app.py`
# EU Enabler

requires python 3.12

only works in the US for rn
## How to use
1. ensure and install python3 and git if you do not have it
2. open a terminal and run `git clone https://github.com/Lrdsnow/EUEnabler.git`
3. run `cd EUEnabler`
4. run `python3 -m pip install pymobiledevice3` if it is not installed
5. run `python3 app.py`
6. follow the instructions and enjoy!
18 changes: 17 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
from exploit.restore import restore_file
import restore
from pathlib import Path
import plistlib

region_code = input("Enter your 2-letter region code (default to US): ").strip().upper() or "US"

def replace_region_code(plist_path, original_code="US", new_code="US"):
with open(plist_path, 'rb') as f:
plist_data = plistlib.load(f)

plist_str = str(plist_data)
updated_plist_str = plist_str.replace(original_code, new_code)
updated_plist_data = eval(updated_plist_str) # Convert string back to dictionary

with open(plist_path, 'wb') as f:
plistlib.dump(updated_plist_data, f)

file_path = Path.joinpath(Path.cwd(), 'eligibility.plist')
replace_region_code(file_path, original_code="US", new_code=region_code)
restore_file(fp=file_path, restore_path='/var/db/os_eligibility/', restore_name='eligibility.plist')

file_path = Path.joinpath(Path.cwd(), 'Config.plist')
replace_region_code(file_path, original_code="US", new_code=region_code)
restore_file(fp=file_path, restore_path='/var/MobileAsset/AssetsV2/com_apple_MobileAsset_OSEligibility/purpose_auto/c55a421c053e10233e5bfc15c42fa6230e5639a9.asset/AssetData/', restore_name='Config.plist')

print("Reboot to see changes!")
Binary file removed exploit/__init__.pyc
Binary file not shown.
Binary file removed exploit/backup.pyc
Binary file not shown.
Binary file removed exploit/mbdb.pyc
Binary file not shown.
Binary file removed exploit/restore.pyc
Binary file not shown.
46 changes: 46 additions & 0 deletions restore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from sparserestore import backup
from pymobiledevice3.lockdown import create_using_usbmux
from pymobiledevice3.services.mobilebackup2 import Mobilebackup2Service
from tempfile import TemporaryDirectory
from pathlib import Path

def restore_file(fp: str, restore_path: str, restore_name: str):
contents = open(fp, 'rb').read()
back = backup.Backup(
files=[
backup.Directory("", "RootDomain"),
backup.Directory("Library", "RootDomain"),
backup.Directory("Library/Preferences", "RootDomain"),
backup.ConcreteFile("Library/Preferences/temp", "RootDomain", owner=33, group=33, contents=contents, inode=0),
backup.Directory(
"",
f"SysContainerDomain-../../../../../../../..{restore_path}",
owner=33,
group=33,
),
backup.ConcreteFile(
"",
f"SysContainerDomain-../../../../../../../..{restore_path}/{restore_name}",
owner=33,
group=33,
contents=b"",
inode=0,
),
backup.ConcreteFile(
"",
"SysContainerDomain-../../../../../../../../var/.backup.i/var/root/Library/Preferences/temp",
owner=501,
group=501,
contents=b"",
), # Break the hard link
backup.ConcreteFile("", "SysContainerDomain-../../../../../../../.." + "/crash_on_purpose", contents=b""),
]
)
with TemporaryDirectory() as backup_dir:
backup_dir_path = Path(backup_dir)
back.write_to_directory(backup_dir_path)
print(f'Backup written to {backup_dir}')
input('Press Enter to continue...')
lockdown = create_using_usbmux()
with Mobilebackup2Service(lockdown) as mb:
mb.restore(backup_dir, system=True, reboot=False, copy=False, source='.')
40 changes: 40 additions & 0 deletions sparserestore/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from tempfile import TemporaryDirectory
from pathlib import Path

from pymobiledevice3.lockdown import create_using_usbmux
from pymobiledevice3.services.mobilebackup2 import Mobilebackup2Service
from pymobiledevice3.exceptions import PyMobileDevice3Exception

from . import backup
from .backup import _FileMode as FileMode

def perform_restore(backup: backup.Backup, reboot: bool = False):
with TemporaryDirectory() as backup_dir:
backup.write_to_directory(Path(backup_dir))

lockdown = create_using_usbmux()
with Mobilebackup2Service(lockdown) as mb:
mb.restore(backup_dir, system=True, reboot=False, copy=False, source=".")

def exploit_write_file(file: backup.BackupFile):
# Exploits in use:
# - Path after SysContainerDomain- or SysSharedContainerDomain- is not sanitized
# - SysContainerDomain will follow symlinks

# /var/.backup.i/var/mobile/Library/Backup/System Containers/Data/com.container.name
# ../ ../ ../ ../ ../ ../ ../ ../
ROOT = "SysContainerDomain-../../../../../../../.."
file.domain = ROOT + file.path
file.path = ""

back = backup.Backup(files=[
file,
# Crash on purpose so that a restore is not actually applied
backup.ConcreteFile("", ROOT + "/crash_on_purpose", contents=b"")
])

try:
perform_restore(back)
except PyMobileDevice3Exception as e:
if "crash_on_purpose" not in str(e):
raise e
185 changes: 185 additions & 0 deletions sparserestore/backup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
from dataclasses import dataclass
from datetime import datetime
import plistlib
from pathlib import Path
from base64 import b64decode
from hashlib import sha1
from . import mbdb
from .mbdb import _FileMode
from random import randbytes
from typing import Optional

# RWX:RX:RX
DEFAULT = _FileMode.S_IRUSR | _FileMode.S_IWUSR | _FileMode.S_IXUSR | _FileMode.S_IRGRP | _FileMode.S_IXGRP | _FileMode.S_IROTH | _FileMode.S_IXOTH

@dataclass
class BackupFile:
path: str
domain: str

def to_record(self) -> mbdb.MbdbRecord:
raise NotImplementedError()

@dataclass
class ConcreteFile(BackupFile):
contents: bytes
owner: int = 0
group: int = 0
inode: Optional[int] = None
mode: _FileMode = DEFAULT

def to_record(self) -> mbdb.MbdbRecord:
if self.inode is None:
self.inode = int.from_bytes(randbytes(8), "big")
return mbdb.MbdbRecord(
domain=self.domain,
filename=self.path,
link="",
hash=sha1(self.contents).digest(),
key=b"",
mode=self.mode | _FileMode.S_IFREG,
#unknown2=0,
#unknown3=0,
inode=self.inode,
user_id=self.owner,
group_id=self.group,
mtime=int(datetime.now().timestamp()),
atime=int(datetime.now().timestamp()),
ctime=int(datetime.now().timestamp()),
size=len(self.contents),
flags=4,
properties=[]
)

@dataclass
class Directory(BackupFile):
owner: int = 0
group: int = 0
mode: _FileMode = DEFAULT

def to_record(self) -> mbdb.MbdbRecord:
return mbdb.MbdbRecord(
domain=self.domain,
filename=self.path,
link="",
hash=b"",
key=b"",
mode=self.mode | _FileMode.S_IFDIR,
#unknown2=0,
#unknown3=0,
inode=0, # inode is not respected for directories
user_id=self.owner,
group_id=self.group,
mtime=int(datetime.now().timestamp()),
atime=int(datetime.now().timestamp()),
ctime=int(datetime.now().timestamp()),
size=0,
flags=4,
properties=[]
)

@dataclass
class SymbolicLink(BackupFile):
target: str
owner: int = 0
group: int = 0
inode: Optional[int] = None
mode: _FileMode = DEFAULT

def to_record(self) -> mbdb.MbdbRecord:
if self.inode is None:
self.inode = int.from_bytes(randbytes(8), "big")
return mbdb.MbdbRecord(
domain=self.domain,
filename=self.path,
link=self.target,
hash=b"",
key=b"",
mode=self.mode | _FileMode.S_IFLNK,
#unknown2=0,
#unknown3=0,
inode=self.inode,
user_id=self.owner,
group_id=self.group,
mtime=int(datetime.now().timestamp()),
atime=int(datetime.now().timestamp()),
ctime=int(datetime.now().timestamp()),
size=0,
flags=4,
properties=[]
)

@dataclass
class Backup:
files: list[BackupFile]

def write_to_directory(self, directory: Path):
for file in self.files:
if isinstance(file, ConcreteFile):
#print("Writing", file.path, "to", directory / sha1((file.domain + "-" + file.path).encode()).digest().hex())
with open(directory / sha1((file.domain + "-" + file.path).encode()).digest().hex(), "wb") as f:
f.write(file.contents)

with open(directory / "Manifest.mbdb", "wb") as f:
f.write(self.generate_manifest_db().to_bytes())

with open(directory / "Status.plist", "wb") as f:
f.write(self.generate_status())

with open(directory / "Manifest.plist", "wb") as f:
f.write(self.generate_manifest())

with open(directory / "Info.plist", "wb") as f:
f.write(plistlib.dumps({}))


def generate_manifest_db(self): # Manifest.mbdb
records = []
for file in self.files:
records.append(file.to_record())
return mbdb.Mbdb(records=records)

def generate_status(self) -> bytes: # Status.plist
return plistlib.dumps({
"BackupState": "new",
"Date": datetime.fromisoformat("1970-01-01T00:00:00+00:00"),
"IsFullBackup": False,
"SnapshotState": "finished",
"UUID": "00000000-0000-0000-0000-000000000000",
"Version": "2.4"
})

def generate_manifest(self) -> bytes: # Manifest.plist
return plistlib.dumps({
"BackupKeyBag": b64decode("""
VkVSUwAAAAQAAAAFVFlQRQAAAAQAAAABVVVJRAAAABDud41d1b9NBICR1BH9JfVtSE1D
SwAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV1JBUAAA
AAQAAAAAU0FMVAAAABRY5Ne2bthGQ5rf4O3gikep1e6tZUlURVIAAAAEAAAnEFVVSUQA
AAAQB7R8awiGR9aba1UuVahGPENMQVMAAAAEAAAAAVdSQVAAAAAEAAAAAktUWVAAAAAE
AAAAAFdQS1kAAAAoN3kQAJloFg+ukEUY+v5P+dhc/Welw/oucsyS40UBh67ZHef5ZMk9
UVVVSUQAAAAQgd0cg0hSTgaxR3PVUbcEkUNMQVMAAAAEAAAAAldSQVAAAAAEAAAAAktU
WVAAAAAEAAAAAFdQS1kAAAAoMiQTXx0SJlyrGJzdKZQ+SfL124w+2Tf/3d1R2i9yNj9z
ZCHNJhnorVVVSUQAAAAQf7JFQiBOS12JDD7qwKNTSkNMQVMAAAAEAAAAA1dSQVAAAAAE
AAAAAktUWVAAAAAEAAAAAFdQS1kAAAAoSEelorROJA46ZUdwDHhMKiRguQyqHukotrxh
jIfqiZ5ESBXX9txi51VVSUQAAAAQfF0G/837QLq01xH9+66vx0NMQVMAAAAEAAAABFdS
QVAAAAAEAAAAAktUWVAAAAAEAAAAAFdQS1kAAAAol0BvFhd5bu4Hr75XqzNf4g0fMqZA
ie6OxI+x/pgm6Y95XW17N+ZIDVVVSUQAAAAQimkT2dp1QeadMu1KhJKNTUNMQVMAAAAE
AAAABVdSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kAAAAo2N2DZarQ6GPoWRgTiy/t
djKArOqTaH0tPSG9KLbIjGTOcLodhx23xFVVSUQAAAAQQV37JVZHQFiKpoNiGmT6+ENM
QVMAAAAEAAAABldSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kAAAAofe2QSvDC2cV7
Etk4fSBbgqDx5ne/z1VHwmJ6NdVrTyWi80Sy869DM1VVSUQAAAAQFzkdH+VgSOmTj3yE
cfWmMUNMQVMAAAAEAAAAB1dSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kAAAAo7kLY
PQ/DnHBERGpaz37eyntIX/XzovsS0mpHW3SoHvrb9RBgOB+WblVVSUQAAAAQEBpgKOz9
Tni8F9kmSXd0sENMQVMAAAAEAAAACFdSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kA
AAAo5mxVoyNFgPMzphYhm1VG8Fhsin/xX+r6mCd9gByF5SxeolAIT/ICF1VVSUQAAAAQ
rfKB2uPSQtWh82yx6w4BoUNMQVMAAAAEAAAACVdSQVAAAAAEAAAAA0tUWVAAAAAEAAAA
AFdQS1kAAAAo5iayZBwcRa1c1MMx7vh6lOYux3oDI/bdxFCW1WHCQR/Ub1MOv+QaYFVV
SUQAAAAQiLXvK3qvQza/mea5inss/0NMQVMAAAAEAAAACldSQVAAAAAEAAAAA0tUWVAA
AAAEAAAAAFdQS1kAAAAoD2wHX7KriEe1E31z7SQ7/+AVymcpARMYnQgegtZD0Mq2U55u
xwNr2FVVSUQAAAAQ/Q9feZxLS++qSe/a4emRRENMQVMAAAAEAAAAC1dSQVAAAAAEAAAA
A0tUWVAAAAAEAAAAAFdQS1kAAAAocYda2jyYzzSKggRPw/qgh6QPESlkZedgDUKpTr4Z
Z8FDgd7YoALY1g=="""),
"Lockdown": {},
"SystemDomainsVersion": "20.0",
"Version": "9.1"
})
Loading

0 comments on commit 46fd732

Please sign in to comment.