Inatafuta udhaifu katika Kivinjari cha UC

Inatafuta udhaifu katika Kivinjari cha UC

Utangulizi

Mwisho wa Machi sisi taarifa, kwamba waligundua uwezo fiche wa kupakia na kuendesha msimbo ambao haujathibitishwa katika Kivinjari cha UC. Leo tutaangalia kwa undani jinsi upakuaji huu unatokea na jinsi wadukuzi wanaweza kuitumia kwa madhumuni yao wenyewe.

Wakati fulani uliopita, Kivinjari cha UC kilitangazwa na kusambazwa kwa ukali sana: kilisakinishwa kwenye vifaa vya watumiaji kwa kutumia programu hasidi, kusambazwa kutoka kwa tovuti mbalimbali chini ya kivuli cha faili za video (yaani, watumiaji walidhani walikuwa wakipakua, kwa mfano, video ya ngono, lakini badala yake ilipokea APK iliyo na kivinjari hiki), ilitumia mabango ya kutisha yenye ujumbe kwamba kivinjari kimepitwa na wakati, kinaweza kuathiriwa na mambo kama hayo. Katika kikundi rasmi cha Kivinjari cha UC kwenye VK kuna mada, ambayo watumiaji wanaweza kulalamika kuhusu matangazo yasiyo ya haki, kuna mifano mingi huko. Mnamo 2016 kulikuwa na usawa matangazo ya video kwa Kirusi (ndiyo, utangazaji wa kivinjari cha kuzuia matangazo).

Wakati wa kuandika, Kivinjari cha UC kina zaidi ya usakinishaji 500 kwenye Google Play. Hii inavutia - ni Google Chrome pekee iliyo na zaidi. Miongoni mwa hakiki unaweza kuona malalamiko mengi kuhusu utangazaji na uelekezaji upya kwa baadhi ya programu kwenye Google Play. Hii ndio ilikuwa sababu ya utafiti wetu: tuliamua kuona ikiwa Kivinjari cha UC kilikuwa kikifanya kitu kibaya. Na ikawa kwamba anafanya hivyo!

Katika nambari ya maombi, uwezo wa kupakua na kuendesha nambari inayoweza kutekelezwa iligunduliwa, ambayo ni kinyume na sheria za uchapishaji wa maombi kwenye Google Play. Mbali na kupakua msimbo unaoweza kutekelezeka, Kivinjari cha UC hufanya hivyo kwa njia isiyo salama, ambayo inaweza kutumika kuzindua shambulio la MitM. Wacha tuone ikiwa tunaweza kufanya shambulio kama hilo.

Kila kitu kilichoandikwa hapa chini kinafaa kwa toleo la UC Browser ambalo lilipatikana kwenye Google Play wakati wa utafiti:

package: com.UCMobile.intl
versionName: 12.10.8.1172
versionCode: 10598
sha1 APK-Ρ„Π°ΠΉΠ»Π°: f5edb2243413c777172f6362876041eb0c3a928c

Vekta ya kushambulia

Katika faili ya maelezo ya Kivinjari cha UC unaweza kupata huduma iliyo na jina linalojieleza com.uc.deployment.UpgradeDeployService.

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

Wakati huduma hii inapoanza, kivinjari hufanya ombi la POST kwa puds.ucweb.com/upgrade/index.xhtml, ambayo inaweza kuonekana katika trafiki muda baada ya kuanza. Kwa kujibu, anaweza kupokea amri ya kupakua sasisho fulani au moduli mpya. Wakati wa uchambuzi, seva haikutoa amri kama hizo, lakini tuligundua kuwa tunapojaribu kufungua PDF kwenye kivinjari, hufanya ombi la pili kwa anwani iliyoainishwa hapo juu, baada ya hapo inapakua maktaba ya asili. Ili kutekeleza shambulio hilo, tuliamua kutumia kipengele hiki cha Kivinjari cha UC: uwezo wa kufungua PDF kwa kutumia maktaba asilia, ambayo haipo kwenye APK na ambayo inapakuliwa kutoka kwa Mtandao ikiwa ni lazima. Inastahili kuzingatia kwamba, kinadharia, Kivinjari cha UC kinaweza kulazimishwa kupakua kitu bila mwingiliano wa mtumiaji - ikiwa unatoa jibu lililoundwa vizuri kwa ombi ambalo linatekelezwa baada ya kivinjari kuzinduliwa. Lakini ili kufanya hivyo, tunahitaji kujifunza itifaki ya mwingiliano na seva kwa undani zaidi, kwa hiyo tuliamua kuwa itakuwa rahisi kuhariri majibu yaliyoingiliwa na kuchukua nafasi ya maktaba kwa kufanya kazi na PDF.

Kwa hivyo, wakati mtumiaji anataka kufungua PDF moja kwa moja kwenye kivinjari, maombi yafuatayo yanaweza kuonekana kwenye trafiki:

Inatafuta udhaifu katika Kivinjari cha UC

Kwanza kuna ombi la POST kwa puds.ucweb.com/upgrade/index.xhtml, basi
Kumbukumbu iliyo na maktaba ya kutazama muundo wa PDF na ofisi inapakuliwa. Ni busara kudhani kwamba ombi la kwanza hupeleka habari kuhusu mfumo (angalau usanifu wa kutoa maktaba inayohitajika), na kwa kukabiliana nayo kivinjari hupokea habari fulani kuhusu maktaba ambayo inahitaji kupakuliwa: anwani na, ikiwezekana. , kitu kingine. Shida ni kwamba ombi hili limesimbwa kwa njia fiche.

Omba kipande

Jibu kipande

Inatafuta udhaifu katika Kivinjari cha UC

Inatafuta udhaifu katika Kivinjari cha UC

Maktaba yenyewe imewekwa katika ZIP na haijasimbwa kwa njia fiche.

Inatafuta udhaifu katika Kivinjari cha UC

Tafuta msimbo wa kusimbua trafiki

Wacha tujaribu kufafanua majibu ya seva. Wacha tuangalie nambari ya darasa com.uc.deployment.UpgradeDeployService: kutoka kwa mbinu onStartCommand enda kwa com.uc.deployment.bx, na kutoka kwake hadi 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);
}

Tunaona uundaji wa ombi la POST hapa. Tunazingatia uundaji wa safu ya ka 16 na kujaza kwake: 0x5F, 0, 0x1F, -50 (=0xCE). Sanjari na kile tulichoona katika ombi hapo juu.

Katika darasa moja unaweza kuona darasa lililowekwa kiota ambalo lina njia nyingine ya kupendeza:

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

Njia huchukua safu ya baiti kama ingizo na hukagua kuwa sifuri byte ni 0x60 au byte ya tatu ni 0xD0, na byte ya pili ni 1, 11 au 0x1F. Tunaangalia majibu kutoka kwa seva: byte zero ni 0x60, pili ni 0x1F, ya tatu ni 0x60. Inaonekana kama kile tunachohitaji. Kwa kuzingatia mistari ("up_decrypt", kwa mfano), njia inapaswa kuitwa hapa ambayo itaondoa jibu la seva.
Wacha tuendelee kwenye mbinu gj. Kumbuka kuwa hoja ya kwanza ni byte katika kukabiliana na 2 (yaani 0x1F kwa upande wetu), na ya pili ni majibu ya seva bila
baiti 16 za kwanza.

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

Ni wazi, hapa tunachagua algorithm ya usimbuaji, na byte sawa ambayo iko kwenye yetu
kesi sawa na 0x1F, inaashiria moja ya chaguo tatu zinazowezekana.

Tunaendelea kuchambua kanuni. Baada ya kuruka kadhaa tunajikuta katika njia yenye jina la kujieleza decryptBytesByKey.

Hapa byte mbili zaidi zimetenganishwa na majibu yetu, na kamba hupatikana kutoka kwao. Ni wazi kwamba kwa njia hii ufunguo wa kufuta ujumbe umechaguliwa.

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

Kuangalia mbele, tunaona kuwa katika hatua hii bado hatujapata ufunguo, lakini tu "kitambulisho" chake. Kupata ufunguo ni ngumu zaidi.

Kwa njia inayofuata, vigezo viwili zaidi vinaongezwa kwa zilizopo, na kufanya nne kati yao: nambari ya uchawi 16, kitambulisho muhimu, data iliyosimbwa, na kamba isiyoeleweka (kwa upande wetu, tupu).

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

Baada ya mfululizo wa mabadiliko tunafika kwenye njia tuliBinarySafeDecryptNoB64 interface com.alibaba.wireless.security.open.staticdataencrypt.IStaticDataEncryptComponent. Hakuna madarasa katika nambari kuu ya programu ambayo hutekelezea kiolesura hiki. Kuna darasa kama hilo kwenye faili lib/armeabi-v7a/libsgmain.so, ambayo kwa hakika si .so, bali ni .jar. Mbinu tunayopendezwa nayo inatekelezwa kama ifuatavyo:

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

Hapa orodha yetu ya vigezo inaongezewa na nambari mbili zaidi: 2 na 0. Kwa kuzingatia
kila kitu, 2 ina maana decryption, kama katika mbinu fanyaMwisho darasa la mfumo javax.crypto.Cipher. Na hii yote huhamishiwa kwa Router fulani na nambari 10601 - hii ni dhahiri nambari ya amri.

Baada ya mlolongo unaofuata wa mabadiliko tunapata darasa linalotumia kiolesura IRouterComponent na mbinu 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);
}
}

Na pia darasa Maktaba ya JNIC, ambayo njia ya asili inatangazwa doCommandNative:

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

Hii inamaanisha tunahitaji kupata njia katika nambari asilia doCommandNative. Na hapa ndipo furaha huanza.

Ufafanuzi wa msimbo wa mashine

Katika faili libsgmain.so (ambayo kwa hakika ni .jar na ambayo tulipata utekelezaji wa baadhi ya violesura vinavyohusiana na usimbaji fiche hapo juu) kuna maktaba moja asilia: libsgmainso-6.4.36.so. Tunaifungua katika IDA na kupata rundo la masanduku ya mazungumzo yenye makosa. Shida ni kwamba jedwali la kichwa cha sehemu si sahihi. Hii inafanywa kwa makusudi ili kufanya uchambuzi kuwa ngumu.

Inatafuta udhaifu katika Kivinjari cha UC

Lakini haihitajiki: kupakia kwa usahihi faili ya ELF na kuchambua, meza ya kichwa cha programu inatosha. Kwa hivyo, tunafuta tu jedwali la sehemu, tukiondoa sehemu zinazolingana kwenye kichwa.

Inatafuta udhaifu katika Kivinjari cha UC

Fungua faili katika IDA tena.

Kuna njia mbili za kuwaambia mashine ya kawaida ya Java ambapo katika maktaba ya asili utekelezaji wa njia iliyotangazwa katika nambari ya Java kama ya asili iko. Ya kwanza ni kuipa jina la spishi Java_package_name_ClassName_MethodName.

Ya pili ni kuisajili wakati wa kupakia maktaba (katika kazi JNI_Onload)
kwa kutumia simu ya kukokotoa WasajiliWenyeji.

Kwa upande wetu, ikiwa tunatumia njia ya kwanza, jina linapaswa kuwa kama hii: Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative.

Hakuna kazi kama hiyo kati ya kazi zilizosafirishwa, ambayo inamaanisha unahitaji kutafuta simu WasajiliWenyeji.
Twende kwenye tamasha JNI_Onload na tunaona picha hii:

Inatafuta udhaifu katika Kivinjari cha UC

Nini kinaendelea hapa? Kwa mtazamo wa kwanza, mwanzo na mwisho wa kazi ni ya kawaida kwa usanifu wa ARM. Maagizo ya kwanza kwenye stack huhifadhi yaliyomo kwenye rejista ambayo kazi itatumia katika uendeshaji wake (katika kesi hii, R0, R1 na R2), pamoja na yaliyomo kwenye rejista ya LR, ambayo ina anwani ya kurudi kutoka kwa kazi. . Maagizo ya mwisho hurejesha rejista zilizohifadhiwa, na anwani ya kurudi mara moja imewekwa kwenye rejista ya PC - hivyo kurudi kutoka kwa kazi. Lakini ukiangalia kwa karibu, utaona kwamba maagizo ya mwisho yanabadilisha anwani ya kurudi iliyohifadhiwa kwenye stack. Wacha tuhesabu itakuwaje baada ya
utekelezaji wa kanuni. Anwani fulani 1xB0 imepakiwa ndani ya R130, 5 imetolewa kutoka kwayo, kisha inahamishiwa kwa R0 na 0x10 inaongezwa kwake. Inageuka 0xB13B. Kwa hivyo, IDA inafikiria kuwa maagizo ya mwisho ni kurudi kwa kazi ya kawaida, lakini kwa kweli inaruka kwa anwani iliyohesabiwa 0xB13B.

Inafaa kukumbuka hapa kwamba wasindikaji wa ARM wana njia mbili na seti mbili za maagizo: ARM na Thumb. Sehemu ndogo zaidi ya anwani huambia kichakataji ni seti gani ya maagizo inatumika. Hiyo ni, anwani kwa kweli ni 0xB13A, na moja kati ya ndogo inaonyesha hali ya Thumb.

"Adapta" sawa imeongezwa mwanzoni mwa kila kazi katika maktaba hii na
msimbo wa takataka. Hatutakaa juu yao kwa undani zaidi - tunakumbuka tu
kwamba mwanzo halisi wa karibu kazi zote uko mbali kidogo.

Kwa kuwa msimbo hauruki kwa 0xB13A kwa uwazi, IDA yenyewe haikutambua kuwa msimbo huo ulikuwa mahali hapa. Kwa sababu hiyo hiyo, haitambui nambari nyingi kwenye maktaba kama nambari, ambayo inafanya uchanganuzi kuwa mgumu. Tunaiambia IDA kuwa hii ndio nambari, na hii ndio hufanyika:

Inatafuta udhaifu katika Kivinjari cha UC

Jedwali linaanza wazi kwa 0xB144. Nini katika sub_494C?

Inatafuta udhaifu katika Kivinjari cha UC

Wakati wa kupiga kazi hii katika rejista ya LR, tunapata anwani ya meza iliyotajwa hapo awali (0xB144). Katika R0 - index katika meza hii. Hiyo ni, thamani inachukuliwa kutoka kwa meza, imeongezwa kwa LR na matokeo yake ni
anwani ya kwenda. Hebu jaribu kuihesabu: 0xB144 + [0xB144 + 8* 4] = 0xB144 + 0x120 = 0xB264. Tunaenda kwa anwani iliyopokelewa na kuona maagizo kadhaa muhimu na tena nenda kwa 0xB140:

Inatafuta udhaifu katika Kivinjari cha UC

Sasa kutakuwa na mpito katika kukabiliana na index 0x20 kutoka kwa jedwali.

Kwa kuzingatia saizi ya jedwali, kutakuwa na mabadiliko mengi kama haya kwenye nambari. Swali linatokea ikiwa inawezekana kwa namna fulani kukabiliana na hili moja kwa moja zaidi, bila kuhesabu anwani kwa mikono. Na maandishi na uwezo wa kubandika nambari katika IDA hutusaidia:

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"

Weka mshale kwenye mstari 0xB26A, endesha hati na uone mpito hadi 0xB4B0:

Inatafuta udhaifu katika Kivinjari cha UC

IDA tena haikutambua eneo hili kama msimbo. Tunamsaidia na kuona muundo mwingine hapo:

Inatafuta udhaifu katika Kivinjari cha UC

Maagizo baada ya BLX hayaonekani kuwa na maana sana, ni kama aina fulani ya uhamishaji. Wacha tuangalie sub_4964:

Inatafuta udhaifu katika Kivinjari cha UC

Na kwa kweli, hapa dword inachukuliwa kwenye anwani iliyoko LR, imeongezwa kwa anwani hii, baada ya hapo thamani kwenye anwani inayosababishwa inachukuliwa na kuweka kwenye stack. Pia, 4 huongezwa kwa LR ili baada ya kurudi kutoka kwa chaguo la kukokotoa, urekebishaji huo huo urukwe. Baada ya hapo amri ya POP {R1} inachukua thamani inayotokana na stack. Ukiangalia kile kilicho kwenye anwani 0xB4BA + 0xEA = 0xB5A4, utaona kitu sawa na meza ya anwani:

Inatafuta udhaifu katika Kivinjari cha UC

