In cerca di vulnerabilità in u Browser UC

In cerca di vulnerabilità in u Browser UC

Introduzione

À a fine di marzu noi rappurtatu, chì anu scupertu una capacità oculata per carica è eseguisce codice micca verificatu in u Browser UC. Oghje, guardemu in dettagliu cumu si faci sta scaricamentu è cumu i pirate ponu aduprà per i so propiu scopi.

Qualchì tempu fà, UC Browser hè statu annunziatu è distribuitu assai aggressivu: hè statu stallatu nantu à i dispositi di l'utilizatori cù malware, distribuitu da parechji siti sottu l'apparizione di fugliali video (vale à dì, l'utilizatori pensanu chì scaricavanu, per esempiu, un video porno, ma invece ricevutu un APK cù stu navigatore), hà utilizatu banners spaventosi cù missaghji chì u navigatore era obsoletu, vulnerabile, è cose cusì. In u gruppu ufficiale UC Browser in VK ci hè temàticu, in quale l'utilizatori ponu lagnà di publicità inghjusta, ci sò parechji esempii. In 2016 ci era ancu publicità video in russo (sì, publicità per un navigatore ad-blocking).

À u mumentu di a scrittura, UC Browser hà più di installazioni 500 in Google Play. Questu hè impressiunanti - solu Google Chrome hà più. Trà e recensioni pudete vede assai lagnanze nantu à publicità è redirects à alcune applicazioni in Google Play. Questu era u mutivu di a nostra ricerca: avemu decisu di vede se UC Browser facia qualcosa di male. È s'hè risultatu ch'ellu face !

In u codice di l'applicazione, a capacità di scaricà è eseguisce codice eseguibile hè stata scuperta, chì hè cuntrariu à e regule per a publicazione di l'applicazioni nant'à Google Play. In più di scaricà u codice eseguibile, UC Browser faci cusì in modu inseguru, chì pò esse usatu per lancià un attaccu MitM. Videmu s'ellu pudemu fà un tali attaccu.

Tuttu ciò chì hè scrittu quì sottu hè pertinente per a versione di UC Browser chì era dispunibule nantu à Google Play à u mumentu di u studiu:

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

Vettore d'attaccu

In u manifestu UC Browser pudete truvà un serviziu cù un nome auto-spiegativu com.uc.deployment.UpgradeDeployService.

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

Quandu stu serviziu principia, u navigatore face una dumanda POST à puds.ucweb.com/upgrade/index.xhtml, chì pò esse vistu in u trafficu qualchì tempu dopu à u principiu. In risposta, pò riceve un cumandamentu per scaricà qualchì aghjurnamentu o novu modulu. Durante l'analisi, u servitore ùn hà micca datu tali cumandamenti, ma avemu nutatu chì quandu avemu pruvatu à apre un PDF in u navigatore, face una seconda dumanda à l'indirizzu specificatu sopra, dopu chì scaricate a biblioteca nativa. Per fà l'attaccu, avemu decisu d'utilizà sta funzione di UC Browser: a capacità di apre u PDF cù una biblioteca nativa, chì ùn hè micca in l'APK è chì scarica da Internet se ne necessariu. Hè da nutà chì, in teoria, UC Browser pò esse furzatu à scaricà qualcosa senza interazzione di l'utilizatori - se furnisce una risposta ben formata à una dumanda chì hè eseguita dopu chì u navigatore hè lanciatu. Ma per fà questu, avemu bisognu di studià u protokollu di interazzione cù u servitore in più detail, cusì avemu decisu chì saria più faciule per edità a risposta interceptata è rimpiazzà a biblioteca per travaglià cù PDF.

Allora, quandu un utilizatore vole apre un PDF direttamente in u navigatore, e seguenti richieste ponu esse vistu in u trafficu:

In cerca di vulnerabilità in u Browser UC

Prima ci hè una dumanda POST puds.ucweb.com/upgrade/index.xhtml, allora
Un archiviu cù una biblioteca per vede PDF è formati di l'uffiziu hè scaricatu. Hè logicu per suppone chì a prima dumanda trasmette infurmazione nantu à u sistema (almenu l'architettura per furnisce a biblioteca necessaria), è in risposta à questu u navigatore riceve qualchì infurmazione nantu à a biblioteca chì deve esse scaricata: l'indirizzu è, possibbilmente , calcosa altru. U prublema hè chì sta dumanda hè criptata.

