secure-software-engineering / FlowDroid

FlowDroid Static Data Flow Tracker
GNU Lesser General Public License v2.1
1.07k stars 300 forks source link

Having problems defining flows for cryptographic functions #626

Open Olasergiolas opened 1 year ago

Olasergiolas commented 1 year ago

Hello everyone, I am trying to learn FlowDroid in order to create a crypto analysis tool for Android applications. Unfortunately, some issues arose while trying to create some proof of concepts. Below is the fragment of Jimple code of interest. This is a simple encryption of bytes using hard-coded parameters with SecretKeySpec:

        $r12 = new javax.crypto.spec.SecretKeySpec;

        r5 = $r12;

        r6 = "0123456789012345";

        r7 = <java.nio.charset.StandardCharsets: java.nio.charset.Charset UTF_8>;

     label01:
        $r8 = virtualinvoke r6.<java.lang.String: byte[] getBytes(java.nio.charset.Charset)>(r7);

        specialinvoke $r12.<javax.crypto.spec.SecretKeySpec: void <init>(byte[],java.lang.String)>($r8, "AES");

        $r9 = staticinvoke <javax.crypto.Cipher: javax.crypto.Cipher getInstance(java.lang.String)>("AES/ECB/PKCS5PADDING");

        virtualinvoke $r9.<javax.crypto.Cipher: void init(int,java.security.Key)>(1, r5);

     label02:
        r6 = "HELLOWORLD";

     label03:
        $r8 = virtualinvoke r6.<java.lang.String: byte[] getBytes()>();

        $r8 = virtualinvoke $r9.<javax.crypto.Cipher: byte[] doFinal(byte[])>($r8);

I would like to identify a flow between the initial creation of a SecretKeySpec (let's say, the first argument for the class constructor) and the Cipher instance used for encryption and decryption. The idea is being able to detect the usage of a hard-coded key to initialize a Cipher instance. For this, I created the following SourcesAndSinks XML file:

<sinkSources>
    <category id="NO_CATEGORY"> 
        <method signature="&lt;javax.crypto.spec.SecretKeySpec: void &lt;init&gt;(byte[],java.lang.String)&gt;">
            <param index="0" type="byte[]">
                <accessPath isSource="true" isSink="false" />
            </param>
        </method>

        <method signature="&lt;javax.crypto.Cipher: void init(int,java.security.Key)&gt;">
            <param index="1" type="java.security.Key">
                <accessPath isSource="false" isSink="true" />
            </param>
        </method>
    </category>
</sinkSources>

When starting the analysis via CLI, sources and sinks are properly identified in the code but no flow is detected. Am I missing something? Furthermore, I would appreciate if you could provide any learning resources for this tool since I am having a hard-time trying to grasp the inner workings of FlowDroid just by looking at source-code.

t1mlange commented 1 year ago

I believe your source sink file doesn't match what you want to do. In your example, $r8 is derived from the zero fact at SecretKeySpec: void <init>(...). There is no flow from $r8 at that line to Cipher: void init(...). (Technically, $r8 passes by the sink but is not used, so there is also no leak).

In this case, you should try to mark the base object of SecretKeySpec: void <init>(...) as a source to find a flow. Though, I'm not sure whether this specification helps you to find hardcoded keys. By the way, there is also the CryptoGuard: High Precision Detection of Cryptographic Vulnerabilities in Massive-sized Java Projects paper that tries to do the same and discusses refinements to rule out false positive flows in section 5. You might be able to use some of their insights for your project.

Olasergiolas commented 1 year ago

After updating my SourcesAndSinks file with your suggestions the flow is now being identified! Although I need to specify the key attribute for the class, it does not seem to work otherwise:

<sinkSources>
    <category id="NO_CATEGORY"> 
        <method signature="&lt;javax.crypto.spec.SecretKeySpec: void &lt;init&gt;(byte[],java.lang.String)&gt;">
            <base type="javax.crypto.spec.SecretKeySpec">
                <accessPath isSource="true" isSink="false">
                    <pathElement type="byte[]" field="key" />
                </accessPath>
            </base>
        </method>

        <method signature="&lt;javax.crypto.Cipher: void init(int,java.security.Key)&gt;">
            <param index="1" type="java.security.Key">
                <accessPath isSource="false" isSink="true" />
            </param>
        </method>
    </category>
</sinkSources>

imagen

Having reached this far, I have one question remaining regarding the suitability of FlowDroid for my interests. Wouldn't it be possible to identify a flow between a string statement (r6 = "...") and a method?

Lastly, thanks for leading me to CryptoGuard, I'll check it out.

t1mlange commented 1 year ago

Of course, that should be possible.

Olasergiolas commented 1 year ago

Of course, that should be possible.

How would I go around doing that? I did not find any examples while looking at the SourcesAndSinks files included in the repo.

Thank you for your help!

t1mlange commented 1 year ago

How would I go around doing that? I did not find any examples while looking at the SourcesAndSinks files included in the repo.

That cannot be expressed via configuration files. You'll have to write your own logic for that, probably by supplying your own SourceSinkManager.