[UNIX] Jabberd Remote Buffer Overflows

From: SecuriTeam (support_at_securiteam.com)
Date: 11/25/04

  • Next message: SecuriTeam: "[NT] Soldier of Fortune II Broadcast Memory Corruption Bug"
    To: list@securiteam.com
    Date: 25 Nov 2004 14:12:50 +0200
    
    

    The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
    - - promotion

    The SecuriTeam alerts list - Free, Accurate, Independent.

    Get your security news from a reliable source.
    http://www.securiteam.com/mailinglist.html

    - - - - - - - - -

      Jabberd Remote Buffer Overflows
    ------------------------------------------------------------------------

    SUMMARY

     <http://jabberd.jabberstudio.org/> Jabberd, the latest release of the
    popular open source messaging system based on the Jabber Protocol. It has
    been rewritten from the ground up to be scalable, architecturally sound
    and to support the latest protocol extensions coming out of the JSF. The
    goal of Jabber is to provide an XML protocol for synchronous and
    asynchronous communication for client to client, client to server and
    server to server messaging, although the primary use of Jabber is instant
    messaging (IM).

    A buffer overflow condition is present in the Jabberd code and allows a
    malicious attacker to crash the server or execute arbitrary code.

    DETAILS

    Vulnerable Systems:
     * Jabberd versions 2.x

    Immune Systems:
     * Jabberd version 1.4

    CVE Information:
     <http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0953>
    CAN-2004-0953 - Jabberd server component multiple buffer overflows

    The client to server component of the Jabberd server handles
    communications with the clients in roughly the following manner:
     * Connects to Jabber clients
     * Passes packets to the SM
     * Authenticates clients
     * Registers users
     * Triggers activity with the SM

    Note: The C2S component connects to the Authentication Data Package
    (authreg) in order to register and authenticate users.

    The vulnerability is made possible when a very long username and password
    strings are passed to the server component. This is due to the fact that
    the server does not perform adequate bounds checking and in fact uses the
    supplied login credentials directly with database related functions which
    are vulnerable to a buffer overflow.

    In the Authreg.c(c2s) file the Jabberd server bounds the length of the
    username like so:
    snprintf(username, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad,
    elem));

    So in fact it seems that the username string is properly truncated to a
    known value. However, when passed to '_ar_mysql_get_user_tuple()'
    function, a stack overflow could occur since the size of the static
    buffers used is smaller than 1024 chars and there is no additional
    truncation. In the authreg_mysql.c file:

    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.

    As the user should be (strlen(username)*2)+1 long so if the username or
    realm is longer than 256 bytes, a buffer overflow occurs. Even patching
    the bug by increasing the buffer sizes to 2049 (which should cover a
    1024-bytes username string), a buffer overflow is still possible. A part
    of the execution flow is illustrated below:

    _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)

    In the '_c2s_sx_sasl_callback()' function (c2s/main.c) the server is using
    the username string directly without performing any bounds checking.

    The problem is not limited to merely speaking to MySQL but the code that
    is speaking to PostgreSQL is also vulnerable. In authreg_pgsql.c:

    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));

    Which is clearly the same issue presented at the beginning of this
    article. A proof of concept is given at the end of this article which
    demonstrates the vulnerabilities.

    Proof of Concept:
    #!/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()

    Patch Availability:
    Stephen Marquard provided a patch for 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);

    ADDITIONAL INFORMATION

    The information has been provided by <mailto:icbm@0x557.org> icbm.

    ========================================

    This bulletin is sent to members of the SecuriTeam mailing list.
    To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
    In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

    ====================
    ====================

    DISCLAIMER:
    The information in this bulletin is provided "AS IS" without warranty of any kind.
    In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.


  • Next message: SecuriTeam: "[NT] Soldier of Fortune II Broadcast Memory Corruption Bug"

    Relevant Pages