Er að leita að veikleikum í UC vafra

Er að leita að veikleikum í UC vafra

Inngangur

Í lok mars við greint frá, að þeir uppgötvuðu falinn hæfileika til að hlaða og keyra óstaðfestan kóða í UC vafra. Í dag munum við skoða í smáatriðum hvernig þetta niðurhal á sér stað og hvernig tölvuþrjótar geta notað það í eigin tilgangi.

Fyrir nokkru síðan var UC Browser auglýstur og dreift mjög ákaft: hann var settur upp á tækjum notenda með því að nota spilliforrit, dreift frá ýmsum síðum í skjóli myndbandsskráa (þ. fékk í staðinn APK með þessum vafra), notaði skelfilega borða með skilaboðum um að vafrinn væri gamaldags, viðkvæmur og svoleiðis. Í opinbera UC vafrahópnum á VK er þema, þar sem notendur geta kvartað yfir ósanngjörnum auglýsingum, þar eru mörg dæmi. Árið 2016 var jafnvel myndbandsauglýsingar á rússnesku (já, auglýsingar fyrir vafra sem hindrar auglýsingar).

Þegar þetta er skrifað hefur UC Browser yfir 500 uppsetningar á Google Play. Þetta er áhrifamikið - aðeins Google Chrome hefur meira. Meðal umsagnanna geturðu séð töluvert af kvörtunum um auglýsingar og tilvísanir í sum forrit á Google Play. Þetta var ástæðan fyrir rannsóknum okkar: við ákváðum að sjá hvort UC Browser væri að gera eitthvað slæmt. Og það kom í ljós að hann gerir það!

Í forritskóðanum uppgötvaðist hæfileikinn til að hlaða niður og keyra keyranlegan kóða, sem er andstætt reglum um birtingu umsókna á Google Play. Auk þess að hlaða niður keyrslukóða gerir UC Browser það á óöruggan hátt, sem hægt er að nota til að hefja MitM árás. Við skulum sjá hvort við getum framkvæmt slíka árás.

Allt sem skrifað er hér að neðan á við um útgáfu UC vafra sem var fáanleg á Google Play þegar rannsóknin fór fram:

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

Árásarvektor

Í upplýsingaskrá UC vafra er hægt að finna þjónustu með nafni sem skýrir sig sjálft com.uc.deployment.UpgradeDeployService.

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

Þegar þessi þjónusta byrjar gerir vafrinn POST beiðni til puds.ucweb.com/upgrade/index.xhtml, sem sést í umferðinni nokkru eftir ræsingu. Sem svar gæti hann fengið skipun um að hlaða niður einhverri uppfærslu eða nýrri einingu. Við greininguna gaf þjónninn ekki slíkar skipanir, en við tókum eftir því að þegar við reynum að opna PDF í vafranum sendir hann aðra beiðni á heimilisfangið sem tilgreint er hér að ofan, en síðan hleður hann niður innfædda bókasafninu. Til að framkvæma árásina ákváðum við að nota þennan eiginleika UC vafra: hæfileikann til að opna PDF með því að nota innbyggt bókasafn, sem er ekki í APK og sem það hleður niður af internetinu ef þörf krefur. Það er athyglisvert að fræðilega séð getur UC vafri neyðst til að hlaða niður einhverju án samskipta notenda - ef þú gefur vel mótað svar við beiðni sem er keyrð eftir að vafrinn er ræstur. En til að gera þetta þurfum við að rannsaka samskiptareglur við netþjóninn nánar, svo við ákváðum að það væri auðveldara að breyta svarinu sem var hlerað og skipta um bókasafnið fyrir að vinna með PDF.

Svo þegar notandi vill opna PDF beint í vafranum má sjá eftirfarandi beiðnir í umferðinni:

Er að leita að veikleikum í UC vafra

Fyrst er POST beiðni til puds.ucweb.com/upgrade/index.xhtml, Þá
Skjalasafn með bókasafni til að skoða PDF og skrifstofusnið er hlaðið niður. Það er rökrétt að gera ráð fyrir að fyrsta beiðnin sendi upplýsingar um kerfið (að minnsta kosti arkitektúrinn til að útvega nauðsynlegt bókasafn) og sem svar við henni fær vafrinn einhverjar upplýsingar um safnið sem þarf að hlaða niður: heimilisfangið og, m.a. , eitthvað annað. Vandamálið er að þessi beiðni er dulkóðuð.

