Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flowdroid could not detect Taint Propagation to Sink for taints in a method #766

Open
marshalwahlexyz1 opened this issue Sep 20, 2024 · 2 comments

Comments

@marshalwahlexyz1
Copy link

Hi @StevenArzt , @t1mlange

I am working on analyzing Android Apps, I included API used to retrieve contact, sms and media in the source and sink file.
Flowdroid does identify these sources and sinks but does not detect a leak when actually data was tainted using those methods (this was confirmed by manually inspecting the code, and when data was tainted it ends up in a network connection, it was also confirmed dynamically using Frida to hook into the methods along that path.)

All I need is to be able to see the taint path for each source flowdroid analyzes. I suppose flowdroid could not find a leak because the source and sink file are in different classes. However, the sink method was called within the source method class.

This is my source and sink file

<android.content.ContentResolver: android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String)> -> SOURCE
<android.database.Cursor: java.lang.String getString(int)> -> SOURCE
<android.net.Uri: android.net.Uri parse(java.lang.String)> -> SOURCE
<android.media.ExifInterface: java.lang.String getAttribute(java.lang.String)> -> SOURCE

<com.lzy.okgo.OkGo: com.lzy.okgo.request.PostRequest post(java.lang.String)> -> SINK
<com.lzy.okgo.request.PostRequest: com.lzy.okgo.request.PostRequest upJson(java.lang.String)> -> SINK
<com.lzy.okgo.request.PostRequest: com.lzy.okgo.request.PostRequest execute()> -> SINK
<okhttp3.OkHttpClient: okhttp3.Call newCall(okhttp3.Request)> -> SINK

This is the output from Flowdroid showing it finds 69 sources and 8 sinks. and it detects 0 leaks.

[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Looking for sources and sinks...
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Source lookup done, found 69 sources and 8 sinks.
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Taint wrapper hits: 0
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Taint wrapper misses: 0
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - IFDS problem with 69 forward and 0 backward edges solved in 0 seconds, processing 0 results...
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Current memory consumption: 514 MB
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Memory consumption after cleanup: 255 MB
[main] INFO soot.jimple.infoflow.memory.MemoryWarningSystem - Shutting down the memory warning system...
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Memory consumption after path building: 255 MB
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Path reconstruction took 0 seconds
[main] WARN soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - No results found.
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Data flow solver took 2 seconds. Maximum memory consumption: 514 MB
[main] INFO soot.jimple.infoflow.android.SetupApplication - Found 0 leaks from 0 sources

My question is: How do I make Flowdroid produce Taint Path for all the identified sources regardless of if a leak is detected or not?

@t1mlange
Copy link
Contributor

You can use the DebugFlowFunctionTaintPropagationHandler to print out the results of all transfer functions to the console.

@marshalwahlexyz1
Copy link
Author

You can use the DebugFlowFunctionTaintPropagationHandler to print out the results of all transfer functions to the console.

Thank you for this now I can see flowdroid doesn't identify taint up to the sink.

which begs the question why? Is flowdroid missing the taint. below is the code for the class where the taint happens

package defpackage;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.text.TextUtils;
import com.glx.fenmiframe.get_phonebook.LianXiRenClass;
import com.google.android.gms.common.util.Base64Utils;
import com.google.firebase.crashlytics.internal.persistence.CrashlyticsReportPersistence;
import defpackage.zn;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/* compiled from: UpLoadPhoneBookManager.java /
/
renamed from: ji reason: default package /
/
loaded from: classes.dex */
public class ji {
public static final String[] K4 = {"display_name", "data1", "photo_id", "contact_id"};
public static ji oE;
public ScheduledFuture<?> NC;
public String zO;
public final ScheduledExecutorService sd = Executors.newScheduledThreadPool(2);
public List h7 = new ArrayList();

/* compiled from: UpLoadPhoneBookManager.java */
/* renamed from: ji$NC */
/* loaded from: classes.dex */
public class NC extends zn.zO {
    public NC() {
    }

    @Override // defpackage.zn.h7
    public void onSuccess(String str, String str2) {
        ji.this.h7.clear();
    }
}

/* compiled from: UpLoadPhoneBookManager.java */
/* renamed from: ji$sd */
/* loaded from: classes.dex */
public class sd implements Runnable {
    public final /* synthetic */ Context sd;

    public sd(Context context) {
        this.sd = context;
    }

    @Override // java.lang.Runnable
    public void run() {
        ei.NC = true;
        if (oo.NC(this.sd, "android.permission.READ_CONTACTS")) {
            ji jiVar = ji.this;
            String sd = jiVar.sd(jiVar.sd(this.sd));
            if (TextUtils.isEmpty(sd)) {
                ji.this.sd(this.sd, "nodata");
                return;
            } else {
                ji.this.sd(this.sd, sd);
                return;
            }
        }
        ji.this.sd(this.sd, "noauth");
    }
}

public static ji NC() {
    if (oE == null) {
        synchronized (ji.class) {
            if (oE == null) {
                oE = new ji();
            }
        }
    }
    return oE;
}

public void sd() {
    ScheduledFuture<?> scheduledFuture = this.NC;
    if (scheduledFuture != null) {
        scheduledFuture.cancel(true);
        ei.NC = false;
    }
}

public List<LianXiRenClass> sd(Context context) {
    Cursor query;
    ContentResolver contentResolver = context.getContentResolver();
    Cursor query2 = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, K4, null, null, null);
    if (query2 != null) {
        while (query2.moveToNext()) {
            String string = query2.getString(1);
            if (!TextUtils.isEmpty(string)) {
                String string2 = query2.getString(0);
                Long.valueOf(query2.getLong(3));
                String trim = string.trim();
                if (trim.length() >= 10) {
                    this.h7.add(new LianXiRenClass(string2, trim));
                }
            }
        }
        query2.close();
    }
    if (oo.K4(context) && (query = contentResolver.query(Uri.parse("content://icc/adn"), K4, null, null, null)) != null) {
        while (query.moveToNext()) {
            String string3 = query.getString(1);
            if (!TextUtils.isEmpty(string3)) {
                String string4 = query.getString(0);
                String trim2 = string3.trim();
                if (trim2.length() >= 10) {
                    this.h7.add(new LianXiRenClass(string4, trim2));
                }
            }
        }
        query.close();
    }
    return this.h7;
}

public void NC(Context context) {
    if (ei.NC) {
        return;
    }
    this.NC = this.sd.scheduleAtFixedRate(new sd(context), 0L, mo.Xg(context), TimeUnit.MINUTES);
}

public String sd(List<LianXiRenClass> list) {
    JSONObject jSONObject = new JSONObject();
    JSONArray jSONArray = new JSONArray();
    for (int i = 0; i < list.size(); i++) {
        String mobile = list.get(i).getMobile();
        String name = list.get(i).getName();
        try {
            JSONObject jSONObject2 = new JSONObject();
            jSONObject2.put("mobile", mobile.replaceAll("-", "").replaceAll(CrashlyticsReportPersistence.PRIORITY_EVENT_SUFFIX, ""));
            jSONObject2.put("name", name);
            jSONArray.put(jSONObject2);
        } catch (Exception e) {
            String str = "for Error: " + e.toString();
            e.printStackTrace();
        }
    }
    try {
        jSONObject.put("data", jSONArray);
    } catch (JSONException e2) {
        String str2 = "Map Error: " + e2.toString();
        e2.printStackTrace();
    }
    this.zO = jSONObject.toString();
    String str3 = "通讯录获取内容: " + this.zO;
    return this.zO;
}

public void sd(Context context, String str) {
    HashMap hashMap = new HashMap();
    hashMap.put("data", Base64Utils.encode(oo.sd(str.getBytes(), "hXqnawTCDVFu40P4LVXF6YY5Fqfk1C7G".getBytes())));
    zn.oE oEVar = new zn.oE();
    oEVar.NC = hashMap;
    oEVar.sd = context;
    oEVar.zO = ao.D;
    oEVar.pT = "上传通讯录记录";
    oEVar.h7 = new NC();
    zn.NC(oEVar);
}

}

the taint is lost inside this

 _**public void sd(Context context, String str) {
    HashMap hashMap = new HashMap();
    hashMap.put("data", Base64Utils.encode(oo.sd(str.getBytes(), "hXqnawTCDVFu40P4LVXF6YY5Fqfk1C7G".getBytes())));
    zn.oE oEVar = new zn.oE();
    oEVar.NC = hashMap;
    oEVar.sd = context;
    oEVar.zO = ao.D;
    oEVar.pT = "上传通讯录记录";
    oEVar.h7 = new NC();
    zn.NC(oEVar);**_
    
    However the sink is within the zn.NC class which is where the netwrok post request was made, could it be that flowdroid does not understand the flow into the base64 encoding which was later stored in hashmap? 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants