If you’ve ever wanted to jailbreak iOS or downgrade to older iOS, you’ve likely heard of the checkm8 exploit — a game-changing vulnerability discovered by axi0mX that targets the SecureROM (BootROM) of Apple devices ranging from A5 to A11.
In this article, I’ll break down the internals of the exploit: how it works, why it works, what CVE-2019-8900 is in technical detail, how the checkm8 implementation executes it, and how tools like checkra1n and palera1n utilize it for jailbreak purposes.
CVE-2019-8900: The SecureROM Memory Corruption Bug Explained
Back in 2019, developer axi0mX released CVE-2019-8900, a critical memory corruption vulnerability in Apple’s SecureROM USB descriptor handling logic.
The root cause lies in insufficient input validation combined with a fixed-size memory buffer used in the DFU (Device Firmware Update) mode’s control transfer handler.
Here’s what the SecureROM logic roughly does when responding to a GET_DESCRIPTOR
request:
int handle_get_descriptor(uint16_t wValue, uint16_t wLength) {
uint8_t *desc = lookup_descriptor(wValue);
if (!desc)
return STALL;
int len = desc[0]; // Assumes valid descriptor
if (wLength < len)
len = wLength;
memcpy(usb_ep0_buffer, desc, len); // vulnerability: unchecked copy into fixed-size buffer
return send_response(len);
}
There are several flaws in this code:
- No null-pointer check — if
lookup_descriptor()
fails or returns a dangling pointer, a crash or memory leak may occur. - No boundary enforcement — SecureROM doesn’t check if
len
exceeds the size ofusb_ep0_buffer
. - Predictable memory layout — since DFU mode is minimal, memory state is deterministic across boots.

