Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Eilam E.Reversing.Secrets of reverse engineering.2005

.pdf
Скачиваний:
65
Добавлен:
23.08.2013
Размер:
8.78 Mб
Скачать

Auditing Program Binaries 261

00401042

mov

[eax+0xc],ecx

00401045

mov

[eax+0x10],ecx

00401048

mov

[eax+0x14],ecx

0040104b

mov

ecx,esi

0040104d

mov

esi,[esp+0xc]

00401051

mov

edx,ecx

00401053

mov

[eax],edi

00401055

shr

ecx,0x2

00401058

lea

edi,[eax+0x18]

0040105b

rep

movsd

0040105d

mov

ecx,edx

0040105f

and

ecx,0x3

00401062

rep

movsb

00401064

pop

edi

00401065

pop

esi

00401066

ret

 

The important thing about this version of allocate_object is the supplied buffer length’s data type. When reading assembly language code, you must always be aware of every little detail—that’s exactly where all the valuable information is hidden. See if you can find the difference between this function and the earlier version.

It turns out that this function is treating the buffer length as a signed short. This creates a potential problem because in C and C++ the compiler doesn’t really care what you’re doing with an integer—as long as it’s defined as signed and it’s converted into a longer data type, it will be sign extended, no matter what the target data type is. In this particular example, malloc takes a size_t, which is of course unsigned. This means that the buffer length would be sign extended before it is passed into malloc and to the code that adds 0x18 to it. Here is what you should be looking for:

00401022 movsx

esi,word ptr [esp+0xc]

This line copies the parameter from the stack into ESI, while treating it as a signed short and therefore sign extends it. Sign extending means that if the buffer length parameter has its most significant bit set, it would be converted into a negative 32-bit number. For example, a buffer length of 0x9400 (which is 37888 in decimal) would become 0xffff9400 (which is 4294939648 in decimal), instead of 0x00009400.

Generally, this would cause an overflow bug in the allocation size and the allocation would simply fail, but if you look carefully you’ll notice that this problem also brings back the bug looked at earlier, where adding the header size to the user-supplied buffer length causedan overflow. That’s because the MOVSX instruction can generate the same large negative values that were causing the overflow earlier. Consider a case where the function is fed 0xfff8 as the buffer length. The MOVSX instruction would convert that into 0xfffffff8, and you’d

262Chapter 7

be back with the same overflow situation caused by the lea edi,[esi+0x18] instruction.

The solution to these problems is to simply define the buffer length as an unsigned short, which would cause the compiler to use MOVZX instead of MOVSX. MOVZX zero extends the integer during conversion (meaning simply that the most significant word in the target 32-bit integer is set to zero), so that its numeric value stays the same.

Case-Study: The IIS Indexing Service Vulnerability

Let’s take a look at what one of these bugs look like in a real commercial software product. This is different from what you’ve done up to this point because all of the samples you’ve looked at so far in this chapter were short samples created specifically to demonstrate one particular bug or another. With a commercial product, the challenging part is typically the magnitude of code we need to look at. Sure, eventually when you locate the bug it looks just like it did in the brief samples, but the challenge is to make out these bugs inside an endless sea of code.

In June 2001, a nasty vulnerability was discovered in versions 4 and 5 of the Microsoft Internet Information Services (IIS). The main problem was that any Windows 2000 Server system was vulnerable in its default configuration out of the box. The vulnerability was caused by an unchecked buffer in an ISAPI (Internet Services Application Programming Interface) DLL. ISAPI is an interface that is used for creating IIS extension DLLs that provide server-side functionality in the Web server. The vulnerability was found in idq.dll—an ISAPI DLL that interfaces with the Indexing Service and is installed as a part of IIS.

The vulnerability (which was posted by Microsoft as security bulletin MS01-044) was actually exploited by the Code Red Worm, of which you’ve probably heard. Code Red had many different variants, but generally speaking it would operate on a monthly cycle (meaning that it would do different things on different days of the month). During much of the time, the worm would simply try to find other vulnerable hosts to which it could spread. At other times, the worm would intercept all incoming HTTP requests and make IIS send back the following message instead of any meaningful Web page:

HELLO! Welcome to http://www.worm.com! Hacked By Chinese!

The vulnerability in IIS was caused by a combination of several flaws, but most important was the fact that URLs sent to IIS that contained an .idq or

.ida file name resulted in the URL parameters being passed into idq.dll (regardless of whether the file is actually found). Once inside idq.dll, the URL was decoded and converted to Unicode inside a limited-sized stack variable, with absolutely no bounds checking.

Disassembled listing of CVariableSet::AddExtensionControlBlock from idq.dll.

Auditing Program Binaries 263

In order to illustrate what this problem actually looks like in the code, I have listed parts of the vulnerable code here. These listings are obviously incomplete—these functions are way too long to be included in their entirety.

CVariableSet::AddExtensionControlBlock

The function that actually contains the overflow bug is CVariableSet:: AddExtensionControlBlock, which is implemented in idq.dll. Listing 7.2 contains a partial listing (I have eliminated some irrelevant portions of it) of that function.

Notice that we have the exact name of this function and of other internal, nonexported functions inside this module. idq.dll is considered part of the operating system and so symbols are available. The printed code was taken from a Windows Server 2000 system with no service packs, but there are quite a few versions of the operating system that contained the vulnerable code, including Service Packs 1, 2, and 3 for Windows 2000 Server.

idq!CVariableSet::AddExtensionControlBlock:

6e90065c

mov

eax,0x6e906af8

6e900661

call

idq!_EH_prolog (6e905c30)

6e900666

sub

esp,0x1d0

6e90066c

push

ebx

6e90066d

xor

eax,eax

6e90066f

push

esi

6e900670

push

edi

6e900671

mov

[ebp-0x24],ecx

6e900674

mov

[ebp-0x2c],eax

6e900677

mov

[ebp-0x28],eax

6e90067a

mov

[ebp-0x4],eax

6e90067d

mov

eax,[ebp+0x8]

.

 

 

.

 

 

.

 

 

6e9006b7

mov

esi,[eax+0x64]

6e9006ba

or

ecx,0xffffffff

6e9006bd

mov

edi,esi

.

 

 

.

 

 

.

 

 

6e9007b7

push

0x3d

6e9007b9

push

edi

6e9007ba

mov

[ebp-0x18],edi

6e9007bd

call

dword ptr [idq!_imp__strchr (6e8f111c)]

Listing 7.2

(continued)

264 Chapter 7

6e9007c3

mov

esi,eax

6e9007c5

pop

ecx

6e9007c6

test

esi,esi

6e9007c8

pop

ecx

6e9007c9

je

6e9008d2

6e9007cf

sub

eax,edi

6e9007d1

push

0x26

6e9007d3

push

edi

6e9007d4

mov

[ebp-0x20],eax

6e9007d7

inc

esi

6e9007d8

call

dword ptr [idq!_imp__strchr (6e8f111c)]

6e9007de

mov

edi,eax

6e9007e0

pop

ecx

6e9007e1

test

edi,edi

6e9007e3

pop

ecx

6e9007e4

jz

6e9007fa

6e9007e6

cmp

edi,esi

6e9007e8

jnb

6e9007f0

6e9007ea

inc

edi

6e9007eb

jmp

6e9008e4

6e9007f0

mov

eax,edi

6e9007f2

sub

eax,esi

6e9007f4

inc

edi

6e9007f5

mov

[ebp-0x14],eax

6e9007f8

jmp

6e900804

6e9007fa

mov

eax,[ebp-0x10]

6e9007fd

sub

eax,esi

6e9007ff

add

eax,ebx

6e900801

mov

[ebp-0x14],eax

6e900804

cmp

dword ptr [ebp-0x20],0x190

6e90080b

jb

6e900828

6e90080d

mov

eax,0x80040e14

6e900812

xor

ecx,ecx

6e900814

mov

[ebp-0x3c],eax

6e900817

lea

eax,[ebp-0x3c]

6e90081a

push

0x6e9071b8

6e90081f

push

eax

6e900820

mov

[ebp-0x38],ecx

6e900823

call

idq!_CxxThrowException (6e905c36)

6e900828

mov

eax,[ebp+0x8]

6e90082b

push

dword ptr [eax+0x8]

6e90082e

lea

eax,[ebp-0x1dc]

6e900834

push

eax

6e900835

lea

eax,[ebp-0x20]

6e900838

push

eax

6e900839

push

dword ptr [ebp-0x18]

6e90083c

call

idq!DecodeURLEscapes (6e9060be)

6e900841

xor

ecx,ecx

 

 

 

Listing 7.2 (continued)

Auditing Program Binaries 265

6e900843

cmp

[ebp-0x20],ecx

6e900846

jnz

6e900861

6e900848

mov

eax,0x80040e14

6e90084d

push

0x6e9071b8

6e900852

mov

[ebp-0x44],eax

6e900855

lea

eax,[ebp-0x44]

6e900858

push

eax

6e900859

mov

[ebp-0x40],ecx

6e90085c

call

idq!_CxxThrowException (6e905c36)

6e900861

lea

eax,[ebp-0x1dc]

6e900867

push

eax

6e900868

call

idq!DecodeHtmlNumeric (6e9060b8)

6e90086d

lea

eax,[ebp-0x1dc]

6e900873

push

eax

6e900874

call

dword ptr [idq!_imp___wcsupr (6e8f1148)]

6e90087a

mov

eax,[ebp-0x14]

6e90087d

pop

ecx

6e90087e

add

eax,0x2

6e900881

mov

[ebp-0x30],eax

6e900884

add

eax,eax

6e900886

push

eax

6e900887

call

idq!ciNew (6e905f86)

6e90088c

mov

[ebp-0x34],eax

6e90088f

mov

ecx,[ebp+0x8]

6e900892

mov

byte ptr [ebp-0x4],0x2

6e900896

push

dword ptr [ecx+0x8]

6e900899

push

eax

6e90089a

lea

eax,[ebp-0x14]

6e90089d

push

eax

6e90089e

push

esi

6e90089f

call

idq!DecodeURLEscapes (6e9060be)

6e9008a4

cmp

dword ptr [ebp-0x14],0x0

6e9008a8

jz

6e9008b2

6e9008aa

push

dword ptr [ebp-0x34]

6e9008ad

call

idq!DecodeHtmlNumeric (6e9060b8)

6e9008b2

mov

ecx,[ebp-0x24]

6e9008b5

lea

edx,[ebp-0x34]

6e9008b8

push

edx

6e9008b9

lea

edx,[ebp-0x1dc]

6e9008bf

mov

eax,[ecx]

6e9008c1

push

edx

6e9008c2

call

dword ptr [eax]

6e9008c4

push

dword ptr [ebp-0x34]

6e9008c7

and

byte ptr [ebp-0x4],0x0

6e9008cb

call

idq!ciDelete (6e905f8c)

6e9008d0

jmp

6e9008e4

6e9008d2

test

edi,edi

6e9008d4

jz

6e9008ec

 

 

 

Listing 7.2 (continued)

266 Chapter 7

6e9008d6

inc

edi

6e9008d7

push

0x26

6e9008d9

push

edi

6e9008da

call

dword ptr [idq!_imp__strchr (6e8f111c)]

6e9008e0

pop

ecx

6e9008e1

mov

edi,eax

6e9008e3

pop

ecx

6e9008e4

test

edi,edi

6e9008e6

jne

6e9007ae

6e9008ec

push

dword ptr [ebp-0x2c]

6e9008ef

or

dword ptr [ebp-0x4],0xffffffff

6e9008f3

call

idq!ciDelete (6e905f8c)

6e9008f8

mov

ecx,[ebp-0xc]

