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

Core Security Introduction To Software Vulnerability Exploitation

The document provides an introduction to software vulnerability exploitation. It covers several topics related to computer basics, memory, microprocessors, and assembly language including: - How data is represented and addressed in memory. - The memory map of a Windows process. - CPU registers and basic assembly operations like mov, add, lea. - Assembly flags, branching operations like jz, jmp, and the difference between signed and unsigned comparisons. - Whether 32 bits is enough to represent all needed data types and operations.

Uploaded by

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

Core Security Introduction To Software Vulnerability Exploitation

The document provides an introduction to software vulnerability exploitation. It covers several topics related to computer basics, memory, microprocessors, and assembly language including: - How data is represented and addressed in memory. - The memory map of a Windows process. - CPU registers and basic assembly operations like mov, add, lea. - Assembly flags, branching operations like jz, jmp, and the difference between signed and unsigned comparisons. - Whether 32 bits is enough to represent all needed data types and operations.

Uploaded by

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

Introduction to Software Vulnerability

Exploitation (2017)
Ricardo Narvaja – Daniel Kazimirow

Based on gera’s dojo – ReCon 2010


Computer basics
The Memory: Relativistic meaning

0x434f5245 (int, big endian) 0x45524f43 (int, little endian)

1000011 1001111 1010010 1000101

43 4f 52 45

C O R E (or even E R O C)
The Memory: Addressing

byte 0 08 01 00 BD

byte 4 31 AB 11 10

.
.

byte 100 4A 21 65 89

byte 104 4A 21 65 89

byte 108 4A 21 65 89

byte 10c 4A 21 65 89
.
.

byte n 2D 3F 6A 2D

byte n+4 45 24 10 76

byte n+8 25 46 79 80
The Memory: Addressing

00000000

Physical
memory

Operating
Virtual System
Address
space processor

other
storage
(HD/swap)

FFFFFFFF
The Memory: Memory map of a windows process

.exe .exe .exe

Process #1 Process #2 Process #3


space space space
.dll .dll
.dll
.dll .dll .dll

Kernel space Kernel space Kernel space


Microprocessor: Registers – IA32 / AMD32

32 bits

EAX dword EAX


EBX word AX
ECX byte AH AL
EDX
EBX
ESI BX
EDI BH BL
EBP ECX
ESP CX
CH CL
EIP
EDX
EFLAGS
DX
DH DL
Microprocessor: Assembly Simple Operations

32 bits

EAX mov ecx, 14


EBX
ECX mov ecx, eax
EDX
mov eax, [eax]
ESI
EDI
mov [eax], edx
EBP
ESP mov ebx, [eax+100]
EIP
mov [eax+ebx*8+100], edx
EFLAGS
Microprocessor: Assembly Simple Operations

32 bits

EAX add ecx, 14


EBX
ECX add ecx, eax
EDX
add eax, [eax]
ESI
EDI
add [eax], edx
EBP
ESP add ebx, [100+eax]
EIP
add [100+eax+ebx*8], edx
EFLAGS
Microprocessor: Assembly Simple Operations

32 bits

EAX mov xchg lea


EBX
ECX or xor and
EDX
sub add mul div
ESI
EDI
inc dec
EBP
ESP shr shl rol ror
EIP
EFLAGS
Microprocessor: Assembly Simple Operations

lea doesn't access memory

32 bits

EAX lea eax, [eax+eax*4]


EBX
ECX lea ecx, [eax+edx+1000]
EDX
lea esp, [edx-1000]
ESI
EDI
lea ebp, [edi+esp*8+44]
EBP
ESP lea esi, [esi]
EIP
lea edi, [1234]
EFLAGS
Microprocessor: Assembly Simple Operations

08 01 00 BD

31 AB 11 10
32 bits
.
.
616c6f48
d0d8d490
6f6c6548 100h 48 6F 6C 61
mov eax, [100] ECX
EDX 104h
mov ebx, [104] 48 65 6C 6F
ESI
add eax, ebx EDI 108h 31
90 AB
D4 11
D8 10
D0
EBP
mov [108], eax ESP .
EIP .

EFLAGS 2D 3F 6A 2D

45 24 10 76

25 46 79 80

45 24 10 76
Microprocessor: Assembly Flags

32 bits

add sub
carry
inc dec
zero
sign
and or xor
overflow
shl/r rol/r
direction
cmp test

