OpenBSD Journal

Dev Blog: djm@'s malloc extra paranoia

Contributed by ray on from the protecting-the-heap dept.

Damien Miller (djm@) gives us a more detailed explanation behind his recent commits to malloc(3):

This was inspired by a talk ("Attacking the Vista Heap" from Ben Hawkes) that I heard at Ruxcon. Some of the attacks that Ben Hawkes described depended on deactivating security features in Microsoft's heap implementation ahead of overwriting heap data structures. Amusingly, most of these attacks work because Microsoft disables aborting on internal consistency check failure by default.

OpenBSD's heap implementation is already the best security-wise. Thanks to prior work by otto@ and tedu@ - it has random "canary" values (similar to the propolice stack cookies) to detect overwrites of critical structures, it deliberately introduces randomness in certain operations to make them non-deterministic and checks its internal consistency in various ways and will (by default) abort if there are any problems.

This work implements two new measures to harden the allocator code:

First, it moves all the runtime options such as guard pages, use-after-free protection, and (especially) abort-on-error into a structure that is made readonly via mprotect(2) after it has been initialised. Under normal operation, the malloc.c code will never modify these values once they have been set so any attempt to write to it is a bug or an attack and the mprotect will ensure that doing so will result in a SEGV.

The remainder of the patch implemets additional protection on the main internal metadata structure of the heap implementation: struct dir_info. This patch allocates this structure using mmap(2), which on OpenBSD returns a randomised address. This alone is a good measure, since the structure will no longer be at a predictable address relative to other objects. It also allocates a "guard page" above and below the structure that are set to PROT_NONE via mprotect(3). Any attempt to read or write to an address inside either guard page will result in a SEGV, so the structure will be quite difficult to overflow - an attacker has to guess the address quite precisely.

The diff as committed implemented a 3rd protection controlled by a new 'L' flag for MALLOC_OPTIONS and /etc/malloc.conf: mprotecting the metadata structure PROT_NONE when we leave the allocator code and remapping it read/write when we enter it. This would render it basically impervious to attack, since an attacker wouldn't be able to clobber no matter how accurately they guessed and wouldn't be able to read off copies of the master canary values that they would need to clobber dependant structures. This part of the patch is likely to be removed - it defended against attacks that are highly unlikely to succeed in real life (go ahead, prove me wrong!), was too slow (~20% worse) to turn on by default and, as the Windows Vista example neatly illustrates, security options that are off by default aren't really much use to our users.

This was written before the 'L' flag was removed, as some readers will notice. Thanks for the info, Damien!

(Comments are closed)


