Thoughts? At first glance, it looks like a good mix of hardening and
sensible defaults. It isn't quite fully locked down as MIA, but I can
imagine using this as a starting point for something. I've also been
chatting with them on Twitter about including Orfox and Orbot.

https://copperhead.co/docs/technical_overview

****

Technical overview of CopperheadOS

This is a technical overview of the currently implemented features in
CopperheadOS. For a list of planned features, see the issues tagged as
enhancements on the tracker. The scope will be expanded as more features
are implemented.

Exec-based spawning model

CopperheadOS spawns applications from the Zygote service in the
traditional Unix way with fork and exec rather than Android’s standard
model using only fork. This results in the address space layout and
stack/setjmp canaries being randomized for each spawned application
rather than having the same layout and canary values reused for all
applications until reboot. In addition to hardening applications from
exploitation, this also hardens the base system as the large, near root
equivalent system_server service is spawned from the Zygote.

This required some workarounds due to code depending on the Zygote
spawning model.

Bionic libc improvements

Hardened allocator

CopperheadOS replaces the system allocator with a port of OpenBSD’s
malloc implementation.

Fine-grained randomization is performed for small allocations by
choosing a random pool to satisfy requests and then choosing a random
free slot within a page provided by that pool. Freed small allocations
are quarantined before being put back into circulation via a randomized
delayed allocation pool. This raises the difficulty of exploiting
vulnerabilities by making the internal heap layout and allocator
behavior unpredictable.

Unlike jemalloc, Android’s standard allocator, OpenBSD uses page-aligned
regions instead of 2MiB aligned regions so it doesn’t lose any of the
mmap randomization entropy. The standard allocator loses 9 bits of mmap
entropy on systems with 4096 byte pages.

Small allocations are filled with junk data upon being released, as is
the initial portion of large allocations. This eliminates many
information leaks caused by lack of initialization and can make the
exploitation of use-after-free and double free vulnerabilities more
difficult. Full junk filling on free is available via a user-facing
setting. Unlike OpenBSD, filling new allocations with junk data rather
than either zeroes or the junk on free pattern is split out into a
separate option (not exposed to users) as it’s much more of an
auditing/debugging feature than a security one.

A user-facing setting is exposed for placing guard pages at the end of
large allocations to prevent and detect overflows at the cost of higher
memory usage and reduced performance. It may be enabled by default on
64-bit in the future. CopperheadOS also extends the allocator with
support for canaries at the end of small allocations, mitigating small
overflows and catching corruption upon free (this could potentially be
upstreamed). This is disabled by default for now and exposed via a
setting due to the memory usage cost.

A user-facing setting is exposed for enabling page cache memory
protection to trigger aborts for use-after-free bugs. For small
allocations, a whole page needs to be cleared out for it to happen, so
CopperheadOS includes an extension providing enhanced use-after-free
detection for all small allocations. It simply checks if the junk
pattern used on free is still present when allocations are moved out of
the delayed free array. This is also exposed via a user-facing setting.
It may be extended further in the future.

The page span cache is also randomized. This avoids the introduction of
determinism, but the value will be limited until a form of fine-grained
randomization is added below the malloc layer like the mmap
randomization on OpenBSD.

The allocator doesn’t use any inline metadata, so traditional allocator
exploitation techniques are not possible. The configuration is made
read-only at runtime and the rest of the global state is protected to
some extent via randomization and canaries. CopperheadOS has some small
extensions to improve the randomization and may do more in the future.

Extended _FORTIFY_SOURCE implementation

The _FORTIFY_SOURCE feature provides buffer overflow checking for
standard C library functions in cases where the compiler can determine
the buffer size at compile-time. In CopperheadOS, it also provides
system calls with dynamic heap overflow checks by querying the allocator
for allocation sizes. The dynamic checks will be extended in the future.

Copperhead has added fortified implementations of the following
functions:

fread - upstreamed
fwrite - upstreamed
getcwd - upstreamed
memchr - upstreamed
memrchr - upstreamed
pread - upstreamed
pread64 - upstreamed
pwrite - upstreamed
pwrite64 - upstreamed
readlink - upstreamed
readlinkat - upstreamed
realpath - upstreamed
send - submitted upstream
sendto - submitted upstream
write - upstreamed
Additionally, the dlmalloc API has been annotated with alloc_size
attributes to provide buffer overflow checking for the remaining code
using the extended API.

Some false positives in jemalloc were fixed in order to land support for
write fortification in AOSP.

Function pointer protection

