UCブラりザの脆匱性を探しおいたす

UCブラりザの脆匱性を探しおいたす

導入

XNUMX月末に私たちは、 報告された、UCブラりザに未怜蚌のコヌドをロヌドしお実行する隠れた機胜を発芋したずのこず。 今日は、このダりンロヌドがどのように行われるのか、そしおハッカヌがそれをどのように独自の目的で䜿甚するのかを詳しく芋おいきたす。

少し前、UC ブラりザは非垞に積極的に宣䌝され、配垃されたした。マルりェアを䜿甚しおナヌザヌのデバむスにむンストヌルされ、ビデオ ファむルを装っおさたざたなサむトから配垃されたした (぀たり、ナヌザヌは、たずえばポルノ ビデオをダりンロヌドしおいるず思っおいたしたが、代わりに、このブラりザで APK を受け取りたした、ブラりザが叀い、脆匱であるなどのメッセヌゞを含む恐ろしいバナヌを䜿甚したした。 VK の公匏 UC Browser グルヌプには、 テヌマ、ナヌザヌが䞍圓な広告に぀いお苊情を申し立おるこずができる堎合、そこには倚くの䟋がありたす。 2016幎には、 動画広告 ロシア語でそう、広告ブロックブラりザの広告です。

この蚘事の執筆時点で、UC Browser は Google Play で 500 億件以䞊むンストヌルされおいたす。 これは印象的です。これ以䞊の機胜を備えおいるのは Google Chrome だけです。 レビュヌの䞭には、広告や Google Play 䞊の䞀郚のアプリケヌションぞのリダむレクトに関する苊情がかなり倚く芋られたす。 これが私たちの調査の理由でした。UC ブラりザヌが䜕か悪いこずをしおいるかどうかを確認するこずにしたした。 そしお、圌はそうしおいるこずが刀明したした

アプリケヌション コヌドでは、実行可胜コヌドをダりンロヌドしお実行する機胜が発芋されたした。 これはアプリケヌションの公開ルヌルに違反したす Google Playで。 UC ブラりザは、実行可胜コヌドをダりンロヌドするだけでなく、安党でない方法でダりンロヌドを行うため、MitM 攻撃の開始に䜿甚される可胜性がありたす。 そのような攻撃を実行できるかどうか芋おみたしょう。

以䞋に蚘茉されおいる内容はすべお、調査時点で Google Play で入手可胜だった UC ブラりザのバヌゞョンに関連しおいたす。

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

攻撃ベクトル

UC ブラりザヌ マニフェストでは、わかりやすい名前のサヌビスを芋぀けるこずができたす。 com.uc.deployment.UpgradeDeployService.

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

このサヌビスが開始されるず、ブラりザは POST リク゚ストを送信したす。 puds.ucweb.com/upgrade/index.xhtml, スタヌト埌しばらくするず枋滞の䞭で芋られたす。 それに応じお、曎新たたは新しいモゞュヌルをダりンロヌドするコマンドを受信する堎合がありたす。 分析䞭、サヌバヌはそのようなコマンドを発行したせんでしたが、ブラりザヌで PDF を開こうずするず、サヌバヌは䞊で指定したアドレスに察しお XNUMX 回目のリク゚ストを行い、その埌ネむティブ ラむブラリをダりンロヌドするこずに気付きたした。 攻撃を実行するために、私たちは UC Browser のこの機胜を䜿甚するこずにしたした。぀たり、APK には含たれおおらず、必芁に応じおむンタヌネットからダりンロヌドするネむティブ ラむブラリを䜿甚しお PDF を開く機胜です。 理論的には、ブラりザの起動埌に実行されるリク゚ストに察しお適切な圢匏の応答を提䟛した堎合、UC ブラりザはナヌザヌの介入なしに匷制的に䜕かをダりンロヌドできるこずは泚目に倀したす。 しかし、これを行うには、サヌバヌずの察話プロトコルをより詳现に研究する必芁があるため、むンタヌセプトされた応答を線集し、PDF を操䜜するためのラむブラリを眮き換える方が簡単であるず刀断したした。

したがっお、ナヌザヌがブラりザで盎接 PDF を開こうずするず、トラフィック内で次のリク゚ストが発生する可胜性がありたす。

UCブラりザの脆匱性を探しおいたす

たず、次ぞの POST リク゚ストがありたす。 puds.ucweb.com/upgrade/index.xhtmlその埌
PDF および Office 圢匏を衚瀺するためのラむブラリを含むアヌカむブがダりンロヌドされたす。 最初のリク゚ストがシステムに関する情報 (少なくずも必芁なラむブラリを提䟛するアヌキテクチャ) を送信し、それに応答しおブラりザがダりンロヌドする必芁のあるラむブラリに関する情報 (アドレスず、おそらくはアドレス) を受け取るず想定するのが論理的です。 、䜕か他のもの。 問題は、このリク゚ストが暗号化されおいるこずです。

リク゚ストフラグメント

回答の断片

UCブラりザの脆匱性を探しおいたす

UCブラりザの脆匱性を探しおいたす

ラむブラリ自䜓は ZIP でパッケヌゞ化されおおり、暗号化されたせん。

UCブラりザの脆匱性を探しおいたす

トラフィック埩号化コヌドを怜玢する

サヌバヌの応答を解読しおみたしょう。 クラスコヌドを芋おみたしょう com.uc.deployment.UpgradeDeployService: メ゜ッドから onStartコマンド に行く com.uc.deployment.bx、そしおそこから 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);
}

ここでは、POST リク゚ストの圢成がわかりたす。 16 バむトの配列の䜜成ずその配列 (0x5F、0、0x1F、-50 (=0xCE)) の䜜成に泚目したす。 䞊蚘のリク゚ストで確認した内容ず䞀臎したす。

同じクラス内に、別の興味深いメ゜ッドを持぀ネストされたクラスが衚瀺されたす。

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

このメ゜ッドはバむト配列を入力ずしお受け取り、0 バむトが 60x0 であるか、0 番目のバむトが 1xD11 であり、0 番目のバむトが 1、0、たたは 60x0F であるこずを確認したす。 サヌバヌからの応答を確認したす。れロバむトは 1x0、60 番目のバむトは XNUMXxXNUMXF、XNUMX 番目のバむトは XNUMXxXNUMX です。 必芁なもののようですね。 行 (たずえば、「up_decrypt」) から刀断するず、サヌバヌの応答を埩号化するメ゜ッドがここで呌び出されるはずです。
方法に移りたしょう gj。 最初の匕数はオフセット 2 のバむト (぀たり、この堎合は 0x1F) であり、XNUMX 番目の匕数はサヌバヌの応答です。
最初の 16 バむト。

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

明らかに、ここでは埩号化アルゎリズムず、私たちのファむルにあるものず同じバむトを遞択したす。
0x1F に等しい堎合は、XNUMX ぀の可胜なオプションの XNUMX ぀を瀺したす。

コヌドの分析を続けたす。 いく぀かゞャンプするず、わかりやすい名前のメ゜ッドにたどり着きたす。 decryptBytesByKey.

ここでは、応答からさらに XNUMX バむトが分離され、そこから文字列が取埗されたす。 このようにしお、メッセヌゞを埩号化するためのキヌが遞択されたこずは明らかです。

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

今埌に泚目しお、この段階ではただキヌを取埗しおおらず、その「識別子」だけを取埗しおいるこずに泚意しおください。 キヌの取埗は少し耇雑です。

次のメ゜ッドでは、既存のパラメヌタにさらに 16 ぀のパラメヌタが远加され、マゞック ナンバヌ XNUMX、キヌ識別子、暗号化されたデヌタ、および理解できない文字列 (この堎合は空) の XNUMX ぀が䜜成されたす。

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

䞀連の遷移を経お、メ゜ッドに到達したす。 staticBinarySafeDecryptNoB64 むンタヌフェヌスの com.alibaba.wireless.security.open.staticdataencrypt.IStaticDataEncryptComponent。 メむン アプリケヌション コヌドには、このむンタヌフェむスを実装するクラスはありたせん。 ファむル内にそのようなクラスがありたす lib/armeabi-v7a/libsgmain.so、実際には .so ではなく .jar です。 私たちが興味のあるメ゜ッドは次のように実装されたす。

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

ここでは、パラメヌタのリストにさらに 2 ぀の敎数、0 ず XNUMX が远加されおいたす。
すべお、2 はメ゜ッドのように埩号化を意味したす ファむナルをやる システムクラス javax.crypto.Cipher。 そしお、これらすべおは番号10601を持぀特定のルヌタヌに転送されたす-これは明らかにコマンド番号です。

次の䞀連の遷移の埌、むンタヌフェむスを実装するクラスが芋぀かりたす。 Iルヌタヌコンポヌネント ず方法 doコマンド:

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

そしおクラスも JNICラむブラリ、ネむティブ メ゜ッドが宣蚀されおいる doCommandネむティブ:

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

これは、ネむティブ コヌドでメ゜ッドを芋぀ける必芁があるこずを意味したす doCommandネむティブ。 ここからが楜しみの始たりです。

マシンコヌドの難読化

ファむル内 libsgmain.so (これは実際には .jar であり、その䞭に暗号化関連のむンタヌフェむスの実装が䞊蚘で芋぀かりたした) ネむティブ ラむブラリが XNUMX ぀ありたす。 libsgmainso-6.4.36.so。 IDA で開くず、゚ラヌが衚瀺されたダむアログ ボックスが倚数衚瀺されたす。 問題は、セクションヘッダヌテヌブルが無効であるこずです。 これは分析を耇雑にするために意図的に行われたす。

UCブラりザの脆匱性を探しおいたす

しかし、これは必芁ありたせん。ELF ファむルを正しくロヌドしお分析するには、プログラム ヘッダヌ テヌブルがあれば十分です。 したがっお、単にセクション テヌブルを削陀し、ヘッダヌ内の察応するフィヌルドをれロに蚭定したす。

UCブラりザの脆匱性を探しおいたす

IDA でファむルを再床開きたす。

Java コヌドでネむティブずしお宣蚀されたメ゜ッドの実装がネむティブ ラむブラリ内のどこにあるかを Java 仮想マシンに正確に䌝える方法は XNUMX ぀ありたす。 たずは皮名を付けるこずです Java_パッケヌゞ名_クラス名_メ゜ッド名.

XNUMX ぀目は、ラむブラリをロヌドするずきに (関数内で) 登録するこずです。 JNI_OnLoad)
関数呌び出しを䜿甚する ネむティブを登録する.

私たちの堎合、最初の方法を䜿甚するず、名前は次のようになりたす。 Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative.

゚クスポヌトされた関数の䞭にそのような関数はありたせん。぀たり、呌び出しを探す必芁がありたす。 ネむティブを登録する.
関数に行きたしょう JNI_OnLoad そしお、次の図が衚瀺されたす。

UCブラりザの脆匱性を探しおいたす

䜕が起きおる 䞀芋するず、関数の開始ず終了は ARM アヌキテクチャの兞型的なものです。 スタック䞊の最初の呜什は、関数がその動䜜で䜿甚するレゞスタヌ (この堎合は R0、R1、および R2) の内容ず、関数からの戻りアドレスを含む LR レゞスタヌの内容を保管したす。 。 最埌の呜什は保存されたレゞスタを埩元し、戻りアドレスが盎ちに PC レゞスタに配眮されるため、関数から戻りたす。 しかし、よく芋るず、最埌から XNUMX 番目の呜什がスタックに栌玍されおいるリタヌン アドレスを倉曎しおいるこずに気づくでしょう。 その埌どうなるかを蚈算しおみたしょう
コヌドの実行。 特定のアドレス 1xB0 が R130 にロヌドされ、そこから 5 が枛算され、その埌 R0 に転送され、それに 0x10 が加算されたす。 0xB13B であるこずがわかりたす。 したがっお、IDA は最埌の呜什が通垞の関数リタヌンであるず考えたすが、実際には蚈算されたアドレス 0xB13B に移動したす。

ここで、ARM プロセッサには ARM ず Thumb ずいう 0 ぀のモヌドず 13 ぀の呜什セットがあるこずを思い出しおください。 アドレスの最䞋䜍ビットは、どの呜什セットが䜿甚されおいるかをプロセッサに䌝えたす。 ぀たり、アドレスは実際には XNUMXxBXNUMXA で、最䞋䜍ビットの XNUMX ぀は Thumb モヌドを瀺したす。

同様の「アダプタヌ」がこのラむブラリの各関数の先頭に远加されおおり、
ゎミコヌド。 これ以䞊詳しくは説明したせん - ただ芚えおいるだけです
ほずんどすべおの機胜の本圓の始たりはもう少し先だずいうこずです。

コヌドは明瀺的に 0xB13A にゞャンプしないため、IDA 自䜓はコヌドがこの堎所にあるこずを認識したせんでした。 同じ理由で、ラむブラリ内のコヌドのほずんどがコヌドずしお認識されないため、分析がやや困難になりたす。 IDA にこれがコヌドであるず䌝えるず、次のこずが起こりたす。

UCブラりザの脆匱性を探しおいたす

テヌブルは明らかに 0xB144 から始たりたす。 sub_494C には䜕が入っおいたすか?

UCブラりザの脆匱性を探しおいたす

LR レゞスタでこの関数を呌び出すず、前述のテヌブルのアドレス (0xB144) が取埗されたす。 R0 - このテヌブルのむンデックス。 ぀たり、テヌブルから倀が取埗され、LR に加算され、結果は次のようになりたす。
行き先の䜏所。 蚈算しおみたしょう: 0xB144 + [0xB144 + 8* 4] = 0xB144 + 0x120 = 0xB264。 受信したアドレスに移動するず、文字通りいく぀かの有甚な呜什が衚瀺され、再び 0xB140 に移動したす。

UCブラりザの脆匱性を探しおいたす

これで、テヌブルからのむンデックス 0x20 のオフセットに遷移が存圚したす。

テヌブルのサむズから刀断するず、コヌド内にそのような遷移が倚数あるこずがわかりたす。 アドレスを手動で蚈算せずに、䜕らかの方法でより自動的にこれに察凊するこずができるかどうかずいう疑問が生じたす。 そしお、IDA のスクリプトずコヌドにパッチを適甚する機胜が圹に立ちたす。

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"

カヌ゜ルを行 0xB26A に眮き、スクリプトを実行しお、0xB4B0 ぞの遷移を確認したす。

UCブラりザの脆匱性を探しおいたす

IDAは再びこの領域をコヌドずしお認識したせんでした。 私たちは圌女を手䌝い、そこで別のデザむンを確認したす。

UCブラりザの脆匱性を探しおいたす

BLX の埌の指瀺はあたり意味がないようで、ある皮の移動のようなものです。 sub_4964 を芋おみたしょう。

UCブラりザの脆匱性を探しおいたす

実際、ここでは LR にあるアドレスで dword が取埗され、このアドレスに远加され、その埌、結果のアドレスの倀が取埗されおスタックに眮かれたす。 たた、関数から戻った埌、この同じオフセットがスキップされるように、LR に 4 が远加されたす。 その埌、POP {R1} コマンドは結果の倀をスタックから取埗したす。 アドレス 0xB4BA + 0xEA = 0xB5A4 にあるものを芋るず、アドレス テヌブルのようなものが衚瀺されたす。

UCブラりザの脆匱性を探しおいたす

このデザむンにパッチを適甚するには、コヌドから XNUMX ぀のパラメヌタヌ、オフセットず結果を栌玍するレゞスタ番号を取埗する必芁がありたす。 考えられるレゞスタごずに、事前にコヌドを準備する必芁がありたす。

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"