6e9008fb

pop

edi

6e9008fc

pop

esi

6e9008fd

mov

fs:[00000000],ecx

6e900904

pop

ebx

6e900905

leave

 

6e900906

ret

0x4

 

 

Listing 7.2

(continued)

CVariableSet::AddExtensionControlBlock starts with the setting up of an exception handler entry and then subtracts ESP by 0x1d0 (464 bytes) to make room for local variables. One can immediately suspect that a significant chunk of data is about to be copied into this stack space—few functions use 464 bytes worth of local variables. In the first snippet the point of interest is the loading of EAX, which is loaded with the value of the first parameter (from [ebp+0x8]).

A quick investigation with WinDbg reveals that CVariableSet:: AddExtensionControlBlock is called from HttpExtensionProc, which is a documented callback that’s used by IIS for communicating with ISAPI DLLs. A quick trip to the Platform SDK reveals that HttpExtension Proc receives a single parameter, which is a pointer to an EXTENSION_ CONTROL_BLOCK structure. In the interest of preserving the earth’s forests, I skip several pages of irrelevant code and get to the three lines at 6e9006b7, where offset +64 from EAX is loaded into ESI and then finally into EDI. Offset +64 in EXTENSION_CONTROL_BLOCK is the lpszQueryString member, which is exactly what we’re after.

The instruction at 6e9007ba stores EDI into [ebp-0x18] (where it remains), and then the code goes to look for character 0x3d within the string using strchr. Character 0x3d is ‘=’, so the function is clearly looking for the end of the string I’m currently dealing with (the ‘=’ character is used as a separator in these request strings). If strchr finds the character the function proceeds to calculate the distance between the character found and the beginning of

Auditing Program Binaries 267

the string (this is done in 6e9007cf). This distance is stored in [ebp-0x20], and is essentially the length of the string I’m are currently dealing with.

An interesting comparison is done in 6e900804, where the function compares the string length with 0x190 (400 in decimal), and throws a C++ exception using _CxxThrowException if it’s 400 or above. So, it seems that the function does have some kind of boundary checking on the URL. Where is the problem here? I’m just getting to it.

When the string length comparison succeeds, the function jumps to where it sets up a call to DecodeURLEscapes. DecodeURLEscapes takes four parameters: The pointer to the string from [ebp-0x18], a pointer to the string length from [ebp-0x20], a pointer to the beginning of the local variable area from [ebp-0x1dc], and offset +8 in EXTENSION_CONTROL_BLOCK. Clearly

DecodeURLEscapes is about to copy, or decode, a potentially problematic string into the local variable area in the stack.

DecodeURLEscapes

In order to better understand this bug, let’s take a look at DecodeURLEscapes, even though it is not strictly where the bug is at. This function is presented in Listing 7.3. Again, this listing is incomplete and only includes the relevant areas of DecodeURLEscapes.

query!DecodeURLEscapes:

68cc697e

mov

eax,0x68d667cc

68cc6983

call

query!_EH_prolog (68d4b250)

68cc6988

sub

esp,0x30

68cc698b

push

ebx

68cc698c

push

esi

68cc698d

xor

eax,eax

68cc698f

push

edi

68cc6990

mov

edi,[ebp+0x10]

68cc6993

mov

[ebp-0x3c],eax

68cc6996

mov

[ebp-0x38],eax

68cc6999

mov

ecx,[ebp+0xc]

68cc699c

mov

[ebp-0x4],eax

68cc699f

mov

[ebp-0x18],eax

68cc69a2

mov

ecx,[ecx]

68cc69a4

cmp

ecx,eax

68cc69a6

mov

[ebp-0x10],ecx

68cc69a9

jz

query!DecodeURLEscapes+0x99 (68cc6a17)

68cc69ab

mov

esi,[ebp+0x8]

68cc69ae

mov

eax,ecx

68cc69b0

inc

eax

68cc69b1

mov

[ebp-0x14],eax

68cc69b4

movzx

bx,byte ptr [esi]

