Purposefully Insecure and Vulnerable Android Application (PIVAA): Part 2

Hacktivities
9 min readJun 17, 2020

--

In part 2 of this series, I will attempt to discuss some of the different encryption vulnerabilities present in the “Purposefully Insecure and Vulnerable Android Application” (PIVAA).

Introduction

For part 2, the vulnerabilities that I will be discussing include:

  • Weak Hashing Algorithms
  • Predictable Random Number Generators (PRNG)
  • Weak Encryption Implementation (AES-ECB)
  • Weak Initialization Vectors (IV) (AES-CBC)

Weak Hashing Algorithms

The PIVAA application provides a feature that allows users to hash any string they enter using the MD5 hashing algorithm.

  • Vulnerability: MD5 hashing algorithm should not be used when reliable hashing of data is required.

This feature is not necessarily a vulnerability in relation to how it has been implemented in the PIVAA app but it is important to understand that weak hashing algorithms such as MD5 or SHA-1 are not reliable. MD5 suffers from the following issues:

  • MD5 is too fast to be a password hashing algorithm. Password hashing algorithms should slow an attacker down.
  • MD5 hashes are vulnerable to collisions, which is when two string inputs generate the same hash.

Statically analyzing the application’s code, I can see how the hashing feature is being implemented.

Hash Function

The hash function can be broken down as follows:

  1. The hash function takes in two string values which represent the hashing algorithm being implemented and the text being hashed.
  2. A “try catch” statement is then declared to handle any exceptions.
  3. The “MessageDigest” class is used to implement the functionality of a message digest algorithm (i.e. MD5).
  4. The “update()” method is used to process the data (i.e. text) provided.
  5. The “String.format(format, object)” method is used to convert an object to a string. The “%032x” means pad with 0, to a length of 32 characters and as a hexadecimal integer. The second value in the method is the object.
  6. The “digest.digest()” method is implemented to complete the hash computation.

To demonstrate the weakness of the MD5 hashing algorithm, I can generate an MD5 hash of a string input using the PIVAA application as seen below:

5F4DCC3B5AA765D61D8327DEB882CF99

I can then use a tool called “hashcat” to launch a dictionary attack.

Hashcat Dictionary Attack

It took 3 seconds to crack the MD5 hash and retrieve the string which is “password”. There are other ways to do this but this approach serves as an example of how easy it is to crack unsalted MD5 hashes.

Recommendations:

  • Use stronger hash algorithm's such as SHA-256, SHA-3, bcrypt , scrypt ,etc. when reliable hashing is required.
  • Make sure to salt password hashes, which is an additional input of data that helps to safeguard passwords in storage.

Predictable Random Number Generator (PRNG)

The PIVAA application uses the Random Java class to generate pseudorandom numbers:

java.util.Random
  • Vulnerability: The numbers generated by this class are not random and can be predictable.

Statically analyzing the applications code, I can see the function used to generate pseudorandom numbers.

Pseudo Random Number Generator

This code returns an integer as a string, with the integer being generated by the “nextInt()” method. This method returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified value (exclusive), i.e.100000.

Recommendations:

  • Use a cryptographically strong random number generator (RNG) like “java.security.SecureRandom” in place of this PRNG when a cryptographically strong random number needs to be used.
  • Use the generated random values only once.
  • You should not expose the generated random value. If you have to store it, make sure that the database or file is secure.

Weak Encryption Implementation

The PIVAA application implements AES encryption with Electronic Code Book mode.

  • Vulnerability: “AES/ECB/PKCS5Padding” has been proven to be insecure.

To understand why AES-ECB is insecure, its important to understand how it works. The diagram below illustrates how AES-ECB encryption operates.

Electronic Code Book (ECB) Mode Encryption

The diagram can be explained as follows:

  1. The message to be encrypted is broken down into blocks of bytes (AES is a block cipher).
  2. In ECB mode, each block of plaintext is encrypted independently with the key.
  3. This is weak, since the same ciphertext will be outputted if the same plaintext input is provided.

Statically analyzing the code of the PIVAA application, I can see the code used to perform AES encryption using ECB mode.

AES-ECB Mode Encryption

Breaking this function down from top to bottom, a single string argument is taken by the function and then a “try catch” is declared to handle any exceptions. The “SecretKeySpec” Java class constructs a secret key from a given array of bytes. The implementation of this class is also vulnerable since a hard coded key is specified.

Hard Coded Key

The “Cipher” Java class provides the “getInstance()” function which is used to return a cipher object that implements the specified transformation (i.e. “AES/ECB/PKCS5Padding”). The cipher instance must be initialized first before it can be used, by implementing the “init()” method which specifies the mode (2) and the secret key (skeySpec). The “doFinal()” method is then used to perform the encryption of the plaintext.