Frammentu di dumanda

Frammentu di risposta

In cerca di vulnerabilità in u Browser UC

In cerca di vulnerabilità in u Browser UC

A biblioteca stessa hè imballata in ZIP è ùn hè micca criptata.

In cerca di vulnerabilità in u Browser UC

Cerca u codice di decrittazione di u trafficu

Pruvemu di decifrare a risposta di u servitore. Fighjemu u codice di a classe com.uc.deployment.UpgradeDeployService: da u metudu onStart Command andà à com.uc.deployment.bx, è da ellu à 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);
}

Avemu vistu a furmazione di una dumanda POST quì. Prestemu attenzione à a creazione di un array di 16 bytes è u so riempimentu: 0x5F, 0, 0x1F, -50 (=0xCE). Coincide cù ciò chì avemu vistu in a dumanda sopra.

In a listessa classa pudete vede una classa anidata chì hà un altru mètudu interessante:

        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");
}
}

U metudu piglia un array di byte cum'è input è verifica chì u byte zero hè 0x60 o u terzu byte hè 0xD0, è u sicondu byte hè 1, 11 o 0x1F. Fighjemu a risposta da u servitore: u byte zero hè 0x60, u sicondu hè 0x1F, u terzu hè 0x60. Sembra ciò chì avemu bisognu. Ghjudicate da e linee ("up_decrypt", per esempiu), un metudu deve esse chjamatu quì chì decriptarà a risposta di u servitore.
Andemu à u metudu gj. Nota chì u primu argumentu hè u byte à l'offset 2 (vale à dì 0x1F in u nostru casu), è u sicondu hè a risposta di u servitore senza
primi 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;
}

Ovviamente, quì selezziunate un algoritmu di decryption, è u stessu byte chì hè in u nostru
casu uguale à 0x1F, denota una di e trè opzioni pussibuli.

Cuntinuemu à analizà u codice. Dopu à un paru di salti ci truvamu in un metudu cù un nome auto-spiegativu decryptBytesByKey.

Quì dui byte più sò separati da a nostra risposta, è una stringa hè ottenuta da elli. Hè chjaru chì in questu modu a chjave per decrypting u messagiu hè sceltu.

    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;
}

Fighjendu avanti, avemu nutatu chì in questa tappa ùn avemu micca ancu ottene una chjave, ma solu u so "identificatore". Ottene a chjave hè un pocu più complicata.

In u prossimu metudu, dui paràmetri sò aghjuntu à quelli esistenti, facendu quattru: u numeru magicu 16, l'identificatore chjave, i dati criptati è una stringa incomprensibile (in u nostru casu, viotu).

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

Dopu una seria di transizzioni ghjunghjemu à u metudu staticBinarySafeDecryptNoB64 interfaccia com.alibaba.wireless.security.open.staticdataencrypt.IStaticDataEncryptComponent. Ùn ci hè micca classi in u codice di l'applicazione principale chì implementanu sta interfaccia. Ci hè una tale classa in u schedariu lib/armeabi-v7a/libsgmain.so, chì ùn hè micca veramente un .so, ma un .jar. U metudu chì ci interessa hè implementatu cusì:

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);
}
//...
}

Quì a nostra lista di paràmetri hè cumplementata cù dui interi più: 2 è 0. Ghjudicate da
tuttu, 2 significa decryption, cum'è in u metudu da Finale classa di sistema javax.crypto.Cipher. È tuttu questu hè trasferitu à un certu Router cù u numeru 10601 - questu hè apparentemente u numeru di cumanda.

Dopu à a prossima catena di transizzioni truvamu una classa chì implementa l'interfaccia IRuterComponent è u metudu 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);
}
}

È ancu classa Biblioteca JNIC, in quale u metudu nativu hè dichjaratu doCommandNative:

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

Questu significa chì avemu bisognu di truvà un metudu in u codice nativu doCommandNative. È questu hè induve u divertimentu principia.

Obfuscazione di codice macchina

In u schedariu libsgmain.so (chì hè in realtà un .jar è in quale avemu truvatu l'implementazione di alcune interfacce di criptografia ghjustu sopra) ci hè una biblioteca nativa: libsgmainso-6.4.36.so. Apermu in IDA è uttene una mansa di scatuli di dialogu cù errori. U prublema hè chì a tabella di l'intestazione di a sezione hè invalida. Questu hè fattu apposta per complicà l'analisi.

In cerca di vulnerabilità in u Browser UC

Ma ùn hè micca necessariu: per carricà currettamente un schedariu ELF è analizà, una tabella di l'intestazione di u prugramma hè abbastanza. Per quessa, simpricimenti sguassate a tavola di a sezione, zeroing i campi currispundenti in l'intestazione.

In cerca di vulnerabilità in u Browser UC

Aprite u schedariu in IDA di novu.

Ci hè dui modi per dì à a macchina virtuale Java induve esattamente in a biblioteca nativa si trova l'implementazione di un metudu dichjaratu in u codice Java cum'è nativu. U primu hè di dà un nome di spezia Java_package_name_ClassName_MethodName.

U sicondu hè di registrà quandu si carica a biblioteca (in a funzione JNI_OnLoad)
usendu una chjama di funzione Registru Nativi.

In u nostru casu, se usemu u primu metudu, u nome deve esse cusì: Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative.

Ùn ci hè micca una tale funzione trà e funzioni esportate, chì significa chì avete bisognu di circà una chjama Registru Nativi.
Andemu à a funzione JNI_OnLoad è vedemu sta stampa:

In cerca di vulnerabilità in u Browser UC

Chì si passa quì ? À u primu sguardu, l'iniziu è a fine di a funzione sò tipici per l'architettura ARM. A prima struzzione nantu à a pila guarda u cuntenutu di i registri chì a funzione aduprà in u so funziunamentu (in questu casu, R0, R1 è R2), è ancu u cuntenutu di u registru LR, chì cuntene l'indirizzu di ritornu da a funzione. . L'ultima struzzione restaurà i registri salvati, è l'indirizzu di ritornu hè immediatamente postu in u registru di u PC - cusì torna da a funzione. Ma s'è guardate attentamente, vi vede chì l'istruzzioni penultima cambia l'indirizzu di ritornu guardatu nantu à a pila. Calculemu ciò chì sarà dopu
esecuzione di codice. Un certu indirizzu 1xB0 hè carricu in R130, 5 hè sottrattu da ellu, dopu hè trasferitu à R0 è 0x10 hè aghjuntu à questu. Risulta 0xB13B. Cusì, IDA pensa chì l'ultima struzzione hè un ritornu di funzione nurmale, ma in fattu hè andatu à l'indirizzu calculatu 0xB13B.

Hè vale a pena ricurdà quì chì i prucessori ARM anu dui modi è dui gruppi di struzzioni: ARM è Thumb. U pocu significativu di l'indirizzu dice à u processatore quale set di struzzioni hè stata utilizata. Questu hè, l'indirizzu hè in realtà 0xB13A, è unu in u pocu significativu indica u modalità Thumb.

Un "adapter" simili hè statu aghjuntu à u principiu di ogni funzione in questa biblioteca è
codice basura. Ùn ci stendemu più nantu à elli in dettagliu - ricurdemu solu
chì u veru principiu di quasi tutte e funzioni hè un pocu più luntanu.

Siccomu u codice ùn salta esplicitamente à 0xB13A, IDA stessu ùn hà micca ricunnisciutu chì u codice era situatu in questu locu. Per u listessu mutivu, ùn ricunnosce micca a maiò parte di u codice in a biblioteca cum'è codice, chì rende l'analisi un pocu difficiule. Dicemu à IDA chì questu hè u codice, è questu hè ciò chì succede:

In cerca di vulnerabilità in u Browser UC

A tavula principia chjaramente à 0xB144. Chì ci hè in sub_494C?

In cerca di vulnerabilità in u Browser UC

Quandu chjamate sta funzione in u registru LR, avemu l'indirizzu di a tavula citata prima (0xB144). In R0 - indice in questa tabella. Questu hè, u valore hè pigliatu da a tavula, aghjuntu à LR è u risultatu hè
l'indirizzu per andà. Pruvemu di calculà: 0xB144 + [0xB144 + 8* 4] = 0xB144 + 0x120 = 0xB264. Andemu à l'indirizzu ricevutu è vede literalmente un paru di struzzioni utili è andemu di novu à 0xB140:

In cerca di vulnerabilità in u Browser UC

Avà ci sarà una transizione à offset cù l'indice 0x20 da a tavula.

A ghjudicà da a dimensione di a tavula, ci saranu parechje transizioni tali in u codice. A quistione hè s'ellu hè pussibule di trattà questu più automaticamente, senza calculà manualmente l'indirizzi. E scripts è a capacità di patch codice in IDA venenu à u nostru aiutu:

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"

Pone u cursore nantu à a linea 0xB26A, eseguite u script è vede a transizione à 0xB4B0:

In cerca di vulnerabilità in u Browser UC

L'IDA di novu ùn hà micca ricunnisciutu sta zona cum'è codice. L'aiutemu è vedemu un altru disignu quì:

In cerca di vulnerabilità in u Browser UC

L'istruzzioni dopu à BLX ùn pare micca avè assai sensu, hè più cum'è un tipu di spustamentu. Fighjemu sub_4964:

In cerca di vulnerabilità in u Browser UC

È veramente, quì un dword hè pigliatu à l'indirizzu chì si trova in LR, aghjuntu à questu indirizzu, dopu chì u valore à l'indirizzu risultatu hè pigliatu è mette nantu à a pila. Inoltre, 4 hè aghjuntu à LR in modu chì dopu à vultà da a funzione, u listessu offset hè saltatu. Dopu chì u cumandimu POP {R1} piglia u valore risultatu da a pila. Se guardate ciò chì si trova à l'indirizzu 0xB4BA + 0xEA = 0xB5A4, vi vede qualcosa simili à una tabella di indirizzu:

In cerca di vulnerabilità in u Browser UC

Per patch stu disignu, avete bisognu di ottene dui parametri da u codice: l'offset è u numeru di registru in quale vulete mette u risultatu. Per ogni registru pussibule, avete da preparà un pezzu di codice in anticipu.

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"

Pusemu u cursore à u principiu di a struttura chì vulemu rimpiazzà - 0xB4B2 - è eseguite u script:

In cerca di vulnerabilità in u Browser UC

In più di e strutture digià citate, u codice cuntene ancu i seguenti:

In cerca di vulnerabilità in u Browser UC

Cum'è in u casu precedente, dopu à l'istruzzioni BLX ci hè un offset:

In cerca di vulnerabilità in u Browser UC

Pigliemu l'offset à l'indirizzu da LR, aghjunghje à LR è andemu quì. 0x72044 + 0xC = 0x72050. U script per stu disignu hè abbastanza simplice:

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"

Risultu di l'esecuzione di script:

In cerca di vulnerabilità in u Browser UC

Una volta chì tuttu hè patched in a funzione, pudete indicà IDA à u so veru principiu. Riuniscerà tuttu u codice di funzione, è pò esse decompiled usendu HexRays.

Decodifica di stringhe

Avemu amparatu à trattà cù l'obfuscazione di u codice macchina in a biblioteca libsgmainso-6.4.36.so da UC Browser è ricevutu u codice funzione 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;
}

