0% found this document useful (0 votes)
13 views

Module-5-Control-Hijacking-Attacks

The document discusses control hijacking attacks, focusing on memory attacks like buffer overflows and integer overflows, and explains the Linux memory layout on i386 32-bit systems. It covers how stack management and function calls work, detailing the exploitation process and providing examples of well-known memory attacks such as the Morris Worm and Glibc 'GHOST'. The document also outlines the implications of buffer overflow vulnerabilities in C programming and how attackers can manipulate memory to execute arbitrary code.

Uploaded by

akun test
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

Module-5-Control-Hijacking-Attacks

The document discusses control hijacking attacks, focusing on memory attacks like buffer overflows and integer overflows, and explains the Linux memory layout on i386 32-bit systems. It covers how stack management and function calls work, detailing the exploitation process and providing examples of well-known memory attacks such as the Morris Worm and Glibc 'GHOST'. The document also outlines the implications of buffer overflow vulnerabilities in C programming and how attackers can manipulate memory to execute arbitrary code.

Uploaded by

akun test
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 61

Control

Hijacking
Attacks
This Week’s Objectives
• Types of control hijacking attacks (mostly memory attacks)
• Understand Linux memory layout on i386 32-bit
• How stack is used to manage function calls
• How buffer overflow and shellcode works
• Next week: Countermeasures

2
Exploitation Stage - Gaining Access

Pre-Engagement Recon Scanning Exploitation

Exploit vulnerabilities
• Exploits for a specific vulnerability - can be downloaded from
proof of concepts and exploitation tools
• Skilled hackers develop new exploits
o Control hijacking exploits such as buffer overflow
Control hijacking attacks
• Take over a target machine (e.g. web server)
by altering the control flow of a legitimate
process
o Execute arbitrary code on target by hijacking
application control flow
• Examples – memory attacks:
o Buffer overflow and integer overflow attacks
o Format string vulnerabilities
o Use after free
First Example: Integer Over/underflow
#include <stdio.h>

