# LSPromise chains a logic bug and a kernel bug into Android root

> A published exploit chain that takes an unprivileged app to root on Android 17 without touching memory corruption, so none of the usual mitigations have to be bypassed. It pairs CVE-2026-49881, a service-layer logic bug reported in July and dismissed as a duplicate, with the DirtyFrag xfrm-ESP page-cache write CVE-2026-43284. The first half is fixed; the article is about why it survived three months.

**LSPosed/LSPromise** — Android complete exploit chain that enables privilege escalation from a local untrusted app to root/kernel, combination of CVE-2026-49881 and CVE-2026-43284

- Repository: https://github.com/LSPosed/LSPromise
- Stars: 432 · Forks: 93
- Language: C
- License: not declared
- Published: 2026-09-18 · Updated: 2026-09-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/lsposed-lspromise

## Two bugs and no memory corruption, which is where the reliability comes from

The chain is described as privilege escalation from a local untrusted app to root and kernel, built from two vulnerabilities: CVE-2026-49881 in the Telecom service and CVE-2026-43284, the xfrm-ESP page-cache write known as DirtyFrag.

The claim that distinguishes it from most Android exploit chains is what it does not use. There is no memory corruption and no race condition, so there is no heap spraying to tune and no mitigation to defeat: not KASLR, not MTE, not CFI. That is the whole reason the stated success rate on vulnerable devices is 100 percent. Reliability here comes from the absence of the variable that makes exploit reliability probabilistic.

It also means the chain does not depend on a specific kernel build, only on the specific code paths in Android 17's Telecom service and the presence of xfrm-ESP in the network stack.

## The userspace half is a logic bug in Telecom, not an overflow

The first vulnerability is described as a simple logic bug introduced in Android 17, located in InCallController.java. The writeup is careful about which package this file belongs to: com.android.server.telecom rather than com.android.phone.

That distinction is the vulnerability. The package declares android:sharedUserId of android.uid.system and runs in the system process, so it executes inside system_server, one of the most privileged userspace processes on Android. A bug in a telephony service is a nuisance; the same bug in a process with system UID is a foothold.

The mechanism described involves loading code from an arbitrary application using a context created with the flags for including code and ignoring security. There are countermeasures, including a false argument to the class-loading call to suppress initialisation, but the writeup notes that an application can declare its own component factory, which is invoked when the class loader is requested.

No exploitation steps are reproduced here; the upstream writeup and the fix commit are the place to read the mechanism in full.

## Reported on July 23, closed as a duplicate, fixed in September

The disclosure timeline is the part worth remembering, and it is stated plainly.

The bug was found and reported to the Android Security Team on July 23, 2026. The team told the reporters it was a duplicate. It was assigned CVE-2026-49881 and fixed in the September 2026 security bulletin, by a change described as removing the serviceClassExists logic to address the security vulnerability.

So a zero-day that was reachable from an unprivileged app spent at least its entire disclosure window in system_server, in the hands of a production fleet, because a triage decision closed it. Whatever the merits of the duplicate classification, the gap between report and patch is the measurable failure here, and it is three months on a release that shipped in June 2026.

The writeup also points at the fixed behaviour as evidence, linking the bulletin entry and the commit that removes the logic rather than adjusting it.

## SELinux is what forces the kernel half to run inside a privileged process

Escalating to system is not the same as getting root. The writeup states plainly that complete root needs at least UID 0 and no longer being restricted by SELinux.

The kernel bug needs xfrm-ESP, and the policy forbids untrusted applications from reaching it. The relevant rule is a neverallow in the app domain's policy file:

```sepolicy
# Privileged netlink socket interfaces.
neverallow { appdomain -network_stack }
    domain:{
        netlink_tcpdiag_socket
        netlink_nflog_socket
        netlink_xfrm_socket
        netlink_audit_socket
        netlink_dnrt_socket
    } *;
```

Only three domains are exempt: system_server, network_stack and netd. So the kernel bug is not usable from an app, which is exactly why the userspace bug is needed first. The chain is not two independent problems, it is one bug unlocking a second by landing the attacker inside the only process the policy trusts for that socket.

## DirtyFrag has two variants and only one of them works on Android

The kernel half is not original work. It is the DirtyFrag bug, and the writeup defers to the original reporter's own analysis rather than restating the underlying principle.

