Bugtraq mailing list archives

Re: machine independent protection from stack-smashing attack


From: John Viega <viega () LIST ORG>
Date: Tue, 15 Aug 2000 08:26:56 -0700

On Tue, Aug 15, 2000 at 01:55:47PM +0900, Hiroaki Etoh wrote:


On 2000/08/11 01:05:57 John Viega wrote:

In particular, you fall prone to the problem that Crispan recently had
to fix with StackGuard; the canary is not bound to the actual return
address, so in some cases it is possible to "jump" the canary.  You
are using boundary canarys instead of xor canarys.

Sure, you're lining up arrays next to each other, but that doesn't
solve every case.  Consider the following example, slightly modified
from the StackGuard page:

foo(char * arg) {
   char *  p[1];    // a vulnerable pointer
   char a[25];    // the buffer that makes the pointer vulnerable
   p[0] = arg;
   gets(a);    // using gets() makes you vulnerable
   gets(p[0]);    // this is the good part
}

ProPolice generates the following activation record for the above program.
| arg | return address | previous frame pointer | GUARD | array a | array *p |

It means that you can not change the value of char *p[0] after the buffer
overflow of the array a. This protection is derived from the analysis of the
vulnerability of XOR random canary protection.  I will post the vulnerability
memo after Crispan's permission.

Okay.  So you're placing all character arrays at the beginning of a
function, and then the rest of the arrays next.  That's definitely a
lot better than what we thought you were doing.  However, it just
isn't as complete a solution as StackGuard (w/ XOR canaries) is.  What
if I have a multidimensional array of characters that I treat as an
array of fixed-size strings?  Where do you put that in your ordering?
Then there are all the cases where the user takes the address of a
non-pointer variable and uses it as a pointer.  Then there is the
problem of strings in stack allocated structs.

Ultimately, the protection model you have put forward is definitely a
lot better than nothing.  I will agree that the cases that allow for
attacks probably aren't all that common.  But as a programmer, I would
prefer to not have to wory about return address modification at all.
ProPolice doesn't afford me that.  I am willing to believe at this
point that StackGuard probably does, though.


As for the portability of ProPolice, ProPolice uses a termination canary if it
fails to open the device "/dev/random". I think the terminator canary protection
introduced by Crispin Cowan is as safe as the random canary protection IF the
previous frame pointer of the activation record is protected.  Speaking the
implementation of ProPolice, When ProPolice fails to open the device
"/dev/random", it uses a series of characters "00", "00", "0a" and "ff" as a
terminator canary.  I think this is portable.

Of course, if you're memcpy'ing out of argv, off the network or out of
an environment variable, then a terminator canary won't work.  There
are other situations where it won't work.  Again, you're only stopping
the most common problems of this type, and not all problems, which is
why I'd much rather use StackGuard.

In short, your attempts to gain better portability when compared to
StackGuard leave you with a technology where a lot can go wrong.
Yes, it probably won't be possible for things to go wrong in most
cases, but when it is possible, the rarity of the occurance doesn't
matter.

I don't think portability was ever much of an issue for StackGuard (as
long as you stick to GCC).  The only thing StackGuard has to do that
isn't strictly portable is add a small bit of asm to the function
prologue and epilogue.

That code is incredibly easy to port, though!  If a reasonably
talented person (e.g., someone who has done a reasonable amount of
compiler work, such as a GCC maintainer) had a good asm reference for
any architecture, it would be incredibly easy to write the code for
that architecture.  Heck, in the average case, you could probably even
get away without testing your changes.

Of course, canaries requiring randomness need a reasonable entropy
source.  And if you don't use an XOR canary w/ StackGuard, then
ProPolice protection would definitely be better.  Thankfully,
something like TrueRand is pretty portable.

John


Current thread: