Welcome to the Acumen Security Blog

FPT_W^X_EXT.1 – A Rundown

Lately, there has been a lot of discussion in the Operating System iTC around an objective SFR in the current revisions of the protection profile, entitled FPT_W^X_EXT.1. The SFR is part of a set which are intended to address the O.INTEGRITY security objective for the OSPP.  While some of the discussion has been around what the actual meaning of the SFR is and what is hoped to be gained, much of it has been around whether any operating systems even support the feature as worded. This post intends to address all of these issues.

First, it is worth reading the actual text of the SFR itself:  “The OS shall prevent allocation of any memory region with both write and execute permissions except for [assignment: list of exceptions].”

The accompanying application note gives us some more insight into the rationale, as well as what is actually expected: “Requesting a memory mapping with both write and execute permissions subverts the platform protection provided by DEP. If the OS provides no exceptions (such as for just-in-time compilation), then “no exceptions” should be indicated in the assignment. Full realization of this requirement requires hardware support, but this is commonly available.”

While nearly all operating systems currently support the use of the NX bit, or the equivalent on processors such as SPARC and ARM, and will correctly mark the stack as non-executable, the fact remains that this in and of itself is deemed insufficient by the PP authors.

This is probably also where some of the confusion comes in.  First of all, the name of the SFR, “W^X,” introduces some confusion for the following reasons:

  1. W^X (write xor execute) is the name of an implementation specifically in OpenBSD. One might therefor think that OpenBSD would meet this SFR, however it does not, as I will demonstrate below.
  2. When you look at the testing activity, W^X does not cover all three of the test activities, which essentially break down as follows:
    1. W^X (write xor execute) – ensure that pages of memory can never be both writable and executable
    2. X!->W (execute never write) – ensure that an executable page of memory cannot be made to be writable
    3. W!->X (write never execute) – ensure that a writable page of memory cannot be made to be executable

The assurance activities direct the evaluator to:

  1. Acquire or construct a test program which attempts to allocate memory that is both writable and executable. The evaluator will run the program and confirm that it fails to allocate memory that is both writable and executable.
  2. Acquire or construct a test program which allocates memory that is executable and then subsequently requests additional write/modify permissions on that memory. The evaluator will run the program and confirm that at no time during the lifetime of the process is the memory both writable and executable.
  3. Acquire or construct a test program which allocates memory that is writable and then subsequently requests additional execute permissions on that memory. The evaluator will run the program and confirm that at no time during the lifetime of the process is the memory both writable and executable.

Obviously, these three assurance activities, as noted above, describe some things which are a little bit different from what one might think.  Additionally, they describe something different than what many operating systems (even OpenBSD) have actually implemented.

The following image shows a test program I constructed running in OpenBSD 5.8 on VMWare Workstation Professional 12.1:

openbsd

FreeBSD, Solaris, RHEL and most other Linux distributions also fail all three tests. Mac OS X passes the first test, but does not have mprotect hardening:

os x

There are, however, two BSD variants which do pass, and both have something in common.

First, let us look at the test running on HardenedBSD:

hbsd

As we can see, all three tests pass.

NetBSD is a little different:

netbsd1

However, we have an option here:

netbsd2

Both HardenedBSD and NetBSD ported the PaX method for ASLR and executable space protection.  Any Linux distribution which uses the  PaX hardening model (often leveraged through grsecurity) should also pass these tests.  Using SELinux to set the Boolean value for deny_execmem prevents one from mapping pages of memory which are executable at all, however, when one maps a page of memory as writable and then escalates permissions via mprotect() to PROT_WRITE|PROT_EXEC, one is able to do so:

rhel

As you see, with “setsebool deny_execmem 1”, we are unable to mmap() any page that is PROT_EXEC to start, but with mprotect(), we can create a PROT_WRITE|PROT_EXEC page of memory.

So, as we can see through illustration, there are clearly operating systems which support the SFR as written. Saying that there are none, as some have suggested, is false. HardenedBSD passes all three tests out of the box, and NetBSD will do so with a single sysctl tweak. Since they are using the PaX model, anything else using PaX, such as a grsecurity-enabled Linux distribution pass these assurance activities as well.

