The Old Way To Decrypt The Malicious Code of CamScanner

Image for post
Image for post
© Andrew Champ

On August 27, 2019, Kaspersky published an article about an Android application called CamScanner. They detected that this very popular app, more than 100 million downloads, contained a malicious dropper component.

I am often curious about the technical details in order to know the latest techniques used by bad actors. So I decided to give it a look.

In their article, Kaspersky gave the list of IOCs. I downloaded c69a2d2b0bf67265590c9be65cd4286b on Koodous

According to the article:

When the app is run, dropper decrypts and executes the malicious code contained in the mutter.zip file in the app resources.

fs0c131y@Elliots-MacBook-Pro:~/CamScanner$ apktool --no-src d 7aed7771b15d795bcd5ee119885613a3d5124f46d72c501fce3429f6c3c78ba4 -o apktool.out
[...]
fs0c131y@Elliots-MacBook-Pro:~/CamScanner$ find apktool.out -name mutter.zip
apktool.out/assets/mutter.zip
fs0c131y@Elliots-MacBook-Pro:~CamScanner$ file apktool.out/assets/mutter.zip
apktool.out/assets/mutter.zip: data
fs0c131y@Elliots-MacBook-Pro:~/CamScanner$ binwalk apktool.out/assets/mutter.zipDECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------

Thanks to Apktool we managed to obtain the file called mutter.zip. As expected, due to the encryption, this file is not a zip file for the moment. We need to understand how CamScanner decrypt it.

So I opened Jeb and search “mutter” in the bytecode and as said in the article, the decryption is done in a class called Duration. Now we have 2 choices:
- We can use Frida in order to intercept the output of the “decrypt method” at the runtime. The only constraint is that we need to be here at the correct moment.
- We can decrypt the zip file outside of the app context by using a good old Java class.

Let’s look at the 2nd option. I created a new folder, moved the mutter.zip in it and created a new file called Duration.java.

Image for post
Image for post

In this Duration.java I created a main function and copy paste the code of the decryption code from CamScanner.

import android.content.Context;
import android.os.Process;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;

If I check the imports, I see 2 Android specific imports. In order to compile our Java class we need to get rid of these two imports.

Image for post
Image for post

These 2 imports are used to get the zip file from the assets and give a specific name to the decrypted file.

Image for post
Image for post

Simplification done, easy right?

I also remove the unused methods and add generic try catch everywhere and we have our final decryptor.

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.ArrayList;
public class Duration { public static void main(String[] args) {
fireman("mutter.zip", "ugi");
}
private static void climate(InputStream arg15, OutputStream arg16, int arg17) {
try {
byte[] v1 = new byte[4];
arg15.read(v1);
int v1_1 = arg17 ^ ((v1[0] & 0xFF) << 24) + ((v1[1] & 0xFF) << 16) + ((v1[2] & 0xFF) << 8) + ((v1[3] & 0xFF) << 0);
arg15.read(new byte[(arg15.read() ^ v1_1) & 0xFF]);
ArrayList v3 = new ArrayList();
int v4;
for(v4 = 0; v4 < 0x100; ++v4) {
v3.add(Integer.valueOf(v4));
}
int[] v4_1 = new int[0x100];
long v7 = (long)v1_1;
int v6;
for(v6 = 0; v6 < v4_1.length; ++v6) {
v7 = (long)(((int)((v7 * 0x3EAABF9EL + 0xACL & 0xFFFFFFFFL) >>> 16)));
v4_1[((Integer)v3.remove(((int)(Math.abs(v7) % (((long)v3.size())))))).intValue()] = v6;
}
byte[] v3_1 = new byte[0x400];
while(true) {
int v5 = arg15.read(v3_1);
if(v5 <= 0) {
return;
}
int v6_1;
for(v6_1 = 0; v6_1 < v5; ++v6_1) {
v3_1[v6_1] = (byte)v4_1[(v3_1[v6_1] ^ v1_1) & 0xFF];
}
arg16.write(v3_1, 0, v5);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static File fireman(String arg5, String arg6) {
try {
File v4 = new File("~/CamScanner/mutter.zip");
InputStream v0 = new FileInputStream(v4);
File v4_1 = new File("~/CamScanner/", "decrypted.zip");
FileOutputStream v5_1 = new FileOutputStream(v4_1);
int v6 = arg6.hashCode() > 0 ? arg6.hashCode() : -arg6.hashCode();
Duration.climate(v0, v5_1, v6 % 0x100);
return v4_1;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

We are ready to compile it and decrypt our zip file.

fs0c131y@Elliots-MacBook-Pro:~/CamScanner/decryptor$ javac Duration.java
Note: Duration.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
fs0c131y@Elliots-MacBook-Pro:~/CamScanner/decryptor$ java Durationfs0c131y@Elliots-MacBook-Pro:~/CamScanner/decryptor$ file decrypted.zip
decrypted.zip: Zip archive data, at least v2.0 to extract
fs0c131y@Elliots-MacBook-Pro:~/CamScanner/decryptor$ unzip decrypted
Archive: decrypted.zip
inflating: classes.dex

And voila! We decrypted mutter.zip and have a new DEX file to analyse.

I wanted to share this story for one reason: don’t get lost with all the fancy tools available on the Internet, sometimes a gold old Java class is enough to do what you want to do.

🇫🇷 Hacker. Fight disinformation at Predicta Lab. Not completely schizophrenic. Not related to USANetwork.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store