About Damn Vulnerable Bank
Damn Vulnerable Bank is designed to be an intentionally vulnerable android application. Numerous resources are available on the internet to get started with android application security. This application provides an interface to assess the security knowledge you gained over time. There are multiple vulnerabilities in the application and we documented all of them in this guide. Take your own sweet time to explore the application and detect all the vulnerabilities.

Outline
Authors
Damn Vulnerable Bank was created by Rewanth Tammana, Akshansh Jaiswal, Hrushikesh Kakade.
Rewanth Tammana is a security ninja, open-source contributor, and Senior Security Architect at Emirates NBD. He is passionate about DevSecOps, Application, and Container Security. He added 17,000+ lines of code to Nmap (famous as Swiss Army knife of network utilities). Holds industry certifications like CKS (Certified Kubernetes Security Specialist), CKA (Certified Kubernetes Administrator), etc. Rewanth speaks and delivers training at multiple international security conferences around the world including Black Hat, Defcon, Hack In The Box (Dubai and Amsterdam), CRESTCon UK, PHDays, Nullcon, Bsides, CISO Platform, null chapters and multiple others. He was recognized as one of the MVP researchers on Bugcrowd (2018) and identified vulnerabilities in several organizations. He also published an IEEE research paper on an offensive attack in Machine Learning and Security. He was also a part of the renowned Google Summer of Code program.
Akshansh Jaiswal is a security engineer at CRED who works closely around Web ,Mobile and Cloud Security.He is also an active CTF player where he has won several CTF's such as Hackerone CTF's -h1 100k CTF, Hacky Holidays CTF,h1-2006 CTF, BugPOC CTF's and community CTF's. He also participates actively in Bug Bounties where he is an active hacker on platforms like Hackerone and Synack Red Team where he finds and reports vulnerabilities to various organisations.He has also been part of Hackerone exclusive Live hacking event h1-2103 where selected hackers got a chance to find security issues in Amazon public applications and infrastructure.
Hrushikesh Kakade is a Payatu bandit who specializes in advanced assessments of Mobile Security (Android and iOS), Network Infrastructure Security, DevSecOps, Container security, Web security, and Cloud security. Hrushikesh is a member of the Synack Red Team and is a holder of renowned OSCP (Offensive Security Certified Professional) certification. He is an active member of local Cybersecurity chapters and has delivered multiple talks and workshops. He is an Open Source Contributor and has a keen understanding of Linux Internals. He has received multiple CVEs to his name for finding vulnerabilities in different applications.
Install tools
To get started with reviewing the android application, as a pre-requisite we need to install a few of tools for smoother testing. We will need the following tools:
- Android phone (rooted preferred) or emulator like genymotion, memu, etc.
- adb
- frida
- apkx
- apktool
- ghidra
Install frida
First things first, we need to install frida-server on the target device. I'm using genymotion as my emulator which uses x86 as underlying architecture. Download, here. If you are using android phone use arm downloadable, here
Download the file, extract the executable and push it to the target device.
wget https://github.com/frida/frida/releases/download/14.2.18/frida-server-14.2.18-android-x86.xz
xz --decompress frida-server-14.2.18-android-x86.xz
adb push frida-server-14.2.18-android-x86 /tmp
Open shell on the emulator with adb shell
and execute the file in the background
[email protected]:/tmp$ adb shell
vbox86p:/$ cd tmp
vbox86p:/tmp$ ./frida-server-14.2.18-android-x86 &
The terminal might stop responding after running frida server in background. Don't panic, kill the session and open a new terminal for further operations.
Install frida client on the attacking machine.
pip install frida-tools
Check frida status
frida-ps -U
If you are getting any different output, possible reasons are frida server might not be running or frida tools aren't installed properly. Refer to the tools page.
Install docker and start backend server
TBD
Install and configure burpsuite with emulator
TBD
Install Application
Get the application running
Download the apk from the repository. Try installing and running it.
Install the application
I'm using genymotion as my emulator to install the application.
adb install dvba.apk
Application installed successfully.
Run the application
Try running the application. It doesn't work.
Looks like the app isn't built to work in an emulator. Let's fix it :-)
Issues while reversing (recompiling) the application with apktool
Depending on the apktool version, you might face an error recompiling the application. One of such error is brut.common.BrutException
The following commands can fix the error.
apktool empty-framework-dir --force
apktool b dvba/ -o dvba-no-gpu.apk
Bypass detection checks
There are multiple detection checks enabled everywhere in the application to check the integrity of the user.
Bypass GPU detection
Run the application
Try running the application. It doesn't work.
Looks like the app isn't built to work in an emulator. Let's fix it :-)
Reverse the android application
Decompile the application with apktool
to view the AndroidManifest
file.
apktool d dvba.apk -o dvba-apktool
Observe the AndroidManifest
for a while to see if something is responsible for not allowing the application to run in an emulator. The hardwareAccelerated
flag is set to true
, that means the application is planning to utilize the mobile's GPU resources to get it running. Here's the catch! Unfortunately, emulators doesn't run with GPU. At least, the genymotion emulator which we are using doesn't have a GPU and that's causing the application to crash.
Change the hardwareAccelerated
to false
, re-build the application and try running it.
Now, try installing the new apk.
Failed to install due to certificate issues. Create certificates and sign the new apk.
Create a release key keystore
keytool -genkey -v -keystore my-release-key.keystore -alias dvba-no-gpu -keyalg RSA -keysize 2048 -validity 10000
Sign the new built apk with the release key
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore dvba-no-gpu.apk dvba-no-gpu
Now, install the application on the device and try running it. Even now the app crashes but this time we get an error message, Emulator Detected
Let's reverse the apk code to see if we can find something related to emulator detection. We will use apkx tool.
We can see the error message, "Emulator Detected" and the app crashes. Let's find the string in the decompiled code.
Open the file MainActivity.java
, and check the code.
As you can see from the above, if var2_4 != 0
, the application prints, "Emulator Detected"
. But if you observe carefully, that isn't the block of code that's killing the application. There's a function call in a few if-else blocks in the below lines, this.finish()
. This particular call is responsible for the application to terminate.
One of the messages on the screen after the application crashed is, "Phone is Rooted"
. So, after the code detected we are using a rooted mobile the application crashed due to this.finish()
call at line 260.
Our target is to bypass this check.
Bypassing root detection check
In line 258, we can see an if
block that makes the application run the block of code. Our target is to toggle the application code or process in a way that it doesn't enter this block of code.
There are multiple ways to achieve this task. According to the code,the return value of a.R()
, at line 258 decides whether the application runs the block of code or not. If we can toggle the value of a.R()
, we can solve the problem. We will use frida
to perform this hack.
We now understand that we need to toggle a.R()
. We need to hook the method, a.R()
and toggle the function's return value to make the application work. Frida comes with Javascript API support to make things easier for us.
// @File: scripts/script.js
setTimeout(function() {
Java.perform(function() {
// We are loading the class here
var Rfunc = Java.use("a.a.a.a.a");
// Whenever a.a.a.a.a.R() is called, the below code is executed
Rfunc.R.implementation = function() {
// Capture the return value of R function
let retval = this.R();
// Printing the value we got from this function
console.log("Original return value = ", retval);
// Toggle the return value to make sure it doesn't enter the if loop
return !retval
}
})
}, 10);
Run the frida command to check it
frida -U -f com.app.damnvulnerablebank -l scripts/script.js
We can see an error message after application crashed, Frida is running
. This confirms the application is having another check to detect if we are trying to hook the application and tamper with it. If we are trying to hook it, the application crashes. Our target is to bypass this method hooking detection check.
Bypassing hooking method (frida) detection check
Look into the code for traces of frida to find the piece of code that looks for method hooking detection.
grep -r -i frida
We can see from the above image, there is a lib/
folder and System.loadLibrary((String)"frida-check")
. This means the application is calling a method written in native code to perform the method hooking/frida check. Let's open the lib/
folder to see what it is having. Since we are using genymotion and it runs on x86
architecture, let's check that directory. We can see some *.so
files and they are executable files.
Our target is to analyze these library files. To analyze these library files, you will need a tool like IDA/ghidra. Let's use ghidra because it has support to multiple architectures with free of cost.
Binary analysis with ghidra
Drag and drop the libfrida-check.so
file to ghidra, select all the default options, and once its loaded double click on the file name to open ghidra.
This isn't a full strech course on binary analysis, so we skip the basics and try to get started with the analysis.
You can see the pseudo code of the fridaCheck
function on the right side. In line 20, we can see a socket connection. The application is trying to connect to particular port and depending on the status, its returning some value. In order to perform socket connection, it requires a host
and port
value and we can see in line 20, its utilizing data in local_28
which is 0xa2690002
.
In most cases, the binaries contain code in big-endian format. So, we need to convert 0xa2690002
to actual value. This is having 8 bits, we have to split it into half. Now, it will become 0xa269
and 0x0002
. In order to convert them from big-endian to decimal value, we have to swap the first 2 and last 2 bits.
0xa269
becomes 0x69a2
whose decimal equivaluent is 27042
.
These values are being passed to connect
function which makes a socket connection. So, this must be the port number, the application is trying to connect. As we know frida runs on client server architecture. Frida's default port is 27042
.
In short, the application is trying to make a socket connection to port 27042
to check if frida is running or not. If frida is running, 27042
will be occupied. Depending on whether the application is able to make socket connection to port 27042
, we decide if frida is running or not.
Bypassing frida detection check
Ideally, the application is looking for default port of frida only. Bypassing this is simple, we will just need to run frida on another port.
Kill the frida server in the emulator and try running it another port like 1337
.
emulator$ ./frida-server-14.2.18-android-x86 -l 0.0.0.0:1337
ubuntu$ frida -H $DEVICE_IP:1337 -f com.app.damnvulnerablebank -l scripts/script.js
You can see from the above image, even when we are hooking the application with frida, it still says frida is not running
. We bypassed both frida detection and root detection checks.
Footprinting the application
Footprinting is the crucial part in pentesting any application. It allows us to look more into multiple things.
Start the backend server, enter the URL in the designated field and check for health status. Perform health check to make sure the API is up and running.
There are some pre-created accounts for testing but nevertheless you can still sign up for the new account.
You can continue to explore the application but all the data is being sent to the server. Let's use burpsuite to intercept the traffic.
Turn on the interceptor in burp suite, make sure you are listening on all interfaces and try re-running the application with frida. Now, login into the application and you can see similar output on your burpsuite.
The application is using REST API as its backend and in every API in the body of POST request, we will usually see data but here instead both the request and response are encrypted. It means the application is performing encryption and decryption before sending the requests.
Consdering we are seeing REST API, we need to look for API vulnerabilities. But both request and response are encrypted and we can't tamper them. In order for us to tamper with the request and response, we have to decrypt them first. Explore the code till you find this encryption and decryption functions.
- Decrypting response
- Recreating crypto functions
Decrypting response
We can see all the intercepted data is encrypted. It means when we get the response from the server, the application has to decrypt it inorder to perform subsequent operations. We extracted java code from the apk with apkx tool. Since, the decryption is inevitable, let's open a random API endpoint and analyze the java code. I'm opening ViewBalance.java
for analysis.
If you remember, in the previous section, we have seen the intercepted burpsuite data is having enc_data
as key. So, the application has to pick the value of, enc_data
for it to decrypt. Search for enc_data
in ViewBalance.java
.
A small snippet from the above screenshot.
...
import c.b.a.e;
...
object = new JSONObject(e.a(object.get("enc_data").toString()));
int n2 = object.getJSONObject("status").getInt("code");
...
We can see from the above snippet that this piece of code fetches data from enc_data
, passes it to e.a
function, makes it a JSON object and stores in object
variable. It means our encrypted data is being passed to e.a
function for processing.
Our primary task is to hook the e.a
function to see the data it receives and sends. Similar to root detection bypass, we need to leverage frida to achieve this goal. Do not tamper anything yet.
/*
Root detection bypass script here
*/
// Decrypting response
setTimeout(function() {
Java.perform(function() {
var decryptFunc = Java.use("c.b.a.e");
decryptFunc.a.implementation = function(enc_data) {
console.log("Recevied data = ", enc_data)
let retval = this.a(enc_data);
console.log("Sending data = ", retval);
return retval
}
})
}, 10);
We can successfully see the decrypted response but its not useful for us. For us to perform pentesting, we need to tamper with the requests but unfortunately even the request body is being encrypted before sending it to the server. It means, the application is encrypting it before sending it to the server. Our task is to find the function encrypting the data.
If you analyze the code of ViewBalance.java
, we can't see any request parameters in it because it doesn't have any request parameters. We need to find another API that contains request body. For example, let's randomly pick BankLogin.java
as they will likely have username and password as body parameters. Analyze the code to find the encryption snippet.
As we can see similar to decrypt function, we have encrypt function here but really obfuscated. The real data here, i.e username and password are being passed to e.b()
for further processing. So, we need to hook e.b()
to view unencrypted data.
/*
Root detection bypass script here
*/
/*
Decrypt response script here
*/
// Decrypting request
setTimeout(function() {
Java.perform(function() {
var encryptFunc = Java.use("c.b.a.e");
encryptFunc.b.implementation = function(enc_data) {
console.log("Encryptfunc sending data = ", enc_data)
let retval = this.b(enc_data);
console.log("Encryptfunc received data = ", retval);
return retval
}
})
}, 10);
Recreating encrypt and decrypt functions locally
We achieved great things so far. We managed to view the decrypted data of both request, response and now we even have the ability to tamper with it. But writing our test cases in frida scripts for all endpoints with different parameters is chaos job. If we can somehow manage to replicate the encrypt and decrypt functions on our local systems, we can encrypt and decrypt intercepted data with ease.
From the above analysis, we can conclude the e.a()
and e.b()
functions inside class c.b.a.e
are responsible for encrypt and decrypt functions. Open the file in code editor for further analysis.
Woah! We found the encrypt, decrypt logic and our goal is to replicate this locally to ease the pentesting process. Re-build equivalent code locally. I prefer coding in NodeJS.
const SECRET = 'amazing';
const SECRET_LENGTH = SECRET.length;
const operate = (input) => {
let result = "";
for (let i in input) {
result += String.fromCharCode(input.charCodeAt(i)^SECRET.charCodeAt(i%SECRET_LENGTH));
}
return result;
}
const decrypt = (encodedInput) => {
let input = Buffer.from(encodedInput, 'base64').toString();
let dec = operate(input);
console.log(dec);
return dec;
}
const encrypt = (input) => {
let enc = operate(input.toString());
let b64 = Buffer.from(enc).toString('base64');
console.log(b64);
return b64;
}
Now, we have successfully re-built the encryption and decryption functions locally. When we intercept the request, we can use these to decrypt the encrypted data.
We have the below string in the response.
{"enc_data":"Gk8SDggaEhJPWwFLDQgFCENAW15XTU8MHxodBgYIQ0BLPRICDgQJGkwaTU8FGx0PRVsWQxgIAgYPDgRYU19XUV1RVksPBAICFBQdMQkUAAMfG0xdUllQQlFdGhw="}
Pass the above encrypted text to the decrypt
function you built locally.
decrypt("Gk8SDggaEhJPWwFLDQgFCENAW15XTU8MHxodBgYIQ0BLPRICDgQJGkwaTU8FGx0PRVsWQxgIAgYPDgRYU19XUV1RVksPBAICFBQdMQkUAAMfG0xdUllQQlFdGhw=")
Perfect! Our code is working :-)
Exploits
There are multiple vulnerabilities inherited by the application.
Looking for REST API vulnerabilities
API vulnerabilities are common entry points for exploitation. OWASP API Top 10 compiled list. We have to explore for all the cases to see if there is any vulnerability.
Exploration 1
In one of the above sections, we explored view balance
API.
decrypt("Gk8SDggaEhJPWwFLDQgFCENAW15XTU8MHxodBgYIQ0BLPRICDgQJGkwaTU8FGx0PRVsWQxgIAgYPDgRYU19XUV1RVksPBAICFBQdMQkUAAMfG0xdUllQQlFdGhw=")
Let's see if we can view balance of other users. If you see the intercepted request, there is no body or query string in the request made to view balance. But we can see the Authorization
header.
In my case, the authorization header is eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwiaXNfYWRtaW4iOmZhbHNlLCJpYXQiOjE2MjMzMzU4NDV9.lHjjBagQ2OYREBBDl0LXUNLNn1nD6nI4KMxgFlfXRWU
. This looks like JWT token. Let's decrypt this JWT token.
As you can see we have a field name "username":"user1"
. Since there is no body/query parameter in the intercepted request, there is high possibility that the application is extracting username from the authorization header. Let's change the username
to user2
, rebuild the request and then send it.
Copy this JWT token and replace the Authorization
header with this value and resend the request.
We will receive an encrypted response, copy that and pass it to your NodeJS script for decryption.
As we can see above, it throws 403
error and says the signature
doesn't match. It means the signature is getting validated on the backend and we can't tamper with the Authorization
token. It doesn't mean authorization is happening in all endpoints. It might happen, it might not happen, as a security expert its your job to verify them all.
Similarly you can explore the remaining scenarios.
Sensitive information disclosure
There are multiple ways for sensitive information leakage in android applications. The most common ones are:
- Hardcoded sensitive information
- Leakage via logcat/debug logs
Hardcoded sensitive information
We have already seen this in the section where we were replicating the cryptographic functions locally. The secret key that's used for crypting purposes is hardcoded in the source code of the application which allowed us to decrypt the encrypted request and responses.
Adb logcat
We have been using the application for a while. So, let's check the logs via adb logcat
.
In the above image, we can see logs of multiple processes. Our target is to find the logs specific to our application. Search for the Process ID (PID) of the damn vulnerable bank application.
adb shell ps -ef | grep damn
Now, grep adb logcat
output with the process ID (PID) specific to our target application.
adb logcat | grep 5021
Exploiting exported activities
Everything that you see in an android app is kind of being done via an activity. For example, in the given application the login screen, the splash screen, etc. everything is an activity. According to Android developer docs, An activity is a single, focused thing that the user can do.
Exported activities
An exported activity is the one that can be accessed by external components or apps. We can say that exported activities are like Public
functions in Java, any other parent class or even package can call them. Some activities are built to be user specific, but if those activities are exported and not protected, that creates issue.
We will use drozer to perform this check. Refer to tools page.
Interesting, we can see multiple exported activities with no permission checks. If an attacker can invoke these activities without authentication, it can create potential financial impact.
That's amazing. We are able to invoke the SendMoney
activity without giving in the credentials. Let's see if we can transfer money to other accounts.
We can get the account numbers list from the github page. Enter one of the valid account numbers to transfer the amount. For ex, user4 account number is 444444.
Login as user4
and get confirmation on the exploit.
Without authneticating into the application, we are able to exploit the sensitive exported activity and transfer amount to other bank accounts.
Exploiting webview via deeplink
Android WebView is a system component for the Android operating system (OS) that allows Android apps to display content from the web directly inside an application. Deep links are a type of link that send users directly to an app instead of a website or a store.
Exploration
In manifest file we can see the CurrencyRates, this activity accepts a deeplink to open webview with user input URL. Now in manifest as we can see that we have a deep link with http schema and xe.com as host and in CurrencyRates activity we can see webview configuration where if a url
paramter is provided then webview will render that URL without verifying host and url given this can be used to open any URI within and also can be used to get an XSS in the webview
To get an XSS prompt we will need to pass javascript to be loaded in webview we can either do this by hosting XSS payload on our website else call javascript in URI itself, we are here demonstrating via javascript URI itself,therefore for this case our final URL will looks as https://xe.com?url=javascript:prompt('Enterpassword')
But to get a working POC either we need to load it via HTML or call via adb, since HTML can be easily done so we For this we can create a html file with code as below
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<a href="https://xe.com?url=javascript:prompt('Enter password')">Lucky Draw</a>
</body>
</html>
Now load this page from mobile browser
As you can see we get option of our Vuldroid app just click on it to confirm, now as you can see we get the XSS prompt sucessfully
Contribution
We appreciate taking your time to explore Damn Vulnerable Bank.
You can contribute and help us to make things better.
Even if you can't code, you can still help us in the following ways.
- By providing your valuable feedback
- By raising pull request/opening issues for feature upgrades
- Improving the documentation/notes
- By spreading the word and sharing with community, friends, and colleagues
Share on social media
Contact me
Additional Resources
TBD