Welcome to the Acumen Security Blog

Revisiting W^X with OpenBSD 6.0

Overview

OpenBSD 6.0 was released today, and with it some exciting new security features.  From my perspective, the chief among them is the technical enforcement of W^X in user-land. Since moving to a technical control rather than a policy statement for enforcing executable space protection was a result of discussions caused by my last blog post on the situation, I’m very excited about this development and thought that giving a demonstration and discussion would be in order. (In the spirit of not putting the headline on Page 1 and the retraction on Page 11, hopefully BSDNow will cover this as well).

Review

Recall that my interest in the subject stems from an objective requirement in the NIAP-sponsored Operating System Protection Profile v4.x, which states that the operating system should prevent an application from being able to map a page of memory with both Write and Execute permissions (protecting mmap(2)) and that once mapped, a page of memory should not be able to have permissions escalated (protecting mprotect(2)).

There is room for interpretation of the meaning of this requirement around two competing ways to implement it.  I made my case for my personal preference in my last post, however the competing case wherein we are merely concerned with a page of memory never being simultaneously W|X and not necessarily enforcing, say W!->X, does have merit and would likely meet the letter of the requirement.

OpenBSD, which is where the branded term “W^X” originates, now enforces the strict W^X definition, and not the PaX/grsec “once write never execute” type of policy in 6.0.

Looking Deeper – A side-by-side comparison with HardenedBSD’s PaX model

In OpenBSD 6.0, when an application attempts to map a page of memory W|X, the process will be terminated with a SIGABRT (signal 6), causing the process to die and drop core. Attempting to run my test application, cc-memtest, which has 3 cases defined, does not get past the first test on OpenBSD 6.0, as the application is aborted:

obsd60-hbsd-cap6

Looking in GDB, we see right where the fault is caught:

obsd60-cap2

Handling this same case, a PaX model implementation does something a little differently.  As we see in both GDB and the procstat output, the page of memory is actually created and mapped, however it is only writable, not executable (and, properly, not readable either, which we didn’t request).

obsd60-hbsd-cap3

The implementation returns a writable page whenever write and execute permissions are asked for.

The application is actually allowed to run, and may or may not be OK.  Attempts to perform an action on that page for which you don’t have permissions will fail (ie, attempting to execute code that is placed in the buffer). However, as we see with OpenBSD’s implementation, the fault is considered to be the allocation of the memory region itself and not the unapproved access to the page.

In this regards, the behavior of the OpenBSD implementation is more readily apparent. The program author requested an R|W|X page of memory and that request causes the program to terminate, as opposed to providing the page of memory with different permissions than was explicitly requested by the program author and then exhibiting failure behavior on a write that may or may not ever happen.

Because HardenedBSD doesn’t abort the process, we can set a new break point and continue onto additional tests to show how mprotect(2) hardening works:

obsd60-hbsd-cap4

Here, we see that the application has successfully mapped a page of memory as executable and then attempts to escalate permissions to include writable. mprotect(2) returns an error code, and we see from procstat that the page of memory in question was created as executable, and then was still ONLY executable after the mprotect(2) attempt.

My quick-and-dirty test program doesn’t have a handler for SIGABRT, and rather than write one in, I pulled test three out as its own program so we can verify mprotect hardening on OpenBSD:

obsd60-7

Again, we see that the page was created properly, but the attempt to make it both executable and writable caused the program to receive SIGABRT and die.

Other differences

One of the other major differences between the PaX/grsecurity model that HardenedBSD implements and OpenBSD 6.0’s implementation is around whether a page of memory should EVER be able to be writable once it has been executable and vice versa.  Some JITs for instance, may want to use mprotect(2) to switch between write and execute permissions on a page of memory in an attempt to not have simultaneously writable and executable permissions. On HardenedBSD, that does not work. On OpenBSD, you can do that:

obsd60-hbsd-cap6

For instance, on HardenedBSD, a piece of code like this will never set write permissions on the page:

obsd60-hbsd-cap5

obsd60-hbsd

So, in order to not run afoul of page exec protections under PaX, a JIT implementer (for instance) would have to take alternative approaches, such as:

  • Maintaining a ‘shadow mapping’ in the same process space and removing one map when it is no longer needed
  • Privilege separating the JIT into two processes with their own address space, each with their own mapping to a shared memory object containing the code to be run through the JIT

Conclusions

When I looked into this issue back in the spring, OpenBSD did not have technical controls in place in user-land, merely a policy that application authors should NOT map W|X memory. HardendBSD and NetBSD, both implemented the PaX model and were able to pass my tests (NetBSD needing a sysctl tweak to enable protection, HardenedBSD by default). However, discussions after the press the previous post received lead to the new technical control implementation coming in for 6.0.

Obviously, the two approaches have some functional differences. OpenBSD’s approach is more deterministic in how one might expect a program to behave than the PaX model. Both the OpenBSD approach and the PaX approach meet the “letter of the law” from the Common Criteria standpoint now, as neither will allow an application to map a page of memory with W|X permissions – OpenBSD merely shuts the door on that activity in a more spectacular fashion.

Over all, though, we are now in a position in which three operating systems exhibit correct behavior with regards to this requirement without requiring additional patches to be applied (i.e., to bring grsecurity into Linux), and all three are BSDs. My hope would be that FreeBSD might look into adopting the HardenedBSD patches up stream so that all three mainline BSDs could meet this requirement, but in the meantime the inclusion of OpenBSD in the club makes it harder for other OS vendors to not take the requirement seriously moving forward.

(and, in related news, NetBSD has actually finally removed the last RWX page of memory in its kernel.  Announcement can be seen here)

 

Welcome Aboard!

At Acumen, are always looking for ways to improve both our customer experience and our testing. In addition to tooling, infrastructure, and training investments, we truly believe the best way to build a world class lab is to bring on the right people. I think we’ve done a great job of bringing not only excellent engineers but excellent people on board. Today is not exception, I am proud and pleased to announce the latest member of the Acumen family, Ryan Thomas.
Many of you may recognize Ryan’s name from his many years leading a FIPS lab, the many crypto related working group he’s a member of, or the conferences he is always at. Both Ashit and I have had the opportunity to work with Ryan both at Acumen and in our previously jobs at Cisco. It is an understatement to say that our experiences with Ryan were nothing but excellent. Ryan truly embodies the Acumen philosophy of putting the customers first and foremost in every interaction. This combined with his excellent technical grasp is why we are so excited to have him join Acumen.
So, if you have a call with us over the next couple weeks, you will likely hear Ryan on the bridge as well. True to form, Ryan is already overly eager to start jumping in and producing. Please say “Hi” if he’s on a call or email chain. Just like Ashit and I (and all of us here at Acumen really), he loves to chat and would love to help you navigate the certification process.
Welcome aboard Ryan, today Acumen is better than yesterday!