Denial of Service Vulnerabilities

Fabian Yamaguchi fabian.yamaguchi at cs.uni-goettingen.de
Mon Feb 25 09:14:31 EST 2013


Hi Pidgin Security Team,

we would like to report some denial of service vulnerabilities we
found during our research on automatically identifying missing checks
in source code. Two of these crashes can be triggered by another user.

(1) msn_message_parse_payload:
==============================

When another user chatting via MSN sends a message containing a
malformed "Content-Type"-field, a NULL-pointer dereference can be
triggered in msn_message_parse_payload. Here is the affected code:

void
msn_message_parse_payload(MsnMessage *msg,
  const char *payload, size_t payload_len,
  const char *line_dem,const char *body_dem)
{

[...]
tokens = g_strsplit(*cur, ": ", 2);

key = tokens[0];
value = tokens[1]; // [1]

[...] //[2]

if (!strcmp(key, "Content-Type"))
{
char *charset, *c;

if ((c = strchr(value, ';')) != NULL) [2]
   {
	[...]
   }
[...]
}

If the header-field "Content-Type" does not contain the symbol ":",
then tokens[1] will be set to NULL as a result of the split-operation
at [1]. In consequence, value will be NULL. This
NULL-pointer then propagates to strchr at [2] where it is dereferenced
and causes a crash.

Code to trigger the crash has been attached.

(2) jabber_vcard_parse_avatar/jabber_vcard_parse:
=================================================

A missing check when parsing vcards supplied by other users allows
attackers to trigger a NULL-pointer dereference. Since vcards are
automatically requested from users appearing online, this allows
attackers to crash Pidgin simply by logging in. Here is the affected
code:

static void
jabber_vcard_parse_avatar(JabberStream *js, const char *from,
                          JabberIqType type, const char *id,
                          xmlnode *packet, gpointer blah)
{
        JabberBuddy *jb = NULL;
        xmlnode *vcard, *photo, *binval, *fn, *nick;
        char *text;

        if(!from)
                return;

        jb = jabber_buddy_find(js, from, TRUE);

        js->pending_avatar_requests =
g_slist_remove(js->pending_avatar_requests, jb);

        if((vcard = xmlnode_get_child(packet, "vCard")) ||
                        (vcard =
xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) {
                /* The logic here regarding the nickname and full name
is copied from
                 * buddy.c:jabber_vcard_parse. */
                gchar *nickname = NULL;
                if ((fn = xmlnode_get_child(vcard, "FN")))
                        nickname = xmlnode_get_data(fn);

                if ((nick = xmlnode_get_child(vcard, "NICKNAME"))) {
                        char *tmp = xmlnode_get_data(nick);
                        char *bare_jid = jabber_get_bare_jid(from); [1]
                        if (tmp && strstr(bare_jid, tmp) == NULL) { [2]
		[...]
}

The return value of jabber_get_bare_jid, which can be NULL, is not
checked at [1]. This value is then passed to strstr causing the NULL
pointer to be dereferenced at [2]. The crash can be triggered as
follows:

(1) The attacker logs in
(2) The user requests the attacker's vcard using a message such as the
following:

 <iq type='get' id='purple5f8085f8' to='attacker at jabber.org'>
                              <vCard xmlns='vcard-temp'/></iq>
(3) The attacker replies with a malformed message containing an
    invalid "from" attribute of the "iq"-tag, for example:

<iq type="result" id="purple5f8085f8" from="@"
to="user at jabber.org/dbe143fe">
<vCard xmlns="vcard-temp"><NICKNAME>X</NICKNAME></vCard></iq>


The same check is missing in buddy.c: jabber_vcard_parse, however, we
did not test whether it can be triggered as well.


(3) msn_parse_oim_xml
=====================

The return value of xmlnode_get_data is not checked at [1] allowing a
NULL-ptr to be passed to atoi at [2] in
msn_parse_oim_xml. To trigger the crash, a rogue server sends an
XML-message containing an "MD/E"-tag containing an empty "IU"-tag, for
example:

"<MD><E><IU/></E></MD>"

Here is the affected code:

static void
msn_parse_oim_xml(MsnOim *oim, xmlnode *node)
{
        xmlnode *mNode;
        xmlnode *iu_node;
        MsnSession *session = oim->session;

        g_return_if_fail(node != NULL);

        if (strcmp(node->name, "MD") != 0) {
                char *xmlmsg = xmlnode_to_str(node, NULL);
                purple_debug_info("msn", "WTF is this? %s\n", xmlmsg);
                g_free(xmlmsg);
                return;
        }

        iu_node = xmlnode_get_child(node, "E/IU");

        if (iu_node != NULL &&
purple_account_get_check_mail(session->account))
        {
                char *unread = xmlnode_get_data(iu_node); // [1]
                const char *passports[2] = {
msn_user_get_passport(session->user) };
                const char *urls[2] = { session->passport_info.mail_url };
                int count = atoi(unread); // [2]

                /* XXX/khc: pretty sure this is wrong */
                if (count > 0)
                        purple_notify_emails(session->account->gc,
count, FALSE, NULL,
                                NULL, passports, urls, NULL, NULL);
                g_free(unread);
        }
	[...]
}

(4) msn_soap_handle_body

The return value of xmlnode_get_data is not checked at [1]. This value
is then used in g_str_equal at [2], leading to a NULL-pointer
dereference in msn_soap_handle_body.To trigger the crash, a rogue
server sends an XML-message containing a "Fault"-tag containing an
empty "faultcode"-tag, for example:

"<Fault><faultcode/></Fault>"

Here is the affected code:

static gboolean
msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response)
{
        xmlnode *body = xmlnode_get_child(response->xml, "Body");
        xmlnode *fault = xmlnode_get_child(response->xml, "Fault");

        if (fault) {
                xmlnode *faultcode = xmlnode_get_child(fault,
                "faultcode");

                if (faultcode != NULL) {
                        char *faultdata = xmlnode_get_data(faultcode);
                        if (g_str_equal(faultdata, "psf:Redirect")) {
			[...]
}

(5) mxit_add_buddy:
===================

Finally, in mxit_add_buddy, the return-value of purple_base64_decode
is not checked at [1] and passed to mxit_send_invite at [2] where it
is used as an argument to snprintf. This is the only crash we did not
trigger as we ran out of time. I am reporting it nonetheless. Here is
the affected code:

void mxit_add_buddy( PurpleConnection* gc, PurpleBuddy* buddy,
PurpleGroup* group, const char* message )
{
        struct MXitSession*     session = (struct MXitSession*)
gc->proto_data;
        GSList*                         list    = NULL;
        PurpleBuddy*            mxbuddy = NULL;
        unsigned int            i;
        const gchar *           buddy_name = purple_buddy_get_name( buddy );
        const gchar *           buddy_alias = purple_buddy_get_alias(
buddy );
        const gchar *           group_name = purple_group_get_name( group );

        purple_debug_info( MXIT_PLUGIN_ID, "mxit_add_buddy '%s'
(group='%s')\n", buddy_name, group_name );

        list = purple_find_buddies( session->acc, buddy_name );
        if ( g_slist_length( list ) == 1 ) {
                purple_debug_info( MXIT_PLUGIN_ID, "mxit_add_buddy
(scenario 1) (list:%i)\n", g_slist_length( list ) );
                /*
                 * we only send an invite to MXit when the user is not
already inside our
                 * blist.  this is done because purple does an
add_buddy() call when
                 * you accept an invite.  so in that case the user is
already
                 * in our blist and ready to be chatted to.
                 */

                if ( buddy_name[0] == '#' ) {
                        gchar *tmp = (gchar*) purple_base64_decode(
buddy_name + 1, NULL );
                        mxit_send_invite( session, tmp, FALSE,
buddy_alias, group_name, message );
                        g_free( tmp );
                }
 	[...]
}


We hope that this information is useful to you.

Best Regards,

Fabian Yamaguchi and Christian Wressnegger
University of Goettingen

-------------- next part --------------
A non-text attachment was scrubbed...
Name: msn_message_parse_payload.tar.gz
Type: application/x-gzip
Size: 27215 bytes
Desc: not available
URL: <http://pidgin.im/cgi-bin/mailman/private/security/attachments/20130225/a96b1f55/attachment-0001.bin>


More information about the security mailing list