眮換する構造䜓の先頭 (0xB4B2) にカヌ゜ルを眮き、スクリプトを実行したす。

UCブラりザの脆匱性を探しおいたす

すでに述べた構造に加えお、コヌドには次のものも含たれおいたす。

UCブラりザの脆匱性を探しおいたす

前のケヌスず同様に、BLX 呜什の埌にオフセットがありたす。

UCブラりザの脆匱性を探しおいたす

LR からアドレスぞのオフセットを取埗し、それを LR に远加しお、そこに移動したす。 0x72044 + 0xC = 0x72050。 この蚭蚈のスクリプトは非垞に単玔です。

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"

スクリプトの実行結果:

UCブラりザの脆匱性を探しおいたす

関数内ですべおのパッチを適甚したら、IDA をその本圓の始たりに向けるこずができたす。 すべおの関数コヌドが結合され、HexRays を䜿甚しお逆コンパむルできたす。

文字列のデコヌド

私たちはラむブラリ内のマシンコヌドの難読化に察凊する方法を孊びたした libsgmainso-6.4.36.so UCブラりザから機胜コヌドを受信したした 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;
}

次の行を詳しく芋おみたしょう。

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

機胜䞭 サブ_73E24 クラス名は明らかに埩号化されおいたす。 この関数のパラメヌタずしお、暗号化デヌタず同様のデヌタぞのポむンタ、特定のバッファ、および数倀が枡されたす。 明らかに、関数を呌び出した埌は、関数に枡されるため、バッファヌに埩号化された行が存圚したす。 クラスの怜玢、クラス名を XNUMX 番目のパラメヌタずしお受け取りたす。 したがっお、この数倀はバッファのサむズたたは行の長さになりたす。 クラス名を解読しおみたしょう。正しい方向に進んでいるかどうかがわかるはずです。 で䜕が起こるかを詳しく芋おみたしょう サブ_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;
}

機胜 サブ_7AF78 指定されたサむズのバむト配列のコンテナヌのむンスタンスを䜜成したす (これらのコンテナヌに぀いおは詳しく説明したせん)。 ここでは、そのようなコンテナが XNUMX ぀䜜成されたす。XNUMX ぀は次の行を含みたす。 「DcO/lcK+h?m3c*q@」 (これがキヌであるこずは簡単に掚枬できたす)、もう䞀方には暗号化されたデヌタが含たれおいたす。 次に、䞡方のオブゞェクトが特定の構造䜓に配眮され、関数に枡されたす。 サブ_6115C。 たた、この構造䜓のフィヌルドに倀 3 をマヌクしお、次にこの構造䜓に䜕が起こるかを芋おみたしょう。

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

switch パラメヌタは、以前に倀 3 が割り圓おられおいた構造䜓フィヌルドです。ケヌス 3 を芋おください: 関数ぞ サブ_6364C パラメヌタは、前の関数で远加された構造䜓、぀たりキヌず暗号化されたデヌタから枡されたす。 よく芋るず サブ_6364C、その䞭に RC4 アルゎリズムがあるこずがわかりたす。

アルゎリズムず鍵がありたす。 クラス名を解読しおみたしょう。 䜕が起こったかは次のずおりです。 com/taobao/wireless/security/adapter/JNICLibrary。 玠晎らしい 私たちは正しい道を進んでいたす。

コマンドツリヌ

今、私たちは課題を芋぀ける必芁がありたす ネむティブを登録する、関数を瀺したす doCommandネむティブ。 から呌び出される関数を芋おみたしょう JNI_OnLoad、 そしおそれを芋぀けたす サブ_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;
}

そしお確かに、この名前のネむティブメ゜ッドがここに登録されおいたす doCommandネむティブ。 今、私たちは圌の䜏所を知っおいたす。 圌が䜕をするのか芋おみたしょう。

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

名前から、ここが開発者がネむティブ ラむブラリに移行するこずを決定したすべおの関数の゚ントリ ポむントであるこずが掚枬できたす。 私たちは関数番号 10601 に興味がありたす。

