KEMBAR78
Preventing Hijacking Attacks1 | PDF | Pointer (Computer Programming) | Computing
0% found this document useful (0 votes)
24 views16 pages

Preventing Hijacking Attacks1

Uploaded by

sonam Kr
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views16 pages

Preventing Hijacking Attacks1

Uploaded by

sonam Kr
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

Control-Hijacking attack

Hijacking is a type of network security attack in which the attacker takes control of a
communication.

Control flow

The order in which individual statements, instructions or function calls of an imperative program
are executed or evaluated.

Control-hijacking attacks = Control-flow hijacking attacks −

• Change of control flow

• Alter a code pointer (i.e., value that influences program counter) or, Gain control of the
instruction pointer

• Change memory region that should not be accessed

Control Flow Graph (CFG)

A representation, using graph notation, of all paths that might be traversed through a program
during its execution.

Code injection attacks

It is a subclass of control hijacking attacks that subverts the intended control flow of a program
to previously injected malicious code Shellcode.

Shellcode is a malicious code supplied by attacker often saved in buffer being overflowed

• Adding a new node to the CFG


− Adversary can execute arbitrary malicious code. Examples: − Return to Shellcode (=
Ret2Shellcode)

Code-reuse attacks

Adding a new path to the CFG − Adversary is limited to the code nodes that are available in the
CFG

● Examples: − Ret2Libc, ROP, JOP (Jump-oriented programming)


Attacker’s goal:

• Takeover target machine (e.g. Web server)

− Execute arbitrary code on target by hijacking the execution flow of a running program

Common control hijacking methods

Buffer overflows

Integer overflows

Heap overflows

Format string vulnerability

Buffer Overflow by NIST’s Definition

“A condition at an interface under which more input can be placed into a buffer or data holding
area than the capacity allocated, overwriting other information.

−Attackers exploit such a condition to crash a system or to insert specially crafted code that
allows them to gain control of the system.”

• In information security and programming, a buffer overflow, or buffer overrun, is


an anomaly where a program, while writing data to a buffer, overruns the buffer's
boundary and overwrites adjacent memory locations.
• Buffers are areas of memory set aside to hold data, often while moving it from one
section of a program to another, or between programs.
• Buffer overflows can often be triggered by malformed inputs; if one assumes all inputs
will be smaller than a certain size and the buffer is created to be that size, then an
anomalous transaction that produces more data could cause it to write past the end of the
buffer.
• If this overwrites adjacent data or executable code, this may result in erratic program
behavior, including memory access errors, incorrect results, and crashes.
• On many systems, the memory layout of a program, or the system as a whole, is well
defined. By sending in data designed to cause a buffer overflow, it is possible to write
into areas known to hold executable code and replace it with malicious code, or to
selectively overwrite data pertaining to the program's state, therefore causing behavior
that was not intended by the original programmer.
• Buffers are widespread in operating system (OS) code, so it is possible to make attacks
that perform privilege escalation and gain unlimited access to the computer's resources.
The famed Morris worm in 1988 used this as one of its attack techniques.
• Programming languages commonly associated with buffer overflows include C and C++,
which provide no built-in protection against accessing or overwriting data in any part of
memory and do not automatically check that data written to an array (the built-in buffer
type) is within the boundaries of that array. Bounds checking can prevent buffer
overflows, but requires additional code and processing time.
• Modern operating systems use a variety of techniques to combat malicious buffer
overflows, notably by randomizing the layout of memory, or deliberately leaving space
between buffers and looking for actions that write into those areas ("canaries").
Example
In the following example expressed in C, a program has two variables which are adjacent in
memory: an 8-byte-long string buffer, A, and a two-byte big-endian integer, B.

char A[8] = "";


unsigned short B = 1979;

Initially, A contains nothing but zero bytes, and B contains the number 1979.

variable name A B

value [null string] 1979

hex value 00 00 00 00 00 00 00 00 07 BB

Now, the program attempts to store the null-terminated string "excessive" with ASCII encoding
in the A buffer.

strcpy(A, "excessive");

"excessive" is 9 characters long and encodes to 10 bytes including the null terminator, but A can
take only 8 bytes. By failing to check the length of the string, it also overwrites the value of B:

variable name A B

value 'e' 'x' 'c' 'e' 's' 's' 'i' 'v' 25856

hex 65 78 63 65 73 73 69 76 65 00
B's value has now been inadvertently replaced by a number formed from part of the character
string. In this example "e" followed by a zero byte would become 25856.
Writing data past the end of allocated memory can sometimes be detected by the operating
system to generate a segmentation fault error that terminates the process.
To prevent the buffer overflow from happening in this example, the call to strcpy could be
replaced with strlcpy, which takes the maximum capacity of A (including a null-termination
character) as an additional parameter and ensures that no more than this amount of data is written
to A:

strlcpy(A, "excessive", sizeof(A));

When available, the strlcpy library function is preferred over strncpy which does not null-
terminate the destination buffer if the source string's length is greater than or equal to the size of
the buffer (the third argument passed to the function), therefore A may not be null-terminated
and cannot be treated as a valid C-style string.

Few unsafe C library routines

• gets(char *str) read line from standard input into str


• sprintf(char *str, char *format, ...) create str according to supplied format and variables
• strcat(char *dest, char *src) append contents of string src to string dest
• strcpy(char *dest, char *src) copy contents of string src to string dest
• vsprintf(char *str, char *fmt, va_list ap) create str according to supplied format and
variables

Stack buffer overflow


• In software, a stack buffer overflow or stack buffer overrun occurs when a program
writes to a memory address on the program's call stack outside of the intended data
structure, which is usually a fixed-length buffer.
• Stack buffer overflow bugs are caused when a program writes more data to a buffer
located on the stack than what is actually allocated for that buffer.
• This almost always results in corruption of adjacent data on the stack, and in cases where
the overflow was triggered by mistake, will often cause the program to crash or operate
incorrectly.
• Stack buffer overflow is a type of the more general programming malfunction known
as buffer overflow (or buffer overrun).
• Overfilling a buffer on the stack is more likely to derail program execution than
overfilling a buffer on the heap because the stack contains the return addresses for all
active function calls.
• A stack buffer overflow can be caused deliberately as part of an attack known as stack
smashing.
• If the affected program is running with special privileges, or accepts data from untrusted
network hosts (e.g. a webserver) then the bug is a potential security vulnerability.
• If the stack buffer is filled with data supplied from an untrusted user then that user can
corrupt the stack in such a way as to inject executable code into the running program and
take control of the process.
• This is one of the oldest and more reliable methods for attackers to gain unauthorized
access to a computer.
• The canonical method for exploiting a stack-based buffer overflow is to overwrite the
function return address with a pointer to attacker-controlled data (usually on the stack
itself).

Control Hijacking Attacks (Runtime exploit)

A control hijacking attack exploits a program error, particularly a memory corruption


vulnerability, at application runtime to subvert the intended control flow of a program.

• Memory corruption bugs in software written in low-level languages like C or C++ are
• one of the oldest problems in computer security.
• The lack of memory safety in these languages allows attackers to alter the program’s
behavior or take full control over it by hijacking its control flow.
• Recent code-injection attacks on iPhones, have shown a level of sophistication that is
able to bypass widely-deployed prevention mechanisms against code injection.

Attacks that divert a program’s control flow for malicious purposes are generally
known as control-hijacking attacks.

• A typical control hijacking attack starts by corrupting a pointer to an attacker supplied


malicious data, which we refer to as the payload.
• Then, the attack proceeds by modifying code pointers, which are objects that affect
control flow of a program. These two steps could be accomplished by, for example an
overflow exploit.
Fig: Steps in Control-Hijacking attack

• In order to hijack control successfully, the attacker needs to know the correct target value
(i.e. the address of the payload) with which to overwrite the code pointer in step 2. That is
represented in a separate step 3 in Figure.
• Assuming a code pointer (e.g. a function pointer) has been successfully corrupted in the
first three steps, an attacker then proceeds by loading the corrupted pointer into the
instruction pointer, which points to next instruction a program executes.
• This can be achieved by executing an indirect control-flow transfer instruction, like a
return instruction, thus diverting the execution from the intended program controlflow.
• The final step of a control-flow hijack exploit is the execution of the payload, which is
often something malicious such as stealing private information, or gaining high privilege
on specific targets.

Defenses Against Control Flow Hijacking

• A variety of defensive mechanisms have been proposed to mitigate control-flow


hijacking attacks.
• As previously mentioned, complete memory safety, code pointer integrity, and control
flow integrity are some of these defenses.
• The practicality of these defenses relies on how a particular implementation balances
security with the performance overhead.

Memory safety enforcement mechanisms:

• Ensure no data pointer is corrupted, thus preventing the attack in step 1.


• Complete memory safety can defend against all control hijacking attacks by protecting all
pointers.
• As shown in Figure, the first step in control hijacking corrupts a pointer by first making it
invalid and deferences the pointer.
• A pointer can become invalid by going out of the bounds of its pointed object or when
the object gets deallocated.
• A pointer pointing to a deleted object is called a dangling pointer. Dereferencing an out-
of-bounds pointer causes a so called spatial error, while dereferencing a dangling pointer
causes a temporal error.
• Memory safety methods that prevent spatial errors provide spatial memory safety,
whereas the methods that prevent temporal errors provide temporal memory safety.
• Fortunately, hardware support can make complete memory safety practical. For instance,
Intel memory protection extensions (MPX) can facilitate better enforcement of memory
safety checks

Code Pointer Integrity (CPI):

• Unlike complete memory safety, CPI relies on protecting only the integrity of sensitive
pointers (e.g. function pointer) to thwart control hijacking.
• Since sensitive pointers make up a subset of all pointers, CPI promises to provide strong
security at a very reasonable performance cost.
• CPI first over-approximately identifies all sensitive pointers via static analysis of the
code. Then, it stores metadata for checking validity of code pointers in a designated "safe
region" of memory.
• The metadata includes the value of the pointer and its lower and upper bounds.
Additionally, CPI adds instrumentation that propagates metadata when pointer operations
occur.
• CPI relies on a secret region which is kept in the same space as the process being
protected, to be isolated. It assumes it has to be leak-proof and cannot be disclosed.

Control Flow Integrity (CFI):

• CFI detects control-hijacking attacks by enforcing a Control Flow Graph, which is a


representation of all paths a program may take during its execution.
• It is an attempt to stop control-flow hijacking at step 4, thus protecting against ROP.
• Any CFI based defense relies on the level of coverage of the CFG.
• If the CFG is perfectly constructed, then we can ensure only valid the control flows
intended by the program.
• However, perfectly constructing and maintaining CFG ahead of time requires the source
code of the program, its hard and inaccurate, and incurs very high performance cost.
These practical concerns demanded that we use a weaker version of CFI, which results in
weaker security guarantees.

NX-bit on AMD Athlon 64, XD-bit on Intel P4 Prescott

• The 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, a feature normally only found in Harvard architecture processors.
• An operating system with support for the NX bit may mark certain areas of memory as
non-executable.
• The processor will then refuse to execute any code residing in these areas of memory.
• The general technique, known as executable space protection, is used to prevent certain
types of malicious software from taking over computers by inserting their code into
another program's data storage area and running their own code from within this section;
this is known as a buffer overflow attack.
• The NX bit specifically refers to bit number 63 (i.e. the most significant bit) of a 64-bit
entry in the page table.
• If this bit is set to 0, then code can be executed from that page; if set to 1, code cannot be
executed from that page, and anything residing there is assumed to be data.

Data Execution Prevention (DEP)

• DEP is a system-level memory protection feature that is built into the operating system
starting with Windows XP and Windows Server 2003.
• DEP enables the system to mark one or more pages of memory as non-executable.
Marking memory regions as non-executable means that code cannot be run from that
region of memory, which makes it harder for the exploitation of buffer overruns.
• DEP prevents code from being run from data pages such as the default heap, stacks, and
memory pools. If an application attempts to run code from a data page that is protected, a
memory access violation exception occurs, and if the exception is not handled, the calling
process is terminated.
• DEP is not intended to be a comprehensive defense against all exploits; it is intended to
be another tool that you can use to secure your application.

How Data Execution Prevention Works

• If an application attempts to run code from a protected page, the application receives an
exception with the status code STATUS_ACCESS_VIOLATION.
• If your application must run code from a memory page, it must allocate and set the proper
virtual memory protection attributes.
• The allocated memory must be marked
• PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE,
or PAGE_EXECUTE_WRITECOPY when allocating memory.
• Heap allocations made by calling the malloc and HeapAlloc functions are non-
executable.
• Applications cannot run code from the default process heap or the stack.
• DEP is configured at system boot according to the no-execute page protection policy
setting in the boot configuration data.

Limitations:

– Some apps need executable heap (e.g. JITs).


– Can be easily bypassed using Return Oriented Programming (ROP)

Return-oriented programming (ROP):


• It is a computer security exploit technique that allows an attacker to execute code in the
presence of security defenses such as executable space protection and code signing.
• In this technique, an attacker gains control of the call stack to hijack program control
flow and then executes carefully chosen machine instruction sequences that are already
present in the machine's memory, called "gadgets".
• Each gadget typically ends in a return instruction and is located in a subroutine within the
existing program and/or shared library code. Chained together, these gadgets allow an
attacker to perform arbitrary operations on a machine employing defenses that prevent
simpler attacks.
A call stack is a stack data structure that stores information about the active subroutines of
a computer program.
This kind of stack is also known as an execution stack, program stack, control stack, run-
time stack, or machine stack, and is often shortened to just "the stack. A call stack is used for
several related purposes, but the main reason for having one is to keep track of the point to which
each active subroutine should return control when it finishes executing..
• “Return-oriented programming is a computer security exploit technique in which the
attacker uses control of the call stack to indirectly execute cherry-picked machine
instructions or groups of machine instructions immediately prior to the return instruction
in subroutines within the existing program code, in a way similar to the execution of a
threaded code interpreter.
• If the data is being written onto the stack, the excess data may overflow the space
allocated to the function's variables (e.g., "locals" in the stack diagram to the right) and
overwrite the return address. This address will later be used by the function to redirect
control flow back to the caller. If it has been overwritten, control flow will be diverted to
the location specified by the new return address.
• “Because all the instructions that are executed are from executable memory areas within
the original program, this avoids the need for direct code injection.
• A return-oriented programming attack does not inject malicious code, but rather uses
instructions that are already present, called "gadgets", by manipulating return addresses.
• A typical data execution prevention cannot defend against this attack because the
adversary did not use malicious code but rather combined "good" instructions by
changing return addresses; therefore the code used would not be marked non-executable.

ROP (Return Oriented Proragmming ) Attack


This type of attack is able to perform arbitrary computation without the necessary use of library
functions by reusing code chunks which he calls GADGETS.

Address Space Layout Randomization (ASLR)


• ASLR attempts to prevent attacks by introducing entropy to memory addresses.
• In particular, in order to prevent the attacker from specifying the target location
• reliably, ASLR randomly arranges the key areas of address space such as stack, heap,
linked libraries, and optionally the program text.
• ASLR is a computer security technique involved in preventing exploitation of memory
corruption vulnerabilities.
• In order to prevent an attacker from reliably jumping to, for example, a particular
exploited function in memory, ASLR randomly arranges the address space positions of
key data areas of a process, including the base of the executable and the positions of
the stack, heap and libraries.
• Address space randomization hinders some types of security attacks by making it more
difficult for an attacker to predict target addresses.
• For example, attackers trying to execute return-to-libc attacks must locate the code to be
executed, while other attackers trying to execute shellcode injected on the stack have to
find the stack first.
• In both cases, the system obscures related memory-addresses from the attackers. These
values have to be guessed, and a mistaken guess is not usually recoverable due to the
application crashing.
Effectiveness
• Address space layout randomization is based upon the low chance of an attacker guessing
the locations of randomly placed areas.
• Security is increased by increasing the search space. Thus, address space randomization
is more effective when more entropy is present in the random offsets. Entropy is
increased by either raising the amount of virtual memory area space over which the
randomization occurs or reducing the period over which the randomization occurs.

