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 net de ganze Patchcode an der Assembléesprooch manuell ze schreiwen, kënnt Dir Android Studio starten, eng Funktioun do schreiwen déi déiselwecht Inputparameter wéi eis Entschlësselungsfunktioun kritt an an eng Datei schreift, da kopéiert-paste de Code deen de Compiler wäert generéieren.

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

Setzt e Commentaire