Open marshalwahlexyz1 opened 5 days ago
Can you please provide your precise source/sink definitions? How did you specify your sources and sinks? Did you create your own SourceSinkManager
, did you use the XML format for defining sources and sinks or did you use the old plaintext definition file?
Which Taint Wrapper did you specify?
Do you run FlowDroid using the API or using the command-line tool? If you use the API, please provide your code. If you used the command-line application, please provide the parameters.
If FlowDroid is able to find the individual paths, it should also find the combined path. I suspect that something is wrong with your taint wapper configuration and hence, getString()
does not correctly propagate the taint.
**Thank you, I am new to Flowdroid so I have been encountering some issues. I ran flowdroid from command line and I also have cloned the repo and I use it on intellij.
I am using the old plaintext definition and this is my sources and sinks file content**
<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
<com.glx.fenmiframe.get_phonebook.LianXiRenClass: void
This is currently my source and sink file, please note my actual sink is the zn.NC(oEVar);** last line of the app code I provided previously.
However, Flowdroid could not identify that as a sink and I have tracked this manually and dynamically using Frida there is a leak as contact data was sent over the network.
So, I am trying to track where the taint actually gets lost, and I found out that from the source I can't even track the taint directly for query method to the list.
I use this command line parameters**
java -Xmx16384m -jar "C:\Users\walea\Desktop\FlowDroid-develop\soot-infoflow-cmd\target\soot-infoflow-cmd-jar-with-dependencies.jar" -a "C:\Apktool\apk_files\easycash.apk" -p "C:\Users\walea\AppData\Local\Android\Sdk\platforms" -s "C:\Users\walea\Desktop\FlowDroid-develop\soot-infoflow-android\ctest.txt" -t "C:\Users\walea\Desktop\FlowDroid-develop\soot-infoflow\ccw.txt" -tw EASY --cgalgo SPARK -o easycash.xml -pr fast -ls -aliasflowins
I am also using the plaintext format for my taint wrapper (ccw.txt) content below as I could not find a sample of format
<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)>
<java.lang.String: java.lang.String trim()>
<com.glx.fenmiframe.get_phonebook.LianXiRenClass: void
This is the result I get
_[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - The sink specialinvoke $r9.<com.glx.fenmiframe.get_phonebook.LianXiRenClass: void
_[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - The sink specialinvoke $r9.<com.glx.fenmiframe.get_phonebook.LianXiRenClass: void
Now I figured when I exclude <android.database.Cursor: java.lang.String getString(int)> -> SOURCE from the sources and sinks file and use my taintwrapper (ccw.txt) flowdroid identifies a leak where the source is from the <android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)>($r3, $r4, null, null, null) in method <ji: java.util.List sd(android.content.Context)> method
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - The sink specialinvoke $r9.<com.glx.fenmiframe.get_phonebook.LianXiRenClass: void
Another observation is that when I use the content below in my taintwrapper file no class get loaded. So I am not sure if the first taintwrapper is the correct format because it actually does not specify any taint propagation rule for me to assume flowdroid uses that to understand the flow. However, the results I get say otherwise.
Taint Wrapper for EasyTaintWrapper
Sources <android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)> -> SOURCE Taint Propagation Methods
Cursor.getString(int): Propagate taint from 'this' to return value <android.database.Cursor: java.lang.String getString(int)>: [this] -> return
String.trim(): Propagate taint from 'this' to return value
<java.lang.String: java.lang.String trim()>: [this] -> return
LianXiRenClass constructor: Propagate taint from parameters to 'this'
<com.glx.fenmiframe.get_phonebook.LianXiRenClass: void
LianXiRenClass.getName() and getMobile(): Propagate taint from 'this' to return value <com.glx.fenmiframe.get_phonebook.LianXiRenClass: java.lang.String getName()>: [this] -> return <com.glx.fenmiframe.get_phonebook.LianXiRenClass: java.lang.String getMobile()>: [this] -> return
List methods: Propagate taint through lists <java.util.List: boolean add(java.lang.Object)>: [params] -> this <java.util.List: java.lang.Object get(int)>: [this] -> return
Map methods: Propagate taint through maps <java.util.Map: java.lang.Object put(java.lang.Object,java.lang.Object)>: [params] -> this <java.util.Map: java.lang.Object get(java.lang.Object)>: [this] -> return
JSONObject methods: Propagate taint through JSON objects
<org.json.JSONObject: org.json.JSONObject put(java.lang.String,java.lang.Object)>: [params] -> this
<org.json.JSONObject: java.lang.String toString()>: [this] -> return
<org.json.JSONObject: void
Base64Utils.encode(byte[]): Propagate taint from parameter to return value <com.google.android.gms.common.util.Base64Utils: java.lang.String encode(byte[])>: [params] -> return
oo.sd(byte[], byte[]): Propagate taint from parameters to return value <oo: byte[] sd(byte[],byte[])>: [params] -> return
StringBuilder methods: Propagate taint through string building <java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>: [params] -> this
OkGo methods: Propagate taint into the network call <com.lzy.okgo.request.PostRequest: com.lzy.okgo.request.base.BodyRequest upJson(org.json.JSONObject)>: [params] -> this <com.lzy.okgo.request.base.BodyRequest: void execute(com.lzy.okgo.callback.Callback)> -> SINK
Sinks <com.lzy.okgo.request.base.BodyRequest: void execute(com.lzy.okgo.callback.Callback)> -> SINK
QUESTIONS: By just running flowdroid with this source and sink.
The result I am expecting from flowdroid is the flow from the source
<android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)> -> SOURCE
to the sink
<com.lzy.okgo.request.base.BodyRequest: void execute(com.lzy.okgo.callback.Callback)> -> SINK
however i am getting no leaks, which prompted me to believe flowdroid lot the taint and tracking was never done till the sink i specified.
So basically while trying to observe where taint is lost, these are the pair of sources and sinks I use in to track if taint flows from the source to the methods that handles the taint from the first sink. Note: I am using the taintwrapper, i only change the sink for each run
<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)> -> SINK
the following result was gotten
The sink $r7 = interfaceinvoke $r5.<android.database.Cursor: java.lang.String getString(int)>(0) in method <ji: java.util.List sd(android.content.Context)> was called with values from the following sources: [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - - $r5 = virtualinvoke $r2.<android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)>($r3, $r4, null, null, null) in method <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - on Path: [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> $r5 = virtualinvoke $r2.<android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)>($r3, $r4, null, null, null) [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> $r7 = interfaceinvoke $r5.<android.database.Cursor: java.lang.String getString(int)>(0)
<android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)> -> SOURCE
<com.glx.fenmiframe.get_phonebook.LianXiRenClass: void
the result gotten was
_[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - The sink specialinvoke $r9.<com.glx.fenmiframe.get_phonebook.LianXiRenClass: void (java.lang.String,java.lang.String)>($r7, $r6) in method <ji: java.util.List sd(android.content.Context)> was called with values from the following sources: [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - - $r5 = virtualinvoke $r2.<android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)>($r3, $r4, null, null, null) in method <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - on Path: [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> $r5 = virtualinvoke $r2.<android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)>($r3, $r4, null, null, null) [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> $r7 = interfaceinvoke $r5.<android.database.Cursor: java.lang.String getString(int)>(0) [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> specialinvoke $r9.<com.glx.fenmiframe.get_phonebook.LianXiRenClass: void (java.lang.String,java.lang.String)>($r7, $r6) [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - - $r5 = virtualinvoke $r2.<android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)>($r3, $r4, null, null, null) in method <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - on Path: [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> $r5 = virtualinvoke $r2.<android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)>($r3, $r4, null, null, null) [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> $r7 = interfaceinvoke $r5.<android.database.Cursor: java.lang.String getString(int)>(0) [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> <ji: java.util.List sd(android.content.Context)> [main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - -> specialinvoke $r9.<com.glx.fenmiframe.getphonebook.LianXiRenClass: void (java.lang.String,java.lang.String)>($r7, $r6)
<android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)> -> SOURCE
<java.util.List: boolean add(java.lang.Object)> -> SINK
At this point I was not getting any leak related to the ji path
So Flowdroid could not track if the list is tainted.
However when I use
<com.glx.fenmiframe.get_phonebook.LianXiRenClass: java.lang.String getMobile()> -> SOURCE <com.glx.fenmiframe.get_phonebook.LianXiRenClass: java.lang.String getName()> -> SOURCE
which is after the taint was added to the list as source and this as sink
<org.json.JSONObject: org.json.JSONObject put(java.lang.String,java.lang.Object)> -> SINK
Flowdroid could detect leak.
So it seems flowdroid lost the taint when <java.util.List: boolean add(java.lang.Object)> was invoked as this is the point where it could no longer detect leak.
The purpose of the taint wrapper is to provide an abstraction over API methods. You don't need to put any methods from your target app into the definition file.
Aside from that, the EasyTaintWrapper
is highly outdated. We have long switched to StubDroid, i.e., SummaryTaintWrapper
. If you use the FlowDroid command-line application and don't explicitly specify a taint wrapper, this is the default option. So I wonder why you ended up with the EasyTaitWrapper
in the first place.
In the default configuration, the SummaryTaintWrapper
reads in the summary files (one XML file per class) that are provided with FlowDroid. That already contains summaries fot Cursor
, List
and so on, so it should work. Keep in mind that you don't need summaries for app classes.
What is your command line?
I built a small test application with the same ContentResolver
on which FlowDroid finds the leak just fine. I hope that we see things clearer if you fix your command line to use the default taint wrapper. Otherwise, I need the original APK file to have a deeper look.
Thank you for your help so far, I uploaded the app to a drive https://drive.google.com/drive/folders/1Ye6LhU9WFrTgO3XVsCj3TiQi91R2G8Ii?usp=sharing
The app retrieves contact data, sms and images hence the need to track this data from source to when they are sent over the network.
The classes of interest where this data retrievals happen is the defpackage,ji defpackage.ki and defpackage.mi, at the later end of each class the code structure is similar and the 3 classes all make a call to the zn.NC class for network connection using okGO post method.
I ended up specifying taint wrapper because the default configuration where I just specified only the source and sink of interest detects 0 leak and i am sure there is a leak.
So I have ran flowdroid with these commands
With just source and sink file _java -Xmx16384m -jar "C:\Users\walea\Desktop\FlowDroid-develop\soot-infoflow-cmd\target\soot-infoflow-cmd-jar-with-dependencies.jar" -a "C:\Apktool\apkfiles\easycash.apk" -p "C:\Users\walea\AppData\Local\Android\Sdk\platforms" -s "C:\Users\walea\Desktop\FlowDroid-develop\soot-infoflow-android\ctest.txt" -o easycash.xml -pr fast -ls
With parameters pointing to the summariesmanual _java -Xmx16384m -jar "C:\Users\walea\Desktop\FlowDroid-develop\soot-infoflow-cmd\target\soot-infoflow-cmd-jar-with-dependencies.jar" -a "C:\Apktool\apkfiles\easycash.apk" -p "C:\Users\walea\AppData\Local\Android\Sdk\platforms" -s "C:\Users\walea\Desktop\FlowDroid-develop\soot-infoflow-android\ctest.txt" -t "C:\Users\walea\Desktop\FlowDroid-develop\soot-infoflow-summaries\summariesManual" -tw STUBDROID -o easycash.xml -pr fast -ls
This is the content of my ctest.txt (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
<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
I have tried all default configuration flowdroid still detects 0 leak.
By manual observation of the app this <android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)> -> SOURCE
<com.lzy.okgo.OkGo: com.lzy.okgo.request.PostRequest post(java.lang.String)> -> SINK
should be the source and sink for the contact data that was retrieved. Hence, I expected flowdroid to detect this leak.
The APK seems to contain malwaree. Google Drive gives me "Only the owner is allowed to download infected files.". Maybe you can upload an encrypted ZIP file and put the password here.
Hi @StevenArzt
https://drive.google.com/file/d/1cmKXOWhYkklh1IRy_UVMchdLCCmerP5b/view?usp=sharing
pwd: testapp
HI @StevenArzt
Just a quick question, hope you now have access to the app?
Thank you
Hi, @t1mlange @StevenArzt
FlowDroid is not tracking the complete taint flow from ContentResolver.query() through Cursor.getString() and List operations to LianXiRenClass methods. While it can track individual segments of this flow, it fails to connect them into a single end-to-end taint propagation.
These are the step i took
Set up FlowDroid with the following source and sink: Source: <android.content.ContentResolver: android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)> Sink: <com.glx.fenmiframe.get_phonebook.LianXiRenClass: void(java.lang.String,java.lang.String)>
This is the code structure: _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();
}_
Observed Behavior
Expected Behavior
FlowDroid should track the complete taint flow from ContentResolver.query() through Cursor.getString() and List operations to LianXiRenClass methods.
Is this a known limitation of FlowDroid? If so, are there any workarounds ?