wnagzihxa1n

wnagzihxa1n

iOS/Android Security

© 2021

浏览器安全周报 2019.11.11 - 2019.11.15

组里又走一大佬,算下来从我去年四月份入职以来已经走了四个了

祝师傅们前程似锦!

上周提到谷歌紧急修复的一个在野利用的洞(1019226),补丁如下,及时修复吧

  • https://chromium.googlesource.com/chromium/src/+/6a2e670a243b815cf043f8da4d26ecb9a64d307b%5E%21/#F0
diff --git a/third_party/blink/renderer/modules/webaudio/convolver_node.cc b/third_party/blink/renderer/modules/webaudio/convolver_node.cc
index c67a0ba..ce19fe6 100644
--- a/third_party/blink/renderer/modules/webaudio/convolver_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/convolver_node.cc
@@ -103,6 +103,8 @@
   DCHECK(IsMainThread());
 
   if (!buffer) {
+    BaseAudioContext::GraphAutoLocker context_locker(Context());
+    MutexLocker locker(process_lock_);
     reverb_.reset();
     shared_buffer_ = nullptr;
     return;

iOS越狱大杀器,目前还处于early beta阶段,可能会失败,多尝试几次,不完美越狱,重启后需要重新越狱

  • https://checkra.in/

Lucas讲了一个相关的议题,分享了这个工具背后的BootROM漏洞的分析和利用

  • http://iokit.racing/oneweirdtrick.pdf

上周的安全周报最后面我放了Flanker_hqd的一篇漏洞分析文章链接,不知道大家看了没有,没看也没事,我这周分析了一下,这里简单讲下漏洞细节

首先三星有一个叫SamsungSMT这么个应用,具体是干啥的我不是很清楚

它有一个导出组件com.samsung.SMT.SamsungTTSService,其onCreate方法里动态注册了一个BroadcastReceiver,我们知道这默认是导出的

public void onCreate() {
    ...
    LangPackMgr.get().a(this.getApplicationContext());  // 入口
    ...
    super.onCreate();
}

public void a(Context context) {
    if(this.context == null) {
        ...
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.samsung.SMT.ACTION_INSTALL_FINISHED");
        this.context.registerReceiver(this.LangPackMgr$2, intentFilter);  // 注册一个广播接收器:"com.samsung.SMT.ACTION_INSTALL_FINISHED"
        ...
    }
}

注册的LangPackMgr$2会将传入的Intent内两个数据作为参数构造一个LangPackMgr$UpdateEngineInfo对象,然后加到一个队列里,经过一系列判断,开始更新

BroadcastReceiver LangPackMgr$2;

public LangPackMgr() {
    ...
    this.LangPackMgr$2 = new LangPackMgr$2(this);
}

public void onReceive(Context context, Intent intent) {
    int v7 = -1;
    if(intent.getAction().equals("com.samsung.SMT.ACTION_INSTALL_FINISHED")) {
        ArrayList arrayList = intent.getCharSequenceArrayListExtra("BROADCAST_CURRENT_LANGUAGE_INFO");
        Class_l v1 = new Class_l(arrayList.toArray(new String[arrayList.size()]));
        v1.a(intent.getStringExtra("BROADCAST_CURRENT_LANGUAGE_VERSION"));
        v1.setArrayList(intent.getCharSequenceArrayListExtra("BROADCAST_DB_FILELIST"));
        v1.a(true);
        int Extra_SMT_ENGINE_VERSION = intent.getIntExtra("SMT_ENGINE_VERSION", v7);
        String Extra_SMT_ENGINE_PATH = intent.getStringExtra("SMT_ENGINE_PATH");
        if(Extra_SMT_ENGINE_VERSION > SmtTTS.get().getEngineVersion() && (r.isFileExist(Extra_SMT_ENGINE_PATH))) {
            if(j.a(Extra_SMT_ENGINE_PATH)) {
                LangPackMgr.getUpdateEngineQueue(this.a).add(new LangPackMgr$UpdateEngineInfo(Extra_SMT_ENGINE_VERSION, Extra_SMT_ENGINE_PATH));
                LOG.b(q.b, "LangPackMgr - Add candidate engine [%d][%s]", new Object[]{Integer.valueOf(Extra_SMT_ENGINE_VERSION), Extra_SMT_ENGINE_PATH});
            }
        ...
        LangPackMgr.doUpdateEngine(this.a);
    }
}

使用多线程来执行,但是在线程里我们看到一个reloadEngine()方法

static void doUpdateEngine(LangPackMgr langPackMgr) {
    langPackMgr.updateEngine();
}

private void updateEngine() {
    if(this.mThreadUpdateEngine == null || !this.mThreadUpdateEngine.isAlive()) {
        this.mThreadUpdateEngine = new LangPackMgr$EngineUpdateThread(this, null);
        this.mThreadUpdateEngine.start();
    }
}

public void run() {
    ...
        if(v1 != null && ((LangPackMgr$UpdateEngineInfo)v1).SMT_ENGINE_VERSION > SmtTTS.get().getEngineVersion()) {
            l.a().b(((LangPackMgr$UpdateEngineInfo)v1).SMT_ENGINE_PATH);
            if(SmtTTS.get().reloadEngine()) {   <== 注意看这里
                LOG.c("Restart engine...");

跟入可以看到直接使用System.load()加载了v0_2指向的路径,那么l.a().k()返回的是什么呢?

public boolean reloadEngine() {
    boolean v0_3;
    boolean v3;
    this.e();
    try {
        String v0_2 = l.a().k();
        if(r.isFileExist(v0_2)) {
            System.load(v0_2);
        }
        else {
            goto label_71;
        }
    }

我们看reloadEngine()上两句代码,这里说的是如果版本号高于本地数据,就进入if逻辑处理,可以看到,最后将Intent内"SMT_ENGINE_PATH"的数据写到本地SP文件的"SMT_INSTALLED_ENGINE_PATH"字段

if(v1 != null && ((LangPackMgr$UpdateEngineInfo)v1).SMT_ENGINE_VERSION > SmtTTS.get().getEngineVersion()) {
    l.a().b(((LangPackMgr$UpdateEngineInfo)v1).SMT_ENGINE_PATH);    <== 1
    
public void b(String param_SMT_ENGINE_PATH) {
    if(param_SMT_ENGINE_PATH != null) {
        l.SMT_LATEST_INSTALLED_ENGINE_PATH = param_SMT_ENGINE_PATH;
        this.a("SMT_LATEST_INSTALLED_ENGINE_PATH", param_SMT_ENGINE_PATH);  <== 2
    }
    else {
        LOG.d("Try to set SMT_INSTALLED_ENGINE_PATH with null");
    }
}

public void a(String SMT_INSTALLED_ENGINE_PATH, String param_SMT_ENGINE_PATH) {
    this.a(n.a, SMT_INSTALLED_ENGINE_PATH, param_SMT_ENGINE_PATH);  <== 3
}

public void a(n arg2, String SMT_INSTALLED_ENGINE_PATH, String param_SMT_ENGINE_PATH) {
    SharedPreferences sp = this.c(arg2);
    if(sp != null) {
        SharedPreferences$Editor sp$editor = sp.edit();
        sp$editor.putString(SMT_INSTALLED_ENGINE_PATH, param_SMT_ENGINE_PATH);  <== 4
        sp$editor.commit();
    }
}

再来看l.a().k(),可以看到返回值就是"SMT_ENGINE_PATH"指向的数据,这个数据我们可控,修改Intent即可

public String k() {
    if(r.isFileExist(l.SMT_LATEST_INSTALLED_ENGINE_PATH)) {
        try {
            if(j.a(l.SMT_LATEST_INSTALLED_ENGINE_PATH)) {
                String v0_1 = l.SMT_LATEST_INSTALLED_ENGINE_PATH;
                return v0_1;
            }

            LOG.a("Invalid INSTALLED_ENGINE_PATH = " + l.SMT_LATEST_INSTALLED_ENGINE_PATH);
        }
        catch(Exception v0) {
            v0.printStackTrace();
        }
    }

    this.j();
    return null;
}

这个漏洞核心的部分大概就是上面这些,其它包括主动触发,利用等在内的详细分析文章还在写(我怎么感觉每次都是详细的文章还在写:)),这是一个双下巴的胖子的微笑)

我觉得我搞安全不是一个死磕的人,更加的偏向思考以及投机,从去年开始的时间我都在想关于逻辑层的漏洞挖掘方式,我跟李神探聊过我思考的一些方法和思路

举个例子,比如我们要挖掘一个导出组件接收Intent,然后获取了其中的数据作为参数不做任何检查直接调用startActivity()这样的一个漏洞,去年我的想法是使用控制流,李神探和我说可以使用污点分析,但是这些最开始的想法超出了我的能力范围,那段时间还有很多其它的事情要做,工作的业务,以及浏览器安全的学习,所以这部分的工作一直处于思考如何使用更优雅的方式去实现的阶段

后来我想到了一种方法,可以构造一个数据库,反向搜索调用,比如我们想看startActivity()都有哪些路径可以调用到,那么我们从这个节点开始,往上找,比如a函数调用了startActivity(),那么我们看a函数都被哪些函数调用到了,然后搜索下去,就是个广搜嘛,最后我们搜到onReceiveronCreate等这些入口函数的时候,就可以判断当前组件是否导出,若是广播接收器,可以判断是静态注册的还是动态注册等,这就属于细节方面了,然后我们将所有路径使用某种方式表达出来,手动去确认调用路径是否可行即可

我还思考了一种更加进阶的方式,自定义漏洞链,什么意思呢?

我再举个例子,比如现在有一个App,通过HTTP(不一定是HTTP,HTTPS也可以)下载了一个压缩包,然后解压缩这个文件,那么在这个过程中,可能会出现使用HTTP导致被劫持的问题,如果使用HTTPS,会有压缩文件存储路径不安全导致文件被替换,路径穿越等,问题是各种各样的,我们可以通过钩子等方式记录下所有的功能点,触发了哪个功能点,就做标记,比如我现在有这么一条自定义的漏洞链,如果发现应用在使用HTTP下载压缩包,就可以给第一个节点做标记,每击中一个,就标记一个,当然,需要连贯性,不能中断,不能解压缩本地应用私有文件夹内的一个压缩包也给算到这条链上,那肯定是不行的

HTTP下载 -> 解压缩 -> getName()

其它的漏洞链可多了,大家可以自己思考

可惜的是,以上我说的绝大部分工作,被MWR的大佬们做完了,大佬们早早地就想到了我说的这些东西,接下来我好好研究下这个工具,好东西啊这是!

  • https://github.com/FSecureLABS/Jandroid
  • https://labs.f-secure.com/blog/automating-pwn2own-with-jandroid/

泉哥写了篇文章简单介绍了下这个工具

  • https://riusksk.me/2019/11/02/Jandroid%EF%BC%9A%E5%8D%8A%E8%87%AA%E5%8A%A8%E5%8C%96Android%E5%BA%94%E7%94%A8%E9%80%BB%E8%BE%91%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98/

说真的,这种思路真的是逻辑洞大杀器,反正都已经聊到这里了,我再说一个我接下来要做的一个工具,不是什么牛逼的工具,就是一个辅助,讲讲思路,首先通过上述的方式获取一个利用链

A.onCreate() -> B.b(getIntent) -> C.c(intent.getXXX("xxx")) -> startActivity()

那么我们去分析的时候还是需要使用反编译工具顺着这条路径去分析的,其中复杂程度其实也挺麻烦的,如果我们能够自动化的把所有需要的代码用图的形式打印出来,生成一张我们需要的流程图,辅助我们分析,就会好很多,也可以节省很多时间,不知道这个思路我描述清楚了没有

IMAGE

跟搞内核,系统漏洞的大佬们比不了,这玩意还是比较简单,大佬们不要嘲笑

最后是私藏的几个准备跟着学习的漏洞,玄武大法好,学学学!

iOS iMessage NSSharedKeyDictionary 解码越界读漏洞

  • https://bugs.chromium.org/p/project-zero/issues/detail?id=1918

Adobe Acrobat Reader DC JBIG2Globals stream 解析指针未初始化漏洞

  • https://bugs.chromium.org/p/project-zero/issues/detail?id=1926

微软研究员关于内存安全的一个演讲《Quest Memory Safety》

  • https://github.com/microsoft/MSRC-Security-Research/tree/master/presentations/2019_09_Ekoparty

Windows win32k xxxCreateWindowEx 内核信息泄露漏洞(CVE-2019-1071)的分析

  • https://www.ragestorm.net/blogs/?p=458

15年BlackHat关于华为海思TrustZone的攻击,不知道李神探知道后会不会害怕:))

  • https://www.blackhat.com/docs/us-15/materials/us-15-Shen-Attacking-Your-Trusted-Core-Exploiting-Trustzone-On-Android-wp.pdf

听说华为20亿重奖反制裁团队,我一瞬间酸成柠檬精了