cld std
EFLAGS
clc stc cmc
Microprocessor: Assembly Branching Operations
Microprocessor: Assembly Branching Operations
Microprocessor: Assembly Branching Operations

32 bits
00000100 mov eax, 50
00000050
00001234
EAX
EBX 00000105 or eax, eax
ECX
EDX 00000107 jz 110
ESI 00000109 mov eax, 1234
EDI
EBP 0000010e jmp 115
ESP
00000110 mov eax, 0abcd
00000115
00000109
00000107
00000105
00000100
0000010e
EIP
EFLAGS 00000115 ...
Microprocessor: Assembly Branching Operations

32 bits
00000100 mov eax, 50
00000050
00000000
0000abcd
EAX
EBX 00000105 xor eax, eax
ECX
EDX 00000107 jz 110
ESI 00000109 mov eax, 1234
EDI
EBP 0000010e jmp 115
ESP
00000110 mov eax, 0abcd
00000100
00000115
00000110
00000107
00000105
EIP
EFLAGS 00000115 ...
Microprocessor: Assembly Branching Operations

32 bits
00000100 mov eax, 50
00000050
0000abcd
EAX
EBX 00000105 call 110
ECX
EDX 0000010a nop
ESI 0000010b jmp 10a
EDI
EBP
ESP
00000110 mov eax, 0abcd
0000010b
00000115
00000110
00000105
00000100
0000010a
EIP
EFLAGS 00000115 ret
Microprocessor: Signed vs Unsigned

C code
Assembly
#define MAX_SIZE 80

int main(int argc, char **argv)


{
unsigned int len; main:
char buf[MAX_SIZE ];
...
...
len = get_len();
call get_len()
if (len > MAX_SIZE ) mov [len],eax
exit();

memcpy(buf, data, len); cmp eax, 50


jbe not_big
} call exit
not_big:
...
call memcpy
Microprocessor: Signed vs Unsigned

C code
Assembly
#define MAX_SIZE 80

int main(int argc, char **argv)


{
int len; main:
char buf[MAX_SIZE ];
...
...
len = get_len();
call get_len()
if (len > MAX_SIZE ) mov [len],eax
exit();

memcpy(buf, data, len); cmp eax, 50


jle not_big
} call exit
not_big:
...
call memcpy
Microprocessor: Are 32 bits enough?

C code
Assembly

int main(int argc, char **argv)


{
unsigned len; main:
int *list;
...
...
len = get_len();
call get_len()
list = malloc(len * 4); mov [len],eax

memcpy(list, data, len * 4); lea eax, [eax*4]


...
}
call malloc

...
call memcpy
Microprocessor: Stack Operations

32 bits

EAX
EBX
ECX push pop
EDX
pushf popf
ESI
EDI
pusha popa
EBP
ESP call ret
EIP
EFLAGS
Microprocessor: Assembly Stack Operations

32 bits

12345678
EAX
EBP
bffffff8
bffffffc
c0000000
ESP
EFLAGS

00000100 push eax

00000101 push 12345678

00000106 pop eax


bfffffec
00000107 call 10d bffffff0
bffffff4
0000010c push eax bffffff8 12345678
bffffffc EAX
0000010d mov eax, 0abc c0000000

00000112 ret
Microprocessor: C Calling Convention

C code Assembly Memory


void _start() {
...
_start:
...

push envp
main(argc, push argv
argv, envp); push argc
call main main's ret addr
... add esp, c argc
... argv
envp
exit(2);
push 2
} call exit
...
Microprocessor: C Calling Convention

C code Assembly Memory


int main(int argc, char **argv) gets' ret addr
{ &buffer
int cookie; main:

} prologue
char buffer[80]; push ebp
mov ebp, esp
buffer
sub esp, 84
...
... cookie
gets(buffer);
ebp before main
push &buffer
call gets main's ret addr
...
pop ecx argc
argv
return 3; EBP
... envp

} ...
mov eax, 3

} epilogue
mov esp, ebp
pop ebp
ret
Microprocessor: C Calling Convention

C code Assembly Memory


void _start() { _start:
... ...

push envp
push argv
push argc
main(argc,
call main
argv, envp); add esp, c
...
...
push 2 argc
exit(2); call exit exit'sargv
ret addr
} envp
2
...
Understanding and exploiting the
bugs
Understanding the bugs: Buffer Overflow

stack1.c:
int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);


gets(buf);
if (cookie == 0x41424344)
printf(“you win!\n”);
}

buf 80 bytes

cookie 4 bytes
EBP 4 bytes
main's return addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Understanding the bugs: Buffer Overflow

C code Memory
Assembly
int main(int argc, char **argv)
gets' ret addr
{ main:
&buffer
int cookie; push ebp
char buffer[80]; mov ebp, esp
sub esp, 54
buffer
...
...
gets(buffer); cookie
push &buffer ebp before main
... call gets argc
pop ecx main's ret addr
EBP argv
envp
return 3;
...
} mov eax, 3

mov esp, ebp


pop ebp
ret
Understanding the bugs: Buffer Overflow

stack2.c:
int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);


gets(buf);
if (cookie == 0x01020305)
printf(“you win!\n”);
}

buf 80 bytes

cookie 4 bytes
EBP 4 bytes
main's return addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Understanding the bugs: Buffer Overflow

stack3.c:
int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);


gets(buf);
if (cookie == 0x01020005)
printf(“you win!\n”);
}

buf 80 bytes

cookie 4 bytes
EBP 4 bytes
main's return addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Understanding the bugs: Buffer Overflow

stack4.c:
int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);


gets(buf);
if (cookie == 0x000d0a00)
printf(“You win!\n”);
}

buf 80 bytes

cookie 4 bytes
EBP 4 bytes
main's return addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Understanding the bugs: Buffer Overflow

C code Memory
Assembly
gets' ret addr
int main(int argc, char **argv) &buffer
{ main:
int cookie; push ebp
char buffer[80]; mov ebp, esp buffer
sub esp, 54
...
... cookie
gets(buffer); main's old ebp
push &buffer main's ret addr
call gets argc
... pop ecx
argv
EBP envp
return 3; ...

} mov eax, 3

mov esp, ebp


pop ebp
ret
Understanding the bugs: Buffer Overflow

C code Memory
Assembly
int main(int argc, char **argv)
{ main:
int cookie; push ebp
char buffer[80]; mov ebp, esp
sub esp, 54
...
...
gets(buffer); push &buffer main's ret addr
call gets argc
... pop ecx
argv
EBP envp
return 3; ...

} mov eax, 3
EIP
mov esp, ebp
pop ebp
ret
Understanding the bugs: Buffer Overflow
abo1.c:
void f(int nada) {
char buf[1024];
gets(buf);
}

int main(int argc,char **argv) {


f(1);
printf("hola\n");
}

gets's return addr 4 bytes


compiler vars 24 bytes

buf 1024 bytes EBP


ESP
EBP 4 bytes
main's return addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Understanding the bugs: Buffer Overflow
abo1.c:
void f(int nada) {
char buf[1024];
gets(buf);
}

int main(int argc,char **argv) {


f(1);
printf("hola\n");
}
gets's return addr 4 bytes
compiler vars 24 bytes

buf 1024 bytes EBP


ESP
EBP 4 bytes
main's return addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Understanding the bugs: Buffer Overflow
abo1.c:
void f(int nada) {
char buf[1024];
gets(buf);
}

int main(int argc,char **argv) {


f(1);
printf("hola\n");
}

gets's return addr 4 bytes


compiler vars 24 bytes

buf 1024 bytes EBP


ESP
EBP 4 bytes
main's return addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Exploiting the bugs: Buffer overflow – Exploiting SEH

abo2.c:
int main(int argc,char **argv) {
char buf[1024];
gets(buf);
exit(1);
}

buf 1024 bytes

main's %ebp 4 bytes


main's return addr 4 bytes
main's arguments n bytes

next ERR 4 bytes


SEH filter 4 bytes

next ERR 4 bytes


SEH filter 4 bytes
Exploiting the bugs: Complex Buffer Overflow

abo3.c:
int main(int argc,char **argv) {
extern system,puts;
void (*fn)(char*)=(void(*)(char*))&system;
char buf[256];

fn=(void(*)(char*))&puts;
gets(buf);
fn(argc[2]);
exit(1);
}

buf 256 bytes

fn's ebp 4 bytes


fn's return addr 4 bytes
fn's “function” arg 4 bytes
Exploiting the bugs: Complex Buffer Overflow

abo4.c (part of it):


void (*fn)(char*)=(void(*)(char*))&system;
int main(int argc,char **argv) {
char *pbuf=malloc(100);
char buf[256];

gets(buf);
gets(pbuf);
fn(buf);
while (1); our address
}