Listing 7.3 Disassembly of DecodeURLEscapes function from query.dll. (continued)

268 Chapter 7

68cc69b8

and

dword ptr [ebp-0x34],0x0

68cc69bc

cmp

bx,0x2b

68cc69c0

jne

query!DecodeURLEscapes+0xdf (68cc6a5d)

68cc69c6

push

0x20

68cc69c8

pop

ebx

68cc69c9

inc

esi

68cc69ca

xor

eax,eax

68cc69cc

cmp

[ebp-0x34],eax

68cc69cf

jnz

query!DecodeURLEscapes+0x79 (68cc69f7)

68cc69d1

cmp

bx,0x80

68cc69d6

jb

query!DecodeURLEscapes+0x79 (68cc69f7)

68cc69d8

cmp

[ebp-0x18],eax

68cc69db

jnz

query!DecodeURLEscapes+0x79 (68cc69f7)

68cc69dd

cmp

[ebp-0x3c],eax

68cc69e0

jnz

query!DecodeURLEscapes+0x73 (68cc69f1)

68cc69e2

mov

eax,[ebp-0x14]

68cc69e5

push

eax

68cc69e6

mov

[ebp-0x38],eax

68cc69e9

call

query!ciNew (68d4a977)

68cc69ee

mov

[ebp-0x3c],eax

68cc69f1

mov

eax,[ebp-0x3c]

68cc69f4

mov

[ebp-0x18],eax

68cc69f7

mov

eax,[ebp-0x18]

68cc69fa

test

eax,eax

68cc69fc

jz

query!DecodeURLEscapes+0x88 (68cc6a06)

68cc69fe

mov

[eax],bl

68cc6a00

inc

eax

68cc6a01

mov

[ebp-0x18],eax

68cc6a04

jmp

query!DecodeURLEscapes+0x8d (68cc6a0b)

68cc6a06

mov

[edi],bx

68cc6a09

inc

edi

68cc6a0a

inc

edi

68cc6a0b

dec

dword ptr [ebp-0x10]

68cc6a0e

dec

dword ptr [ebp-0x14]

68cc6a11

cmp

dword ptr [ebp-0x10],0x0

68cc6a15

jnz

query!DecodeURLEscapes+0x36 (68cc69b4)

68cc6a17

test

eax,eax

68cc6a19

jz

query!DecodeURLEscapes+0xb4 (68cc6a32)

68cc6a1b

sub

eax,[ebp-0x3c]

68cc6a1e

push

eax

68cc6a1f

push

edi

68cc6a20

push

eax

68cc6a21

push

dword ptr [ebp-0x3c]

68cc6a24

push

0x1

68cc6a26

push

dword ptr [ebp+0x14]

68cc6a29

call

dword ptr [query!_imp__MultiByteToWideChar (68c61264)]

68cc6a2f

lea

edi,[edi+eax*2]

68cc6a32

and

word ptr [edi],0x0

 

 

 

Listing 7.3 (continued)

 

 

Auditing Program Binaries 269

 

 

 

 

68cc6a36

sub

edi,[ebp+0x10]

 

68cc6a39

mov

eax,[ebp+0xc]

 

68cc6a3c

push

dword ptr [ebp-0x3c]

 

68cc6a3f

or

dword ptr [ebp-0x4],0xffffffff

 

68cc6a43

sar

edi,1

 

68cc6a45

mov

[eax],edi

 

68cc6a47

call

query!ciDelete (68d4a9ae)

 

68cc6a4c

mov

ecx,[ebp-0xc]

 

68cc6a4f

pop

edi

 

68cc6a50

pop

esi

 

68cc6a51

mov

fs:[00000000],ecx

 

68cc6a58

pop

ebx

 

68cc6a59

leave

 

 

68cc6a5a

ret

0x10

 

.

 

 

 

.

 

 

 

.

 

 

 

 

 

 

 

Listing 7.3 (continued)

