本文原创作者:bt0sea
0×00、业务需求
我一直运营一个安全相关的公众号,突然有一天,一个微信公众号用户就问我是否销售位置定位软件(包括手机、微信)。可能是看到我一篇有关微信定位好友位置的 文章 ,听起来好高科技的东西,但是要实现上述功能,如果你不是国安局的,估计都没这个权利,呵呵,而且这可是犯法的事情。但是,还是很兴趣:用户到底想要什么功能,这些功能如果是你开发需要使用什么技术手段实现。
经过沟通了解到,他们是一家小额贷款公司(黑白道都做),最近有一个用户无担保借了上百万资金,快到还款日时候,人失联。后来通过寻找家属等方式,抓住关到拘留所后,得知,在短短的几个星期之内,把借来的钱挥霍一空,钱已无法追回。这件事,给他们公司敲响了警钟,一定要有一些客户资金风险预警措施,而且不能在用户手机上安装软件定位。及时挽回资金损失。
用户最关心的产品需求:
(1)微信定位 (2)身份证使用轨迹查询 (3)手机通话记录 (4)手机定位
0×01、研究方法论
(一)、微信定位好友位置
本主题简单聊一下,iphone手机图片库中看到的以时间轴为标准的照片是因为在拍摄的时候手机相机通过GPS传感器把GPS数据写入到照片文件头中。你可以通过ACDSee打开 查看All EXIF,GPS分类。如下图:
然后通过GPS数据地理位置查询网站:http://www.gpsspg.com/maps.htm 查询即可获得拍摄地点的物理位置。但是上传到微信的图片已经把EXIF中的GPS头部分的数据删除掉,所以说,无法定位。影视剧《后海不是海》中说的黑客入侵微信查到手机物理位置,我怀疑是入侵icloud账号,然后下载iphone拍摄的最近的原始照片获取GPS数据找到。
(二)、身份证使用轨迹查询
目前只有公安部才有这种权利做这样的系统,因为他可以汇聚
(1)民航飞机票大数据 (2)火车票大数据 (3)宾馆住宿登记大数据 (4)个人基本信息(家庭住址等)
然后通过汇总的大数据,通过hadoop等相关的大数据技术定制快速(一到两个小时之内)查询机制,出报表。例如:查询两个犯罪嫌疑人是否在同一个地区出现过。各方数据在同一时间段内出现的轨迹。
但是只有国家层面才能做这个事情,而且,没有刑事立案,各地公安人员也无权查询。,要想获得数据源,也只能通过第三方PMS(酒店管理系统)、12306、携程旅行网 好吧,如果你能搞定以上几家,我只能说恳求带我ZB带我飞,Pass。
12306 android APP客户端的壳是梆梆安全VIP的壳,目前公布的现有的脱壳手段都无法破解。
(三)、手机通话记录
Web网站暴力破解估计无望,转战中国联通Android APP分析,让我惊讶的是:居然没有加壳,直接反编译,还没代码混淆。。。吓的我一身冷汗。通过代理抓包分析:
POST http://m.client.10010.com/mobileService/login.htm HTTP/1.1 Host: m.client.10010.com Connection: keep-alive Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Cookie: __utma=231252639.554630269.1444024551.1444024551.1444133594.2; __utmv=231252639.Beijing; __utmz=231252639.1444024551.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _n3fa_cid=30beed4172d847c7f54b6220e7139f97; _n3fa_ext=ft=1444133601; _n3fa_lvt_a9e72dfe4a54a20c3d6e671b3bad01d9=1444133601; clientid=98|0; Hm_lvt_9208c8c641bfb0560ce7884c36938d9d=1444133601; WT_FPC=id=2cb01a11f3a259ba8521444024551272:lv=1444133683807:ss=1444133593717 Accept-Language: zh-cn Accept: */* Content-Length: 318 Connection: keep-alive User-Agent: ChinaUnicom3.x/3.1200 CFNetwork/711.5.6 Darwin/14.0.0 deviceModel=iPhone&deviceOS=8.4.1&keyVersion=1&password=424a271d262593384dffcdb5b7b9f861&deviceBrand=iphone&deviceCode=465CB728-1471-45AE-893C-D7C890DA970E&mobile=cb18958319f9f66201d89076a4601097&version=iphone_c@3.12&netWay=3G&deviceId=d96b2710aad7bbe1a432a1b9966b9be8134e3aa4b7793c9337a8bc4a191156eb&isRemberPwd=true
搜索关键字:mobileService 在package com.sinovatech.unicom.ui;发现登录函数。
private void autoLogin() { if (!SystemServiceUtils.netIsAvailable()) { enter(); return; } RequestParams localRequestParams = new RequestParams(); localRequestParams.put("mobile", EncodeUtils.hexEncode(Cryptos.aesEncrypt(this.userManager.getUserAccountName().getBytes(), EncodeUtils.hexDecode("f6b0d3f905bf02939b4f6d29f257c2ab"), EncodeUtils.hexDecode("1a42eb4565be8628a807403d67dce78d")))); localRequestParams.put("password", this.userManager.getUserPassword()); localRequestParams.put("isRemberPwd", String.valueOf(this.userManager.getAutoLoginStatus())); localRequestParams.put("keyVersion", this.userManager.getKeyVersion()); localRequestParams.put("version", getString(2131362124)); localRequestParams.put("deviceId", DeviceHelper.getDeviceID(true)); localRequestParams.put("deviceCode", DeviceHelper.getDeviceID(false)); localRequestParams.put("netWay", DeviceHelper.getNETType(getApplicationContext())); localRequestParams.put("deviceBrand", DeviceHelper.getDeviceBrand()); localRequestParams.put("deviceModel", DeviceHelper.getDeviceModel()); localRequestParams.put("deviceOS", DeviceHelper.getDeviceOSVersion()); localRequestParams.put("timestamp", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())); App.getAsyncHttpClient().post("http://m.client.10010.com/mobileService/login.htm", localRequestParams, new AsyncHttpResponseHandler()
在package com.sinovatech.unicom.common;中发现加密算法,虽然是AES但是使用硬编码做秘钥,那和使用固定编码有什么区别,代码尾部还有测试程序,那直接把函数拷贝一份就知道其手机号和密码的加解密方式,python写一个自动破解程序不是难事。
public class Cryptos { private static final String AES = "AES"; private static final String AES_CBC = "AES/CBC/PKCS5Padding"; private static final int DEFAULT_AES_KEYSIZE = 128; private static final int DEFAULT_HMACSHA1_KEYSIZE = 160; private static final int DEFAULT_IVSIZE = 16; private static final String HMACSHA1 = "HmacSHA1"; private static SecureRandom random = new SecureRandom(); private static byte[] aes(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2, int paramInt) { try { paramArrayOfByte2 = new SecretKeySpec(paramArrayOfByte2, "AES"); Cipher localCipher = Cipher.getInstance("AES"); localCipher.init(paramInt, paramArrayOfByte2); paramArrayOfByte1 = localCipher.doFinal(paramArrayOfByte1); return paramArrayOfByte1; } catch (GeneralSecurityException paramArrayOfByte1) {} return null; } private static byte[] aes(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2, byte[] paramArrayOfByte3, int paramInt) { try { paramArrayOfByte2 = new SecretKeySpec(paramArrayOfByte2, "AES"); paramArrayOfByte3 = new IvParameterSpec(paramArrayOfByte3); Cipher localCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); localCipher.init(paramInt, paramArrayOfByte2, paramArrayOfByte3); paramArrayOfByte1 = localCipher.doFinal(paramArrayOfByte1); return paramArrayOfByte1; } catch (GeneralSecurityException paramArrayOfByte1) {} return null; } public static String aesDecrypt(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2) { return new String(aes(paramArrayOfByte1, paramArrayOfByte2, 2)); } public static String aesDecrypt(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2, byte[] paramArrayOfByte3) { return new String(aes(paramArrayOfByte1, paramArrayOfByte2, paramArrayOfByte3, 2)); } public static byte[] aesEncrypt(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2) { return aes(paramArrayOfByte1, paramArrayOfByte2, 1); } public static byte[] aesEncrypt(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2, byte[] paramArrayOfByte3) { return aes(paramArrayOfByte1, paramArrayOfByte2, paramArrayOfByte3, 1); } public static byte[] generateAesKey() { return generateAesKey(128); } public static byte[] generateAesKey(int paramInt) { try { Object localObject = KeyGenerator.getInstance("AES"); ((KeyGenerator)localObject).init(paramInt); localObject = ((KeyGenerator)localObject).generateKey().getEncoded(); return (byte[])localObject; } catch (GeneralSecurityException localGeneralSecurityException) {} return null; } public static byte[] generateIV() { byte[] arrayOfByte = new byte[16]; random.nextBytes(arrayOfByte); return arrayOfByte; } public static void main(String[] paramArrayOfString) { System.out.println(EncodeUtils.hexEncode(aesEncrypt("18610676365".getBytes(), EncodeUtils.hexDecode("f6b0d3f905bf02939b4f6d29f257c2ab"), EncodeUtils.hexDecode("1a42eb4565be8628a807403d67dce78d")))); System.out.println(aesDecrypt(EncodeUtils.hexDecode("62542ddfbf62702cdac6c27c67a423af"), EncodeUtils.hexDecode("f6b0d3f905bf02939b4f6d29f257c2ab"), EncodeUtils.hexDecode("1a42eb4565be8628a807403d67dce78d"))); } }
很抱歉,你多次密码输入错误,账号已经锁定,请3小时后再试
搜索一下客户端APP源代码,没有相关的关键字,那就是服务器端判断,撞库和遍历对应手机号的服务密码这个事PASS吧。
当然可以看看是否有越权查找用户信息的情况,由于本节不是本篇文章的重点,就不多研究了,有兴趣的小伙伴可以Deep in。
public static final String MyInfo = "http://m.client.10010.com/mobileService/operationservice/getUserinfo.htm?menuId=000200020010";
(四)、手机定位
可以从以下两个方面着手
如果你是国安局,中电信、移动和联通,在设备端可以查询到其手机号连接的基站位置,然后通过三角定位的方式确定其具体的物理位置。
可以安装一个APP程序,想自己架设的web服务器报告其GPS数据。但是,目前的需求是不允许安装任何APP程序,那么,怎么办呢?
思来想去,如果可以利用现有的带有定位功能的APP程序的漏洞,获取相应的手机所对应的GPS数据。
(1)直接提供地图服务的APP程序,例如:百度地图、高德地图、腾讯地图等。 (2)嵌入地图SDK APP程序,例如:滴滴出行。
和位置定位相关的参数:包含:手机号、GPS数据(经纬度等)、IMEI(手机唯一序列号)、APN等数据是我们寻找的重点关键字。
其次、需要搜索的是和位置相关的http请求指令:location、geolocation、latitude(北纬)、看看是不是可以使用最近比较火的wormhole漏洞获取到百度地图的相关地理位置数据。
(1)geolocation 获取用户手机的GPS地理位置(城市,经度,纬度) (2)getcuid 获取imei
百度地图:v8.7.5 (37.99 MB/APK/2015-11-02/4.0及更高固件)
百度地图:v8.7.0 (37.98 MB/APK/2015-10-29/4.0及更高固件)
百度地图:v8.5.0 (35.51 MB/APK/2015-08-31/4.0及更高固件)
因为wormhole是乌云wooyun 28开始审阅的,那么估计v8.50版本还存在漏洞。
分别下载v8.5.0和v8.7.5,由于目前漏洞还没有公布,所以先对比一下相关的文件。
v8.7.5版本:
package com.baidu.hello.patch.moplus.nebulaNaNd; import android.content.Context; import android.text.TextUtils; import com.baidu.hello.patch.moplus.nebula.b.b; import com.baidu.hello.patch.moplus.nebula.b.m; import java.util.HashMap; import java.util.Map; public class e { private static final Map a = new HashMap(); private static final String b = SendIntent.class.getPackage().getName() + "."; private Context c; static { a.put("geolocation", b + "GetLocLiteString"); a.put("getsearchboxinfo", b + "GetSearchboxInfo"); a.put("getapn", b + "GetApn"); a.put("getserviceinfo", b + "GetServiceInfo"); a.put("getpackageinfo", b + "GetPackageInfo"); a.put("sendintent", b + "SendIntent"); a.put("getcuid", b + "GetCuid"); a.put("getlocstring", b + "GetLocString"); a.put("scandownloadfile", b + "ScanDownloadFile"); a.put("addcontactinfo", b + "AddContactInfo"); a.put("getapplist", b + "GetAppList"); a.put("downloadfile", b + "DownloadFile"); a.put("uploadfile", b + "UploadFile"); }
v8.5.0版本:
package com.baidu.hello.patch.moplus.nebulaNaNd; import android.content.Context; import android.text.TextUtils; import com.baidu.hello.patch.moplus.nebula.b.m; import java.util.HashMap; import java.util.Map; public class a { private static final Map a = new HashMap(); private static final String b = SendIntent.class.getPackage().getName() + "."; private Context c; static { a.put("geolocation", b + "GetLocLiteString"); a.put("getsearchboxinfo", b + "GetSearchboxInfo"); a.put("getapn", b + "GetApn"); a.put("getserviceinfo", b + "GetServiceInfo"); a.put("getpackageinfo", b + "GetPackageInfo"); a.put("sendintent", b + "SendIntent"); a.put("getcuid", b + "GetCuid"); a.put("getlocstring", b + "GetLocString"); }
////////////////////////////////
a.put("scandownloadfile", b + "ScanDownloadFile"); a.put("addcontactinfo", b + "AddContactInfo"); a.put("getapplist", b + "GetAppList"); a.put("downloadfile", b + "DownloadFile");下载任意文件到指定路径如果文件是apk则进行安装 a.put("uploadfile", b + "UploadFile");
很明显去掉以上危险API的调用。
import android.content.Context; import android.text.TextUtils; import com.baidu.hello.patch.moplus.nebula.b.m; import com.baidu.hello.patch.moplus.nebula.c.a; import com.baidu.hello.util.NoProGuard; import com.baidu.loc.strWebApp.BDLocManager; import java.util.Map; import org.json.JSONException; import org.json.JSONObject; public class GetLocString implements b, NoProGuard { private static final boolean DEBUG = false; public static final int ERROR_FAIL = 1; private static final String TAG = "getLocString"; Context mContext = null; private int mErrcode = -1; public com.baidu.hello.patch.moplus.nebula.b.b execute(m paramm, Map paramMap1, Map paramMap2, Map paramMap3) { if ((paramMap2 == null) || (paramMap2.size() < 1)) {} do { return null; paramMap2 = (String)paramMap2.get("callback"); this.mContext = a.a().b(); } while (this.mContext == null); paramm = (String)paramMap1.get("referer"); if (!com.baidu.hello.patch.moplus.nebula.c.b.a(this.mContext).a(paramm)) { this.mErrcode = 4; } if (this.mErrcode != 4) { this.mErrcode = 1; } for (paramm = new BDLocManager(this.mContext).getLocString();; paramm = null) { paramMap1 = new JSONObject(); try { if (!TextUtils.isEmpty(paramm)) { paramMap1.put("locstring", paramm); this.mErrcode = 0; } paramMap1.put("error", this.mErrcode); } catch (JSONException paramm) { for (;;) {} } paramm = paramMap1.toString(); if (paramMap2 != null) { paramm = paramMap2 + " && " + paramMap2 + "(" + paramm + ");"; return new com.baidu.hello.patch.moplus.nebula.b.b(paramm); } } } }
http://127.0.0.1:6259/{key:value}&&callback(locstring)
大致的流程是:
获取IEMI收据或者手机号码,还有wifi热点相关信息(由于可能存在多个手机卡的情况,使用json的方式转发),经过加密后传给本地nano http server,然后返回html方式的位置参数给请求方。
由于漏洞的整体细节还没有公布,我就说到这里吧。
0×02、结论
(1)泄露手机地理数据信息,间接的泄露拿手机人的物理位置。各个地图厂商提供的地图APP和SDK这个需要大家重视。 (2)还有这次百度系APP事件告诉我们,APK安全一定要做好呀,否则人家迟早会挖到你的漏洞。 (3)做为信息安全工作者,如果你要是拿黑客技术去做坏事,那么,警务千度在那里等着你。
* 本文作者:bt0sea,本文属FreeBuf原创奖励计划,未经许可禁止转载