Ili kuunganisha muundo huu, utahitaji kupata vigezo viwili kutoka kwa msimbo: kukabiliana na nambari ya rejista ambayo unataka kuweka matokeo. Kwa kila rejista inayowezekana, itabidi uandae kipande cha msimbo mapema.

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"

Tunaweka mshale mwanzoni mwa muundo ambao tunataka kubadilisha - 0xB4B2 - na kuendesha hati:

Inatafuta udhaifu katika Kivinjari cha UC

Mbali na miundo iliyotajwa tayari, nambari pia ina yafuatayo:

Inatafuta udhaifu katika Kivinjari cha UC

Kama ilivyo katika kesi iliyopita, baada ya maagizo ya BLX kuna kukabiliana:

Inatafuta udhaifu katika Kivinjari cha UC

Tunachukua kukabiliana na anwani kutoka kwa LR, ongeza kwa LR na uende huko. 0x72044 + 0xC = 0x72050. Nakala ya muundo huu ni rahisi sana:

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"

Matokeo ya utekelezaji wa hati:

Inatafuta udhaifu katika Kivinjari cha UC

Mara tu kila kitu kitakapowekwa viraka kwenye chaguo la kukokotoa, unaweza kuelekeza IDA kwa mwanzo wake halisi. Itaunganisha nambari zote za kazi, na inaweza kugawanywa kwa kutumia HexRays.

Kusimbua masharti

Tumejifunza kukabiliana na msimbo wa mashine kwenye maktaba libsgmainso-6.4.36.so kutoka kwa Kivinjari cha UC na kupokea nambari ya kazi 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;
}

Wacha tuangalie kwa karibu mistari ifuatayo:

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

Katika utendaji ndogo_73E24 jina la darasa linasimbwa kwa uwazi. Kama vigezo vya chaguo hili la kukokotoa, kielekezi kwa data sawa na data iliyosimbwa, bafa fulani na nambari hupitishwa. Ni wazi, baada ya kuita kazi, kutakuwa na laini iliyosimbwa kwenye buffer, kwani inapitishwa kwa kazi. FindClass, ambayo inachukua jina la darasa kama parameta ya pili. Kwa hivyo, nambari ni saizi ya bafa au urefu wa mstari. Wacha tujaribu kufafanua jina la darasa, inapaswa kutuambia ikiwa tunaenda katika mwelekeo sahihi. Wacha tuangalie kwa karibu kile kinachotokea ndogo_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;
}

Kazi ndogo_7AF78 huunda mfano wa kontena kwa safu za saizi maalum (hatutakaa kwenye vyombo hivi kwa undani). Hapa vyombo viwili vile vinaundwa: moja ina mstari "DcO/lcK+h?m3c*q@" (ni rahisi kukisia kuwa huu ni ufunguo), nyingine ina data iliyosimbwa. Ifuatayo, vitu vyote viwili vimewekwa kwenye muundo fulani, ambao hupitishwa kwa kazi ndogo_6115C. Hebu pia tuweke alama kwenye sehemu yenye thamani 3 katika muundo huu. Hebu tuone kitakachotokea kwa muundo huu baadaye.

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

Kigezo cha kubadili ni uwanja wa muundo ambao hapo awali ulipewa thamani 3. Angalia kesi ya 3: kwa kazi ndogo_6364C vigezo hupitishwa kutoka kwa muundo ambao uliongezwa hapo katika kazi ya awali, yaani ufunguo na data iliyosimbwa. Ukiangalia kwa makini ndogo_6364C, unaweza kutambua algorithm ya RC4 ndani yake.

Tuna algorithm na ufunguo. Wacha tujaribu kufafanua jina la darasa. Hiki ndicho kilichotokea: com/taobao/wireless/security/adapta/JNICLibrary. Kubwa! Tuko kwenye njia sahihi.

Mti wa amri

Sasa tunahitaji kupata changamoto WasajiliWenyeji, ambayo itatuelekeza kwenye kazi doCommandNative. Wacha tuangalie kazi zinazoitwa kutoka JNI_Onload, na tunaipata ndani ndogo_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;
}

Na kwa kweli, njia ya asili iliyo na jina imesajiliwa hapa doCommandNative. Sasa tunajua anwani yake. Hebu tuone anachofanya.

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

Kwa jina unaweza kudhani kuwa hapa ndio mahali pa kuingilia kazi zote ambazo watengenezaji waliamua kuhamisha kwenye maktaba ya asili. Tunavutiwa na nambari ya chaguo 10601.

Unaweza kuona kutoka kwa nambari ambayo nambari ya amri hutoa nambari tatu: amri/10000, amri % 10000/100 ΠΈ amri% 10, yaani, kwa upande wetu, 1, 6 na 1. Nambari hizi tatu, pamoja na pointer kwa JNIEnv na hoja zinazopitishwa kwenye kazi huongezwa kwenye muundo na kupitishwa. Kutumia nambari tatu zilizopatikana (hebu tuwaashiria N1, N2 na N3), mti wa amri hujengwa.

Kitu kama hiki:

Inatafuta udhaifu katika Kivinjari cha UC

Mti umejaa kwa nguvu ndani JNI_Onload.
Nambari tatu husimba njia kwenye mti. Kila jani la mti lina anwani ya pocked ya kazi inayolingana. Ufunguo uko kwenye nodi ya mzazi. Kupata mahali katika msimbo ambapo kazi tunayohitaji imeongezwa kwenye mti si vigumu ikiwa unaelewa miundo yote inayotumiwa (hatuwaelezi ili tusifungue makala tayari badala kubwa).

Usumbufu zaidi

Tulipokea anwani ya chaguo la kukokotoa ambayo inapaswa kusimbua trafiki: 0x5F1AC. Lakini ni mapema sana kufurahiya: watengenezaji wa Kivinjari cha UC wametuandalia mshangao mwingine.

Baada ya kupokea vigezo kutoka kwa safu ambayo iliundwa katika msimbo wa Java, tunapata
kwa kazi katika anwani 0x4D070. Na hapa kuna aina nyingine ya upotoshaji wa kificho inatungoja.

Tunaweka fahirisi mbili katika R7 na R4:

Inatafuta udhaifu katika Kivinjari cha UC

Tunabadilisha faharisi ya kwanza kuwa R11:

Inatafuta udhaifu katika Kivinjari cha UC

Ili kupata anwani kutoka kwa jedwali, tumia faharisi:

Inatafuta udhaifu katika Kivinjari cha UC

Baada ya kwenda kwa anwani ya kwanza, index ya pili inatumiwa, ambayo iko katika R4. Kuna vipengele 230 kwenye jedwali.

Nini cha kufanya kuhusu hilo? Unaweza kumwambia IDA kuwa hii ni swichi: Hariri -> Nyingine -> Bainisha neno la kubadili.

Inatafuta udhaifu katika Kivinjari cha UC

Nambari inayotokana ni mbaya. Lakini, ukipitia msitu wake, unaweza kugundua simu kwa kazi ambayo tayari tunaifahamu ndogo_6115C:

Inatafuta udhaifu katika Kivinjari cha UC

Kulikuwa na swichi ambayo katika kesi ya 3 kulikuwa na usimbuaji kwa kutumia algoriti ya RC4. Na katika kesi hii, muundo uliopitishwa kwa kazi umejaa kutoka kwa vigezo vilivyopitishwa doCommandNative. Wacha tukumbuke kile tulichokuwa nacho huko magicInt na thamani 16. Tunaangalia kesi inayofanana - na baada ya mabadiliko kadhaa tunapata kanuni ambayo algorithm inaweza kutambuliwa.

Inatafuta udhaifu katika Kivinjari cha UC

Hii ni AES!

Algorithm ipo, yote iliyobaki ni kupata vigezo vyake: mode, ufunguo na, ikiwezekana, vector ya uanzishaji (uwepo wake unategemea hali ya uendeshaji ya algorithm ya AES). Muundo pamoja nao lazima uundwe mahali fulani kabla ya simu ya kazi ndogo_6115C, lakini sehemu hii ya msimbo imefichwa vizuri, kwa hivyo wazo linatokea kuweka kiraka msimbo ili vigezo vyote vya kazi ya usimbuaji vitupwe kwenye faili.

Kiraka

