---
# System prepended metadata

title: Unstoppable Mobile Wallet Pentest Report
tags: [final report]

---

---
title: Unstoppable Mobile Wallet Pentest Report
tags: final report
---
{%hackmd 3hnaPC9lQLqX1TSLwsUqpQ %}

<div style="background-color: #545554; background-image: url(https://i.imgur.com/6MR4SvZ.png); background-position: right bottom; background-repeat: no-repeat; background-size: contain; color: #ddd; padding: 45px; width: 100%; height: 100%;">
<br/><br/>
<center>
<img src="https://i.imgur.com/WdH8098.png" style="background-color: transparent" height="232" />
</center>
<p style="font-size: 28px">Unstoppable Mobile Wallet</p>
<p style="font-size: 22px">Penestration Test</p>
<p style="font-size: 17px">
Initial Report: December 10th, 2020<br>Revision: February 5th, 2021
</p>
<br/><br/><br/><br/>
<p style="line-height: 1">
For : Unstoppable.money<br/>
By  :
Peiyu Wang @ CertiK
<a href="mailto:peiyu.wang@certik.org" style="color: rgb(249, 159, 28);">peiyu.wang@certik.org</a><br/>
Minzhi He @ CertiK
<a href="mailto:minzhi.he@certik.org" style="color: rgb(249, 159, 28)">minzhi.he@certik.org</a>
 <br/><br/><br/><br/><br/><br/></div></p><br/>
</div>

---

## <img src="https://svgshare.com/i/QKR.svg" height=40px;/>Confidentiality Statement

All information contained in this document is provided in confidence for the sole purpose of adjudication of the document and shall not be published or disclosed wholly or in part to any other party without CertiK's prior permission in writing and shall be held in safe custody. These obligations shall not apply to information that is published or becomes known legitimately from some source other than CertiK.
All transactions are subject to the appropriate CertiK Standard Terms and Conditions. Certain information given in connection with this proposal is marked “In Commercial Confidence”. That information is communicated in confidence, and disclosure of it to any person other than with CertiK’s consent will be a breach of confidence actionable on the part of CertiK.



## <img src="https://svgshare.com/i/QKR.svg" height=40px;/>Disclaimer
This document is provided for information purposes only. CertiK accepts no responsibility for any errors or omissions that it may contain.
This document is provided without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and non-infringement. In no event shall CertiK be liable for any claim, damages or other liability (either direct or indirect or consequential), whether in an action of contract, tort or otherwise, arising from, out of or in connection with this document or the contents thereof.
This document represents our budgetary price proposal for the solution further described in this herein and is provided for information and evaluation purposes only and is not currently a formal offer capable of acceptance.

<div style="page-break-after: always; break-after: page;"></div>

---

## <img src="https://svgshare.com/i/QKR.svg" height=40px;/>Overview

### Scope

At the start of the engagement, CertiK worked with Unstoppable.money to identify the target and set the limits on the scope of the test. A White Box type of testing approach was done where CertiK performed the test with the source code available from the public GitHub repository. 

