Nggoleki kerentanan ing UC Browser

Nggoleki kerentanan ing UC Browser

Pambuka

Ing pungkasan Maret kita kacarita, yen dheweke nemokake kemampuan sing didhelikake kanggo mbukak lan mbukak kode sing ora diverifikasi ing UC Browser. Dina iki kita bakal nliti kanthi rinci babagan carane download iki kedadeyan lan carane peretas bisa nggunakake kanggo tujuane dhewe.

Sawetara wektu kepungkur, UC Browser diiklanake lan disebarake kanthi agresif: diinstal ing piranti pangguna nggunakake malware, disebarake saka macem-macem situs kanthi kedok file video (yaiku, pangguna ngira yen lagi ngundhuh, contone, video porno, nanging tinimbang nampa APK nganggo browser iki), nggunakake spanduk medeni kanthi pesen manawa browser kasebut wis ketinggalan jaman, rawan, lan liya-liyane. Ing grup UC Browser resmi ing VK ana tema, ing ngendi pangguna bisa sambat babagan iklan sing ora adil, ana akeh conto ing kana. Ing 2016 ana malah iklan video ing basa Rusia (ya, iklan kanggo browser pamblokiran iklan).

Nalika nulis, UC Browser duwe luwih saka 500 panginstalan ing Google Play. Iki nyengsemaken - mung Google Chrome sing duwe liyane. Ing antarane review sampeyan bisa ndeleng cukup akeh keluhan babagan pariwara lan pangalihan menyang sawetara aplikasi ing Google Play. Iki minangka alesan kanggo riset kita: kita mutusake kanggo ndeleng apa UC Browser nindakake tumindak ala. Lan ternyata dheweke nindakake!

Ing kode aplikasi, kemampuan kanggo ndownload lan mbukak kode eksekusi ditemokake, sing bertentangan karo aturan kanggo nerbitake aplikasi ing Google Play. Saliyane ndownload kode eksekusi, UC Browser nindakake kanthi cara sing ora aman, sing bisa digunakake kanggo miwiti serangan MitM. Ayo ndeleng apa kita bisa nindakake serangan kaya ngono.

Kabeh sing ditulis ing ngisor iki cocog kanggo versi UC Browser sing kasedhiya ing Google Play nalika sinau:

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

Vektor serangan

Ing manifest UC Browser sampeyan bisa nemokake layanan kanthi jeneng sing jelas com.uc.deployment.UpgradeDeployService.

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

Nalika layanan iki diwiwiti, browser nggawe request POST kanggo puds.ucweb.com/upgrade/index.xhtml, sing bisa dideleng ing lalu lintas sawetara wektu sawise wiwitan. Nanggepi, kang bisa nampa printah kanggo download sawetara nganyari utawa modul anyar. Sajrone analisis, server ora menehi prentah kasebut, nanging kita ngerteni manawa nalika nyoba mbukak PDF ing browser, bakal njaluk panjaluk kaping pindho menyang alamat sing kasebut ing ndhuwur, lan banjur ndownload perpustakaan asli. Kanggo nindakake serangan kasebut, kita mutusake nggunakake fitur UC Browser iki: kemampuan kanggo mbukak PDF nggunakake perpustakaan asli, sing ora ana ing APK lan diundhuh saka Internet yen perlu. Wigati dicathet yen, kanthi teoritis, UC Browser bisa dipeksa ndownload soko tanpa interaksi pangguna - yen sampeyan menehi respon sing apik kanggo panjaluk sing ditindakake sawise browser diluncurake. Nanging kanggo nindakake iki, kita kudu nyinaoni protokol interaksi karo server kanthi luwih rinci, mula kita mutusake manawa bakal luwih gampang nyunting tanggapan sing dicegat lan ngganti perpustakaan kanggo nggarap PDF.

Dadi, nalika pangguna pengin mbukak PDF langsung ing browser, panjaluk ing ngisor iki bisa dideleng ing lalu lintas:

Nggoleki kerentanan ing UC Browser

Pisanan ana panjalukan POST kanggo puds.ucweb.com/upgrade/index.xhtml, banjur
Arsip kanthi perpustakaan kanggo ndeleng format PDF lan kantor diundhuh. Iku logis kanggo nganggep yen panjalukan pisanan ngirimake informasi babagan sistem (paling ora arsitektur kanggo nyedhiyakake perpustakaan sing dibutuhake), lan kanggo nanggepi browser nampa sawetara informasi babagan perpustakaan sing kudu didownload: alamat lan, bisa uga. , liyane. Masalahe yaiku panyuwunan iki dienkripsi.

Njaluk fragmen

Wangsulan pecahan

Nggoleki kerentanan ing UC Browser

Nggoleki kerentanan ing UC Browser

Perpustakaan kasebut dhewe dikemas ing ZIP lan ora dienkripsi.

Nggoleki kerentanan ing UC Browser

Telusuri kode dekripsi lalu lintas

Coba decipher respon server. Ayo ndeleng kode kelas com.uc.deployment.UpgradeDeployService: saka metode onStartCommand pindhah menyang com.uc.deployment.bx, lan saka iku kanggo 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);
}

We ndeleng tatanan saka request POST kene. Kita menehi perhatian marang nggawe array 16 bait lan ngisi: 0x5F, 0, 0x1F, -50 (=0xCE). Cocog karo apa sing kita deleng ing panyuwunan ing ndhuwur.

Ing kelas sing padha, sampeyan bisa ndeleng kelas bersarang sing nduweni cara liyane sing menarik:

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

Cara kasebut njupuk array bait minangka input lan mriksa manawa bait nol yaiku 0x60 utawa bait katelu yaiku 0xD0, lan bait kapindho yaiku 1, 11 utawa 0x1F. Kita ndeleng respon saka server: bait nol yaiku 0x60, sing kapindho yaiku 0x1F, sing katelu yaiku 0x60. Muni kaya apa kita kudu. Ditilik dening garis ("up_decrypt", contone), cara kudu disebut ing kene sing bakal decrypt respon server.
Ayo pindhah menyang cara gj. Elinga yen argumen pisanan yaiku byte ing offset 2 (yaiku 0x1F ing kasus kita), lan sing nomer loro yaiku respon server tanpa
pisanan 16 bait.

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

Temenan, ing kene kita milih algoritma dekripsi, lan bait sing padha ing kita
cilik witjaksono kanggo 0x1F, nuduhake salah siji saka telung opsi bisa.

Kita terus nganalisa kode kasebut. Sawise saperangan mlumpat kita temokake dhéwé ing cara karo jeneng poto-panjelasan decryptBytesByKey.

Ing kene rong bait liyane dipisahake saka respon kita, lan senar dijupuk saka wong-wong mau. Cetha yen kanthi cara iki kunci kanggo dekripsi pesen dipilih.

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

Ing ngarep, kita nyathet yen ing tahap iki kita durung entuk kunci, nanging mung "pengenal". Njupuk kunci rada rumit.

Ing cara sabanjure, rong paramèter sing wis ana ditambahake, dadi papat: nomer ajaib 16, pengenal kunci, data sing dienkripsi, lan senar sing ora bisa dingerteni (ing kasus kita, kosong).

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

Sawise sawetara transisi, kita teka ing cara kasebut staticBinarySafeDecryptNoB64 antarmuka com.alibaba.wireless.security.open.staticdataencrypt.IStaticDataEncryptComponent. Ora ana kelas ing kode aplikasi utama sing ngetrapake antarmuka iki. Ana kelas kasebut ing file kasebut lib/armeabi-v7a/libsgmain.so, kang ora bener .dadi, nanging .jar. Cara sing kita minati ditindakake kaya ing ngisor iki:

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

Ing kene dhaptar paramèter kita ditambah karo rong integer: 2 lan 0. Ditilik dening
kabeh, 2 tegese decryption, minangka ing cara doFinal kelas sistem javax.crypto.Cipher. Lan kabeh iki ditransfer menyang Router tartamtu karo nomer 10601 - iki ketoke nomer printah.

Sawise chain transisi sabanjuré kita nemokake kelas sing ngleksanakake antarmuka IRouterComponent lan metode 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);
}
}

Lan uga kelas Perpustakaan JNICL, ing ngendi cara asli diumumake doCommandNative:

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

Iki tegese kita kudu nemokake cara ing kode asli doCommandNative. Lan iki ngendi fun wiwit.

Obfuscation saka kode mesin

