5

As we know that on most of the processor architectures, the stack grows downwards. Hence, memory exploits involving smashing of stack and buffer overflow and their explanation make sense.

Just wondering whether it is possible to implement such attacks involving stack smashing on systems like HP's PA-RISC, where the stack grows upwards? If yes, how would it work? If no, why won't it work?

10

Buffer overflows occur because of a programming bug: due to circumstances unforeseen by the developer, but triggered by the attacker, the code is writing data beyond the end of the buffer. What end ? Well, there are two: toward high addresses, and toward low addresses.

The traditional way to write a loop looks like this:

for (i = 0; i < n; i ++)

and not like this:

for (i = n - 1; i >= 0; i --)

which means that developers tend to apply algorithms in the low-to-high order. There is no mathematical reason for that, but developers are trained to think like that; and hardware's evolution has followed (pre-fetching strategies in RAM chips rely on this pattern, too). Usual suspects for string manipulation (strcat(), sprintf()...) also overflow on the high end for the same reason: they operate low-to-high.

This implies that most buffer overflows also occur on the high end of the buffer, not on the low end. On a stack which grows downwards, this allows these overflows to target the return address of the current function: the best a buffer overflow exploit can hope for is redirecting execution into attacker-chosen code, and for that the overflow shall overwrite a memory slot which contains a pointer to code, which the code will follow at some ulterior point. The "return address" slot on the stack is convenient for that. But that's not the only pointer to code.

In particular, when using C++, any object with virtual methods must contain some sort of reference to a structure containing pointer to these methods (the vtable). This gives a lot of extra targets which may be in range of a buffer overflow. C++ objects can be stack allocated.

Another point is that when a buffer is overflown, it could be located in another stack frame. For instance, this:

void foo(char *s)
{
    char buf[10];
    bar(buf, "x", s);
}

void bar(char *dst, char *s1, char *s2)
{
    sprintf(dst, "%s/%s", s1, s2);
}

In this example, if the attacker can put an oversized string as parameter to foo(), the buffer allocated on the stack frame for foo() will be overflowed in bar(). If the stack grows upwards, the return address for foo() will be undamaged, but the one for bar() will be overwritten.

To sum up:

  • An upward-growing stack means that the function return address of the function which allocated the buffer will usually be unharmed.
  • But other function return address slots for other functions may be in range (and actually be more in range than if the stack had grown downwards).
  • And though they are rarer, buffer overflows on the low end (sometimes called "underflows") also occur in practice.
  • And the return address slot is just one of the juicy targets for an attacker.

So we cannot really think of upwards-growing stacks as a protection against exploitation of buffer overflows. It changes the attack conditions, but not necessarily toward safer grounds. Experience shows that buffer overflows have been exploited with success on architectures with upwards-growing stacks.

(Note: the PA-RISC CPU, by itself, knows of no stack. The stack is part of the calling convention, which says that GR 30 is to be conventionally used as stack pointer, and that the stack grows upwards. This is how HP defined it but any operating system could decide otherwise. It is a generic trend for most RISC CPU: the hardware knows of no stack, so the position and direction of the stack are a matter of convention between OS and applications.)

  • One of the most widely exploited vulnerability commonly known as the MS08-067 NetAPI vulnerability was actually a buffer underflow. – void_in Nov 2 '13 at 13:25
  • Awesome explanation @Thomas. So, what I think after reading your explanation is that it is possible only if there's a Pass by Reference in the child function. Am I right? Or there can be some other way also? – Rahil Arora Nov 2 '13 at 19:28
3

Yes, buffer overflows exist on RISC architectures such as MIPS and PA-RISC. Phrack magazine published an article on PA-RISC buffer overflows around 12 years ago in article #58, but you probably would have already known that if you knew how to query Google.

Admittedly I'm not expert on anything about PA-RISC, but I have some basic understanding of it. Therefore rather than me paraphrasing the phrack article, I suggest you go read it for yourself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.