That doesn’t mean that the SFR can’t be improved, however. For instance, it does not address dual-aliasing a memory mapped file where on map has PROT_WRITE and the other has PROT_EXEC. Attempting to do so succeeds on all operating systems tried.

So, to recap where we are:

  1. The SFR can, and has, been met by several operating systems, though they are primarily non-commercial.
  2. The SRF is, however, written essentially to the PaX model for doing executable space protection. This isn’t to say that one must do things HOW PaX implemented the protections, but in order to meet the SFR, an OS needs to implement what they did.
  3. OpenBSD, the OS for which the SFR is named, does not actually pass the assurance activities.  The name of the SFR should be changed, since what OpenBSD W^X actually implements is not what is being asked for or tested in this SFR.
  4. There are still edge cases, such as dual-mmap aliasing files, which are not handled by the assurance activity, and for which no operating system appears to provide a prevention mechanism.  In fact, there are documents online illustrating doing exactly this to get around potential issues related to this type of restrictions.  Depending on the LOE to implement protection around this, it may or may not make sense to extend the assurance activities and have the SFR address this situation directly.

Given all of this, it makes sense to leave this as an Objective SFR for the time being. Hopefully it will serve as a nudge for OS vendors to move towards full implementation.

 

 

** UPDATE **

In the comments below, a reader helpfully brought up the new pledge(2) system call in OpenBSD 5.9 and how it might be used to edge further towards meeting the strict enforcement requirement of the protection profile.  I responded to the comment below, and I will likely do another post in the future delving more into pledge(2), particularly in the context of the Software Application Protection Profile, since it is more inline with providing a mechanism for meeting requirements in that protection profile.  I will likely also do a side-by-side comparsion against the similar Capsicum framework found in FreeBSD.

However, keeping in mind that, as written, the protection profile requires mandatory enforcement with the ability to opt-out on a per-application basis in order to meet needs such as those of JIT compilers, I would like to provide a few further illustrations of how pledge(2) doesn’t further compliance with this specific objective requirement in the OS protection profile.

First, a similar illustration to the one from OpenBSD 5.8, we attempt to map a page of memory as W|X, and this succeeds.  Code sample and execution output are in the right terminal, with procmap output in the left:

The test tool prints out the PID and the address of *page, which contains the mmap(2)’d page of memory.  In procmap, we see that we do have W|X permissions.

obsd59-memory1

In this next example, we see that I use pledge(“stdio”, NULL) above the call to mmap(2), so that the peldge enforcement kicks in prior to my attempt to map W|X memory.  Attempting to run the code causes it to die with sigabrt and a core dump, as by default a pledge(2) on stdio disable the ability to map memory as PROT_EXEC.

obsd59-memory2

Next, however, we see that If I move the peldge(2) call below the call to mmap(2), I am successfully able to map a W|X page of memory.  Again, we confirm this with procmap.

obsd59-memory3

In the final example, I move the pledge(2) call back above the mmap(2) call.  This time, in addition to pledging stdio, I also pledge prot_exec. Doing so allows me to now map a W|X page of memory.

obsd59-memory4

So, while I am absolutely a fan of pledge(2) due to its ease of use and ability to help enforce safe, correct behavior in software, the lack of mandatory, across-the-board enforcement of W^X pages in userland without the developer having to review or modify code first in order to conform to policy, does not meet the letter of the requirement that is being discussed here. That shouldn’t be taken as a slight against OpenBSD, their implementation, or all they have done for software  quality and security as well as general Internet hygiene, just an illustration that the requirement, as written, is almost expecting to be met via a PaX-model implementation.

