Professional Documents
Culture Documents
int i = 1;
while (i > 0)
{
i = i * 2;
}
results
At the end of the loop the invariant i = 2c-1 holds
(and the loop has been executed c-1 times.)
For 32-bit integers, after the 31st iteration we
have i = 231;
for signed integers this number is negative and
the loop terminates.
For unsigned integers, after one more iteration
we have i = 232 0 and the loop also terminates.
Computing with Integers
Unsigned 8-bit integers are performed modulo 256.
255 + 1 = 0 16 17 = 16
0 – 1 = 255
Signed 8-bit integers are represented as 2’s complement.
Sign bit = 0 number is positive, sign bit =1 number is negative.
127 + 1 = -128 -128/-1 = -1
In mathematics: a + b a for b 0
As you can see, such obvious “facts” are
no longer true.
Two’s Complement
Signed integers are usually represented as 2’s complement
numbers.
Most significant bit (sign bit) indicates the sign of the integer:
– If sign bit is zero, the number is positive.
– If sign bit is one, the number is negative.
Positive numbers given in normal binary representation.
Negative numbers are represented as the binary number that
when added to a positive number of the same magnitude
equals zero.
Two’s complement Rule
To find the 8-bit two’s complement of a
positive integer a that is at most 255:
1. Write the 8-bit binary representation for a.
2. Flip the bits (that is, switch all the 1’s to 0’s
and all the 0’s to 1’s).
3. Add 1 in binary notation.
Finding the two’s complement of 19
1. Write the 8-bit binary representation for 19,
2. Switch all the 0’s to 1’s and all the 1’s to 0’s,
3. and add 1.
Checking your results
return
address write to A: my_address
value2
value1|
value2| value1
buffer for
my_address
variable A
Code Example
Declare a local short string variable
char buffer[80];
use the standard C library routine call
gets(buffer);
to read a single text line from standard input and save it into
buffer.
Works fine for normal-length lines, but corrupts the stack if
the input is longer than 79 characters.
Attacker loads malicious code into buffer and redirects return
address to start of attack code.
Shellcode
Overwrite return address so that execution jumps to the
attack code (‘shellcode’).
Where to put the shellcode?
Shellcode may be put on the stack as part of the malicious
input; a.k.a. argv[]-method.
– To guess the location, guess distance between return address and
address of the input containing the shellcode.
Details e.g. in Smashing the Stack for Fun and Profit.
return-to-libc method: attack calls system library; change
to control flow, but no shellcode inserted.
Heap Overruns
More difficult to determine how to overwrite a specific buffer.
More difficult to determine which other buffers will be
overwritten in the process; if you are an attacker, you may not
want to crash the system before you have taken over.
Even attacks that do not succeed all the time are a threat.
Can overwrite filenames and function pointers, and mess up
memory management.
Memory Allocation
Overwriting Pointers
Modify return address with buffer overrun on stack.
– Attacker can fairly easily guess the location of this pointer relative to a
vulnerable buffer.
– Defender knows which target to protect.
More powerful attack: overwrite arbitrary pointer with an
arbitrary value.
More targets, hence more difficult to defend against.
Attacker does not even have to overwrite the pointer!
Attacker can lure the operating system into reading
malformed input and then do the job for the attacker.
Managing Memory in C
Allocating memory:
– void * malloc (size_t size)
– Returns pointer to newly allocated block of size bytes.
– Contents of the block are not initialized.
– Returns null pointer if block cannot be allocated.
Deallocating memory:
– void free (void *ptr)
– *ptr must have been returned by a previous call to malloc(),
calloc()or realloc().
– If ptr is null, no operation is performed.
– Behaviour undefined if free(ptr) has already been called.
Memory Organization
Case study: Doug Lea malloc.
Memory divided into chunks.
A chunk contains user data and control data.
Chunks allocated by malloc contain boundary tags.
Free chunks are placed in bins; a bin is a double linked lists.
– Several bins, for chunks of different sizes.
Free chunks contain boundary tags and forward and backward
pointer to their neighbours in the bin.
Allocated and Free Chunks
previously
unused used for
data
user data
size of bk offset 12
chunk fd offset 8
size
size PREV_INUSE offset 4
Details
Double free vulnerability
The memory allocator divides the heap memory
at its disposal into contiguous chunks, which vary
in size as the various allocation routines (malloc,
free, realloc, . . . ) are called.
An invariant is that a free chunk never borders
another free chunk when one of these routines
has completed:
if two free chunks had bordered, they would
have been coalesced into one larger free chunk.
Double free vulnerability
These free chunks are kept in sorted doubly linked
lists:
of the same size (for small chunks)
or of an interval of size (for larger chunks),
which are accessed via an array (whose elements are
called bins).
When the memory allocator at a later time requests a
chunk of the same size as one of these free chunks,
the first chunk of appropriate size will be removed
from the list and will be made available for use in the
program (i.e. it will turn into an allocated chunk).
Double free
Chunk1 is an allocated chunk containing information
about: the size of the chunk stored before it and its own
size.
The rest of this chunk is available for the program to
write data in.
Chunk2 is a free chunk: it is stored in the doubly linked
list of chunks of equal size.
The information for this list is stored over the first 8 bytes
of what was user data when the chunk was allocated.
This particular chunk is located between chunk3 in
chunk4 in the list.
Double free
Chunk3 is the first chunk in the chain:
• its backward pointer points to chunk2
• and its forward pointer points to a previous chunk in
the list.
Chunk2 is the next chunk,
• with its forward pointer pointing to chunk3
• and its backward pointer pointing to chunk4.
Chunk4 is the last chunk in our example:
• its backward pointer points to a next chunk in the list
• and its forward pointer points to chunk2.
Double free vulnerability
The size of allocated chunks is always a multiple of
eight, so the three least significant bits of the size field
are used for management information:
– a bit to indicate if the previous chunk is in use or not
– and one to indicate if the memory is mapped or not.
– The last bit is currently unused.
The "previous chunk in use"-bit can be modified by an
attacker to force coalescing of chunks.
The representation of chunk2 is not entirely correct:
if chunk1 is in use, it will be used to store 'user data'
for chunk1 and not the size of chunk1.
Exploitation- what may happen if an array that is
located in chunk1 is overflowed.
Double free
An attacker has overwritten the management
information of chunk2.
The size fields are left unchanged (although
these could be modified if needed).
The forward pointer has been changed to
point to 12 bytes before the return address
and the backward pointer has been changed
to point to code that will jump over the next
few bytes.
Double free
When chunk1 is subsequently freed, it will be coalesced
together with chunk2 into a larger chunk.
As chunk2 will no longer be a separate chunk after the
coalescing it must first be removed from the list of free
chunks.
The unlink macro takes care of this: internally a free chunk is
represented by a structure containing the following unsigned
long integer fields (in this order):
– prev sizesize,
– fd
– and bk.
Double free vulnerability
chun Lower addresses chun Chu
k1 k2 nk3
return
address my_address
write to A:
check value value2 ≠ check value
canary value1|
value2| value1
buffer for my_address
variable A
to A attack
detected
Detection – Code Inspection
Code inspection is tedious: we need automation.
K. Ashcraft & D. Engler: Using Programmer-Written Compiler
Extensions to Catch Security Holes, IEEE Symposium on
Security &Privacy 2002.
Meta-compilation for C source code; ‘expert system’
incorporating rules for known issues: untrustworthy sources
sanitizing checks trust sinks; raises alarm if
untrustworthy input gets to sink without proper checks.
Code analysis to learn new design rules: Where is the sink
that belongs to the check we see?
Microsoft has internal code inspection tools.
Detection – Testing
White box testing: tester has access to source code.
Black-box testing when source code is not available.
You do not need source code to observe how memory is
used or to test how inputs are checked.
Example: syntax testing of protocols based on formal
interface specification, valid cases, anomalies.
Applied to SNMP implementations: vulnerabilities in trap
handling and request handling found
http://www.cert.org/advisories/CA-2002-03.html
– Found by Oulu University Secure Programming Group
http://www.ee.oulu.fi/research/ouspg/
Mitigation – Least Privilege
Limit privileges required to run code; if code running with few
privileges is compromised, the damage is limited.
Do not give users more access rights than necessary; do not
activate options not needed.
Example – debug option in Unix sendmail: when switched on
at the destination, mail messages can contain commands that
will be executed on the destination system.
Useful for system managers but need not be switched on all
the time; exploited by the Internet Worm of 1988.
Lesson Learned
In the past, software was shipped in open configurations
(generous access permissions, all features activated); user
had to harden the system by removing features and
restricting access rights.
Today, software often shipped in locked-down
configurations; users have to activate the features they
want to use.
Reaction – Keeping Up-to-date
Information sources : CERT advisories, BugTraq at
www.securityfocus.com, security bulletins from software
vendors.
Hacking tools use attack scripts that automatically search for
and exploit known type of vulnerabilities.
Analysis tools following the same ideas will cover most real
attacks.
Patching vulnerable systems is not easy: you have to get the
patches to the users and avoid introducing new vulnerabilities
through the patches.
Summary
Many of the problems listed may look trivial.
There is no silver bullet:
– Code-inspection: better at catching known problems, may raise
false alarms.
– Black-box testing: better at catching known problems.
– Type safety: guarantees from an abstract (partial) model need not
carry over to the real system.
Experience in high-level programming languages may be a
disadvantage when writing low level network routines.