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