首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>漏洞资料>文章内容
Jabberd2.x 服务器多个远程缓冲区溢出漏洞
来源:www.0x557.org 作者:icbm 发布时间:2004-11-26  

Jabberd2.x 服务器多个远程缓冲区溢出漏洞

安全公告: [AD_LAB-04002]Jabberd2.x remote Buffer Overflows
作者: icbm (icbm@venustech.com.cn)
CVE:CAN-2004-0953
影响:远程执行任意指令
漏洞类型:缓冲区溢出
发现日期:2004.6.1
受影响版本:
Jabberd2s2
Jabberd2s3
Jabberd2s4
未受影响版本:
Jabberd1.4*

I.Jabber技术简介:
----------------
Jabber技术的应用是由开源社区发起并一直领导的即时消息的实时系统。Jabber即时消息(IM)系统和现有IM服务相比较由以下几个关键特点:
1. XML为基础
2. 分布式网络
3. 开放的协议和内核代码
4. 模块化的、可扩展的系统架构
Jabber可以在一个使用共同协议的服务器组成的分布式网络上提供通信,连接这个网络的客户端,可以接收消息一样发送消息给同一个服务器或其他Internet上的服务器上的用户。Jabber通过两个附加功能提供这些IM标准特性,首先是一个允许消息系统间协同作业的开放协议。其次是建立在XML上的强大根本,它使得非但是两个人之间的通信,甚至是应用软件之间的通信成为了可能。

II.Jabberd服务器简介:
-------------
Jabber项目由Jeremie Miller在1998年开始的一个免费、开源的项目,用于提供给AIM、ICQ、MSN、Yahoo的IM服务。Jabber项目的核心通常都是jabberd服务器,一个用C编写,使用了GNU的pth线程库的模块化的服务器。jabberd 1.0在2000年五月发布。过渡性质的1.2版本于2000年十月发布,紧接着2001年1月发布了1.4版本,2001年4月发布1.4.1版本,2002年2月发布1.4.2版本,2003年11月发布1.4.3版本。在开发jabberd 1.4.x版本的同时,jabberd 2 也被开发出来,并在2003年12月有了首个稳定版本,2004年10月公布第四个稳定版本。Jabberd 2几乎重写了所有代码,比如SQL的支持。

III.漏洞简介:
-------------
Jabberd2.x中的C2S模块存在远程缓冲区溢出漏洞,C2S负责用户注册和认证了,远程攻击者可以在服务器进行用户认证前利用该漏洞使Jabberd服务崩溃,或在服务器上运行任意代码。

该漏洞的存在可导致攻击者绕过用户名和密码的长度检查,并提供恶意构造数据给mysql_real_escape_string, PqescapeString函数导致远程缓冲区溢出。

V.漏洞细节:
-----------

在Authreg.c(c2s)文件中Jabberd2.x服务器截断了用户名的长度:

snprintf(username, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));

所以我们可以得到username最长为1024字节,让我们来看看第一个漏洞:

在authreg_mysql.c中

static MYSQL_RES *_ar_mysql_get_user_tuple(authreg_t ar, char *username, char *realm) {
mysqlcontext_t ctx = (mysqlcontext_t) ar->private;
MYSQL *conn = ctx->conn;
char euser[513], erealm[513], sql[2049]; //euser and erealm only 513 long
MYSQL_RES *res;

if(mysql_ping(conn) != 0) {
log_write(ar->c2s->log, LOG_ERR, "mysql: connection to database lost");
return NULL;
}

mysql_real_escape_string(conn, euser, username, strlen(username));//Thers is the buffer overflow
mysql_real_escape_string(conn, erealm, realm, strlen(realm));//and there also has one.

我们可以看到mysql_real_escape_string ()函数中使用了我们的username字符串作为输入参数,经过处理后输出到euser里面,查阅mysql手册可知euser长度应该为(strlen(username)*2)+1,但这里的长度却使用用户提交数据的长度:strlen(username)直接导致了缓冲区溢出。

漏洞二:

在authreg_pgsql.c文件中我们发现了和authreg_mysql.c相同原理的漏洞,只不过使用了pgsql的函数PQescapeString:

char euser[513], erealm[513], sql[2049]; /* query(1024) + euser(512) + erealm(512) + \0(1) */
PGresult *res;

PQescapeString(euser, username, strlen(username));
PQescapeString(erealm, realm, strlen(realm));

所以如果用户在username字段里提交最少256字节的数据就有可能发生溢出,这个漏洞提交给Jabberd小组后他们做了修补:

static MYSQL_RES *_ar_mysql_get_user_tuple(authreg_t ar, char *username, char *realm) {
mysqlcontext_t ctx = (mysqlcontext_t) ar->private;
MYSQL *conn = ctx->conn;
char euser[2049], erealm[2049], sql[5121]; /* query(1024) + euser(2048) + erealm(2048) + \0(1) */ à这里把长度增加到了1024*2+1==2049,
MYSQL_RES *res;

if(mysql_ping(conn) != 0) {
log_write(ar->c2s->log, LOG_ERR, "mysql: connection to database lost");
return NULL;
}

mysql_real_escape_string(conn, euser, username, strlen(username));
mysql_real_escape_string(conn, erealm, realm, strlen(realm));
……

Jabberd开发小组把euser,realm变量长度增加到了1024*2+1==2049后,经过测试发现了第三个缓冲区溢出漏洞:

Jabberd在处理用户认证时的数据流如下:

_sx_sasl_scod_callback(sasl.c:501)
|
|----->_c2s_sx_sasl_callback(main.c:264)
|
|----->_ar_mysql_get_password(authreg_mysql.c:92)
|
|----->_ar_mysql_get_user_tuple(authreg_mysql.c:42)

通过上面的数据流分析发现了这个漏洞原因: 在_c2s_sx_sasl_callback(c2s/main.c)函数中服务器直接调用了有漏洞的数据库处理函数来处理用户提交的数据而没有对用户提交的数据作充分的长度检查。

VI.POC&补丁:
-------------------

Just a POC:)
#!/usr/bin/python
import xmpp
name = 'a'*10240
# Born a client
cl=xmpp.Client('localhost')
if not cl.connect(server=('192.168.10.138',5222)):
raise IOError('Can not connect to server.')
if not cl.auth(name,'jabberuserpassword','optional resource name'):
raise IOError('Can not auth with server.')
cl.disconnect()

Stephen Marquard gave a rapidly patch on this issue:

diff -ru c2sorig/authreg.c c2s/authreg.c
--- c2sorig/authreg.c Mon Nov 22 15:53:34 2004
+++ c2s/authreg.c Mon Nov 22 20:06:25 2004
@@ -623,7 +623,7 @@
log_write(c2s->log, LOG_NOTICE, "[%d] created user: user=%s; realm=%s", sess->s->tag, username, sess->realm);

/* extract the password */
- snprintf(password, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
+ snprintf(password, 257, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));

/* change it */
if((c2s->ar->set_password)(c2s->ar, username, sess->realm, password) != 0)
diff -ru c2sorig/authreg_mysql.c c2s/authreg_mysql.c
--- c2sorig/authreg_mysql.c Mon Nov 22 15:53:34 2004
+++ c2s/authreg_mysql.c Mon Nov 22 16:55:37 2004
@@ -24,6 +24,10 @@

#ifdef STORAGE_MYSQL

+#define MYSQL_LU 1024 /* maximum length of username - should correspond to field length */
+#define MYSQL_LR 256 /* maximum length of realm - should correspond to field length */
+#define MYSQL_LP 256 /* maximum length of password - should correspond to field length */
+
#include <mysql.h>

typedef struct mysqlcontext_st {
@@ -42,7 +46,8 @@
static MYSQL_RES *_ar_mysql_get_user_tuple(authreg_t ar, char *username, char *realm) {
mysqlcontext_t ctx = (mysqlcontext_t) ar->private;
MYSQL *conn = ctx->conn;
- char euser[2049], erealm[2049], sql[5121]; /* query(1024) + euser(2048) + erealm(2048) + \0(1) */
+ char iuser[MYSQL_LU+1], irealm[MYSQL_LR+1];
+ char euser[MYSQL_LU*2+1], erealm[MYSQL_LR*2+1], sql[1024 + MYSQL_LU*2 + MYSQL_LR*2 + 1]; /* query(1024) + euser + erealm + \0(1) */
MYSQL_RES *res;

if(mysql_ping(conn) != 0) {
@@ -50,8 +55,11 @@
return NULL;
}

- mysql_real_escape_string(conn, euser, username, strlen(username));
- mysql_real_escape_string(conn, erealm, realm, strlen(realm));
+ snprintf(iuser, MYSQL_LU+1, "%s", username);
+ snprintf(irealm, MYSQL_LR+1, "%s", realm);
+
+ mysql_real_escape_string(conn, euser, iuser, strlen(iuser));
+ mysql_real_escape_string(conn, erealm, irealm, strlen(irealm));

sprintf(sql, ctx->sql_select, euser, erealm);

@@ -127,15 +135,21 @@
static int _ar_mysql_set_password(authreg_t ar, char *username, char *realm, char password[257]) {
mysqlcontext_t ctx = (mysqlcontext_t) ar->private;
MYSQL *conn = ctx->conn;
- char euser[2049], erealm[2049], epass[513], sql[5633]; /* query(1024) + euser(2048) + erealm(2048) + epass(512) + \0(1) */
+ char iuser[MYSQL_LU+1], irealm[MYSQL_LR+1];
+ char euser[MYSQL_LU*2+1], erealm[MYSQL_LR*2+1], epass[513], sql[1024+MYSQL_LU*2+MYSQL_LR*2+512+1]; /* query(1024) + euser + erealm + epass(512) + \0(1) */

if(mysql_ping(conn) != 0) {
log_write(ar->c2s->log, LOG_ERR, "mysql: connection to database lost");
return 1;
}

- mysql_real_escape_string(conn, euser, username, strlen(username));
- mysql_real_escape_string(conn, erealm, realm, strlen(realm));
+ snprintf(iuser, MYSQL_LU+1, "%s", username);
+ snprintf(irealm, MYSQL_LR+1, "%s", realm);
+
+ password[256]= '\0';
+
+ mysql_real_escape_string(conn, euser, iuser, strlen(iuser));
+ mysql_real_escape_string(conn, erealm, irealm, strlen(irealm));
mysql_real_escape_string(conn, epass, password, strlen(password));

sprintf(sql, ctx->sql_setpassword, epass, euser, erealm);
@@ -195,15 +209,19 @@
static int _ar_mysql_set_zerok(authreg_t ar, char *username, char *realm, char hash[41], char token[11], int sequence) {
mysqlcontext_t ctx = (mysqlcontext_t) ar->private;
MYSQL *conn = ctx->conn;
- char euser[2049], erealm[2049], ehash[81], etoken[21], sql[5233]; /* query(1024) + euser(2048) + erealm(2048) + ehash(80) + etoken(20) + sequence(12) + \0(1) */
+ char iuser[MYSQL_LU+1], irealm[MYSQL_LR+1];
+ char euser[MYSQL_LU*2+1], erealm[MYSQL_LR*2+1], ehash[81], etoken[21], sql[1024+MYSQL_LU*2+MYSQL_LR*2+80+20+12+1]; /* query(1024) + euser + erealm + ehash(80) + etoken(20) + sequence(12) + \0(1) */

if(mysql_ping(conn) != 0) {
log_write(ar->c2s->log, LOG_ERR, "mysql: connection to database lost");
return 1;
}

- mysql_real_escape_string(conn, euser, username, strlen(username));
- mysql_real_escape_string(conn, erealm, realm, strlen(realm));
+ snprintf(iuser, MYSQL_LU+1, "%s", username);
+ snprintf(irealm, MYSQL_LR+1, "%s", realm);
+
+ mysql_real_escape_string(conn, euser, iuser, strlen(iuser));
+ mysql_real_escape_string(conn, erealm, irealm, strlen(irealm));
mysql_real_escape_string(conn, ehash, hash, strlen(hash));
mysql_real_escape_string(conn, etoken, token, strlen(token));

@@ -222,7 +240,8 @@
static int _ar_mysql_create_user(authreg_t ar, char *username, char *realm) {
mysqlcontext_t ctx = (mysqlcontext_t) ar->private;
MYSQL *conn = ctx->conn;
- char euser[2049], erealm[2049], sql[5121]; /* query(1024) + euser(2048) + erealm(2048) + \0(1) */
+ char iuser[MYSQL_LU+1], irealm[MYSQL_LR+1];
+ char euser[MYSQL_LU*2+1], erealm[MYSQL_LR*2+1], sql[1024+MYSQL_LU*2+MYSQL_LR*2+1]; /* query(1024) + euser + erealm + \0(1) */
MYSQL_RES *res = _ar_mysql_get_user_tuple(ar, username, realm);

if(res != NULL) {
@@ -237,8 +256,11 @@
return 1;
}

- mysql_real_escape_string(conn, euser, username, strlen(username));
- mysql_real_escape_string(conn, erealm, realm, strlen(realm));
+ snprintf(iuser, MYSQL_LU+1, "%s", username);
+ snprintf(irealm, MYSQL_LR+1, "%s", realm);
+
+ mysql_real_escape_string(conn, euser, iuser, strlen(iuser));
+ mysql_real_escape_string(conn, erealm, irealm, strlen(irealm));

sprintf(sql, ctx->sql_create, euser, erealm);

@@ -255,15 +277,19 @@
static int _ar_mysql_delete_user(authreg_t ar, char *username, char *realm) {
mysqlcontext_t ctx = (mysqlcontext_t) ar->private;
MYSQL *conn = ctx->conn;
- char euser[2049], erealm[2049], sql[5121]; /* query(1024) + euser(2048) + erealm(2048) + \0(1) */
+ char iuser[MYSQL_LU+1], irealm[MYSQL_LR+1];
+ char euser[MYSQL_LU*2+1], erealm[MYSQL_LR*2+1], sql[1024+MYSQL_LU*2+MYSQL_LR*2+1]; /* query(1024) + euser + erealm + \0(1) */

if(mysql_ping(conn) != 0) {
log_write(ar->c2s->log, LOG_ERR, "mysql: connection to database lost");
return 1;
}

- mysql_real_escape_string(conn, euser, username, strlen(username));
- mysql_real_escape_string(conn, erealm, realm, strlen(realm));
+ snprintf(iuser, MYSQL_LU+1, "%s", username);
+ snprintf(irealm, MYSQL_LR+1, "%s", realm);
+
+ mysql_real_escape_string(conn, euser, iuser, strlen(iuser));
+ mysql_real_escape_string(conn, erealm, irealm, strlen(irealm));

sprintf(sql, ctx->sql_delete, euser, erealm);

diff -ru c2sorig/authreg_pgsql.c c2s/authreg_pgsql.c
--- c2sorig/authreg_pgsql.c Mon Nov 22 15:53:34 2004
+++ c2s/authreg_pgsql.c Mon Nov 22 16:52:20 2004
@@ -26,6 +26,10 @@

#include <libpq-fe.h>

+#define PGSQL_LU 1024 /* maximum length of username - should correspond to field length */
+#define PGSQL_LR 256 /* maximum length of realm - should correspond to field length */
+#define PGSQL_LP 256 /* maximum length of password - should correspond to field length */
+
typedef struct pgsqlcontext_st {
PGconn * conn;
char * sql_create;
@@ -42,11 +46,16 @@
static PGresult *_ar_pgsql_get_user_tuple(authreg_t ar, char *username, char *realm) {
pgsqlcontext_t ctx = (pgsqlcontext_t) ar->private;
PGconn *conn = ctx->conn;
- char euser[2049], erealm[2049], sql[5121]; /* query(1024) + euser(2048) + erealm(2048) + \0(1) */
+
+ char iuser[PGSQL_LU+1], irealm[PGSQL_LR+1];
+ char euser[PGSQL_LU*2+1], erealm[PGSQL_LR*2+1], sql[1024+PGSQL_LU*2+PGSQL_LR*2+1]; /* query(1024) + euser + erealm + \0(1) */
PGresult *res;

- PQescapeString(euser, username, strlen(username));
- PQescapeString(erealm, realm, strlen(realm));
+ snprintf(iuser, PGSQL_LU+1, "%s", username);
+ snprintf(irealm, PGSQL_LR+1, "%s", realm);
+
+ PQescapeString(euser, iuser, strlen(iuser));
+ PQescapeString(erealm, irealm, strlen(irealm));

sprintf(sql, ctx->sql_select, euser, erealm);

@@ -114,11 +123,15 @@
static int _ar_pgsql_set_password(authreg_t ar, char *username, char *realm, char password[257]) {
pgsqlcontext_t ctx = (pgsqlcontext_t) ar->private;
PGconn *conn = ctx->conn;
- char euser[2049], erealm[2049], epass[513], sql[5633]; /* query(1024) + euser(2048) + erealm(2048) + epass(512) + \0(1) */
+ char iuser[PGSQL_LU+1], irealm[PGSQL_LR+1];
+ char euser[PGSQL_LU*2+1], erealm[PGSQL_LR*2+1], epass[513], sql[1024+PGSQL_LU*2+PGSQL_LR*2+512+1]; /* query(1024) + euser + erealm + epass(512) + \0(1) */
PGresult *res;

- PQescapeString(euser, username, strlen(username));
- PQescapeString(erealm, realm, strlen(realm));
+ snprintf(iuser, PGSQL_LU+1, "%s", username);
+ snprintf(irealm, PGSQL_LR+1, "%s", realm);
+
+ PQescapeString(euser, iuser, strlen(iuser));
+ PQescapeString(erealm, irealm, strlen(irealm));
PQescapeString(epass, password, strlen(password));

sprintf(sql, ctx->sql_setpassword, epass, euser, erealm);
@@ -177,11 +190,15 @@
static int _ar_pgsql_set_zerok(authreg_t ar, char *username, char *realm, char hash[41], char token[11], int sequence) {
pgsqlcontext_t ctx = (pgsqlcontext_t) ar->private;
PGconn *conn = ctx->conn;
- char euser[2049], erealm[2049], ehash[81], etoken[21], sql[5233]; /* query(1024) + euser(2048) + erealm(2048) + ehash(80) + etoken(20) + sequence(12) + \0(1) */
+ char iuser[PGSQL_LU+1], irealm[PGSQL_LR+1];
+ char euser[PGSQL_LU*2+1], erealm[PGSQL_LR*2+1], ehash[81], etoken[21], sql[1024 + PGSQL_LU*2 + PGSQL_LR*2 + 80 + 20 + 12 + 1]; /* query(1024) + euser + erealm + ehash(80) + etoken(20) + sequence(12) + \0(1) */
PGresult *res;

- PQescapeString(euser, username, strlen(username));
- PQescapeString(erealm, realm, strlen(realm));
+ snprintf(iuser, PGSQL_LU+1, "%s", username);
+ snprintf(irealm, PGSQL_LR+1, "%s", realm);
+
+ PQescapeString(euser, iuser, strlen(iuser));
+ PQescapeString(erealm, irealm, strlen(irealm));
PQescapeString(ehash, hash, strlen(hash));
PQescapeString(etoken, token, strlen(token));

@@ -210,7 +227,8 @@
static int _ar_pgsql_create_user(authreg_t ar, char *username, char *realm) {
pgsqlcontext_t ctx = (pgsqlcontext_t) ar->private;
PGconn *conn = ctx->conn;
- char euser[2049], erealm[2049], sql[5121]; /* query(1024) + euser(2048) + erealm(2048) + \0(1) */
+ char iuser[PGSQL_LU+1], irealm[PGSQL_LR+1];
+ char euser[PGSQL_LU*2+1], erealm[PGSQL_LR*2+1], sql[1024+PGSQL_LU*2+PGSQL_LR*2+1]; /* query(1024) + euser + erealm + \0(1) */
PGresult *res;

res = _ar_pgsql_get_user_tuple(ar, username, realm);
@@ -221,8 +239,11 @@

PQclear(res);

- PQescapeString(euser, username, strlen(username));
- PQescapeString(erealm, realm, strlen(realm));
+ snprintf(iuser, PGSQL_LU+1, "%s", username);
+ snprintf(irealm, PGSQL_LR+1, "%s", realm);
+
+ PQescapeString(euser, iuser, strlen(iuser));
+ PQescapeString(erealm, irealm, strlen(irealm));

sprintf(sql, ctx->sql_create, euser, erealm);

@@ -249,11 +270,15 @@
static int _ar_pgsql_delete_user(authreg_t ar, char *username, char *realm) {
pgsqlcontext_t ctx = (pgsqlcontext_t) ar->private;
PGconn *conn = ctx->conn;
- char euser[2049], erealm[2049], sql[5121]; /* query(1024) + euser(2048) + erealm(2048) + \0(1) */
+ char iuser[PGSQL_LU+1], irealm[PGSQL_LR+1];
+ char euser[PGSQL_LU*2+1], erealm[PGSQL_LR*2+1], sql[1024+PGSQL_LU*2+PGSQL_LR*2+1]; /* query(1024) + euser + erealm + \0(1) */
PGresult *res;

- PQescapeString(euser, username, strlen(username));
- PQescapeString(erealm, realm, strlen(realm));
+ snprintf(iuser, PGSQL_LU+1, "%s", username);
+ snprintf(irealm, PGSQL_LR+1, "%s", realm);
+
+ PQescapeString(euser, iuser, strlen(iuser));
+ PQescapeString(erealm, irealm, strlen(irealm));

sprintf(sql, ctx->sql_delete, euser, erealm);

VII.感谢:
------------

感谢"Niubi" Houzi!, Alex, Simon and rob@cataclysm.cx对我的帮助,感谢Stephen Marquard对该安全事件的快速响应。
感谢Sam, Air, Kkqq, Swan, Flashsky, S0f, Cjj, keji 与所有启明星辰积极防御试验室同事.
And my LeiLei...:*

VIII.关于:
------------

启明星辰:
诚信为先、技术领先、服务本地化、用户第一,国际一流的TSP:
                Product (安全产品)
Trusted(诚信)Security(安全){Solution(解决方案)} Provider(提供商)
                Service (安全服务)

Venustech 积极防御实验室:
启明星辰积极防御实验室多年来专注于网络安全技术研究,具有一支较高安全职业素质、多方面研究技能专长的优秀团队。在计算基础设施安全缺陷研究;黑客及病毒技术研究;防御保护技术研究等方面形成长期积累。拥有动态更新的应用于安全应急服务和产品核心策略的恶意代码知识库及漏洞信息库。在安全漏洞机理研究方面取得多项研究成果,尤其在堆溢出漏洞利用通用方法研究及堆管理技术方面取得多项突破。



 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·XSOK环境变量本地命令执行漏洞
·N点虚拟主机管理系统 致命漏洞。
·南方数据企业网站管理系统V10.0
·动网(DVBBS)Version 8.2.0 后
·Solaris 10 telnet漏洞及解决
·破解无线路由器密码,常见无线密
·Nginx %00空字节执行php漏洞
·WinWebMail、7I24提权漏洞
·XPCD xpcd-svga本地缓冲区溢出漏
·Struts2多个漏洞简要分析
·ecshop2.72 api.php 文件鸡肋注
·Discuz!后台拿Webshell 0day
  相关文章
·多个浏览器嵌套数组sort()循环栈
·phpBB远程任意SQL注入漏洞
·Microsoft WINDOWS ani 光标文件
·JAF CMS多个安全漏洞
·Microsoft Windows winhlp32.exe
·Merak Mail Server远程验证用户
·Allied Telesyn AT-TFTP Server
·Microsoft Windows LoadImage AP
·Windows 2000系统终端服务器拒绝
·Linux 2.6.* 内核Capability LSM
·最新公布的动易4.03上传漏洞
·UBBThreads dosearch.php远程SQL
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved