当前位置: 首页 > news >正文

WiFi万能钥匙蹭网原理详细剖析

路人甲 · 2015/02/26 17:00

0x00 wifi万能钥匙究竟有没有获取root之后偷偷上传密码?


本次测试版本号为3.2.3,首先通过可疑shell语句定位到疑问的问题代码:类名com.snda.wifilocating.f.ba

这段代码的作用是在有了root权限的情况下 将系统的wifi.conf拷贝出来到应用自己的目录,并赋予其全局可读写权限(其实这是个漏洞了...)。

对其做cross-ref查找引用之后可以发现,该函数主要在两个地方被直接调用。一个是com.snda.wifilocating.e.av:

这是一个api接口,主要功能是用于用户注册了之后备份自己的ap密码,同时在 WpaConfUploadActivity直接调用、GetBackupActivity中间接调用。第一个Activity在分析的版本中已经被从AndroidManifest中删除,而第二个Activity则是用户备份私有wifi时的对应的界面。这证实了备份的时候密码确实会被上传,而且从下文来看这个密码是完全可逆的。

不过在使用过程中,该应用并没有其他可疑的root行为操作。笔者打开了SuperSu的root执行监控,短暂的使用过程中也只发现了执行了上述的这一条命令。

0x01 Android系统Wifi连接API概述


Android系统通过WifiManager类来提供对Wifi的扫描、连接接口。应用在请求相应权限之后可以扫描、连接、断开无线等。在连接无线功能中,客户端基本上只要指定SSID,Pre-shared-key(即密码),就可以用代码的方式连接无线。连接一个WPA(2)无线典型代码如下,

#!bash
wifiConfiguration.SSID = "\"" + networkSSID + "\"";
wifiConfiguration.preSharedKey = "\"" + networkPass + "\"";
wifiConfiguration.hiddenSSID = true;
wifiConfiguration.status = WifiConfiguration.Status.ENABLED;
wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);

int res = wifiManager.addNetwork(wifiConfiguration);
Log.d(TAG, "### add Network returned " + res);
复制代码

0x02 wifi万能钥匙是怎么连接上无线的,密码从哪里来?


这也是争议较大的地方,首先该应用肯定是有云端存储了很多密码,因为应用会引导用户备份自己的密码,但这些密码有没有被滥用我们在客户端就不得而知了。在2月底的这次测试中,笔者先私有备份了自己建立的测试无线(注意不是分享),然后使用另外一个手机安装该客户端测试,该客户端的API请求接口并没有返回这个测试的无线的密码。不过这也可能只是个例说明不了什么,还是建议各位自行测试,但注意测试前清除保存的无线并给测试无线设定一个弱密码以免真的泄露了自己的密码。

无线密码获取分析


回到正题,笔者通过代理拦截到了该应用获取wifi密码的请求。应用发送目标的ssid,mac信息向云端做查询,获取到的密码到本地之后并不是明文的,而是一个AES加密。首先为了证明其在本地最终还是会以明文出现,先取了个巧,没有去逆这个算法(虽然逆下也不会很困难),而是直接hook了系统添加无线的代码(回忆上文里密码就在NetworkConfiguration.preSharedKey里)。

部分HOOK代码:

#!java
            Class wifimgr = XposedHelpers.findClass(
                    "android.net.wifi.WifiManager",
                    lpparam.classLoader);
            XposedBridge.hookAllMethods(wifimgr, "addNetwork",
                    new XC_MethodHook() {

                        @Override
                        protected void beforeHookedMethod(MethodHookParam param)
                                throws Throwable {
                            WifiConfiguration configuration = (WifiConfiguration) param.args[0];
                            if(configuration.preSharedKey != null)
                            {

                                Log.e("FUCKFUCK", "psk: "+configuration.preSharedKey);
                            }
                        }
                    });

            XposedBridge.hookAllMethods(wifimgr, "updateNetwork",
                    new XC_MethodHook() {

                        @Override
                        protected void beforeHookedMethod(MethodHookParam param)
                                throws Throwable {
                            WifiConfiguration configuration = (WifiConfiguration) param.args[0];
                            if(configuration.preSharedKey != null)
                            {

                                Log.e("FUCKFUCK", "psk: "+configuration.preSharedKey);
                            }
                        }
                    });
            }
复制代码

