Sich no Schwachstelle am UC Browser

Sich no Schwachstelle am UC Browser

Aféierung

Enn MÀerz hu mir gemellt, datt se eng verstoppte FÀegkeet entdeckt hunn fir onverifizéiert Code am UC Browser ze lueden an ze lafen. Haut wÀerte mir am Detail kucken wéi dësen Download geschitt a wéi Hacker et fir hir eegen Zwecker benotze kënnen.

Virun enger ZÀit gouf den UC Browser ganz aggressiv ugekënnegt a verdeelt: et gouf op den Apparater vun de Benotzer mat Malware installéiert, verdeelt vu verschiddene Site ënner dem Deckmantel vu Videodateien (dh d'Benotzer hu geduecht datt se zum Beispill e Pornovideo eroflueden, awer amplaz en APK mat dësem Browser kritt), grujeleg Bannere mat Messagen benotzt datt de Browser al, vulnérabel a sou Saachen ass. An der offizieller UC Browser Grupp op VK gëtt et Thema, an deenen d'Benotzer sech iwwer ongerecht Reklamm kënne beschwéieren, ginn et vill Beispiller. 2016 gouf et souguer Video Reklammen op Russesch (jo, Reklammen fir en Ad-blocking Browser).

Zu der ZĂ€it vum Schreiwen huet UC Browser iwwer 500 Installatiounen op Google Play. DĂ«st ass beandrockend - nĂ«mmen Google Chrome huet mĂ©i. Ënnert de Rezensiounen kĂ«nnt Dir zimmlech vill Reklamatiounen iwwer Reklammen a Viruleedungen op e puer Apps op Google Play gesinn. DĂ«st war de Grond fir eis Fuerschung: mir hu beschloss ze kucken ob den UC Browser eppes Schlechtes mĂ©cht. An et huet sech erausgestallt datt hien et mĂ©cht!

Am Applikatiounscode gouf d'FĂ€egkeet fir ausfĂŒhrbare Code erofzelueden an ze lafen entdeckt, wat gĂ©int d'Regele fir d'VerĂ«ffentlechung vun Uwendungen ass op Google Play. ZousĂ€tzlech zu der Tatsaach datt UC Browser ausfĂŒhrbare Code eroflueden, mĂ©cht et op eng onsĂ©cher ManĂ©ier, dĂ©i benotzt ka ginn fir e MitM Attack auszefĂ©ieren. Mol kucken ob mir esou en UgrĂ«ff kĂ«nnen duerchfĂ©ieren.

Alles hei Ă«nnendrĂ«nner ass relevant fir d'Versioun vum UC Browser, dĂ©i op Google Play zur ZĂ€it vun der Studie verfĂŒgbar war:

package: com.UCMobile.intl
versionName: 12.10.8.1172
versionCode: 10598
sha1 APK-фаĐčла: f5edb2243413c777172f6362876041eb0c3a928c

Attack Vektor

Am UC Browser Manifest kënnt Dir e Service mat engem selbstverstÀndlechen Numm fannen com.uc.deployment.UpgradeDeployService.

    <service android_exported="false" android_name="com.uc.deployment.UpgradeDeployService" android_process=":deploy" />

Wann dĂ«se Service ufĂ€nkt, mĂ©cht de Browser eng POST Ufro un puds.ucweb.com/upgrade/index.xhtml, dĂ©i e puer ZĂ€it nom Start am Trafic ze gesinn ass. Als Äntwert kann hien e Kommando krĂ©ien fir e puer Update oder neie Modul erofzelueden. WĂ€rend der Analyse huet de Server net sou Befehle ginn, awer mir hu gemierkt datt wa mir probĂ©ieren e PDF am Browser opzemaachen, mĂ©cht et eng zweet Ufro un dĂ©i uewe spezifizĂ©iert Adress, duerno luet d'native BibliothĂ©ik erof. Fir den Attack auszefĂ©ieren, hu mir dĂ©cidĂ©iert dĂ«s Feature vum UC Browser ze benotzen: d'FĂ€egkeet fir PDF opzemaachen mat enger gebierteg BibliothĂ©ik, dĂ©i net an der APK ass an dĂ©i se vum Internet erofluet wann nĂ©ideg. Et ass derwĂ€ert ze notĂ©ieren datt, theoretesch, den UC Browser gezwongen ka ginn eppes ouni Benotzerinteraktioun erofzelueden - wann Dir eng gutt geformt Äntwert op eng Ufro ubitt, dĂ©i ausgefouert gĂ«tt nodeems de Browser gestart gouf. Awer fir dĂ«st ze maachen, musse mir de Protokoll vun der Interaktioun mam Server mĂ©i detaillĂ©iert studĂ©ieren, also hu mir dĂ©cidĂ©iert datt et mĂ©i einfach wier d'interceptĂ©iert Äntwert z'Ă€nneren an d'BibliothĂ©ik ze ersetzen fir mat PDF ze schaffen.

Also, wann e Benotzer e PDF direkt am Browser opmaache wëllt, kënnen déi folgend Ufroen am Traffic gesi ginn:

Sich no Schwachstelle am UC Browser

Als éischt gëtt et eng POST Ufro un puds.ucweb.com/upgrade/index.xhtml, dann
En Archiv mat enger BibliothĂ©ik fir PDF an BĂŒroformater ze gesinn gĂ«tt erofgelueden. Et ass logesch unzehuelen datt dĂ©i Ă©ischt Ufro Informatioun iwwer de System iwwerdrĂ©it (op d'mannst d'Architektur fir dĂ©i erfuerderlech BibliothĂ©ik ze bidden), an als Äntwert dorop kritt de Browser e puer Informatioun iwwer d'BibliothĂ©ik dĂ©i erofgeluede muss ginn: d'Adress an eventuell , eppes anescht. De Problem ass datt dĂ«s Ufro verschlĂ«sselt ass.

Ufro Fragment

Äntwert Fragment

Sich no Schwachstelle am UC Browser

Sich no Schwachstelle am UC Browser

D'Bibliothéik selwer ass am ZIP verpackt an ass net verschlësselt.

Sich no Schwachstelle am UC Browser

Sich no Traffic Entschlësselungscode

Loosst eis probĂ©ieren d'ServerÄntwert z'entschlĂ«sselen. Loosst eis de Klassecode kucken com.uc.deployment.UpgradeDeployService: aus Method onStartCommand gĂ©i op com.uc.deployment.bx, an vun et zu com.uc.browser.core.dcfe:

    public final void e(l arg9) {
        int v4_5;
        String v3_1;
        byte[] v3;
        byte[] v1 = null;
        if(arg9 == null) {
            v3 = v1;
        }
        else {
            v3_1 = arg9.iGX.ipR;
            StringBuilder v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]product:");
            v4.append(arg9.iGX.ipR);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]version:");
            v4.append(arg9.iGX.iEn);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]upgrade_type:");
            v4.append(arg9.iGX.mMode);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]force_flag:");
            v4.append(arg9.iGX.iEo);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]silent_mode:");
            v4.append(arg9.iGX.iDQ);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]silent_type:");
            v4.append(arg9.iGX.iEr);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]silent_state:");
            v4.append(arg9.iGX.iEp);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]silent_file:");
            v4.append(arg9.iGX.iEq);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]apk_md5:");
            v4.append(arg9.iGX.iEl);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]download_type:");
            v4.append(arg9.mDownloadType);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]download_group:");
            v4.append(arg9.mDownloadGroup);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]download_path:");
            v4.append(arg9.iGH);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]apollo_child_version:");
            v4.append(arg9.iGX.iEx);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]apollo_series:");
            v4.append(arg9.iGX.iEw);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]apollo_cpu_arch:");
            v4.append(arg9.iGX.iEt);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]apollo_cpu_vfp3:");
            v4.append(arg9.iGX.iEv);
            v4 = new StringBuilder("[");
            v4.append(v3_1);
            v4.append("]apollo_cpu_vfp:");
            v4.append(arg9.iGX.iEu);
            ArrayList v3_2 = arg9.iGX.iEz;
            if(v3_2 != null && v3_2.size() != 0) {
                Iterator v3_3 = v3_2.iterator();
                while(v3_3.hasNext()) {
                    Object v4_1 = v3_3.next();
                    StringBuilder v5 = new StringBuilder("[");
                    v5.append(((au)v4_1).getName());
                    v5.append("]component_name:");
                    v5.append(((au)v4_1).getName());
                    v5 = new StringBuilder("[");
                    v5.append(((au)v4_1).getName());
                    v5.append("]component_ver_name:");
                    v5.append(((au)v4_1).aDA());
                    v5 = new StringBuilder("[");
                    v5.append(((au)v4_1).getName());
                    v5.append("]component_ver_code:");
                    v5.append(((au)v4_1).gBl);
                    v5 = new StringBuilder("[");
                    v5.append(((au)v4_1).getName());
                    v5.append("]component_req_type:");
                    v5.append(((au)v4_1).gBq);
                }
            }
 
            j v3_4 = new j();
            m.b(v3_4);
            h v4_2 = new h();
            m.b(v4_2);
            ay v5_1 = new ay();
            v3_4.hS("");
            v3_4.setImsi("");
            v3_4.hV("");
            v5_1.bPQ = v3_4;
            v5_1.bPP = v4_2;
            v5_1.yr(arg9.iGX.ipR);
            v5_1.gBF = arg9.iGX.mMode;
            v5_1.gBI = arg9.iGX.iEz;
            v3_2 = v5_1.gAr;
            c.aBh();
            v3_2.add(g.fs("os_ver", c.getRomInfo()));
            v3_2.add(g.fs("processor_arch", com.uc.b.a.a.c.getCpuArch()));
            v3_2.add(g.fs("cpu_arch", com.uc.b.a.a.c.Pb()));
            String v4_3 = com.uc.b.a.a.c.Pd();
            v3_2.add(g.fs("cpu_vfp", v4_3));
            v3_2.add(g.fs("net_type", String.valueOf(com.uc.base.system.a.Jo())));
            v3_2.add(g.fs("fromhost", arg9.iGX.iEm));
            v3_2.add(g.fs("plugin_ver", arg9.iGX.iEn));
            v3_2.add(g.fs("target_lang", arg9.iGX.iEs));
            v3_2.add(g.fs("vitamio_cpu_arch", arg9.iGX.iEt));
            v3_2.add(g.fs("vitamio_vfp", arg9.iGX.iEu));
            v3_2.add(g.fs("vitamio_vfp3", arg9.iGX.iEv));
            v3_2.add(g.fs("plugin_child_ver", arg9.iGX.iEx));
            v3_2.add(g.fs("ver_series", arg9.iGX.iEw));
            v3_2.add(g.fs("child_ver", r.aVw()));
            v3_2.add(g.fs("cur_ver_md5", arg9.iGX.iEl));
            v3_2.add(g.fs("cur_ver_signature", SystemHelper.getUCMSignature()));
            v3_2.add(g.fs("upgrade_log", i.bjt()));
            v3_2.add(g.fs("silent_install", String.valueOf(arg9.iGX.iDQ)));
            v3_2.add(g.fs("silent_state", String.valueOf(arg9.iGX.iEp)));
            v3_2.add(g.fs("silent_file", arg9.iGX.iEq));
            v3_2.add(g.fs("silent_type", String.valueOf(arg9.iGX.iEr)));
            v3_2.add(g.fs("cpu_archit", com.uc.b.a.a.c.Pc()));
            v3_2.add(g.fs("cpu_set", SystemHelper.getCpuInstruction()));
            boolean v4_4 = v4_3 == null || !v4_3.contains("neon") ? false : true;
            v3_2.add(g.fs("neon", String.valueOf(v4_4)));
            v3_2.add(g.fs("cpu_cores", String.valueOf(com.uc.b.a.a.c.Jl())));
            v3_2.add(g.fs("ram_1", String.valueOf(com.uc.b.a.a.h.Po())));
            v3_2.add(g.fs("totalram", String.valueOf(com.uc.b.a.a.h.OL())));
            c.aBh();
            v3_2.add(g.fs("rom_1", c.getRomInfo()));
            v4_5 = e.getScreenWidth();
            int v6 = e.getScreenHeight();
            StringBuilder v7 = new StringBuilder();
            v7.append(v4_5);
            v7.append("*");
            v7.append(v6);
            v3_2.add(g.fs("ss", v7.toString()));
            v3_2.add(g.fs("api_level", String.valueOf(Build$VERSION.SDK_INT)));
            v3_2.add(g.fs("uc_apk_list", SystemHelper.getUCMobileApks()));
            Iterator v4_6 = arg9.iGX.iEA.entrySet().iterator();
            while(v4_6.hasNext()) {
                Object v6_1 = v4_6.next();
                v3_2.add(g.fs(((Map$Entry)v6_1).getKey(), ((Map$Entry)v6_1).getValue()));
            }
 
            v3 = v5_1.toByteArray();
        }
 
        if(v3 == null) {
            this.iGY.iGI.a(arg9, "up_encode", "yes", "fail");
            return;
        }
 
        v4_5 = this.iGY.iGw ? 0x1F : 0;
        if(v3 == null) {
        }
        else {
            v3 = g.i(v4_5, v3);
            if(v3 == null) {
            }
            else {
                v1 = new byte[v3.length + 16];
                byte[] v6_2 = new byte[16];
                Arrays.fill(v6_2, 0);
                v6_2[0] = 0x5F;
                v6_2[1] = 0;
                v6_2[2] = ((byte)v4_5);
                v6_2[3] = -50;
                System.arraycopy(v6_2, 0, v1, 0, 16);
                System.arraycopy(v3, 0, v1, 16, v3.length);
            }
        }
 
        if(v1 == null) {
            this.iGY.iGI.a(arg9, "up_encrypt", "yes", "fail");
            return;
        }
 
        if(TextUtils.isEmpty(this.iGY.mUpgradeUrl)) {
            this.iGY.iGI.a(arg9, "up_url", "yes", "fail");
            return;
        }
 
        StringBuilder v0 = new StringBuilder("[");
        v0.append(arg9.iGX.ipR);
        v0.append("]url:");
        v0.append(this.iGY.mUpgradeUrl);
        com.uc.browser.core.d.c.i v0_1 = this.iGY.iGI;
        v3_1 = this.iGY.mUpgradeUrl;
        com.uc.base.net.e v0_2 = new com.uc.base.net.e(new com.uc.browser.core.d.c.i$a(v0_1, arg9));
        v3_1 = v3_1.contains("?") ? v3_1 + "&dataver=pb" : v3_1 + "?dataver=pb";
        n v3_5 = v0_2.uc(v3_1);
        m.b(v3_5, false);
        v3_5.setMethod("POST");
        v3_5.setBodyProvider(v1);
        v0_2.b(v3_5);
        this.iGY.iGI.a(arg9, "up_null", "yes", "success");
        this.iGY.iGI.b(arg9);
    }

