{"id":692,"date":"2013-12-10T15:40:06","date_gmt":"2013-12-10T23:40:06","guid":{"rendered":"http:\/\/www.outflux.net\/blog\/?p=692"},"modified":"2013-12-10T15:40:44","modified_gmt":"2013-12-10T23:40:44","slug":"live-patching-the-kernel","status":"publish","type":"post","link":"https:\/\/outflux.net\/blog\/archives\/2013\/12\/10\/live-patching-the-kernel\/","title":{"rendered":"live patching the kernel"},"content":{"rendered":"<p>A nice set of recent posts have done a great job detailing the remaining ways that a root user can get at kernel memory. Part of this is driven by the ideas behind UEFI Secure Boot, but they come from the same goal: making sure that the root user cannot directly subvert the running kernel. My perspective on this is toward making sure that an attacker who has gained access and then gained root privileges can&#8217;t continue to elevate their access and install invisible kernel rootkits.<\/p>\n<p>An outline for possible attack vectors is spelled out by Matthew Gerrett&#8217;s continuing <a href=\"https:\/\/lkml.org\/lkml\/2013\/9\/9\/532\">&#8220;useful kernel lockdown&#8221; patch series<\/a>. The set of attacks was examined by Tyler Borland in <a href=\"http:\/\/turbochaos.blogspot.com\/2013\/10\/writing-linux-rootkits-301_31.html\">&#8220;Bypassing modules_disabled security&#8221;<\/a>. His post describes each vector in detail, and he ultimately chooses MSR writing as the way to write kernel memory (and shows an example of how to re-enable module loading). One thing not mentioned is that many distros have MSR access as a module, and it&#8217;s rarely loaded. If <code>modules_disabled<\/code> is already set, an attacker won&#8217;t be able to load the MSR module to begin with. However, the other general-purpose vector, kexec, is still available. To prove out this method, Matthew wrote a <a href=\"http:\/\/mjg59.dreamwidth.org\/28746.html\">proof-of-concept for changing kernel memory via kexec<\/a>.<\/p>\n<p>Chrome OS is several steps ahead here, since it has hibernation disabled, MSR writing disabled, kexec disabled, modules verified, root filesystem read-only and verified, kernel verified, and firmware verified. But since not all my machines are Chrome OS, I wanted to look at some additional protections against kexec on general-purpose distro kernels that have <code>CONFIG_KEXEC<\/code> enabled, especially those without UEFI Secure Boot and Matthew&#8217;s lockdown patch series.<\/p>\n<p>My goal was to disable kexec without needing to rebuild my entire kernel. For future kernels, I have <a href=\"https:\/\/lkml.org\/lkml\/2013\/12\/9\/790\">proposed adding <code>\/proc\/sys\/kernel\/kexec_disabled<\/code><\/a>, a partner to the existing <code>modules_disabled<\/code>, that will one-way toggle kexec off. For existing kernels, things got more ugly.<\/p>\n<p>What options do I have for patching a running kernel?<\/p>\n<p>First I looked back at what I&#8217;d done in the past with <a href=\"http:\/\/www.outflux.net\/blog\/archives\/2012\/01\/22\/fixing-vulnerabilities-with-systemtap\/\">fixing vulnerabilities with systemtap<\/a>. This ends up being a rather heavy-duty way to go about things, since you need all the distro kernel debug symbols, etc. It does work, but has a significant problem: since it uses kprobes, a root user can just turn off the probes, reverting the changes. So that&#8217;s not going to work.<\/p>\n<p>Next I looked at ksplice. The <a href=\"https:\/\/oss.oracle.com\/ksplice\/software\/\">original upstream has gone away<\/a>, but there is still some <a href=\"https:\/\/github.com\/jirislaby\/ksplice\">work being done by Jiri Slaby<\/a>. However, even with his updates which fixed various build problems, there were still more, even when building a 3.2 kernel (Ubuntu 12.04 LTS). So that&#8217;s out too, which is too bad, since ksplice does exactly what I want: modifies the running kernel&#8217;s functions via a module.<\/p>\n<p>So, finally, I decided to just do it by hand, and wrote a friendly kernel rootkit. Instead of dealing with flipping page table permissions on the normally-unwritable kernel code memory, I borrowed from PaX&#8217;s KERNEXEC feature, and just turn off write protect checking on the CPU briefly to make the changes. The return values for functions on x86_64 are stored in RAX, so I just need to stuff the <code>kexec_load<\/code> syscall with &#8220;<code>mov -1, %rax; ret<\/code>&#8221; (-1 is <code>EPERM<\/code>):<\/p>\n<pre class=\"brush:c\">\r\n#define pr_fmt(fmt) KBUILD_MODNAME \": \" fmt\r\n\r\n#include &lt;linux\/init.h&gt;\r\n#include &lt;linux\/module.h&gt;\r\n#include &lt;linux\/slab.h&gt;\r\n\r\nstatic unsigned long long_target;\r\nstatic char *target;\r\nmodule_param_named(syscall, long_target, ulong, 0644);\r\nMODULE_PARM_DESC(syscall, \"Address of syscall\");\r\n\r\n\/* mov $-1, %rax; ret *\/\r\nunsigned const char bytes[] = { 0x48, 0xc7, 0xc0, 0xff, 0xff, 0xff, 0xff,\r\n                                0xc3 };\r\nunsigned char *orig;\r\n\r\n\/* Borrowed from PaX KERNEXEC *\/\r\nstatic inline void disable_wp(void)\r\n{\r\n        unsigned long cr0;\r\n\r\n        preempt_disable();\r\n        barrier();\r\n        cr0 = read_cr0();\r\n        cr0 &= ~X86_CR0_WP;\r\n        write_cr0(cr0);\r\n}\r\n\r\nstatic inline void enable_wp(void)\r\n{\r\n        unsigned long cr0;\r\n\r\n        cr0 = read_cr0();\r\n        cr0 |= X86_CR0_WP;\r\n        write_cr0(cr0);\r\n        barrier();\r\n        preempt_enable_no_resched();\r\n}\r\n\r\nstatic int __init syscall_eperm_init(void)\r\n{\r\n        int i;\r\n        target = (char *)long_target;\r\n\r\n        if (target == NULL)\r\n                return -EINVAL;\r\n\r\n        \/* save original *\/\r\n        orig = kmalloc(sizeof(bytes), GFP_KERNEL);\r\n        if (!orig)\r\n                return -ENOMEM;\r\n        for (i = 0; i &lt; sizeof(bytes); i++) {\r\n                orig[i] = target[i];\r\n        }\r\n\r\n        pr_info(\"writing %lu bytes at %p\\n\", sizeof(bytes), target);\r\n\r\n        disable_wp();\r\n        for (i = 0; i &lt; sizeof(bytes); i++) {\r\n                target[i] = bytes[i];\r\n        }\r\n        enable_wp();\r\n\r\n        return 0;\r\n}\r\nmodule_init(syscall_eperm_init);\r\n\r\nstatic void __exit syscall_eperm_exit(void)\r\n{\r\n        int i;\r\n\r\n        pr_info(\"restoring %lu bytes at %p\\n\", sizeof(bytes), target);\r\n\r\n        disable_wp();\r\n        for (i = 0; i &lt; sizeof(bytes); i++) {\r\n                target[i] = orig[i];\r\n        }\r\n        enable_wp();\r\n\r\n        kfree(orig);\r\n}\r\nmodule_exit(syscall_eperm_exit);\r\n\r\nMODULE_LICENSE(\"GPL\");\r\nMODULE_AUTHOR(\"Kees Cook &lt;kees@outflux.net&gt;\");\r\nMODULE_DESCRIPTION(\"makes target syscall always return EPERM\");\r\n<\/pre>\n<p>If I didn&#8217;t want to leave an obvious indication that the kernel had been manipulated, the module could be changed to:<\/p>\n<ul>\n<li>not announce what it&#8217;s doing<\/li>\n<li>remove the exit route to not restore the changes on module unload<\/li>\n<li>error out at the end of the init function instead of staying resident<\/li>\n<\/ul>\n<p>And with this in place, it&#8217;s just a matter of loading it with the address of <code>sys_kexec_load<\/code> (found via <code>\/proc\/kallsyms<\/code>) before I <a href=\"http:\/\/www.outflux.net\/blog\/archives\/2012\/11\/28\/clean-module-disabling\/\">disable module loading via modprobe<\/a>. Here&#8217;s my upstart script:<\/p>\n<pre class=\"brush:shell\">\r\n# modules-disable - disable modules after rc scripts are done\r\n#\r\ndescription \"disable loading modules\"\r\n\r\nstart on stopped module-init-tools and stopped rc\r\n\r\ntask\r\nscript\r\n        cd \/root\/modules\/syscall_eperm\r\n        make clean\r\n        make\r\n        insmod .\/syscall_eperm.ko \\\r\n                syscall=0x$(egrep ' T sys_kexec_load$' \/proc\/kallsyms | cut -d\" \" -f1)\r\n        modprobe disable\r\nend script\r\n<\/pre>\n<p>And now I&#8217;m safe from kexec before I have a kernel that contains <code>\/proc\/sys\/kernel\/kexec_disabled<\/code>.<\/p>\n<p style='text-align:left'>&copy; 2013, <a href=\"https:\/\/outflux.net\/blog\/\">Kees Cook<\/a>. This work is licensed under a <a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/4.0\/\">Creative Commons Attribution-ShareAlike 4.0 License<\/a>.<br \/><a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/4.0\/\"><img decoding=\"async\" alt=\"CC BY-SA 4.0\" style=\"border-width:0\" src=\"https:\/\/i.creativecommons.org\/l\/by-sa\/4.0\/88x31.png\" \/><\/a> <\/p>\n","protected":false},"excerpt":{"rendered":"<p>A nice set of recent posts have done a great job detailing the remaining ways that a root user can get at kernel memory. Part of this is driven by the ideas behind UEFI Secure Boot, but they come from the same goal: making sure that the root user cannot directly subvert the running kernel. [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,20,18,6,14,19],"tags":[],"_links":{"self":[{"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/posts\/692"}],"collection":[{"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/comments?post=692"}],"version-history":[{"count":5,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/posts\/692\/revisions"}],"predecessor-version":[{"id":697,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/posts\/692\/revisions\/697"}],"wp:attachment":[{"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/media?parent=692"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/categories?post=692"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/tags?post=692"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}