作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
尼古拉·德卡奇的头像

Nikolay Derkach

尼古拉(MSc)的职业生涯始于在谷歌的实习, worked full-stack, built iOS apps, 现在喜欢帮助创业公司推出mvp.

Previously At

Amadeus
Share

旅行是我的爱好,我是Couchsurfing的忠实粉丝. Couchsurfing是一个全球性的旅行者社区, 你在哪里可以找到住宿的地方,或者与其他旅行者分享你自己的家. On top of that, 沙发客帮你享受真正的旅行体验,同时与当地人互动. 我加入Couchsurfing社区已经有3年多了. 我一开始只是参加聚会,后来我终于能够主持会议了. 这是一次多么奇妙的旅行啊! 我遇到了很多来自世界各地的了不起的人,交了很多朋友. 这整个经历真的改变了我的生活.

I’ve hosted a lot of travelers 我自己,比我实际冲浪的次数要多. 我住在法国里维埃拉的主要旅游胜地之一, 我收到了大量的沙发请求(旺季时每天多达10个)。. As a 自由后端开发人员,我立刻注意到沙发客的问题.Com网站的一个问题是,它并没有真正正确地处理这种“高负载”的情况. 没有关于你的沙发可用性的信息-当你收到一个新的沙发请求时,你不能确定你当时是否已经接待了某人. 应该有一个可视化的表示您已接受的和待处理的请求, 这样你就能更好地管理它们. 此外,如果您可以公开您的沙发可用性,您可以避免不必要的沙发请求. 为了更好地理解我的想法,可以看看Airbnb的日历.

许多公司因不听取用户意见而臭名昭著. 了解Couchsurfing的历史,我不能指望他们很快实现这个功能. 自从该网站成为一家以营利为目的的公司以来,社区就每况愈下. 为了更好地理解我所说的,我建议阅读这两篇文章:

我知道很多社区成员会很高兴拥有这个功能. 所以,我决定做一个应用来解决这个问题. 事实证明,没有公开的Couchsurfing API可用. 以下是我从他们的支持团队收到的回复:

“不幸的是,我们不得不告诉你,我们的API实际上并不是公开的,目前也没有计划将其公开.”

闯入我的沙发

是时候使用一些我最喜欢的软件逆向工程技术来破解Couchsurfing了.com. 我假设他们的移动应用必须使用某种API来查询后端. 所以,我必须拦截来自移动应用程序到后端的HTTP请求. 为此,我在本地网络中设置了一个代理, 并将我的iPhone连接起来拦截HTTP请求. This way, 我能够找到他们私有API的访问点,并找出他们的JSON有效负载格式.

最后,我创建了一个网站,目的是帮助人们管理他们的沙发需求, 并向冲浪者展示沙发可用日历. 我在社区论坛上发布了一个链接(在我看来,社区论坛也很分散), 而且很难在那里找到信息). 反响大多是积极的, 虽然有些人不喜欢这个网站需要沙发客的想法.Com证书,这真的是一个信任问题.

这个网站是这样运作的:你用你的沙发客登录这个网站.com credentials, 点击几下后,你就可以得到HTML代码,你可以把它嵌入到你的沙发冲浪中.Com配置文件,瞧,你有一个自动更新的日历在你的个人资料. 下面是日历的截图,下面是我制作日历的文章:

Example calendar

我为Couchsurfing创建了一个很棒的功能, 我很自然地认为他们会欣赏我的工作——甚至可能在他们的开发团队中给我一个职位. 我发了一封电子邮件给 (在)couchsurfing工作.com 附上网站链接,我的简历和推荐信. 我的一位沙发客留下的感谢信

Thank you note.

几天后,他们跟进了我的逆向工程工作. 在答复中很明显,他们唯一关心的是他们自己的安全, 所以他们让我撤下我写的关于API的博文, 最后是网站. 我已经立即删除了那些帖子, 因为我的意图不是违反使用条款和获取用户凭证, 而是为了帮助沙发客社区. 我有一种感觉,我被当作罪犯对待, 公司只关注我的网站需要用户凭证这一事实.

我提议免费向他们提供我的应用. 他们可以将其托管在自己的环境中,并通过Facebook身份验证进行连接. 毕竟,这是一个伟大的特性,社区需要它. 以下是我收到的最终解决方案:

“假期过后,我们又回到了这里的生活节奏,我们想继续跟进.

我们已经对你们的应用程序进行了一些内部讨论,讨论了我们如何既尊重它所展示的创造力和主动性,又不会在Couchsurfing用户向第三方网站输入凭据时潜在地损害用户数据的隐私和安全.

日历显然填补了我们网站上的一个功能空白, 这个功能是我们正在开发的一个更大项目的一部分.

