Fuelet Wallet Android App - Fuelet


Prepared by:

Halborn Logo

HALBORN

Last Updated 01/15/2025

Date of Engagement: September 12th, 2024 - September 25th, 2024

Summary

86% of all REPORTED Findings have been addressed

All findings

7

Critical

0

High

0

Medium

3

Low

3

Informational

1


1. Introduction

Fuelet engaged Halborn to conduct a security assessment of their Fuelet wallet Android mobile application, which began on September 12th, 2024 and ended on September 25th, 2024. The security assessment was scoped to the Fuelet wallet Android mobile application. The client team provided both the source code and the respective APK file to allow the security engineers to conduct testing using tools for scanning, detecting, and validating possible vulnerabilities, and to report the findings at the end of the engagement.

2. Assessment Summary

The team at Halborn was provided a timeline for the engagement and assigned two full-time security engineers to verify the security of the assets in scope. The security engineers are penetration testing experts with advanced knowledge in web, mobile (Android and iOS), reconnaissance, blockchain and infrastructure penetration testing.

The goals of our security assessments are to improve the quality of the systems we review and to target sufficient remediation to help protect users.

Of note was the ability to extract other wallets' information from memory when creating a new wallet, namely the private key and seed phrase. While the information was originally encrypted, by tracing calls to the Cipher class the encrypted information could be retrieved. Furthermore, the encryption algorithm was deemed insecure, as AES in CBC mode suffers from collision attacks. Instead, AES with CGM mode should be preferred, to further secure the information. Furthermore, the Fuelet wallet's fingerprint check could be bypassed, which would provide an attacker with access to the user's wallet. In conjunction with the lack of checks on when deleting wallets, an attacker could ultimately steal the devices, bypass the fingerprint check. In regard to the backend server handling GraphQL queries and mutations, it was misconfigured and affected by several Denial-of-Service (DoS) vulnerabilities, which may lead to wallets' not being able to retrieve information from the infrastructure.

Lastly, address the remaining issues in a timely manner to improve the overall security posture of users' wallets and the backend infrastructure.

For public release, this report was redacted per Fuelet request to exclude certain critical issues. It should be noted that all removed critical issues were fully addressed and resolved by the Fuelet team prior to the report’s publication

Scope

The security assessment was scoped to:

3. Test Approach and Methodology

Halborn performed a combination of manual and automated security testing to balance efficiency, timeliness, practicality, and accuracy regarding the scope of the pentest. While manual testing is recommended to uncover flaws in logic, process and implementation; automated testing techniques assist enhance coverage of the infrastructure and can quickly identify flaws in it.

The following phases and associated tools were used throughout the term of the assessment:

    • Storing private keys and assets securely

    • Send/Receive tokens and assets securely to another wallet

    • Any attack that impacts funds, such as draining or manipulating of funds

    • Application logic flaws

    • Areas where insufficient validation allows for hostile input

    • Application of cryptography to protect secrets

    • Brute-force attempts

    • Input handling

    • Source code review

    • Fuzzing of all input parameters

    • Technology stack-specific vulnerabilities and code assessment

    • Known vulnerabilities in 3rd party/OSS dependencies


4. RISK METHODOLOGY

Vulnerabilities or issues observed by Halborn are ranked based on the risk assessment methodology by measuring the LIKELIHOOD of a security incident and the IMPACT should an incident occur. This framework works for communicating the characteristics and impacts of technology vulnerabilities. The quantitative model ensures repeatable and accurate measurement while enabling users to see the underlying vulnerability characteristics that were used to generate the Risk scores. For every vulnerability, a risk level will be calculated on a scale of 5 to 1 with 5 being the highest likelihood or impact.
RISK SCALE - LIKELIHOOD
  • 5 - Almost certain an incident will occur.
  • 4 - High probability of an incident occurring.
  • 3 - Potential of a security incident in the long term.
  • 2 - Low probability of an incident occurring.
  • 1 - Very unlikely issue will cause an incident.
RISK SCALE - IMPACT
  • 5 - May cause devastating and unrecoverable impact or loss.
  • 4 - May cause a significant level of impact or loss.
  • 3 - May cause a partial impact or loss to many.
  • 2 - May cause temporary impact or loss.
  • 1 - May cause minimal or un-noticeable impact.
The risk level is then calculated using a sum of these two values, creating a value of 10 to 1 with 10 being the highest level of security risk.
Critical
High
Medium
Low
Informational
  • 10 - CRITICAL
  • 9 - 8 - HIGH
  • 7 - 6 - MEDIUM
  • 5 - 4 - LOW
  • 3 - 1 - VERY LOW AND INFORMATIONAL
Our penetration tests use the industry standard Common Vulnerability Scoring System (CVSS) to calculate the severity of our findings.