int main() {

char command;
Example
unsigned char c = 5; //value range 0-255

while (1) {
printf("The current value is %d\n", c);

printf("Up(u) or down(d)?\n");

command = getchar();

if (command == 'u')

c = c + 1;
else if (command == 'd')

c = c - 1;

return 0;

5
Well-Known Memory Attacks
6
Morris Worm (1988)
• First internet worm
• Exploited BoF vulnerability in the finger daemon
• Overflowed 512-byte buffer to open a reverse shell
• “Smashing the Stack for Fun and Profit” by Aleph One

http://www.cs.umd.edu/class/fall2017/cmsc414/readings/stack-smashing.pdf

https://github.com/arialdomartini/morris-worm
7
Glibc “GHOST” (2015)
CVE-2015-0235

• HEAP based buffer overflow


• GNU C library (glibc)
• Attacks gethostbyname()
• At most sizeof(char*), 8 bytes on 64bit and 4 bytes on 32bit OS, can be
overwritten with digits and dot (“.”)
• 10 out of 10 on the Common Vulnerability Scoring System (CVSS)

https://www.qualys.com/2015/01/27/cve-2015-0235/GHOST-CVE-2015-
0235.txt

8
Computer Architecture
(x86 – 32 bit)
9
Von Neumann Architecture

Stored Program Computer


10
Code to Execution
Native Compiled Bytecode Interpreted
Languages Languages Languages
• C/C++ • .Net
• Java • Python
• Ruby
Compiler

Compiler CIL/Bytecode Interpreter

Machine Code

At the Machine Code


Level, it’s all the same to
the CPU
11
Running a C program
#include<stdio.h>
int main () {
int year = 2024;
printf("hello csf %d\n", year);
Machine code
}
return 0;
raw bit Assembly code

year= 2024

12
From C Code to Memory

● Compiler: Converts C code into assembly


address 0xFFFFFFFF
code
● Assembler: Converts assembly code into
Higher addresses
machine code (raw bits)
● Linker: Deals with dependencies and
libraries
● Loader: Sets up memory space and runs
the machine code
Lower addresses

○ Memory is just a long array of bytes


address 0x00000000

4 bytes

13
Process Memory Layout
Instantiated global/static
Higher addresses
int foo = 1;
int x; Uninstantiated global
main() STACK
{
static char[] bar = "abc";
Local Variables long y; Grows downwards
} Grows upwards
foo(char[] bar, long y)
{
char buffer[500]; HEAP
char *ptr;
Dynamic
ptr=malloc(sizeof(char)*10); BSS
allocation
.. DATA
free(ptr);
} CODE (PROGRAM)
Executable Instructions Lower addresses

14
x86 (32bit)Architecture

Special registers:
EIP (Extended Instruction Pointer) = points to the current instruction
ESP (Extended Stack Pointer) = points to the “bottom” of stack
EBP (Extended Base Pointer) = points 4 bytes below the return pointer, used
for referencing address of the previous frame
15
x86 Calling Convention
16
Calling a Function in x86
● When calling a function, a new stack frame is created to store local variables
o the ESP and EBP need to shift to create a new stack frame,
o and the EIP must move to the callee’s code
● When returning from a function, the ESP, EBP, and EIP must return to their old values

EBP EBP
ESP ESP
Stack

Stack

Stack
EBP
ESP

EIP caller code caller code EIP caller code


Code

Code

Code
callee code EIP callee code callee code

Before function During function After function


call call call

17
Stack and registers
● The stack starts at higher
addresses and grows down.
...
● In a 32-bit system, registers Registers

The stack grows this way


ebp
are 32-bit (or 4-byte, or 1-
esp
word) units of memory located

STACK
on CPU. eip

...

CODE
Code for foo

Code for main


18
ebp and esp

...
● Two pointers to mark the Registers
extent of the current stack ebp

frame. esp

STACK
● ebp is used for the top of eip
the stack frame, and esp is
used for the bottom of the
stack frame.
● whenever you push a value
onto the stack, esp must ...
adjust to match the lowest

CODE
Code for foo
value on the stack.
Code for main
19
eip

Registers ...
● We need some way to
ebp
keep track of what step
we’re at in the instructions. esp

STACK
● We use the eip register to eip
store a pointer to the
current instruction.

...

Code for foo

CODE
Code for main
20
Stack Changes During Function Calls

Registers Stack frame for main


● Every time a function is
ebp
called, a new stack frame
must be created. When the esp

STACK
function returns, the stack eip
frame must be discarded.
● Each stack frame needs to
have space for local
variables.
● We also need to figure out ...
how to pass arguments to
Code for foo

CODE
functions using the stack.
Code for main
21
Stack Changes During Function Calls

● This is what the stack might


Stack frame for main
look like after a function foo is Registers
called. ebp Stack frame for foo
● The ebp and esp registers esp

STACK
should adjust to give us a eip
stack frame for foo with the
correct size.
● The eip register should adjust
to let us execute the
instructions for foo.
...

Code for foo

CODE
Code for main
22
Stack Changes During Function Calls

● Then after foo returns, the Registers Stack frame for main
stack should look exactly ebp
like it did before foo was esp
called.

STACK
eip

...

Code for foo

CODE
Code for main
23
Save Register Values So We Can Go Back

Registers Stack frame for main


● If we ever change a register
value and need it later, we ebp
should remember its old value esp

STACK
by putting it on the stack. eip

...

Code for foo

CODE
Code for main
24
1. Arguments

Registers Stack frame for main


● First, push the arguments
ebp Argument #2
onto the stack.
● then adjust esp to point to esp Argument #1

STACK
the new lowest value on the eip
stack.

...

Code for foo

CODE
Code for main
2. Keep track of eip

Stack frame for main


● Next, push the current value Registers
ebp Argument #2
of eip on the stack.
○ This tells us what code to esp Argument #1

STACK
execute next after the eip Old eip (rip)
function returns
○ This value is sometimes
known as the rip (return
instruction pointer),
○ esp now points to the new
...
lowest value on the stack.
Code for foo

CODE
Code for main
26
3. Keep track of ebp

Registers Stack frame for main


● Next, push the current value
ebp Argument #2
of ebp on the stack.
○ This will let us restore the esp Argument #1

STACK
top of the previous stack eip Old eip (rip)
frame when we return
Old ebp (sfp)
○ This value is sometimes
known as the sfp (saved
frame pointer)
○ esp points to the new lowest
...
value on the stack.
Code for foo

CODE
Code for main
27
4. Adjust the stack frame

Stack frame for main


● To adjust the stack frame, we
need to update all three Argument #2
registers.
Registers Argument #1
● We can safely do this

STACK
because we’ve just saved ebp Old eip (rip)
the old values of ebp and esp Old ebp (sfp)
eip.
eip

dashed lines = esp, ebp ...

pointers before this step Code for foo

CODE
Code for main
28
4. Adjust the stack frame

Stack frame for main


● eip now points to the
instructions for foo. Argument #2

● eip previously pointed to Registers Argument #1

STACK
code in main. ebp Old eip (rip)
esp
Old ebp (sfp)
eip

dashed line = eip


...
pointer before this Code for foo

CODE
step Code for main
5. Execute the function

Stack frame for main


● Now the stack frame is ready to
do whatever the function Argument #2

instructions say to do. Registers Argument #1

STACK
● Any local variables can be ebp Old eip (rip)
moved onto the stack now.
esp Old ebp (sfp)
eip Local variable

Local variable

...

Code for foo

CODE
Code for main
6. Normal operation - Restore everything

Registers Stack frame for main


● After the function is finished, we
put all three registers back ebp Argument #2

where they were. esp Argument #1

STACK
● We use the addresses stored in eip Old eip (rip)
rip and sfp to restore eip and
Old ebp (sfp)
ebp to their old values.
Local variable

Local variable

...

Code for foo

CODE
Code for main
Buffer Overflow (BoF)
AND (REMOTE) CODE EXECUTION

32
Buffer Overflows
• A type of control hijacking attacks (involves malicious user input)
• Runs as the privilege of the exploited process
• Occurs mostly commonly in C and C++ programs
• Other languages (Rust, Java, Python, etc) have good memory
management/protection

(Coding in C/C++/Assembly is like driving a


manual transmission car – you have more
freedom and power, but more things can go
wrong)

A buffer is any allocated space in memory


where data (often user input) are stored.
• can be stored in stack or heap.
33
Common Weakness Enumeration (2023)
• Mitre CWE Top 25 Most Dangerous Software Weaknesses

34
Example Vulnerable Code
#include<stdio.h>

int main() {

char c = 'X';

char buff[3];

printf("Variable c holds: %c\n", c);

printf("Enter a 2-digit number:");

gets(buff); //reads input stream until a newline character


printf("Got %s\n", buff);

printf("Variable c holds: %c\n", c);

return 0;

What happens if you enter more than 2 digits??

35
Buffer Overflow Vulnerabilities in C

● C has no concept of array length; it just sees a sequence of bytes


● If you allow an attacker to start writing at a location and don’t define when
they must stop, they can overwrite other parts of memory!

char buff[3]; 0
buff[5] = ‘0';

This is technically valid


C code, because C
doesn’t check bounds!
36
Stack Smashing Vulnerabilities in C
main()
{
func1()
func1(); { Call Stack
return; func2()
func2();
} { return addr main
return;
} char buf[12];
gets(buf); return addr func1
return;
}
return addr func2

buf

37
What happens if you enter more
than 12 characters?
main()
{
func1(); func1()
return; {
}
func2(); func2()
return; { return addr main
} char buf[12]
gets(buf) return addr func1
special_func() return;
{ }
//special return addr func2
}

buf

Having control over the return address is


key to stack overflow attack
38
Jump to other function

main() 39
{
func1(); func1()
return; {
}
func2(); func2()
return; { return main
} char buf[12]
gets(buf) return func1
special_func() return;
{ }
} return func2

buf

39
Overwriting the RIP

Assume that the attacker wants


... ... ... ...
to execute instructions at gets starts writing here
address 0xdeadbeef. and can overwrite ... ... ... ...
... ... ... ...
anything above buff,
What value should the attacker ... ... ... ...

write in memory? Where should


including the RIP! ... ... ... ...
the value be written? ... ... ... ...
... ... ... ...

What should an attacker supply ... ... ... ...


as input to the gets function? RIP of vulnerable RIP
SFP of vulnerable SFP
buff
void vulnerable(void) { buff
char buff[20];

buff
buff
gets(buff); buff
} buff 40
Overwriting the RIP Note the NULL byte that terminates
the string, automatically added by
gets!

... ... ... ...


● Input: 'A' * 24 + '\xef\xbe\xad\xde'
... ... ... ...
○ 24 garbage bytes to overwrite all of buff ... ... ... ...
and the SFP of vulnerable ... ... ... ...
○ The address of the instructions we want to ... ... ... ...

execute ... ... ... ...


... ... ... ...
■ Remember: Addresses are little-endian!
'\x00' ... ... ...
● What if we want to execute specific malicious '\xef' '\xbe' '\xad' '\xde' RIP
instructions? - Shellcode 'A' 'A' 'A' 'A' SFP
'A' 'A' 'A' 'A'
void vulnerable(void) { 'A' 'A' 'A' 'A'
char buff[20];

buff
'A' 'A' 'A' 'A'
gets(buff); 'A' 'A' 'A' 'A'
} 'A' 'A' 'A' 'A' 41
What code to inject?
SHELLCODE

• The payload for stack overflow is often compact shell


• Could be local, bind, or reverse shell
• Creation of a small shellcode is an art

Shell-Storm library

42
Shellcode Example

43
Arbitrary code injection & execution

PAYLOAD Malicious code to


execute, injected
through user input

RETURN Overwritten return


address

Not easy to hit the start of malicious code EXACTLY


44
NOP SLED

PAYLOAD Malicious code to


execute
NOP NOP NOP NOP NOP
NOP NOP NOP NOP NOP
NOP NOP NOP NOP NOP
NOP NOP NOP NOP NOP
NOP NOP NOP NOP NOP
RETURN Overwritten return
address

Not easy to hit the start of malicious code EXACTLY


45
PSEUDO NOP SLED

PAYLOAD Malicious code to


execute

NOP HLT PWR INT NOT NOP


HLT PWR INT NOT JMP
NOP HLT PWR INT JMP NOT
NOP HLT PWR INT NOT
XOR DIV CMP MOV NOP

RETURN Overwritten return


address

NOP=\X90

Series of Op Codes that looks realistic but does nothing


46
Putting Together a Buffer Overflow Attack

1. Find a memory safety (e.g. buffer overflow) vulnerability


2. Write malicious shellcode at a known memory address
3. Overwrite the RIP with the address of the shellcode
○ Often, the shellcode can be written and the RIP can be overwritten in the
same function call (e.g. gets), like in the previous example
4. Return from the function
5. Begin executing malicious shellcode

47
Walking Through a Buffer Overflow

Input: ... ... ... ...


SHELLCODE + 'A' * 12 +
... ... ... ...
‘\xef\xbe\xad\xde'
... ... ... ...
vulnerable:
... ... ... ...
...
EIP call gets ... ... ... ...
void vulnerable(void) { addl 0x04, esp
char buff[20]; movl ebp, esp ...
gets(buff); popl ebp ...
} ret
RIP of vulnerable
int main(void) { main:
EBP SFP of vulnerable
vulnerable(); ...
return 0; call vulnerable buff
} ...
buff
buff
buff
buff

ESP ... 48
Walking Through a Buffer Overflow

Input: ... ... ... ...


SHELLCODE + 'A' * 12 +
... ... ... ...
'\xef\xbe\xad\xde'
... ... ... ...
vulnerable:
... ... ... ...
...
call gets ... ... ... ...
void vulnerable(void) { EIP addl 0x04, esp
char buff[20]; movl ebp, esp ...
gets(buff); popl ebp '\x00' ...
} ret
(RIP) 0xdeadbeef
int main(void) { main:
EBP (SFP) 'AAAA'
vulnerable(); ...
return 0; call vulnerable (buff) 'AAAA'
} ...
(buff) 'AAAA'
(buff) SHELLCODE
We overwrite the RIP (saved EIP) with the address of (buff) SHELLCODE
our shellcode 0xdeadbeef, so the RIP is now pointing
(buff) SHELLCODE
at our shellcode! Remember, this value will be restored
to EIP (the instruction pointer) later. ESP ... 49
Walking Through a Buffer Overflow

Input: ... ... ... ...


SHELLCODE + 'A' * 12 +
... ... ... ...
‘\xef\xbe\xad\xde'
... ... ... ...
vulnerable:
... ... ... ...
...
call gets ... ... ... ...
void vulnerable(void) { addl 0x04, esp
char buff[20]; movl ebp, esp ...
gets(buff); popl ebp '\x00' ...
} ESP
ret
(RIP) 0xdeadbeef
int main(void) { main: (SFP) 'AAAA'
vulnerable(); ...
return 0; call vulnerable (buff) 'AAAA'
} ...
(buff) 'AAAA'
(buff) SHELLCODE
Function epilogue: Restore the RIP into EIP. (buff) SHELLCODE
We overwrote RIP to the address of shellcode,
EIP (buff) SHELLCODE
so the EIP (instruction pointer) now points to
our shellcode! ... 50
Walking Through a Buffer Overflow

/bin/bash # _
Input: ... ... ... ...
SHELLCODE + 'A' * 12 +
... ... ... ...
‘\xef\xbe\xad\xde'
... ... ... ...
vulnerable: ... ... ... ...
...
call gets ... ... ... ...
vo id vulnerable(void) { addl $4, %esp
char name[20]; movl %ebp, %esp ...
gets(name); popl %ebp '\x00' ...
ESP
} ret
(RIP) 0xdeadbeef
int main(void) { main: (SFP) 'AAAA'
vulnerable(); ...
return 0; call vulnerable (name) 'AAAA'
} ...
(name) 'AAAA'
(name) SHELLCODE
(name) SHELLCODE

EIP (name) SHELLCODE


... 51
Format string attacks

52
Format String Attacks
• printf(“my name is %s”, “Haque“);
• printf(“1 + 1 = %d”, 5);
• sprintf(buffer, “today is %s of %s”, “3rd”, “March”);

What will this code do?


int main() {
printf(“Your lucky number is %d!”);
}
No arg supplied
int main() {
printf(“Your lucky numbers are %x and %x!”);
}

You still get results!

53
What is going on?
• printf / sprintf are variable-argument functions (can take arbitrary number of
arguments)
• They blindly trust that the number of arguments match the placeholders…

Normal case ??? case


printf(“Lucky nums are %x and %x, 13, 4) printf(“Lucky nums are %x and %x)
main’s frame main’s frame

???
???
arg3 = 13 arg1 = PTR to “Lucky nums
arg2 = 4 are %x and %x)”

arg1 = PTR to “Lucky nums printf frame return addr printf frame
are %x and %x)” prev frame ptr
return addr “Lucky nums are: %s of %s”
prev frame ptr
“Lucky nums are: %s of %s”

54
Vulnerable functions

Any function using a format string.

Printing:
printf, fprintf, sprintf, …
vprintf, vfprintf, vsprintf, …

Logging:
syslog, err, warn
Can’t be that bad if it’s just over-read right?

printf has a nifty feature: %n


The number of characters written so far is stored into the integer indicated
by the int * (or variant) pointer argument. No argument is converted.
printf can write to memory location as well!

Normal case ??? case


int main() { int main() {
int val; int val;
printf(“one two %n three\n", &val); printf(“one two %n three\n");
printf("val = %d\n", val); printf("val = %d\n", val);
return 0; return 0;
} }

-Write to arbitrary memory location


-Execute shell code
56
Integer overflow

57
Integer Overflows

Problem: what happens when int exceeds max value?

int m; (32 bits) short s; (16 bits) char c; (8 bits)


c = 0x80 + 0x80 = 128 + 128 ⇒ c=0
s = 0xff80 + 0x80 ⇒ s=0
m = 0xffffff80 + 0x80 ⇒ m=0

Think of ways this can be exploited!


Integer Over/Underflow

Dec. 2020: integer underflow in F5 Big IP


if (8190 − nlen <= vlen ) // length
check return -1;

In the original Civilization, Each leader in the game had an "aggression" rating,
and Gandhi - to best reflect his real-world persona - was given the lowest score
possible, a 1. When a player adopted democracy in Civilization, their
aggression would be automatically reduced by 2. If Gandhi went democratic his
aggression wouldn't go to -1, it looped back around to the ludicrously high figure
of 255, making him as aggressive as a civilization could possibly be.
59
Lecture 0x05 - Summary
• Different types of control hijacking attacks
• x86 architecture and control flow
• Buffer overflows,
• Interger overflows
• Format string attacks

60
Looking Ahead
1. Workshop 5: hands-on memory attacks
a) Real world control hijacking attacks
b) Assignment 3 dues at the beginning of week 7
c) Attend the drop-in session if you need extra help
2. Lecture 6 will cover more memory attacks and defence

61

You might also like