Biðja um brot

Svarbrot

Er að leita að veikleikum í UC vafra

Er að leita að veikleikum í UC vafra

Bókasafnið sjálft er pakkað í ZIP og er ekki dulkóðað.

Er að leita að veikleikum í UC vafra

Leitaðu að dulkóðunarkóða umferðar

Við skulum reyna að ráða svar þjónsins. Við skulum skoða bekkjarkóðann com.uc.deployment.UpgradeDeployService: frá aðferð áStartCommand fara til com.uc.deployment.bx, og frá því til 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);
}

Við sjáum myndun POST beiðni hér. Við gefum gaum að því að búa til fylki með 16 bætum og fyllingu þess: 0x5F, 0, 0x1F, -50 (=0xCE). Samhliða því sem við sáum í beiðninni hér að ofan.

Í sama flokki geturðu séð hreiður flokk sem hefur aðra áhugaverða aðferð:

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

Aðferðin tekur fylki af bætum sem inntak og athugar að núllbætið sé 0x60 eða þriðja bætið sé 0xD0 og annað bæti er 1, 11 eða 0x1F. Við skoðum svarið frá þjóninum: núllbætið er 0x60, annað er 0x1F, það þriðja er 0x60. Hljómar eins og það sem við þurfum. Miðað við línurnar ("up_decrypt", til dæmis), ætti að kalla hér aðferð sem mun afkóða svar þjónsins.
Við skulum halda áfram að aðferðinni gj. Athugaðu að fyrri röksemdin er bæti við offset 2 (þ.e. 0x1F í okkar tilfelli), og sá síðari er svar þjónsins án
fyrstu 16 bætin.

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

Augljóslega, hér veljum við afkóðunaralgrím og sama bæti og er í okkar
fall jafnt og 0x1F, táknar einn af þremur mögulegum valkostum.

Við höldum áfram að greina kóðann. Eftir nokkur stökk finnum við okkur í aðferð með sjálfskýrandi nafni decryptBytesByKey.

Hér eru tvö bæti til viðbótar aðskilin frá svarinu okkar og strengur er fenginn úr þeim. Ljóst er að þannig er valinn lykill til að afkóða skilaboðin.

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

Þegar horft er fram á veginn tökum við eftir því að á þessu stigi fáum við ekki enn lykil, heldur aðeins „auðkenni“ hans. Að fá lykilinn er aðeins flóknara.

Í næstu aðferð er tveimur breytum til viðbótar bætt við þær sem fyrir eru, sem gerir fjórar þeirra: töfratalan 16, lykilauðkenni, dulkóðuðu gögnin og óskiljanlegan streng (í okkar tilfelli, tómur).

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

Eftir röð umbreytinga komum við að aðferðinni staticBinarySafeDecryptNoB64 viðmót com.alibaba.wireless.security.open.staticdataencrypt.IStaticDataEncryptComponent. Það eru engir flokkar í aðalforritskóðanum sem útfæra þetta viðmót. Það er svona class í skránni lib/armeabi-v7a/libsgmain.so, sem er í raun ekki .so, heldur .jar. Aðferðin sem við höfum áhuga á er útfærð sem hér segir:

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

Hér er listi okkar yfir færibreytur bætt við tvær heiltölur í viðbót: 2 og 0. Miðað við
allt, 2 þýðir afkóðun, eins og í aðferðinni loka kerfisflokkur javax.crypto.Cipher. Og allt er þetta flutt yfir á ákveðinn leið með númerinu 10601 - þetta er greinilega skipunarnúmerið.

Eftir næstu keðju umbreytinga finnum við flokk sem útfærir viðmótið IRouterComponent og aðferð 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);
}
}

Og líka bekk JNICLibrary, þar sem innfædd aðferð er lýst yfir doCommandNative:

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

Þetta þýðir að við þurfum að finna aðferð í innfæddum kóða doCommandNative. Og þetta er þar sem gamanið byrjar.

Skýring á vélkóða

Í skrá libsgmain.so (sem er í raun .jar og þar sem við fundum útfærslu á sumum dulkóðunartengdum viðmótum rétt fyrir ofan) það er eitt innbyggt bókasafn: libsgmainso-6.4.36.so. Við opnum það í IDA og fáum fullt af valmyndum með villum. Vandamálið er að hlutahaustaflan er ógild. Þetta er gert viljandi til að flækja greininguna.