Fix bugs:
1: Audit software
• Automated tools: Coverity, Prefast/Prefix.

Coverity Tool:

• It is a proprietary static code analysis tool from Synopsys. This product enables engineers
and security teams to find and fix software defects.
• Coverity is a static code analysis tool for C, C++, C#, Java, JavaScript, PHP, Python, .Net
Core, ASP.NET, Objective-C, Go, JSP, Ruby, Swift, Fortran, Scala, VB.NET, ioS,
and Typescript. It also supports more than 70 different frameworks for Java, JavaScript,
C# and other languages.
• The starting point with Coverity is what we call central analysis.

Periodically, an automated process will check out your code from your source control
system and then build and analyze it with Coverity.

Those results are then sent to a Coverity server.


• Coverity’s speed, accuracy, ease of use, and scalability meet the needs of even the
largest, most complex environments. Choose where and how to do your development:
on-premises.
• Coverity provides developers all the information they need to fix identified issues—
detailed descriptions, categories, severities, CWE information, defect location, detailed
remediation guidance, and dataflow traces—as well as issue triage and management
features, within their IDE. In addition, Coverity provides context-specific eLearning
lessons specific to CWEs identified in their code so they can fix it quickly today and
avoid similar defects in the future.

PREfast Tool:

• PREfast is a utility for static code analysis (it means that analysis is done at compile
time). It can find defects in C/C++ code such as buffer overruns, null pointer
dereferencing, forgetting to check function return value and so on. Especially it is good in
checking kernel-mode code due to a larger set of rules which the code has to follow. You
can treat PREfast as a tool for an automatic code review.
• To make its job better, PREfast has to know additional information about the code. That’s
where annotations come into the action.

2: Rewrite software in a type safe languange (Java, ML)

• Difficult for existing (legacy) code


3: Marking memory as non-execute (DEP)
• Prevent attack code execution by marking stack and heap as non-executable

Stack Smashing

• Stack smashing is a form of vulnerability where the stack of a computer application or


OS is forced to overflow. This may lead to subverting the program/system and crashing
it.

A stack, a first-in last-out circuit, is a form of buffer holding intermediate results of


operations within it. To simplify, stack smashing putting more data into a stack than its
holding capacity. Skilled hackers can deliberately introduce excessive data into the stack.
• The excessive data might be stored in other stack variables, including the function return
address. When the function returns, it jumps to the malicious code on the stack, which
might corrupt the entire system. The adjacent data on the stack is affected and forces the
program to crash.

More explanation about Stack Smashing


• If the program affected by stack smashing accepts data from untrusted networks and runs
with special privileges, it is a case of security vulnerability. If the buffer contains data
provided by an untrusted user, the stack may be corrupted by injecting executable code
into the program, thus gaining unauthorized access to a computer. An attacker can also
overwrite control flow information stored in the stack.

As stack smashing has grown into a very serious vulnerability, certain technologies are
implemented to overcome the stack smashing disaster. Stack buffer overflow protection
changes the organization of data in the stack frame of a function call to include canary
values.
• These values when destroyed indicate that a buffer preceding it in memory has been
overflowed. Canary values monitor buffer overflows and are placed between the control
data and the buffer on the stack. This ensures that a buffer overflow corrupts the canary
first. A failed verification of canary data signifies an overflow in the stack.
• The three types of canary are Random, Terminator, and Random XOR.
• Solar Designer'' has developed a Linux patch that makes the stack non-executable,
precisely to address the stack smashing problem.
• This patch simply makes the stack portion of a user process's virtual address space non-
executable, so that attack code injected onto the stack cannot be executed.
• This patch offers the advantages of zero performance penalty, and that programs work
and are protected without re-compilation. However, it does necessitate running a
specially-patched kernel, unless this extension is adopted as standard.

