software security

93
Software Security December 2014 Roman Oliynykov Professor at Information Technologies Security Department Kharkov National University of Radioelectronics Head of Scientific Research Department JSC “Institute of Information Technologies” Ukraine Visiting professor at Samsung Advanced Technology Training Institute Korea [email protected]

Upload: roman-oliynykov

Post on 28-Jul-2015

166 views

Category:

Internet


0 download

TRANSCRIPT

Software Security December 2014

Roman Oliynykov

Professor atInformation Technologies Security Department

Kharkov National University of Radioelectronics

Head of Scientific Research Department JSC “Institute of Information Technologies”

Ukraine

Visiting professor at Samsung Advanced Technology Training Institute

[email protected]

Lectures outline

A few words about myself Importance of secure solutions Buffer overflow vulnerability and

countermeasures Heap overruns and integer overflows,

recommendations for avoiding Format string vulnerabilities and security

recommendations

For these lecturesI suppose you are familiar

for general understanding of discussing problems: C and C++ programming languages (basic level)

it will be discussed, but would be an advantage if you already familiar with operation system architecture (process, address

space, stack, heap, etc.) x86 assembler language source code (preferably

AT&T notation) basics of Linux (command line) and computer

networking

About myself (I)

I’m from Ukraine (Eastern part of Europe), host country of Euro2012 football championship

I live in Kharkov (the second biggest city in the country, population is 1.5 million people), Eastern Ukraine (near Russia),former capital of the Soviet Ukraine (1918-1934)three Nobel prize winners worked at Kharkov University

About myself (II)

Professor of Information Technologies Security Department at Kharkov National University of Radioelectronics courses on computer networks and

operation system security, special mathematics for cryptographic applications

Head of Scientific Research Department at JSC “Institute of Information Technologies” Scientific interests: symmetric

cryptographic primitives synthesis and cryptanalysis

A few words about importance of secure solutions

Modern malware

functions of different categories: viruses, worms and Trojan horses

belongs to network of bot programs (botnet) executes commands received from the

botnet control center has stealth/polymorphysm/armoring

functions against antivirus software

Consequences of cyberattacks for the society

[Norton/Symantec cybercrime report, 2013]

Consequences of cyberattacks for the society

[Norton/Symantec cybercrime report, 2013]

Consequences of cyberattacks for the society

[Norton/Symantec cybercrime report, 2013]

Buffer overflow vulnerability overview

Address space layout in different operation systems

Address space layout in different 32-bit operation systems

Process switching example

Linux process address space layout (32-bit)

Example of code without boundary check

How it looks like after a compilation

What happens if strcpy() argument is longer than the destination buffer

strcpy( &dst, &src ) in contrast to

strncpy( &dst, &src, sizeof (dst) ) takes into account only destination string length (buffer size) and copies data until finds termination zero in src

Example of vulnerable network daemon (service) for Linux, and exploit for it

netcalcd – vulnerable daemon (service) for Linux (x86)

intentionally written for this lecture and contains intentionally man-made vulnerabilities

processes simple network text requests for basic calculations

prints debug information about its stack on the server console

netcalcd normal operation

netcalcd normal operation

netcacld source code: part of the main() function

netcacld source code: process_request() function

netcacld source code: get_result() function

netcacld source code in asm: get_result() function

Vulnerability in get_result() function

strcpy( &dst, &src ) in contrast to

strncpy( &dst, &src, sizeof (dst) ) takes into account only destination string length (buffer size) and copies data until finds termination zero in src

netcalcd stack after strcpy() call with malicious data (hacker’s code) from the network

netcalcd normal operation

Running exploit against netcalcd

netcalcd buffer overflow in get_result()

Open ports on the victim computer: before and after

Victim computer successfully cracked

What’s inside exploit and how it works?

Exploit: usual C program for Windows sending block of data (shellcode):

Shellcode in the example: relocatable binary code can be run at any user address

Protect the running code in the stack, find absolute address it is run at and decode the rest part of the shellcode

Why encode the main part of the shellcode?

After encoding the rest part of the shellcode runs web server at port 8801

or does everything intruder wants to do with the vulnerable process privileges

NB: cross-platform exploits working well different processors and OS

How to protect our software against such an attack?

Possible countermeasures against buffer overflow

write secure code based on secure functions calls and all necessary user input verification (the most important recommendation)

make your operation system to use Address Space Layout Randomization (ASLR)

make your operation system use processor NX bit (on x86 platform)

keep on canary words in your compiler run the code with the least necessary privileges

Write secure code based on secure functions calls

strcpy( &dst, &src ) fills destination buffer without taking into account its size;

strncpy( &dst, &src, sizeof( dst ) ) won’t write outside the destination buffer (but it’s possible the lost of terminating zero)

Write secure code based on secure functions calls

And many other recommendations for writing secure code…

Security check of existing projects: automated tools