Comments

  1. OpenBSD userland W^X is a policy not enforced by the implementation, however, kernel W^X is enforced in the kernel by hardware (NX) and any attempts to create such mappings will panic the kernel.

    Another thing to be considered is pledge(2), which covers a large portion of the base userland and some ports. In this case, by default, PROT_EXEC mappings are disallowed and the process is killed with an uncatchable SIGABRT unless “prot_exec” is promised.

    • W. Dean Freemean, CISSP GCIH says:

      Hi Darude,

      I did recently watch a talk by Theo on pledge(2) in OpenBSD 5.9. The initial above example was on 5.8. That said, I understand the OpenBSD model and their approach and reasoning and I appreciate it greatly. I didn’t mean to hammer on OpenBSD per se, however given that the requirement in the NIAP-authored protection profile is called W^X, which is an OpenBSD-ism, it would naturally cause some people to think that OpenBSD supports the requirements as stated in the PP, which it does not.

      The requirement, as written, asks for out-of-the-box, mandatory enforcement except in certain, enumerated cases. The enumerated cases are basically only allowed to be JIT compilers. To that end, lack of mandatory enforcement in userland wouldn’t meet the protection profile requirement. Getting a Common Criteria certification is likely no more a goal of the OpenBSD project than getting a FIPS certification for LibreSSL is.

      In the Application software protection profile there is a requirement that the application itself cannot use W|X pages of memory except for JIT compilers. Using pledge(2) in this context can help ensure that this is the case, as when one pledges stdio, as you noted, mmap() and mprotect() are not allowed (by default) to map executable pages. However, as the name of the system call implies, this is really just the developer promising that from time of call on down, they promise they’re not going to try and use any additional permissions or system calls than what they claim, with the OS acting as a traffic cop. Application developers absolutely should take into consideration what they think their program is doing, what it needs to accomplish that goal, and make use of mechanisms which help ensure that they’re kept honest.

      In short, pledge(2) is great, and I intend to be spending more time in OpenBSD in the future, and will be leveraging pledge(2) myself. But, since it needs to be added into every program, including third-party programs, it wouldn’t actually help OpenBSD meet the requirement as written in the protection profile, were a) the requirement moved from an objective to mandatory requirement, and b) OpenBSD chose to close any other gaps they might have and pursue a CC evaluation.

      • “the protection profile requires mandatory enforcement with the ability to opt-out on a per-application basis in order to meet needs such as those of JIT compilers”

        Only one major JIT still requires such a opt-out: Chrome/V8.

        All the other JITs (of consequence) were replaced or repaired over the last 15 years.

        So mandatory W^X is within our reach. One company just needs to settle the matter; V8 is open source, so they could demonstrate how they want mandatory W^X to be achieved in the codebase, then the community can help them re-optimize for performance afterwards.

        If that happened, a document requiring mandatory W^X could be half the length. It would just mandage the security principe of mandating W^X, period. No opt-out would be permitted.

        Turning it around: The current document requires the ability (for 1 application) to opt-out of a mandatory security-mechanism.

        As it stands, this document is not INSISTING on a better future, by allowing continued bad practice. Such an opt-out mechanism wil be abused, and may result in more W^X JITs in the future, rather than zero.

        OpenBSD does mandatory W^X today, it is just that the system uses the opt-out for all applications! Major vendors will say the same. This document lacks teeth.

  2. Darude says:

    Thanks for your enlightening follow-up.

  3. Hi!
    Can you publish the test code somewhere on the net (github, bitbucket, …)?

    • W. Dean Freemean, CISSP GCIH says:

      Hi David,

      I can’t publish my test code right now (though I may be able to in the future), however Red Hat came up with a more robust and portable version after some discussions I had with them around this requirement, which can be found here: https://pagure.io/execmod-tests

      There really isn’t any magic in what I was doing, which was essentially driven by an attempt to prove support in HardenedBSD. Basically the three tests involve attempts to mmap() an anonymous, shared page of memory. The first time, you map it PROT_WRITE|PROT_EXEC. The second time, just PROT_WRITE and then try to make it executable with mprotect(). The third time, you make it PROT_EXEC and then try to make it writable with mprotect(). The major “magic” is in the fact that in a pax implementation such as HardenedBSD, an mmap() with W|X permission returns a W page and not an error. So, to programmatically prove correct behavior, some small bit of shell code or assembly (int3 op code will work fine, but I demonstrated lack of mprotect() protection to Red Hat with some 64-bit execve shell code popping up a Bash shell). You then fork off a child process and attempt to execute via function pointer the code in the mapped page. Doing so will, on BSD systems and hardened Linux systems, result in a segfault of the child process and the value will be returned up to the parent and can be checked there. On Mac, you get a different error code (not a seg fault).

  4. mprotect is now on by default in NetBSD (amd64, i386).
    http://mail-index.netbsd.org/source-changes/2016/05/20/msg074797.html

  5. We would love to do mandatory W^X in OpenBSD.

    Some backstory first. I coined the name “W^X”. W^X was documented since day 1 (around 2001) as “MECHANISM” supplied by the kernel, and a “POLICY” for applications to follow. Because there were hordes of violating programs at the time, mandatory W^X could only be a hope, a dream, a target.

    Some roadmaps are very long. Some technology adoptions take longer than 10 years. As an example, the stack protector landed in Android/iPhone 5 years after full integration in OpenBSD. That was an extremely fast. Most mitigation adoptions take much, much, much longer. (This year glibc may finally gain stack protection for itself. 15 years.)

    The word “mandatory” for me means 100% enforcement, no bypass, no matter what.

    For a mitigation to become mandatory, all applications must play along.

    I have been reminding and helping people towards the goal of mandatory W^X for more than 15 years. The writeup seems overly critical towards the group of people who have most substantially pressured the application ecosystem for improvement in this area. Application authors must convert their software to follow the mandatory rules, otherwise we cannot make the rules mandatory.

    OpenBSD is 100% ready to go mandatory W^X at any point, but a few applications still violate the principle. We’ve been moving towards this for about 15 years, poking & prodding for the applications to get caught up.

    Now, your article says some other systems have made W^X mandatory. How then do they run those common applications which violate the mandatory? How do they run Chrome or other V8 applications.

    Well, they have a filesystem-supported flag which allows marking of programs which are permitted to create W^X mapping violations. You even demonstrate use.

    So the enforcement is mandatory… except when it isn’t. Really, come on.

    In OpenBSD we don’t want to have a per-system or per-program flags to enable/disable behaviours. Instead we want the last few programs to transition, then we will go 100% mandatory, and from that day forward never allow any W|X mappings at all, in any circumstance! If you support the idea that W^X should be mandatory, you should also rail against the presence of a per-program knob introduced specifically to permit W^X violations.

    Surely you have a sense of history and recognize once the final applications are fixed, the operating systems with knobs probably won’t be removing the knobs.

    OpenBSD also lacks knobs to disable the stack protector, ASLR, or a host of other mitigations. When we incorporate mitigations, we always try to avoid on/off knobs. This is strategic. Once we know the entire software ecosystem can adapt to a new design rule, we want to entirely block use of the old ways. A mixture of new ways & old ways is a more complicated programming environment, and complication stands directly opposed to security quite often.

    Over 20 years OpenBSD ports developers working in such an environment have slowly applied pressure to upstream software projects to clean their code and make them ready for these types of changes. We are a small group, but we are very persistant, and you have all benefited.

    In my experience, the best way to get a security practice 100% adopted is to make it mandatory and non-optional inside (at least one) software ecosystem.

    Much of the problem comes from JIT engines. Around the same time W^X was introduced, JIT was becoming a popular design pattern. At the time, using mprotect() to toggle W and X was very expensive on some platforms, especially on SMP systems. However around 2010 those hurdles (or perceptions?) seem to have gone away and we start seeing new JIT engines carefully coded to avoid creating W|X mappings, for instance luajit. A couple older JIT engines were redesigned to avoid W|X.

    Unfortunately some older JITs designs have persisted. Ones with considerable user base. Two years ago, the main programs standing in the way of mandatory W^X were Firefox and the V8/Chrome ecosystem.

    Firefox no longer creates W|X mappings.

    So we are essentially down to 1 major program: Chrome/V8 has a JIT which requires W|X mappings. We’ve been poking and prodding, but haven’t heard any promising news yet.

    Additionally, there is another worrisome detail. A few JIT are using shadow page methods, for instance firefox which maintains a W mapping to an object, and a seperate X mapping for the same object. At very least this is a violation of the spirit of W^X (and mandatory W^X). It is quite likely someone will demonstrate an exploit bypassing this restriction eventually, writing to the W page and execution in the X page. So was Firefox really fixed?

    In summary, currently there are two ways to have mandatory W^X:

    1) As an extension to POSIX, mandate a filesystem-backed per-file flag which permits creation of W|X mappings, directly violating the mandatory W^X principle.
    2) Tell users they cannot use chrome.

    In your article it seems you have selected option 1.

    I think there is still hope for a 3rd option 3. But we are not quite there yet.

    • W. Dean Freemean, CISSP GCIH says:

      Hi Theo,

      First of all, thank you for taking the time to respond to this post. I do have to say, that I’m a big fan of yours and appreciate all the work that you have put in over the years. I know I have personally benefited from OpenBSD and related projects, as has nearly all of the internet-connected world, whether they realize it or not.

      I apologize if I came off as overly critical to OpenBSD — I assure you that was not my intent. I was merely attempting to provide some illustration around how testing this requirement might be performed and give examples of some operating systems which pass (since some of the vendors who participate in the technical community which has been established to help enhance and maintain the protection profile in question have insisted that they are unaware of anything which would pass). I did not author the requirement, this post was a side effect of some work I was doing in order to ensure that if I were to receive a client who wished to validate an operating system against this protection profile AND claim support for this requirement that I would have tools and techniques in place to be able to test it.

      I and many others understand and share your concerns regarding the validity of requiring a “mandatory” requirement which then requires significant opportunity to opt out. In your recent talk on pledge(2) I referenced in another comment response, you called out PaX for essentially forcing users into a situation where they enable a security control which might break a number of applications on which they rely, but which are not necessarily coded correctly with regards to how they handle memory protection, causing them to have a number of caveats and exemptions. I agree with you that application authors need to be held accountable for the correctness of their software rather than have operating system vendors provide opportunities to turn off security mechanisms leaving large swaths of the attack surface exposed but with additional hassle.

      You may or may not be aware, but in the Application Software Protection Profile, also authored by NIAP, there are requirements for the evaluator to ensure that an application does not make any W|X mappings. If an application wants to be Common Criteria certified, then it must abide by W^X and this is a firm requirement. Right now, from the OS side, it is an objective requirement, meaning they (NIAP, which is under the NSA), would like to see it turned on in the future in any operating system that wishes to become CC certified, however right now it is optional. The ability to add a selection for exemptions exists largely for JIT (in fact, in the Application Software PP, the ONLY acceptable exemption is JIT), however in the context of an evaluation, that would basically just be a list in documentation. The testing activities would require that I create and run a program like the one I used in the article and show that for my j. random program, by default I’m not allowed to have W|X mappings.

      So, yes, I support the idea of true, mandatory W^X that cannot be disabled. I believe that application authors should understand the issue and be pressured either through users, or customer procurement requirements (which for the U.S. and allied governments, Common Criteria is one), be pressured into implementing software correctly so that mandatory mechanisms can be truly mandatory and don’t need caveats and exemptions. I believe that there should be more stringent requirements in the Application Software PP to push towards enforcing privilege separation where it can be appropriately leveraged (right now, that is not the case), too. There are many places where there is room for enhancement in the requirements, starting from a position that security is a subset of correctness and that anything worth doing should be done right.

      However, right now, this is the requirement as written, and if an operating system vendor wants to claim support, they essentially have to do what HardenedBSD or some other PaX-enabled system does, which is apply the protection out of the gate and then allow the administrator to craft a policy which excludes anything that needs W|X through policy. Obviously, there is a lot of room for improvement.

  6. “The ability to add a selection for exemptions exists largely for JIT (in fact, in the Application Software PP, the ONLY acceptable exemption is JIT)”

    The only programs which actually do W|X are JITs with a 10+ year history. They are also the ones with the greatest risk and exposure. That is chrome/V8 for sure; also firefox and some java stuff if shadow-mappings are considered an equally risky practice.

    Mandating that cat, sed, emacs, perl, nginx, etc may not do W|X mappings is weak sauce.

    When chrome will be immediately tagged to permit such mappings. Chrome won’t be fixed. Future JIT engines will be coded specifically utilizing this exemption. It is too easy.

Speak Your Mind

*