Limitations:

• In addition to the above vulnerabilities, making the stack non-executable fails to address
the problem of buffer overflow attacks that do not place attack code on the stack.
• The attacker may inject the attack code into a heap-allocated or statically allocated buffer,
and simply re-point a function return address or function pointer to point to the attack
code.
• The attacker may not even need to inject attack code at all, if the right code fragment can
be found within the body of the program itself. Thus additional protection for critical data
structures such as function pointers and function return addresses, as described in Section

Stack Protection Mechanisms


• An effective method for protecting programs against classic stack overflow attacks is to
instrument the function entry and exitcode to setup and then check its stack frame for any
evidence of corruption.
• If any modification is found, the program is aborted rather than allowing the attack to
proceed.
Stackguard

• It is one of the best known protection mechanisms. It is a GCC Compiler extension that
inserts additional function entry and exit code.
• The added function entry code writes a canary12 value below the old frame pointer
address, before the allocation of space for local variables. The added function exit code
checks that the canary value has not changed before continuing with the usual function
exit operations of restoring the old frame pointer and transferring control back to the
return address.
• Any attempt at a classic stack buffer overflow would have to alter this value in order to
change the old frame pointer and return addresses, and would thus be detected, resulting
in the program being aborted.
• For this defense to function successfully, it is critical that the canary value be
unpredictable and should be different on different systems.
• If this were not the case, the attacker would simply ensure the shellcode included the
correct canary value in the required location.
• Typically, a random value is chosen as the canary value on process creation and saved as
part of the processes state.
• The code added to the function entry and exit then use this value.

Limitation:

• It requires that all programs needing protection be recompiled.


• As the structure of the stack frame has changed, it can cause problems with programs,
such as debuggers, which analyze stack frames.
• However, the canary technique has been used to recompile an entire Linux distribution
and provide it with a high level of resistance to stack overflow attacks.
• Similar functionality is available for Windows programs by compiling them using
Microsoft’s /GS Visual C++ compiler option.

Using canaries

• A buffer overflow is dangerous, so the compiler uses various protection mechanisms to


guard against it. One such mechanism is a canary.

• A canary is a randomly generated value, and in the case of a buffer overflow, the canary
is overwritten; when, upon comparison with the known value, the compiler detects that
the stack was compromised it throws the stack smashing detected error.

Canary Types:

• Random canary:
– Random string chosen at program startup.
– Insert canary string into every stack frame.
– Verify canary before returning from function.
• Exit program if canary changed.
Turns potential exploit into DoS.
– To corrupt, attacker must learn current random string.

• Terminator canary: Canary = {0, newline, linefeed, EOF}


– String functions will not copy beyond terminator.

– Attacker cannot use string functions to corrupt stack

Random canaries are randomly generated, usually from an entropy-gathering daemon, in order
to prevent an attacker from knowing their value. Usually, it is not logically possible or plausible
to read the canary for exploiting; the canary is a secure value known only by those who need to
know it—the buffer overflow protection code in this case.
Normally, a random canary is generated at program initialization, and stored in a global variable.

Terminator canaries

• Most buffer overflow attacks are based on certain string operations which end at string
terminators. A terminator canary contains NULL(0x00), CR (0x0d), LF (0x0a), and EOF
(0xff), four characters that should terminate most string operations, rendering the
overflow attempt harmless.
• This prevents attacks using strcpy() and other methods that return upon copying a null
character while the undesirable result is that the canary is known.
• This type of protection can be bypassed by an attacker overwriting the canary with its
known values and the return address with specially-crafted value resulting in a code
execution.
• This can be when non-string functions are used to copy buffers and both the buffer
contents and the length of the buffer are attacker controlled.

You might also like