Fighjemu più attente à e seguenti linee:

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

In funzione sottu_73E24 u nome di a classe hè chjaramente esse decriptatu. Comu paràmetri di sta funzione, un punteru à e dati simili à i dati criptati, un certu buffer è un numeru sò passati. Ovviamente, dopu avè chjamatu a funzione, ci sarà una linea decriptata in u buffer, postu chì hè passatu à a funzione. Truvà a Classe, chì piglia u nome di a classe cum'è u sicondu paràmetru. Dunque, u numeru hè a dimensione di u buffer o a durata di a linea. Pruvemu di decifrare u nome di a classa, ci vole à dì s'ellu andemu in a direzzione bona. Fighjemu un ochju più vicinu à ciò chì succede in sottu_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;
}

funziunava sottu_7AF78 crea una istanza di un containeru per arrays di byte di a dimensione specificata (ùn stendemu micca nantu à sti cuntenituri in dettagliu). Eccu dui cuntenituri tali sò creati: unu cuntene a linea "DcO/lcK+h?m3c*q@" (hè faciule d'invintà chì questu hè una chjave), l'altru cuntene dati criptati. Dopu, i dui ogetti sò posti in una certa struttura, chì hè passatu à a funzione sottu_6115C. Marcamu ancu un campu cù u valore 3 in questa struttura.Videmu ciò chì succede à sta struttura dopu.

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;
}