There are two variants, and the difference is decisive. CVE-2026-43500 requires RxRPC, which is disabled for Android generic kernels, so it is a dead end on the target platform. CVE-2026-43284 requires xfrm-ESP instead and is exploitable on Android.

That narrows the chain to exactly one combination: the Telecom logic bug for the userspace half, and the xfrm-ESP page-cache write for the kernel half. There is no fallback and no variant to try if one fails, which is both the strength and the fragility of the design.

The writeup also notes the follow-on constraint that even inside system_server, SELinux blocks loading native libraries from /data and blocks mapping anonymous executable memory, so the sequence has to work within those constraints rather than ignoring them.

## Quarterly bulletins are offered as the reason a three-month-old bug survived

The writeup offers an explanation for the delay rather than only a complaint about it: Google switched the monthly security bulletin to a quarterly release, and that change may be why the vulnerability was not fixed three months after Android 17 shipped.

If that is right, the failure is structural rather than individual. A quarterly cadence gives a vulnerability three months of guaranteed exposure before the fix can even be published, and any report triaged as a duplicate inherits that whole window. Any team shipping an annual or quarterly patch cycle has the same arithmetic problem, whatever the vendor.

It also changes what a disclosure deadline means. A ninety-day clock measured against a quarterly bulletin is not a policy breach; it is a scheduling artifact. Reporting windows are usually written against monthly updates, and the two have drifted apart.

## A Gradle app, a screen recording, and no declared licence

The repository is small and ordinary at the file level: a .gitignore, the README, an app directory, build.gradle, settings.gradle, gradle.properties, the gradlew wrapper scripts on both platforms, and a screen recording named VID_20260804_231937_915.mp4.

So this is a Gradle Android application, not a set of patches or a script collection. The demonstration is a video rather than a transcript, and the app is designed to be paired with a KernelSU installation so that a successful run can be verified by the root manager rather than by output text.

The licensing is the open question. No licence is recorded for the repository, and for an artifact of this kind that matters more than it would for a library: without a grant, the default position on redistribution and modification is unfavourable to anyone but the author. The code is a demonstration of two vulnerabilities that are now patched upstream, so the interesting content is arguably the writeup rather than the app.

## Conclusion

Read LSPromise as a disclosure story rather than as a tool. Both halves of the chain are now public in upstream form, the userspace half was fixed in the September 2026 bulletin, and the repository's own value is the writeup explaining how a trivially reachable service bug survived three months because the security bulletin moved from monthly to quarterly. Do not treat it as something to run: it targets a specific set of devices, it does not work on Pixel 6a, and it may also affect other devices on 6.1.xxx-android14 kernel trees for a separate reason. If you are on Android 17 and have not taken the September patch, the practical response is to update rather than to evaluate the chain. If you maintain Android code, the transferable lesson is narrower than the exploit: a package that runs as system must not resolve service classes from arbitrary applications.

## FAQ

### What vulnerabilities does the LSPromise chain combine?

Two. CVE-2026-49881 is a logic bug in the Telecom service that lets code run inside system_server, and CVE-2026-43284 is the DirtyFrag xfrm-ESP page-cache write, a kernel bug disclosed about three months earlier. The writeup defers to the original reporter's analysis for the kernel half.

### Which Android devices does the LSPromise exploit target?

It was tested on a Pixel 10 running the initial official Android 17 release. It does not work on a Pixel 6a, and the writeup notes the issue may also occur on other devices running 6.1.xxx-android14 kernel trees because of a separate bug in those kernels.

### Is the Android vulnerability behind LSPromise fixed?

Yes. CVE-2026-49881 was reported to the Android Security Team on July 23, 2026, told to the reporters as a duplicate, and fixed in the September 2026 security bulletin by removing the serviceClassExists logic from InCallController.java. The writeup notes that AOSP and Pixel devices were still vulnerable at the time of writing, apart from those on beta QPR versions.

### Why does the LSPromise chain claim a high success rate?

Because it involves neither memory corruption nor a race condition. With no heap spraying to tune and no mitigation to bypass, the chain does not have to defeat KASLR, MTE or CFI, which is why the stated success rate on vulnerable devices is 100 percent.

## Sources

- [Issues](https://github.com/LSPosed/LSPromise/issues)
- [LSPosed/LSPromise on GitHub](https://github.com/LSPosed/LSPromise)
- [README](https://github.com/LSPosed/LSPromise/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lsposed-lspromise