buf 256 bytes

padding y bytes
pbuf 4 bytes
padding x bytes
main's ebp 4 bytes
main's return addr 4 bytes
main's arguments n bytes
write-anything-anywhere primitive
Use-After-Free
Exploiting the bugs: Use-After-Free

heap0.c
int main(int argc, char **argv) {
char buf[80];
fgets(buf, sizeof(buf), stdin);

contact *c = new contact();


delete c
char *pbuf = (char *)malloc(4);
c->edit(1, "someone");
}

&edit user
size=4 code controlled
vftable ptr buffer
Exploiting the bugs: Use-After-Free

heap0.c
int main(int argc, char **argv) {
char buf[80];
fgets(buf, sizeof(buf), stdin);

contact *c = new contact();


delete c
char *pbuf = (char *)malloc(4);
c->edit(1, "someone");
}

user
size=4 controlled
vftable ptr buffer
Understanding the bugs: Use-After-Free – Hands on!
/* .. who’s there? */

using namespace std;

class contact {
public:
virtual void edit(unsigned int contact, string
name);
};

void contact::edit(unsigned int contact, string name) {


cout << "editing " << name << endl;
}

int main(int argc, char **argv) {


char buf[256];
fgets(buf, 256, stdin);
contact *c = new contact();
delete c;
char *p1 = (char *)malloc(sizeof(contact));
fgets(p1, 4, stdin);
c->edit(1, "someone");
}
Signed vs Unsigned (or “computers
vs numbers”)
Understanding the bugs: Signed vs Unsigned

• A register/dword can be positive or negative

• The sign is determined by the highest bit

• A 32 bits number can represents:


•Unsigned number: 0 ~ 4294967295
•Signed number: -2147483648 ~ 2147483647

• Range:
•0x80000000 … 0 … 0x7FFFFFFF
Understanding the bugs: Signed vs Unsigned

• Ex:
•v = 3  0x00000003
•v = -3  0xFFFFFFFD

• Negating a number ( 3  -3 ):
•v = 3  NOT ( 0x00000003 )  0xFFFFFFFC + 1
•v = -3  0xFFFFFFFD

• Comparing unsigned numbers:


•If ( 0x00000001 < 0x7FFFFFFF ) … ?
•If ( 0xFFFFFFFF < 0x00000001 ) … ?
•If ( 0x7FFFFFFF < 0x80000000 ) … ?
Understanding the bugs: Signed vs Unsigned

• Comparing signed numbers:


•If ( 0x00000001 < 0x7FFFFFFF ) … ?
•If ( 0xFFFFFFFF < 0x00000001 ) … ?
•If ( 0x7FFFFFFF < 0x80000000 ) … ?

• Comparing numbers in ASM:


•EAX = 0x00000001
•EBX = 0xFFFFFFFF

•“cmp eax,ebx”
•“jb 0x80808080” ( JUMP is BELOW ) … ?
•“jl 0x80808080” ( JUMP is LESS ) … ?
Integer Overflow
Understanding the bugs: Integer Overflow

• It’s produced when the result of an arithmetic operation is too


large to be represented within the available storage space.

• It’s produced by operations like:


•Addition, substraction, multiplication and division.

• Unsigned 32 bit numbers:


•Range: 0 ~ 4294967295
•4294967295 + 1 = ?  0 … INTEGER OVERFLOW !
•0 – 1 = ?  4294967295 … INTEGER UNDERFLOW !
Protection mechanisms
Windows Protection Mechanisms

• Canary (Cookies)
• Data Execute Prevention (bit NX)
• ASLR
• Windows SEH Protections
• Heap Protections
Protections: Stack – Stackguard

example.c
int main() {
char buf[80];
gets(buf);
}

buf 80 bytes
ebp before
cookie main 4 bytes
canary 4 bytes
main's ret addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Protections: Propolice and /GS

example.c
int main() {
char buf[80];
gets(buf);
}

cookie 4 bytes
buf 80 bytes
canary 4 bytes
esi before main 4 bytes
edi before main 4 bytes
ebp before main 4 bytes
main's ret addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Protections: W^X - DEP

example.c
int main() {
char buf[80];
gets(buf);
}
W + X
Memory

buf 80 bytes
ebp before
cookie main 4 bytes
main's ret addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Protections: ASLR

example.c
int main() {
char buf[80];
gets(buf);
}
memory