但收集用户名和密码的问题依然存在. 我们无法想出一种简单的方法来设置它,这样我们就可以在不允许您访问数据或将您的网站视为我们的工作产品的情况下托管或支持它.

当前可用的API很快将被需要访问它的应用程序进行身份验证/授权的版本所取代.”

今天,当我在写这个逆向工程软件教程的时候(事件发生一年后), Couchsurfing上还没有实现日历功能.

回归纯真-再次入侵我的沙发

几周前,我受到启发,写了一篇关于私有api逆向工程技术的文章. Naturally, 我决定总结一下我之前写的关于这个话题的文章, 再添加一些细节. 当我开始写这篇新文章时,我想用一个最新的API展示逆向工程过程,并对API黑客进行另一个研究. 根据我以前的经验, Couchsurfing最近发布了一款全新的网站和移动应用 http://blog.couchsurfing.com/the-future-of-couchsurfing-is-on-the-way/,我决定再次破解他们的API.

我为什么要做这个逆向工程过程? 首先,对软件进行逆向工程是很有趣的. 我特别喜欢它的地方, 这不仅仅涉及到你的技术能力吗, 还有你的直觉. Sometimes, 解决问题的最佳方法是进行有根据的猜测——与暴力破解相比,这将为您节省大量时间. 最近,我听到了一个来自一家公司的故事,该公司不得不使用专有api,几乎没有文档. 几天来,他们一直在努力以一种未知的格式解密API响应负载, 然后有人决定试一试 ?decode=true 在url的末尾,他们有一个正确的JSON. 有时候,如果你很幸运,你所需要做的就是 美化JSON响应.

我编写本教程的另一个原因是,一些公司需要很长时间才能采用用户要求的特定功能. 而不是等待它被实现, 您可以利用其私有API的强大功能并自己构建它.

所以,有了新的沙发客.我采用了类似的方法,安装了他们最新的iOS应用.

First, 你需要在局域网中设置一个代理,通过执行中间人攻击(MITM)来伪造来自应用程序到API的HTTP请求。.

对于未加密的连接,攻击非常简单——客户端连接到代理,然后将传入的请求来回转发到目标服务器. 如果有必要,您可以修改有效载荷. 在公共WLAN中,通过伪装WiFi路由器来执行此操作相当容易.

对于加密连接,有一个小小的区别:所有请求都是端到端加密的. 攻击者不可能解密该消息, 除非他以某种方式获得了私钥的访问权(当然,在这些交互过程中不会发送私钥). Having said that, 即使API通信通道是安全的, 端点——尤其是客户端——并不是那么安全.

为了使SSL正常工作,必须满足以下条件:

  • 服务器的证书必须由受信任的证书颁发机构(CA)签名。
  • 证书中服务器的通用名称必须与服务器的域名相匹配

克服MITM攻击中的加密, 我们的代理需要充当CA(证书颁发机构)并动态生成证书. 例如,如果客户端试图连接到www.google.代理动态地为WWW创建证书.google.com and signs it. 现在,客户端认为代理实际上是www.google.com

此图概述了对私有API进行反向工程的步骤.

要实现用于对私有API进行反向工程的嗅探代理,我将使用名为 mitmproxy. 您可以使用任何其他透明HTTPS代理. Charles是另一个具有良好GUI的例子. 要做到这一点,我们需要设置以下内容:

将手机的WiFi连接默认网关设置为代理(这样代理就在中间,所有数据包都可以通过) 在电话上安装代理的证书(以便客户端在其信任存储区中拥有代理的公钥)

检查代理的关于安装证书的文档. Here 是关于mitmproxy的说明吗. And here 是iOS的证书PEM文件.

监控被截获的HTTP请求, 您只需启动mitmproxy并从您的手机连接到它(默认端口是8080).

手机设置.

在手机浏览器中打开一个网站. 此时,您应该能够看到mitmproxy中的流量.

一旦确认一切正常,逆向软件工程就可以开始了.

一旦你确保一切按计划进行, 是时候开始探索您选择的私有API了. Basically, 此时,您可以打开应用程序, 试用一下,了解一下API端点和请求结构.

对于如何对软件API进行逆向工程,没有严格的算法——大多数情况下,你依赖于你的直觉和假设.

我的方法是复制API调用并使用不同的选项. 一个好的开始是重放在mitmproxy中捕获的请求, 并看看它是否工作(按' r '重放请求). 第一步是确定哪些头文件是必须的. 使用mitmproxy处理头文件非常方便:按' e '进入编辑模式, 然后' h '来修改头文件. 有了他们使用的快捷方式,vim成瘾者会有宾至如归的感觉. 你也可以使用像Postman这样的浏览器扩展来测试API, 但它们往往会添加不必要的标题, 所以我建议坚持使用mitmproxy或curl.

我编写了一个脚本,读取mitmproxy转储文件并生成一个curl字符串- http://gist.github.com/nderkach/bdb31b04fb1e69fa5346

让我们从登录时发送的请求开始.

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

本逆向工程教程的第一步是复制API调用并使用生成的选项.

我注意到的第一件事是,每个请求都包含一个强制头 X-CS-Url-Signature 每次都有什么不同. 我还尝试在一段时间后重放请求,以检查服务器上是否有时间戳检查, and there is none. 接下来要做的是弄清楚这个签名是如何计算出来的.

在这一点上,我决定逆向工程二进制并找出算法. Naturally, 我有开发iPhone的经验,而且我有一部iPhone, 我决定从iPhone ipa (iPhone应用交付)开始。. 要解密,我需要一部越狱手机. Stop! Hammer Time.

然后,我想起他们也有一个Android应用程序. 我对尝试这种方法有点犹豫,因为我对Android或Java一无所知. 然后我想这将是一个学习新东西的好机会. 事实证明,通过反编译java字节码来获得人类可读的准源代码比大量优化的iphone机器码更容易.

Apk (Android应用交付)基本上是一个zip文件. 您可以使用任何zip解压程序来解压缩其内容. 您将找到一个名为classes的文件.这是一个Dalvik字节码. Dalvik是一个用于在Android上运行翻译后的Java字节码的虚拟机.

To decompile the .dex file into .我使用了名为dex2jar的工具. 该工具的输出是一个jar文件,您可以使用各种工具对其进行反编译. 您甚至可以在Eclipse或IntelliJ IDEA中打开一个jar,它将为您完成所有工作. 大多数这些工具都会产生类似的结果. 我们并不关心是否可以编译回运行它, 我们只是用它来分析源代码.

以下是我尝试过的一些工具:

CFR和FernFlower最适合我. JD-GUI无法反编译代码的某些关键部分,因此毫无用处, 而其他的质量差不多. 幸运的是,Java代码似乎没有被混淆,但是有像ProGuard这样的工具 http://developer.android.com/tools/help/proguard.html 来帮助您消除代码的混淆.

Java反编译并不是这个逆向工程教程的真正范围——有很多关于这个主题的文章, 因此,让我们假设您成功地反编译和解混淆了Java代码.

我在以下要点中结合了所有用于计算X-CS-Url-Signature的相关代码: http://gist.github.com/nderkach/d11540e9af322f1c1c74

首先,我搜索了提到 X-CS-Url-Signature,我在 RetrofitHttpClient. 一个特别的电话似乎很有趣 EncUtils module. 深入研究后,我意识到他们使用的是HMAC SHA1. HMAC是一种消息认证代码,它使用加密函数(在本例中为SHA1)来计算消息的哈希值. 它用于确保完整性(例如.e. 防止中间的人修改请求和身份验证.

我们需要两样东西来计算 X-CS-Url-Signature:私钥和编码的消息(可能是HTTP请求有效负载和URL的一些变体).

最终字符串a2 = EncUtils.a(EncUtils.a(a, s));
 
final ArrayList
list = new ArrayList
(request.getHeaders()); list.add(new Header("X-CS-Url-Signature", a2));

In the code a is a message and s 是用来计算头的键吗 a2 对……的双重呼唤 EncUtils 只是计算一个HMAC SHA1十六进制摘要).

查找密钥不是问题-它以纯文本形式存储在 ApiModule,用于初始化RetrofitHttpClient的第二个参数.

RetrofitHttpClient a(OkHttpClient OkHttpClient) {
    返回新的RetrofitHttpClient(okHttpClient,“v3#”)!R3v44y3ZsJykkb E@CG #美元XreXeGCh”);
}

如果我们看一下呼叫 EncUtils, 我们可以看到上面的字符串字面值被一字不差地用作计算HMAC的键, 除非在这种情况下 this.b is defined. 在后一种情况下, 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路径的连接, i.e. /api/v2/sessions 和一个带有JSON负载的字符串(如果有的话).

最终字节[]b = this.b(request.getUrl());
byte[] a;
if (request.getBody() != null && request.getBody()实例的JsonTypedOutput) {
    System.out.println("body");
    // this.A (x, y)连接字节数组  
    a = this.(b) ((JsonTypedOutput)请求.getBody()).a);
}
else {
    a = b;
}

仅通过查看代码,很难找出HMAC计算的确切算法. 所以,我决定用调试符号重新构建应用程序,以弄清楚应用程序是如何工作的. 我使用了一个叫做apktool的工具 http://code.google.com/p/android-apktool/ 使用small来反汇编Dalvik字节码 http://code.google.com/p/smali/. 我跟着向导走 http://code.google.com/p/android-apktool/wiki/SmaliDebugging