Writable function pointers in libc have been eliminated, removing
low-hanging fruit for hijacking control flow with memory corruption
vulnerabilities.

The at_quick_exit and pthread_atfork callback registration functions
have been extended with the same memory protection offered by the atexit
implementation inherited from OpenBSD.

The vDSO function pointer table is made read-only after initialization,
as is the pointer to the function pointer table used to implement
Android’s malloc debugging features. This has been upstreamed.

Mangling of the setjmp registers was implemented upstream based on input
from Copperhead.

Miscellaneous improvements

Allocations larger than PTRDIFF_MAX are prevented, preventing a class of
overflows. This has been upstreamed for mmap and mremap.

A dedicated memory region is created for mapping libraries, to isolate
them from the rest of the mmap heap. It is currently 128M on 32-bit and
1024M on 64-bit. A randomly sized protected region of up to the same
size is placed before it to provide a random base within the mmap heap.
The address space is reused via a simple address-ordered best-fit
allocator to keep fragmentation at a minimum for dynamically
loaded/unloaded libraries (plugin systems, etc.).

Secondary stacks are randomized by inserting a random span of protected
memory above the stack and choosing a random base within it. This has
been submitted upstream.

Secondary stacks are guaranteed to have at least one guard page above
the stack, catching sequential overflows past the stack mapping. The
pthread_internal_t structure is placed in a separate mapping rather than
being placed within the stack mapping directly above the stack. It
contains thread-local storage and the value compared against stack
canaries is stored there on some architectures.

Signal stacks were given guard pages to catch stack overflows. This was
upstreamed.

Assorted small improvements:

have getauxval(…) set errno to ENOENT for missing values, per glibc 2.19
- upstreamed
name the atexit handler pages - upstreamed
name the arc4random structure mappings - upstreamed
fix the mremap signature - upstreamed
implementations of explicit_memset and secure_getenv for use elsewhere
replaced the broken implementations of issetugid and explicit_bzero
larger default stack size on 64-bit (1MiB -> 8MiB)
and more…
PaX kernel

CopperheadOS has ports of the PaX kernel hardening patch to the
supported devices. It may use a larger subset of grsecurity in the
future, but most of the additional features over PaX are not usable on
Android or are already provided in other ways. It would make more sense
to extract only the useful features (PROC_MEMMAP, KSTACKOVERFLOW,
HIDESYM, RANDSTRUCT, DEVICE_SIDECHANNEL, RWXMAP_LOG and maybe a few
more) rather than maintaining a full port.

Userspace features

PaX’s userspace hardening features are all enabled. The RANDMMAP feature
provides significantly stronger Address Space Layout Randomization
(ASLR) than the vanilla kernel and eliminates the mmap address hint
footgun. PAGEEXEC turns no-execute violations into fatal errors rather
than recoverable ones. MPROTECT prevents runtime code
modification/injection.

There are a few MPROTECT exceptions (mediaserver, mm-qcamera-app,
mm-qcamera-daemon) for the base system to work around proprietary
libraries that are not easily fixable. CopperheadOS contains a custom
exception system for dealing with Android applications via group-based
permissions and has package manager integration to set the permissions
automatically. The package manager scans for calls to the WebView
widget’s setJavaScriptEnabled method in an Android package’s bytecode
and automatically adds MPROTECT exceptions as necessary. The remaining
issues are dealt with using a hard-wired exception table in the package
manager mapping application names like com.chrome.beta to a list of
necessary exceptions. Very few of these manually made exceptions are
required and more issues could be auto-detected in the future.

A toggle for soft mode is exposed in Settings -> Developer options along
with a status entry in Settings -> About device displaying whether a PaX
kernel is in use and the soft mode setting.

Kernel self-protection

Some kernel self-protection features are enabled (MEMORY_SANITIZE,
REFCOUNT, USERCOPY) along with the baseline improvements without
configuration options. KERNEXEC and UDEREF will be available for devices
using the 3.10 kernel. The features implemented via compiler plugins
have been tested and are work well, but the necessary changes to support
them are unfinished and aren’t yet published.

Compiler hardening

