逆向工程軟件私有 API 的教程:破解你的沙發

已發表: 2022-03-11

旅行是我的熱情所在,我是 Couchsurfing 的忠實粉絲。 Couchsurfing 是一個全球旅行者社區,您可以在其中找到住宿地點或與其他旅行者分享自己的家。 最重要的是,Couchsurfing 可以幫助您在與當地人互動的同時享受真正的旅行體驗。 我參與 Couchsurfing 社區已有 3 年多了。 起初我參加了聚會,然後我終於能夠接待人了。 這是一次多麼奇妙的旅程! 我遇到了來自世界各地的許多不可思議的人,並結交了很多朋友。 這整個經歷真正改變了我的生活。

我自己接待過很多旅行者,比我實際上網的次數多得多。 當我住在法國里維埃拉的主要旅遊目的地之一時,我收到了大量的沙發請求(旺季每天最多 10 個)。 作為一名自由後端開發人員,我立即註意到 couchsurfing.com 網站的問題在於它並不能真正正確地處理這種“高負載”情況。 沒有關於您的沙發可用性的信息 - 當您收到新的沙發請求時,您無法確定您當時是否已經在接待某人。 您接受和待處理的請求應該有一個可視化的表示,這樣您就可以更好地管理它們。 此外,如果您可以公開您的沙發可用性,您可以避免不必要的沙發請求。 為了更好地理解我的想法,請查看 Airbnb 日曆。

許多公司因不聽取用戶的意見而臭名昭著。 了解 Couchsurfing 的歷史,我不能指望他們很快就會實現這個功能。 自從該網站成為一家營利性公司後,社區就惡化了。 為了更好地理解我在說什麼,我建議閱讀以下兩篇文章:

  • 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/

我知道很多社區成員會很高興擁有此功能。 所以,我決定製作一個應用程序來解決這個問題。 事實證明,沒有可用的公共 Couchsurfing API。 以下是我從他們的支持團隊收到的回复:

“不幸的是,我們必須通知您,我們的 API 實際上並未公開,目前也沒有公開的計劃。”

闖入我的沙發

是時候使用我最喜歡的一些軟件逆向工程技術闖入 Couchsurfing.com。 我假設他們的移動應用程序必須使用某種 API 來查詢後端。 因此,我不得不攔截來自移動應用程序到後端的 HTTP 請求。 為此,我在本地網絡中設置了一個代理,並將我的 iPhone 連接到它以攔截 HTTP 請求。 通過這種方式,我能夠找到他們私有 API 的訪問點並找出他們的 JSON 有效負載格式。

最後,我創建了一個網站,旨在幫助人們管理他們的沙發請求,並向衝浪者展示沙發可用性日曆。 我在社區論壇上發布了一個鏈接(我認為這些論壇也很細分,在那裡很難找到信息)。 接待大多是積極的,儘管有些人不喜歡該網站需要 couchsurfing.com 憑據的想法,這確實是一個信任問題。

該網站的工作方式是這樣的:您使用您的 couchsurfing.com 憑據登錄該網站,單擊幾下後,您將獲得可以嵌入到您的 couchsurfing.com 個人資料中的 html 代碼,瞧——您有一個自動更新的日曆你的個人資料。 下面是日曆的截圖,這裡是關於我如何製作它的文章:

  • https://github.com/nderkach/couchsurfing-python

示例日曆

我為 Couchsurfing 創建了一個很棒的功能,我自然而然地認為他們會欣賞我的工作——甚至可能會在他們的開發團隊中為我提供一個職位。 我已經向jobs(at)couchsurfing.com發送了一封電子郵件,其中包含指向該網站的鏈接、我的簡歷和參考資料。 我的一位沙發衝浪客人留下的感謝信:

謝謝注意。

幾天后,他們跟進了我的逆向工程工作。 在回復中很明顯,他們唯一關心的是他們自己的安全,所以他們要求我刪除我寫的關於 API 的博客文章,最後是網站。 我立即刪除了這些帖子,因為我的意圖不是違反使用條款和獲取用戶憑據,而是幫助沙發衝浪社區。 我的印像是我被視為罪犯,公司只關注我的網站需要用戶憑據這一事實。