Ili usiandike msimbo wote wa kiraka katika lugha ya kusanyiko kwa mikono, unaweza kuzindua Studio ya Android, kuandika kazi huko ambayo inapokea vigezo sawa vya ingizo kama kazi yetu ya usimbuaji na kuiandikia faili, kisha unakili na ubandike msimbo ambao mkusanyaji atafanya. kuzalisha.

Marafiki zetu kutoka kwa timu ya UC Browser pia walitunza urahisi wa kuongeza msimbo. Hebu tukumbuke kwamba mwanzoni mwa kila kazi tuna msimbo wa takataka ambao unaweza kubadilishwa kwa urahisi na nyingine yoyote. Urahisi sana πŸ™‚ Hata hivyo, mwanzoni mwa kazi ya lengo hakuna nafasi ya kutosha kwa msimbo unaohifadhi vigezo vyote kwenye faili. Ilinibidi kuigawanya katika sehemu na kutumia vitalu vya takataka kutoka kwa kazi za jirani. Kulikuwa na sehemu nne kwa jumla.

Sehemu ya kwanza:

Inatafuta udhaifu katika Kivinjari cha UC

Katika usanifu wa ARM, vigezo vinne vya kwanza vya kazi vinapitishwa kupitia rejista R0-R3, wengine, ikiwa wapo, hupitishwa kupitia stack. Rejesta ya LR hubeba anwani ya kurejesha. Yote hii inahitaji kuokolewa ili kazi iweze kufanya kazi baada ya kutupa vigezo vyake. Tunahitaji pia kuhifadhi rejista zote ambazo tutatumia katika mchakato, kwa hiyo tunafanya PUSH.W {R0-R10,LR}. Katika R7 tunapata anwani ya orodha ya vigezo vilivyopitishwa kwa kazi kupitia stack.

Kwa kutumia kipengele fopen hebu tufungue faili /data/local/tmp/aes katika hali ya "ab".
yaani kwa nyongeza. Katika R0 tunapakia anwani ya jina la faili, katika R1 - anwani ya mstari inayoonyesha mode. Na hapa nambari ya takataka inaisha, kwa hivyo tunaendelea na kazi inayofuata. Ili iweze kuendelea kufanya kazi, tunaweka mwanzoni mpito kwa msimbo halisi wa kazi, tukipita takataka, na badala ya takataka tunaongeza kuendelea kwa kiraka.

Inatafuta udhaifu katika Kivinjari cha UC

Kupiga simu fopen.

Vigezo vitatu vya kwanza vya chaguo la kukokotoa aES kuwa na aina int. Kwa kuwa tulihifadhi rejista kwenye stack mwanzoni, tunaweza kupitisha kazi tu andika anwani zao kwenye stack.

Inatafuta udhaifu katika Kivinjari cha UC

Ifuatayo, tuna miundo mitatu ambayo ina ukubwa wa data na kielekezi kwa data ya ufunguo, vekta ya uanzishaji na data iliyosimbwa.

Inatafuta udhaifu katika Kivinjari cha UC

Mwishoni, funga faili, kurejesha madaftari na udhibiti wa uhamisho kwa kazi halisi aES.

Tunakusanya APK iliyo na maktaba iliyo na viraka, kuitia sahihi, kuipakia kwenye kifaa/emulator na kuizindua. Tunaona kwamba utupaji wetu unaundwa, na data nyingi zinaandikwa huko. Kivinjari hutumia usimbuaji sio tu kwa trafiki, na usimbaji fiche wote hupitia kazi inayohusika. Lakini kwa sababu fulani data muhimu haipo, na ombi linalohitajika halionekani kwenye trafiki. Ili tusingoje hadi Kivinjari cha UC kitengeneze ombi linalohitajika, hebu tuchukue jibu lililosimbwa kwa njia fiche kutoka kwa seva iliyopokelewa hapo awali na kiraka programu tena: ongeza usimbuaji kwenye Unda shughuli kuu.

    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

Tunakusanya, kusaini, kufunga, kuzindua. Tunapokea NullPointerException kwa sababu njia ilirudishwa.

