Talos Vulnerability Report

TALOS-2016-0143

Pidgin MXIT Suggested Contacts Memory Disclosure Vulnerability

June 21, 2016
CVE Number

CVE-2016-2375

DESCRIPTION

An exploitable out-of-bounds ready exists in the handling of the MXIT protocol in Pidgin. Specially crafted MXIT contact information sent from the server can result in memory disclosure.

CVSSv3 SCORE

5.3 CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

TESTED VERSIONS

Pidgin 2.10.11

PRODUCT URLs

https://www.pidgin.im/

DETAILS

In the function mxit_parse_cmd_suggestcontacts in the file mxit/protocol.c at line 2020 the number of attributes will be read from the incoming packet into the variable count.

2020 	count = atoi( records[0]->fields[3]->data );

This value is subsequently used as the bounds for a loop at line 2030 and the loop index is used as an array index at lines 2034-2036.

2030	for ( j = 0; j < count; j++ ) {

2034	fname = records[0]->fields[4 + j]->data;		/* field name */
		if ( records[i]->fcount > ( 2 + j ) )
			fvalue = records[i]->fields[2 + j]->data;	/* field value */

The pointers set at these locations will subsequently be used to read data, potentially resulting in an out-of-bounds read, copying data into results fields, for example at lines 2056-2059:

2056	else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) {
			/* nickname */
			g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) );
2059	}

Most of the out-of-bounds reads would simply result in a crash if a memory page is inaccessible since most information is not sent back to the server. However, when the add button is pushed, the following callback is called (defined in mxit/profile.c at lines 288-291):

static void mxit_search_results_add_cb( PurpleConnection *gc, GList *row, gpointer user_data )
{
	/* display add buddy dialog */
	purple_blist_request_add_buddy( purple_connection_get_account( gc ), g_list_nth_data( row, 0 ), NULL, g_list_nth_data( row, 1 ) );
}

The data in this row is set in mxit_show_search_results() in mxit/profile.c at lines 340-346:

340		row = g_list_append( NULL, g_strdup_printf( "#%s", tmp ) );
341		row = g_list_append( row, g_strdup( profile->nickname ) );
		row = g_list_append( row, g_strdup( profile->firstname ) );
		row = g_list_append( row, g_strdup( profile->lastname ) );
		row = g_list_append( row, g_strdup( profile->male ? "Male" : "Female" ) );
		row = g_list_append( row, g_strdup_printf( "%i", calculateAge( profile->birthday ) ) );
346		row = g_list_append( row, g_strdup( profile->whereami ) );

This means that the nickname will be sent to the function purple_blist_request_add_buddy() as last argument, which ends up calling:

ui_ops->request_add_buddy(account, username, group, alias);

Which should call the callback mxit_add_buddy() defined in roster.c at line 729:

void mxit_add_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* group, const char* message )

This function then sends the alias back as a message to the server at lines 754 and 759:

741		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 );
				if ( tmp ) {
					754	mxit_send_invite( session, tmp, FALSE, buddy_alias, group_name, message );
					g_free( tmp );
				}
			}
			else
759				mxit\_send\_invite( session, buddy_name, TRUE, buddy_alias, group_name, message );
760		}

TIMELINE

2016-04-13 - Vendor Notification
2016-06-21 - Public Disclosure

Credit

Discovered by Yves Younan of Cisco Talos.