我提議免費給他們我的應用程序。 他們可以在他們的環境中託管它並通過 Facebook 身份驗證連接它。 畢竟,這是一個很棒的功能,社區需要它。 這是我收到的最終解決方案:

“假期結束後,我們正在重新開始這裡的事情,並希望跟進。

我們已經就您的應用程序進行了一些內部討論,以及我們如何既尊重它所展示的創造力和主動性,同時又不會在 Couchsurfing 用戶將憑據輸入第三方站點時潛在地損害他們的隱私和安全性。

日曆清楚地填補了我們網站上的一個功能漏洞,該功能是我們正在開展的一個更大項目的一部分。

但是收集用戶名和密碼的問題仍然存在。 我們無法想出一個簡單的方法來設置它,以便我們可以在我們這邊託管或支持它,而不允許您訪問該數據或讓您的網站被視為我們的工作產品。

當前可用的 API 很快就會被一個需要訪問它的應用程序進行身份驗證/授權的版本所取代。”

今天,當我在寫這個逆向工程軟件教程時(事件發生一年後),日曆功能仍然沒有在 Couchsurfing 上實現。

回歸純真 - 再次入侵我的沙發

幾週前,我受到啟發寫了一篇關於逆向工程私有 API 的技術的文章。 自然地,我決定總結一下我之前寫的關於這個主題的文章,並添加更多細節。 當我開始寫這篇新文章時,我想用最新的 API 展示逆向工程過程,並將另一個存根用於 API hacking。 根據我以前的經驗,以及 Couchsurfing 最近宣布推出全新的 Wesbite 和移動應用程序 http://blog.couchsurfing.com/the-future-of-couchsurfing-is-on-the-way/ 的事實,我已經決定再次破解他們的 API。

為什麼我要做這個逆向工程過程? 嗯,首先,逆向工程軟件通常很有趣。 我特別喜歡它的一點是,它不僅涉及您的技術技能,還涉及您的直覺。 有時,弄清楚事情的最佳方法是做出有根據的猜測 - 與蠻力相比,它會為您節省大量時間。 最近,我聽到一家公司的故事,該公司不得不使用專有 API 並且很少或根本沒有文檔。 幾天來,他們一直在努力以未知格式解密 API 響應有效負載,然後有人決定在 url 末尾嘗試?decode=true並且他們有一個正確的 JSON。 有時,如果幸運的話,您需要做的就是美化 JSON 響應。

我編寫本教程的另一個原因是,一些公司需要很長時間才能採用用戶要求的特定功能。 您可以利用他們的私有 API 的力量並自己構建它,而不是等待它被實現。

因此,使用新的 couchsurfing.com API,我從類似的方法開始,並安裝了他們最新的 iOS 應用程序。

首先,您需要在 LAN 中設置代理,通過執行中間人攻擊 (MITM) 來偽造從應用程序到 API 的 HTTP 請求。

對於未加密的連接,攻擊非常簡單——客戶端連接到代理,然後您將傳入的請求來回中繼到目標服務器。 如有必要,您可以修改有效負載。 在公共 WLAN 中,通過冒充 WiFi 路由器來偽裝執行此操作相當容易。

對於加密連接,有一個小的區別:所有請求都是端到端加密的。 攻擊者不可能解密消息,除非他以某種方式訪問了私鑰(當然在這些交互過程中不會發送)。 話雖如此,即使 API 通信通道是安全的,端點——尤其是客戶端——也不是那麼安全。

必須滿足以下條件才能使 SSL 正常工作:

  • 服務器的證書必須使用受信任的證書頒發機構 (CA) 進行簽名
  • 服務器的公用名,在證書中,必須與服務器的域名匹配

為了克服 MITM 攻擊中的加密,我們的代理需要充當 CA(證書頒發機構)並即時生成證書。 例如,如果客戶端嘗試連接到 www.google.com,代理會動態地為 www.google.com 創建證書並對其進行簽名。 現在,客戶端認為代理實際上是 www.google.com

