====== Kismet auf dem Pi ====== Kismet soll auf dem Pi nicht zum "war-drive" zum Einsatz kommen, sondern ein wenig Überblick über die gewünschten und ggfls. auch unerwünschten Besucher im heimischen Umfeld geben. ====== Voraussetzungen ====== Ein paar Pakete und dev-Pakete sind für's kompilieren und für den "Betrieb" notwendig. Außerdem gibt es ein paar Tools, die einem den Umgang mit dem System erleichtern. # Voraussetzungen apt-get install build-essential wireshark libncurses5-dev libcap-dev \ libpcre3-dev libnl-3-dev libnl-genl-3-dev # Tools apt-get install vim rcconf ====== Kismet kompilieren ====== wget http://www.kismetwireless.net/code/kismet-2013-03-R1b.tar.gz tar xvf kismet-2013-03-R1b.tar.gz cd kismet-2013-03-R1b/ ./configure --with-suidgroup=pi --prefix=/usr/local/kismet --sysconfdir=/etc/kismet ########################## OUTPUT # ... Configuration complete: Compiling for: linux-gnueabihf (armv6l) C++ Library: stdc++ Installing as group: root Man pages owned by: man Installing into: /usr/local/kismet Setuid group: pi Terminal Control: ncurses Linux WEXT capture : yes OSX/Darwin capture : n/a (only OSX/Darwin) PCRE Regex Filters : yes pcap capture: yes airpcap control: n/a (only Cygwin/Win32) PPI log format: yes LibCapability (enhanced privilege dropping): no Linux Netlink: yes (mac80211 VAP creation) - libnl-3.0 libnl-genl-3.0 # ... ########################################################### make make suidinstall ====== Konfigurieren ====== # vi /etc/kismet/kismet.conf # ... logprefix=/var/log/kismet # ... ncsource=wlan0:type=rt73,forcevap=false,validatefcs=true # ... #ouifile=/etc/manuf ouifile=/etc/kismet/manuf # ... gps=false # ... Eine aktuelle Manufacture (manuf) Datei holen: cd /etc/kismet/ wget http://anonsvn.wireshark.org/wireshark/trunk/manuf # vi /etc/default/ifplugd # ... #INTERFACES="auto" INTERFACES="eth0" #HOTPLUG_INTERFACES="all" HOTPLUG_INTERFACES="" # ... ===== Startskript ===== Aus dem Skeleton-Skript (/etc/init.d/skeleton) lässt sich "auf die Schnelle" ein Startskript bauen, damit der kismet_server automatisch startet. Mit Hilfe von "rcconf" lässt sich dies dann auch in die entsprechende Systemkonfiguration einfügen. #! /bin/sh ### BEGIN INIT INFO # Provides: skeleton # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Example initscript # Description: This file should be used to construct scripts to be # placed in /etc/init.d. ### END INIT INFO # Author: Foo Bar # # Please remove the "Author" lines above and replace them # with your own name if you copy and modify this script. # Do NOT "set -e" # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/kismet/bin DESC="Kismet Server" NAME=kismet_server DAEMON=/usr/local/kismet/bin/kismet_server DAEMON_ARGS="--daemonize" PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.2-14) to ensure that this file is present # and status_of_proc is working. . /lib/lsb/init-functions # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; #reload|force-reload) # # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # #log_daemon_msg "Reloading $DESC" "$NAME" #do_reload #log_end_msg $? #;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac ====== plugins ====== Die Plugins werden im Source-Code mitgeliefert, müssen aber noch kompiliert werden. Man kann alle Plugins "auf ein Mal" kompilieren: cd /install/kismet-2013-03-R1b make plugins make plugins-install Benötigt werden für dieses "Projekt" aber nur das 'plugin-syslog' und das 'plugin-btscan'. ===== plugin-syslog ===== Explizites Kompilieren des plugin-syslog cd /install/kismet-2013-03-R1b/plugin-syslog export KIS_SRC_DIR=/install/kismet-2013-03-R1b # Pfad zu den Kismet-Sourcen mitgeben export KIS_DEST_DIR=/usr/local/kismet/plugins # Installations-Ziel # funktioniert so nicht. # Plugins werden nach /usr/local/kismet/lib/kismet installiert make make install ===== config ===== In der kismet.conf # vi /etc/kismet/kismet.conf # ... # servername=Kismet Server servername=kismet # => hiermit wird der Syslog-Tag gefüllt. Wenn nichts angegeben wird, # wird der Hostname genommen, der eh schon im Syslog steht. # ... # JB NEW -> # Syslog log types can be: # all All messages from Kismet are logged # none No messages from Kismet are logged # info INFO-class messages # error ERROR-class messages # fatal FATAL-class messages # alert ALERT-class messages syslogtype=all # <- JB NEW # ... #logtypes=pcapdump,gpsxml,netxml,nettxt,alert logtypes=alerts # wenn das plugin-syslog benutzt wird, kann auf die meissten logs verzichtet werden. ===== plugin-btscan ===== Bluetooth-Erkennung apt-get install bluetooth libbluetooth-dev cd /install/kismet-2013-03-R1b/plugin-btscan export KIS_SRC_DIR=/install/kismet-2013-03-R1b # Pfad zu den Kismet-Sourcen mitgeben make make install ====== Weiterverarbeitung / WhoIsWho ====== Zur Weiterverarbeitung sollen die Syslog-Meldungen in eine MySQL-DB geschrieben werden. Wir nutzen dazu das rsyslog-MySQL Modul und lassen den rsyslog direkt passend filtern. apt-get install rsyslog-mysql # ... # dbconfig-common # Konfigurieren der Datenbank für rsyslog-mysql mit dbconfig-common? Die automatische Konfiguration wird abgelehnt, da eine DB auf einem anderen System genutzt werden soll und weil zusätzliche Tabellen benötigt werden. ===== rsyslog ===== Die vom MySQL-Modul mitgebrachte /etc/rsyslog.d/mysql.conf kann gelöscht werden. # vi /etc/rsyslog.d/kismet.conf # MySQL Modul $ModLoad ommysql # 1 Tabelle - alles vom Kismet if $syslogtag startswith 'kismet:' then :ommysql:192.168.11.11,kismet,kismet,password # zweite Tabelle - nur Netzwerke und clients $template SystemEventsKismet,"insert into Incoming (FromHost, Facility, Priority, Message, DeviceReportedTime, ReceivedAt, InfoUnitID, SyslogTag ) values ('%HOSTNAME%', ' %syslogfacility%', '%syslogpriority%', '%msg%', '%timereported:::date-mysql%', '%timegenerated:::date-mysql%', %iut%, '%syslogtag%')",SQL #if $syslogtag startswith 'kismet:' then :ommysql:192.168.11.11,kismet,kismet,password;SystemEventsKismet :msg,contains,"new managed network" :ommysql:192.168.11.11,kismet,kismet,password;SystemEventsKismet :msg,contains,"new probe network" :ommysql:192.168.11.11,kismet,kismet,password;SystemEventsKismet :msg,contains,"new data network" :ommysql:192.168.11.11,kismet,kismet,password;SystemEventsKismet :msg,contains,"new ad-hoc network" :ommysql:192.168.11.11,kismet,kismet,password;SystemEventsKismet ===== Datenbank ===== Für die Datenbank brauchen wir, falls noch nicht vorhanden, einen MySQL-Server und sinnvollerweise PHPMyAdmin für die Verwaltung. apt-get install mysql-server apt-get install phpmyadmin Die entsprechende Datenbank kann dann über den Reiter "SQL" im PHPMyAdmin angelegt werden. Bitte vorher "mypassword" durch etwas sinnvolles ersetzen. ==== whoiswho.sql ==== -- -- Anlegen der DB und eines entsprechenden Users -- CREATE USER 'kismet'@'%' IDENTIFIED BY 'mypassword'; GRANT USAGE ON * . * TO 'kismet'@'%' IDENTIFIED BY 'mypassword' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ; CREATE DATABASE IF NOT EXISTS `kismet` ; GRANT ALL PRIVILEGES ON `kismet` . * TO 'kismet'@'%'; -- -------------------------------------------------------- -- -- Tabellenstruktur für Tabelle `Incoming` -- CREATE TABLE IF NOT EXISTS `Incoming` ( `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `CustomerID` bigint(20) DEFAULT NULL, `ReceivedAt` datetime DEFAULT NULL, `DeviceReportedTime` datetime DEFAULT NULL, `Facility` smallint(6) DEFAULT NULL, `Priority` smallint(6) DEFAULT NULL, `FromHost` varchar(60) DEFAULT NULL, `Message` text, `NTSeverity` int(11) DEFAULT NULL, `Importance` int(11) DEFAULT NULL, `EventSource` varchar(60) DEFAULT NULL, `EventUser` varchar(60) DEFAULT NULL, `EventCategory` int(11) DEFAULT NULL, `EventID` int(11) DEFAULT NULL, `EventBinaryData` text, `MaxAvailable` int(11) DEFAULT NULL, `CurrUsage` int(11) DEFAULT NULL, `MinUsage` int(11) DEFAULT NULL, `MaxUsage` int(11) DEFAULT NULL, `InfoUnitID` int(11) DEFAULT NULL, `SysLogTag` varchar(60) DEFAULT NULL, `EventLogType` varchar(60) DEFAULT NULL, `GenericFileName` varchar(60) DEFAULT NULL, `SystemID` int(11) DEFAULT NULL, `processid` varchar(60) NOT NULL DEFAULT '', `checksum` int(11) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`ID`), KEY `FromHost` (`FromHost`), KEY `checksum` (`checksum`), KEY `DeviceReportedTime` (`DeviceReportedTime`), KEY `EventID` (`EventID`), KEY `InfoUnitID` (`InfoUnitID`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=33 ; -- -------------------------------------------------------- -- -- Tabellenstruktur für Tabelle `Manufacturer` -- CREATE TABLE IF NOT EXISTS `Manufacturer` ( `MAC` varchar(20) NOT NULL, `ShortDescription` varchar(20) NOT NULL, `Description` text NOT NULL, PRIMARY KEY (`MAC`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- -------------------------------------------------------- -- -- Tabellenstruktur für Tabelle `Seen` -- CREATE TABLE IF NOT EXISTS `Seen` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `SyslogID` int(11) NOT NULL, `MAC` varchar(20) NOT NULL, `Time` datetime NOT NULL, `Name` varchar(50) NOT NULL, `Type` varchar(20) NOT NULL, `Encryption` varchar(10) NOT NULL, `Channel` int(3) NOT NULL, `BitRate` int(8) NOT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ; -- -------------------------------------------------------- -- -- Tabellenstruktur für Tabelle `SystemEvents` -- CREATE TABLE IF NOT EXISTS `SystemEvents` ( `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `CustomerID` bigint(20) DEFAULT NULL, `ReceivedAt` datetime DEFAULT NULL, `DeviceReportedTime` datetime DEFAULT NULL, `Facility` smallint(6) DEFAULT NULL, `Priority` smallint(6) DEFAULT NULL, `FromHost` varchar(60) DEFAULT NULL, `Message` text, `NTSeverity` int(11) DEFAULT NULL, `Importance` int(11) DEFAULT NULL, `EventSource` varchar(60) DEFAULT NULL, `EventUser` varchar(60) DEFAULT NULL, `EventCategory` int(11) DEFAULT NULL, `EventID` int(11) DEFAULT NULL, `EventBinaryData` text, `MaxAvailable` int(11) DEFAULT NULL, `CurrUsage` int(11) DEFAULT NULL, `MinUsage` int(11) DEFAULT NULL, `MaxUsage` int(11) DEFAULT NULL, `InfoUnitID` int(11) DEFAULT NULL, `SysLogTag` varchar(60) DEFAULT NULL, `EventLogType` varchar(60) DEFAULT NULL, `GenericFileName` varchar(60) DEFAULT NULL, `SystemID` int(11) DEFAULT NULL, `processid` varchar(60) NOT NULL DEFAULT '', `checksum` int(11) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`ID`), KEY `FromHost` (`FromHost`), KEY `checksum` (`checksum`), KEY `DeviceReportedTime` (`DeviceReportedTime`), KEY `EventID` (`EventID`), KEY `InfoUnitID` (`InfoUnitID`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=288 ; -- -------------------------------------------------------- -- -- Tabellenstruktur für Tabelle `WellKnown` -- CREATE TABLE IF NOT EXISTS `WellKnown` ( `MAC` varchar(20) NOT NULL, `Comment` text NOT NULL, PRIMARY KEY (`MAC`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ===== Webfrontend ===== apt-get install apache2 php5 php5-mysql Ein einfaches Webfront-End lässt sich schnell mit PHP erstellen. Natürlich braucht der Apache dafür noch eine entsprechende Config. # vi /etc/apache2/conf.d/whoiswho.conf Alias /whoiswho "/usr/local/whoiswho" Options +FollowSymLinks AllowOverride None # AuthName "WhoIsWho Access" # AuthType Basic # AuthUserFile /usr/local/whoiswho/etc/htpasswd.users # Require valid-user Unter /usr/local/whoiswho wird die eigentliche Webseite angelegt. mkdir /usr/local/whoiswho mkdir /usr/local/whoiswho/cron mkdir /usr/local/whoiswho/etc mkdir /usr/local/whoiswho/include #mkdir /usr/local/whoiswho/images # noch nicht benötigt ==== index.php ==== = 0 && $_GET['col'] < 5) { //Validitätsprüfung, nur Spalten 0..4 erlaubt $sort_column = $_GET['col']; } else { echo "

HANDS OFF MY URL!!!

"; $sort_column = 0; } } else { $sort_column = 0; }; if (isset($_GET['group'])) { $group = $_GET['group']; } else { $group = "no"; }; #error_log("INDEX.PHP GROUP: $group"); $title = "Who are my 'wireless' visitors"; $db = dbconnect(); $table = SEENTABLE; switch($sort_column) { case '0': $sql = "SELECT * FROM $table ORDER BY ID DESC"; $title = $title." (sort by ID)"; break; case '1': $sql = "SELECT * FROM $table ORDER BY TIME DESC"; $title = $title." (sort by Time)"; break; case '2': if ($group == "yes") { $sql = "SELECT * FROM $table GROUP BY MAC ASC"; $title = $title." (grouped by MAC)"; } else { $sql = "SELECT * FROM $table ORDER BY MAC ASC"; $title = $title." (sort by MAC)"; }; break; case '3': if ($group == "yes") { $sql = "SELECT * FROM $table GROUP BY Name DESC"; $title = $title." (grouped by Network name)"; } else { $sql = "SELECT * FROM $table ORDER BY Name DESC"; $title = $title." (sort by Network name)"; }; break; case '4': $sql = "SELECT * FROM $table ORDER BY Type DESC"; $title = $title." (sort by Network type)"; break; default: $sql = "SELECT * FROM $table ORDER BY Time DESC;"; $title = $title." (sort by Time)"; } ?> 'WhoIsWho' query($sql); while ($row = $sth->fetch()) { $comment = lookup_mac($row['MAC']); ?> ==== whoiswho.css ==== table { margin:0; padding:0; font-family: arial, verdana, serif; color: black; font-size: 11px; } table, tr, th, td { border-collapse: collapse; } caption { margin:0; padding:0; background: #f0f0f0; height: 25px; line-height: 25px; text-indent: 5px; font-family: arial, verdana, serif; font-weight: bold; color: black; font-size: 13px; text-align: left; /*letter-spacing: 3px;*/ border: solid 1px #c0c0c0; } thead th { height: 22px; line-height: 20px; text-align: left; color: black; font-size: 13px; background: #A2A2A2; } tbody tr { background: white; padding: 3px; } tbody tr:hover { background: #d0d0d0; /*text-decoration: underline;*/ } table a { /*color: #2c3763;*/ color: black; text-decoration: none; font-size: 11px; font-weight: bold; border-bottom: solid 1px black; } table a:hover { /*color: #2c3763;*/ color: black; font-weight: bold; text-decoration: underline; border-bottom: none; } table a:visited { /*color: #2c3763;*/ color: black; font-weight: bold; } ==== macinfo.php ==== 'WhoIsWho - MAC Info'
ID Time MAC   (group) Network name   (group) Network type Comment
&comment=","WhoIsWho MAC-Info","directories=0,titlebar=0,toolbar=0,location=0,status=0,menubar=0,scrollbars=yes,resizable=no,width=650,height=350"); return false;'> ","WhoIsWho Network-Info","directories=0,titlebar=0,toolbar=0,location=0,status=0,menubar=0,scrollbars=yes,resizable=no,width=950,height=350"); return false;'>
query($sql); $sql_rc = $db->errorCode(); error_log("$sql_rc\n"); while ($row = $sth->fetch()) { ?> ==== netinfo.php ==== 'WhoIsWho - Network Info'
Time Network name Network type Encryption Channel BitRate
","WhoIsWho Network-Info","directories=0,titlebar=0,toolbar=0,location=0,status=0,menubar=0,scrollbars=yes,resizable=no,width=950,height=350"); return false;'>
query($sql); $sql_rc = $db->errorCode(); error_log("$sql_rc\n"); while ($row = $sth->fetch()) { $comment = lookup_mac($row['MAC']); ?> ==== import-manuf.php ==== Auf dem Pi ist der I/O nicht wirklich schnell, weshalb es zu empfehlen ist, die "manuf"-Datei in eine entsprechende Tabelle der Datenbank zu importieren. Eine aktuelle "manuf"-Datei gibts unter https://code.wireshark.org/review/gitweb?p=wireshark.git;a=blob_plain;f=manuf;hb=HEAD ... abgelegt wird diese unter /usr/local/whoiswho/etc/. ==== mysql.inc.php ==== "SET NAMES 'UTF8'", PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION );*/ $opt = array( PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'" ); $dsn = sprintf('%s:host=%s;dbname=%s', DBDRIVER, DBHOST, DBNAME); #error_log("Connect with " . $dsn); $db = new PDO($dsn, DBUSER, DBPASS, $opt); return $db; } ==== tools.inc.php ==== query($sql); while ($row = $sth->fetch(PDO::FETCH_ASSOC)){ (isset($row['ShortDescription'])) ? $shortdescr = $row['ShortDescription'] : $shortdesc = "none"; (isset($row['Description'])) ? $descr = $row['Description'] : $descr = "none"; } if (isset($shortdescr) && isset($descr)) return array($shortdescr,$descr); } function lookup_mac($mac) { $db = dbconnect(); $table = WELLKNOWNTABLE; $sql = "SELECT * from $table WHERE MAC='$mac';"; $sth = $db->query($sql); while ($row = $sth->fetch(PDO::FETCH_ASSOC)) { $comment = "".$row['Comment'].""; } if (!isset($comment)) { #list($shortdescr,$descr) = lookup_manuf_file($mac); list($shortdescr,$descr) = lookup_manuf_db($mac); if (isset($shortdescr)) { if ($descr == "none") { $comment = $shortdescr; } else { $comment = $shortdescr." (".$descr.")"; } } else $comment = "unknown"; } return $comment; } ?>
Time MAC Network type Encryption Channel BitRate Comment
&comment=","WhoIsWho MAC-Info","directories=0,titlebar=0,toolbar=0,location=0,status=0,menubar=0,scrollbars=yes,resizable=no,width=650,height=350"); return false;'>