Mir gesinn d'Bildung vun enger POST Ufro hei. Mir bezuelen Opmierksamkeet op d'Schafung vun engem Array vun 16 Bytes a seng Fëllung: 0x5F, 0, 0x1F, -50 (=0xCE). GlÀichzÀiteg mat deem wat mir an der Ufro uewen gesinn.

An der selwechter Klass kënnt Dir eng nestéiert Klass gesinn déi eng aner interessant Method huet:

        public final void a(l arg10, byte[] arg11) {
            f v0 = this.iGQ;
            StringBuilder v1 = new StringBuilder("[");
            v1.append(arg10.iGX.ipR);
            v1.append("]:UpgradeSuccess");
            byte[] v1_1 = null;
            if(arg11 == null) {
            }
            else if(arg11.length < 16) {
            }
            else {
                if(arg11[0] != 0x60 && arg11[3] != 0xFFFFFFD0) {
                    goto label_57;
                }
                int v3 = 1;
                int v5 = arg11[1] == 1 ? 1 : 0;
                if(arg11[2] != 1 && arg11[2] != 11) {
                    if(arg11[2] == 0x1F) {
                    }
                    else {
                        v3 = 0;
                    }
                }
                byte[] v7 = new byte[arg11.length - 16];
                System.arraycopy(arg11, 16, v7, 0, v7.length);
                if(v3 != 0) {
                    v7 = g.j(arg11[2], v7);
                }
                if(v7 == null) {
                    goto label_57;
                }
                if(v5 != 0) {
                    v1_1 = g.P(v7);
                    goto label_57;
                }
                v1_1 = v7;
            }
        label_57:
            if(v1_1 == null) {
                v0.iGY.iGI.a(arg10, "up_decrypt", "yes", "fail");
                return;
            }
            q v11 = g.b(arg10, v1_1);
            if(v11 == null) {
                v0.iGY.iGI.a(arg10, "up_decode", "yes", "fail");
                return;
            }
            if(v0.iGY.iGt) {
                v0.d(arg10);
            }
            if(v0.iGY.iGo != null) {
                v0.iGY.iGo.a(0, ((o)v11));
            }
            if(v0.iGY.iGs) {
                v0.iGY.a(((o)v11));
                v0.iGY.iGI.a(v11, "up_silent", "yes", "success");
                v0.iGY.iGI.a(v11);
                return;
            }
            v0.iGY.iGI.a(v11, "up_silent", "no", "success");
        }
    }

