PowerPC Stack Attacks, Part 1 - May 31, 2000
Christopher A Shepherd <cshepher@linux-florida.com>

Introduction
This assumes the reader's familiarity with buffer-overflow attacks on the Intel architecture, and introduces the reader to the possibility of doing the same on the PowerPC. If you're not familiar with this, have a look at Phrack 49. Essentially, the return address is saved at the top of the stack frame, and data written below the return address can overwrite the return address, allowing us to execute evil code.

PowerPC Errata
Buffer overflow attacks have been easy to write for the Intel architecture, in part because function calls are implemented with the 'call' opcode, which saves the calling address on the stack, to be retrieved later when the function exits. But as we see here, this may not actually be the case on the PPC.

A typical ppc function call is executed with the 'blr' instruction, which saves the caller's return address to a special-purpose register called the 'link register.' If it were left at that, stack attacks would not be possible because we don't get a chance to alter the link register simply by overwriting the stack.

Fortunately for the attacker, the link register must be saved anytime a function calls another function. Let's take a look at a sample program as compiled by gcc on LinuxPPC:

Figure 1: C Source Code
-----------------------
void function(int a, int b, int c) {
        char buffer1[5];
        char buffer2[10];
        int *ret;

        ret = buffer1 + 284;
        (*ret) += 8;
        printf("hi.\n");
}

void main() {
        int x;

        x = 0;
        function(1,2,3);
        x = 1;
        printf("%d\n",x);
}

And gcc -S yields...
Figure 2: PPC Assembly of Same
------------------------------
        .file   "test2.c"
gcc2_compiled.:
        .section        ".rodata"
        .align 2
.LC0:
        .string "hi.\n"
        .section        ".text"
        .align 2
        .globl function
        .type    function,@function
function:
        stwu 1,-304(1)
        mflr 0
        stw 31,300(1)
        stw 0,308(1)
        mr 31,1
        stw 3,8(31)
        stw 4,12(31)
        stw 5,16(31)
        addi 9,31,24
        addi 0,9,284
        stw 0,280(31)
        lwz 9,280(31)
        lwz 11,280(31)
        lwz 10,0(11)
        addi 0,10,8
        stw 0,0(9)
        lis 9,.LC0@ha
        la 3,.LC0@l(9)
        crxor 6,6,6
        bl printf
.L2:
        lwz 11,0(1)
        lwz 0,4(11)
        mtlr 0
        lwz 31,-4(11)
        mr 1,11
        blr
.Lfe1:
        .size    function,.Lfe1-function
        .section        ".rodata"
        .align 2
.LC1:
        .string "%d\n"
        .section        ".text"
        .align 2
        .globl main
        .type    main,@function
main:
        stwu 1,-32(1)
        mflr 0
        stw 31,28(1)
        stw 0,36(1)
        mr 31,1
        li 0,0
        stw 0,8(31)
        li 3,1
        li 4,2
        li 5,3
        bl function
        li 0,1
        stw 0,8(31)
        lis 9,.LC1@ha
        la 3,.LC1@l(9)
        lwz 4,8(31)
        crxor 6,6,6
        bl printf
.L3:
        lwz 11,0(1)
        lwz 0,4(11)
        mtlr 0
        lwz 31,-4(11)
        mr 1,11
        blr
.Lfe2:
        .size    main,.Lfe2-main
        .ident  "GCC: (GNU) 2.95.1 19990809 (prerelease)"


Don't worry about most of that code. For now, let's look at where main() calls our 'function' function. This is done with:
	li	3,1
	li	4,2
	li	5,3
	bl	function

The SysV PowerPC ABI, used on LinuxPPC, stipulates that arguments be passed via registers (we do have lots of em on the PPC). Since r1 is the stack pointer and r2 is the RTOC, we start with r3. So you see, this simulates the C code "function(1,2,3);"

Now, when function() gets called, it goes through a prologue routine, same as intel does. In this case, our stack is set up by:
	stwu	1,-304(1)	; sp = sp - 304
	mflr	0		; move lr to r0 - watch this!
	stw	31,300(1)	; save old frame pointer
	stw	0,308(1)	; save old lr
	mr	31,1		; fp = sp

At this point, we've allocated a stack frame 304 bytes long, below the old SP, and set our stack and frame pointers to point to it. Presently at the bottom of the frame, there's nothing, but at the top (300) is the 4-byte old frame pointer. But notice above _that_, above the frame we allocated, we stack the old link register. Notice that by doing it this way, we're actually storing the old link register at the _bottom_ of our caller's stack. This was probably done to be more like the other UNIXes. But it isn't always done! If we don't call another function from inside our function, LR isn't preserved like this and conventional stack-smashing attacks can't happen.

Now, what happens here is most likely predictable. We can find buffer1's offset from the top of the frame pointer by noticing that the assembly references it as:

	addi	9,31,24		; r9 = frame pointer plus 24

So buffer sits 24 bytes from the bottom of the frame pointer, or 304 - 24 = 280 bytes from the top. But remember our preserved LR is going to sit 4 bytes ahead of that in the caller's frame, so that makes it 284. That is why in the 'C' code, we're doing:
	ret = buffer1 + 284;

Now, like Aleph1 told us in Phrack, *ret is pointing to our return address. Just bump it up by two instructions, and we can bypass the "x=1" statement in main() when we return. PowerPC instructions are always 4 bytes, so that means an 8-byte increment. For the interested, objdump --disassemble on the test2 compiled object yields:
10000490:       4b ff ff 71     bl      10000400 <function>
10000494:       38 00 00 01     li      r0,1
10000498:       90 1f 00 08     stw     r0,8(r31)
1000049c:       3d 20 10 00     lis     r9,4096

See where we called the function, the link register would be set to point to the next instruction (0x10000494), but the "li r0,1 / stw r0,8(r31)" is what does the "x=1" statement, and those two statements are precisely the 8 bytes we're jumping over.

So when you compile and run this, it goes something like:
[cshepher@hal9000 cshepher]$ ./test2
hi.
0

We've just done a proof-of-concept now that tells us that the stack can be smashed and we can have our evil way with it. The rest may be left to your imagination, but just in case it isn't, part 2 follows later this week. By the end of the week, we'll be smashing stacks on LinuxPPC and Mac OS X. Woohoo!

-Chris