Root-Erkennung: Welche Erkennungsmethoden die App Doctolib anwendet

Im Jahr 2021 hat das Unternehmen Doctolib einen BigBrotherAward »gewonnen«:

Der BigBrotherAward in der Kategorie „Gesundheit“ geht andie Firma Doctolib in Berlin für ihr Terminvermittlungsportal für Ärzte.

Gerade bei solchen Unternehmen ist es interessant, mal den App-Datenverkehr zu analysieren. Bei der Android-Version 3.4.23 wollte ich das tun. Unmittelbar nach dem Start erscheint allerdings der Hinweis:

Die Verwendung eines modifizierten Geräts (Jailbreak/Root) ist derzeit nicht gestattet, um die Sicherheit Ihrer persönlichen Daten auf Doctolib zu gewährleisten.

Die App hat also Mechanismen zur Root-Erkennung implementiert. Ich habe das mal genauer analysiert. Insgesamt muss man einige Erkennungsmethoden aushebeln, damit man den Datenverkehr auf einem gerooteten Gerät analysieren kann. Anbei mal ein paar Aufrufe, die die App durchführt, um zu erkennen, ob das Gerät gerootet ist:

message: {'type': 'send', 'payload': 'Bypass return value for binary: Superuser.apk'} data: None
message: {'type': 'send', 'payload': 'Bypass return value for binary: su'} data: None
message: {'type': 'send', 'payload': 'Bypass /system/xbin/which,su command'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.noshufou.android.su'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.noshufou.android.su.elite'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: eu.chainfire.supersu'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.koushikdutta.superuser'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.thirdparty.superuser'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.yellowes.su'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.topjohnwu.magisk'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.koushikdutta.rommanager'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.koushikdutta.rommanager.license'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.dimonvideo.luckypatcher'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.chelpus.lackypatch'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.ramdroid.appquarantine'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.ramdroid.appquarantinepro'} data: None
message: {'type': 'send', 'payload': 'Bypass root check for package: com.android.vending.billing.InAppBillingService.COIN'} data: None
message: {'type': 'send', 'payload': 'Bypass test-keys check'} data: None
message: {'type': 'send', 'payload': 'Bypass which,su command'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /data/local/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /data/local/bin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /data/local/xbin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /sbin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /su/bin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /system/bin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /system/bin/.ext/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /system/bin/failsafe/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /system/sd/xbin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /system/usr/we-need-root/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /system/xbin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /cache/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /data/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /dev/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /system/sbin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /product/bin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /apex/com.android.runtime/bin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /odm/bin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /vendor/bin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass native fopen >> /vendor/xbin/su'} data: None
message: {'type': 'send', 'payload': 'Bypass return value for binary: magisk'} data: None

Die App versucht unter anderem:

  • App-Packages zu erkennen, die Root ermöglichen bzw. verschleiern (eu.chainfire.supersu, com.devadvance.rootcloak etc.)
  • Befehle (Root-Binaries) auszuführen, die eine Ausführung im Root- bzw. Admin-Kontext ermöglichen (/sbin/su)
  • Magisk zu erkennen

Mit einem angepassten Frida-Hook lassen sich allerdings alle Anfragen mit False bzw. nicht vorhanden beantworten. Somit kann die Root-Erkennung ausgehebelt werden und der App-Datenverkehr analysiert werden. Game over.

Tippt man beim Consent-Banner bspw. auf Ablehnen wird Folgendes übermittelt [api.privacy-center.org]:

POST /v1/events HTTP/1.1
Host: api.privacy-center.org
Content-Length: 1116
User-Agent: react-native/android/3.4.23/10/MSM8953/Mi A1
X-Requested-With: XMLHttpRequest
Content-Type: application/json
Accept: */*
Origin: https://www.doctolib.de
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://www.doctolib.de/
Accept-Encoding: gzip, deflate
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

{
   "source":{
      "type":"sdk-web",
      "domain":"www.doctolib.de",
      "key":"df2b9f8c-760a-445f-810e-4b47a4fe41a7",
      "deployment_id":"4GbFn3Lx",
      "beacon":false
   },
   "user":{
      "agent":"react-native/android/3.4.23/10/MSM8953/Mi A1",
      "id_type":"uuid",
      "country":"DE",
      "regs":[
         "gdpr"
      ],
      "id":"18484cfe-cacb-6fba-a0e0-39fbb5b1625c",
      "token":{
         "user_id":"18484cfe-cacb-6fba-a0e0-39fbb5b1625c",
         "created":"2022-11-17T17:58:50.566Z",
         "updated":"2022-11-17T18:00:27.474Z",
         "vendors":{
            "enabled":[
            ],
            "disabled":[
               "c:cloudflare-mYYFMYNT",
               "c:didomi-fYPBYxWa","c:doctolibn-Wp87CpXA",
               "c:doctoliba-tgtb3W8P"
            ]
         },
         "purposes":{
            "enabled":[
            ],
            "disabled":[
               "analytik-N2ZH9BqQ"
            ]
[...]

Weiter habe ich noch nicht geprüft – werde ich vielleicht irgendwann nachholen. Mir ging es erstmal darum, exemplarisch aufzuzeigen, welche Anstrengungen Entwickler gehen, um gerootete Geräte zu erkennen. Hint: Bisher gab es keine App, bei der sich die Root-Erkennung nicht aushebeln ließ. ;-)

Unterstütze den Blog mit einem Dauerauftrag! Mitmachen ➡