这是一个万能钥匙上传wifi ssid以及mac以请求密码的截图:

响应截图:

密码以AES可逆加密的形式通过pwd这个json key传递了回来。

同时,在其尝试通过这个密码连接目标无线的时候,本地hook模块也获取到了真实的明文密码:

个人备份分析


而个人备份模块,也就是直接会读取wifi.conf的模块,是通过findprivateap和saveprivateap这两个json api method进行,具体的http请求逻辑在com.snda.wifilocating.e.av中可以找到,这个类也基本上囊括了所有万能钥匙的api请求逻辑。

备份时的请求:把整个wifi.conf全部上传了上去。

而恢复备份时,只是将密码从云端拖了下来。

其他连接方式分析


除此之外,Wifi万能钥匙还自带了2000条的数据库记录在ap8.db中,记录了常见的弱密码。 例如

这些密码用在所谓的“深度连接”功能中,其实按代码逻辑来看就是一个wifi密码爆破,每次在字典中尝试10个密码。看下logcat就很明显。

#!bash
I/wpa_supplicant( 884): wlan0: WPA: 4-Way Handshake failed - pre-shared key may be incorrect
I/wpa_supplicant( 884): wlan0: CTRL-EVENT-SSID-TEMP-DISABLED id=1 ssid="aaaaaaaaa" auth_failures=2 duration=20
D/SupplicantStateTracker( 818): Failed to authenticate, disabling network 1
I/wpa_supplicant( 884): wlan0: CTRL-EVENT-SSID-REENABLED id=1 ssid="aaaaaaaaa"
I/wpa_supplicant( 884): wlan0: Trying to associate with 5c:a4:8a:4d:09:a0 (SSID='aaaaaaaaa' freq=2412 MHz)
I/wpa_supplicant( 884): wlan0: Associated with 5c:a4:8a:4d:09:a0
I/wpa_supplicant( 884): wlan0: CTRL-EVENT-DISCONNECTED bssid=5c:a4:8a:4d:09:a0 reason=23
I/wpa_supplicant( 884): wlan0: CTRL-EVENT-SSID-TEMP-DISABLED id=1 ssid="aaaaaaaaa" auth_failures=1 duration=10
I/wpa_supplicant( 884): wlan0: WPA: 4-Way Handshake failed - pre-shared key may be incorrect
I/wpa_supplicant( 884): wlan0: CTRL-EVENT-SSID-TEMP-DISABLED id=1 ssid="aaaaaaaaa" auth_failures=2 duration=20
I/wpa_supplicant( 884): wlan0: CTRL-EVENT-SSID-REENABLED id=1 ssid="aaaaaaaaa"
I/wpa_supplicant( 884): wlan0: Trying to associate with 5e:aa:aa:aa:aa:aa (SSID='aaaaaaaaa' freq=2462 MHz)
I/wpa_supplicant( 884): wlan0: Associated with 5e:aa:aa:aa:aa:aa
D/dalvikvm(13893): GC_CONCURRENT freed 356K, 4% free 18620K/19220K, paused 9ms+2ms, total 29ms
I/wpa_supplicant( 884): wlan0: CTRL-EVENT-DISCONNECTED bssid=5e:aa:aa:aa:aa:aa reason=23
I/wpa_supplicant( 884): wlan0: CTRL-EVENT-SSID-TEMP-DISABLED id=1 ssid="aaaaaaaaa" auth_failures=1 duration=10
I/wpa_supplicant( 884): wlan0: WPA: 4-Way Handshake failed - pre-shared key may be incorrect
I/wpa_supplicant( 884): wlan0: CTRL-EVENT-SSID-TEMP-DISABLED id=1 ssid="aaaaaaaaa" auth_failures=2 duration=20
复制代码

Wifi密码加解密分析


当然真正去逆向加密代码也不是很困难,简单的搜寻即可得到解密代码:(部分直接从反编译的代码中抠出,风格未做修饰)

#!java
public class AESFun {

      String a =//略去;
      String b = //略去;
      String c = //略去;

      Cipher cipher;
      IvParameterSpec spec;
      SecretKeySpec secretKeySpec;
      void init() throws NoSuchAlgorithmException, NoSuchPaddingException {
            spec = new IvParameterSpec(b.getBytes());
            secretKeySpec = new SecretKeySpec(a.getBytes(), "AES");
            cipher = Cipher.getInstance("AES/CBC/NoPadding");
      }