By sending a maliciously crafted descriptor request and interrupting it with an asynchronous cancel, the attacker can:
- Corrupt internal state (leaving a pointer half-initialized)
- Leak memory (dumping uninitialized SRAM content)
- Overwrite heap/stack memory structures
This forms the basis of arbitrary memory write and code execution.
Deep Dive into the checkm8 Exploit Code
Heap Grooming and Callback Hijacking
While triggering the memory corruption is one thing, getting actual execution from it requires some clever tricks. This is where heap grooming, aka heap feng shui, comes into play — and the real magic of checkm8 starts to shine.
In certain SoCs (like A10, A10X, A11), a bug allows zero-length packet requests to leak in memory under the right conditions.
By sending crafted USB transfers that aren’t multiples of 64 bytes (a quirk of USB), we can trigger this leak and cause the USB stack to allocate request objects that never get cleaned up.
These requests start to pile up on the heap. If timed right — and combined with a forced DFU shutdown before the transfers finish — they leave behind exactly-sized holes in heap memory. This is where we can land our payload structures on a future allocation.
On older devices like A8 or A9, this memory leak step isn’t even needed. You can go straight into code execution using a DFU abort bug. On A7 devices, the leak behavior is even more aggressive it happens on every request, so heap setup is even easier there.
Once we have the heap prepped, we intentionally cause a use-after-free by restarting DFU while SecureROM still holds pointers to freed buffers. The next DFU session allocates new buffers on top of these poisoned holes.
We then overwrite a usb_device_io_request object, replacing its function pointer (used when the request completes) with one pointing to our payload in SRAM.
The final trick? A USB reset. This triggers cleanup in the DFU USB queue, and as each request’s callback is invoked, the one we hijacked gets executed, and our shellcode runs inside SecureROM.
This is the precise moment that checkm8 wins, turning a memory corruption bug into full SecureROM code execution.
From there, it’s just a matter of uploading PongoOS, custom bootloaders, or a patched kernel depending on your tool of choice.
Here’s exactly what happens at the Python script level:
USB Corruption Primitive: stall()
def stall(device):
libusb1_async_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 'A' * 0xC0, 0.00001)
bmRequestType = 0x80
– Device-to-host, standard, device.bRequest = 6
– GET_DESCRIPTORwValue = 0x304
– Invalid descriptor (intentionally malformed)wIndex = 0x40A
– Arbitrarydata = 0xC0
bytes of junktimeout = 10us
– Forces immediate timeout and cancellation
The libusb1_async_ctrl_transfer()
call submits this and then cancels it using libusb_cancel_transfer
.
This results in an inconsistent USB state machine and a corrupted heap.
Amazing, especially considering that the SecureROM is not that big of a codebase and Apple audits the heck out of it after limera1n back in the days. Still, as you can see, you can never be too sure.
Memory Leak or Hole Creation
for i in range(config.hole):
no_leak(device)
usb_req_leak(device)
The exploit can either:
- Dump memory by misusing descriptors (leak)
- Create a hole for ROP chain/shellcode (hole)
Payload Injection + Overwrite
libusb1_no_error_ctrl_transfer(device, 0, 0, 0, 0, config.overwrite, 100)
for i in range(0, len(payload), 0x800):
libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 100)
The overwrite injects fake USB descriptors or function pointers. The payload is injected into SRAM and positioned for execution.
Analyzing the BootROM Payload: Patching USB Descriptors and Launching Shellcode
The checkm8 payload is hand-crafted ARM64 assembly that executes inside the SecureROM environment. From my understanding, here’s a breakdown of its behavior:
.pool
.set PAYLOAD_OFFSET, 0xBAD00006
.set PAYLOAD_SIZE, 0xBAD00007
.set PAYLOAD_DEST, 0xBAD00005
.set PAYLOAD_PTR, 0xBAD00008
.set gUSBSerialNumber, 0xBAD00002
.set gUSBSRNMStringDescriptor, 0xBAD00004
.set gUSBDescriptors, 0xBAD00001
.set usb_create_string_descriptor, 0xBAD00003
These symbols are placeholders replaced by actual values during runtime (in prepare_shellcode
).
From what I can see, they define memory locations for SecureROM symbols like USB descriptor tables, serial number, and payload pointers.
The .bin payload:
- Copies the
PWND:[checkm8]
string into the USB serial number field - Calls
usb_create_string_descriptor()
- Sets up the descriptor table with a custom descriptor
- Copies secondary shellcode (the main exploit payload) to PAYLOAD_DEST using a manual loop:
copy_loop:
LDP X3, X4, [X1]
STP X3, X4, [X0]
...
ADD X0, X0, #0x40
ADD X1, X1, #0x40
ADD X2, X2, #0x40
CMP X2, X3
B.CC copy_loop
This loads the main payload into memory and flushes the data cache with:
DC CIVAC, X0
DMB SY
Execution concludes with SYS #0, c7, c5, #0
to synchronize context and RET
.
Post-Exploitation Applications: Jailbreaking with checkra1n and palera1n
checkra1n (iOS 12–14.x tethered jailbreak)
checkra1n is the first tool that leveraged checkm8 to deliver a full semi-tethered jailbreak for iOS 12 up to 14.x. It was abandoned by iOS 15 when SSV became a thing.
How it works:
- Uses checkm8 to enter pwned DFU mode
- Uploads patched iBSS/iBEC
- Boots custom Ramdisk
- Injects bootstrap + Cydia
Advantages:
- Supports iPhone 5S through iPhone X (A7–A11)
- Doesn’t rely on kernel bugs
- Works even with passcode-locked or iCloud devices
palera1n (iOS 15–18 semi-tethered rootless jailbreak)
palera1n modernized checkm8 usage for iOS 15+ where SEP and USB enforcement are stronger. It uses rootless techniques and fake root volume overlays to bypass iOS 15’s SSV.
Workflow:
- Pwn device with checkm8
- Patch SecureROM, disable signature checks
- Load custom kernel arguments (boot-args)
- Remount rootFS or overlay new volume
Both tools prove that once checkm8 succeeds, everything else becomes possible — whether it’s tethered booting, iCloud bypass, or forensic data extraction. Wait till you see what they did to SEP in turdus_merula.
From my testing over the past 5 years, most checkm8-based tools are very stable and have a great success rate. It’s an exploit that very rarely fails, and since it’s so powerful, it negates the need for complicated userland patches.
Over the years, there have been some issues caused by the new M1-M3 Macs, where the device wouldn’t get recognized properly via USB-C, etc., but those were not major issues. This really was the spark of life in jailbreaking post-2020.
Final Thoughts: The Impact of CVE-2019-8900 and checkm8
CVE-2019-8900 is one of the most profound iOS vulnerabilities ever disclosed, maybe even bigger than geohot’s limera1n, because this one works on much newer devices with much greater security.
Its placement in SecureROM, the most trusted code on the device, makes it unpatchable and universally reliable for affected models.
checkm8’s impact includes:
- Unpatchable jailbreak access (checkra1n, palera1n)
- Forensics access on locked devices
- iCloud bypass tooling (e.g., Broque Ramdisk Pro)
- Deep reverse engineering of Apple’s boot chain
- Downgrade tools like turdus_merula.
Now that iOS 26 Developer Beta has been announced, Apple has finally discontinued the last checkm8-compatible device that was still receiving updates: the iPad 7th Generation.
This officially marks the end of the checkm8-jailbreaks era. I write this with both pride and sadness in my chest.
At first, I am proud that I was here to live what might have been the best era in jailbreaking history, fully unpatchable jailbreaks for iOS 16, iOS 17, iOS 18… and then the sadness that it’s now finally over after 5 years of checkm8 tools.
What axi0mX released in 2019 sparked new life into jailbreaking for many years, now we have to find the next best thing. Who knows what that may be? But it’s certainly not Windows Vista-themed iOS.
So long, checkm8!
Frequently Asked Questions
What is checkm8?
checkm8 is a powerful, unpatchable BootROM exploit discovered by axi0mX in 2019. It affects Apple devices with A5–A11 chips and allows unsigned code execution in DFU mode. It’s the foundation for many modern jailbreak tools.
What does CVE-2019-8900 refer to?
CVE-2019-8900 is the identifier for the memory corruption vulnerability in Apple’s SecureROM exploited by checkm8. It involves unchecked USB descriptor handling in DFU mode, leading to arbitrary memory write and code execution.
Which devices are affected by the checkm8 exploit?
All iOS, iPadOS, tvOS, and watchOS devices with A5 to A11 chips are vulnerable. This includes iPhone 4S through iPhone X, various iPads, Apple TVs, and even the iPod touch 6th/7th Gen.
Is checkm8 still usable in 2025?
Yes. Even though Apple discontinued the last supported device (iPad 7th Gen), the exploit still works on all A5–A11 devices and cannot be patched via software updates, making it permanently viable.
What is pwned DFU mode?
Pwned DFU is a modified Device Firmware Update state achieved using checkm8. In this mode, the device bypasses Apple’s signature checks and accepts arbitrary commands and payloads from a computer.
Why is checkm8 unpatchable?
Because it targets the SecureROM (also called BootROM), which is read-only hardware code burned into the chip. Apple cannot patch it through iOS updates.
Can I downgrade iOS using checkm8?
Yes. Tools like turdus_merula and kloader-based workflows leverage checkm8 to downgrade devices, bypassing Apple’s signing requirements by booting custom firmware. Turdus Merula doesn’t even require SHSH2 blobs (although downgrades are tethered in this case).
More iDevice Central Guides
- iOS 17 Jailbreak RELEASED! How to Jailbreak iOS 17 with PaleRa1n
- How to Jailbreak iOS 18.0 – iOS 18.2.1 / iOS 18.3 With Tweaks
- Download iRemovalRa1n Jailbreak (CheckRa1n for Windows)
- Dopamine Jailbreak (Fugu15 Max) Release Is Coming Soon for iOS 15.0 – 15.4.1 A12+
- Cowabunga Lite For iOS 16.2 – 16.4 Released in Beta! Install Tweaks and Themes Without Jailbreak
- Fugu15 Max Jailbreak: All Confirmed Working Rootless Tweaks List
- iOS 14.0 – 16.1.2 – All MacDirtyCow Tools IPAs
- iOS Jailbreak Tools for All iOS Versions
Leave a Reply
You must be logged in to post a comment.