For the complete documentation index, see llms.txt. This page is also available as Markdown.

89095 w a high android process death defeats recovery phrase clipboard expiry and exposes the mnemonic to later foreground apps

Submitted on Aug 17th 2026 at 06:03:52 UTC by @Ailenia for Audit Comp | Quantus

  • Report ID: #89095

  • Report Type: Websites & Apps

  • Report severity: High

  • Target: https://github.com/immunefi-team/audit-comp-quantus-apps/tree/audit-comp-ready/mobile-app

  • Impacts:

    • Improperly disclosing confidential user information, such as:

      • Email address

      • Phone number

      • Physical address, etc.

Description

Brief/Intro

The Quantus mobile wallet copies a 24-word recovery phrase to Android's global clipboard and promises to erase it after three minutes, but implements that guarantee with an in-process Dart Timer.

This means that if Android kills the wallet process, or the process is explicitly terminated, before the deadline, the timer disappears while the phrase remains in the system clipboard.

A separate application that becomes foreground after the promised expiry can then recover the phrase.

The clipboard entry is also not marked sensitive.

Vulnerability Details

At frozen apps commit d5fc4d6572ebc2244e95009a33f5da5048935867, mobile-app/lib/v2/components/recovery_phrase_body.dart:61 passes the joined recovery phrase to copySensitiveTextWithToaster, which calls SecureClipboardService.copyWithExpiry with its production default TTL of three minutes.

The service has an OS-backed implementation only for iOS. Since on Android, Platform.isIOS is false, the lines 45-46 write the phrase through Flutter's global clipboard API and schedule an in-process timer:

iOS instead sets UIPasteboard with .expirationDate and .localOnly, which remains effective after suspension or termination. Android has no native secure_clipboard channel handler. Terminating the Android process destroys the Dart VM and _clearTimer; reopening the wallet creates a new service instance and does not know that an old clipboard entry must be cleared.

The source comment says that after backgrounding "the clear happens when it next resumes," but no persisted deadline or resume handler exists. The Android copy also omits ClipDescription.EXTRA_IS_SENSITIVE, so Android does not conceal the mnemonic in clipboard previews.

On Android 10 and later, a clipboard reader must be the foreground application or default IME. The attack therefore requires another application already installed on the victim's device. The victim performs the wallet's intended Copy Recovery Phrase action; Android later kills the backgrounded process under ordinary lifecycle pressure, or the process is otherwise terminated. The attacker application can then be opened normally after the three-minute deadline and read the phrase. Merely backgrounding the wallet without process death is not sufficient. This does not require physical access, local-network access, a compromised Quantus service, or a malicious blockchain transaction.

Android 13 and later can provide system auto-clear, but its timeout is device-configurable and clipboard access can refresh the system's timer. On the tested Android 16 device the configured timeout was 3,600 seconds, so Quantus's promised 180-second bound was not enforced. Android's official guidance states that older versions do not provide this automatic deletion.

Impact Details

This matches the programme's High websites-and-applications impact 48, "Improperly disclosing confidential user information." A recovery phrase is the wallet's most confidential user information. The PoC proves that an independent foreground application can retrieve it after Quantus's advertised security deadline has passed.

The report does not claim that process death grants a new Android clipboard permission or that every Android user is affected automatically. It creates a temporal disclosure path: without the defect, an application first foregrounded after three minutes receives an empty clipboard; with the defect, it receives the complete phrase. Possession of a funded wallet's phrase can ultimately enable fund theft, but this report conservatively selects the demonstrated High disclosure impact rather than claiming Critical fund loss.

Remediation

Do not place recovery phrases on Android's global clipboard unless the user explicitly accepts that exposure. If copying remains supported, implement a native Android path that marks the clip with ClipDescription.EXTRA_IS_SENSITIVE, records a persistent expiry deadline, and clears a matching stale clip on lifecycle recovery or next launch. Treat the sensitive flag as preview protection rather than an expiry mechanism, and do not claim a hard three-minute bound unless an OS-backed mechanism can enforce it after process death.

References

https://gist.github.com/niafreu/beddb6e36f215ece0cdb0d38e8431e69

Proof of Concept

Gist at https://gist.github.com/niafreu/beddb6e36f215ece0cdb0d38e8431e69

The attached PoC is entirely local and uses a fake BIP-39 canary which for the demo is the equivalent of a real wallet phrase. It builds a minimal Flutter victim around the SecureClipboardService and a separate native Android foreground reader.

Requirements: Linux or macOS, Flutter 3.47.0 with Dart 3.13.0, JDK 11 or later, Android SDK platform 36 and build-tools 36.1.0, and either an Android emulator or disposable Android device reachable through adb.

1

Run the PoC

2

Process-death arm

Launch the frozen-code victim, copy the canary, terminate it three seconds later, wait another 185 seconds, then foreground the reader. The reader must recover the complete canary and report sensitive_extra=false.

3

Live-process control

Launch the victim and leave it running for 188 seconds before foregrounding the reader. The reader must receive an empty value.

I used Android 16/API 36 and it shows:

Both readers ran approximately 188 seconds after the copy. The live control returned an empty value at the same boundary, isolating process termination as the cause.

The absent sensitive flag is additional exposure evidence; it does not itself affect clipboard expiry or programmatic reads.

The attached execution.log preserves the passing Android 16/API 36 run.

PoC files

run.sh

victim_main.dart

secure_clipboard_service.dart

ReaderActivity.java

AndroidManifest-reader.xml

Was this helpful?