Er að leita að veikleikum í UC vafra

En það er ekki nauðsynlegt: til að hlaða ELF skrá á réttan hátt og greina hana nægir forritahaustafla. Þess vegna eyðum við einfaldlega hlutatöflunni, núllstillum samsvarandi reiti í hausnum.

Er að leita að veikleikum í UC vafra

Opnaðu skrána í IDA aftur.

Það eru tvær leiðir til að segja Java sýndarvélinni hvar nákvæmlega í innfædda bókasafninu er útfærsla á aðferð sem lýst er í Java kóða sem innfædd er staðsett. Í fyrsta lagi er að gefa henni tegundarheiti Java_pakka_nafn_ClassName_MethodName.

Annað er að skrá það þegar þú hleður bókasafninu (í aðgerðinni JNI_OnLoad)
með því að nota aðgerðarkall SkráNatives.

Í okkar tilviki, ef við notum fyrstu aðferðina, ætti nafnið að vera svona: Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative.

Það er engin slík aðgerð meðal útfluttra aðgerða, sem þýðir að þú þarft að leita að símtali SkráNatives.
Við skulum fara í aðgerðina JNI_OnLoad og við sjáum þessa mynd:

Er að leita að veikleikum í UC vafra

Hvað er í gangi hér? Við fyrstu sýn eru upphaf og lok aðgerðarinnar dæmigerð fyrir ARM arkitektúr. Fyrsta leiðbeiningin á staflanum geymir innihald skránna sem aðgerðin mun nota í rekstri sínum (í þessu tilviki R0, R1 og R2), sem og innihald LR skrárinnar, sem inniheldur skilafangið frá fallinu. . Síðasta leiðbeiningin endurheimtir vistaðar skrár og skilavistfangið er strax sett í tölvuskrána - þannig til baka frá aðgerðinni. En ef þú skoðar vel, munt þú taka eftir því að næstsíðasta leiðbeiningin breytir skila heimilisfanginu sem er geymt á staflanum. Við skulum reikna út hvernig það verður eftir
keyrslu kóða. Ákveðið heimilisfang 1xB0 er hlaðið inn í R130, 5 er dregið frá því, síðan er það flutt yfir í R0 og 0x10 bætt við það. Það kemur í ljós 0xB13B. Þannig telur IDA að síðasta leiðbeiningin sé eðlileg fallaftur, en í raun er hún að fara á reiknaða heimilisfangið 0xB13B.

Það er þess virði að muna hér að ARM örgjörvar hafa tvær stillingar og tvö sett af leiðbeiningum: ARM og Thumb. Minnsti hluti heimilisfangsins segir örgjörvanum hvaða leiðbeiningasett er verið að nota. Það er að segja, heimilisfangið er í raun 0xB13A, og einn í minnsta marktæka bitanum gefur til kynna Thumb ham.

Svipuðu „millistykki“ hefur verið bætt við upphaf hverrar aðgerðar í þessu safni og
ruslakóði. Við munum ekki fjalla frekar um þau í smáatriðum - við munum bara
að raunverulegt upphaf næstum allra aðgerða er aðeins lengra í burtu.

Þar sem kóðinn hoppar ekki beinlínis í 0xB13A, viðurkenndi IDA sjálft ekki að kóðinn væri staðsettur á þessum stað. Af sömu ástæðu þekkir það ekki megnið af kóðanum í bókasafninu sem kóða, sem gerir greiningu nokkuð erfiða. Við segjum IDA að þetta sé kóðinn og þetta er það sem gerist:

Er að leita að veikleikum í UC vafra

Taflan byrjar greinilega á 0xB144. Hvað er í sub_494C?

Er að leita að veikleikum í UC vafra

Þegar kallað er á þessa aðgerð í LR skránni fáum við heimilisfang áðurnefndrar töflu (0xB144). Í R0 - vísitala í þessari töflu. Það er, gildið er tekið úr töflunni, bætt við LR og niðurstaðan er
heimilisfangið til að fara á. Við skulum reyna að reikna það: 0xB144 + [0xB144 + 8* 4] = 0xB144 + 0x120 = 0xB264. Við förum á móttekið heimilisfang og sjáum bókstaflega nokkrar gagnlegar leiðbeiningar og förum aftur í 0xB140:

Er að leita að veikleikum í UC vafra

Nú verður umskipti á móti með vísitölu 0x20 frá töflunni.

Miðað við stærð töflunnar verða mörg slík umskipti í kóðanum. Spurningin vaknar hvort það sé einhvern veginn hægt að takast á við þetta sjálfvirkara, án þess að reikna heimilisföng handvirkt. Og forskriftir og hæfileikinn til að plástra kóða í IDA koma okkur til hjálpar:

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"

Settu bendilinn á línu 0xB26A, keyrðu handritið og sjáðu umskiptin í 0xB4B0:

Er að leita að veikleikum í UC vafra

IDA viðurkenndi aftur ekki þetta svæði sem kóða. Við hjálpum henni og sjáum aðra hönnun þar:

Er að leita að veikleikum í UC vafra

Leiðbeiningarnar eftir BLX virðast ekki meika mikið sens, þetta er meira eins og einhvers konar tilfærslu. Við skulum líta á sub_4964:

Er að leita að veikleikum í UC vafra

Og reyndar, hér er dorð tekið á heimilisfanginu sem liggur í LR, bætt við þetta heimilisfang, eftir það er gildið á heimilisfanginu sem myndast er tekið og sett á staflann. Einnig er 4 bætt við LR þannig að eftir að farið er aftur úr fallinu er þessi sama offset sleppt. Eftir það tekur POP {R1} skipunin gildið sem myndast úr staflanum. Ef þú skoðar hvað er staðsett á heimilisfangi 0xB4BA + 0xEA = 0xB5A4, muntu sjá eitthvað svipað og heimilisfangatöflu:

Er að leita að veikleikum í UC vafra

Til að laga þessa hönnun þarftu að fá tvær breytur úr kóðanum: offsetið og skráarnúmerið sem þú vilt setja niðurstöðuna í. Fyrir hverja mögulega skráningu þarftu að útbúa kóða fyrirfram.

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"

Við setjum bendilinn í byrjun skipulagsins sem við viljum skipta út - 0xB4B2 - og keyrum skriftuna:

Er að leita að veikleikum í UC vafra

Til viðbótar við þegar nefnd mannvirki inniheldur kóðinn einnig eftirfarandi:

Er að leita að veikleikum í UC vafra

Eins og í fyrra tilvikinu, eftir BLX kennsluna er offset:

Er að leita að veikleikum í UC vafra

Við tökum móttökuna á heimilisfangið frá LR, bætum því við LR og förum þangað. 0x72044 + 0xC = 0x72050. Handritið fyrir þessa hönnun er frekar einfalt:

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"

Niðurstaða framkvæmd handrits:

Er að leita að veikleikum í UC vafra

Þegar allt hefur verið lagfært í aðgerðinni geturðu bent IDA á raunverulegt upphaf hennar. Það mun púsla saman öllum aðgerðakóðanum og hægt er að afþýða hann með HexRays.

Afkóðun strengja

Við höfum lært að takast á við obfuscation vélkóða á bókasafninu libsgmainso-6.4.36.so frá UC Browser og fékk aðgerðarkóðann 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;
}

Við skulum skoða nánar eftirfarandi línur:

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

Í virkni undir_73E24 það er greinilega verið að afkóða nafn bekkjarins. Sem færibreytur fyrir þessa aðgerð er bendi á gögn sem líkjast dulkóðuðum gögnum, ákveðin biðminni og númer send. Augljóslega, eftir að hafa hringt í aðgerðina, verður afkóðuð lína í biðminni, þar sem hún er send til aðgerðarinnar FindClass, sem tekur flokksheitið sem aðra færibreytu. Því er talan stærð biðminni eða lengd línunnar. Við skulum reyna að ráða nafn bekkjarins, það ætti að segja okkur hvort við séum að fara í rétta átt. Við skulum skoða nánar hvað gerist í undir_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;
}

Virka undir_7AF78 býr til tilvik af íláti fyrir bætafylki af tilgreindri stærð (við munum ekki dvelja í smáatriðum í þessum gámum). Hér eru tveir slíkir ílát búnir til: annar inniheldur línuna "DcO/lcK+h?m3c*q@" (það er auðvelt að giska á að þetta sé lykill), hitt inniheldur dulkóðuð gögn. Næst eru báðir hlutir settir í ákveðna uppbyggingu sem er send til fallsins undir_6115C. Við skulum líka merkja reit með gildinu 3 í þessari uppbyggingu. Við skulum sjá hvað verður um þessa uppbyggingu næst.

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

