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 clientStatus, city 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
- firstName: Marsha (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);
- referralUrl: https://www.uber.com/invite/j2ez6lxxx (URL per l'invito);
- profileType: uber (tipo di profilo);
- mobileCountryIso2: US (codice paese);
- lastSelectedPaymentProfileUUID: eafd0a35-9c67-5589-xxx (identificatore dell'ultimo metodo di pagamento selezionato);
- uuid: 4028f734-7f4b-45b0-xxx (identificatore univoco).
clientStatus
- lastModifiedTimeMs: 1626554060317 (data e ora dell'ultimo utilizzo nel formato Unix Epoch ms: 17 luglio 2021 20:34:20);
- statusDescription: Looking (descrizione dello stato).
city
- cityId: 10 (identificatore della città);
- cityName: seattle (città);
- currencyCode: USD (codice valuta);
- timezone: America/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:
- latitude: 40.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
- minEta: 4 (arrivo stimato in minuti);
- etaStringShort: 4 mins (arrivo stimato in formato stringa breve);
- vehiclePaths: ec57563e0d835800327512548d8b7169xxx (array con le posizioni del percorso);
- [0]:
- latitude: 47.444059999999993 (latitudine);
- longitude: -122.30142000000001 (longitudine);
- course: 225 (direzione);
- epoch: 1626554031505 (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.
- cardType: Apple Pay Display (tipo carta: Visa, MasterCard, Cash, etc.);
- accountName: Apple Pay Display (titolo);
- cardBin: (Bank Identification Number: 407152->Visa);
- cardNumber: appl (numero carta: 3767, appl, Cash, etc.);
- cardCategory: (tipologia di addebito: DEBIT, etc.)
- cardExpirationEpoch: (data di scadenza in Unix Epoch ms);
- status: active (stato);
- useCase: personal (tipo di utilizzo: business);
- billingCountryIso2: US (codice paese: IT, etc.);
- uuid: eafd0a35-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_ms: 2024-04-05 18:42:00 (timestamp nel formato Unix Epoch in secondi: 05 aprile 2024 18:42:00);
- p.tag: (etichetta/marcatore);
- p.title_segment: Nuova fiera di Roma Ingresso Nord (nome/titolo);
- p.subtitle_segment: Centro Direzionale, Via Ugo Tiberio, 368, 00148 Roma RM (indirizzo completo);
- p.latitude_v2: 41.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_ms: 2024-04-14 17:03:25 (data e ora dell'ultima visita nel formato Unix Epoch in secondi: 14 aprile 2024 17:03:25);
- p.tag: Auditorium della tecnica (etichetta/marcatore);
- p.place_result: <TEXT> (risposta in formato JSON);
- p.latitude_v2: 41.8362338 (latitudine);
- p.longitude_v2: 12.4636195 (longitudine);
- p.uber_id: 1122950956 (identificatore Uber).
- payload.personalPayload.labelType: FAVORITE (tipo di etichetta);
- location.addressLine1: Centro Congressi Auditorium della Tecnica (nome del luogo);
- location.fullAddress: Viale Umberto Tupini, 65, 00144 Roma RM (indirizzo);
- location.provider: google_places (fornitore della posizione);
- location.categories: establishment, 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.).
- 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, ######.log, MANIFEST-######, CURRENT, LOG, Log.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:
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).
- _name: UBPersistenceMetadata.com.uber.location.UBDeviceLocationSource;
- _uuid: DD646F14-F15C-4599-B603-0DB6C9327163.
La mappatura completa delle key: value di __METADATA:
- UBPersistenceMetadata.UBFileUploadClientPersistanceIdentifier: A1CC07C8-0378-4E25-B5D9-8EA0BB1C4A9B;
- UBPersistenceMetadata.UBMorpheusExperimentsPersistence: DD646F14-F15C-4599-B603-0DB6C9327163;
- UBPersistenceMetadata.UBNetworkHostReplacementPersistenceContext: 43098A1E-BAF2-41F4-9AF2-ED765BE09741;
- UBPersistenceMetadata.com.uber.location.UBDeviceLocationSource: 4E240390-4CFE-4D6D-8550-B813F90B4063;
- UBPersistenceMetadata.com.ubercab.UberClient.login.modelprovider: 6B5263C4-1F49-4490-897E-096B7130C4EC;
- 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.kCLLocationCodingKeyCourse: 46.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:
0 comments:
Posta un commento