5. SCOPE

Out-of-Scope: New features/implementations after the remediation commit IDs.

6. Assessment Summary & Findings Overview

Critical

0

High

0

Medium

3

Low

3

Informational

1

Impact x Likelihood

HAL-01

HAL-02

HAL-03

HAL-05

HAL-07

HAL-04

HAL-06

Security analysisRisk levelRemediation Date
Fingerprint BypassMediumSolved - 12/05/2024
Insecure AES Encryption ModeMediumSolved - 01/09/2025
GraphQL MisconfigurationMediumRisk Accepted - 12/05/2024
Missing Root Detection MechanismLow-
Missing Checks on Delete Wallet FunctionalityLowSolved - 12/05/2024
Certificate Pinning BypassLowRisk Accepted - 12/05/2024
Missing Authentication Check on Application StartInformationalAcknowledged - 12/05/2024

7. Findings & Tech Details

7.1 Fingerprint Bypass

//

Medium

Description

The application had insufficient checks on biometric controls for the authentication process, which led to a bypass when in possession of the device.

The focus is on the onAuthenticationSucceeded callback, which is crucial in the authentication process. The fingerprint bypass could be conducted by setting NULL as CryptoObject in onAuthenticationSucceeded(...). The Frida script forces an automatic bypass of the fingerprint authentication upon the method's invocation by hooking the callback and modifying the CryptoObject automatically.

Proof of Concept

The Frida script used to hook the interactions between the application and biometric android hardware/implementation in order to bypass it can be found here. The Frida script can be run with the following command:

frida -U Fuelet -l fingerprint-bypass.js
Fingerprint bypass using a Frida script.
Score
CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N(6.8)
Recommendation

Use the second overload of the authenticate method, using a CryptoObject to decrypt some asymmetric or symmetric key stored on the device and use that key to allow access to the wallet:

  1. The key to generating the CryptoObject used for biometric authentication should be stored securely in the Android Keystore system. We also need to ensure that the key is set up in a way that user authentication is required on each use and is invalidated when a new biometric is enrolled.

  2. Use .setUserAuthenticationRequired(true) and .setInvalidatedByBiometricEnrollment(true) as shown in the code example below to generate and retrieve the key for strong biometric authentication:

    const val BIOMETRIC_KEY_STORE_ALIAS = "yourChoiceOfBiometricKeyStoreAlias"
    const val ANDROID_KEYSTORE_NAME = "AndroidKeyStore"
    
    private fun getBiometricKeyGenParameterSpec(): KeyGenParameterSpec {
        val builder = KeyGenParameterSpec.Builder(
            BIOMETRIC_KEY_STORE_ALIAS,
            KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
        ).setBlockModes(KeyProperties.BLOCK_MODE_CBC)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
            .setUserAuthenticationRequired(true)
            .setInvalidatedByBiometricEnrollment(true)
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            builder.setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG)
        } else {
            builder.setUserAuthenticationValidityDurationSeconds(-1)
        }
        return builder.build()
    }
    
    fun Context.generateStrongBiometricAuthenticationKey() {
        if (isStrongBiometricAvailable()) {
            val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE_NAME)
            keyGenerator.init(getBiometricKeyGenParameterSpec())
            keyGenerator.generateKey()
        }
    }
    
    fun getStrongBiometricAuthenticationKey(): SecretKey? {
        val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE_NAME)
        keyStore.load(null)
        return keyStore.getKey(BIOMETRIC_KEY_STORE_ALIAS, null) as? SecretKey
    }
    
    fun getCipher(): Cipher {
        return Cipher.getInstance(
            KeyProperties.KEY_ALGORITHM_AES + "/" +
                KeyProperties.BLOCK_MODE_CBC + "/" +
                KeyProperties.ENCRYPTION_PADDING_PKCS7
        )
    }
    
    override fun onUnlock() {
        generateStrongBiometricAuthenticationKey()
    }

Note

Calling setInvalidatedByBiometricEnrollment(true) will invalidate the generated key stored in the Keystore when a new biometric is enrolled. This means that users would not be able to authenticate when a new biometric assigned because the Biometric Prompt will not create a new dialog. A solution would be to use the PIN and, if authentication is successful, regenerate the key for the biometric authentication. The key regeneration can be done in the onUnlock method, which would be called immediately after the user successfully unlocks the device:

override fun onUnlock() {
    generateStrongBiometricAuthenticationKey()
}

References

Remediation

SOLVED: The biometric functionality was disabled/eliminated from the application provided.

7.2 Insecure AES Encryption Mode

//

Medium

Description

During encryption and decryption routines performed when creating new wallets, AES in CBC mode is used, which is known to present several weaknesses.

Padding Oracle Attacks (on CBC):

  • CBC mode requires padding to make the last block of plaintext the same size as the AES block size (usually 128 bits). This introduces the risk of padding oracle attacks, where an attacker can exploit differences in how a system handles padding errors to learn information about the plaintext. This attack can effectively allow decryption without needing the key.

Lack of Authenticity/Integrity Verification

  • CBC mode does not provide authentication of the ciphertext. This means that an attacker could manipulate the encrypted data without being detected. For example, bit-flipping attacks can be used to modify the ciphertext and, consequently, alter the decrypted plaintext.

Initialization Vector (IV) Requirements and Reuse Vulnerabilities

  • In CBC mode, a unique Initialization Vector (IV) is required for each encryption operation to prevent the same plaintext from producing the same ciphertext. However, if the IV is reused (either accidentally or due to improper implementation), IV reuse attacks can occur, allowing attackers to detect relationships between ciphertexts and potentially decrypt data.

Proof of Concept

The Frida script to retrieve the seed phrase and the private key from the application memory can be found here.

Run the following command after starting the Fuelet application, but before creating a new wallet:

frida -U Fuelet -l tracer-cipher.js

The screenshots below detail the AES encryption/decryption routines performed first on the previous wallet, and afterwards only on the new wallet being created.

AES in CBC mode used during the encryption/decryption of new wallet information.
Score
CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N(6.7)
Recommendation

Consider using AES in GCM mode for the following reasons:

  1. Built-in Authentication (AEAD):

    • GCM is an AEAD mode, meaning it provides both confidentiality and integrity in a single step. This removes the need for additional mechanisms (e.g., HMAC) to verify the integrity of the message.

  2. Efficiency and Performance:

    • GCM can be parallelized and is optimized for hardware performance.

  3. Avoids Padding Issues:

    • GCM mode does not require padding, which eliminates entire classes of attacks (e.g., padding oracle attacks) and simplifies the encryption process.


Remediation

SOLVED: The same AES mode is still used due to the library’s limitations however the data it encrypts is protected using AES in GCM mode. Thus, the final encryption process is effectively: secret = AES(AES_gcm(data)).

7.3 GraphQL Misconfiguration

//

Medium

Description

Several GraphQL configurations could allow users to perform Denial-of-Service (Dos) attacks at the application level.

The following GraphQL issues have been identified:

  • Alias Overloading

  • Field Duplication

  • Introspection-based Circular Query

  • Introspection Query

Alias overloading - Alias Overloading with 100+ aliases is allowed

GraphQL's alias feature allows a client to perform the same operation multiple times in the same HTTP request. Increasing the number of aliases in the query had an impact on the response length and time from the server, thus opened a possibility of Denial-of-Service condition.

Field duplication - Queries are allowed with 500 of the same repeated field

GraphQL allowed duplicate or repetitive fields in the query. An end-user could make the server process the same field again and again n number of times. Increasing the number of same fields in the query had an impact on the response time from the server and thus opened a possibility of Denial-of-Service (Dos) condition.

Introspection-based Circular Query

The more the fragments are referencing each other in a query and the more the server response length and time increased. This opened a possibility of a Denial-of-Service (Dos) condition.

Introspection Query

The GraphQL introspection could be called by unauthenticated users.

Proof of Concept

1. Use a tool such as graphql-cop to identify GraphQL vulnerabilities. Run all the queries through a proxy using the flag --proxy to identify the vulnerable queries.

2. Example of a request utilizing Alias Overloading.

GraphQL query with alias overloading.


3. Example of a request utilizing Field Duplication.

GraphQL query with field duplication.


4. Example of a request utilizing Introspection-based Circular Query.

GraphQL query with Introspection-based circular query.


5. Example of an Introspection Query.

GraphQL introspection query.
Score
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L(6.5)
Recommendation

Resolve the Alias Overloading Vulnerability

The alias functionality cannot be directly disabled. Instead, it is advisable to implement a validation process to ensure that the query doesn’t contain more aliases than the chosen maximum.

The following open source project may help to resolve the issue: GraphQL No Alias Directive Validation

No alias directive for graphql mutation and query types. It can limit the amount of alias fields that can be used for queries and mutations, preventing batch attacks.

Resolve the Field Duplication Vulnerability

To prevent this attack, GraphQL servers should validate the incoming queries and reject any that contain duplicate fields. Additionally, servers could limit the complexity of queries by setting a limit on the response time, to prevent excessive resource consumption.

Resolve the Introspection-based Circular Query Vulnerability

Several possibilities exist to remediate this issue: either limit the maximum depth of the introspection query or limit the maximum elapsed time to execute a GraphQL query. Alternatively, disable introspection.

Resolve the Introspection Query Vulnerability

Disable the introspection query, or consider implementing authentication mechanisms to prevent unauthenticated users from retrieving it.

Remediation