Skiptabreytan er uppbyggingarreitur sem áður var úthlutað gildinu 3. Horfðu á tilvik 3: til fallsins undir_6364C færibreytur eru sendar frá uppbyggingunni sem var bætt við þar í fyrri aðgerðinni, þ.e. lykill og dulkóðuð gögn. Ef grannt er skoðað undir_6364C, þú getur þekkt RC4 reikniritið í því.

Við höfum reiknirit og lykil. Við skulum reyna að ráða nafn bekkjarins. Hér er það sem gerðist: com/taobao/wireless/security/adapter/JNICLibrary. Frábært! Við erum á réttri leið.

Skipunartré

Nú þurfum við að finna áskorun SkráNatives, sem mun benda okkur á aðgerðina doCommandNative. Við skulum skoða föllin sem kallast frá JNI_OnLoad, og við finnum það í undir_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;
}

Og reyndar er innfædd aðferð með nafninu skráð hér doCommandNative. Nú vitum við heimilisfangið hans. Við skulum sjá hvað hann gerir.

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

Með nafninu geturðu giskað á að hér sé inngangspunktur allra aðgerða sem verktaki ákváðu að flytja yfir á innfædda bókasafnið. Við höfum áhuga á aðgerð númer 10601.

Þú getur séð af kóðanum að skipunarnúmerið framleiðir þrjár tölur: skipun/10000, skipun % 10000 / 100 и skipun % 10, þ.e.a.s., í okkar tilviki, 1, 6 og 1. Þessar þrjár tölur, sem og vísbending um JNIEnv og rökin sem send eru til fallsins eru bætt við uppbyggingu og send áfram. Með því að nota þær þrjár tölur sem fengust (táknum þær N1, N2 og N3), er skipunartré byggt.

Eitthvað eins og þetta:

Er að leita að veikleikum í UC vafra

Tréð er fyllt á kraftmikinn hátt JNI_OnLoad.
Þrjár tölur kóða slóðina í trénu. Hvert lauf trésins inniheldur netfang samsvarandi falls. Lykillinn er í foreldrahnútnum. Að finna staðinn í kóðanum þar sem aðgerðinni sem við þurfum er bætt við tréð er ekki erfitt ef þú skilur öll mannvirkin sem notuð eru (við lýsum þeim ekki til að blása ekki upp þegar frekar stóra grein).

Meira rugl

Við fengum heimilisfang aðgerðarinnar sem ætti að afkóða umferð: 0x5F1AC. En það er of snemmt að gleðjast: þróunaraðilar UC Browser hafa undirbúið aðra óvart fyrir okkur.

Eftir að hafa fengið færibreyturnar frá fylkinu sem var myndað í Java kóðanum, fáum við
til fallsins á heimilisfangi 0x4D070. Og hér bíður okkar önnur tegund af kóðaþoku.

Við setjum tvær vísitölur í R7 og R4:

Er að leita að veikleikum í UC vafra

Við færum fyrstu vísitöluna í R11:

Er að leita að veikleikum í UC vafra

Til að fá heimilisfang úr töflu, notaðu vísitölu:

Er að leita að veikleikum í UC vafra

Eftir að hafa farið á fyrsta heimilisfangið er önnur vísitalan notuð, sem er í R4. Það eru 230 þættir í töflunni.

Hvað á að gera við því? Þú getur sagt IDA að þetta sé rofi: Breyta -> Annað -> Tilgreina rofaorðatiltæki.

Er að leita að veikleikum í UC vafra

Kóðinn sem myndast er hræðilegur. En þegar þú ferð í gegnum frumskóginn geturðu tekið eftir því að hringt er í aðgerð sem okkur er þegar kunn undir_6115C:

Er að leita að veikleikum í UC vafra

Það var rofi þar sem í tilviki 3 var afkóðun með RC4 reikniritinu. Og í þessu tilviki er uppbyggingin sem send er til fallsins fyllt út úr breytunum sem sendar eru til doCommandNative. Við skulum muna hvað við áttum þar magicInt með gildinu 16. Við skoðum samsvarandi fall - og eftir nokkrar umbreytingar finnum við kóðann sem hægt er að bera kennsl á reikniritið með.