| **Project Name**  | Unstoppable Mobile Wallet                                    |
| ----------------- | ------------------------------------------------------------ |
| **Download Link** | [Google Play Store](https://play.google.com/store/apps/details?id=io.horizontalsystems.bankwallet)<br>[App Store](https://apps.apple.com/app/bank-bitcoin-wallet/id1447619907?ls=1) |
| **Codebase**      | [GitHub Repository(iOS)](https://github.com/horizontalsystems/unstoppable-wallet-ios/tree/0.17.1)<br>[GitHub Repository(Android)](https://github.com/horizontalsystems/unstoppable-wallet-android/tree/0.17.1) |
| **Initial Commit**       | 34146660891c9e132bcfce96816ef9c3afccbd0a(iOS)<br>a9ebd7e5262ac305e32b18415f9b6e81b36fac54(Android) |
| **Updated App**       |https://testflight.apple.com/join/JaKdKPY5 |


### Audit Summary

| **Delivery Date**       | Feb. 5, 2021                      |
| ----------------------- | ----------------------------------- |
| **Method of Audit**     | Static Code Review, Dynamic Testing |
| **Consultants Engaged** | 2                                   |
| **Initial Test**            | Dec. 2, 2020 - Dec. 9, 2020         |
| **Retest**            | Feb. 5, 2021 |
### Vulnerability Summary

| **Total Issues**        | 4    |
| ----------------------- | ---- |
| **Total Meduim**        | 1    |
| **Total Low**           | 2    |
| **Total Informational** | 1    |

<div style="page-break-after: always; break-after: page;"></div>

---


## <img src="https://svgshare.com/i/QKR.svg" height=40px;/>Executive Summary

Unstoppable.money engaged CertiK to perform an application penetration test for their mobile wallet. The main objective of the engagement is to test the overall resiliency of the application to various real-world attacks against the application’s controls and functions, and thereby be able to identify its weaknesses and provide recommendations to fix and improve its overall security posture.

CertiK started the test on December 2, 2020 and completed on December 9, 2020.

After a thorough review of the application, CertiK believes that the Unstoppable mobile wallet is securely built and follows the mobile application security best practices. Given the severity of the vulnerabilities on the application, it is unlikely that the application will be directly compromised. 

The only notable issue is we were able to bypass the old passcode verification and reset the passcode in the application running on an Android device. 

Unstoppable.money has worked diligently to remediate issues discovered by CertiK, significantly increasing the overall security posture of their application. CertiK performed the re-test on February 5, 2021 and verified issues mentioned in the report have been fixed. We suggest that Unstoppable.money maintain this level of security on future development and leverage our team for a follow up security assessment after any major development changes.

<div style="page-break-after: always; break-after: page;"></div>

---

## <img src="https://svgshare.com/i/QKR.svg" height=40px;/>Findings

| ID                | Title                                                        | Severity                                         | Vulnerability Class                                          | Status |
| :---------------- | ------------------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------ | ------ |
| [UNM-01](#UNM-01) | <span class="medium">Reset passcode without entering the current passcode</span> | <span class="medium">Medium</span>               | <span class="medium">Broken Access Control </span>           | <span class="informational">Fixed</span>    |
| [UNM-02](#UNM-02) | <span class="low">Insufficient warning for wipe out wallet data when disabling device passcode</span> | <span class="low">Low</span>                     | <span class="low">Miscellaneous</span>                       | <span class="informational">Fixed</span>    |
| [UNM-03](#UNM-03) | <span class="low">Mess up the passcode lockout timer with device reboot</span> | <span class="low">Low</span>                     | <span class="low">Denial of services</span>                  | <span class="informational">Fixed</span>    |
| [UNM-04](#UNM-04) | <span class="informational">No root or jailbreak detection on Android/iOS application</span> | <span class="informational">Informational</span> | <span class="informational">Security Misconfiguration</span> | <span class="informational">Fixed</span>    |


<div style="page-break-after: always; break-after: page;"></div>

---


<section id="medium" style="padding: 0px">

### ![](https://svgshare.com/i/QKR.svg)UNM-01: Reset passcode without entering the current passcode

##### <span class="medium">Severity: Medium</span>

##### Platform: Android

##### Description

<div style="margin-top:5px">
The Unstoppable wallet allows users to set up a passcode to protect their wallet account. The application requires the user to enter the old passcode in order to reset the password. However, we were able to reset the passcode without entering the current one in the application running on an Android device. 
</div>

##### Impact
<div style="margin-top:5px">
An attacker can retrieve the wallet mnemonic by resetting the passcode on an Android device if he has physical access to the device, and the wallet application is running in the foreground.
</div>

##### Step to Reproduce  
<div style="margin-top:5px">
1. Create a wallet in Unstoppable application on an Android device and set up the passcode.<br>
2. Navigate to the "Security Center" interface and click the highlighted button in the following screenshot to disable the passcode.<br>
<img src=https://goodrives.com/img/screen_1.png style="zoom:25%;">
<div style="page-break-after: always; break-after: page;"></div>
3. After entering the "verify old passcode" screen, click the return button at the bottom of the screen.<br>
<img src=https://goodrives.com/img/screen_2.png style="zoom:25%;">
<br><br>
4. Notice the passcode is shown disabled on the screen. The user can set a new passcode for the wallet after clicking the passcode enable button.<br>
<img src=https://goodrives.com/img/screen_3.png style="zoom:25%;">
</div>

##### Recommendation

<div style="margin-top:5px">
The "Security Center" page should not show the passcode is disabled and allow users to enter the new passcode setting screen, after the user clicks the return button at the verify old passcode screen.</div>


</section>

<div style="page-break-after: always; break-after: page;"></div>

---

<section id="low" style="padding: 0px">

### ![](https://svgshare.com/i/QKR.svg)UNM-02: Insufficient warning for wipe out wallet data when disabling device passcode

##### <span class="low">Severity: Low</span>

##### Description
<div style="margin-top:5px">
We found that if users turn off the passcode on their devices after creating wallet accounts, all wallet accounts will be deleted from the application. The application only displays the warning regarding this behavior when users start using the wallet without setting up a device passcode. We could not find any place in the application that warns users their wallet data will be gone if they disable the device's passcode after users start using it.
</div>

##### Impact
<div style="margin-top:5px">
Users could turn off the device passcode without knowing the fact that this action can cause their account in the  application to be wiped out.   
</div>

##### Step to Reproduce  
<div style="margin-top:5px">1. Create wallet accounts in the application. <br>
2. Turn off the device passcode and turn it back on again<br>
3. Back to the Unstoppable wallet and notice all wallet accounts are gone. <br>
</div>

##### Recommendation
<div style="margin-top:5px">
Display a warning in a noticeable location in the application to let users be aware of the consequence of turning off the device passcode. 
</div>
</section>

<div style="page-break-after: always; break-after: page;"></div>

---

<section id="low" style="padding: 0px">

### ![](https://svgshare.com/i/QKR.svg)UNM-03: Mess up the passcode lockout timer with device reboot

##### <span class="low">Severity: Low</span>

##### Description
<div style="margin-top:5px">

The Unstoppable wallet uses the passcode to provide an extra layer of security. If a user enters the wrong passcode more than four times, the application would be locked for a certain period of time.  We discovered that if the device reboots during the passcode lockout period, the lockout timer changed a lot.

After reviewing the source code, we figure out that the application calculates the remaining lock time in the following way: When the user enters the wrong passcode more than four times, the application fetches the current uptime, stores it as the `lockoutTimestamp`, and enter the lock state. When the user attempts to unlock the wallet, the application fetchs the current uptime, calculates the difference between the current uptime and the stored `lockoutTimestamp`, and uses total locked time(5 * 60 * 1000) minus the difference to get the remaining lock time. 

The problem with the implementation is if the device reboots during the lockout period, the uptime resets to zero. This causes the remaining lock time to become total locked time(ex. 5 * 60) plus the stored `lockoutTimestamp`. The `lockoutTimestamp` can be a large number if the device stays up for a long time before the application is locked.
</div>

##### Location
<div style="margin-top:5px">
Android: components/pin/src/main/java/io/horizontalsystems/pin/core/LockoutUntilDateFactory.kt

```kotlin
when {
    failedAttempts == 5 -> timeFrame = 5 * 60 * 1000 - (uptime - lockoutTimestamp)
    failedAttempts == 6 -> timeFrame = 10 * 60 * 1000 - (uptime - lockoutTimestamp)
    failedAttempts == 7 -> timeFrame = 15 * 60 * 1000 - (uptime - lockoutTimestamp)
    failedAttempts >= 8 -> timeFrame = 30 * 60 * 1000 - (uptime - lockoutTimestamp)
        }
```

iOS:
The `lockoutUntilDate` function in component-kit-ios/PinKit/Classes/Factories/LockoutUntilDateFactory.swift
</div>

##### Impact
<div style="margin-top:5px">
The application will potentially be locked for a massive amount of time if users accidentally reboot the device when the application is locked. An attacker who has access to the unlocked mobile device can also perform the attack to deny the victim from using the wallet. Users most likely has to re-install the application and restored his accounts.
</div>

##### Step to Reproduce  
<div style="margin-top:5px">
1. Set up a passcode in the Unstoppable wallet and close the application.
<br>
2. Open the Unstoppable wallet and enter a wrong passcode five times. Notice the application gets locked for 5 minutes.
<br>
3. Reboot the device and open the application. Notice the "Disabled until" timer changed a lot.
</div>

##### Recommendation
<div style="margin-top:5px">

This issue's root cause is the uptime gets reset after the device reboot. One of the potential solutions is when calculating the `timeFrame`, check if the uptime is smaller than the `lockoutTimestamp`. If the uptime is smaller than the stored `lockoutTimestamp`, let the `timeFrame` equal to 
`locktime(ex. 5*60) - uptime`. 

With the implementation above, the device reboot would only reset the timer instead of causing the application to lockout for a massive amount of time if the `lockoutTimestamp` is a large number.
</div>

</section>

<div style="page-break-after: always; break-after: page;"></div>

---

<section id="informational" style="padding: 0px">

### ![](https://svgshare.com/i/QKR.svg)UNM-04:  No root or jailbreak detection on Android/iOS application

##### <span class="informational">Severity: Informational</span>

##### Description
<div style="margin-top:5px">
No root or jailbreak detection is implemented in the application. This allows an attacker to modify the app on a rooted Android or jailbroken iOS device, which means the attacker can potentially induce behaviors that otherwise would not occur. Examples of the impact include accessing sensitive application data, overwriting critical functions, and an overall wider attack surface.
</div>

##### Impact
<div style="margin-top:5px">
A malicious app with root permission can potentially steal a user's mnemonic with the following method:<br>

iOS: Dump the keychain data when the device is unlocked.
Android: Reset passcode by altering "io.horizontalsystems.bankwallet_preferences.xml" in shared_pref folder to retrieve the mnemonic.
</div>

##### Step to Reproduce  
<div style="margin-top:5px">
Install and run the Unstoppable wallet on a rooted Android or jailbroken iOS device. 
</div>

##### Recommendation

<div style="margin-top:5px">
Implement root and jailbreak detection at the beginning of the runtime of your application. Shut down the application or at least display a warning message when a user attempts to use the wallet on a rooted or jailbroken device. 

Some methods to check for a rooted/jailbroken device are listed below:

###### Android
* Check for existing su binaries, such as: 
    * /system/bin/su
    * /system/xbin/su
    * /sbin/su
    * /system/su
    * /system/bin/.ext/.su
    
* Attempt to use the su command directly and compare the current user ID before and after to see if the user was successfully upgraded to root.
* Utilize SafetyNet by Google. This was developed to try and detect device modifications and will prevent the application from running if the device fails the checks.

###### iOS
* Check for the existence of common files or directories that are associated with a jailbroken device. A list of some of the potential files to check can be seen here:
    * /Applications/Cydia.app
    * /Applications/FakeCarrier.app
    * /Applications/Icy.app
    * /usr/bin/sshd

* Attempt to write to a file in a location that is outside of the application's virtual sandbox. An example would be to try and write to the /private directory. This attempt would fail on a non-jailbroken device because the application does not have permission to access anything outside of its virtual sandbox, but if the application succeeds in writing to that location, then it can be deduced that it has root permissions, which means that the device is jailbroken.
</div>

</section>

<div style="page-break-after: always; break-after: page;"></div>

---
## <img src="https://svgshare.com/i/QKR.svg" height=50px;/>Appendix – Methodology 

CertiK uses a comprehensive penetration testing methodology which adheres to industry best practices and standards in security assessments including from OWASP (Open Web Application Security Project), NIST, PTES (Penetration Testing Execution Standard).

Below is a flowchart of our assessment process:

<img src="https://www.wispwisp.com/flow.png" style="zoom:50%;" />


### Coverage and Prioritization
As many components as possible will be tested manually. Priority is generally based on three factors: critical security controls, sensitive data, and the likelihood of vulnerability.

Critical security controls will always receive the top priority in the test. If a vulnerability is discovered in the critical security control, the entire application is likely to be compromised, resulting in a critical-risk to the business. For most applications, critical controls will include the login page, but it could also include major workflows such as the checkout function in an online store.

The Second priority is given to application components that handle sensitive data. This is dependent on business priorities, but common examples include payment card data, financial data, or authentication credentials.

Final priority includes areas of the application that are most likely to be vulnerable. This is based on CertiK’ experience with similar applications developed using the same technology or with other applications that fit the same business role. For example, large applications will often have older sections that are less likely to utilize modern security techniques.
### Reconnaissance
CertiK gathers information about the target application from various sources depending on the type of test being performed. CertiK obtains whatever information that is possible and appropriate from the client during scoping and supplements it with relevant information that can be gathered from public sources. This helps provide a better overall picture and understanding of the target.
### Application Mapping
CertiK examines the application, reviewing its contents, and mapping out all its functionalities and components. CertiK makes use of different tools and techniques to traverse the entire application and document all input areas and processes. Automated tools are used to scan the application and it is then manually examined for all its parameters and functionalities.  With this, CertiK creates and widens the overall attack surface of the target application. 
### Vulnerability Discovery
Using the information that is gathered, CertiK comes up with various attack vectors to test against the application. CertiK uses a combination of automated tools and manual techniques to identify vulnerabilities and weaknesses. Industry-recognized testing tools will be used, including Burp Suite, Nikto, Metasploit, and Kali. Furthermore, any controls in place that would inhibit the successful exploitation of a particular system will be noted. 
### Vulnerability Confirmation
After discovering vulnerabilities in the application, CertiK validates the vulnerabilities and assesses its overall impact. To validate, CertiK performs a Proof-of-Concept of an attack on the vulnerability, simulating real world scenarios to prove the risk and overall impact of the vulnerability.

Through CertiK’ knowledge and experience on attacks and exploitation techniques, CertiK is able to process all weaknesses and examine how they can be combined to compromise the application. CertiK may use different attack chains, leveraging different weaknesses to escalate and gain a more significant compromise.

To minimize any potential negative impact, vulnerability exploitation was only attempted when it would not adversely affect production applications and systems, and then only to confirm the presence of a specific vulnerability. Any attack with the potential to cause system downtime or seriously impact business continuity was not performed. Vulnerabilities were never exploited to delete or modify data; only read-level access was attempted. If it appeared possible to modify data, this was noted in the list of vulnerabilities below. 
### Immediate escalation of High or Critical Findings
If critical or high findings are found whereby application elements are compromised, client’s key security contacts will be notified immediately.


<div style="page-break-after: always; break-after: page;"></div>

---
## Vulnerability Classes

| Insecure Data Storage                  | • Sensitive Data Store in Plant Text <br> • Use of Public Storage <br>• Logging sensitive data |
| -------------------------------------- | ------------------------------------------------------------ |
| Information Disclosure                 | • Directory Indexing <br>• Verbose Error Messages<br>• HTML CommentsDefault Content |
| Account Policy                         | • Default / Weak Passwords<br>• Unlimited Login Attempts<br>• Password Reset<br>• Insufficient Session Expiration |
| Session Management                     | • Session Identifier PredictionSession Hijacking<br>• Cross-Site Request Forgery |
| Injection                              | • SQL Injection<br>• Cross-Site Scripting<br>• HTML InjectionXML Injection<br>• OS Command Injection |
| Broken Access Control                  | • Authentication Bypass<br>• Authorization Bypass<br>• Privilege Escalation<br>• Insecure Inter-Process Communication(intents, sockets) |
| Application Resource Handling          | • Path Traversal<br>• Predictable Object Identifiers<br>• XML External Entity Expansion |
| Logic Flaws                            | • Abuse of FunctionalityWorkflow Bypass                      |
| Insufficient Cryptography              | • Weak Hashing Algorithms<br>• Weak Encryption Algorithms<br>• Hard Coded Cryptographic Key |
| Denial of Service                      | • Server-side Denial of service<br>• Client-side Denial of service |
| Security Misconfiguration              | • Missing Security Headers<br>• Debugging Enabled            |
| Reverse Engineering and Code Tampering | • Lack of Root Detection<br>• Lack of Tampering Detection<br>• Lack of Code Obfuscation |


<div style="page-break-after: always; break-after: page;"></div>

---
## Risk Assessment
The following risk levels categorize the risk level of issues presented in the report:

 

| Risk Level                                          | CVSS Score | Impact                                                       | Exploitability                                               |
| --------------------------------------------------- | ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| <span class="critical">Critical</span>              | 9.0-10.0   | Root-level or full-system compromise, large-scale data breach | Trivial and straightforward                                  |
| <span class="high">High</span>                      | 7.0-8.9    | Elevated privilege access, significant data loss or downtime | Easy, vulnerability details or exploit code are publicly available, but may need additional attack vectors (e.g., social engineering) |
| <span class="medium">Medium</span>                  | 4.0-6.9    | Limited access but can still cause loss of tangible assets, which may violate, harm, or impede the org's mission, reputation, or interests. | Difficult, requires a skilled attacker, needs additional attack vectors, attacker must reside on the same network, requires user privileges |
| <span class="low">Low</span>                        | 0.1-3.9    | Very little impact on an org’s business                      | Extremely difficult, requires local or physical system access |
| <span class="informational"> Informational  </span> | 0.0        | Discloses information that may be of interest to an attacker. | Not exploitable but rather is a weakness that may be useful to an attacker should a higher risk issue be found that allows for a system exploit |