Comments
  1. By tedu (udet) tedu@openbsd.org on

    Ben had another talk about 2 years ago at ruxcon about attacking allocators that was quite good. We had to make some fixes then too, such as the non-deterministic free code.

  2. By Anonymous Coward (128.171.90.200) on

    That was really quite interesting, thanks.

  3. By Anonymous Coward (219.90.166.242) on

    Couldn't an application mprotect() the structure read/write again? Even so, I'm sure it would be hard for an attacker to do.

    Comments
    1. By Otto Moerbeek (otto) on http://www.drijf.net

      > Couldn't an application mprotect() the structure read/write again? Even so, I'm sure it would be hard for an attacker to do.

      You are right, an application would be able to revert the mprotect. For an attacker it would be quite difficult.

      The problem with a "locking mprotect" is that it would deviate from POSIX, which make it less desirable.

      Comments
      1. By Anonymous Coward (219.90.166.242) on

        So adding such a function in addition to the regular mprotect (e.g. mprotect_immutable) is not allowed either? Would changes to mprotect to honour mprotect_immutable's immutability deviate from POSIX? Or does the requirement of adding an extra syscall make it not worth the effort?

        Comments
        1. By Damien Miller (djm) on http://www.mindrot.org/~djm/

          > So adding such a function in addition to the regular mprotect
          > (e.g. mprotect_immutable) is not allowed either? Would changes
          > to mprotect to honour mprotect_immutable's immutability deviate from
          > POSIX? Or does the requirement of adding an extra syscall make it
          > not worth the effort?

          I guess it could be made a flag to mprotect() without too many compatibility woes, but I don't think there is much value in it. It would be fairly unlikely for an application to be subvertible in a way that allows mprotect() of arbitrary regions before the attacker had gained arbitrary code execution. If the attacker can execute mprotect() directly, then they already have code execution and the new code in malloc can't save you...

          Comments
          1. By Alfred (200.126.158.167) ortegaalfredo@gmail.com on

            > > So adding such a function in addition to the regular mprotect
            > > (e.g. mprotect_immutable) is not allowed either? Would changes
            > > to mprotect to honour mprotect_immutable's immutability deviate from
            > > POSIX? Or does the requirement of adding an extra syscall make it
            > > not worth the effort?
            >
            > I guess it could be made a flag to mprotect() without too many compatibility woes, but I don't think there is much value in it. It would be fairly unlikely for an application to be subvertible in a way that allows mprotect() of arbitrary regions before the attacker had gained arbitrary code execution. If the attacker can execute mprotect() directly, then they already have code execution and the new code in malloc can't save you...
            >
            >

            You can subvert an application to execute mprotect, or for the matter, any other piece of code, using the so called "return based code-execution". It works, I have done multiple times. You will need a stack overflow for this, but sometimes you can combine two vulnerabilities or transform a heap-overflow into a stack overflow. All depends of the application and the data controlled by the attacker.

            Comments
            1. By Otto Moerbeek (otto) on http://www.drijf.net

              > You can subvert an application to execute mprotect, or for the matter, any other piece of code, using the so called "return based code-execution". It works, I have done multiple times. You will need a stack overflow for this, but sometimes you can combine two vulnerabilities or transform a heap-overflow into a stack overflow. All depends of the application and the data controlled by the attacker.

              The point is, is there a case where an attacker does NOT have general code injection possibilities, but can use mprotect(3) via some other mechanism to unprotect the malloc data structure protections.

              djm@ says that is quite unlikely to happen (and I agree with him). From these two things it follows that a "locking" mprotect(3) would not do much to improve malloc(3) data structure protection.

    2. By tedu (udet) on

      > Couldn't an application mprotect() the structure read/write again? Even so, I'm sure it would be hard for an attacker to do.

      If the attacker could do that, there are more interesting things they could already do. Like if a burglar is in your house, they could just turn the lock instead of picking it, but that's not usually considered a problem.

      Comments
      1. By locksmith (149.254.56.241) on

        > If the attacker could do that, there are more interesting things they could already do. Like if a burglar is in your house, they could just turn the lock instead of picking it, but that's not usually considered a problem.

        I'm not picking on your logic re malloc but you don't know that secure locks have existed for many years that actually do disallow this? When you lock it with the key outside it can't be unlocked from inside, which means burglars must exit the way they went in which can be inconvenient if they are trying to steal things. This also stops them breaking a small window and injecting a trained monkey to go open the main door, for instance..

        Comments
        1. By Anonymous Coward (98.127.110.254) on

          > > If the attacker could do that, there are more interesting things they could already do. Like if a burglar is in your house, they could just turn the lock instead of picking it, but that's not usually considered a problem.
          >
          > I'm not picking on your logic re malloc but you don't know that secure locks have existed for many years that actually do disallow this? When you lock it with the key outside it can't be unlocked from inside, which means burglars must exit the way they went in which can be inconvenient if they are trying to steal things. This also stops them breaking a small window and injecting a trained monkey to go open the main door, for instance..

          Holy Overtaxed Metaphor, Batman!

Credits

Copyright © - Daniel Hartmeier. All rights reserved. Articles and comments are copyright their respective authors, submission implies license to publish on this web site. Contents of the archive prior to as well as images and HTML templates were copied from the fabulous original deadly.org with Jose's and Jim's kind permission. This journal runs as CGI with httpd(8) on OpenBSD, the source code is BSD licensed. undeadly \Un*dead"ly\, a. Not subject to death; immortal. [Obs.]