构建apk后,您需要签名并将其安装到设备上. 因为我没有Android设备,所以我使用了Android SDK附带的模拟器. 通过一些填鸭式的灌输,你可以这样做:

jsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android  androiddebugkey

jarsigner -verify -verbose -certs 

zipalign -v 4  

我使用了一个内置的Android模拟器,该模拟器附带sdk和一个启用了HAXM的Atom x86虚拟映像,以确保其顺利运行.

Tools /emulator -avd mydroid -no-boot- animm -cpu-delay 0

下面是关于如何设置虚拟映像的一个很好的指南: http://jolicode.com/blog/speed-up-your-android-emulator

一定要看到线 HAX是工作和模拟器运行在快速虚拟模式 在模拟器启动时确保已启用HAXM.

然后,我将apk安装到模拟器中并运行应用程序. 遵循apktool指南, 我利用IntelliJ IDEA远程调试器连接到模拟器并设置一些行断点:

一些逆向工程技术包括运行应用程序,看看会发生什么.

玩了一会儿应用, 我能够找出用于初始化的私钥 RetrofitHttpClient 用于计算登录请求签名的HMAC. 在对login POST的响应中,您将收到一个用户ID和accessToken (X-Access-Token). 访问令牌用于对以下所有请求进行授权. 所有post登录请求的HMAC的构造方式与登录请求相同, 除了键是通过追加组成的 . 到原始私钥.

这显示了反向工程此私有API所需的授权过程.

一旦你被授权,应用程序发送以下请求:

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

根据我的经验推断,这个请求对于身份验证是可选的. 如果你知道它的用途,那就加分了!

一旦通过认证,你可以发送一个请求来获取你的(或任何人的)用户配置文件,如下所示:

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

在这个逆向工程过程中,您可以获取任何人的用户配置文件.

我没有详细说明,但我注意到配置文件是用PUT请求更新的. Just for fun, 我尝试用相同的请求更新另一个配置文件-它没有被授权, 显然基本的安全措施已经实施了.

我编写了一个简单的Python脚本来使用您的couchsurfing登录.Com凭据并获取您的用户配置文件: http://gist.github.com/nderkach/899281d7e6dd0d497533. 以下是该API的Python包装器: http://github.com/nderkach/couchsurfing-python 使用pypi存储库中可用的包(pip install couchsurfing).

Next Steps

我不确定这次我要用API做什么. 用户配置文件中的HTML代码不再被允许, 所以我必须想出一个不同的方法来解决这个老问题. 我将继续开发和增强python API包装器, 如果有需求的话, 假设沙发客.Com不会造成太多的问题. 我没有过多地研究API,只是测试了它的一些基本漏洞. 看起来很安全, 但是,如果你能通过网站获得这些数据,那将是一件很有趣的事情. Either way, 现在你可以使用我的反向软件工程来为Windows Phone构建一个替代客户端, Pebble, 或者你的智能沙发.

用一个问题来结束

有一个讨论我想打开-为什么不发布你的API并使其公开? 即使我没有设法破解API,仍然有可能抓取网站. 它会更慢,更难以维护, 但他们肯定更希望消费者使用API而不是web scraper. api的可用性将允许第三方开发人员改进公司的产品, 并以此为基础建立增值服务. One can make an argument that it would be more expensive to maintain the public API rather than a private one; but then again, 在产品之上构建社区服务的优势将超过API维护成本.

是否有可能完全阻止第三方客户端使用私有API? I don’t think so. 使用SSL绑定可以防止使用前面描述的简单透明代理技术嗅探API请求. In the end, 即使你混淆了二进制文件, 一个有一些资源和时间的黑客总是能够对应用程序二进制进行反向工程,并获得私钥/证书. 我认为客户机端点是安全的这种假设本质上是错误的. An API 客户端是一个弱点.

通过将API保密,公司基本上是在向用户传递不信任的信息. 当然,您可以尝试进一步保护您的私有API. However, wouldn’t you rather implement a basic security for the API to prevent malicious usage; and instead focus your resources on improving the software to provide a better user experience?

沙发客,拜托,加糖,打开API.

就这一主题咨询作者或专家.
Schedule a call
尼古拉·德卡奇的头像
Nikolay Derkach

Located in 加拿大蒙特利尔,QC

Member since December 16, 2013

About the author

尼古拉(MSc)的职业生涯始于在谷歌的实习, worked full-stack, built iOS apps, 现在喜欢帮助创业公司推出mvp.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Previously At

Amadeus

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.