Pulse 0x1 – Pidgin IRC TOPIC message DoS
Libpurple is the communication core of several IM clients, such as Pidgin and Adium, that allows communications over a great number of IM protocols.
A vulnerability that may allow a malicious IRC server to crash Pidgin, or other clients relying on libpurple, by means of a malformed TOPIC message, is present in the libpurple release of Pidgin 2.6.1, that is the latest release at the time of writing.
Such vulnerability is hereby discussed, but, for better comprehension, a brief overview of how a TOPIC message is handled in libpurple is needed.
The TOPIC message is an IRC message, used when the topic in an IRC channel (room) is changed or for retrieving information about the current topic. (ref. RFC1459 by J. Oikarinen)
The message:
:niceone TOPIC #room :relativistic binary pulsars\r\n
will be sent by the IRC server to the clients in the room, informing that the user niceone has set the topic for #room to “relativistic binary pulsars”. The CRLF sequence clearly marks the end of the message.
All the functions needed for handling IRC messages are located inside libpurple/protocols/irc in the pidgin source tree.
[1] The first function relevant to our discussion is read_input located into irc.c:
605: read input(struct irc_conn *irc, int len) |
It processes the message received via the irc_conn struct by searching for the first occurrence of ‘\n’ or ‘\r\n’ and replacing its first char with ‘\0’, in order to turn the message into a null-terminated string.
The function irc_parse_msg then is called at line 625 of irc.c:
625: irc_parse_msg(irc, cur) //cur points to the string start. |
[2] irc_parse_msg, located into parse.c, performs basic checks on the message syntax, and uses the command (TOPIC) for identifying the proper member into an array of structures (_irc_msgs[] in parse.c), describing the callback function for the specific message. The TOPIC message callback function is irc_msg_topic.
The structure reports also information on the information needed by the callback function, that are passed in an array of pointers.
A zero filled array of two pointers is initialized at:
691: args = g_new0(char *, strlen(msgent->format)) |
A ‘for loop’ parses the remaining part of the message and fills the args array, with the room name and the topic message.
Then the function irc_msg_topic is called:
723: (msgent->cb)(irc, msgent->name, tmp, args); |
[3] irc_msg_topic, located in msgs.c, directly uses the information available into args array, performing no checks and calls the function irc_mirc2txt with the topic string as argument:
449: topic = irc_mirc2txt (args[1]) |
[4] irc_msg_mirc2txt, located into parse.c, attempts to make a copy of the strings, without performing any check on the input parameter:
466: char *result = g_strdup (string); //args[1] is string here 467: for (i = 0, j = 0; result[i]; i++) { |
and then parses it using result as a pointer, without performing checks on it.
Now, onto the vulnerability!
Let’s consider now the following message:
:hostile TOPIC\r\n #r00m :dis-topic\r\n
In [1] it will be changed by read_input into:
:hostile TOPIC\0\n #r00m :dis-topic\r\n
In [2] it will pass the semantic checks performed by irc_parse_msg (only the presence of “:” and of at least a space is required).
irc_msg_topic will be correctly identified as the callback function, but the filling of the zero initialized args vector (with room name and topic) will fail, because of the null byte right after TOPIC.
In [3] irc_msg_topic will be called with a NULL filled array as argument
In [4] g_strdup will return NULL, because args[1] is NULL.
Finally, result will then be a NULL pointer and by dereferencing it in the following for loop, the process crashes with a SEGFAULT for accessing memory at addres 0x0.
The lack of checks for NULL pointers in irc_msg_topic and irc_mirc2txt led to a NULL ptr dereference vulnerability and to a possible Denial of Service.
It worths noticing that most of the callback functions described in _irc_msgs struct DO have checks for NULL input parameters.
Same reasoning and consequences applies also to the message:
:hostile TOPIC #r00m\r\n :dis-topic\r\n
that is able to trigger the vulnerability as well.
This has been also confirmed on Windows platform, below a screenshot of location where crash occurs, located inside libirc.dll
The highlighted instruction is where segfaults occurs, it is a pointer dereference via the eax register, that holds the NULL return value of the g_strdup function.
This vulnerability can be exploited by a malicious IRC server, or by a malicious party that is mitm’ing the connection (possibly a proxy).
It is not required that the client is inside a specific room for triggering the vulnerability, the message parsing will occur upon message reception, regardless of the client status. Just the connection to the IRC server is required.
Being a NULL pointer dereference vulnerability, its exploitation leads to a Denial of Service condition, but code execution seems not to be possible in this context.
A quickly coded proof of concept code is available here