Binary‐level program analysis: Stack Smashing Gang Tan CSE 597 Spring 2019 Penn State University 1
Program Stack • For implementing procedure calls and returns • Keep track of program execution and state by storing – local variables – Some arguments to the called procedure (callee) • Depending on the calling convention – return address of the calling procedure (caller) – ... 2
*Slide by Robert Seacord 3
Stack Frames • Stack grows from high mem to low mem • The stack pointer points to the top of the stack – RSP in Intel x86‐64 • The frame pointer points to the end of the current frame – also called the base pointer – RBP in Intel x86‐64 • The stack is modified during – function calls – function initialization – returning from a function 4
A Running Example void function(int a, int b) { char buffer[12]; gets(buffer); Run “gcc –S –o example.s example.c” to return; see its assembly code } • The exact assembly code will depend on many void main() { factors (the target architecture, optimization int x; levels, compiler options, etc); • We show the case for unoptimized x86‐64 x = 0; function(1,2); x = 1; printf("%d\n",x); } 5
Function Calls function (1,2) pass the 2 nd arg movl $2, %esi pass the 1 st arg movl $1, %edi call function push the ret addr onto the stack, and jumps to the function Note: in x86‐64, the first 6 args are passed via registers (rdi, rsi, rdx, rcx, r8, r9) 6
Function Calls: Stacks Before After rsp rsp ret stack frame stack frame for main for main rbp rbp 7
Function Initialization void function(int a, int b) { save the frame pointer pushq %rbp set the new frame pointer movq %rsp, %rbp allocate space for local subq $32, %rsp variables Procedure prologue 8
Function Initialization: Stacks Before After rsp buffer rbp rsp old rbp ret ret stack frame stack frame for main for main rbp 9
Function Return return; restores the old stack pointer movq %rbp, %rsp restores the old frame pointer popq %rbp gets the return address, and ret jumps to it 10
Function Return: Stacks Before After rsp buffer buffer rbp old rbp old rbp rsp ret ret stack frame stack frame for main for main rbp 11
A Running Example void function(int a, int b) { char buffer[12]; gets(buffer); return; rsp } buffer rbp void main() { int x; old rbp x = 0; ret function(1,2); stack frame x = 1; for main printf("%d\n",x); } 12
Overwriting the Return Address void function(int a, int b) { char buffer[12]; gets(buffer); rsp buffer long* ret = rbp (long *) ((long)buffer+?); old rbp ret *ret = *ret + ?; stack frame for main return; } 13
Overwriting the Return Address void function(int a, int b) { char buffer[12]; gets(buffer); long* ret = (long *) ((long)buffer+40); *ret = *ret + 7; The output will be 0 return; } void main() { int x; x = 0; the original return address function(1,2); x = 1; the new return address printf("%d\n",x); } 14
The Previous Attack • Not very realistic – Attackers are usually not allowed to modify code – Threat model: the only thing they can affect is the input – Can they still carry out similar attacks? • YES, because of possible buffer overflows 15
Buffer Overflow • A buffer overflow occurs when data is written outside of the boundaries of the memory allocated to a particular data structure • Happens when buffer boundaries are neglected and unchecked • Can be exploited to modify – return address on the stack – function pointer – local variable – heap data structures 16
Smashing the Stack • Occurs when a buffer overflow overwrites data in the program stack • Successful exploits can overwrite the return address on the stack – Allowing execution of arbitrary code on the targeted machine 17
Smashing the Stack: example.c What happens if we input a large string? ./example ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff Segmentation fault 18
What Happened? The Stack is Smashed void function(int a, int b) { char buffer[12]; gets(buffer); return; f buffer ⁞ } f old rbp ret f If the input is large, then gets(buffer) will write outside the stack frame bound of buffer, and the return for main address is overwritten 19
Figure Out A Nasty Input void function (int a, int b) { char buffer[12]; gets(buffer); return; } void main() { ret int x; stack frame x = 0; for main function(1,2); x = 1; Arc injection: a nasty input puts printf("%d\n",x); the return address after x=1. } 20
Code Injection void function (int a, int b) { char buffer[12]; gets(buffer); Injected return; code } void main() { ret int x; stack frame x = 0; for main function(1,2); x = 1; The injected code can do anything. printf("%d\n",x); E.g., download and install a worm } 21
Code Injection • Attacker creates a malicious argument—a specially crafted string that contains a pointer to malicious code provided by the attacker • When the function returns, control is transferred to the malicious code – Injected code runs with the permission of the vulnerable program when the function returns. – Programs running with root or other elevated privileges are normally targeted • Programs with the setuid bit on 22
Injecting Shell Code • This brings up a shell • Attacker can execute any command in the shell execve • The shell has the same (“/bin/sh”) privilege as the process • Usually a process with the root privilege is attacked ret stack frame for main 23
Recommend
More recommend