此圖概述了對私有 API 進行逆向工程的步驟。

為了實現用於對私有 API 進行逆向工程的嗅探代理,我將使用名為 mitmproxy 的工具。 您可以使用任何其他透明 HTTPS 代理。 Charles 是另一個具有良好 GUI 的示例。 為了完成這項工作,我們需要設置以下內容:

將手機的WiFi連接默認網關配置為代理(這樣代理在中間,所有的數據包都通過) 在手機上安裝代理的證書(這樣客戶端的信任庫中有代理的公鑰)

檢查代理的有關安裝證書的文檔。 以下是 mitmproxy 的說明。 這是 iOS 的證書 PEM 文件。

要監控截獲的 HTTP 請求,您只需啟動 mitmproxy 並從您的手機連接到它(默認端口為 8080)。

手機設置。

在您的移動瀏覽器中打開一個網站。 此時你應該可以看到 mitmproxy 中的流量了。

一旦你確認一切正常,逆向軟件工程就可以開始了。

一旦您確保一切都按計劃進行,就可以開始探索您選擇的私有 API。 基本上,此時您可以打開應用程序,使用它並了解 API 端點和請求結構。

對於如何對軟件 API 進行逆向工程,沒有嚴格的算法——大多數時候你依靠你的直覺並做出假設。

我的方法是複制 API 調用並使用不同的選項。 一個好的開始是重播您在 mitmproxy 中捕獲的請求,並查看它是否有效(按“r”重播請求)。 第一步是確定哪些標題是強制性的。 使用 mitmproxy 處理 headers 非常方便:按 'e' 進入編輯模式,然後按 'h' 修改 headers。 使用他們使用的快捷方式,vim 上癮者會感到賓至如歸。 您也可以使用 Postman 等瀏覽器擴展來測試 API,但它們往往會添加不必要的標頭,因此我建議堅持使用 mitmproxy 或 curl。

我製作了一個腳本來讀取 mitmproxy 轉儲文件並生成一個 curl 字符串 - https://gist.github.com/nderkach/bdb31b04fb1e69fa5346

讓我們從登錄時發送的請求開始。

 POST https://hapi.couchsurfing.com/api/v2/sessions ← 200 application/json 

本逆向工程教程的第一步是複制 API 調用並使用生成的選項。

我注意到的第一件事是每個請求都包含一個強制性的標頭X-CS-Url-Signature ,它每次都不同。 我還嘗試在一段時間後重播請求以檢查服務器上是否有時間戳檢查,但沒有。 接下來要做的是弄清楚這個簽名是如何計算的。

在這一點上,我決定對二進製文件進行逆向工程並找出算法。 很自然地,有了為 iPhone 開發的經驗並擁有一部 iPhone,我決定從 iPhone ipa(iPhone 應用交付)開始。 原來是解密一個,我需要一部越獄的手機。 停止! 錘子時間。

然後,我記得他們也有一個 Android 應用程序。 我有點猶豫要不要嘗試這種方法,因為我對 Android 或 Java 一無所知。 然後我認為這將是一個學習新東西的好機會。 事實證明,通過反編譯 java 字節碼比高度優化的 iphone 機器碼更容易獲得人類可讀的準源代碼。

Apk(Android 應用交付)基本上是一個 zip 文件。 您可以使用任何 zip 提取器來解壓縮其內容。 你會發現一個名為 classes.dex 的文件,它是一個 Dalvik 字節碼。 Dalvik 是一個用於在 Android 上運行翻譯後的 Java 字節碼的虛擬機。

為了將 .dex 文件反編譯為 .java 源代碼,我使用了名為 dex2jar 的工具。 該工具的輸出是一個 jar 文件,您可以使用各種工具對其進行反編譯。 您甚至可以在 Eclipse 或 IntelliJ IDEA 中打開一個 jar,它會為您完成所有工作。 這些工具中的大多數都會產生類似的結果。 我們並不關心是否可以將它編譯回來運行它,我們只是用它來分析源代碼。