Lightweight bounds checking for statically sized arrays via
-fsanitize=bounds -fsanitize-trap=bounds.
For some sub-projects, lightweight object size bounds checking is
performed (including extended checks for arrays) via
-fsanitize=object-size -fsanitize-trap=object-size. This has a lot of
overlap with the bounds sanitizer, but the redundant checks are
optimized out when both are set to trap on error. It would be nice to
enable this globally, but there’s too much code relying on undefined
out-of-bounds accesses.
For some sub-projects, both unsigned and signed integer overflow
checking via -fsanitize=integer -fsanitize-trap=integer (mostly
backported from AOSP master).
Stack overflow checking for supported architectures via -fstack-check
(not on ARM yet due to a severe bug) - submitted upstream for the NDK
Signed integer overflow is made well-defined via -fwrapv to avoid having
incorrectly written overflow checks optimized out.
Expanded stack overflow canaries to all relevant functions via
-fstack-protector-strong for both the NDK and base system. It has also
been upstreamed for both the NDK and base system.
Added -Wsuggest-attribute=format warning to audit for missing format
attributes - dozens of cases have been found and fixed, providing more
coverage for warnings about exploitable format string bugs.
_FORTIFY_SOURCE=2 is enabled for local usage of the NDK, since
portability isn’t a concern.
Enhanced SELinux policies

eliminated code injection holes
removed gpu_device execute access
removed ashmem execute access
removed tmpfs execute access
the remaining hole in full (not just in-memory) w^x is app_data_file
execution, and the intention is to address this with a whitelisting
model in the package manager similar to the handling of PaX exceptions
removed mediaserver’s write access to sysfs
Encryption

Full disk encryption is enabled by default on all supported devices, not
just those shipping that way with the stock operating system.

Support for a separate encryption password

In vanilla Android, the encryption password is tied to the lockscreen
password. That’s the default in CopperheadOS, but there’s full support
for setting a separate encryption password. This allows for a convenient
pattern, pin or password to be used for unlocking the screen while using
a very strong encryption passphrase. If desired, the separate encryption
password can be removed in favor of coupling it to the lockscreen
password again.

When a separate encryption password is set, the lockscreen will force a
reboot after 5 failed unlocking attempts to force the entry of the
encryption passphrase. This makes it possible to use a convenient
unlocking method without brute force being feasible. It offers similar
benefits as wiping after a given number of failures or using a
fingerprint scanner without the associated drawbacks.

Better defaults

The default settings have been altered to emphasize privacy/security
over small conveniences.

Location tagging is disabled by default in the Camera app, and there is
no longer a prompt about choosing whether to enable it on the first
launch. It can still be enabled using the Camera’s settings menu.

passwords are hidden by default
sensitive notifications are hidden on the lockscreen by default
NFC and NDEF Push are disabled by default
Browser:

DuckDuckGo as the default browser home page and search engine instead of
Google
link preloading in the browser is disabled by default
search result preloading in the browser is disabled by default
plugins are disabled by default (although there aren’t any around by
default)
Miscellaneous features

SQLite’s SECURE_DELETE feature is enabled, resulting in deleted content
being overwritten with zeros. This prevents sensitive data from
lingering around in databases after it’s deleted. SQLite is widely used
by Android’s base system is the standard storage mechanism for
applications, so this results in lots of coverage.

The hidepid=2 option is enabled for procfs, hiding processes owned by
other UIDs. Since non-system apps each have a unique UID, this prevents
apps from obtaining sensitive information about each other via /proc.
There are exceptions for a few of the core services via the gid mount
option (lmkd, servicemanager, keystore, debuggerd, logd, system_server)
but not for apps. A subset of this was provided by SELinux, but it isn’t
fine-grained enough. This enhancement was adopted upstream based on the
implementation in CopperheadOS (it had been planned, but they were
unaware of the gid mount option).

Some misuses of memset for sanitizing data were replaced with
explicit_memset, to stop the compiler from optimizing them out.

Interfaces are given a random MAC address whenever they are brought up.
This can be disabled via a toggle in the network settings.

The kernel TCP/IP settings are adjusted to prioritize security. A
minimal firewall is provided with some options that are always sane.
Support for IP sets is enabled in the kernel and the ipset utility is
provided, but not yet integrated in an automated way. Android has
group-based control over networking so basic controls over networking
are in the realm of PrivacyGuard, but more advanced firewall features
might be provided down the road.

Many global function pointers in Android’s codebase have been made
read-only. This is ongoing work and will need to be complemented with
Control Flow Integrity (CFI) as many are compiler-generated. Some of
this work has been upstreamed: 1, 2.

-- 
  Nathan of Guardian
  nat...@guardianproject.info
_______________________________________________
List info: https://lists.mayfirst.org/mailman/listinfo/guardian-dev
To unsubscribe, email:  guardian-dev-unsubscr...@lists.mayfirst.org

Reply via email to