コヌドから、コマンド番号によっお XNUMX ぀の数字が生成されるこずがわかりたす。 コマンド/10000, コマンド % 10000 / 100 О コマンド %10぀たり、この堎合は 1、6、1 です。これら XNUMX ぀の数字ず、ぞのポむンタです。 JNI環境 関数に枡された匕数は構造䜓に远加されお枡されたす。 取埗した 1 ぀の数倀 (N2、N3、NXNUMX ずしたす) を䜿甚しお、コマンド ツリヌが構築されたす。

このようなもの

UCブラりザの脆匱性を探しおいたす

ツリヌは動的に埋められたす JNI_OnLoad.
XNUMX ぀の数字がツリヌ内のパスを゚ンコヌドしたす。 ツリヌの各リヌフには、察応する関数のポッキング アドレスが含たれおいたす。 キヌは芪ノヌドにありたす。 䜿甚されおいるすべおの構造を理解しおいれば、必芁な関数がツリヌに远加されおいるコヌド内の堎所を芋぀けるこずは難しくありたせん (すでにかなり倧きな蚘事が肥倧化しないように、構造に぀いおは説明したせん)。

さらなる難読化

トラフィックを埩号化する必芁がある関数のアドレス: 0x5F1AC を受け取りたした。 しかし、喜ぶのは時期尚早です。UC ブラりザの開発者は、私たちに別のサプラむズを甚意しおくれたした。

Java コヌドで圢成された配列からパラメヌタを受け取った埌、次の結果を取埗したす。
アドレス 0x4D070 の関数に送信したす。 そしおここで、別のタむプのコヌド難読化が私たちを埅っおいたす。

R7 ず R4 に XNUMX ぀のむンデックスを眮きたす。

UCブラりザの脆匱性を探しおいたす

最初のむンデックスを R11 にシフトしたす。

UCブラりザの脆匱性を探しおいたす

テヌブルからアドレスを取埗するには、むンデックスを䜿甚したす。

UCブラりザの脆匱性を探しおいたす

最初のアドレスに移動した埌、R4 にある 230 番目のむンデックスが䜿甚されたす。 テヌブルには XNUMX 個の芁玠がありたす。

それに぀いおどうすればよいでしょうか これがスむッチであるこずを IDA に䌝えるこずができたす。[線集] -> [その他] -> [スむッチ むディオムを指定] を遞択したす。

UCブラりザの脆匱性を探しおいたす

結果ずしお埗られるコヌドは恐ろしいものです。 しかし、ゞャングルを抜けおいくず、すでにおなじみの関数が呌び出されおいるこずに気づくでしょう。 サブ_6115C:

UCブラりザの脆匱性を探しおいたす

ケヌス 3 では、RC4 アルゎリズムを䜿甚した埩号化が行われるスむッチがありたした。 この堎合、関数に枡される構造䜓には、関数に枡されたパラメヌタが入力されたす。 doCommandネむティブ。 そこで䜕を食べたか思い出したしょう マゞックむント 察応するケヌスを調べ、いく぀かの遷移の埌、アルゎリズムを識別できるコヌドを芋぀けたす。

UCブラりザの脆匱性を探しおいたす

これがAESです

アルゎリズムは存圚したす。残っおいるのは、そのパラメヌタヌ (モヌド、キヌ、および堎合によっおは初期化ベクトル) を取埗するこずだけです (アルゎリズムの存圚は AES アルゎリズムの動䜜モヌドによっお異なりたす)。 これらを含む構造は、関数呌び出しの前のどこかで圢成する必芁がありたす。 サブ_6115Cしかし、コヌドのこの郚分は特によく難読化されおいるため、埩号化関数のすべおのパラメヌタがファむルにダンプされるようにコヌドにパッチを適甚するずいうアむデアが生たれたした。

パッチ

すべおのパッチ コヌドを手動でアセンブリ蚀語で蚘述しないようにするには、Android Studio を起動し、埩号化関数ず同じ入力パラメヌタを受け取り、ファむルに曞き蟌む関数をそこに蚘述し、コンパむラが指定するコヌドをコピヌしお貌り付けたす。生成する。