U paràmetru di u cambiamentu hè un campu di struttura chì hè stata assignata prima u valore 3. Fighjate à u casu 3: à a funzione sottu_6364C i paràmetri sò passati da a struttura chì anu aghjustatu quì in a funzione precedente, vale à dì a chjave è i dati criptati. Se guardate attentamente sottu_6364C, pudete ricunnosce l'algoritmu RC4 in questu.

Avemu un algoritmu è una chjave. Pruvemu di decifrare u nome di a classe. Eccu ciò chì hè accadutu: com/taobao/wireless/security/adapter/JNICLibrary. Perfettu! Semu nantu à a strada bona.

Arbulu di cumanda

Avà avemu bisognu di truvà una sfida Registru Nativi, chì ci indicà à a funzione doCommandNative. Fighjemu e funzioni chjamate da JNI_OnLoad, è a truvamu in sottu_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;
}

È veramente, un metudu nativu cù u nome hè registratu quì doCommandNative. Avà sapemu u so indirizzu. Videmu ciò chì face.

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;
}

Da u nome pudete guessà chì quì hè u puntu di entrata di tutte e funzioni chì i sviluppatori anu decisu di trasfiriri à a biblioteca nativa. Semu interessati à u numeru di funzione 10601.

Pudete vede da u codice chì u numeru di cumanda pruduce trè numeri: cumanda / 10000, cumanda % 10000 / 100 и cumanda % 10, vale à dì, in u nostru casu, 1, 6 è 1. Questi trè numeri, è ancu un punteru à JNIEnv è l'argumenti passati à a funzione sò aghjuntu à una struttura è trasmessi. Utilizendu i trè numeri ottenuti (denutemu N1, N2 è N3), un arbre di cumanda hè custruitu.

Qualcosa cum'è questu:

In cerca di vulnerabilità in u Browser UC

L'arbre hè pienu dinamicamente JNI_OnLoad.
Trè numeri codificanu a strada in l'arbulu. Ogni foglia di l'arbulu cuntene l'indirizzu pocked di a funzione currispundente. A chjave hè in u node parent. Truvà u locu in u codice induve a funzione chì avemu bisognu hè aghjuntu à l'arbulu ùn hè micca difficiule s'ellu capisce tutte e strutture aduprate (ùn l'avemu micca discrittu per ùn gonfià un articulu digià abbastanza grande).

Più offuscazione

Avemu ricevutu l'indirizzu di a funzione chì deve decrypt u trafficu: 0x5F1AC. Ma hè troppu prestu per rallegra: i sviluppatori di UC Browser anu preparatu una altra sorpresa per noi.

Dopu avè ricivutu i paràmetri da l'array chì era furmatu in u codice Java, avemu
à a funzione à l'indirizzu 0x4D070. È quì ci aspetta un altru tipu di offuscazione di codice.

On met deux indices dans R7 et R4 :

In cerca di vulnerabilità in u Browser UC

On déplace le premier indice à R11 :

In cerca di vulnerabilità in u Browser UC

Per piglià un indirizzu da una tavula, utilizate un indice:

In cerca di vulnerabilità in u Browser UC

Dopu à andà à u primu indirizzu, u sicondu indice hè utilizatu, chì hè in R4. Ci sò 230 elementi in a tavula.

Cosa da fà? Pudete dì à IDA chì questu hè un switch: Edit -> Other -> Specify switch idiom.

In cerca di vulnerabilità in u Browser UC

U codice risultatu hè terribili. Ma, facendu u vostru modu à traversu a so jungla, pudete nutà una chjama à una funzione chì ci hè digià familiar sottu_6115C:

In cerca di vulnerabilità in u Browser UC

Ci era un cambiamentu in quale in casu 3 ci era una decryption cù l'algoritmu RC4. È in questu casu, a struttura passata à a funzione hè piena da i paràmetri passati doCommandNative. Ricordemu ciò chì avemu avutu quì magicInt cù u valore 16. Fighjemu u casu currispundente - è dopu à parechje transizzioni truvamu u codice per quale l'algoritmu pò esse identificatu.

In cerca di vulnerabilità in u Browser UC

Questu hè AES!

L'algoritmu esiste, tuttu ciò chì resta hè di ottene i so paràmetri: modalità, chjave è, possibbilmente, u vettore di inizializazione (a so prisenza dipende di u modu di funziunamentu di l'algoritmu AES). A struttura cun elli deve esse furmata in un locu prima di a chjama di a funzione sottu_6115C, Ma sta parte di u codice hè sopratuttu bè obfuscata, perchè l'idea nasce di patch u codice per chì tutti i paràmetri di a funzione di decryption sò scaricati in un schedariu.

Patch

Per ùn scrive micca tuttu u codice di patch in lingua assemblea manualmente, pudete lancià Android Studio, scrivite una funzione quì chì riceve i stessi parametri di input cum'è a nostra funzione di decifrazione è scrive in un schedariu, poi copia-incolla u codice chì u compilatore hà da esse. generà.

I nostri amichi da a squadra di u Browser UC anu ancu cura di a cunvenzione di aghjunghje codice. Ricordemu chì à u principiu di ogni funzione avemu un codice di basura chì pò esse facilmente rimpiazzatu cù qualsiasi altru. Moltu convenientu 🙂 Tuttavia, à u principiu di a funzione di destinazione ùn ci hè micca abbastanza spaziu per u codice chì salva tutti i paràmetri in un schedariu. Aviu avutu a split in parte è aduprà blocchi di basura da funzioni vicini. Ci era quattru parti in totale.

A prima parte:

In cerca di vulnerabilità in u Browser UC

In l'architettura ARM, i primi quattru paràmetri di funzione sò passati per i registri R0-R3, u restu, se qualchissia, hè passatu per a pila. U registru LR porta l'indirizzu di ritornu. Tuttu chistu deve esse salvatu in modu chì a funzione pò travaglià dopu à dump i so paràmetri. Avemu ancu bisognu di salvà tutti i registri chì avemu aduprà in u prucessu, cusì facemu PUSH.W {R0-R10,LR}. In R7 avemu l'indirizzu di a lista di i paràmetri passati à a funzione via a pila.

Utilizà a funzione fopen apremu u schedariu /data/local/tmp/aes in modu "ab".
vale à dì per aghjunghje. In R0 caricamu l'indirizzu di u nome di u schedariu, in R1 - l'indirizzu di a linea chì indica u modu. E quì u codice di basura finisci, cusì andemu à a funzione dopu. Per pudè cuntinuà à travaglià, mettimu à u principiu a transizione à u codice veru di a funzione, sguassendu a basura, è invece di a basura aghjunghjemu una continuazione di u patch.

In cerca di vulnerabilità in u Browser UC

Chjama fopen.

I primi trè paràmetri di a funzione Eram avè tipu tram. Siccomu avemu salvatu i registri à a pila à u principiu, pudemu solu passà a funzione fscrivi i so indirizzi nantu à a pila.

In cerca di vulnerabilità in u Browser UC

Dopu avemu trè strutture chì cuntenenu a dimensione di dati è un punteru à i dati per a chjave, u vettore di inizializazione è i dati criptati.

In cerca di vulnerabilità in u Browser UC

À a fine, chjude u schedariu, restaurà i registri è trasferisce u cuntrollu à a funzione vera Eram.

Raccogliemu un APK cù una libreria patched, firmamu, carichemu à u dispositivu / emulatore, è lanciamu. Avemu vistu chì u nostru dump hè creatu, è assai dati hè scrittu quì. U navigatore usa a criptografia micca solu per u trafficu, è tutta a criptografia passa per a funzione in quistione. Ma per qualchì mutivu i dati necessarii ùn sò micca quì, è a dumanda necessaria ùn hè micca visibile in u trafficu. Per ùn aspittà micca finu à chì u navigatore UC si degna per fà a dumanda necessaria, pigliamu a risposta criptata da u servitore ricevutu prima è patch l'applicazione di novu: aghjunghje a decryption à onCreate di l'attività principale.

    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