Cipher Class Implementation

Finally, the encrypted string is encoded using base64 and returned by the function. This base64 encoded string is also printed to the standard output by the “System.out.println()” method.

Base64 Encoding

I can demonstrate this issue in the PIVAA application by using a hypothetical scenario. In this scenario, I have acquired an encrypted ciphertext generated by the application, as seen below.

pni8kjpVJ5MnuXan/vf9Sg==

I can try performing a chosen plaintext attack. This essentially means I can try and guess what the original text was by entering in random strings and then comparing the encrypted output with the ciphertext I was somehow able to acquire.

Test 1
Test 2
Test 3

Test 3 has generated the same output as the ciphertext I was trying to find the plaintext for. I now know that the encrypted plaintext was the word “encryption”.

Recommendations:

  • Do not use ECB mode for cryptographic operations. Use the code below instead for cryptographic operations:
Cipher cipher = Cipher.getInstance("AES/CFB/PKCS5Padding");
  • Be aware that the code below will default to ECB mode, even if it is not specified:
Cipher cipher = Cipher.getInstance("AES");

Usage of Weak Initialization Vector

The PIVAA application implements AES encryption with Cipher Block Chaining (CBC) mode but uses a predictable initialization vector (IV).

  • Vulnerability: Predictable IVs can be exploited by chosen plain text attack.

It is important to have a general understanding of how AES-CBC works, in order to fully grasp why a predictable IV makes an encrypted item vulnerable to crypto-analysis techniques (e.g. Chosen-Plaintext Attack). The image below shows how AES-CBC encryption works.

Cipher Block Chaining (CBC) Mode Encryption

The diagram can be explained as follows:

  1. The message to be encrypted is broken down into blocks of bytes (AES is a block cipher).
  2. Each message block is encrypted using the AES algorithm and the same key.
  3. CBC works by taking the ciphertext output from the first message block and XOR it with the next plaintext message block before it is encrypted with AES. This is repeated for each subsequent message block. This is done to avoid redundancy in plaintext message blocks, where if the same key is used to encrypt the same input multiple times, then it will result in the same output each time.
  4. The first message block (M0) is XOR with an Initialization Vector (IV). This is done to start the CBC process and avoids repetition in the first message block. If the same key was used to encrypt the same input multiple times, then the same output would be generated for the first message block each time and would be subsequently used to XOR with the next message block before it is encrypted with AES.

I can statically analyse the code of the PIVAA application and observe the function used to perform AES encryption with Cipher Block Chaining (CBC) mode.

AES-CBC Mode Encryption

Breaking this function down from top to bottom, a single string argument is taken by the function and then a “try catch” is declared to handle any exceptions. The “IvParameterSpec” Java class is used to specify an Initialization Vector each time the function is used. Looking at the Initialization Vector, I can see that a hard coded value has been used.

Hard Coded Initialization Vector (IV)

The “SecretKeySpec” Java class constructs a secret key from a given array of bytes. The implementation of this class is also vulnerable since a hard coded key is specified.

Hard Coded Secret Key

The “Cipher” Java class provides the “getInstance()” function which is used to return a cipher object that implements the specified transformation (i.e. “AES/CBC/PKCS5Padding”). The cipher instance must be initialized first before it can be used, by implementing the “init()” method which specifies the mode (2), secret key (skeySpec) and Initialization Vector (iv). The “doFinal()” method is then used to perform the encryption of the plaintext.

Cipher Class Implementation

Finally, the encrypted string is encoded using base64 and returned by the function. This base64 encoded string is also printed to the standard output by the “System.out.println()” method.

Base64 Encoding

With a better understanding of what the applications code is doing, I can now demonstrate how to exploit this vulnerability in the application. Lets say I was able to get a hold of a string of ciphertext generated by the PIVAA application, such as the example below:

eAbbR0cmmbnlgfvlX7si9Q==

I don’t know what the encrypted text is but based on what I know about how the application has used a weak initialization vector, I can try performing a chosen plaintext attack. This essentially means I can try and guess what the original text was by entering in random strings and then comparing the encrypted output with the ciphertext I was somehow able to acquire.

Test 1
Test 2
Test 3

Test 3 has generated the same output as the ciphertext I was trying to find the plaintext for. I now know that the encrypted plaintext was the word “password”.

Recommendations:

  • Initialization Vector should be unpredictable.
  • Initialization Vector should be Random.
  • Initialization Vector should not be hard coded.
  • Initialization Vector should be created using java.security.SecureRandom rather than java.util.Random .

--

--

Hacktivities
Hacktivities

Written by Hacktivities

Interested in all things Cyber Security and Technology.

No responses yet