But no guarantee that all vulnerabilities are discovered

Address Space Layout Randomization

computer security method which involves randomly arranging the positions of key data areas, usually including the base of the executable and position of libraries, heap, and stack, in a process's address space

Each running time stack, heap, etc. are put at random addresses in the process address space

Address Space Layout Randomization (example)

It’s difficult to guess correct return address to be written on the stack smashing. But it is possible: only16 less bits of address are changed

Running code addresses are NOT changed

ASLR appeared:

Linux kernel support: 2.6.12 (released June 2005)

Microsoft's Windows Vista (released January 2007), Windows Server 2008, Windows 7, and later have ASLR enabled by default

Android 4.0 Ice Cream Sandwich provides ASLR

ASLR evasion techniques

brute force address search attempt return into code on non-randomized memory jmp *esp (ret address points to such bytes in code) etc.

x86 calling conventions

cdecl (C declaration) arguments are pushed to the stack in the reverse

order (the last one has the higher address) calling function cleans stack (allows arbitrary

number of arguments after program compilation)

stdcall (standard call) arguments are pushed to the stack in the reverse

order (like in cdecl) called function cleans stack

x86 calling conventions (cont.)

pascal arguments are pushed to the stack in the direct order (the

last one has the lower address) called function cleans stack

fastcall first two arguments are passed via registers, the rest (if

any) are pushed to the stack in the reverse order (like in cdecl)

called function cleans stack thiscall (for OOP non-static member functions)

this pointer (to object *this) is an additional parameter for gcc: calling function cleans stack; for MS Visual C++: it

depends on fixed (called) or variable (calling) number of arguments

x86_64 calling conventions

the single calling conversion: first 4 arguments are passed via registers rcx, rdx,

r8, r9, the rest (if any) are pushed to the stack in the reverse order (like in cdecl)

calling function cleans stack (like in cdecl)

Make your operation system use processor NX bit (on x86 platform)

NX bit, which stands for Never eXecute, is a technology used in CPUs to segregate areas of memory for use by either storage of processor instructions (or code) or for storage of data

NX bit protection evasion:return-to-libc attack

no code in the stack (no processor exception)

return address is overwritten and points to the existing code

intruder calls standard function and passes arbitrary arguments to it

in Windows it is possible to call a sequence of functions due to _stdcall_ convention

Never switch off canary words in your compiler

Canary words are known values that are placed between a buffer and control data on the stack to monitor buffer overflows

Canary words

Implementation: GCC Stack-Smashing Protector (ProPolice) Microsoft Visual Studio 2003 and higher ( /GS ) etc.

What cannot be handled: buffer overflows in the heap

(intruder uses pointers to functions in virtual method tables of dynamic objects)

Beyond stack overflow: heap overruns

heap memory allocation: dynamic allocation (no fixed addresses) considered as data only (no execution)

Object oriented programming

encapsulation inheritance polymorphism

Polymorphism implementation: virtual methods table (VMT)

Heap overflow: VMT is overwritten

C++ code example

C++ code examples (cont.)

Heap overflow influence

Threats for heap overflow beyond OOP

function pointers indexes for arrays (will be discussed with

integer overflows) other data structures located at heap may

have influence to program execution

cf: Heap Spraying methods used by intruders

Heap overflow

NX bit does NOT protect

no canary words

ASLR evasion techniques work well

Integer overflows

Integer overflows (example for 64-bit application)

Possible string length

size_t strlen( const char *s );

size_t: long unsigned int (64 bit for 64-bit platform)

Integer conversion rules

1) signed (n bits) to unsigned of the same size positive and zero: the same value negative: value + 2n

2) signed to signed of the bigger size positive and zero: the same value negative: most significant bit extends

3) signed to unsigned of the bigger size signed converts to signed of the bigger size, and then

converts to unsigned positive and zero: the same value negative: see item 2) then item 1)

Integer conversion rules (cont.)

4) unsigned to unsigned of the bigger size the same value (extended by zero bits)

5) signed to signed of the bigger size the same value

6) unsigned (n bits) to signed of the same size 0.. 2n-1: the same value bigger than 2n-1: negative value

7) unsigned to signed of the bigger size unsigned converts to signed of the same size, see 6) and

then converts to signed of the necessary size

Integer conversion rules (cont.)

signed or unsigned to integer with smaller size: rather complex behavior depending on system

architecture (little endian, big endian) should be avoided unless predictable special

function appropriate for program logic is implemented.

Rules for integer types conversion for different operations

if operands size are less than 32 bit (char, short), they conversed to int (32 bit)

bitwise logical operators follow this rule, e.g.: result of (unsigned short) | (unsigned short) belongs to int

Rules for integer types conversion for different operations (cont.)

unary ~ (compliment) also extends result type

++ and – do not change result type

% should be carefully analyzed during index check:

Rules for integer types conversion for different operations (cont.)