Er að leita að veikleikum í UC vafra

Þetta er AES!

Reikniritið er til, allt sem er eftir er að fá færibreytur þess: ham, lykil og hugsanlega frumstillingarvigur (tilvist hans fer eftir rekstrarham AES reikniritsins). Uppbyggingin með þeim verður að myndast einhvers staðar á undan fallkallinu undir_6115C, en þessi hluti kóðans er sérlega vel skyggður, þannig að hugmyndin vaknar að plástra kóðann þannig að öllum breytum afkóðunaraðgerðarinnar sé hent í skrá.

Plástur

Til að skrifa ekki allan plásturkóðann á samsetningartungumáli handvirkt geturðu ræst Android Studio, skrifað aðgerð þar sem fær sömu innsláttarfæribreytur og afkóðunaraðgerðin okkar og skrifar í skrá, síðan afritaðu og líma kóðann sem þýðandinn mun mynda.

Vinir okkar úr UC Browser teyminu sáu líka um þægindin við að bæta við kóða. Við skulum muna að í upphafi hverrar aðgerð er sorpkóða sem auðvelt er að skipta út fyrir hvern annan. Mjög þægilegt 🙂 Hins vegar í upphafi markaðgerðarinnar er ekki nóg pláss fyrir kóðann sem vistar allar breytur í skrá. Ég þurfti að skipta því í hluta og nota ruslakubba frá nálægum aðgerðum. Það voru fjórir hlutar alls.

Fyrsti hluti:

Er að leita að veikleikum í UC vafra

Í ARM arkitektúrnum eru fyrstu fjórar aðgerðafæribreyturnar sendar í gegnum skrárnar R0-R3, restin, ef einhver er, fara í gegnum staflann. LR skráin ber skilafangið. Allt þetta þarf að vista svo aðgerðin geti virkað eftir að við töpuðum breytum hennar. Við þurfum líka að vista allar skrárnar sem við munum nota í ferlinu, svo við gerum PUSH.W {R0-R10,LR}. Í R7 fáum við heimilisfang lista yfir færibreytur sem sendar eru til aðgerðarinnar í gegnum staflann.

Að nota aðgerðina fopen við skulum opna skrána /data/local/tmp/aes í "ab" ham
e.a.s. til viðbótar. Í R0 hleðum við heimilisfang skráarnafnsins, í R1 - heimilisfang línunnar sem gefur til kynna stillinguna. Og hér lýkur sorpkóði, svo við höldum áfram í næstu aðgerð. Til þess að það geti haldið áfram að virka setjum við í upphafi umskiptin yfir í raunverulegan kóða aðgerðarinnar, framhjá sorpinu, og í staðinn fyrir sorpið bætum við framhaldi af plástrinum.

Er að leita að veikleikum í UC vafra

Hringir fopen.

Fyrstu þrjár færibreytur fallsins AES hafa tegund int. Þar sem við vistuðum skrárnar í staflanum í upphafi getum við einfaldlega sent aðgerðina fskrifaðu heimilisföng þeirra á stafla.

Er að leita að veikleikum í UC vafra

Næst höfum við þrjú mannvirki sem innihalda gagnastærðina og bendi á gögnin fyrir lykilinn, frumstillingarvektor og dulkóðuð gögn.

Er að leita að veikleikum í UC vafra

Lokaðu skránni í lokin, endurheimtu skrárnar og færðu stjórn yfir í raunverulega aðgerðina AES.

Við söfnum APK með pjatluðu bókasafni, undirritum það, hlaðum því upp í tækið/keppinautinn og ræsum það. Við sjáum að sorphaugurinn okkar er að búa til og þar er verið að skrifa mikið af gögnum. Vafrinn notar dulkóðun ekki aðeins fyrir umferð, og öll dulkóðun fer í gegnum viðkomandi aðgerð. En af einhverjum ástæðum eru nauðsynleg gögn ekki til staðar og nauðsynleg beiðni er ekki sýnileg í umferðinni. Til þess að bíða ekki þar til UC vafri dregur fram nauðsynlega beiðni skulum við taka dulkóðaða svarið frá þjóninum sem barst áðan og plástra forritið aftur: bæta afkóðuninni við onCreate af aðalaðgerðinni.

    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

