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 toFILTER_MODE_ON_BLOCK, but checks theRefererheader rather than the requested URL.FILTER_MODE_ON_RESPONSE_REFER: similar toFILTER_MODE_ON_RESPONSE, but checks theRefererheader 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:
-
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> -
Then, the App polls on
/dev/urluntil 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> -
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)