1

My question is how to detect ADB connection or unusual user activity in shell on the Android devices.

I have an Android 5.1.1 device with Wi-Fi hotspot enabled and also with opened ADB port, similar to the setup in this challenge, except that I do not have a web server running on it, just ADB is directly open.

Now I want to detect a "hacker" that cracked the WiFi hotspot password and connected via port 5555 into shell, su and run various shell commands.

However, I noticed that the output of logcat, even verbose for all buffers (like 'adb logcat *:V') does not allow me to detect anything like that. Neither ADB connection, nor su, nor any shell commands are not visible in the logcat output.

How can I generate / collect logs that will contain the "hacker's activities"? So far I found auditd for Android, however it is not so easy to install, especially for on older Android versions. Do you know any alternative or any other easier way to monitor shell activities / ADB connection?

3
  • The adb port should require public key authentication, therefore an hacker can not just connect to it. Android is not designed to record adb activity logcat is designed to get app logs for debugging and identifying other problems. Adb should never be used outside of a secure environment.
    – Robert
    Jul 8 at 20:43
  • yes, I fully understand how to secure it. The question is about how to monitor and detect it. Jul 9 at 12:15
  • Create a honeypot to catch hacker. ADB isn't designed for your setup.
    – defalt
    Jul 11 at 13:50
1
+150

In Android 5.1.1, all processes can be listed (ps command) through an App (tested on AVD).

An Android application can be used as a background services that log (in logcat) all the processes that have a PPID the same as adbd process (recursively):

    fun adbMonitor(){
        try {

            
            val process = Runtime.getRuntime().exec("/system/bin/ps")
            val reader = BufferedReader(
                InputStreamReader(process.inputStream)
            )
            var read: Int
            val buffer = CharArray(4096)
            val output = StringBuffer()
            while (reader.read(buffer).also { read = it } > 0) {
                output.append(buffer, 0, read)
            }

            reader.close()
            process.waitFor()

            val processes = output.toString().split("\n")
            var pidADBD = ""
            for (proc in processes){
                val splitted = proc.split("\\s+".toRegex())
                if (proc.isEmpty())
                    break
                if (splitted[splitted.size -1] == "/sbin/adbd" ) {
                    pidADBD = splitted[1]
                }
            }

            for (child in childProcesses(output.toString(), pidADBD)) {
                Log.d("ADBD", Calendar.getInstance().getTime().toString() + " " + child)
            }
            
        } catch (e: IOException) {
            throw RuntimeException(e)
        } catch (e: InterruptedException) {
            throw RuntimeException(e)
        }
    }
    
    fun childProcesses(processList : String, pid : String) : List<String> {
        var  adbdChilds: List<String> = listOf<String>()
        val processes = processList.split("\n")

        for (proc in processes){
            val splitted = proc.split("\\s+".toRegex())

            if (proc.isEmpty())
                continue

            if (splitted[2] == pid ) {
                Log.d("ADBD", splitted[splitted.size -1])
                adbdChilds += childProcesses(processList, splitted[1])
            }
        }
        return adbdChilds
    }

Starting the service:

 private fun startService() {
        if (isServiceStarted) return
        isServiceStarted = true
        setServiceState(this, ServiceState.STARTED)

        // we need this lock so our service gets not affected by Doze Mode
        wakeLock =
            (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
                newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Monitoring::lock").apply {
                    acquire()
                }
            }

        // we're starting a loop in a coroutine
        GlobalScope.launch(Dispatchers.IO) {
            while (isServiceStarted) {
                launch(Dispatchers.IO) {
                    adbMonitor()
                }
                delay(100)
            }
             Log.d("Monitoring","End of the loop for the service")
        }
    }

And in the MainActivity:

        Intent(this, Monitoring::class.java).also {
            it.action = Actions.START.toString()
                startService(it)
        }

Example of logs in logcat:

07-15 23:05:48.653 13697-13778/com.example.audit D/ADBD: logcat
07-15 23:05:48.750 13697-13844/com.example.audit D/ADBD: /system/bin/sh
07-15 23:05:48.823 13697-13851/com.example.audit D/ADBD: /system/bin/sh
07-15 23:05:49.268 13697-13770/com.example.audit D/ADBD: dumpsys
07-15 23:05:49.293 13697-13770/com.example.audit D/ADBD: /system/bin/sh
07-15 23:05:49.301 13697-13770/com.example.audit D/ADBD: /data/local/tmp/perfd/transport
07-15 23:05:49.317 13697-13770/com.example.audit D/ADBD: /system/bin/sh
07-15 23:05:49.326 13697-13770/com.example.audit D/ADBD: top
07-15 23:05:49.769 13697-13723/com.example.audit D/ADBD: /data/local/tmp/perfd/transport
07-15 23:05:50.058 13697-13724/com.example.audit D/ADBD: /system/bin/sh
07-15 23:05:50.526 13697-13794/com.example.audit D/ADBD: /system/bin/sh

Note that not all the commands will be logged as there is a delay of 100 milliseconds.

If needed, all the source code of the app can be provided.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.