AnyProxy 和 mitmproxy 的使用 February 28, 2020 / 7 min read
代码在这里
前言
上篇介绍了如何使用 Charles 来改写接口,达到 hack 的目的。其实针对 Charles 的抓包破解,app 可以很简单的防范住,比如客户端发送一个字符串 X,后台经过与客户端相同的算法 处理变成 Y 返回给客户端, 这时客户端判断 foo(X) === Y
即可。因为在 Charles 中只能做简单的字符替换或正则替换,比较困难的算法处理是没办法解决的。幸运的是,上篇介绍的 Sip 采用了此中防范策略,可以继续拿它来测(kai)试(dao)。
还要考虑一种情况,接口请求来判断剩余试用天数,如果试用结束,那么返回的状态信息可能会千变万化,而采用 Charles 的替换 response 是不能够满足的, 所以针对这种情况,最好能够拦截请求,直接响应返回,不经过服务器处理。
所以本篇的 AnyProxy 和 mitmproxy 就出场了。
AnyProxy
它是阿里团队写的一个基于 Node.js 的 HTTP 代理服务。
针对 HTTPS 抓包,需要下载 CA 并信任,教程在官方文档上都有,这里不多赘述。
使用方法有两种,命令行模式和 node 模式。命令行模式比较单纯,可以配置的只有几个参数:是否代理 https / 代理端口等。
所以命令行模式比较单一,只有代码模式是符合我们的需求的。
设置系统代理
抓包 app 需要配置系统的 http / https 代理,手动打开「网络」-「高级」-「代理」-「http 和 https」-「填上server 和 port」.
这样做比较呆,幸运的是 AnyProxy 提供了简便的方法:
AnyProxy.utils.systemProxyMgr. enableGlobalProxy ( ' 127.0.0.1 ' , PORT_PROXY , ' http ' )
AnyProxy.utils.systemProxyMgr. enableGlobalProxy ( ' 127.0.0.1 ' , PORT_PROXY , ' https ' )
如果它不提供也可以使用命令行来完成:
# set HTTP proxy to 127.0.0.1:8888
$ networksetup -setwebproxy Wi-Fi 127.0.0.1 8888 && networksetup -setproxybypassdomains Wi-Fi 127.0.0.1 localhost
# set HTTPS proxy to 127.0.0.1:8888
$ networksetup -setsecurewebproxy Wi-Fi 127.0.0.1 8888 && networksetup -setproxybypassdomains Wi-Fi 127.0.0.1 localhost
查看系统的代理设置:
ExceptionsList : <array> {
代码编写
const AnyProxy = require ( ' anyproxy ' )
const SIP_URL = ' https://api.sipapp.io/2.0/trial '
* beforeDealHttpsRequest ({ host , _req }) {
if (host. includes ( ' api.sipapp.io ' )) {
* beforeSendRequest ({ url , requestData }) {
const { pin , id } = JSON . parse (requestData)
const match = pin. toString (). split (). reduce (( num , str ) => str + PIN_MAP [num])
const body = JSON . stringify ({
environment : ' production ' ,
header : { ' content-type ' : ' application/json ' }
dangerouslyIgnoreUnauthorized : true
const server = new AnyProxy. ProxyServer ( OPTIONS )
server. on ( ' ready ' , () => console. log ( ' 😁 [PROXY READY] ' ))
server. on ( ' error ' , e => console. error ( ' 😭 [PROXY ERROR]: ' , e))
if ( ! AnyProxy.utils.certMgr. ifRootCAFileExists ()) {
AnyProxy.utils.systemProxyMgr. enableGlobalProxy ( ' 127.0.0.1 ' , PORT_PROXY , ' http ' )
AnyProxy.utils.systemProxyMgr. enableGlobalProxy ( ' 127.0.0.1 ' , PORT_PROXY , ' https ' )
AnyProxy.utils.systemProxyMgr. disableGlobalProxy ()
使用 Node 来执行它,就可以抓包并更改接口。它主要做了以下几件事:
初始化一个代理服务器
开启系统全局代理为当前代理服务器
设置抓包规则,开启 https 抓包
规则是: 代理服务器只处理 host 为 api.sipapp.io
的请求,当收到请求(并未发送给「真」服务器)时候做以下处理:
解析请求 body
通过算法将 pin
转成 match
, 此算法即抓包 app 采用的算法
构造返回信息进行返回
滑铁卢
在进行测试时候发现,每次打开 Sip 都会提示 Invalid JSON , 怎么修改都不能解决,经过一下午的挣扎,终于发现端倪,因为 Sip 发出的请求方法是 VIEW
, 并非传统的 POST, GET
, 应该是 AnyProxy 的 bug 导致不能处理 VIEW
的请求。
代码其实没有任何问题,只是被框架限制住了。于是再采用 mitmproxy 方法。
mitmproxy
它名字就非常形象,MITM (man-in-the-middle): 中间人代理。
废话不多讲,首先安装信任 CA. 经过简单的翻看文档,可以看到它提供三种命令:
mitmproxy 和 AnyProxy 类似
mitmdump 和 tcpdump 类似
mitmweb 提供图形界面
AnyProxy 也提供图形界面,可以在配置中打开。
因为本人的 python 水平有限,所以文档也粗略查看,不像写 js 代码,哪里不会点哪里,VSCode 会自动帮你找到你的方法对应的 ts 文件,写 python 真是煎熬,只能查文档或看源码。
from mitmproxy import http
from functools import reduce
if flow.request.pretty_url.endswith( " api.sipapp.io/2.0/trial " ):
pin = json.loads(flow.request.content)[ " pin " ]
match = reduce ( lambda x , y : x + PIN_MAP [y], str (pin), "" )
" environment " : " production " ,
" id " : json.loads(flow.request.content)[ " id " ]
flow.response = http.HTTPResponse.make(
json.dumps(body, separators = ( ' , ' , ' : ' )),
{ " content-type " : " application/json " }
简简单单一个方法,使用方法是搭配命令行:
$ mitmproxy -p 8888 -s ~/bin/hack-sip.py
打开 Sip 测试,bingo!完美 hack!
总结
这么一用就即可发现,mitmproxy 比 AnyProxy 好用,因为 AnyProxy 不支持 VIEW
的请求方法,让我 debug 半天!
最后,感谢 Sip,向开发人员致敬。
参考