El Capitan is finally released and System Integrity Protection aka SIP aka rootless is finally a reality we must face. Let me briefly describe SIP (technical details maybe in another post, now that El Capitan is final and out of NDAs). This post by Rich Trouton contains a very good description of its userland implementation and configuration.
What is SIP anyway?
The description that I like to use is that SIP is a giant system-wide sandbox, that controls access to what Apple considers critical files and folders. One of the reasons for this is that most of kernel side SIP implementation exists into the Sandbox.kext, the same TrustedBSD kernel extension that implements OS X sandbox mechanism.
For example, if we try to write to /System folder we get the following result:
sh-3.2# touch /System/test touch: /System/test: Operation not permitted
And in system logs:
12/10/15 17:27:20,650 sandboxd: () touch(424) System Policy: deny file-write-create /System/test
In practice it means that even with root access we are unable to modify those critical files and folders.
But, how are updates possible?
Apple created a few new private entitlements (only available to binaries signed with Apple certificates) that allow binaries that have them to modify the files and folders. This is one of the possible attack points against SIP – find a vulnerability in the right entitled binary and you can do whatever you want to the system.
Can SIP be disabled?
Yes, there is an util called csrutil that can be used to update its settings. It must be run in Recovery or Install mode. Once again refer to Rich Trouton’s post for more details.
Are there any other ways to disable it?
Yes. If we are able to run kernel code we can disable SIP. For example, we can mess with the TrustedBSD hooks and remove them out of the way. Or attack the hook decision engine and always authorize, and so on. Once you are able to run code at the same privilege lowest level that exists in the machine there’s very few that can protect you, if anything (reference, PatchGuard kernel implementation by Microsoft, bypassed a few times and which is starting to move to hypervisor level).
This doesn’t mean that SIP is necessarily a bad idea. Apple’s goal is to increase the amount of work required by an attacker and reduce attack surface. The problem with this strategy are too many issues in OS X kernel, but that’s another discussion.
Hacking is mostly about posing questions that break out someone’s else assumptions. So the other day I was thinking if there was an easier way to disable SIP other than attacking the hooks and so on (I did that before on beta releases). Because there is an option that is read from NVRAM about disabling SIP, this gives us a signal that there is a kernel decision somewhere about enabling/disabling SIP (because EFI has nothing to do on this decision). Essentially we are chasing either a variable or a classical cracking je/jne decision.
How is csrutil implemented?
If we disassemble csrutil we will see the following flow:
csrutil -> csr_get_active_config() -> _csrctl() -> syscall 0x1E3 -> csrctl()
So essentially there’s a new syscall 0x1E3 that implements a new function csrctl in the kernel, that csrutil uses to retrieve the active SIP configuration. If there’s info about active configuration there’s information somewhere about its status. And this info can be found at address 0xFFFFFF8000ACFF48 (XNU 10.11.0 build 15A284). There are a few references to this address, all from functions with interesting and very descriptive names. If you disassemble them you will understand that this variable will be the jackpot we were looking for. It will enable or disable SIP.
To make something runtime we just need to find the address of this variable and modify it as we wish to disable or enable SIP. To find its location we can disassemble the simplest function that references it and find its value, or just hardcode it.
Is there an easy way?
Of course there is, else this question would be meaningless. Look at the function names…
FFFFFF80007835C0 public _csr_set_allow_all FFFFFF80007835C0 _csr_set_allow_all proc near FFFFFF80007835C0 push rbp FFFFFF80007835C1 mov rbp, rsp FFFFFF80007835C4 test edi, edi FFFFFF80007835C6 setnz al FFFFFF80007835C9 movzx eax, al FFFFFF80007835CC mov cs:dword_FFFFFF8000ACFF48, eax FFFFFF80007835D2 pop rbp FFFFFF80007835D3 retn FFFFFF80007835D3 _csr_set_allow_all endp
This function does exactly what we are looking for. It will modify the SIP status based on the value that we pass on its argument. To disable SIP call it with 1, to enable with 0. The function has zero references inside the kernel, meaning that either no other function is calling it or a function pointer is used. It can be found in the Private.kext kernel KPI.
To use this function in a kext, we either solve the symbol ourselves (the solution I used), or just link against the Private KPI (I’m not sure if this would make kextd reject loading the kext for some reason – haven’t poked around the new kextd yet).
A fully working kext and small GUI app to control it can be found here.
First you need to manually load the kernel extension and then you can start the GUI and connect to the kernel extension. Then you can disable and enable SIP as you wish.
While this function is not a security vulnerability (once again we don’t really need it to disable SIP, it just makes everything easier and cleaner) it’s not clear that Apple would allow a binary distribution of this kernel extension. This means that you will need yourself a kernel code signing certificate to sign your own version, if you wish to use this. Some games could be played to make sure this was moderately secure against abuse by malware. Still it’s not good enough to risk company certificate on this (maybe worth it to risk $99 and give it a try on a personal certificate).
Anyway, it’s purpose is more to developers and expert users that know what they are doing and don’t want to reboot their machines for testing stuff.
I forgot one interesting detail in the original post. Disabling SIP this way doesn’t kill its log feature, meaning that you can still find in the logs who tried to write to those unauthorized folders.
12/10/15 22:14:38,304 sandboxd: () touch(466) System Policy: allow file-write-create /System/test
My SentinelOne colleague Julien-Pierre created a menu item that will load the kext and enable/disable it. I’ll add it to the project soon.
Many thanks to SentinelOne’s Assaf for the name suggestion, better than my original rootful.
Greetings to Apple security related teams boys and girls 😄.
And say thank you to SentinelOne for my time.
P.S.: The blog is a bit messed up. WordPress f*cked up somewhere and things are a bit shaky. Will try to fix or evaluate alternatives.
P.S.2: It’s an happy day today, fuck off the right wing parties in Portugal. Hopefully it’s a bye bye you fucking morons. </political rant>