WiFi萬能鑰匙蹭網原理詳細剖析

wyzsk發表於2020-08-19
作者: 路人甲 · 2015/02/26 17:00

0x00 wifi萬能鑰匙究竟有沒有獲取root之後偷偷上傳密碼?


本次測試版本號為3.2.3,首先透過可疑shell語句定位到疑問的問題程式碼:類名com.snda.wifilocating.f.ba

Alt text

這段程式碼的作用是在有了root許可權的情況下 將系統的wifi.conf複製出來到應用自己的目錄,並賦予其全域性可讀寫許可權(其實這是個漏洞了...)。

對其做cross-ref查詢引用之後可以發現,該函式主要在兩個地方被直接呼叫。一個是com.snda.wifilocating.e.av:

Alt text

這是一個api介面,主要功能是用於使用者註冊了之後備份自己的ap密碼,同時在Alt text WpaConfUploadActivity直接呼叫、GetBackupActivity中間接呼叫。第一個Activity在分析的版本中已經被從AndroidManifest中刪除,而第二個Activity則是使用者備份私有wifi時的對應的介面。這證實了備份的時候密碼確實會被上傳,而且從下文來看這個密碼是完全可逆的。

不過在使用過程中,該應用並沒有其他可疑的root行為操作。筆者開啟了SuperSu的root執行監控,短暫的使用過程中也只發現了執行了上述的這一條命令。

Alt text

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以請求密碼的截圖:

Alt text

響應截圖:

Alt text

密碼以AES可逆加密的形式透過pwd這個json key傳遞了回來。

同時,在其嘗試透過這個密碼連線目標無線的時候,本地hook模組也獲取到了真實的明文密碼:

Alt text

個人備份分析


而個人備份模組,也就是直接會讀取wifi.conf的模組,是透過findprivateap和saveprivateap這兩個json api method進行,具體的http請求邏輯在com.snda.wifilocating.e.av中可以找到,這個類也基本上囊括了所有萬能鑰匙的api請求邏輯。

備份時的請求:把整個wifi.conf全部上傳了上去。 Alt text

而恢復備份時,只是將密碼從雲端拖了下來。

其他連線方式分析


除此之外,Wifi萬能鑰匙還自帶了2000條的資料庫記錄在ap8.db中,記錄了常見的弱密碼。 Alt text 例如 Alt text

這些密碼用在所謂的“深度連線”功能中,其實按程式碼邏輯來看就是一個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]的格式,如下圖所示,中間就是目標無線明文密碼。

Alt text

此外介面請求中有一個sign欄位是加簽,事實上是把請求引數合併在一起與預置的key做了個md5,細節就不贅述了。這兩個清楚了之後其實完全可以利用這個介面實現一個自己的Wifi鑰匙了。

0x03 總結


此版本的WiFi萬能鑰匙不會主動把root之後手機儲存的無線密碼發向雲端但在做備份操作(安裝時預設勾選自動備份)時會傳送,當有足夠的使用者使用該應用時,雲端就擁有了一個龐大的WiFi資料庫,查詢WiFi的密碼時,應用會傳送目標的ssid,mac資訊向雲端做查詢,獲取到的密碼到本地之後並不是明文的,而是一個AES加密,本地解密後連線目標WiFi。同時內建了常見的2000條WiFi弱口令,在雲端沒有該WiFi密碼的時候,可以嘗試爆破目標的密碼。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章