Wakati wa uchanganuzi zaidi wa msimbo, chaguo la kukokotoa liligunduliwa ambalo hufafanua mistari ya kuvutia: "META-INF/" na ".RSA". Inaonekana kama programu inathibitisha cheti chake. Au hata hutoa funguo kutoka kwake. Sitaki kabisa kushughulika na kile kinachotokea na cheti, kwa hivyo tutaiingiza cheti sahihi. Hebu tubandika laini iliyosimbwa ili badala ya "META-INF/" tupate "BLABLINF/", unda folda iliyo na jina hilo kwenye APK na uongeze cheti cha kivinjari cha squirrel hapo.

Tunakusanya, kusaini, kufunga, kuzindua. Bingo! Tuna ufunguo!

MitM

Tulipokea ufunguo na vekta ya uanzishaji sawa na ufunguo. Hebu tujaribu kusimbua jibu la seva katika modi ya CBC.

Inatafuta udhaifu katika Kivinjari cha UC

Tunaona URL ya kumbukumbu, kitu sawa na MD5, "extract_unzipsize" na nambari. Tunaangalia: MD5 ya kumbukumbu ni sawa, ukubwa wa maktaba isiyofunguliwa ni sawa. Tunajaribu kubandika maktaba hii na kuipa kivinjari. Ili kuonyesha kwamba maktaba yetu iliyo na viraka imepakiwa, tutazindua Nia ya kuunda SMS yenye maandishi "PWNED!" Tutabadilisha majibu mawili kutoka kwa seva: puds.ucweb.com/upgrade/index.xhtml na kupakua kumbukumbu. Katika kwanza tunabadilisha MD5 (ukubwa haubadilika baada ya kufungua), kwa pili tunatoa kumbukumbu na maktaba iliyopigwa.

Kivinjari kinajaribu kupakua kumbukumbu mara kadhaa, baada ya hapo inatoa hitilafu. Inaonekana kitu
hapendi. Kama matokeo ya kuchambua muundo huu mbaya, iliibuka kuwa seva pia inasambaza saizi ya kumbukumbu:

Inatafuta udhaifu katika Kivinjari cha UC

Imesimbwa katika LEB128. Baada ya kiraka, saizi ya kumbukumbu iliyo na maktaba ilibadilika kidogo, kwa hivyo kivinjari kilizingatia kuwa kumbukumbu ilipakuliwa vibaya, na baada ya majaribio kadhaa ilifanya makosa.

Tunarekebisha ukubwa wa kumbukumbu ... Na - ushindi! πŸ™‚ Matokeo ni kwenye video.

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

Madhara na mwitikio wa msanidi programu

Vivyo hivyo, wavamizi wanaweza kutumia kipengele kisicho salama cha UC Browser kusambaza na kuendesha maktaba hasidi. Maktaba hizi zitafanya kazi katika muktadha wa kivinjari, kwa hivyo watapokea vibali vyake vyote vya mfumo. Matokeo yake, uwezo wa kuonyesha madirisha ya ulaghai, pamoja na upatikanaji wa faili za kazi za squirrel ya machungwa ya Kichina, ikiwa ni pamoja na kuingia, nywila na vidakuzi vilivyohifadhiwa kwenye hifadhidata.

Tuliwasiliana na watengenezaji wa UC Browser na kuwafahamisha kuhusu tatizo tulilopata, tukajaribu kubainisha hatari na hatari yake, lakini hawakujadiliana nasi chochote. Wakati huo huo, kivinjari kiliendelea kuonyesha kipengele chake cha hatari mbele ya macho. Lakini mara tulipofichua maelezo ya athari, haikuwezekana tena kuipuuza kama hapo awali. Machi 27 ilikuwa
toleo jipya la UC Browser 12.10.9.1193 lilitolewa, ambalo lilifikia seva kupitia HTTPS: puds.ucweb.com/upgrade/index.xhtml.

Kwa kuongeza, baada ya "kurekebisha" na hadi wakati wa kuandika makala hii, kujaribu kufungua PDF kwenye kivinjari ilisababisha ujumbe wa makosa na maandishi "Lo! Ombi kwa seva halikufanywa wakati wa kujaribu kufungua PDF, lakini ombi lilifanywa wakati kivinjari kilipozinduliwa, ambayo inadokeza uwezo unaoendelea wa kupakua msimbo unaoweza kutekelezeka kwa kukiuka sheria za Google Play.

Chanzo: mapenzi.com

Kuongeza maoni