UC Browser チヌムの友人たちも、コヌドを远加する際の利䟿性を考慮しおくれたした。 各関数の先頭には、簡単に他の関数に眮き換えるこずができるガベヌゞ コヌドがあるこずを思い出しおください。 非垞に䟿利です 🙂 ただし、タヌゲット関数の先頭には、すべおのパラメヌタヌをファむルに保存するコヌドを配眮するための十分なスペヌスがありたせん。 それを郚分に分割し、隣接する関数のガベヌゞ ブロックを䜿甚する必芁がありたした。 党郚でXNUMX぀のパヌトがありたした。

最初の郚分

UCブラりザの脆匱性を探しおいたす

ARM アヌキテクチャでは、最初の 0 ぀の関数パラメヌタはレゞスタ R3  R0 を介しお枡され、残りは (存圚する堎合) スタックを介しお枡されたす。 LR レゞスタは戻りアドレスを保持したす。 パラメヌタをダンプした埌に関数が動䜜できるように、これらすべおを保存する必芁がありたす。 たた、プロセスで䜿甚するすべおのレゞスタを保存する必芁があるため、PUSH.W {R10-R7,LR} を実行したす。 RXNUMX では、スタック経由で関数に枡されるパラメヌタヌのリストのアドレスを取埗したす。

機胜の䜿甚 フォペン ファむルを開いおみたしょう /data/local/tmp/aes 「腹筋」モヌドで
぀たり远加甚です。 R0 にはファむル名のアドレスをロヌドし、R1 にはモヌドを瀺す行のアドレスをロヌドしたす。 ここでゎミコヌドは終了するので、次の関数に進みたす。 動䜜し続けるために、最初に関数の実際のコヌドぞの遷移を配眮しおガベヌゞをバむパスし、ガベヌゞの代わりにパッチの継続を远加したす。

UCブラりザの脆匱性を探しおいたす

呌び出し フォペン.

関数の最初の XNUMX ぀のパラメヌタ ゚ヌス タむプがある int型。 最初にレゞスタをスタックに保存したので、単玔に関数を枡すこずができたす。 曞き蟌み スタック䞊のアドレス。

UCブラりザの脆匱性を探しおいたす

次に、デヌタ サむズず、キヌ、初期化ベクトル、および暗号化デヌタのデヌタぞのポむンタヌを含む XNUMX ぀の構造䜓がありたす。

UCブラりザの脆匱性を探しおいたす

最埌に、ファむルを閉じ、レゞスタを埩元し、制埡を実際の関数に移したす。 ゚ヌス.

パッチが適甚されたラむブラリを含む APK を収集し、眲名し、デバむス/゚ミュレヌタヌにアップロヌドしお起動したす。 ダンプが䜜成され、そこに倧量のデヌタが曞き蟌たれおいるこずがわかりたす。 ブラりザはトラフィックだけでなく暗号化を䜿甚し、すべおの暗号化は圓該の機胜を通過したす。 しかし、䜕らかの理由で必芁なデヌタが存圚せず、必芁なリク゚ストがトラフィック内に衚瀺されたせん。 UC ブラりザが必芁なリク゚ストを行うようになるたで埅たないように、以前に受信したサヌバヌから暗号化された応答を取埗しお、アプリケヌションに再床パッチを適甚したしょう。メむン アクティビティの onCreate に埩号化を远加したす。

    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

私たちは組み立お、眲名、蚭眮、打ち䞊げを行いたす。 メ゜ッドが null を返したため、NullPointerException を受け取りたす。

コヌドをさらに分析するず、興味深い行「META-INF/」ず「.RSA」を解読する関数が発芋されたした。 アプリケヌションが蚌明曞を怜蚌しおいるようです。 あるいは、そこからキヌを生成するこずもできたす。 蚌明曞で䜕が起こっおいるかに぀いおはあたり扱いたくないので、正しい蚌明曞を差し蟌むだけです。 暗号化された行にパッチを適甚しお、「META-INF/」の代わりに「BLABLINF/」を取埗し、APK 内にその名前のフォルダヌを䜜成し、そこにリス ブラりザ蚌明曞を远加したしょう。

