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); }
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)"
li 3,1 li 4,2 li 5,3 bl function
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
addi 9,31,24 ; r9 = frame pointer plus 24
ret = buffer1 + 284;
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
[cshepher@hal9000 cshepher]$ ./test2 hi. 0