D'Methode hĂ«lt eng Array vu Bytes als Input a kontrollĂ©iert datt den Nullbyte 0x60 ass oder den drĂ«tten Byte 0xD0 ass, an den zweeten Byte ass 1, 11 oder 0x1F. Mir kucken d'Äntwert vum Server: den Nullbyte ass 0x60, dĂ©i zweet ass 0x1F, dĂ©i drĂ«tt ass 0x60. KlĂ©ngt wĂ©i wat mir brauchen. No de Linnen beurteelen ("up_decrypt", zum Beispill), soll hei eng Method genannt ginn, dĂ©i d'Äntwert vum Server entschlĂ«sselt.
Loosst eis op d'Method goen gj. NotĂ©iert datt dat Ă©ischt Argument de Byte am Offset 2 ass (dh 0x1F an eisem Fall), an dat zweet ass d'Server Äntwert ouni
éischt 16 Bytes.

     public static byte[] j(int arg1, byte[] arg2) {
        if(arg1 == 1) {
            arg2 = c.c(arg2, c.adu);
        }
        else if(arg1 == 11) {
            arg2 = m.aF(arg2);
        }
        else if(arg1 != 0x1F) {
        }
        else {
            arg2 = EncryptHelper.decrypt(arg2);
        }
        return arg2;
    }

Natierlech, hei wielt mir en Entschlësselungsalgorithmus, an dee selwechte Byte deen an eisem ass
Fall glÀich 0x1F, bezeechent eng vun drÀi méiglech Optiounen.

Mir analyséieren weider de Code. No e puer Spronge fanne mir eis an enger Method mat engem selbstverstÀndlechen Numm decryptBytesByKey.

Hei sinn zwee mĂ©i Bytes vun eiser Äntwert getrennt, an e String gĂ«tt vun hinnen kritt. Et ass kloer datt op dĂ«s ManĂ©ier de SchlĂ«ssel fir d'EntschlĂ«sselung vum Message ausgewielt gĂ«tt.

    private static byte[] decryptBytesByKey(byte[] bytes) {
        byte[] v0 = null;
        if(bytes != null) {
            try {
                if(bytes.length < EncryptHelper.PREFIX_BYTES_SIZE) {
                }
                else if(bytes.length == EncryptHelper.PREFIX_BYTES_SIZE) {
                    return v0;
                }
                else {
                    byte[] prefix = new byte[EncryptHelper.PREFIX_BYTES_SIZE];  // 2 баĐčта
                    System.arraycopy(bytes, 0, prefix, 0, prefix.length);
                    String keyId = c.ayR().d(ByteBuffer.wrap(prefix).getShort()); // Đ’Ń‹Đ±ĐŸŃ€ Đșлюча
                    if(keyId == null) {
                        return v0;
                    }
                    else {
                        a v2 = EncryptHelper.ayL();
                        if(v2 == null) {
                            return v0;
                        }
                        else {
                            byte[] enrypted = new byte[bytes.length - EncryptHelper.PREFIX_BYTES_SIZE];
                            System.arraycopy(bytes, EncryptHelper.PREFIX_BYTES_SIZE, enrypted, 0, enrypted.length);
                            return v2.l(keyId, enrypted);
                        }
                    }
                }
            }
            catch(SecException v7_1) {
                EncryptHelper.handleDecryptException(((Throwable)v7_1), v7_1.getErrorCode());
                return v0;
            }
            catch(Throwable v7) {
                EncryptHelper.handleDecryptException(v7, 2);
                return v0;
            }
        }
 
        return v0;
    }

Wa mir no vir kucken, bemierken mir datt mir op dëser Etapp nach kee Schlëssel kréien, awer nëmmen sÀin "Identifier". De Schlëssel ze kréien ass e bësse méi komplizéiert.

An der nĂ€chster Method ginn zwee weider Parameteren op dĂ©i existent bĂ€igefĂŒĂŒgt, dĂ©i vĂ©ier dovunner maachen: d'Magie Nummer 16, de SchlĂ«sselidentifizĂ©ierer, dĂ©i verschlĂ«sselte DonnĂ©eĂ«n an eng onverstĂ€ndlech String (an eisem Fall eidel).

    public final byte[] l(String keyId, byte[] encrypted) throws SecException {
        return this.ayJ().staticBinarySafeDecryptNoB64(16, keyId, encrypted, "");
    }

No enger Rei vun IwwergÀng komme mir bei der Method staticBinarySafeDecryptNoB64 Interface com.alibaba.wireless.security.open.staticdataencrypt.IStaticDataEncryptComponent. Et gi keng Klassen am Haaptapplikatiounscode déi dësen Interface implementéieren. Et gëtt esou eng Klass am Dossier lib/armeabi-v7a/libsgmain.so, wat eigentlech net e .so ass, mee e .jar. D'Method déi mir interesséiert sinn ass wéi follegt ëmgesat:

package com.alibaba.wireless.security.a.i;
 
// ...
 
public class a implements IStaticDataEncryptComponent {
    private ISecurityGuardPlugin a;
// ...
    private byte[] a(int mode, int magicInt, int xzInt, String keyId, byte[] encrypted, String magicString) {
        return this.a.getRouter().doCommand(10601, new Object[]{Integer.valueOf(mode), Integer.valueOf(magicInt), Integer.valueOf(xzInt), keyId, encrypted, magicString});
    }
// ...
    private byte[] b(int magicInt, String keyId, byte[] encrypted, String magicString) {
        return this.a(2, magicInt, 0, keyId, encrypted, magicString);
    }
// ...
    public byte[] staticBinarySafeDecryptNoB64(int magicInt, String keyId, byte[] encrypted, String magicString) throws SecException {
        if(keyId != null && keyId.length() > 0 && magicInt >= 0 && magicInt < 19 && encrypted != null && encrypted.length > 0) {
            return this.b(magicInt, keyId, encrypted, magicString);
        }
 
        throw new SecException("", 301);
    }
//...
}

Hei ass eis Lëscht vun de Parameteren ergÀnzt mat zwee méi ganz Zuelen: 2 an 0. Riichter no
alles, 2 heescht Entschlësselung, wéi an der Method do Finale System Klass javax.crypto.Cipher. An all dat gëtt op e bestëmmte Router mat der Nummer 10601 transferéiert - dat ass anscheinend d'Kommandonummer.

No der nÀchster Kette vun IwwergÀng fanne mir eng Klass déi d'Interface implementéiert IRouterComponent a Method doCommand:

package com.alibaba.wireless.security.mainplugin;

import com.alibaba.wireless.security.framework.IRouterComponent;
import com.taobao.wireless.security.adapter.JNICLibrary;
 
public class a implements IRouterComponent {
    public a() {
        super();
    }
    public Object doCommand(int arg2, Object[] arg3) {
        return JNICLibrary.doCommandNative(arg2, arg3);
    }
}

An och Klass JNICLibrary, an deem déi gebierteg Method deklaréiert ass doCommandNative:

package com.taobao.wireless.security.adapter;
 
public class JNICLibrary {
    public static native Object doCommandNative(int arg0, Object[] arg1);
}

Dëst bedeit datt mir eng Method am gebiertege Code musse fannen doCommandNative. An hei fÀnkt de Spaass un.

Obfuscatioun vun Maschinn Code

Am Dossier libsgmain.so (wat eigentlech e .jar ass an an deem mir d'Ëmsetzung vun e puer VerschlĂ«sselungsverbonne Interfaces just uewen fonnt hunn) gĂ«tt et eng gebierteg BibliothĂ©ik: libsgmainso-6.4.36.so. Mir öffnen et an IDA a krĂ©ien eng RĂ«tsch Dialogboxen mat Feeler. De Problem ass datt d'Sektioun Header Tabelle ongĂ«lteg ass. DĂ«st gĂ«tt virsiichteg gemaach fir d'Analyse ze komplizĂ©ieren.

Sich no Schwachstelle am UC Browser

Awer et ass net néideg: fir eng ELF Datei richteg ze lueden an ze analyséieren, ass e Programmheader Dësch genuch. Dofir lÀschen mir einfach d'Sektiounstabell, nulléieren déi entspriechend Felder am Header.

Sich no Schwachstelle am UC Browser

Öffnen d'Datei an IDA erĂ«m.

Et ginn zwou WeeĂ«r fir d'Java virtuell Maschinn ze soen, wou genau an der gebierteg BibliothĂ©ik d'Ëmsetzung vun enger Method ass, dĂ©i am Java Code als gebierteg deklarĂ©iert ass. DĂ©i Ă©ischt ass et en Artennumm ze ginn Java_package_name_ClassName_MethodName.

Déi zweet ass et ze registréieren wann Dir d'Bibliothéik luet (an der Funktioun JNI_OnLoad)
benotzt eng Funktioun Opruff RegisterNatives.

An eisem Fall, wa mir déi éischt Method benotzen, soll den Numm esou sinn: Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative.

Et gëtt keng sou eng Funktioun ënnert den exportéierte Funktiounen, dat heescht datt Dir no engem Uruff kuckt RegisterNatives.
Loosst eis op d'Funktioun goen JNI_OnLoad a mir gesinn dëst Bild:

Sich no Schwachstelle am UC Browser

Wat ass hei lass? Op den éischte Bléck sinn den Ufank an Enn vun der Funktioun typesch fir ARM Architektur. Déi éischt Instruktioun um Stack spÀichert den Inhalt vun de Registere déi d'Funktioun a senger Operatioun benotzt (an dësem Fall R0, R1 an R2), souwéi den Inhalt vum LR Register, deen d'Retouradress vun der Funktioun enthÀlt . Déi lescht Instruktioun restauréiert déi gespÀichert Registere, an d'Retouradress gëtt direkt am PC Register gesat - also zréck vun der Funktioun. Awer wann Dir enk kuckt, mierkt Dir datt déi lescht Instruktioun d'Retouradress Ànnert déi um Stack gespÀichert ass. Loosst eis berechnen wéi et duerno wÀert sinn
Code Ausféierung. Eng gewësse Adress 1xB0 gëtt an R130 gelueden, 5 gëtt dovun ofgezunn, da gëtt se op R0 transferéiert an 0x10 gëtt derbÀigesat. Et stellt sech eraus 0xB13B. Also mengt IDA datt déi lescht Instruktioun en normale Funktiounsretour ass, awer tatsÀchlech spréngt se op déi berechent Adress 0xB13B.

Et ass derwÀert ze erënneren datt ARM Prozessoren zwee Modi an zwee SÀtz vun Instruktiounen hunn: ARM an Thumb. Dat mannst bedeitendst Bit vun der Adress seet dem Prozessor wéi eng Instruktiounsset benotzt gëtt. Dat ass, d'Adress ass tatsÀchlech 0xB13A, an een am mannsten bedeitende Bit weist den Thumb Modus un.

En Àhnlechen "Adapter" gouf am Ufank vun all Funktioun an dëser Bibliothéik a
Gerempels Code. Mir wÀerten se net méi am Detail ophalen - mir erënnere just un
datt de richtegen Ufank vu bal all Funktiounen e bësse méi wÀit ewech ass.

Well de Code net explizit op 0xB13A spréngt, huet d'IDA selwer net erkannt datt de Code op dëser Plaz lÀit. Aus dem selwechte Grond erkennt et de gréissten Deel vum Code an der Bibliothéik net als Code, wat d'Analyse e bësse schwéier mécht. Mir soen IDA datt dëst de Code ass, an dat ass wat geschitt:

Sich no Schwachstelle am UC Browser

Den Dësch fÀnkt kloer bei 0xB144 un. Wat ass am sub_494C?

Sich no Schwachstelle am UC Browser

Wann Dir dĂ«s Funktioun am LR Register rufft, krĂ©ie mir d'Adress vun der virdru genannter Tabell (0xB144). An R0 - Index an dĂ«ser Tabell. Dat ass, de WĂ€ert gĂ«tt aus der Tabell geholl, op LR bĂ€igefĂŒĂŒgt an d'Resultat ass
d'Adress fir ze goen. Loosst eis probéieren et ze berechnen: 0xB144 + [0xB144 + 8* 4] = 0xB144 + 0x120 = 0xB264. Mir ginn op déi kritt Adress a gesinn wuertwiertlech e puer nëtzlech Instruktiounen a gitt erëm op 0xB140:

Sich no Schwachstelle am UC Browser

Elo gëtt et en Iwwergang am Offset mam Index 0x20 vum Dësch.

No der Gréisst vum Dësch beuerteelen, ginn et vill sou Transitioune am Code. D'Fro stellt sech, ob et méiglech ass, iergendwéi méi automatesch mat deem ëmzegoen, ouni Adressen manuell ze berechnen. A Scripten an d'FÀegkeet fir Code an IDA ze patchen kommen eis zur Hëllef:

def put_unconditional_branch(source, destination):
    offset = (destination - source - 4) >> 1
    if offset > 2097151 or offset < -2097152:
        raise RuntimeError("Invalid offset")
    if offset > 1023 or offset < -1024:
        instruction1 = 0xf000 | ((offset >> 11) & 0x7ff)
        instruction2 = 0xb800 | (offset & 0x7ff)
        patch_word(source, instruction1)
        patch_word(source + 2, instruction2)
    else:
        instruction = 0xe000 | (offset & 0x7ff)
        patch_word(source, instruction)
 
 
ea = here()
if get_wide_word(ea) == 0xb503: #PUSH {R0,R1,LR}
    ea1 = ea + 2
    if get_wide_word(ea1) == 0xbf00: #NOP
        ea1 += 2
    if get_operand_type(ea1, 0) == 1 and get_operand_value(ea1, 0) == 0 and get_operand_type(ea1, 1) == 2:
        index = get_wide_dword(get_operand_value(ea1, 1))
        print "index =", hex(index)
        ea1 += 2
        if get_operand_type(ea1, 0) == 7:
            table = get_operand_value(ea1, 0) + 4
        elif get_operand_type(ea1, 1) == 2:
            table = get_operand_value(ea1, 1) + 4
        else:
            print "Wrong operand type on", hex(ea1), "-", get_operand_type(ea1, 0), get_operand_type(ea1, 1)
            table = None
        if table is None:
            print "Unable to find table"
        else:
            print "table =", hex(table)
            offset = get_wide_dword(table + (index << 2))
            put_unconditional_branch(ea, table + offset)
    else:
        print "Unknown code", get_operand_type(ea1, 0), get_operand_value(ea1, 0), get_operand_type(ea1, 1) == 2
else:
    print "Unable to detect first instruction"

Place de Cursor op der Linn 0xB26A, lafen de Skript a kuckt den Iwwergank op 0xB4B0:

Sich no Schwachstelle am UC Browser

IDA huet dëst Gebitt erëm net als Code unerkannt. Mir hëllefen hir a gesinn en aneren Design do:

Sich no Schwachstelle am UC Browser

D'Instruktioune no BLX schéngen net vill Sënn ze maachen, et ass méi wéi eng Zort Verlagerung. Loosst eis op sub_4964 kucken:

Sich no Schwachstelle am UC Browser

An zwar gĂ«tt hei en Dword op der Adress geholl dĂ©i am LR lĂ€it, op dĂ«s Adress bĂ€igefĂŒĂŒgt, duerno gĂ«tt de WĂ€ert op der resultĂ©ierender Adress geholl an op de Stack gesat. Och gĂ«tt 4 op LR bĂ€igefĂŒĂŒgt, sou datt nom Retour vun der Funktioun dee selwechte Offset iwwersprongen gĂ«tt. Duerno hĂ«lt de POP {R1} Kommando de resultĂ©ierende WĂ€ert vum Stack. Wann Dir kuckt wat op der Adress 0xB4BA + 0xEA = 0xB5A4 lĂ€it, gesitt Dir eppes Ă€hnlech wĂ©i eng Adresstabell:

Sich no Schwachstelle am UC Browser

Fir dësen Design ze patchen, musst Dir zwee Parameteren aus dem Code kréien: de Offset an d'Registrierungsnummer, an dÀr Dir d'Resultat wëllt setzen. Fir all méiglech Enregistréiere musst Dir am Viraus e Stéck Code virbereeden.

patches = {}
patches[0] = (0x00, 0xbf, 0x01, 0x48, 0x00, 0x68, 0x02, 0xe0)
patches[1] = (0x00, 0xbf, 0x01, 0x49, 0x09, 0x68, 0x02, 0xe0)
patches[2] = (0x00, 0xbf, 0x01, 0x4a, 0x12, 0x68, 0x02, 0xe0)
patches[3] = (0x00, 0xbf, 0x01, 0x4b, 0x1b, 0x68, 0x02, 0xe0)
patches[4] = (0x00, 0xbf, 0x01, 0x4c, 0x24, 0x68, 0x02, 0xe0)
patches[5] = (0x00, 0xbf, 0x01, 0x4d, 0x2d, 0x68, 0x02, 0xe0)
patches[8] = (0x00, 0xbf, 0xdf, 0xf8, 0x06, 0x80, 0xd8, 0xf8, 0x00, 0x80, 0x01, 0xe0)
patches[9] = (0x00, 0xbf, 0xdf, 0xf8, 0x06, 0x90, 0xd9, 0xf8, 0x00, 0x90, 0x01, 0xe0)
patches[10] = (0x00, 0xbf, 0xdf, 0xf8, 0x06, 0xa0, 0xda, 0xf8, 0x00, 0xa0, 0x01, 0xe0)
patches[11] = (0x00, 0xbf, 0xdf, 0xf8, 0x06, 0xb0, 0xdb, 0xf8, 0x00, 0xb0, 0x01, 0xe0)
 
ea = here()
if (get_wide_word(ea) == 0xb082 #SUB SP, SP, #8
        and get_wide_word(ea + 2) == 0xb503): #PUSH {R0,R1,LR}
    if get_operand_type(ea + 4, 0) == 7:
        pop = get_bytes(ea + 12, 4, 0)
        if pop[1] == 'xbc':
            register = -1
            r = get_wide_byte(ea + 12)
            for i in range(8):
                if r == (1 << i):
                    register = i
                    break
            if register == -1:
                print "Unable to detect register"
            else:
                address = get_wide_dword(ea + 8) + ea + 8
                for b in patches[register]:
                    patch_byte(ea, b)
                    ea += 1
                if ea % 4 != 0:
                    ea += 2
                patch_dword(ea, address)
        elif pop[:3] == 'x5dxf8x04':
            register = ord(pop[3]) >> 4
            if register in patches:
                address = get_wide_dword(ea + 8) + ea + 8
                for b in patches[register]:
                    patch_byte(ea, b)
                    ea += 1
                patch_dword(ea, address)
        else:
            print "POP instruction not found"
    else:
        print "Wrong operand type on +4:", get_operand_type(ea + 4, 0)
else:
    print "Unable to detect first instructions"

Mir setzen de Cursor am Ufank vun der Struktur déi mir ersetzen wëllen - 0xB4B2 - a lafen de Skript:

Sich no Schwachstelle am UC Browser

ZousÀtzlech zu de scho genannte Strukturen enthÀlt de Code och déi folgend:

Sich no Schwachstelle am UC Browser

Wéi am virege Fall, no der BLX Instruktioun gëtt et eng Offset:

Sich no Schwachstelle am UC Browser

Mir huelen d'Offset op d'Adress vum LR, fĂŒgen se op d'LR a ginn dohinner. 0x72044 + 0xC = 0x72050. D'Skript fir dĂ«sen Design ass ganz einfach:

def put_unconditional_branch(source, destination):
    offset = (destination - source - 4) >> 1
    if offset > 2097151 or offset < -2097152:
        raise RuntimeError("Invalid offset")
    if offset > 1023 or offset < -1024:
        instruction1 = 0xf000 | ((offset >> 11) & 0x7ff)
        instruction2 = 0xb800 | (offset & 0x7ff)
        patch_word(source, instruction1)
        patch_word(source + 2, instruction2)
    else:
        instruction = 0xe000 | (offset & 0x7ff)
        patch_word(source, instruction)
 
 
ea = here()
if get_wide_word(ea) == 0xb503: #PUSH {R0,R1,LR}
    ea1 = ea + 6
    if get_wide_word(ea + 2) == 0xbf00: #NOP
        ea1 += 2
    offset = get_wide_dword(ea1)
    put_unconditional_branch(ea, (ea1 + offset) & 0xffffffff)
else:
    print "Unable to detect first instruction"

Resultat vun der Ausféierung vum Skript:

Sich no Schwachstelle am UC Browser

Wann alles an der Funktioun gepatcht ass, kënnt Dir IDA op sÀin richtegen Ufank weisen. Et wÀert all d'Funktiounscode zesummeschaffen, an et kann mat HexRays dekompiléiert ginn.

Decodéieren Strings

Mir hunn geléiert mat der Verdueblung vum Maschinncode an der Bibliothéik ze këmmeren libsgmainso-6.4.36.so vum UC Browser a krut de Funktiounscode JNI_OnLoad.

int __fastcall real_JNI_OnLoad(JavaVM *vm)
{
  int result; // r0
  jclass clazz; // r0 MAPDST
  int v4; // r0
  JNIEnv *env; // r4
  int v6; // [sp-40h] [bp-5Ch]
  int v7; // [sp+Ch] [bp-10h]
  v7 = *(_DWORD *)off_8AC00;
  if ( !vm )
    goto LABEL_39;
  sub_7C4F4();
  env = (JNIEnv *)sub_7C5B0(0);
  if ( !env )
    goto LABEL_39;
  v4 = sub_72CCC();
  sub_73634(v4);
  sub_73E24(&unk_83EA6, &v6, 49);
  clazz = (jclass)((int (__fastcall *)(JNIEnv *, int *))(*env)->FindClass)(env, &v6);
  if ( clazz
    && (sub_9EE4(),
        sub_71D68(env),
        sub_E7DC(env) >= 0
     && sub_69D68(env) >= 0
     && sub_197B4(env, clazz) >= 0
     && sub_E240(env, clazz) >= 0
     && sub_B8B0(env, clazz) >= 0
     && sub_5F0F4(env, clazz) >= 0
     && sub_70640(env, clazz) >= 0
     && sub_11F3C(env) >= 0
     && sub_21C3C(env, clazz) >= 0
     && sub_2148C(env, clazz) >= 0
     && sub_210E0(env, clazz) >= 0
     && sub_41B58(env, clazz) >= 0
     && sub_27920(env, clazz) >= 0
     && sub_293E8(env, clazz) >= 0
     && sub_208F4(env, clazz) >= 0) )
  {
    result = (sub_B7B0(env, clazz) >> 31) | 0x10004;
  }
  else
  {
LABEL_39:
    result = -1;
  }
  return result;
}

Loosst eis déi folgend Zeilen méi no kucken:

  sub_73E24(&unk_83EA6, &v6, 49);
  clazz = (jclass)((int (__fastcall *)(JNIEnv *, int *))(*env)->FindClass)(env, &v6);

An Funktioun sub_73E24 de Klassennumm gëtt kloer entschlësselt. Als Parameter fir dës Funktioun gëtt e Pointer op Daten Àhnlech wéi verschlësselte Donnéeën, e bestëmmte Puffer an eng Zuel iwwerginn. SelbstverstÀndlech, nodeems Dir d'Funktioun opgeruff hutt, gëtt et eng dekryptéiert Linn am Puffer, well se an d'Funktioun weidergeleet gëtt FindClass, deen den Numm vun der Klass als zweete Parameter hëlt. Dofir ass d'Zuel d'Gréisst vum Puffer oder d'LÀngt vun der Linn. Loosst eis probéieren den Numm vun der Klass ze entzifferen, et soll eis soen ob mir an déi richteg Richtung goen. Loosst eis e bësse méi no kucken wat geschitt an sub_73E24.

int __fastcall sub_73E56(unsigned __int8 *in, unsigned __int8 *out, size_t size)
{
  int v4; // r6
  int v7; // r11
  int v8; // r9
  int v9; // r4
  size_t v10; // r5
  int v11; // r0
  struc_1 v13; // [sp+0h] [bp-30h]
  int v14; // [sp+1Ch] [bp-14h]
  int v15; // [sp+20h] [bp-10h]
  v4 = 0;
  v15 = *(_DWORD *)off_8AC00;
  v14 = 0;
  v7 = sub_7AF78(17);
  v8 = sub_7AF78(size);
  if ( !v7 )
  {
    v9 = 0;
    goto LABEL_12;
  }
  (*(void (__fastcall **)(int, const char *, int))(v7 + 12))(v7, "DcO/lcK+h?m3c*q@", 16);
  if ( !v8 )
  {
LABEL_9:
    v4 = 0;
    goto LABEL_10;
  }
  v4 = 0;
  if ( !in )
  {
LABEL_10:
    v9 = 0;
    goto LABEL_11;
  }
  v9 = 0;
  if ( out )
  {
    memset(out, 0, size);
    v10 = size - 1;
    (*(void (__fastcall **)(int, unsigned __int8 *, size_t))(v8 + 12))(v8, in, v10);
    memset(&v13, 0, 0x14u);
    v13.field_4 = 3;
    v13.field_10 = v7;
    v13.field_14 = v8;
    v11 = sub_6115C(&v13, &v14);
    v9 = v11;
    if ( v11 )
    {
      if ( *(_DWORD *)(v11 + 4) == v10 )
      {
        qmemcpy(out, *(const void **)v11, v10);
        v4 = *(_DWORD *)(v9 + 4);
      }
      else
      {
        v4 = 0;
      }
      goto LABEL_11;
    }
    goto LABEL_9;
  }
LABEL_11:
  sub_7B148(v7);
LABEL_12:
  if ( v8 )
    sub_7B148(v8);
  if ( v9 )
    sub_7B148(v9);
  return v4;
}

Funktioun sub_7AF78 erstellt eng Instanz vun engem Container fir Byte-Arrays vun der spezifizéierter Gréisst (mir wÀerten net op dës Container am Detail wunnen). Hei ginn zwee esou Container erstallt: een enthÀlt d'Linn "DcO/lcK+h?m3c*q@" (et ass einfach ze roden datt dëst e Schlëssel ass), deen aneren enthÀlt verschlësselte Donnéeën. Als nÀchst gi béid Objeten an enger bestëmmter Struktur plazéiert, déi un d'Funktioun weidergeleet gëtt sub_6115C. Mir markéieren och an dëser Struktur e Feld mam WÀert 3. Loosst eis kucken wat duerno mat dëser Struktur geschitt.

int __fastcall sub_611B4(struc_1 *a1, _DWORD *a2)
{
  int v3; // lr
  unsigned int v4; // r1
  int v5; // r0
  int v6; // r1
  int result; // r0
  int v8; // r0
  *a2 = 820000;
  if ( a1 )
  {
    v3 = a1->field_14;
    if ( v3 )
    {
      v4 = a1->field_4;
      if ( v4 < 0x19 )
      {
        switch ( v4 )
        {
          case 0u:
            v8 = sub_6419C(a1->field_0, a1->field_10, v3);
            goto LABEL_17;
          case 3u:
            v8 = sub_6364C(a1->field_0, a1->field_10, v3);
            goto LABEL_17;
          case 0x10u:
          case 0x11u:
          case 0x12u:
            v8 = sub_612F4(
                   a1->field_0,
                   v4,
                   *(_QWORD *)&a1->field_8,
                   *(_QWORD *)&a1->field_8 >> 32,
                   a1->field_10,
                   v3,
                   a2);
            goto LABEL_17;
          case 0x14u:
            v8 = sub_63A28(a1->field_0, v3);
            goto LABEL_17;
          case 0x15u:
            sub_61A60(a1->field_0, v3, a2);
            return result;
          case 0x16u:
            v8 = sub_62440(a1->field_14);
            goto LABEL_17;
          case 0x17u:
            v8 = sub_6226C(a1->field_10, v3);
            goto LABEL_17;
          case 0x18u:
            v8 = sub_63530(a1->field_14);
LABEL_17:
            v6 = 0;
            if ( v8 )
            {
              *a2 = 0;
              v6 = v8;
            }
            return v6;
          default:
            LOWORD(v5) = 28032;
            goto LABEL_5;
        }
      }
    }
  }
  LOWORD(v5) = -27504;
LABEL_5:
  HIWORD(v5) = 13;
  v6 = 0;
  *a2 = v5;
  return v6;
}

De Schaltparameter ass e Strukturfeld dat virdru de WĂ€ert zougewisen huet 3. Kuckt de Fall 3: op d'Funktioun sub_6364C Parameteren ginn aus der Struktur weidergeleet, dĂ©i do an der viregter Funktioun bĂ€igefĂŒĂŒgt goufen, dh de SchlĂ«ssel a verschlĂ«sselte DonnĂ©eĂ«n. Wann Dir genau kuckt sub_6364C, Dir kĂ«nnt den RC4 Algorithmus dran erkennen.

Mir hunn en Algorithmus an e Schlëssel. Loosst eis probéieren den Numm vun der Klass ze entzifferen. Hei ass wat geschitt ass: com/taobao/wireless/security/adapter/JNICLibrary. Super! Mir sinn um richtege Wee.

Kommando Bam

Elo musse mir eng Erausfuerderung fannen RegisterNatives, déi eis op d'Funktioun weisen doCommandNative. Loosst eis d'Funktiounen kucken, déi vun JNI_OnLoad, a mir fannen et an sub_B7B0:

int __fastcall sub_B7F6(JNIEnv *env, jclass clazz)
{
  char signature[41]; // [sp+7h] [bp-55h]
  char name[16]; // [sp+30h] [bp-2Ch]
  JNINativeMethod method; // [sp+40h] [bp-1Ch]
  int v8; // [sp+4Ch] [bp-10h]
 
  v8 = *(_DWORD *)off_8AC00;
  decryptString((unsigned __int8 *)&unk_83ED9, (unsigned __int8 *)name, 0x10u);// doCommandNative
  decryptString((unsigned __int8 *)&unk_83EEA, (unsigned __int8 *)signature, 0x29u);// (I[Ljava/lang/Object;)Ljava/lang/Object;
  method.name = name;
  method.signature = signature;
  method.fnPtr = sub_B69C;
  return ((int (__fastcall *)(JNIEnv *, jclass, JNINativeMethod *, int))(*env)->RegisterNatives)(env, clazz, &method, 1) >> 31;
}

An tatsÀchlech ass eng gebierteg Method mam Numm hei registréiert doCommandNative. Elo kennen mir seng Adress. Loosst eis kucken wat hien mécht.

int __fastcall doCommandNative(JNIEnv *env, jobject obj, int command, jarray args)
{
  int v5; // r5
  struc_2 *a5; // r6
  int v9; // r1
  int v11; // [sp+Ch] [bp-14h]
  int v12; // [sp+10h] [bp-10h]
  v5 = 0;
  v12 = *(_DWORD *)off_8AC00;
  v11 = 0;
  a5 = (struc_2 *)malloc(0x14u);
  if ( a5 )
  {
    a5->field_0 = 0;
    a5->field_4 = 0;
    a5->field_8 = 0;
    a5->field_C = 0;
    v9 = command % 10000 / 100;
    a5->field_0 = command / 10000;
    a5->field_4 = v9;
    a5->field_8 = command % 100;
    a5->field_C = env;
    a5->field_10 = args;
    v5 = sub_9D60(command / 10000, v9, command % 100, 1, (int)a5, &v11);
  }
  free(a5);
  if ( !v5 && v11 )
    sub_7CF34(env, v11, &byte_83ED7);
  return v5;
}

Mam Numm kënnt Dir roden datt hei den Entrée vun all de Funktiounen ass, déi d'Entwéckler decidéiert hunn an d'gebierteg Bibliothéik ze transferéieren. Mir sinn interesséiert fir d'Funktioun Nummer 10601.

Dir kĂ«nnt aus dem Code gesinn datt d'Kommandonummer drĂ€i Zuelen produzĂ©iert: Kommando / 10000, Kommando % 10000 / 100 Đž Kommando % 10, also an eisem Fall 1, 6 an 1. DĂ«s drĂ€i Zuelen, wĂ©i och e Pointer op JNIEnv an d'Argumenter, dĂ©i un d'Funktioun passĂ©iert sinn, ginn an eng Struktur bĂ€igefĂŒĂŒgt a weidergeleet. Benotzt dĂ©i drĂ€i kritt Zuelen (loosst eis N1, N2 an N3 bezeechnen), e Kommando Bam gebaut.

Sou eppes:

Sich no Schwachstelle am UC Browser

De Bam gëtt dynamesch gefëllt JNI_OnLoad.
DrĂ€i Zuelen codĂ©ieren de Wee am Bam. All Blat vum Bam enthĂ€lt d'Pocked Adress vun der entspriechender Funktioun. De SchlĂ«ssel ass am Elterendeel Node. D'Plaz am Code ze fannen, wou d'Funktioun, dĂ©i mir brauchen, un de Bam bĂ€igefĂŒĂŒgt ass, ass net schwĂ©ier, wann Dir all d'Strukturen versteet, dĂ©i benotzt ginn (mir beschreiwen se net fir net e schonn zimlech groussen Artikel ze bloat).

Méi Obfuscatioun

Mir kruten d'Adress vun der Funktioun, déi de Traffic entschlësselt: 0x5F1AC. Awer et ass ze fréi fir sech ze freeën: d'Entwéckler vum UC Browser hunn eng aner Iwwerraschung fir eis virbereet.

Nodeems mir d'Parameter vun der Array kritt hunn, déi am Java Code geformt gouf, kréien mir
op d'Funktioun op der Adress 0x4D070. An hei waart op eis eng aner Aart vu Code-Obfuscatioun.

Mir setzen zwee Indizes an R7 an R4:

Sich no Schwachstelle am UC Browser

Mir verschécken den éischten Index op R11:

Sich no Schwachstelle am UC Browser

Fir eng Adress aus engem Dësch ze kréien, benotzt en Index:

Sich no Schwachstelle am UC Browser

Nodeems Dir op déi éischt Adress gitt, gëtt den zweeten Index benotzt, deen am R4 ass. Et gi 230 Elementer an der Tabell.

Wat ze maachen doriwwer? Dir kĂ«nnt IDA soen datt dĂ«st e Schalter ass: Änneren -> Aner -> Schalter Idiom uginn.

Sich no Schwachstelle am UC Browser

De resultĂ©ierende Code ass grujeleg. Awer wann Dir Äre Wee duerch sĂ€in Dschungel mĂ©cht, kĂ«nnt Dir en Uruff un eng Funktioun bemierken dĂ©i eis scho vertraut ass sub_6115C:

Sich no Schwachstelle am UC Browser

Et war e Schalter an deem am Fall 3 eng Entschlësselung mam RC4 Algorithmus war. An an dësem Fall ass d'Struktur, déi un d'Funktioun passéiert ass, aus de Parameteren gefëllt, déi un d'Fonktioun passéiert sinn doCommandNative. Loosst eis erënneren wat mir do haten magicInt mam WÀert 16. Mir kucken de entspriechende Fall - an no e puer IwwergÀng fanne mir de Code, duerch deen den Algorithmus identifizéiert ka ginn.

Sich no Schwachstelle am UC Browser

Dëst ass AES!

Den Algorithmus existéiert, alles wat bleift ass seng Parameteren ze kréien: Modus, Schlëssel an eventuell den Initialisierungsvektor (seng PrÀsenz hÀnkt vum Operatiounsmodus vum AES Algorithmus of). D'Struktur mat hinnen muss iergendwou virum Funktiounsopruff geformt ginn sub_6115C, awer dësen Deel vum Code ass besonnesch gutt verstoppt, sou datt d'Iddi entsteet de Code ze patchen sou datt all Parameter vun der Entschlësselungsfunktioun an eng Datei gedumpt ginn.

Flecken

Fir ze vermeiden, datt de ganze Patchcode manuell an der Assemblersprooch geschriwwe gëtt, kënnt Dir folgendes ausféieren: Android Studio, schreift do eng Funktioun, déi déiselwecht Inputparameter empfÀnkt wéi eis Entschlësselungsfunktioun, a schreift se an eng Datei, da kopéiert a pecht de Code, deen de Compiler generéiert.

Eis FrĂ«nn vum UC Browser Team hunn och Ă«m d'Kamoudheet vum Code derbĂ€i gesuergt. Loosst eis drun erĂ«nneren datt mir am Ufank vun all Funktioun MĂŒllcode hunn, dĂ©i einfach mat all aner ersat kĂ«nne ginn. Ganz praktesch 🙂 Ee, am Ufank vun der Zil- Funktioun ass et net genuch Plaz fir de Code, datt all d'Parameteren zu engem Fichier rett. Ech hu missen et an Deeler opzedeelen an MĂŒllblocken aus Nopeschfunktiounen benotzen. Et waren am Ganzen vĂ©ier Deeler.

Den éischten Deel:

Sich no Schwachstelle am UC Browser

An der ARM Architektur ginn déi éischt véier Funktiounsparameter duerch d'Register R0-R3 weidergeleet, de Rescht, wann iwwerhaapt, ginn duerch de Stack passéiert. De LR Register dréit d'Retouradress. All dëst muss gespÀichert ginn fir datt d'Funktioun funktionéiere kann nodeems mir seng Parameteren dumpen. Mir mussen och all Registere spÀicheren, déi mir am Prozess benotzen, also maache mir PUSH.W {R0-R10,LR}. Am R7 kréie mir d'Adress vun der Lëscht vun de Parameteren, déi un d'Funktioun iwwer de Stack weiderginn.

Benotzt d'Funktioun fopen loosst eis d'Datei opmaachen /data/local/tmp/aes am "ab" Modus
dh fir Zousatz. An R0 luede mir d'Adress vum Dateinumm, an R1 - d'Adress vun der Linn dĂ©i de Modus ugeet. An hei endet de MĂŒllcode, also gi mir op dĂ©i nĂ€chst Funktioun weider. Fir datt et weider funktionnĂ©iert, setzen mir am Ufank den Iwwergank zum realen Code vun der Funktioun, Ă«mgoen de MĂŒll, an amplaz vum MĂŒll addĂ©iere mer eng Fortsetzung vum Patch.

Sich no Schwachstelle am UC Browser

Rufft fopen.

Déi éischt drÀi Parameter vun der Funktioun aes hunn Typ INT. Well mir am Ufank d'Register op de Stack gespÀichert hunn, kënne mir d'Funktioun einfach passéieren schreiwen hir Adressen op de Stack.

Sich no Schwachstelle am UC Browser

Als nÀchst hu mir drÀi Strukturen, déi d'Dategréisst enthalen an e Pointer op d'Donnéeën fir de Schlëssel, d'Initialiséierungsvektor a verschlësselte Daten.

Sich no Schwachstelle am UC Browser

Um Enn, schléisst d'Datei, restauréiert d'Register a transferéiert d'Kontroll op déi richteg Funktioun aes.

Mir sammelen eng APK mat enger patchĂ©ierter BibliothĂ©ik, Ă«nnerschreiwe se, lued se op den Apparat / den Emulator erop a starten se. Mir gesinn, datt eisen Dump geschaf gĂ«tt, a vill DonnĂ©eĂ«n do geschriwwe ginn. De Browser benotzt VerschlĂ«sselung net nĂ«mme fir Traffic, an all VerschlĂ«sselung geet duerch d'Funktioun a Fro. Awer aus iergendengem Grond sinn dĂ©i nĂ©ideg DonnĂ©eĂ«n net do, an dĂ©i erfuerderlech Ufro ass net am Traffic ze gesinn. Fir net ze waarden bis den UC Browser deignĂ©iert fir dĂ©i nĂ©ideg Ufro ze maachen, loosst eis d'verschlĂ«sselte Äntwert vum Server huelen, dee virdru kritt gouf an d'Applikatioun erĂ«m patchen: fĂŒĂŒgt d'EntschlĂ«sselung un onCreate vun der HaaptaktivitĂ©it.

    const/16 v1, 0x62
    new-array v1, v1, [B
    fill-array-data v1, :encrypted_data
    const/16 v0, 0x1f
    invoke-static {v0, v1}, Lcom/uc/browser/core/d/c/g;->j(I[B)[B
    move-result-object v1
    array-length v2, v1
    invoke-static {v2}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
    move-result-object v2
    const-string v0, "ololo"
    invoke-static {v0, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

Mir montéieren, ënnerschreiwen, installéieren, starten. Mir kréien eng NullPointerException well d'Methode null zréckginn.

Bei weiderer Analyse vum Code gouf eng Funktioun entdeckt déi interessant Linnen entschlësselt: "META-INF/" an ".RSA". Et gesÀit aus wéi wann d'Applikatioun sÀin Zertifika verifizéiert. Oder souguer generéiert Schlësselen aus et. Ech wëll net wierklech beschÀftegen wat mam Zertifika geschitt, also rutsche mir et just de richtege Zertifika. Loosst d'verschlësselte Linn Patch sou datt amplaz "META-INF /" mir kréien "BLABLINF /", erstellt en Dossier mat deem Numm an der APK an dobÀi d'Kaweechelchen Browser Zertifikat.

Mir montéieren, ënnerschreiwen, installéieren, starten. Bingo! Mir hunn de Schlëssel!

MitM

Mir kruten e SchlĂ«ssel an en Initialisierungsvektor glĂ€ich mam SchlĂ«ssel. Loosst eis probĂ©ieren d'ServerÄntwert am CBC Modus ze entschlĂ«sselen.

Sich no Schwachstelle am UC Browser

Mir gesinn d'Archiv URL, eppes Ă€hnlech wĂ©i MD5, "extract_unzipsize" an eng Zuel. Mir kontrollĂ©ieren: d'MD5 vum Archiv ass d'selwecht, d'GrĂ©isst vun der ausgepackter BibliothĂ©ik ass d'selwecht. Mir probĂ©ieren dĂ«s BibliothĂ©ik ze patchen an et dem Browser ze ginn. Fir ze weisen datt eis patched BibliothĂ©ik gelueden ass, lancĂ©iere mir eng Intent fir eng SMS mam Text "PWNED!" Mir ersetzen zwou Äntwerte vum Server: puds.ucweb.com/upgrade/index.xhtml a fir den Archiv erofzelueden. Am Ă©ischten ersetzen mir MD5 (d'GrĂ©isst Ă€nnert sech net nom Auspacken), an der zweeter gi mir d'Archiv mat der patchĂ©ierter BibliothĂ©ik.

De Browser probéiert d'Archiv e puer Mol erofzelueden, duerno gëtt et e Feeler. Anscheinend eppes
hien huet net gĂ€r. Als Resultat vun der Analyse vun dĂ«sem dĂŒsteren Format huet et erausgestallt datt de Server och d'GrĂ©isst vum Archiv iwwerdrĂ©it:

Sich no Schwachstelle am UC Browser

Et ass am LEB128 kodéiert. Nom Patch huet d'Gréisst vum Archiv mat der Bibliothéik e bëssen geÀnnert, sou datt de Browser geduecht huet datt d'Archiv kromm erofgeluede gouf, an no e puer Versuche gouf et e Feeler.

Mir passen d'GrĂ©isst vum Archiv un ... An - Victoire! 🙂 D'Resultat ass am Video.

https://www.youtube.com/watch?v=Nfns7uH03J8

Konsequenzen an Entwéckler Reaktioun

Am selwechte Wee kënnen Hacker d'onsécher Feature vum UC Browser benotze fir béiswëlleg Bibliothéiken ze verdeelen an ze lafen. Dës Bibliothéike funktionnéieren am Kontext vum Browser, sou datt se all seng Systempermissiounen kréien. Als Resultat ass d'FÀegkeet fir Phishingfenster ze weisen, souwéi Zougang zu den Aarbechtsdateien vum orange chinesesche Kaweechelchen, dorënner Login, Passwierder a Cookien, déi an der Datebank gespÀichert sinn.

Mir hunn d'Entwéckler vum UC Browser kontaktéiert an informéiert hinnen iwwer de Problem dee mir fonnt hunn, probéiert d'Schwachheet a seng Gefor ze weisen, awer si hunn nÀischt mat eis diskutéiert. Mëttlerweil huet de Browser weider mat senger geféierlecher Feature am einfache Siicht flaunt. Awer wa mir d'Detailer vun der Schwachstelle opgedeckt hunn, war et net méi méiglech ze ignoréieren wéi virdrun. 27. MÀerz war
eng nei Versioun vum UC Browser 12.10.9.1193 gouf verëffentlecht, deen de Server iwwer HTTPS zougrÀift: puds.ucweb.com/upgrade/index.xhtml.

ZousĂ€tzlech, no der "Fix" a bis zum ZĂ€itpunkt vum Schreiwen vun dĂ«sem Artikel, probĂ©iert e PDF an engem Browser opzemaachen huet zu enger Fehlermeldung mam Text "Oops, eppes ass falsch gaang!" Eng Ufro un de Server gouf net gemaach wann Dir probĂ©iert e PDF opzemaachen, awer eng Ufro gouf gemaach wann de Browser lancĂ©iert gouf, wat Hiweiser op dĂ©i weider FĂ€egkeet fir ausfĂŒhrbare Code erofzelueden a Verletzung vu Google Play Regelen.

Source: will.com

Kaaft zouverlĂ€sseg Hosting fir Site mat DDoS Schutz, VPS VDS Server đŸ”„ Kaaft zouverlĂ©issegt WebsĂ€ithosting mat DDoS-Schutz, VPS VDS Server | ProHoster