Við setjum saman, undirritum, setjum upp, ræsum. Við fáum NullPointerException vegna þess að aðferðin skilaði núll.

Við frekari greiningu á kóðanum kom í ljós fall sem leysir áhugaverðar línur: „META-INF/“ og „.RSA“. Það lítur út fyrir að forritið sé að staðfesta vottorð sitt. Eða býr jafnvel til lykla úr því. Ég vil eiginlega ekki takast á við það sem er að gerast með skírteinið, svo við sendum því bara rétta skírteininu. Við skulum plástra dulkóðuðu línuna þannig að í stað „META-INF/“ fáum við „BLABLINF/“, búum til möppu með því nafni í APK og bætum skírteininu fyrir íkorna vafra þar.

Við setjum saman, undirritum, setjum upp, ræsum. Bingó! Við erum með lykilinn!

MitM

Við fengum lykil og upphafsvigur sem jafngildir lyklinum. Við skulum reyna að afkóða svar netþjónsins í CBC ham.

Er að leita að veikleikum í UC vafra

Við sjáum slóð skjalasafnsins, eitthvað svipað og MD5, „extract_unzipsize“ og númer. Við athugum: MD5 skjalasafnsins er það sama, stærð ópakkaðs safns er sú sama. Við erum að reyna að laga þetta bókasafn og gefa það til vafrans. Til að sýna að pjatlað bókasafnið okkar hefur hlaðast, munum við ræsa áform um að búa til SMS með textanum „PWNED! Við munum skipta út tveimur svörum frá þjóninum: puds.ucweb.com/upgrade/index.xhtml og til að sækja skjalasafnið. Í þeim fyrsta skiptum við út MD5 (stærðin breytist ekki eftir að hafa verið pakkað upp), í þeirri seinni gefum við skjalasafnið með pjatlaða bókasafninu.

Vafrinn reynir að hlaða niður skjalasafninu nokkrum sinnum, eftir það gefur hann villu. Greinilega eitthvað
honum líkar ekki. Sem afleiðing af því að greina þetta grugguga snið kom í ljós að þjónninn sendir einnig stærð skjalasafnsins:

Er að leita að veikleikum í UC vafra

Það er kóðað í LEB128. Eftir plásturinn breyttist stærð skjalasafnsins með bókasafninu örlítið, þannig að vafrinn taldi að skjalasafninu væri hlaðið niður skakkt og eftir nokkrar tilraunir kom upp villu.

Við stillum stærð skjalasafnsins... Og – sigur! 🙂 Niðurstaðan er í myndbandinu.

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

Afleiðingar og viðbrögð þróunaraðila

Á sama hátt gætu tölvuþrjótar notað óöruggan eiginleika UC vafra til að dreifa og keyra illgjarn bókasöfn. Þessi bókasöfn munu virka í samhengi við vafrann, þannig að þau fá allar kerfisheimildir hans. Þar af leiðandi er möguleikinn á að birta vefveiðarglugga, sem og aðgang að vinnuskrám appelsínugulu kínversku íkornanna, þar á meðal innskráningar, lykilorð og vafrakökur sem eru geymdar í gagnagrunninum.

Við höfðum samband við þróunaraðila UC Browser og upplýstum þá um vandamálið sem við fundum, reyndum að benda á varnarleysið og hættuna við það, en þeir ræddu ekkert við okkur. Á meðan hélt vafrinn áfram að flagga hættulegum eiginleikum sínum í augsýn. En þegar við opinberuðum smáatriðin um varnarleysið var ekki lengur hægt að hunsa það eins og áður. 27. mars var
gefin var út ný útgáfa af UC vafra 12.10.9.1193 sem fékk aðgang að þjóninum í gegnum HTTPS: puds.ucweb.com/upgrade/index.xhtml.

Að auki, eftir „lagfæringuna“ og þar til þessi grein var skrifuð, leiddi það af sér villuboð með textanum „Úbbs, eitthvað fór úrskeiðis“ að opna PDF í vafra. Beiðni til netþjónsins var ekki gerð þegar reynt var að opna PDF-skjal, heldur var beðið um það þegar vafrinn var ræstur, sem gefur í skyn að hægt sé að hlaða niður keyrslukóða í bága við reglur Google Play.

Heimild: www.habr.com

Bæta við athugasemd