Ing file libsgmain.so (sing bener .jar lan ing ngendi kita nemokake implementasine sawetara antarmuka sing gegandhengan karo enkripsi ing ndhuwur) ana siji perpustakaan asli: libsgmainso-6.4.36.so. Kita mbukak ing IDA lan njaluk Bunch saka kothak dialog karo kasalahan. Masalahe yaiku tabel header bagean ora valid. Iki ditindakake kanthi tujuan kanggo nyepetake analisis.

Nggoleki kerentanan ing UC Browser

Nanging ora perlu: kanggo mbukak file ELF kanthi bener lan nganalisa, tabel header program cukup. Mulane, kita mung mbusak tabel bagean, nul metu kolom sing cocog ing header.

Nggoleki kerentanan ing UC Browser

Bukak file ing IDA maneh.

Ana rong cara kanggo ngandhani mesin virtual Jawa ing ngendi persis ing perpustakaan asli, implementasine metode sing diumumake ing kode Jawa minangka asli. Sing pisanan yaiku menehi jeneng spesies Java_package_name_ClassName_MethodName.

Kapindho yaiku ndhaptar nalika mbukak perpustakaan (ing fungsi JNI_OnLoad)
nggunakake telpon fungsi RegisterNatives.

Ing kasus kita, yen kita nggunakake cara pisanan, jeneng kudu kaya iki: Java_com_taobao_wireless_security_adapter_JNICLlibrary_doCommandNative.

Ora ana fungsi kasebut ing antarane fungsi sing diekspor, tegese sampeyan kudu golek telpon RegisterNatives.
Ayo menyang fungsi JNI_OnLoad lan kita ndeleng gambar iki:

Nggoleki kerentanan ing UC Browser

Apa sing kedadeyan ing kene? Sepisanan, wiwitan lan pungkasan fungsi kasebut khas kanggo arsitektur ARM. Instruksi pisanan ing tumpukan nyimpen isi ndhaftar sing fungsi bakal digunakake ing operasi (ing kasus iki, R0, R1 lan R2), uga isi LR register, kang ngemot alamat bali saka fungsi. . Instruksi pungkasan mulihake ndhaftar sing disimpen, lan alamat bali langsung diselehake ing ndhaptar PC - saéngga bali saka fungsi kasebut. Nanging yen katon rapet, sampeyan bakal sok dong mirsani yen instruksi penultimate ngganti alamat bali sing disimpen ing tumpukan. Ayo ngetung kaya apa sawise
eksekusi kode. Alamat tartamtu 1xB0 dimuat menyang R130, 5 dikurangi, banjur ditransfer menyang R0 lan 0x10 ditambahake. Pranyata 0xB13B. Mangkono, IDA mikir yen instruksi pungkasan minangka bali fungsi normal, nanging nyatane bakal menyang alamat sing diwilang 0xB13B.

Perlu dielingake yen prosesor ARM duwe rong mode lan rong set instruksi: ARM lan Thumb. Alamat paling ora penting ngandhani prosesor sing set instruksi digunakake. Yaiku, alamat kasebut bener-bener 0xB13A, lan siji sing paling sithik nuduhake mode Jempol.

A padha "adaptor" wis ditambahake ing awal saben fungsi ing perpustakaan iki lan
kode sampah. Kita ora bakal ngelingi kanthi rinci - kita mung ngelingi
sing wiwitan nyata meh kabeh fungsi punika sethitik luwih adoh.

Amarga kode kasebut ora langsung mlumpat menyang 0xB13A, IDA dhewe ora ngerteni manawa kode kasebut ana ing lokasi kasebut. Kanggo alesan sing padha, ora ngenali paling kode ing perpustakaan minangka kode, kang ndadekake analisis Luwih angel. Kita ngandhani IDA yen iki kode, lan iki kedadeyan:

Nggoleki kerentanan ing UC Browser

Tabel kasebut kanthi jelas diwiwiti ing 0xB144. Apa sing ana ing sub_494C?

Nggoleki kerentanan ing UC Browser