Assemblamu, firmemu, installemu, lanciamu. Ricevemu una NullPointerException perchè u metudu hà tornatu null.

Durante l'analisi ulteriore di u codice, hè stata scuperta una funzione chì decifra i linii interessanti: "META-INF/" è ".RSA". Sembra chì l'applicazione verifica u so certificatu. O ancu genera chjave da questu. Ùn vogliu micca veramente affruntà ciò chì succede cù u certificatu, per quessa, l'avemu solu slip u certificatu currettu. Patch a linea criptata in modu chì invece di "META-INF/" avemu "BLABLINF/", crea un cartulare cù quellu nome in l'APK è aghjunghje u certificatu di u navigatore squirrel.

Assemblamu, firmemu, installemu, lanciamu. Bingo ! Avemu a chjave!

MitM

Avemu ricevutu una chjave è un vettore d'inizializazione uguale à a chjave. Pruvemu di decifrare a risposta di u servitore in modu CBC.

In cerca di vulnerabilità in u Browser UC

Avemu vistu l'URL di l'archiviu, qualcosa simili à MD5, "extract_unzipsize" è un numeru. Cuntrollamu: u MD5 di l'archiviu hè u stessu, a dimensione di a biblioteca unpacked hè a stessa. Pruvemu di patch sta biblioteca è dà à u navigatore. Per dimustrà chì a nostra biblioteca patched hè stata caricata, lanceremu una Intenzione di creà un SMS cù u testu "PWNED!" Sustituiremu duie risposte da u servitore: puds.ucweb.com/upgrade/index.xhtml è per scaricà l'archiviu. In u primu rimpiazzà MD5 (a dimensione ùn cambia micca dopu à unpacking), in u sicondu demu l'archiviu cù a biblioteca patched.

U navigatore prova di scaricà l'archiviu parechje volte, dopu chì dà un errore. Apparentemente qualcosa
ùn li piace micca. In u risultatu di l'analisi di stu formatu torbu, hè risultatu chì u servitore trasmette ancu a dimensione di l'archiviu:

In cerca di vulnerabilità in u Browser UC

Hè codificata in LEB128. Dopu à u patch, a dimensione di l'archiviu cù a biblioteca hà cambiatu un pocu, cusì u navigatore hà cunsideratu chì l'archiviu hè stata telecaricata in modu currettu, è dopu à parechji tentativi hà fattu un errore.

Ajustemu a dimensione di l'archiviu... È - vittoria! 🙂 U risultatu hè in u video.

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

Cunsequenze è reazione di u sviluppatore

In u listessu modu, i pirate puderanu aduprà a funzione insicura di UC Browser per distribuisce è eseguisce biblioteche maliziusi. Queste biblioteche funzionanu in u cuntestu di u navigatore, perchè riceveranu tutti i so permessi di u sistema. In u risultatu, a capacità di vede Windows phishing, è ancu l'accessu à i schedarii di travagliu di l'aranciu squirrel Chinese, cumpresi logins, password è cookies almacenati in a basa di dati.

Avemu cuntattatu i sviluppatori di UC Browser è l'avemu infurmatu nantu à u prublema chì avemu trovu, pruvatu à indicà a vulnerabilità è u so periculu, ma ùn anu micca discututu nunda cun noi. Intantu, u navigatore hà cuntinuatu à affissà a so funzione periculosa in vista. Ma una volta avemu revelatu i dettagli di a vulnerabilità, ùn era più pussibule di ignurà cum'è prima. U 27 di marzu era
una nova versione di UC Browser 12.10.9.1193 hè stata liberata, chì accede à u servitore via HTTPS: puds.ucweb.com/upgrade/index.xhtml.

Inoltre, dopu a "riparazione" è finu à u mumentu di a scrittura di stu articulu, pruvate d'apre un PDF in un navigatore hà risultatu in un missaghju d'errore cù u testu "Oops, qualcosa hè andatu male!" Una dumanda à u servitore ùn hè micca stata fatta quandu pruvate d'apre un PDF, ma una dumanda hè stata fatta quandu u navigatore hè stata lanciata, chì suggerisce a capacità continuata di scaricà codice eseguibile in violazione di e regule di Google Play.

Source: www.habr.com

Add a comment