Before you start inspecting DecodeURLEscapes, you must remember that the first parameter it receives is a pointer to the source string, and the third is a pointer to the local variable area in the stack. That local variable is where one expects the function will be writing a decoded copy of the source string. The first parameter is loaded into ESI and the third into EDI. The second parameter is a pointer to the string length and is copied into [ebp-0x10]. So much for setups.

The function then gets into a copying loop that copies ASCII characters from ESI into BX (this is that MOVZX instruction at 68cc69b4). It then writes them into the address from EDI as zero-extended 16-bit values (this happens at 68cc6a06). This is simply a conversion into Unicode, where the Unicode string is being written into a local variable whose pointer was passed from

CVariableSet::AddExtensionControlBlock.

In the process, the function is looking for special characters in the string which indicate special values within the string that need to be decoded (most of the decoding sequences are not included in this listing). The important thing to notice is how the function is decrementing the value at [ebp-0x10] and checking that it’s nonzero. You now have a full picture of what causes this bug.

CVariableSet::AddExtensionControlBlock is allocating what seems to be a 400-bytes-long buffer that receives the decoded string from Decode URLEscapes. The function is checking that the source string (which is in ASCII) is 400 characters long, but DecodeURLEscapes is writing the string in Unicode! Most likely the buffer in CVariableSet::AddExtensionControlBlock was defined as a 200-character Unicode string (usually defined using the WCHAR type). The bug is that the length comparison is confusing bytes with Unicode

270Chapter 7

characters. The buffer can only hold 200 Unicode characters, but the check is going to allow 400 characters.

As with many buffer overflow conditions, exploiting this bug isn’t as easy as it seems. First of all, whatever you do you wouldn’t be able to affect Decode URLEscapes, only CVariableSet::AddExtensionControlBlock. That’s because the vulnerable local variable is part of CVariableSet::Add ExtensionControlBlock’s stack area, and DecodeURLEscapes stores its local variables in a lower address in the stack. You can overwrite as many as 400 bytes of stack space beyond the end of the WCHAR local variable (that’s the difference between the real buffer size and the maximum bytes the boundary check would let us write). This means that you can definitely get to

CVariableSet::AddExtensionControlBlock’s return value, and probably to the return values of several calls back. It turns out that it’s not so simple.

First of all, take a look at what CVariableSet::AddExtensionControl Block does after DecodeURLEscapes returns. Assuming that the function succeeds, it goes on to perform some additional processing on the converted string (it calls DecodeHtmlNumeric and wcsupr to convert the string to uppercase). In most cases, these operations will be unaffected by the fact that the stack has been overwritten, so the function will simply keep on running. The trouble starts afterward, at 6e90088f when the function is reading the pointer to EXTENSION_CONTROL_BLOCK from [ebp+0x8]—there is no way to modify the function’s return value without affecting this parameter. That’s because even if the last bit of data transmitted is a carefully selected return address for CVariableSet::AddExtensionControlBlock, DecodeURLEscapes would still overwrite 2 bytes at [ebp+0x8] when it adds a Unicode NULL terminator.

This creates a problem because the function tries to access the EXTENSION _CONTROL_BLOCK before it returns. Corrupting the pointer at [ebp+0x8] means that the function will crash before it jumps to the new return value (this will probably happen at 6e900896, when the function tries to access offset +8 in that structure). The solution here is to use the exception handler pointer instead of the function’s return value. If you go back to the beginning of CVariable Set::AddExtensionControlBlock, you’ll see that it starts by setting EAX to 0x6e906af8 and then calls idq!_EH_prolog. This sequence sets up exception handling for the function. 0x6e906af8 is a pointer to code that the system will execute in case of an exception.

The call to idq!_EH_prolog is essentially pushing exception-handling information into the stack. The system is keeping a pointer to this stack address in a special memory location that is accessed through fs:[0]. When the buffer overflow occurs, it’s also overwriting this exception-handling data structure, and you can replace the exception handler’s address with whatever you wish. This way, you don’t have to worry about corrupting the