Nalika nelpon fungsi iki ing ndhaptar LR, kita njaluk alamat meja kasebut sadurunge (0xB144). Ing R0 - indeks ing tabel iki. Sing, Nilai dijupuk saka meja, ditambahake kanggo LR lan asil
alamat sing arep dituju. Coba etung: 0xB144 + [0xB144 + 8* 4] = 0xB144 + 0x120 = 0xB264. Kita menyang alamat sing ditampa lan ndeleng kanthi harfiah sawetara instruksi sing migunani lan maneh menyang 0xB140:

Nggoleki kerentanan ing UC Browser

Saiki bakal ana transisi ing offset karo indeks 0x20 saka tabel.

Miturut ukuran meja, bakal ana akeh transisi ing kode kasebut. Pitakonan muncul apa sampeyan bisa ngatasi masalah kasebut kanthi otomatis, tanpa ngetung alamat kanthi manual. Lan skrip lan kemampuan kanggo nambal kode ing IDA mbantu kita:

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"

Selehake kursor ing baris 0xB26A, mbukak script lan ndeleng transisi kanggo 0xB4B0:

Nggoleki kerentanan ing UC Browser

IDA maneh ora ngenali wilayah iki minangka kode. Kita nulungi dheweke lan ndeleng desain liyane ing kana:

Nggoleki kerentanan ing UC Browser

Pandhuan sawise BLX koyone ora nggawe akeh pangertèn, iku luwih kaya sawetara jenis pamindahan. Ayo ndeleng sub_4964:

Nggoleki kerentanan ing UC Browser

Lan pancen, ing kene dword dijupuk ing alamat sing ana ing LR, ditambahake ing alamat iki, sawise nilai ing alamat sing diasilake dijupuk lan dilebokake ing tumpukan. Kajaba iku, 4 ditambahake menyang LR supaya sawise bali saka fungsi kasebut, offset sing padha dilewati. Sawise printah POP {R1} njupuk nilai asil saka tumpukan. Yen sampeyan ndeleng apa sing ana ing alamat 0xB4BA + 0xEA = 0xB5A4, sampeyan bakal weruh sing padha karo tabel alamat:

Nggoleki kerentanan ing UC Browser

Kanggo nambal desain iki, sampeyan kudu entuk rong parameter saka kode: offset lan nomer registrasi sing pengin dilebokake asil. Kanggo saben ndhaptar bisa, sampeyan kudu nyiapake potongan kode luwih dhisik.

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"

Kita nyelehake kursor ing awal struktur sing pengin diganti - 0xB4B2 - lan mbukak skrip:

Nggoleki kerentanan ing UC Browser

Saliyane struktur sing wis kasebut, kode kasebut uga ngemot:

Nggoleki kerentanan ing UC Browser

Kaya ing kasus sadurunge, sawise instruksi BLX ana offset:

Nggoleki kerentanan ing UC Browser

We njupuk offset kanggo alamat saka LR, nambah menyang LR lan pindhah ana. 0x72044 + 0xC = 0x72050. Skrip kanggo desain iki cukup prasaja:

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"

Hasil eksekusi script:

Nggoleki kerentanan ing UC Browser

Sawise kabeh ditambal ing fungsi kasebut, sampeyan bisa ngarahake IDA menyang wiwitan sing nyata. Iku bakal Piece bebarengan kabeh kode fungsi, lan bisa decompiled nggunakake HexRays.

Decoding strings

Kita wis sinau kanggo menehi hasil karo obfuscation kode mesin ing perpustakaan libsgmainso-6.4.36.so saka UC Browser lan nampa kode fungsi 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;
}

Ayo katon luwih cetha ing baris ing ngisor iki:

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

Ing fungsi sub_73E24 jeneng kelas wis cetha decrypted. Minangka paramèter kanggo fungsi iki, pointer kanggo data padha karo data ndhelik, buffer tartamtu lan nomer liwati. Temenan, sawise nelpon fungsi kasebut, bakal ana baris dekripsi ing buffer, amarga diterusake menyang fungsi kasebut. GolekKelas, sing njupuk jeneng kelas minangka parameter kapindho. Mulane, nomer kasebut minangka ukuran buffer utawa dawa garis. Ayo dadi nyoba kanggo decipher jeneng kelas, iku kudu ngomong apa kita arep ing arah tengen. Ayo dadi njupuk dipikir nyedhaki ing apa mengkono ing 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;
}

