OWASP / owasp-mastg

The Mobile Application Security Testing Guide (MASTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes the technical processes for verifying the controls listed in the OWASP Mobile Application Security Verification Standard (MASVS).
https://mas.owasp.org/
Creative Commons Attribution Share Alike 4.0 International
11.75k stars 2.33k forks source link

/proc/self/maps for Frida library detection #1130

Closed sushi2k closed 5 years ago

sushi2k commented 5 years ago

Describe the issue it seems /proc/self/maps should be removed as a method for frida detection (at least when running as gadget). As an app will not have access to this information (pid would need to be known to access /proc//self/maps where the frida-gadget can be found. Need to be investigated further if detection in memory can be executed on recent android versions and if frida can be found.

Optional: Testcase or section https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05j-Testing-Resiliency-Against-Reverse-Engineering.md#testing-the-detection-of-reverse-engineering-tools

commjoen commented 5 years ago

I think we can do:

TheDauntless commented 5 years ago

As an app will not have access to this information (pid would need to be known to access /proc//self/maps where the frida-gadget can be found.

Has there been a change to the behaviour of Android in this regard? I can't immediately see something in https://developer.android.com/about/versions/pie/android-9.0-changes-all or https://developer.android.com/about/versions/oreo/android-8.0-changes and it works on Android 7.

Could you clarify the problem a little bit more?

sushi2k commented 5 years ago

Thanks @TheDauntless. If the app is running as gadget you can access /proc/process_id/maps.

    public boolean checkMemory() {

        boolean returnValue = false;

        // get Process ID of the running app
        int pid = android.os.Process.myPid();

        // check if Frida is running as gadget as part of the app
        try {
            Process process = Runtime.getRuntime().exec("cat /proc/" +pid+ "/self/maps");
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            int read;
            char[] buffer = new char[4096];
            StringBuffer output = new StringBuffer();
            while ((read = reader.read(buffer)) > 0) {
                output.append(buffer, 0, read);
            }
            reader.close();

            // Waits for the command to finish.
            process.waitFor();
            Log.d("fridamemory PID: ", Integer.toString(pid));
            Log.d("fridamemory: ", output.toString());

            if(output.toString().contains("frida-gadget")) {
                Log.d("fridaserver","Frida Server process found!" );
                returnValue = true;
            }

        } catch (IOException e) {

        } catch (InterruptedException e) {

        }

        return returnValue;

    }

Can of course be bypassed by renaming the frida gadget. This works on Android 7, just tested it.

If you want to go through /proc/self/maps it should work according to the POC from Bernhard: https://github.com/b-mueller/frida-detection-demo/. But I cannot run the app properly, https://github.com/b-mueller/frida-detection-demo/issues/5

sushi2k commented 5 years ago

I think we can do:

  • integrity protection verification (can help, as gadget needs to be in app)
  • check if our app starts consuming more memory / less memory (kinda useless),
  • We can create sockets to see if there are ports open in our own app ^^ and we assume there should not be... Maybe try that path? (could help)
  • What we should investigate is what we can do with the NDK in terms of checking for certain symbols to be there (long run, could help)

Thanks Jeroen. I have now a very basic portscan within a demo app

   public boolean PortScanFrida(String ipAddress) throws UnknownHostException {

        final boolean[] fridaDetected = {false};

        // Perform synchronous port scan
        Log.d("portscan","PortScan started");
        Log.d("portscan","PortScanning IP: " + ipAddress);

        final long startTimeMillis = System.currentTimeMillis();

        ArrayList<Integer> openPorts = PortScan.onAddress(ipAddress).setPort(27042).setMethodTCP().doScan();

        Log.d("portscan","Open Ports: " + openPorts.size());
        Log.d("portscan","Time taken: " + ((System.currentTimeMillis() - startTimeMillis)/1000.0f));

        if (openPorts.size() > 0) {
            fridaDetected[0] = true;
        } else {
            fridaDetected[0] = false;
        }

        return fridaDetected[0];

Can of course be easily bypassed by changing the port.

Bernhard has implemented the port scan more sophisticated with the NDK https://github.com/b-mueller/frida-detection-demo/

TheDauntless commented 5 years ago

@sushi2k you wrote:

it seems /proc/self/maps should be removed as a method for frida detection (at least when running as gadget).

Thanks @TheDauntless. If the app is running as gadget you can access /proc/process_id/maps.

Either I'm still not getting it, or you agree that it shouldn't be removed and it's still a possible way to detect it?

Or is there actually a difference between self and PID ?

sushi2k commented 5 years ago

An App doesn't have access anymore to /proc/self/maps since Android 7.0, but it can still access /proc//maps. That was the result when testing on a non rooted phone

cpholguera commented 5 years ago

@sushi2k @TheDauntless do we have a conclusion here?

cpholguera commented 5 years ago

As Sven also demonstrated, an app can obtain its own PID by using android.os.Process.myPid(), then use this to read 'cat /proc/' + pid + '/maps' via Runtime.getRuntime().exec. That way it's possible to detect Frida (as discussed, not very effective actually).

Tested on Android 8 rooted phone using Frida (to detect Frida, yes :P) and via CLI (Termux). Also tested simply via CLI (Termux) on Android 9 non-rooted (the app can retrieve its own PID and use it to read maps).

rubaljain commented 1 year ago

When I read the content of proc/pid/maps, it shows the path to frida agent which is possibly being used to check frida. Is there any way to bypass this? I have tried creating the fake maps file with below lines removed and pointing the function to fake file but it didn't work.

Screenshot 2023-02-07 at 4 47 56 PM

Alhyoss commented 1 year ago

Just for future reference:

It seems that on Android 12 it is possible for an application to read /proc/self/maps on a non-rooted device, unlike on Android 7.

I don't know however since when it is again possible.