RISK ACCEPTED: The Fuelet team accepted the risk derived from this issue.

References
- [OWASP: GraphQL Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html)
- [OWASP: Testing GraphQL](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/12-API_Testing/01-Testing_GraphQL)

7.4 Missing Root Detection Mechanism

//

Low

Description

Anti-root detection mechanisms were not implemented in the Android application. These mechanisms could help to mitigate reverse engineering, application modification, and unauthorized versions of mobile applications to some extent, but few if any will be completely successful against a determined adversary. Those measures should nonetheless be implemented to deter attackers from attempting to reverse engineer the application, or at least increase the amount of effort required to do so.

Proof of Concept

Root an Android device using Magisk and observe that no check exists to determine whether the application runs a rooted phone.

Fuelet wallet installed and running on a rooted Android device.
Score
CVSS:3.1/AV:P/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N(2.7)
Recommendation

Methods to detect rooted devices should be implemented in the application to prevent dynamic analysis. As a security best practice, implement a mechanism to check the rooted status of the mobile device. This can be done either manually by implementing a custom solution or using libraries already built for this purpose. Search for commonly known files and locations, check file permissions and attempt to find common rooting services such as SuperSU, Magisk or OpenSSH, for example.

Remediation

RISK ACCEPTED: The Fuelet team accepted the risk derived from this issue.

7.5 Missing Checks on Delete Wallet Functionality

//

Low

Description

The Fuelet application did not ask for a PIN or fingerprint verification when deleting the wallet.

Proof of Concept

Access the application and attempt to delete the wallet. See that no PIN or fingerprint verification will prevent this action.

Score
CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L(2.0)
Recommendation

Secure the functionality with the PIN or fingerprint check to ensure that only the user himself can delete the wallet.

Remediation

SOLVED: The Fuelet team solved this finding following the aforementioned recommendation.

7.6 Certificate Pinning Bypass

//

Low

Description

Certificate pinning is a security measure that involves hardcoding the certificate or public key of a known server into the client application to prevent man-in-the-middle (MITM) attacks. This measure ensures that the client establishes connections only with the designated server, even in the presence of a seemingly valid certificate from a trusted Certificate Authority (CA).

Bypassing this security control can be achieved through various methods, such as exploiting vulnerabilities in the client-side implementation of pinning, manipulating the mobile device environment (e.g., root or jailbreak exploits), or leveraging control over a Root CA to issue deceptive certificates. Flaws in the implementation of pinning, such as inadequate validation of the certificate chain, reliance on intermediate CAs, or insufficient coverage across all communication channels, further exacerbate the risk. This bypass can lead to successful MitM attacks, allowing attackers to intercept, modify, or redirect data transmitted over supposedly secure connections.

Proof of Concept

With objection, the SSL pinning can be disable by running the command android sslpinning disable.

Disabling SSL pinning using objection.
Score
CVSS:3.1/AV:P/AC:H/PR:H/UI:N/S:C/C:L/I:N/A:N(1.9)
Recommendation

Choose whether to pin the whole certificate or just its public key. Two options are possible when choosing to pin the public key:

● Pin the subjectPublicKeyInfo.
● Pin one of the concrete types such as RSAPublicKey or DSAPublicKey.

Alternatively, consider whether to pin the root Certification Authority (CA), intermediate CA or leaf certificate:

● Pinning the root CA is generally not recommended since it highly increases the risk because it implies also trusting all its intermediate CAs.
● Pinning a specific intermediate CA reduces the risk, but the application will also be trusted to other certificates issued by this CA, not only the ones meant for your application.
● Pinning a leaf certificate is recommended but must include backup (e.g. intermediate CA). It provides 100% certainty that the app exclusively trusts the remote hosts it was designed to connect to.

Further information on certificate pinning may be found at OWASP's Certificate and Public Key Pinning resource.

Remediation

RISK ACCEPTED: The Fuelet team accepted the risk derived from this issue.

7.7 Missing Authentication Check on Application Start

//

Informational

Description

When switching between applications, the Fuelet application did not lock the wallet to re-ask for the PIN or fingerprint when accessing it again after the application resumed from background.

Such a security measure would provide security in depth. In the case of a stolen phone with the application remaining opened in the background, the thief having access to the screen could view the list of applications opened, re-access the Magic Eden wallet and attempt to steal the funds.

Proof of Concept

Put the application in the background, open another application and switch back to the Fuelet application. No prompt will appear asking for a fingerprint check or PIN verification.

Score
CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:N(0.0)
Recommendation
  • Implement a lockout mechanism when switching to another application.

  • Ask for a PIN or fingerprint check when accessing it again.

Remediation

ACKNOWLEDGED: The Fuelet team acknowledged the risks associated with this issue.

Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.

© Halborn 2025. All rights reserved.