When a Routing Rule Becomes a Security Claim
Android’s RW dispatch path has a specific failure mode: the system can route an NFC-shaped request to an application without giving that application a dependable way to verify that a fresh hardware discovery caused the request. This chapter diagnoses the two design weaknesses behind that gap and explains what the resulting Tag-Dispatch Forgery Attack (TDFA) does—and does not—establish. The important distinction is between reaching a handler and proving why it was reached.
The first weakness: NFC dispatch uses ordinary routing rules
A normal RW discovery eventually becomes an implicit Intent. Unlike an explicit Intent, which names a particular component, an implicit Intent describes the kind of action and data involved and asks Android to find a suitable recipient. The NFC discovery actions are part of that description. Depending on the tag and the matching stage, the request may use ACTION_NDEF_DISCOVERED, ACTION_TECH_DISCOVERED, or the broader ACTION_TAG_DISCOVERED action.
Android then compares the request with manifest-declared intent filters. A filter can constrain the action, the default category used for Activity resolution, and data such as a URI or MIME type. A technology filter can describe the NFC technologies that a handler accepts. These predicates are useful because they let an application declare its area of interest. A payment entry point can ask for a particular NDEF form; a provisioning entry point can ask for a configuration record; a general-purpose handler can accept a wider class of tags.
The security problem is not that the matching rules are imprecise. It is that they answer only a routing question: which component is eligible for this request? Android’s ordinary resolver and Activity-start path then apply the generic conditions that normally govern an inter-app launch, such as whether the component is exported and whether the caller holds any required permission.
The paper identifies the missing condition as a system-only admission rule. There is no additional cross-package check in the analyzed path that says an Activity carrying an NFC discovery action may be started only when the request came from the NFC dispatch component. A request can therefore be treated like an ordinary Activity start once it satisfies the target’s filter and the usual component checks. The label “NDEF discovered” helps the resolver select the handler, but it does not force the resolver to establish that the NFC subsystem—not another application—supplied the request.
This is a boundary between ownership and description. The NFC stack owns the fact that it observed a discovery. The manifest owns the declaration that an Activity is interested in a class of requests. The resolver compares the two descriptions and chooses a component. But the resolver’s successful comparison does not transfer ownership of the original physical event to the receiving application.
That distinction is easy to miss because a legitimate request follows the same route every time. A tag is detected, Android constructs the discovery Intent, the filter matches, and the handler opens. From the app’s point of view, the arrival of the Intent is closely correlated with the physical tap. Correlation becomes a design assumption: code enters the NFC branch whenever it sees the expected action or data. The assumption is safe only if the platform makes the correlation exclusive—if no other caller can produce the same accepted transition.
Generic component-start rules were not designed to carry that exclusive meaning. They are designed to let applications communicate through declared interfaces. Export status and permissions can control who may invoke a component, but an exported Activity without an adequate caller gate is, by definition, available to other packages under the platform’s start rules. If that Activity also interprets an NFC action as a physical-world signal, ordinary reachability becomes security-relevant.
The result is not a universal bypass of Android’s component model. The manifest still matters. A non-exported handler can block the cross-package route. A caller permission can block a sender that does not hold it. A filter can be too narrow to match. Runtime logic can reject the request after launch. W1 identifies the missing system-only origin condition in the dispatch path; it does not erase the other conditions that determine whether a particular Activity can actually be started.
The second weakness: the request carries shape, not provenance
The second weakness appears after the resolver has selected a target. The receiving Activity gets the same general kind of Intent interface used by other Activity starts: an action, categories, data, and extras. In a normal NFC delivery, those fields may include an NDEF-derived URI or MIME type, a Tag object, and other content the app expects to parse. The app can check whether the fields are present, whether a URI has an expected form, or whether a payload is syntactically valid.
Those checks establish what the request looks like. They do not establish why it exists.
The difference can be stated plainly. A content check asks, “Does this request contain the value or format my workflow expects?” A provenance check asks, “Can I verify that the NFC system created this request after a current hardware discovery?” Android’s application-facing dispatch path, as analyzed in the paper, does not provide a stable system-only signal for the second question. The fields used by the receiver are supplied through the ordinary Intent boundary and are not made unforgeable by being associated with an NFC action.
This does not mean that every extra is interchangeable or that every app accepts arbitrary data. An Activity may require a particular URI, MIME type, technology match, or app-specific payload. It may reject malformed input or demand a later server response. The point is narrower and more important: the ordinary fields themselves do not let the receiver distinguish a system-generated tag-dispatch request from a software-constructed request that has the same routing shape.
The distinction resembles a familiar problem in other software interfaces. A web server can route a request to a privileged endpoint because its URL matches, but the URL is not proof that a trusted user clicked a particular button. A file can have the expected name without proving who created it. In Android RW dispatch, the action and data fields are the routing vocabulary. They become dangerous only when application logic treats that vocabulary as an attestation of a physical event.
The Android Tag object makes the boundary especially easy to misunderstand. In the normal path, the object represents a tag state observed at discovery time. The app may use its ID or technology list, and it may open technology-specific connections. But the fact that a field is called a tag identifier or is delivered in an EXTRA_TAG extra does not, by itself, give the receiving Activity an app-verifiable chain back to the NFC dispatcher. The object is part of the delivery representation; it is not a universal statement that the current caller is the hardware-backed NFC system.
Together, the two weaknesses create the semantic origin gap. W1 makes the handler reachable through ordinary implicit-Intent resolution when the manifest and component rules allow it. W2 leaves the receiver with fields that describe an NFC-like request but do not prove that the NFC dispatcher originated it. Once both conditions hold, an application can mistake a successful route for evidence of a physical tap.
The diagram does not say that the two inputs are identical in every internal detail or that all targets accept both. It shows the security-relevant convergence: once both requests reach the same resolver and satisfy the same startability conditions, the application boundary may not carry enough information to distinguish their origin. The platform has preserved a path to the component without preserving a fact that the component can independently verify.
What TDFA requires—and where its claim stops
The paper names the combined condition TDFA. Its threat model is local and specific. The attacker controls a third-party app installed and running on the victim device. The app does not request runtime permissions, use NFC hardware I/O, rely on overlays or Accessibility, or require root or a privileged system API. Its relevant capability is the ordinary ability to start an externally reachable Activity under Android’s component rules.
The target must first be a Reader/Writer handler whose manifest registration can be matched. The selected Activity must also be externally startable—for example, exported and not protected by a caller permission that the attacker lacks. These are not incidental details. They explain why the issue is a platform-and-application interaction rather than a claim that any installed app can enter any NFC workflow.
Modern Activity-start policy adds a state constraint. The paper assumes that the attacker triggers the start from a user-visible context that Android permits to launch an Activity. That requirement can limit when the request is made, but it is not an NFC permission and does not restore provenance. A timing or foreground condition is different from a system-only assertion that a tag was just detected.
At a high level, the attack substitutes a software-origin request for the hardware-origin discovery at the delivery boundary. The local app causes Android to process a request that matches the target handler’s NFC registration, and the system may launch the same Activity that would have opened after a real tag. The core result is handler entry without a physical tag at trigger time. That is why the paper distinguishes TDFA from relay or distance-extension attacks: it does not require a live RF session, a tag emulator, or NFC I/O at the moment the victim handler is launched.
Some workflows need more than entry. An application may expect tag-derived content before it can proceed, and a controlled experiment may obtain legitimate content through an auxiliary channel. That content transport is not the root bypass. The platform-level finding is that the handler can be launched through the software path; whether the app then accepts data, makes a state change, completes a transaction, or waits for a user depends on its own logic and surrounding services.
This distinction keeps the claim calibrated. TDFA demonstrates a way to reach a vulnerable RW handler without a physical tap at trigger time. It does not mean that every reachable handler produces a high-impact exploit. It does not mean that every application lacks content validation. It does not mean that a payment backend will authorize every request or that a user will never see a confirmation screen. The attack’s first claim is about origin and entry; downstream consequences must be evaluated one application at a time.
The same calibration also explains why the issue is more serious than an unexpected screen transition. A handler may perform meaningful work before it becomes the top-visible Activity. It may parse data, update local state, invoke a system service, or begin a workflow whose later steps have their own gates. Conversely, an Activity may launch successfully but fail at a runtime check or stop because the required data is absent. The existence of a route changes what is possible; it does not predetermine the final outcome.
The diagnosis therefore has four separate questions. Can the request satisfy the target’s routing predicates? Can Android’s component-start rules admit the caller? Can the Activity distinguish a real system delivery from a software-created one? What does the application do after it accepts the entry? The first two questions are about reachability. The third is about provenance. The fourth is about impact. Treating them as one question is precisely what allows a routing rule to become a security claim.
A useful way to carry the model forward is to refuse the phrase “the app received NFC” unless its meaning is specified. The app may have received an Intent associated with the NFC dispatch vocabulary. That is an observation about the request. The paper’s finding is that, for the analyzed RW path, the app may not have a trustworthy application-facing proof that the request came from a fresh hardware-backed discovery. The next task is to test that distinction against actual application consequences without assuming that entry, visibility, and harm are interchangeable.