Un tutorial per il reverse engineering dell'API privata del tuo software: Hacking Your Couch
Pubblicato: 2022-03-11Viaggiare è la mia passione e sono un grande fan di Couchsurfing. Couchsurfing è una comunità globale di viaggiatori, dove puoi trovare un posto dove stare o condividere la tua casa con altri viaggiatori. Inoltre, Couchsurfing ti aiuta a vivere un'esperienza di viaggio genuina mentre interagisci con la gente del posto. Sono stato coinvolto nella comunità di Couchsurfing per oltre 3 anni. All'inizio ho partecipato ai meetup e poi finalmente sono stato in grado di ospitare persone. Che viaggio fantastico è stato! Ho incontrato così tante persone incredibili da tutto il mondo e ho fatto molti amici. Tutta questa esperienza ha davvero cambiato la mia vita.
Io stesso ho ospitato molti viaggiatori, molto più di quanti ne abbia effettivamente navigati. Vivendo in una delle principali destinazioni turistiche della Costa Azzurra, ho ricevuto un'enorme quantità di richieste di posti letto (fino a 10 al giorno in alta stagione). Come sviluppatore back-end freelance, ho immediatamente notato che il problema con il sito couchsurfing.com è che non gestisce correttamente casi così "ad alto carico". Non ci sono informazioni sulla disponibilità del tuo divano - quando ricevi una nuova richiesta di divano non puoi essere sicuro se stai già ospitando qualcuno in quel momento. Dovrebbe esserci una rappresentazione visiva delle tue richieste accettate e in sospeso, in modo da poterle gestire meglio. Inoltre, se potessi rendere pubblica la disponibilità del tuo divano, potresti evitare richieste di divano non necessarie. Per capire meglio cosa ho in mente dai un'occhiata al calendario di Airbnb.
Molte aziende sono famose per non ascoltare i propri utenti. Conoscendo la storia di Couchsurfing, non potevo contare su di loro per implementare presto questa funzione. Da quando il sito Web è diventato una società a scopo di lucro, la comunità è peggiorata. Per capire meglio di cosa sto parlando, suggerisco di leggere questi due articoli:
- http://www.nithincoca.com/2013/03/27/the-rise-and-fall-of-couchsurfing/
- http://mechanicalbrain.wordpress.com/2013/03/04/couchsurfing-a-sad-end-to-a-great-idea/
Sapevo che molti membri della comunità sarebbero stati felici di avere questa funzionalità. Quindi, ho deciso di creare un'app per risolvere questo problema. Si scopre che non è disponibile alcuna API Couchsurfing pubblica. Ecco la risposta che ho ricevuto dal loro team di supporto:
"Purtroppo dobbiamo informarvi che la nostra API non è effettivamente pubblica e al momento non ci sono piani per renderla pubblica".
Entrare nel mio divano
Era giunto il momento di utilizzare alcune delle mie tecniche di reverse engineering del software preferite per entrare in Couchsurfing.com. Ho pensato che le loro app mobili dovessero utilizzare una sorta di API per interrogare il back-end. Quindi, ho dovuto intercettare le richieste HTTP provenienti da un'app mobile al back-end. A tale scopo ho impostato un proxy nella rete locale e ho collegato il mio iPhone ad esso per intercettare le richieste HTTP. In questo modo, sono stato in grado di trovare punti di accesso della loro API privata e capire il loro formato di payload JSON.
Alla fine ho creato un sito Web che ha lo scopo di aiutare le persone a gestire le loro richieste sul divano e mostrare ai surfisti un calendario di disponibilità del divano. Ho pubblicato un link ad esso sui forum della community (che sono anche abbastanza segmentati secondo me, ed è difficile trovare informazioni lì). L'accoglienza è stata per lo più positiva, anche se ad alcune persone non è piaciuta l'idea che il sito Web richiedesse le credenziali di couchsurfing.com, il che era davvero una questione di fiducia.
Il sito funzionava così: accedi al sito con le tue credenziali couchsurfing.com, e dopo pochi click ottieni il codice html che puoi incorporare nel tuo profilo couchsurfing.com, e voilà - hai un calendario aggiornato automaticamente in Il tuo profilo. Di seguito lo screenshot del calendario e qui gli articoli su come l'ho realizzato:
- https://github.com/nderkach/couchsurfing-python
Ho creato un'ottima funzionalità per Couchsurfing e naturalmente ho pensato che avrebbero apprezzato il mio lavoro, forse anche offrirmi una posizione nel loro team di sviluppo. Ho inviato un'e-mail a jobs(at)couchsurfing.com con un link al sito Web, il mio curriculum e un riferimento. Un biglietto di ringraziamento lasciato da uno dei miei ospiti couchsurfing:
Pochi giorni dopo hanno seguito i miei sforzi di reverse engineering. Nella risposta era chiaro che l'unica cosa di cui erano preoccupati era la propria sicurezza, quindi mi hanno chiesto di rimuovere i post del blog che ho scritto sull'API e, infine, sul sito web. Ho rimosso immediatamente i post, poiché la mia intenzione non era quella di violare i termini di utilizzo e cercare le credenziali dell'utente, ma piuttosto di aiutare la comunità di couchsurfing. Ho avuto l'impressione di essere trattato come un criminale e l'azienda si è concentrata esclusivamente sul fatto che il mio sito Web richiede le credenziali dell'utente.
Ho proposto di dare loro la mia app gratuitamente. Potrebbero ospitarlo nel loro ambiente e collegarlo tramite l'autenticazione di Facebook. Dopotutto, è un'ottima funzionalità e la community ne aveva bisogno. Ecco la risoluzione finale che ho ricevuto:
“Stiamo tornando al ritmo delle cose qui dopo le vacanze e volevamo continuare.
Abbiamo avuto alcune discussioni interne sulla tua applicazione e su come possiamo onorare la creatività e l'iniziativa che mostra senza compromettere potenzialmente la privacy e la sicurezza dei dati degli utenti di Couchsurfing quando inseriscono le loro credenziali in un sito di terze parti.
Il calendario riempie chiaramente un vuoto di funzionalità sul nostro sito, una funzionalità che fa parte di un progetto più ampio su cui stiamo lavorando ora.
Ma il problema della raccolta di nomi utente e password rimane. Non siamo riusciti a trovare un modo semplice per configurarlo in modo da poterlo ospitare o supportare dalla nostra parte senza consentirti di accedere a quei dati o che il tuo sito fosse visto come il nostro prodotto di lavoro.
L'API attualmente disponibile sarà presto sostituita con una versione che richiederà l'autenticazione/autorizzazione dalle applicazioni che vi accedono".
Oggi mentre scrivo questo tutorial sul software di reverse engineering (un anno dopo gli eventi), la funzionalità del calendario non è ancora implementata su Couchsurfing.
Ritorno all'innocenza - Hacking My Couch, ancora
Qualche settimana fa sono stato ispirato a scrivere un articolo sulle tecniche per decodificare le API private. Naturalmente, ho deciso di riassumere i precedenti articoli che ho scritto su questo argomento e aggiungere qualche dettaglio in più. Quando ho iniziato a scrivere il nuovo articolo, volevo mostrare il processo di reverse engineering con un'API aggiornata e fare un altro abbozzo sull'hacking delle API. Sulla base della mia precedente esperienza e del fatto che Couchsurfing ha recentemente annunciato un'app mobile e web completamente nuova http://blog.couchsurfing.com/the-future-of-couchsurfing-is-on-the-way/, ho deciso di hackerare di nuovo la loro API.
Perché sto facendo questo processo di reverse engineering? Bene, prima di tutto è molto divertente decodificare il software in generale. Quello che mi piace particolarmente è che non coinvolge solo la tua abilità tecnica, ma anche il tuo intuito. A volte, il modo migliore per capire le cose è fare un'ipotesi plausibile: ti farà risparmiare un sacco di tempo rispetto alla forza bruta. Recentemente ho sentito la storia di un'azienda che ha dovuto lavorare con API proprietarie e poca o nessuna documentazione. Erano giorni che faticavano a decrittografare il payload di risposta dell'API in un formato sconosciuto, quindi qualcuno ha deciso di provare ?decode=true
alla fine dell'URL e avevano un JSON corretto. A volte, se sei fortunato, tutto ciò che devi fare è abbellire la risposta JSON.
Un altro motivo per cui sto facendo questo tutorial è che ci vogliono anni prima che alcune aziende adottino una particolare funzionalità richiesta dai loro utenti. Invece di aspettare che venga implementato, puoi sfruttare la potenza della loro API privata e costruirla tu stesso.
Quindi, con la nuova API couchsurfing.com, ho iniziato con un approccio simile e ho installato la loro ultima app iOS.
Innanzitutto, devi configurare un proxy nella tua LAN per falsificare le richieste HTTP provenienti dall'app all'API eseguendo un attacco man-in-the-middle (MITM).
Per le connessioni non crittografate l'attacco è abbastanza semplice: un client si connette al proxy e si inoltrano le richieste in arrivo al server di destinazione avanti e indietro. Potresti eventualmente modificare il carico utile, se necessario. In una WLAN pubblica, è abbastanza facile farlo sotto mentite spoglie impersonando il router WiFi.
Per le connessioni crittografate, c'è una piccola differenza: tutte le richieste sono crittografate end-to-end. non è possibile per l'attaccante decifrare il messaggio, a meno che non ottenga in qualche modo l'accesso alla chiave privata (che ovviamente non viene inviata durante queste interazioni). Detto questo, anche se il canale di comunicazione dell'API è sicuro, gli endpoint, in particolare il client, non sono così sicuri.
Affinché SSL funzioni correttamente, devono essere soddisfatte le seguenti condizioni:
- Il certificato del server deve essere firmato con un'autorità di certificazione (CA) affidabile
- Il nome comune del server, nel certificato, deve corrispondere al nome di dominio del server
Per superare la crittografia in un attacco MITM, il nostro Proxy deve agire come una CA (Autorità di certificazione) e generare certificati al volo. Ad esempio, se un client tenta di connettersi a www.google.com, il proxy crea dinamicamente un certificato per www.google.com e lo firma. Ora, il cliente pensa che il proxy sia in realtà www.google.com
Per implementare un proxy di sniffing utilizzato per decodificare l'API privata, utilizzerò uno strumento chiamato mitmproxy. Puoi utilizzare qualsiasi altro proxy HTTPS trasparente. Charles è un altro esempio con una bella GUI. Per fare questo lavoro abbiamo bisogno di impostare le seguenti cose:
Configura il gateway predefinito della connessione WiFi del tuo telefono come proxy (in modo che il proxy sia nel mezzo e tutti i pacchetti passino) Installa il certificato del proxy sul telefono (in modo che il client abbia la chiave pubblica del proxy nel suo truststore)
Controlla la documentazione del tuo proxy sull'installazione del certificato. Ecco le istruzioni per mitmproxy. Ed ecco il file PEM del certificato per iOS.
Per monitorare le richieste HTTP intercettate, avvia semplicemente mitmproxy e ti connetti ad esso dal tuo telefono cellulare (la porta predefinita è 8080).
Apri un sito web nel tuo browser mobile. A questo punto dovresti essere in grado di vedere il traffico in mitmproxy.
Dopo esserti assicurato che tutto funzioni come previsto, è il momento di iniziare a esplorare l'API privata di tua scelta. Fondamentalmente, a questo punto puoi semplicemente aprire l'app, giocarci e farti un'idea degli endpoint API e della struttura delle richieste.
Non esiste un algoritmo rigoroso su come decodificare un'API software: la maggior parte delle volte fai affidamento sul tuo intuito e fai ipotesi.
Il mio approccio è replicare le chiamate API e giocare con diverse opzioni. Un buon inizio è riprodurre una richiesta che hai catturato in mitmproxy e vedere se funziona (premere 'r' per riprodurre una richiesta). Il primo passo è capire quali intestazioni sono obbligatorie. È abbastanza comodo giocare con le intestazioni con mitmproxy: premi "e" per entrare in modalità di modifica, quindi "h" per modificare le intestazioni. Con le scorciatoie che usano, i vim addicted si sentirebbero come a casa. Puoi anche utilizzare estensioni del browser come Postman per testare l'API, ma tendono ad aggiungere intestazioni non necessarie, quindi suggerisco di attenersi a mitmproxy o curl.
Ho creato uno script che legge il file di dump mitmproxy e genera una stringa curl - https://gist.github.com/nderkach/bdb31b04fb1e69fa5346

Iniziamo con la richiesta inviata al momento dell'accesso.
POST https://hapi.couchsurfing.com/api/v2/sessions ← 200 application/json
La prima cosa che ho notato è che ogni richiesta contiene un'intestazione obbligatoria X-CS-Url-Signature
che è ogni volta diversa. Ho anche provato a riprodurre una richiesta dopo un po' per verificare se c'è un controllo del timestamp sul server e non ce n'è. La prossima cosa da fare è capire come viene calcolata questa firma.
A questo punto ho deciso di decodificare il binario e capire l'algoritmo. Naturalmente, avendo esperienza di sviluppo per iPhone e avendo un iPhone a mia disposizione, ho deciso di iniziare con l'iPhone ipa (fornibile dell'app per iPhone). Risulta per decifrarne uno, ho bisogno di un telefono jailbroken. Fermare! Tempo del martello.
Poi, ho ricordato che hanno anche un'app per Android. Ero un po' riluttante a provare questo approccio, poiché non so nulla di Android o Java. Allora ho pensato che sarebbe stata una buona occasione per imparare qualcosa di nuovo. Si è rivelato più facile ottenere un codice quasi sorgente leggibile dall'uomo decompilando il bytecode java rispetto al codice macchina iPhone fortemente ottimizzato.
Apk (fornibile dell'app per Android) è fondamentalmente un file zip. Puoi usare qualsiasi estrattore zip per decomprimere il suo contenuto. Troverai un file chiamato classes.dex, che è un bytecode di Dalvik. Dalvik è una macchina virtuale utilizzata per eseguire il bytecode Java tradotto su Android.
Per decompilare il file .dex nel codice sorgente .java ho usato lo strumento chiamato dex2jar. L'output di questo strumento è un file jar, che puoi decompilare con una varietà di strumenti. Puoi persino aprire un barattolo in Eclipse o IntelliJ IDEA e farà tutto il lavoro per te. La maggior parte di questi strumenti produce un risultato simile. Non ci interessa davvero se possiamo compilarlo di nuovo per eseguirlo, lo stiamo semplicemente usando per analizzare il codice sorgente.
Ecco un elenco di strumenti che ho provato:
- FernFlower (ora parte di IntelliJ IDEA)
- CFR
- JD-GUI
- Krakatau
- Procione
CFR e FernFlower hanno funzionato al meglio per me. JD-GUI non è stato in grado di decompilare alcune parti critiche del codice ed era inutile, mentre le altre avevano all'incirca la stessa qualità. Fortunatamente, sembra che il codice del codice Java non sia stato offuscato, ma ci sono strumenti come ProGuard http://developer.android.com/tools/help/proguard.html per aiutarti a deoffuscare il codice.
La decompilazione Java non è proprio lo scopo di questo tutorial di reverse engineering: c'è molto scritto su questo argomento, quindi supponiamo che tu abbia decompilato e deoffuscato con successo il tuo codice Java.
Ho combinato tutto il codice pertinente utilizzato per calcolare la firma X-CS-Url nel seguente gist: https://gist.github.com/nderkach/d11540e9af322f1c1c74
In primo luogo, ho cercato le menzioni di X-CS-Url-Signature
, che ho trovato in RetrofitHttpClient
. Una chiamata in particolare sembrava interessante: al modulo EncUtils
. Scavandoci dentro, mi sono reso conto che stanno usando HMAC SHA1. HMAC è un codice di autenticazione del messaggio che utilizza una funzione crittografica (SHA1 in questo caso) per calcolare un hash di un messaggio. Viene utilizzato per garantire l'integrità (cioè impedire a un uomo nel mezzo di modificare la richiesta) e l'autenticazione.
Abbiamo bisogno di due cose per calcolare l' X-CS-Url-Signature
: la chiave privata e il messaggio codificato (probabilmente qualche variazione del payload e dell'URL della richiesta HTTP).
final String a2 = EncUtils.a(EncUtils.a(a, s)); final ArrayList<Header> list = new ArrayList<Header>(request.getHeaders()); list.add(new Header("X-CS-Url-Signature", a2));
Nel codice a
è un messaggio e s
è la chiave utilizzata per calcolare l'intestazione a2
(la doppia chiamata a EncUtils
calcola solo un digest esadecimale HMAC SHA1).
Trovare la chiave non è stato un problema: è stata archiviata in testo normale in ApiModule
ed è stata utilizzata per inizializzare il secondo parametro di RetrofitHttpClient.
RetrofitHttpClient a(OkHttpClient okHttpClient) { return new RetrofitHttpClient(okHttpClient, "v3#!R3v44y3ZsJykkb$E@CG#XreXeGCh"); }
Se osserviamo la chiamata a EncUtils
, possiamo vedere che la stringa letterale sopra viene utilizzata testualmente come chiave per calcolare l'HMAC, tranne nel caso in cui this.b
sia definito. In quest'ultimo caso, this.b
viene aggiunto un punto.
String s; if (this.b == null) { s = this.a; } else { s = this.a + "." + this.b; }
Ora, solo guardando il codice non mi era chiaro dove e come viene inizializzato this.b
(l'unica cosa che sono stato in grado di scoprire è che è chiamato in un metodo con una firma this.a(String b)
, ma non sono riuscito a trovare una chiamata da nessuna parte nel codice).
public void a(final String b) { this.b = b; }
Ti incoraggio a decompilarlo e scoprilo tu stesso :)
Capire il messaggio è stato abbastanza semplice: nel codice puoi vedere che è una concatenazione del percorso dell'URL, ovvero /api/v2/sessions
e una stringa con payload JSON (se presente).
final byte[] b = this.b(request.getUrl()); byte[] a; if (request.getBody() != null && request.getBody() instanceof JsonTypedOutput) { System.out.println("body"); // this.a(x, y) concatenates byte arrays a = this.a(b, ((JsonTypedOutput)request.getBody()).a); } else { a = b; }
Solo guardando il codice, era difficile capire l'esatto algoritmo per il calcolo HMAC. Quindi, ho deciso di ricostruire l'app con i simboli di debug per capire esattamente come funziona l'app. Ho usato uno strumento chiamato apktool https://code.google.com/p/android-apktool/ per smontare il bytecode Dalvik usando smali https://code.google.com/p/smali/. Ho seguito la guida su https://code.google.com/p/android-apktool/wiki/SmaliDebugging
Dopo aver creato l'apk, devi firmarlo e installarlo sul tuo dispositivo. Poiché non avevo un dispositivo Android, ho usato l'emulatore fornito con Android SDK. Con un po' di cucchiaio, ecco come si fa:
jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android <path_to_your_built_apk> androiddebugkey jarsigner -verify -verbose -certs <path_to_your_built_apk> zipalign -v 4 <path_to_your_built_apk> <path_to_your_output_signed_apk>
Ho usato un emulatore Android integrato che viene fornito con l'SDK e un'immagine virtuale Atom x86 con HAXM abilitato per assicurarmi che funzioni senza intoppi.
tools/emulator -avd mydroid -no-boot-anim -cpu-delay 0
Ecco una bella guida su come impostare un'immagine virtuale: http://jolicode.com/blog/speed-up-your-android-emulator
Assicurati di vedere che la riga HAX funziona e l'emulatore viene eseguito in modalità virt veloce all'avvio dell'emulatore per assicurarti di avere HAXM abilitato.
Quindi, ho installato l'apk nell'emulatore ed ho eseguito l'app. Seguendo la guida di apktool, ho sfruttato il debugger remoto IntelliJ IDEA per connettermi all'emulatore e impostare alcuni punti di interruzione di riga:
Giocando un po' con l'app, sono riuscito a capire che la chiave privata utilizzata per inizializzare RetrofitHttpClient
viene utilizzata per calcolare l'HMAC di una firma di richiesta di accesso. Nella risposta al POST di accesso ricevi un ID utente e accessToken ( X-Access-Token
). Il token di accesso viene utilizzato per autorizzare tutte le seguenti richieste. L'HMAC per tutte le richieste di accesso successivo è costruito allo stesso modo della richiesta di accesso, tranne per il fatto che la chiave è composta dall'aggiunta di .<user_id>
alla chiave privata originale.
Una volta che sei autorizzato, l'app invia la seguente richiesta:
POST https://hapi.couchsurfing.com/api/v2/users/1003669205/registerDevice ← 200 application/json
Come ho potuto dedurre empiricamente, questa richiesta è facoltativa per l'autenticazione. Punti bonus se capisci a cosa serve!
Una volta autenticato puoi inviare una richiesta per recuperare il tuo profilo utente (o di chiunque altro), in questo modo:
GET https://hapi.couchsurfing.com/api/v2/users/1003669205 ← 200 application/json
Non sono andato molto nei dettagli, ma ho notato che un profilo viene aggiornato con una richiesta PUT. Solo per divertimento, ho provato ad aggiornare un altro profilo con la stessa richiesta: non era autorizzato, quindi a quanto pare le basi della sicurezza sono state implementate.
Ho scritto un semplice script Python per accedere utilizzando le tue credenziali couchsurfing.com e ottenere il tuo profilo utente: https://gist.github.com/nderkach/899281d7e6dd0d497533. Ecco il wrapper Python per l'API: https://github.com/nderkach/couchsurfing-python con un pacchetto disponibile nel repository pypi (pip install couchsurfing).
Prossimi passi
Non sono sicuro di cosa farò esattamente con l'API questa volta. Il codice HTML nei profili utente non è più consentito, quindi dovrò trovare un approccio diverso al vecchio problema. Continuerò a sviluppare e migliorare il wrapper dell'API Python, se ce n'è una richiesta, e supponendo che couchsurfing.com non causerà troppi problemi. Non ho esplorato troppo l'API e l'ho appena testata per alcune vulnerabilità di base. Sembra abbastanza sicuro, ma sarebbe interessante scoprire se è possibile accedere ai dati che non sono disponibili tramite il sito web. Ad ogni modo, ora puoi utilizzare il mio reverse software engineering per creare un client alternativo per Windows Phone, Pebble o il tuo smart-couch.
Conclusione con una domanda
C'è una discussione che vorrei aprire: perché non pubblicare la tua API e renderla pubblica? Anche se non fossi riuscito ad hackerare l'API, sarebbe comunque possibile raschiare il sito web. Sarebbe più lento e più difficile da mantenere, ma sicuramente preferirebbero che i consumatori utilizzassero un'API piuttosto che un web scraper. La disponibilità delle API consentirebbe agli sviluppatori di terze parti di migliorare il prodotto dell'azienda e di creare servizi a valore aggiunto attorno ad esso. Si può argomentare che sarebbe più costoso mantenere l'API pubblica piuttosto che privata; ma ancora una volta, i vantaggi dei tuoi servizi di creazione di comunità in aggiunta al tuo prodotto supererebbero i costi di manutenzione dell'API.
È possibile impedire completamente l'utilizzo di un'API privata da parte di client di terze parti? Non credo. L'utilizzo del blocco SSL impedirebbe lo sniffing nelle richieste API utilizzando una semplice tecnica proxy trasparente come descritto in precedenza. Alla fine, anche se offuschi il binario, un hacker motivato con alcune risorse e tempo sarà sempre in grado di decodificare il binario dell'app e ottenere la chiave/certificato privato. Penso che il presupposto che l'endpoint del client sia sicuro sia intrinsecamente sbagliato. Un client API è un punto debole.
Mantenendo un'API privata, un'azienda sta fondamentalmente trasmettendo un messaggio di sfiducia ai propri utenti. Sicuramente, puoi provare a proteggere ulteriormente la tua API privata. Tuttavia, non preferiresti implementare una sicurezza di base per l'API per prevenire l'utilizzo dannoso; e invece concentrare le tue risorse sul miglioramento del software per fornire una migliore esperienza utente?
Couchsurfing, per favore, con lo zucchero in cima, apri l'API.