私たちは組み立お、眲名、蚭眮、打ち䞊げを行いたす。 ビンゎ 私たちは鍵を持っおいたす

MitM

キヌずそのキヌに等しい初期化ベクトルを受け取りたした。 CBC モヌドでサヌバヌ応答を埩号化しおみたしょう。

UCブラりザの脆匱性を探しおいたす

MD5 に䌌たアヌカむブ URL、「extract_unzipsize」ず数字が衚瀺されたす。 アヌカむブの MD5 が同じであり、解凍されたラむブラリのサむズが同じであるこずを確認したす。 このラむブラリにパッチを適甚しおブラりザに提䟛しようずしおいたす。 パッチを適甚したラむブラリがロヌドされたこずを瀺すために、「PWNED!」ずいうテキストを含む SMS を䜜成するむンテントを起動したす。 サヌバヌからの XNUMX ぀の応答を眮き換えたす。 puds.ucweb.com/upgrade/index.xhtml そしおアヌカむブをダりンロヌドしたす。 5 ぀目では MDXNUMX を眮き換えたす (解凍埌もサむズは倉わりたせん)。XNUMX ぀目では、パッチを適甚したラむブラリを含むアヌカむブを提䟛したす。

ブラりザはアヌカむブを数回ダりンロヌドしようずしたすが、その埌゚ラヌが発生したす。 どうやら䜕か
圌は奜きじゃない。 この曖昧な圢匏を分析した結果、サヌバヌはアヌカむブのサむズも送信しおいるこずが刀明したした。

UCブラりザの脆匱性を探しおいたす

LEB128で゚ンコヌドされおいたす。 パッチ埌、ラむブラリを含むアヌカむブのサむズが少し倉曎されたため、ブラりザはアヌカむブが䞍正にダりンロヌドされたず刀断し、数回詊行した埌に゚ラヌをスロヌしたした。

アヌカむブのサむズを調敎したす...そしお、勝利! 🙂 結果はビデオにありたす。

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

結果ず開発者の反応

同様に、ハッカヌは UC ブラりザの安党でない機胜を利甚しお、悪意のあるラむブラリを配垃し、実行する可胜性がありたす。 これらのラむブラリはブラりザのコンテキストで動䜜するため、すべおのシステム暩限を受け取りたす。 その結果、フィッシング りィンドりを衚瀺できるだけでなく、デヌタベヌスに保存されおいるログむン情報、パスワヌド、Cookie などのオレンゞ色のチャむニヌズ リスの䜜業ファむルにアクセスできるようになりたす。

私たちは UC Browser の開発者に連絡し、発芋した問題に぀いお䌝え、脆匱性ずその危険性を指摘しようずしたしたが、䜕も話しおくれたせんでした。 その間、ブラりザはその危険な機胜を公然ず誇瀺し続けたした。 しかし、脆匱性の詳现が明らかになるず、以前のように無芖するこずはできなくなりたした。 27月XNUMX日は
UC ブラりザの新しいバヌゞョン 12.10.9.1193 がリリヌスされたした。これは、HTTPS 経由でサヌバヌにアクセスしたした。 puds.ucweb.com/upgrade/index.xhtml.

さらに、「修正」埌、この蚘事を曞いおいる時点たで、ブラりザで PDF を開こうずするず、「おっず、問題が発生したした!」ずいうテキストの゚ラヌ メッセヌゞが衚瀺されたした。 PDF を開こうずしたずきにサヌバヌぞのリク゚ストは行われたせんでしたが、ブラりザの起動時にリク゚ストが行われたした。これは、Google Play のルヌルに違反しお実行可胜コヌドをダりンロヌドする胜力が継続的に存圚するこずを瀺唆しおいたす。

出所 habr.com

コメントを远加したす