lunedì 3 giugno 2024

Published giugno 03, 2024 by Django Faiola with 0 comment

iOS Uber - Request a ride

Indice dei contenuti

Premessa

Alcuni giorni fa ho pubblicato l'articolo di Google Translate con il relativo plugin googleTranslate.py per iLEAPP e a causa di un mio errore nella pull request” su GitHub è stato inserito anche il plugin uberClient.py ancora in fase di sviluppo. Non è che non funzioni, ma aspettavo solo l'occasione per richiedere un servizio Uber a Roma, completare l'analisi con le corse, autisti, pagamenti, etc. e revisionare il codice. Anche se la ricerca è incompleta ho deciso di pubblicare quanto scritto strada facendo come "Prima Parte" dell'analisi di Uber. 😅

Introduzione

iOS Uber - Request a ride (Richiedi una corsa) è un’applicazione mobile gratuita sviluppata da Uber Technologies, Inc. che collega una rete di autisti privati partner del servizio con utenti che hanno bisogno di raggiungere una destinazione; una sorta di servizio "taxi" a costi più popolari.

Grazie all'app l'utente può richiedere una corsa, visualizzare su mappa la posizione delle auto Uber nelle sue vicinanze e avere già un preventivo per la corsa specificata. Pagare la corsa è facile: tramite le carte di credito o di debito, utilizzando il conto PayPal, attraverso Apple Pay, scalando i crediti in Uber Cash, etc. A fine corsa l'utente deve solo scendere dall'auto ed entro pochi minuti, a transazione confermata, riceverà via Email la ricevuta di pagamento in PDF.

Per maggiori dettagli sulle funzionalità dell'app consulta https://www.uber.com.

App Store: https://apps.apple.com/us/app/uber-request-a-ride/id368677368

Per lo studio di questa app sono state utilizzate le immagine pubbliche del CTF 2021 - Marsha's iPhone della Cellebrite con Uber versione 3.467.10003 e del BelkaCTF #6: Bogus Bill della Belkasoft con Uber versione 3.608.10000. Inoltre ho integrato qualcosa dai dati estrapolati dal mio iPhone SE 2020 (test) con la versione 3.610.10000.

Percorsi

Di seguito lo stralcio di Uber del poster della SANS “iOS Third-Party Apps Forensics Reference Guide Poster” con le informazioni più rilevanti per l’analisi dell’app.

Account

Per la ricostruzione dell'account è sufficiente come da poster il file client presente nell'Internal App Path "/Library/Application Support/PersistentStorage/BootstrapStore/RealtimeRider.StreamModelKey/". Per ottenere un profilo più dettagliato bisogna tenere in considerazione anche i file clientStatuscity e targetLocationSynced presenti nello stesso percorso.

Tutti questi file sono formattati nel formato JSON. Per la visualizzazione ho usato dfDataViewer, il mio visualizzatore di (B)Plist, JSON(B), (B)XML, ASN.1, etc. ancora in fase di sviluppo e non pubblico.