/ is also should be carefully analyzed

Operations with pointer comparison: different behavior on 32- and 64-bit platforms

Recommendations for avoiding integer overflows

keep in mind, that there is NO universal solution, speed and effectiveness of C/C++ should be paid by developer’s attention

don’t compare signed and unsigned types carefully check array indexes, pointer operations use unsigned variables for array indexes and sizes

for memory allocation always pay attention to compiler’s warnings during software testing always check boundary

values (0, max, -1, -2, max-1, max+1, etc.) given to input of your code

Format string vulnerabilities

Format string vulnerabilities

untrusted input (user, network, etc.) is sent directly to *prinf() function from the standard library

Vulnerable functions

and all functions directly passing arguments to these (error(), syslog(), etc.)

Examples of printf() application

Principles of vulnerability exploitation (32-bit systems)

printf() allows variable number of arguments passed through the stack

Results of exploiting vulnerability

program/service/daemon crash (DoS attack) viewing local function variables viewing any block of memory available to the

process (thread) write access to memory available to the

process (thread)

Program crash

“%s%s%s%s%s%s%s%s%s%s%s%s” is taken from untrusted input

printf() expects to find 12 pointers to the zero-ended string at stack among it’s parameters

if there is at least one “pointer” found on stack is invalid, program crashes

Viewing local function variables

“%08x. %08x. %08x. %08x. %08x\n” is taken from untrusted input

printf() expects to find 5 integer variables at stack as it’s parameters

values are taken from the stack (variables of the calling function, its returning address, etc.) and printed

Viewing any block of memory

“\x10\x01\x48\x08_%08x.%08x.%08x.%08x.%08x|%s|” is taken from untrusted input

it’s very likely that buffer for untrusted input is also located in the stack

printf() is made to take necessary number of bytes from the stack by %08x parameters given by hacker

address of interesting memory is given in little-endian format by \x10\x01\x48\x08 and printed out with %s parameter

Write-access and code execution (I)

it seems (but seems only) that attack like buffer overflow is protected by %400 string limit

"%497d\x3c\xd3\xff\xbf<nops><shellcode>“is taken from untrusted input

Write-access and code execution (I)

buffer for untrusted input is located in the stack and can be used for smashing returning address

\x3c\xd3\xff\xbf address points to nops or to the beginning of shellcode

example works in case of canary absence and disabled NX bit (but there are evasion methods)

Write-access and code execution(I): is it enough to use such solution for protection?

snprintf() is used instead of insecure sprintf()

end of string array is marked by \0 in any case

secure functions are used for further processing of this string

is it enough for securing a program?

printf() and other similar functions can write variables:

%n is used to get the number of already written bytes to the variable which address is given as a parameter to printf()

number written by printf() can be adjusted with %100n, %150n, etc. format string value

Write-access to process memory (II): tasks for exploiters

find format-string vulnerability find a block of code putting parameter to the

stack write a basic sequence for format string find an offset in stack for taking parameters

and addresses in case of code execution (not only memory

read/write): ASLR, canary and NX bit evasion [beyond this attack]

Write-access to process memory (II)

format string contains: address of memory to be modified (0xbfffc8c0) %08x parameters for setting enough stack offset %n parameter to make printf() writing to process

memory

Write-access to process memory (II)

additional features: %hn may be used to write 2 bytes instead of 4 global offset table (GOT) in Linux process or

Windows stubs may be modified instead of stack returning address (preserving canaries and evading NX bit), including “return-to-libc method”

addresses of virtual functions, destructors, etc. in heap can be also overwritten

NB: Sequential calls with untrusted input are also vulnerable!

any nested sequence of snprintf() can be used

for attacker it’s enough to find one call with snprintf( outbuf, buf ) without %s specifier

all similar functions are also affected

Recommendations for functions taking format string: use snprintf( outbuf, “%s”, buf ) in all cases; remember

that snprintf( outbuf, buf ) vulnerable in any nested call if some part of buf is taken from untrusted input

use only secure functions (snprintf() instead of sprintf(), etc., as pointed out in man pages and msdn)

mark the end of string array by \0 end in any case keep in mind, that there is NO universal solution, speed

and effectiveness of C/C++ should be paid by developer’s attention

don’t believe that this is only C/C++ problem: Java virtual machines (JVM), Perl, etc. are written in C/C++ and may cause this vulnerability even to script languages

Recommended books: further reading 24 Deadly Sins of Software Security: Programming

Flaws and How to Fix Them (Michael Howard, David LeBlanc, John Viega)

Writing Secure Code (Michael Howard) Software Security: Building Security In (Gary McGraw) The Security Development Lifecycle (Michael Howard,

Steve Lipner) Secure Coding: Principles and Practices (Mark G. Graff,

Kenneth R. van Wyk) Scott Meyers. Effective C++: 55 Specific Ways to

Improve Your Programs and Designs (3rd Edition)