fungsi sub_7AF78 nggawe conto saka wadhah kanggo array bait saka ukuran sing ditemtokake (kita ora bakal manggon ing wadhah kasebut kanthi rinci). Ing kene loro kontaner kasebut digawe: siji ngemot baris "DcO/lcK+h?m3c*q@" (gampang kanggo guess sing iki tombol), liyane ngandhut data ndhelik. Sabanjure, loro obyek dilebokake ing struktur tartamtu, sing diterusake menyang fungsi kasebut sub_6115C. Ayo uga menehi tandha lapangan kanthi nilai 3 ing struktur iki. Ayo ndeleng apa sing kedadeyan ing struktur iki sabanjure.

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

Parameter switch minangka kolom struktur sing sadurunge diwenehi nilai 3. Deleng kasus 3: menyang fungsi sub_6364C paramèter liwati saka struktur sing ditambahake ana ing fungsi sadurungé, IE tombol lan data ndhelik. Yen sampeyan ndeleng kanthi teliti sub_6364C, sampeyan bisa ngenali algoritma RC4 ing.

Kita duwe algoritma lan kunci. Coba decipher jeneng kelas. Mangkene kedadeyane: com / taobao / nirkabel / keamanan / adaptor / JNICLlibrary. apik tenan! Kita ana ing dalan sing bener.

Wit printah

Saiki kita kudu golek tantangan RegisterNatives, sing bakal nggawa kita menyang fungsi kasebut doCommandNative. Ayo kang katon ing fungsi disebut saka JNI_OnLoad, lan kita nemokake ing 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;
}

Lan pancen, cara asli kanthi jeneng kasebut didaftar ing kene doCommandNative. Saiki kita ngerti alamate. Ayo ndeleng apa sing ditindakake.

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

Kanthi jeneng sampeyan bisa ngira manawa iki minangka titik entri kabeh fungsi sing diputusake para pangembang kanggo transfer menyang perpustakaan asli. Kita kasengsem ing fungsi nomer 10601.

Sampeyan bisa ndeleng saka kode sing nomer printah ngasilake telung nomer: printah / 10000, printah% 10000 / 100 и printah% 10, I.e., ing kasus kita, 1, 6 lan 1. Iki nomer telu, uga pointer kanggo JNIEnv lan argumen sing diterusake menyang fungsi ditambahake menyang struktur lan diterusake. Nggunakake telung nomer sing dipikolehi (ayo nuduhake N1, N2 lan N3), wit perintah dibangun.

Kaya iki:

Nggoleki kerentanan ing UC Browser

Wit kasebut diisi kanthi dinamis JNI_OnLoad.
Telung nomer encode path ing wit. Saben godhong wit ngemot alamat pocked saka fungsi sing cocog. Tombol ana ing simpul induk. Nemokake papan ing kode ing ngendi fungsi sing kita butuhake ditambahake ing wit ora angel yen sampeyan ngerti kabeh struktur sing digunakake (kita ora njlèntrèhaké supaya ora bloat artikel sing wis rada gedhe).

Luwih obfuscation

Kita nampa alamat fungsi sing kudu dekripsi lalu lintas: 0x5F1AC. Nanging isih awal banget kanggo bungah: pangembang UC Browser wis nyiapake kejutan liyane kanggo kita.

Sawise nampa paramèter saka array sing dibentuk ing kode Jawa, kita entuk
menyang fungsi ing alamat 0x4D070. Lan ing kene ana jinis obfuscation kode liyane sing nunggu kita.

Kita sijine loro indeks ing R7 lan R4:

Nggoleki kerentanan ing UC Browser

Kita mindhah indeks pisanan menyang R11:

Nggoleki kerentanan ing UC Browser

Kanggo njupuk alamat saka tabel, gunakake indeks:

Nggoleki kerentanan ing UC Browser

Sawise pindhah menyang alamat pisanan, indeks kapindho digunakake, yaiku ing R4. Ana 230 unsur ing tabel.

Apa sing kudu ditindakake? Sampeyan bisa ngandhani IDA yen iki saklar: Sunting -> Liyane -> Nemtokake idiom switch.

Nggoleki kerentanan ing UC Browser

Kode asil elek. Nanging, liwat alas, sampeyan bisa sok dong mirsani telpon kanggo fungsi wis menowo kanggo kita sub_6115C:

Nggoleki kerentanan ing UC Browser

Ana ngalih kang ing kasus 3 ana decryption nggunakake algoritma RC4. Lan ing kasus iki, struktur liwati kanggo fungsi diisi saka paramèter liwati menyang doCommandNative. Ayo padha ngelingi apa sing ana ing kana magicInt karo nilai 16. We katon ing kasus sing cocog - lan sawise sawetara transisi kita nemokake kode kang algoritma bisa dikenali.

Nggoleki kerentanan ing UC Browser

Iki AES!

Algoritma kasebut ana, sing isih ana yaiku entuk paramèter: mode, tombol lan, bisa uga, vektor initialization (anane gumantung saka mode operasi algoritma AES). Struktur karo wong-wong mau kudu kawangun nang endi wae sadurunge telpon fungsi sub_6115C, nanging bagéan saka kode iki utamané uga obfuscated, supaya idea muncul kanggo patch kode supaya kabeh paramèter saka fungsi decryption dibuwang menyang file.

Tambalan

Supaya ora nulis kabeh kode patch ing basa perakitan kanthi manual, sampeyan bisa miwiti Android Studio, nulis fungsi ing kana sing nampa parameter input sing padha karo fungsi dekripsi kita lan nulis menyang file, banjur nyalin-tempel kode sing bakal dikompiler. ngasilaken.

Kanca-kanca saka tim UC Browser uga ngurusi penak kanggo nambah kode. Ayo kita elinga yen ing wiwitan saben fungsi kita duwe kode sampah sing bisa gampang diganti karo liyane. Trep banget 🙂 Nanging, ing wiwitan fungsi target ora cukup spasi kanggo kode sing nyimpen kabeh parameter menyang file. Aku kudu dibagi dadi bagean lan nggunakake blok sampah saka fungsi tetanggan. Ana papat bagean total.

Pérangan pisanan:

Nggoleki kerentanan ing UC Browser

Ing arsitektur ARM, papat paramèter fungsi pisanan dilewati liwat register R0-R3, liyane, yen ana, liwati tumpukan. Register LR nggawa alamat bali. Kabeh iki kudu disimpen supaya fungsi bisa digunakake sawise kita mbucal paramèter. Kita uga kudu nyimpen kabeh ndhaftar sing bakal digunakake ing proses, supaya kita nindakake PUSH.W {R0-R10,LR}. Ing R7 kita entuk alamat dhaptar paramèter sing diterusake menyang fungsi liwat tumpukan.

Nggunakake fungsi fopen ayo mbukak file kasebut /data/local/tmp/aes ing mode "ab".
yaiku kanggo tambahan. Ing R0 kita mbukak alamat jeneng file, ing R1 - alamat baris sing nuduhake mode. Lan ing kene kode sampah rampung, supaya kita pindhah menyang fungsi sabanjure. Supaya bisa terus, kita sijine ing awal transisi menyang kode nyata saka fungsi, bypassing uwuh, lan tinimbang uwuh kita nambah terusan saka tembelan.

Nggoleki kerentanan ing UC Browser

nelpon fopen.

Telung paramèter pisanan saka fungsi AES duwe jinis int. Wiwit kita nyimpen ndhaftar menyang tumpukan ing awal, kita mung bisa pass fungsi nulis alamat ing tumpukan.

Nggoleki kerentanan ing UC Browser

Sabanjure kita duwe telung struktur sing ngemot ukuran data lan pointer menyang data kanggo kunci, vektor initialization lan data ndhelik.

Nggoleki kerentanan ing UC Browser

Ing pungkasan, nutup file, mulihake ndhaftar lan transfer kontrol menyang fungsi nyata AES.

Kita ngumpulake APK karo perpustakaan patched, mlebu, upload menyang piranti / emulator, lan miwiti. Kita weruh manawa mbucal kita lagi digawe, lan akeh data sing ditulis ing kana. Browser nggunakake enkripsi ora mung kanggo lalu lintas, lan kabeh enkripsi dadi liwat fungsi ing pitakonan. Nanging sakperangan alesan data sing dibutuhake ora ana, lan panjaluk sing dibutuhake ora katon ing lalu lintas. Supaya ora ngenteni nganti UC Browser njaluk panjaluk sing dibutuhake, ayo njupuk tanggapan sing dienkripsi saka server sing ditampa sadurunge lan tempel maneh aplikasi kasebut: tambahake dekripsi menyang onCreate kegiatan utama.

    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

Kita ngumpul, mlebu, nginstal, miwiti. Kita nampa NullPointerException amarga cara bali null.

Sajrone analisis luwih saka kode, ditemokaké fungsi sing deciphers garis menarik: "META-INF /" lan ".RSA". Katon kaya aplikasi kasebut verifikasi sertifikat. Utawa malah ngasilake kunci saka iku. Aku ora pengin menehi hasil karo apa sing kedadeyan karo sertifikat kasebut, mula kita bakal nyelehake sertifikat sing bener. Ayo patch baris ndhelik supaya tinimbang "META-INF /" kita njaluk "BLABLINF /", nggawe folder karo jeneng kasebut ing APK lan nambah sertifikat browser bajing ana.

Kita ngumpul, mlebu, nginstal, miwiti. Bingo! Kita duwe kunci!

MitM

Kita nampa tombol lan vektor initialization padha karo tombol. Coba dekripsi respon server ing mode CBC.

Nggoleki kerentanan ing UC Browser

Kita ndeleng URL arsip, sing padha karo MD5, "extract_unzipsize" lan nomer. Kita mriksa: MD5 arsip padha, ukuran perpustakaan sing ora dibungkus padha. Kita nyoba patch perpustakaan iki lan menehi menyang browser. Kanggo nuduhake manawa perpustakaan sing ditambal wis dimuat, kita bakal miwiti Niat kanggo nggawe SMS kanthi teks "PWNED!" Kita bakal ngganti rong tanggapan saka server: puds.ucweb.com/upgrade/index.xhtml lan kanggo ngundhuh arsip. Ing kawitan kita ngganti MD5 (ukuran ora ngganti sawise unpacking), ing kaloro menehi arsip karo perpustakaan patched.

Browser nyoba ndownload arsip kaping pirang-pirang, banjur menehi kesalahan. Ketoke soko
dheweke ora seneng. Minangka asil nganalisa format murky iki, ternyata server uga ngirim ukuran arsip:

Nggoleki kerentanan ing UC Browser

Iki dienkode ing LEB128. Sawise tembelan, ukuran arsip karo perpustakaan diganti sethithik, mula browser nganggep manawa arsip kasebut diundhuh kanthi bengkok, lan sawise sawetara upaya, kesalahan kasebut.

Kita nyetel ukuran arsip ... Lan - kamenangan! 🙂 Asil ana ing video.

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

Akibat lan reaksi pangembang

Kanthi cara sing padha, peretas bisa nggunakake fitur UC Browser sing ora aman kanggo nyebarake lan mbukak perpustakaan sing jahat. Pustaka kasebut bakal bisa digunakake ing konteks browser, saengga bakal nampa kabeh ijin sistem kasebut. Akibaté, kemampuan kanggo nampilake jendhela phishing, uga akses menyang file kerja bajing Cina oranye, kalebu logins, sandhi lan cookie sing disimpen ing database.

Kita ngubungi pangembang UC Browser lan ngandhani babagan masalah sing ditemokake, nyoba nunjukake kerentanan lan bebaya, nanging dheweke ora ngrembug apa-apa karo kita. Sauntara kuwi, browser kasebut terus nampilake fitur mbebayani kanthi jelas. Nanging yen kita mbukak rincian kerentanan kasebut, mula ora bisa ditolak maneh kaya sadurunge. 27 Maret iku
versi anyar UC Browser 12.10.9.1193 dirilis, sing ngakses server liwat HTTPS: puds.ucweb.com/upgrade/index.xhtml.

Kajaba iku, sawise "ndandani" lan nganti wektu nulis artikel iki, nyoba mbukak PDF ing browser nyebabake pesen kesalahan kanthi teks "Oops, ana sing salah!" Panjaluk menyang server ora digawe nalika nyoba mbukak PDF, nanging panjaluk digawe nalika browser diluncurake, sing nuduhake kemampuan terus kanggo ngundhuh kode eksekusi sing nglanggar aturan Google Play.

Source: www.habr.com

Add a comment