Switch branches/tags
Nothing to show
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
..
Failed to load latest commit information.
README.md
secnetfilter-crash1.txt

README.md

URL filtering in kernel land: what could possibly go wrong?

  • Authors: Roberto Paleari (@rpaleari) and Aristide Fattori (@joystick)
  • ID: CVE-2016-2036
  • Samsung ID: SVE-2016-5036
  • Notification date: 20/10/2015
  • Release date: 20/01/2016

While looking at the Samsung Android kernel, we stumbled into a custom module named secfilter. This module captured our attention right from its initalization routine, where some Netfilter hooks are promptly registered:

...
static struct nf_queue_handler sec_url_queue_handler = {
	.name = SEC_MODULE_NAME,
	.outfn = sec_url_filter_slow
};

static struct nf_hook_ops sec_url_filter = {
	.hook       = sec_url_filter_hook,
	.pf         = PF_INET,
	.hooknum    = NF_INET_LOCAL_OUT,
	.priority   = NF_IP_PRI_FIRST
	};

static struct nf_hook_ops sec_url_recv_filter = {
	.hook       = sec_url_filter_recv_hook,
	.pf         = PF_INET,
	.hooknum    = NF_INET_LOCAL_IN,
	.priority   = NF_IP_PRI_FIRST
};
...
if ((add_send_hook =nf_register_hook( &sec_url_filter)) <0) break;
if (nf_register_hook( &sec_url_recv_filter) <0) break;
...
nf_register_queue_handler(PF_INET, &sec_url_queue_handler);
...

These hooks are configured to intercept all incoming (NF_INET_LOCAL_IN) and outgoing (NF_INET_LOCAL_OUT) packets. After being intercepted, outgoing TCP packets that match certain criteria eventually reach an internal function named getURL() which (surprise!) checks whether the TCP data resembles an HTTP request, and parses the contained URL.

So why Samsung put an URL parser in the Android kernel? Well, this is part of a mechanism accessible from a user-space application. Possible use cases include parental control apps, which need to implement system-wide URL filtering.

But how it works?

Netfilter hooks are always present, but the URL filtering mechanism is enabled only after a proper ioctl() request issued to user-space device /dev/url/. On our test phones, this device is accessible members of the secnetfilter group:

shell@hlte:/ $ ls -l /dev/url
crw-rw---- secnetfilter secnetfilter 226,   0 2014-01-04 19:36 url

In turn, access to the secnetfilter group is granted to applications via the com.sec.android.SAMSUNG_GET_URL permission, created with protection level signature. An App with this permission can thenconfigure the device to enable different monitoring modes:

  • FILTER_MODE_ON_BLOCK: standard blocking mode, URLs are checked on outgoing packets and eventually blocked.
  • FILTER_MODE_ON_RESPONSE: alternative blocking mode, URLs are extracted from outgoing packets, but responses are blocked and replaced with a custom HTTP/404 message.
  • FILTER_MODE_ON_BLOCK_REFER: similar to FILTER_MODE_ON_BLOCK, but checks the Referer header rather than the requested URL.
  • FILTER_MODE_ON_RESPONSE_REFER: similar to FILTER_MODE_ON_RESPONSE, but checks the Referer header rather than the requested URL.

If any of the blocking modes is enabled, then each visited URL is put in a queue and blocked until the App unblocks it.

To wrap up, an App that wants to use the URL filtering feature should implement the following logic:

  1. First, the App enables one of the blocking modes available, for example by writing the following binary data to /dev/url:

    <2-byte ver (0x00)><4-byte SET_FILTER_MODE cmd (0x01)><4 byte mode>

  2. Then, the App polls on /dev/url until some data is available for reading. Read data has the following format:

    <4-byte header><4-byte blocked URL ID><4 irrelevant bytes><4-byte URL length><n-byte URL>

  3. Finally, the App checks the URL, takes a decision according to some application-specific logics, and communicates the decision to the driver, writing the following bytes on /dev/url:

    <2-byte ver (0x00)><4-byte SET_USER_SELECT cmd (0x01)><4-byte URL ID><4-byte choice (0x64 for block, 0xx65 for allow)>

The kernel module reads integers from the buffer through direct dereferences (e.g., filterMode = *(int *)data), so data must be in little-endian format. When the kernel module receives a SET_USER_SELECT command, it checks if the received ID corresponds to a blocked request (i.e., to a blocked TCP flow) and acts on it, according to the App decision.

We currently identified only a single application which uses this permission, namely com.symantec.familysafety, thus the whole URL filtering mechanism is probably not so widespread, or maybe it is still at an early stage of its development.

Security implications

As discussed before, it is true that only applications signed by Samsung (and installed exclusively through the Samsung App store) can obtain the SAMSUNG_GET_URL permission, yet this kernel module should raise the concerns of more than one privacy-aware Samsung users. It can be certainly wrapped in a "child protection" coat, but it still is a module that allows an "authorized" App to monitor any visited URL.

Anyway, besides ethical and privacy-related considerations, this module is quite worrisome even from a merely technical perspective. Indeed, after the URL filtering mechanism has been activated by an "authorized" App, any user-space application can then trigger a NULL pointer dereference in the HTTP parsing code, and crash the system.

To trigger the bug, it is simply enough to issue an HTTP request with no URL, such as (see this file):

GET  HTTP/1.1
Host: www.google.com

The actual destination of such a request is irrelevant, as all the outgoing HTTP traffic is inspected when URL filtering is enabled.

Technically speaking, Samsung releases the source code of its Android kernels through its "Open Source Release Center". For the NULL pointer dereference outlined above, the vulnerable code lies in drivers/secfilter/urlparser.c, function getURL(). In a nutshell, the module extracts the HTTP request path through the following call:

findStringByTag(node, &(node->url), dataStart, " HTTP/");

Where dataStart is a char string pointing right after the HTTP request method token. However, when findStringByTag() processes the malformed request above it finds a zero-length string for the HTTP path, and leaves node->url uninitialized. Few lines after, node->url is dereferenced while still NULL.

Affected devices

We confirm this issue affects the following device models. Other models and firmware versions are probably affected as well, but they were not tested.

  • SM-N9005, build N9005XXUGBOB6 (Note 3)
  • SM-G920F, build G920FXXU2COH2 (Galaxy S6)