這是我嘗試過的工具列表:

  • FernFlower(現在是 IntelliJ IDEA 的一部分)
  • 病死率
  • 京東圖形界面
  • 喀拉喀托
  • 南河四

CFR 和 FernFlower 對我來說效果最好。 JD-GUI 無法反編譯代碼的一些關鍵部分,並且毫無用處,而其他的質量幾乎相同。 幸運的是,Java 代碼代碼似乎沒有被混淆,但是有像 ProGuard http://developer.android.com/tools/help/proguard.html 這樣的工具可以幫助您對代碼進行反混淆。

Java 反編譯並不是這個逆向工程教程的真正範圍——關於這個主題的文章很多,所以讓我們假設你成功地反編譯和反混淆了你的 Java 代碼。

我在以下要點中結合了用於計算 X-CS-Url-Signature 的所有相關代碼:https://gist.github.com/nderkach/d11540e9af322f1c1c74

首先,我搜索了在RetrofitHttpClient中找到的X-CS-Url-Signature的提及。 一個特別的調用似乎很有趣——對EncUtils模塊。 深入研究,我意識到他們正在使用 HMAC SHA1。 HMAC 是一種消息認證代碼,它使用加密函數(在本例中為 SHA1)來計算消息的哈希值。 它用於確保完整性(即防止中間人修改請求)和身份驗證。

我們需要兩件事來計算X-CS-Url-Signature :私鑰和編碼消息(可能是 HTTP 請求負載和 URL 的一些變體)。

 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));

在代碼中a是一條消息, s是用於計算標頭a2的密鑰(對EncUtils的雙重調用僅計算 HMAC SHA1 十六進制摘要)。

找到密鑰不是問題——它以純文本形式存儲在ApiModule中,用於初始化 RetrofitHttpClient 的第二個參數。

 RetrofitHttpClient a(OkHttpClient okHttpClient) { return new RetrofitHttpClient(okHttpClient, "v3#!R3v44y3ZsJykkb$E@CG#XreXeGCh"); }

如果我們查看對EncUtils的調用,我們可以看到上面的字符串文字被逐字用作計算 HMAC 的鍵,除非定義了this.b 在後一種情況下, this.b被附加一個點。

 String s; if (this.b == null) { s = this.a; } else { s = this.a + "." + this.b; }

現在,僅通過查看代碼,我就不清楚this.b的初始化位置和方式(我唯一能發現的是它是在帶有簽名this.a(String b)的方法中調用的,但我在代碼中的任何地方都找不到對它的調用)。

 public void a(final String b) { this.b = b; }

我鼓勵你反編譯它並找出你自己:)

弄清楚消息非常簡單——在代碼中,您可以看到它是 url 路徑的串聯,即/api/v2/sessions和帶有 JSON 有效負載的字符串(如果有)。

 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; }

僅通過查看代碼,很難弄清楚 HMAC 計算的確切算法。 所以,我決定用調試符號重新構建應用程序,以弄清楚應用程序是如何工作的。 我使用了一個名為 apktool https://code.google.com/p/android-apktool/ 的工具來使用 smali https://code.google.com/p/smali/ 分解 Dalvik 字節碼。 我遵循了 https://code.google.com/p/android-apktool/wiki/SmaliDebugging 上的指南

構建 apk 後,您需要對其進行簽名並將其安裝在您的設備上。 由於我沒有Android設備,我使用了Android SDK附帶的模擬器。 通過一些勺子餵食,您可以這樣做:

 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>

我使用了一個內置的 Android 模擬器,它帶有 sdk 和一個啟用了 HAXM 的 Atom x86 虛擬映像,以確保它運行順利。

 tools/emulator -avd mydroid -no-boot-anim -cpu-delay 0

這是關於如何設置虛擬圖像的一個很好的指南:http://jolicode.com/blog/speed-up-your-android-emulator