client

  • firstNameMarsha (nome);
  • lastName: Mellos (cognome);
  • mobileDigits: 9735203xxx (numero di telefono);
  • email: marshaxxx@gmail.com (indirizzo email);
  • pictureUrl: https://d1w2poirtb3as9.cloudfront.net/default.jpeg?Expires=1626597195&Signature=X0UvfW3vKezh8ut8v1GJ9lxxx (URL dell'immagine del profilo utente);
  • referralCode: j2ez6lxxx (codice di invito condivisibile con altri per ottenere corse gratuite e/o ricompense);
  • referralUrlhttps://www.uber.com/invite/j2ez6lxxx (URL per l'invito);
  • profileTypeuber (tipo di profilo);
  • mobileCountryIso2US (codice paese);
  • lastSelectedPaymentProfileUUIDeafd0a35-9c67-5589-xxx (identificatore dell'ultimo metodo di pagamento selezionato);
  • uuid: 4028f734-7f4b-45b0-xxx (identificatore univoco).

clientStatus

  • lastModifiedTimeMs1626554060317 (data e ora dell'ultimo utilizzo nel formato Unix Epoch ms: 17 luglio 2021 20:34:20);
  • statusDescription: Looking (descrizione dello stato).

city

  • cityId10 (identificatore della città);
  • cityNameseattle (città);
  • currencyCodeUSD (codice valuta);
  • timezoneAmerica/Los_Angeles (fuso orario).

targetLocationSynced

  • latitude: 47.44265 (latitudine all'avvio dell'app);
  • longitude-122.30036 (longitudine all'avvio dell'app).

Indirizzo dell'utente e veicoli nelle vicinanze

Sempre all'interno dello stesso percorso è presente un altro file eyeball (JSON) che contiene i tracciati dei percorsi delle auto Uber nelle vicinanze dell'utente al momento della richiesta della corsa; una sorta di radar oculare di prossimità dei veicoli Uber. Comunque, l'elemento più importante in questo file è l'indirizzo dell'utente al momento della richiesta del servizio Uber, ottenuto tramite una geocodifica inversa.

Indirizzo dell'utente

Nota: L'oggetto reverseGeocode non è presente nel CTF della Cellebrite, mentre in quello della Belkasoft i campi di interesse contengono valori "zero/null". Per dare una forma a questi dati li ho inseriti manualmente come da "A Dynamic and Static Analysis of the Uber Mobile Application from a Privacy Perspective". In assenza di geocodifica inversa è comunque possibile risalire all'area dove si trovava l'utente riportando in mappa i percorsi delle auto Uber.

L'oggetto reverseGeocode:

  • latitude40.7102306652868 (latitudine);
  • longitude-74.00613835924165 (longitudine);
  • longAddress: 163 William Street, New York, NY 10038, USA (indirizzo completo).

Veicoli nelle vicinanze

La Direzione (Course) è misurata in gradi a partire con lo 0 rivolto a nord e in verso orario. Quindi nord=0, est=90, sud=180 e ovest=270. Un valore negativo indica che la direzione non è valida (Apple Developer).

L'oggetto nearbyVehicles:

Per esempio la key=174

  • minEta4 (arrivo stimato in minuti);
  • etaStringShort4 mins (arrivo stimato in formato stringa breve);
  • vehiclePathsec57563e0d835800327512548d8b7169xxx (array con le posizioni del percorso);
    • [0]:
      • latitude: 47.444059999999993 (latitudine);
      • longitude-122.30142000000001 (longitudine);
      • course225 (direzione);
      • epoch1626554031505 (timestamp nel formato Unix Epoch ms: 17 luglio 2021 20:33:51).
    • [1]: ...

Profilo pagamenti

Nell'Internal App Path "/Library/Application Support/PersistentStorage/Store/PaymentFoundation.PaymentStreamModelKey/" è presente il file profiles (JSON) che contiene i dati relativi ai profili dei pagamenti.

  • cardTypeApple Pay Display (tipo carta: Visa, MasterCard, Cash, etc.);
  • accountNameApple Pay Display (titolo);
  • cardBin: (Bank Identification Number: 407152->Visa);
  • cardNumberappl (numero carta: 3767, appl, Cash, etc.);
  • cardCategory: (tipologia di addebito: DEBIT, etc.)
  • cardExpirationEpoch: (data di scadenza in Unix Epoch ms);
  • statusactive (stato);
  • useCasepersonal (tipo di utilizzo: business);
  • billingCountryIso2US (codice paese: IT, etc.);
  • uuideafd0a35-9c67-5589xxx (identificativo univoco);

Corse cercate e luoghi

Queste informazioni sono presenti nel database SQLite database.db nell'Internal App Path "/Documents/". Due sono le tabelle di maggior interesse, place e hits che contengono rispettivamente la cache dei luoghi e le corse cercate.

Nota: Il file database.db in esame è quello quello del mio iPhone.

Corse cercate

SELECT
    h._id AS "h_id",
    p._id AS "p_id",
    datetime(h.timestamp_ms, 'unixepoch') AS "timestamp",
    p.tag,
    p.title_segment AS "destTitle",
    p.subtitle_segment AS "destAddr",
    coalesce(p.latitude_v2, p.latitude, "") AS "destLat",
    coalesce(p.longitude_v2, p.longitude, "") AS "destLng"
FROM hits AS "h"
LEFT JOIN place AS "p" ON (h.fk_place_row_id = p._id)

La tabella hits (esempio h._id=16, p._id=4):

  • h._id: 16 (chiave primaria);
  • h.timestamp_ms2024-04-05 18:42:00 (timestamp nel formato Unix Epoch in secondi: 05 aprile 2024 18:42:00);
  • p.tag: (etichetta/marcatore);
  • p.title_segmentNuova fiera di Roma Ingresso Nord (nome/titolo);
  • p.subtitle_segmentCentro Direzionale, Via Ugo Tiberio, 368, 00148 Roma RM (indirizzo completo);
  • p.latitude_v241.808615 (latitudine);
  • p.longitude_v2: 12.3225869 (longitudine);
  • h.fk_place_row_id: 4 (chiave esterna dei luoghi, p._id della tabella place).

Luoghi

SELECT
    p._id AS "p_id",
    datetime(p.timestamp_ms, 'unixepoch') AS "lastHit",
    json_extract(p.place_result, '$.payload.personalPayload.labelType') AS "type",
    p.tag,
    json_extract(p.place_result, '$.location.addressLine1') AS "name",
    json_extract(p.place_result, '$.location.fullAddress') AS "address",
    coalesce(p.latitude_v2, p.latitude, "") AS "lat",
    coalesce(p.longitude_v2, p.longitude, "") AS "lng",
    json_extract(p.place_result, '$.location.provider') AS "provider",
    (SELECT group_concat(json_each.value, ', ') FROM json_each(json_extract(p.place_result, '$.location.categories'))) AS "categories",
    p.uber_id
FROM place AS "p"

La tabella place (esempio p._id=2):

  • p._id: 2 (chiave primaria);
  • p.timestamp_ms2024-04-14 17:03:25 (data e ora dell'ultima visita nel formato Unix Epoch in secondi14 aprile 2024 17:03:25);
  • p.tagAuditorium della tecnica (etichetta/marcatore);
  • p.place_result: <TEXT> (risposta in formato JSON);
  • p.latitude_v2: 41.8362338 (latitudine);
  • p.longitude_v212.4636195 (longitudine);
  • p.uber_id: 1122950956 (identificatore Uber).
Questo è il contenuto del campo place_result (JSON) relativo alla ricerca di una corsa per la destinazione "Auditorium della tecnica".

  • payload.personalPayload.labelTypeFAVORITE (tipo di etichetta);
  • location.addressLine1Centro Congressi Auditorium della Tecnica (nome del luogo);
  • location.fullAddressViale Umberto Tupini, 65, 00144 Roma RM (indirizzo);
  • location.providergoogle_places (fornitore della posizione);
  • location.categoriesestablishment, point_of_interest (lista delle categorie).

Geo-Locations (SQLite)

Insieme al database SQLite database.db, nell'Internal App Path "/Documents/" è comparso di recente il database SQLite ur_message.db (unified-reporter message) subito dopo aver aggiornato l'app dalla versione 3.604.10001 (assente) alla più recente 3.610.10000. I dati raccolti nella tabella message sono distinti per tipo (message_type) e presenti nel campo content nel formato JSON. 

Le strutture dei dati presenti in content a "colpo d'occhio" sembrano analoghe a quelle presenti nei database LevelDB "/Library/Application Support/com.ubercab.UberClient/storagev2/unified-reporter-client.<type>.*".  

SELECT
    m.auto_row_id AS "m_id",
    datetime(json_extract(m.content, '$.jsonConformingObject.meta.time_ms') / 1000, 'unixepoch') AS "timestamp",
    m.message_type AS "messageType",
    datetime(json_extract(m.content, '$.jsonConformingObject.meta.location.gps_time_ms') / 1000, 'unixepoch') AS "gpsTime",
    json_extract(m.content, '$.jsonConformingObject.meta.location.city') AS "city",
    json_extract(m.content, '$.jsonConformingObject.meta.location.latitude') AS "lat",
    json_extract(m.content, '$.jsonConformingObject.meta.location.longitude') AS "lng",
    json_extract(m.content, '$.jsonConformingObject.meta.location.horizontal_accuracy') AS "accuracy",
    json_extract(m.content, '$.jsonConformingObject.meta.location.speed') AS "speed",
    json_extract(m.content, '$.jsonConformingObject.data.name') AS "name",
    datetime(json_extract(m.content, '$.jsonConformingObject.data.ui_state.timestamp_ms') / 1000, 'unixepoch') AS "uiTimestamp",
    json_extract(m.content, '$.jsonConformingObject.data.ui_state.metadata') AS "uiMetadata",
    json_extract(m.content, '$.jsonConformingObject.data.ui_state.scene') AS "uiScene",
    json_extract(m.content, '$.jsonConformingObject.data.app_type_value_map') AS "appTypeValueMap",
    json_extract(m.content, '$.jsonConformingObject.data.active_trips') AS "actTrips"
FROM message AS "m"

La tabella message:

  • m.auto_row_id: (chiave primaria);
  • m.content: (JSON);
  • m.message_type: (tipo di messaggio es: log, event, analytics, etc.).
Un esempio di una JSON presente in m.content:

  • jsonConformingObject.meta.time_ms: 1713114166539 (timestamp in ms del metadata: 14 aprile 2024 17:02:46);
  • jsonConformingObject.meta.location.gps_time_ms: 1713114176739 (timestamp in ms della posizione: 14 aprile 2024 17:02:56);
  • jsonConformingObject.meta.location.city: rome (città);
  • jsonConformingObject.meta.location.latitude: 41.969700000000003 (latitudine);
  • jsonConformingObject.meta.location.longitude: 12.53917 (longitudine);
  • jsonConformingObject.meta.location.horizontal_accuracy: 35 (accuratezza orizzontale in metri);
  • jsonConformingObject.meta.location.speed: -1 (velocità m/s);
  • jsonConformingObject.data.ui_state.timestamp_ms: 1713114165679 (UI timestamp in Unix Epoch ms: 14 aprile 2024 17:02:45);
  • jsonConformingObject.data.ui_state.metadata: {"mode":"uberHome"} (UI metadata);
  • jsonConformingObject.data.ui_state.scene: ["HomeScreenShimmer"] (UI scene);
  • jsonConformingObject.data.app_type_value_map: {"activeTrips":"[]","riderStatus":"looking"} (map key-value "Seconda Parte");
  • jsonConformingObject.data.active_trips[] (informazioni sulle corse "Seconda Parte").

Al centro commerciale Porta di Roma: Google Maps.

Introduzione a LevelDB

Le procedure di seguito mostrate vengono svolte su database LevelDB, ovvero un database NoSQL basato sul modello chiave-valore. Sia le chiavi che i valori sono array di byte e possono essere compressi con la libreria Google Snappy.

La tipica struttura su file system di un database LevelDB è una cartella che contiene una serie di file: ######.ldb######.logMANIFEST-######CURRENTLOGLog.old e LOCK dove ###### è il numero progressivo esadecimale della creazione del file. I file con estensione .ldb e .log contengono i record con i dati effettivi, gli altri i metadati di supporto.

Per la comprensione di questo formato è consigliata la lettura di questi due ottimi articoli di Alex Caithness della CCL Solutions Group:

  1. Hang on! That’s not SQLite! Chrome, Electron and LevelDB;
  2. IndexedDB on Chromium.

Nell'Internal App Path "/Library/Application Support/com.ubercab.UberClient/" è presente una grande mole di dati in vari formati, molti dei quali sono database LevelDB.

__METADATA

Il database LevelDB "/Library/Application Support/com.ubercab.UberClient/__METADATA/" rappresenta il nostro "catalogo" generale dei database presenti nelle sottocartelle <UUID>; una mappatura key-value, dove la key è il tipo di oggetto e value è la struttura (B)Plist serializzata. Deserializzando una di queste si vede chiaramente sia il nome della sottocartella (_uuid) che il tipo di oggetto (_name).

  • _nameUBPersistenceMetadata.com.uber.location.UBDeviceLocationSource;
  • _uuidDD646F14-F15C-4599-B603-0DB6C9327163.

La mappatura completa delle key: value di __METADATA:

  1. UBPersistenceMetadata.UBFileUploadClientPersistanceIdentifier: A1CC07C8-0378-4E25-B5D9-8EA0BB1C4A9B;
  2. UBPersistenceMetadata.UBMorpheusExperimentsPersistence: DD646F14-F15C-4599-B603-0DB6C9327163;
  3. UBPersistenceMetadata.UBNetworkHostReplacementPersistenceContext: 43098A1E-BAF2-41F4-9AF2-ED765BE09741;
  4. UBPersistenceMetadata.com.uber.location.UBDeviceLocationSource: 4E240390-4CFE-4D6D-8550-B813F90B4063;
  5. UBPersistenceMetadata.com.ubercab.UberClient.login.modelprovider: 6B5263C4-1F49-4490-897E-096B7130C4EC;
  6. UBPersistenceMetadata.UBDynamicLocalizationTranslationStorage: B9AF4903-A2C7-46D6-99DA-E2DACF3BB3A3;

Luoghi

I luoghi sono memorizzati nel database 4E240390-4CFE-4D6D-8550-B813F90B4063 in questo caso specifico (punto 4) e comunque è sempre il database con la key UBPersistenceMetadata.com.uber.location.UBDeviceLocationSource.

  • _sample.sample_location.kCLLocationCodingKeyTimestamp: 634113220.008416 (timestamp come real nel formato MAC Absolute Time 04 febbraio 2021 06:33:40);
  • _sample.sample_location.kCLLocationCodingKeyCoordinateLatitude: 47.4797494663368 (latitudine);
  • _sample.sample_location.kCLLocationCodingKeyCoordinateLongitude-122.201318582634 (longitudine);
  • _sample.kCLLocationCodingKeyHorizontalAccuracy: 5 (accuratezza orizzontale in metri);
  • _sample.sample_location.kCLLocationCodingKeyAltitude: 26.9660034179688 (altitudine in metri);
  • _sample.kCLLocationCodingKeyVerticalAccuracy: 6 (accuratezza verticale in metri);
  • _sample.sample_location.kCLLocationCodingKeyCourse46.7116737365723 (direzione in gradi decimali);
  • _sample.kCLLocationCodingKeyCourseAccuracy: 2.41023544676681 (accuratezza della direzione in gradi decimali)
  • _sample.sample_location.kCLLocationCodingKeySpeed: 29.7000007629395 (velocità in m/s);
  • _sample.kCLLocationCodingKeySpeedAccuracy: 1.11000001430511 (accuratezza della velocità in m/s).

L'oggetto sample_location è un oggetto CLLocation.

Geo-Locations (LevelDB)

"/Library/Application Support/com.ubercab.UberClient/storagev2/" contiene una una serie di cartelle unified-reporter-client.<type>.* ognuna delle quali è un database LevelDB. Alexis Brignoni ha creato un parser per queste posizioni New parser for Uber app geo-locations in iOS using iLEAPP. I report sono in formato JSON e la loro analisi in parte è già stata discussa in Geo-Locations (SQLite).

iLEAPP 💖

Come di consueto, a supporto del progetto iLEAPP (iOS Logs, Events, And Plists Parser) di Alexis Brignoni ho sviluppato il plugin uberClient.py che alla data odierna come detto in premessa è stato già integrato nel progetto.

Di seguito alcuni screenshot del report di Uber:

Read More