      public final String b(String arg7) throws Exception {
        byte[] array_b1;
        byte[] array_b = null;
        int i = 2;
        String string = null;
        {
            try {
                this.cipher.init(2, secretKeySpec, spec);
                Cipher cipher = this.cipher;
                if(arg7 != null && arg7.length() >= i) {
                    int i1 = arg7.length() / 2;
                    array_b = new byte[i1];
                    int i2;
                    for(i2 = 0; i2 < i1; ++i2) {
                        String string1 = arg7.substring(i2 * 2, i2 * 2 + 2);
                        array_b[i2] = ((byte)Integer.parseInt(string1, 0x10));
                    }
                }

                array_b1 = cipher.doFinal(array_b);
            }
            catch(Exception exception) {
                StringBuilder stringBuilder = new StringBuilder("[decrypt] ");
                string = exception.getMessage();
                StringBuilder stringBuilder1 = stringBuilder.append(string);
                string = stringBuilder1.toString();
                exception.printStackTrace();
                throw new Exception(string);
            }

            string = new String(array_b1);
        }

        return string;
    }
复制代码

将API请求中获取的16进制pwd字段代入解密程序,得到的结果是如下格式:[length][password][timestamp]的格式,如下图所示,中间就是目标无线明文密码。

此外接口请求中有一个sign字段是加签,事实上是把请求参数合并在一起与预置的key做了个md5,细节就不赘述了。这两个清楚了之后其实完全可以利用这个接口实现一个自己的Wifi钥匙了。

0x03 总结


此版本的WiFi万能钥匙不会主动把root之后手机保存的无线密码发向云端但在做备份操作(安装时默认勾选自动备份)时会发送,当有足够的用户使用该应用时,云端就拥有了一个庞大的WiFi数据库,查询WiFi的密码时,应用会发送目标的ssid,mac信息向云端做查询,获取到的密码到本地之后并不是明文的,而是一个AES加密,本地解密后连接目标WiFi。同时内置了常见的2000条WiFi弱口令,在云端没有该WiFi密码的时候,可以尝试爆破目标的密码。

相关文章:

  • V8 的 Error 对象与栈追踪的妙用
  • VR直播很火,但能取代传统电视直播吗?
  • Hibernate最全面试题
  • Python_网络编程
  • JDK1.7版本中的HashMap
  • 数字图像处理---直方图均衡化
  • Chef在InSpec 2.0增强了云安全的自动化功能
  • Kafka源码分析Consumer
  • cad提供的坐标转换
  • Java锁--Semaphore
  • Win8 Metro(C#)数字图像处理--2.43图像马赛克效果算法
  • PowerDesigner 概念数据模型(CDM)
  • [Contest20180313]灵大会议
  • Windows Developer Day - Adaptive Cards
  • 乘法逆元模板(Orz尧神)
  • 77. Combinations
  • CSS 三角实现
  • C语言笔记(第一章:C语言编程)
  • Git同步原始仓库到Fork仓库中
  • Gradle 5.0 正式版发布
  • IndexedDB
  • JS笔记四:作用域、变量(函数)提升
  • leetcode386. Lexicographical Numbers
  • Mocha测试初探
  • mysql外键的使用
  • Nacos系列:Nacos的Java SDK使用
  • PHP变量
  • Rancher-k8s加速安装文档
  • REST架构的思考
  • Shadow DOM 内部构造及如何构建独立组件
  • 从0实现一个tiny react(三)生命周期
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 好的网址,关于.net 4.0 ,vs 2010
  • 计算机在识别图像时“看到”了什么?
  • 前嗅ForeSpider教程:创建模板
  • 浅谈web中前端模板引擎的使用
  • 为什么要用IPython/Jupyter?
  • 最简单的无缝轮播
  • PostgreSQL之连接数修改
  • 带你开发类似Pokemon Go的AR游戏
  • 新海诚画集[秒速5センチメートル:樱花抄·春]
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #13 yum、编译安装与sed命令的使用
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • #stm32整理(一)flash读写
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (二)丶RabbitMQ的六大核心
  • (二十四)Flask之flask-session组件
  • (附源码)python房屋租赁管理系统 毕业设计 745613