buf 80 bytes
ebp before
cookie main 4 bytes
? main's ret addr 4 bytes
main's argc 4 bytes
main's argv 4 bytes
Protections: Windows SEH protections

SE Handler pointing to code


Must not be in stack
DEP – NX (Heap is not an option)

SAFE SEH
Handlers white list (only modules compiled with “/SafeSEH”)

SEHOP
The last “SEH handler” has to point to 
“ntdll!FinalExceptionHandler” (ASLR is the problem !)
Protections: Heap protections

example.c
int main() {
...

free( B );

safe unlink
cookies
pointer encoding
direct mmap

A B C
Protections: Heap protections

next block overwrite


other techniques

function
pointers
Protections: Hardware DEP

This protection takes advantage of the NX bit (No Execute


page)

NX marks parts of the memory (data regions) as non


executable. The processor will then refuse to execute any
code residing in these areas of memory.
Protections: Hardware DEP

The DEP’s behavior in Windows XP /2003 can be


changed via a boot.ini parameter.

Starting from Windows Vista, DEP state can be


changed by using the bcedit command.
Protections: Hardware DEP – The APIs

The most important


API added is
SetProcessDEPPolicy,
which sets the DEP
policy for the
running process.
Bypassing DEP: ROP techniques – Gadget

When hardware DEP is


enabled, you cannot just
jump to your shellcode

Gadget definition
Bypassing DEP: ROP techniques – Gadget example

Example gadget to set ECX to an arbitrary value:

Return address must point to the POP ECX/RET gadget;


next item on the stack must be the value that will be
loaded into ECX.
In this example, ECX will take the value 0x41424344. After
that, execution will continue with the next gadget at
0x9239bc.
– VirtualAlloc / VirtualProtect

For bypassing DEP, the most common


technique is building a call to VirtualAlloc (to
allocate a new memory region with
executable permissions) or VirtualProtect (to
give executable permissions to an existing
memory region) using ROP.
Our gadgets chain will set the right values for
the arguments and finally jump to one of the
APIs mentioned above, ensuring to return to
our “new” executable code.
Bypassing DEP: ROP techniques – Using PUSHAD -
RET - RET

The techniques used to provide the correct


arguments for an API to allow the execution of
our code are numerous and depend exclusively
on the gadgets found in the application code.

The most popular one is the PUSHAD-RET-RET


technique, because this gagdet allows to easily
setup the arguments in the stack, and it’s usually
easy to found it in binary code.
Bypassing DEP: ROP techniques – Using PUSHAD -
RET - RET

We can use Ollydbg to manually


build a ROP chain.
When the analyzed program
crashes, a handy technique is to
change the register values to
0x41414141, 0x42424242, etc,
except for ESP.
Bypassing DEP: ROP techniques – Using PUSHAD -
RET - RET

Assemble a PUSHAD - RET gadget in


the next instruction to execute.
Trace till RET by pressing F7.
The stack will be modified placing our
register values in the right position.
Bypassing DEP: ROP techniques – Using PUSHAD -
RET - RET

Manually changing ESI to point to VirtualAlloc,


EDI to a RET instruction and tracing the
PUSHAD-RET-RET sequence we can see in the
stack the desired arguments for this API.
Bypassing DEP: ROP techniques – Using PUSHAD -
RET - RET

These are the arguments needed for VirtualAlloc to enable


execution of our code buffer. We need to put the right
value in the right register, with a chain of gadgets, and
finally jump to a PUSHAD–RET gadget.
Bypassing DEP: ROP techniques – Using PUSHAD -
RET - RET

EBP (45454545) = Return address


EBX (44444444) = Size
EDX (43434343) = Allocation type
ECX (42424242) = Protect
ESI (46464646) = VirtualAlloc
EDI (47474747) = RET
Bypassing DEP: ROP techniques – Using PUSHAD -
RET - RET

Setting “Address” argument is not


trivial without harcoding, but with
the PUSHAD RET-RET technique, ESP
will automatically end up in the
position of the “Address” argument.
Bypassing DEP: ROP techniques – Using PUSHAD -
RET - RET
The correct initial values of the registers previous to
PUSHAD-RET-RET must be the following:

EBP = JMP ESP


EBX = Size (0x1)
EDX = 0x1000 (Allocation type: MEM_COMMIT)
ECX = 0x40 (Protect: PAGE_EXECUTE_READWRITE)
ESI = VirtualAlloc
EDI = RET
Thank you

You might also like