確保您看到行HAX 正在工作並且模擬器在模擬器啟動時以快速 virt 模式運行,以確保您啟用了 HAXM。

然後,我將 apk 安裝到模擬器中並運行該應用程序。 按照 apktool 指南,我利用 IntelliJ IDEA 遠程調試器連接到模擬器並設置了一些行斷點:

一些逆向工程技術涉及運行應用程序並只是看看會發生什麼。

玩了一會兒這個應用程序,我發現用於初始化RetrofitHttpClient的私鑰用於計算登錄請求籤名的 HMAC。 在對登錄 POST 的響應中,您會收到一個用戶 ID 和 accessToken ( X-Access-Token )。 訪問令牌用於授權以下所有請求。 所有登錄後請求的 HMAC 的構造方式與登錄請求相同,只是密鑰是通過將.<user_id>附加到原始私鑰來組成的。

這顯示了對這個私有 API 進行逆向工程所需的授權過程。

獲得授權後,應用程序會發送以下請求:

 POST https://hapi.couchsurfing.com/api/v2/users/1003669205/registerDevice ← 200 application/json

正如我能夠根據經驗推斷的那樣,此請求對於身份驗證是可選的。 如果您弄清楚它的用途,則可以加分!

通過身份驗證後,您可以發送請求以獲取您(或任何其他人的用戶配置文件),如下所示:

 GET https://hapi.couchsurfing.com/api/v2/users/1003669205 ← 200 application/json 

在這個逆向工程過程中,您可以獲取任何人的用戶資料。

我沒有詳細介紹,但我注意到配置文件是使用 PUT 請求更新的。 只是為了好玩,我嘗試使用相同的請求更新另一個配置文件 - 它未經授權,因此顯然實現了安全基礎。

我編寫了一個簡單的 Python 腳本來使用您的 couchsurfing.com 憑據登錄並獲取您的用戶資料:https://gist.github.com/nderkach/899281d7e6dd0d497533。 這是 API 的 Python 包裝器:https://github.com/nderkach/couchsurfing-python,其中包含 pypi 存儲庫中可用的包(pip install couchsurfing)。

下一步

我不確定這次我要對 API 做什麼。 不再允許用戶配置文件中的 HTML 代碼,所以我將不得不想出一種不同的方法來解決舊問題。 如果有需求,我將繼續開發和增強 python API 包裝器,並假設 couchsurfing.com 不會引起太多問題。 我沒有過多地探索API,只是針對一些基本漏洞進行了測試。 它似乎足夠安全,但如果您可以訪問無法通過網站獲得的數據,將會很有趣。 無論哪種方式,現在您都可以使用我的逆向軟件工程為 Windows Phone、Pebble 或您的智能沙發構建替代客戶端。

總結一個問題

我想公開一個討論 - 為什麼不發布您的 API 並將其公開? 即使我沒有設法破解 API,仍然可以抓取網站。 它會更慢且更難維護,但他們肯定更喜歡消費者使用 API 而不是網絡爬蟲。 API 的可用性將允許第三方開發人員改進公司的產品,並圍繞它構建增值服務。 可以說維護公共 API 比維護私有 API 更昂貴。 但話又說回來,在您的產品之上構建社區服務的優勢將超過 API 維護成本。

是否可以完全阻止第三方客戶端使用私有 API? 我不這麼認為。 使用 SSL pinning 可以防止使用前面描述的簡單透明代理技術嗅探 API 請求。 最後,即使你混淆了二進製文件,有一些資源和時間的積極黑客也總是能夠對應用程序二進製文件進行逆向工程並獲得私鑰/證書。 我認為客戶端端點是安全的假設本質上是錯誤的。 API 客戶端是一個弱點。

通過保持 API 的私密性,公司基本上向用戶傳達了不信任的信息。 當然,您可以嘗試進一步保護您的私有 API。 但是,您不是更願意為 API 實現基本安全性以防止惡意使用嗎? 而是將資源集中在改進軟件以提供更好的用戶體驗上?

沙發衝浪,漂亮,請在上面加糖,打開 API。