
Tutorial: Plugins für den Glastopf
Inhaltsverzeichnis
1. Einleitung
2. Was wird gebraucht?
3. Ein Überblick: Die Struktur des Plugins
4. Los geht's: Das Plugin
5. Sprachenwechsel: Annehmen der Glastopf Daten in PHP
6. Ausgeben der Daten in PHP
7. Schlusswort
» Einleitung
Seit einiger Zeit kann man für den Glastopf Honeypot eigene Module/Plugins basteln. Genauso wie beim Amun Honeypot, werden diese in der Skriptsprache “Python” geschrieben.
Ich habe mich jetzt die letzten Tage ein klein wenig damit beschäftigt, vorallem, weil ich für meinem Blog gerne eine Statistk wollte.
Es hat mich überrascht, das es kaum Zeit brauchte, bis alles funktionierte. Die mitgelieferten Plugins, wie das Twitter oder IRC Plugin reichen vollkommen aus, um sich abzuleiten wie man was eigenes schreibt.
In diesem Tutorial will ich euch an einem Beispiel zeigen, wie man solch ein Plugin schreibt. Hierbei werden wir Python und PHP verwenden, um dies zu realisieren.
» Was wird gebraucht?
Um das Ganze hier schnell zu machen, zähle ich die Dinge, die benötigt werden mal eben in einer Liste auf:
- Grundlegende Kenntnisse in Python und PHP
- Python >= 2.5 auf dem Glastopf Server, PHP >= 5 auf dem Webserver, MySQLdb Modul für Python 2.5, MySQL Datenbank auf dem Glastopf Server System
- mysql.py in der glastopf.cfg aktiviert
- funktionierende Glastopf Revision >= 189 Installation
Wichtig ist vorallem das ihr MySQL auf eurem Glastopf Server installiert habt, sowie das Plugin in der Config aktiviert ist, da sonst ohne dies keine Statistiken erfasst werden und zum Webupdater gesendet werden können.
» Was programmieren wir jetzt?
Wir werden uns jetzt ein kleines Plugin für den Glastopf schreiben, was jede Stunde Statistiken übermittelt, ähnlich wie das Twitter Plugin.
Dieses Plugin übermittelt in diesem vorgegebenen Zeitraum folgendes: Anzahl der Unique IPs, Anzahl der Unique Paths und die Anzahl der Angriffe in den letzten 60 Minuten.
Um diese Sachen zu erfassen, werden wir zwei PHP Skripte benötigen, das Erste nimmt die Daten vom Glastopf entgegen, das Zweite gibt diese wieder aus.
» Ein Überblick: Die Struktur des Plugins
Sehen wir uns die Ordnerstruktur des Glastopfes einmal an:
glastopf@honeypot:/home/glastopf/topf$ ls
AUTHORS CREDITS README files modules res
CHANGELOG LICENSE conf log plugins webserver.py
Im Hauptordner finden wir einen “files”-Ordner, wo die Malware gespeichert wird, im “modules”-Ordner sind Topfeigene Module enthalten, der “conf”-Ordner beinhaltet die Konfigurationsdateien, im “log”-Ordner finden wie Logdateien und im letzten Ordner “plugins” sind unsere hausgemachten Plugins enthalten.
Uns interessiert jetzt eigentlich nur der “conf” und der “plugins”-Ordner. Die restlichen können wir ausser acht lassen. Theoretisch ist das Plugin, was wir schreiben ein Logging Module, wie das hauseigene Twitter Modul. Allerdings werden wir ein wenig tricksen, damit unser Plugin als Dataplugin durchgeht. (Wir könnten uns auch die Mühe machen und die “logger.py” im “modules”-Ordner editieren, damit wir unser Plugin im “logmodules”-Unterordner ablegen können.)
Das soll uns aber nicht weiter groß stören. Mal sehen ob da in Zukunft noch eine Unterteilung in Log – und Datahandling Module eingebettet wird. An sich ist es aber gar nicht mal schlecht, unser Plugin als Datahandling Modul zu bauen, da wir später noch die Möglichkeit haben, komplette Datensätze zu verschicken. (Aber dazu werde ich vielleicht irgendwann nochmal einen Artikel schreiben.)
Da wir flexibel sind, bekommt unser Plugin natürlich auch eine eigene Konfiguration. (Ob das jetzt UNBEDINGT von Nöten ist, ist ja erstmal egal.)
Dazu können wir jetzt schonmal in unserer IDE einen Unterordner “conf” sowie eine leere Datei “gsweb.cfg” anlegen. Danach folgt dann auch gleich noch der Unterordner “plugins”, in dem wir eine Datei “gsweb.py” anlegen.
Die PHP Dateien können wir erstmal in einem Unterordner “php” ablegen. Wie diese genannt werden, ist relativ egal, Vollständigkeitshalber nennen wir diese “gssubmit.php” und “gsshow.php”.
» Los geht’s: Das Plugin
Als erstes machen wir uns an die Python-Programmierarbeit. Um es einfacher zu gestalten, werde ich immer ein Stückchen Code posten und diesen dann erklären.
Schreiben wir uns zu allererst mal die benötigte Konfigurationsdatei, die wir gerade angelegt haben:
[gsweb]
UpdateURL: http://glastopfblog.tld/gssubmit.php # URL to the submit script
Wir ihr unschwer erkennen könnt, hat diese Konfiguration nur 2 Zeilen. Das “[gsweb]“ dient dazu, dass der ConfigReader erkennen kann, in welchem Abschnitt er gerade ist und “UpdateURL” ist die URL zu eurer PHP Datei, die Daten vom Glastopf entgegen nimmt.
Nur noch kurz abspeichern, und die Konfiguration wäre soweit geschafft.
Jetzt geht es an den Käse! Die “gsweb.py”!
Schauen wir uns zunächst die Imports an:
""" Importing modules """
import datetime
import urllib
import urllib2
import ConfigParser
import threading
import time
import MySQLdb
import string
from time import localtime, strftime
from settings import options
Diesen Abschnitt müsste ich eigentlich nicht weiter erklären, da er wichtige Module lädt, die für die Ausführung des Plugins benötigt werden. Unteranderem “urllib” und “urllib2″, die zur Übermittlung von HTTP Daten genutzt werden.
Wir bauen uns als erstes eine kleine Klasse.
def __init__(self):
config = ConfigParser.ConfigParser()
""" Reading MySQL Options """
config.read("conf/glastopf.cfg")
self.mysql_opts = {
"host" : config.get("mysql", "host"),
"port" : config.get("mysql", "port"),
"user" : config.get("mysql", "user"),
"pass" : config.get("mysql", "pass"),
"db" : config.get("mysql", "db")
}
""" Reading Glastopf Webupdate Options """
config.read("conf/gsweb.cfg")
self.UpdateURL = config.get("gsweb", "UpdateURL")
Hier lesen wir zuerst unsere beiden Konfigurationsdateien aus, einmal die für den MySQL Server und unsere Webupdater Konfiguration. Danach starten wir einen Timer, der in einem vorgegeben Zeitraum etwas ausführt. (Dazu später noch mehr.)
def update(self, data):
try:
""" POST new data to the Webupdate.php """
parameter = {'update' : data}
parameter = urllib.urlencode(parameter)
request = urllib2.Request(self.UpdateURL, parameter)
response = urllib2.urlopen(request)
if response.read(1024) != "SUCCESS":
print "GsWEB - Error: Couldn't update. (%s)" % (response.read(1024))
else: print "GsWEB: Successfully updated..."
except:
print "GsWEB - Error: Couldn't update."
pass
Nachdem wir nun alles initialisiert und ausgelesen haben, schauen wir uns die Funktion an, die unsere Daten vom Glastopf übermittelt.
Um den Glastopf vor unschönen Abstürzen zu bewahren, packen wir unseren Code in einen Try/Except-Block. Hier geben wir mit der “parameter”-Variable an, was an unsere “gssubmit.php”-Datei per POST gesendet wird. Hier gibt es eigentlich nicht mehr viel zu erklären, wir encoden die Daten, so dass wir sie danach senden können. Ist alles in Ordnung, sendet unsere PHP Datei “SUCCESS” zurück.
def dbconnect(self):
try:
mysql = MySQLdb.connect(
self.mysql_opts["host"],
self.mysql_opts["user"],
self.mysql_opts["pass"],
self.mysql_opts["db"],
int(self.mysql_opts["port"])
)
except MySQLdb.Error, e:
print "MySQL Error %d: %s" % (e.args[0], e.args[1])
pass
mysql.threadsafety = 2
return mysql
Auch hier ist nicht viel zu sagen. Die Funktion verbindet sich mit dem MySQL Server.
Hier kommen wir noch einmal zu einer interessanten Funktion, die ein wenig näher erläutert werden sollte. Mit dieser Funktion ziehen wir unsere Daten aus der MySQL Datenbank und setzen sie zu einem einheitlichen Datensatz zusammen, der später von unserem PHP Skript bearbeitet werden kann.
mysql = gsweb.dbconnect()
cursor = mysql.cursor()
""" sql1 = unique IPs, sql2 = unique paths, sql3 = attacks in 60 minutes """
sql = {
"1" : """
SELECT ip FROM log
GROUP BY ip
""",
"2" : """
SELECT vicpath FROM path
""",
"3" : """
SELECT id FROM log
WHERE attime > %s
"""
}
Hier verbinden wir uns zur MySQL Datenbank und legen dann fest, welche Queries gesendet werden sollen. Hier habe ich ein Dict als Variablentyp genommen, da es am sinnvollsten erscheint. (Klar, man könnte auch alle 3 SQL Queries in 3 verschiedene Variablen packen…)
Was die einzelnen Queries nun machen, steht im Kommentar.
""" first sql query for the IPs """
cursor.execute(sql['1'])
message = str(cursor.rowcount) + "|"
""" second sql query for the paths """
cursor.execute(sql['2'])
message = message + str(cursor.rowcount) + "|"
""" third sql query for the attacks """
lasttime = (datetime.datetime.now() - datetime.timedelta(minutes=60)).strftime("%Y-%m-%d %X")
try:
cursor.execute(sql['3'], (lasttime,))
except MySQLdb.Error, e:
print "MySQL Error %d: %s" % (e.args[0], e.args[1])
message = message + str(cursor.rowcount) + "|" + strftime("%a, %d %b %Y %H:%M:%S", localtime())
Jetzt führen wir diese Queries aus, packen das Result davon in eine Variable und fügen zusätzlich noch einen “I” als Delimiter hinzu.
Bei der “lasttime”-Variable wird festgestellt, welche Angriffe in den letzten 60 Minuten stattfanden.
Wenn wir alle 3 Results zusammengesetzt haben, senden wir noch Zeit und Datum mit.
""" close the MySQL connection and update """
mysql.close()
gsweb.update(message)
Ist alles getan, schließen wir die Verbindung zum MySQL Server und senden den Datensatz an unser PHP Skript.
Jetzt haben wir unsere Klasse soweit fertiggestellt. Aber bisher haben wir NUR die Klasse, die bisher noch nichts macht, ausser im Quellcode stehen. Wir brauchen also noch ein Stückchen Code, der mit der Klasse umgeht. Zu diesem Abschnitt kommen wir jetzt.
Oh, eine Funktion die gar nichts macht? Erinnert euch einmal zurück, ein Stück weiter oben hatte ich erwähnt, dass wir ein wenig tricksen müssen um unser Modul als Datahandlingplugin auszugeben. Die “dbwrite”-Funktion wird normalerweise vom Datahandler mit Daten gefüttert, die wir jetzt theoretisch noch weiterverarbeiten könnten. Da wir aber nur eine grobe Statistik führen wollen, fällt das ganze hier flach. Die Funktion habe ich nur mit in den Code genommen, damit uns der Glastopf keine Exception ausgibt.
class Timed():
def action(self):
if not self.canceled:
gsweb = toWeb()
gsweb.squawk_all()
time.sleep(1)
self.timed.start_timer()
def start_timer(self):
self.canceled = False
self.timed = Timed()
self.t = threading.Timer(3600, timed.action)
self.t.start()
def stop_timer(self):
self.t.cancel()
self.canceled = True
Diese Klasse dient als Timer. Sie sorgt quasi dafür, dass jede Stunde der Updateprozess gestartet wird und Daten an unser PHP Skript geschickt werden. Bedarf eigentlich auch keiner größeren Beschreibung.
gsweb = toWeb()
timed = Timed()
timed.start_timer()
def cancelit():
timed.stop_timer()
print "GsWeb - Webupdater plugin loaded..."
time.sleep(1)
Puh. Gleich habens wir geschafft. Die letzten Zeilen Code sind dafür da, die Klassen zu initialisieren und eine kurze Meldung auszugeben, dass das Plugin gestartet wurde.
» Sprachenwechsel: Annehmen der Glastopf Daten in PHP
Das Plugin für den Glastopf wäre soweit fertig. Aber ohne unser PHP Skript kann es noch nicht viel machen. Als erstes werden wir also ein Skript schreiben, dass die Daten entgegennimmt und diese in eine Datei schreibt. Klingt einfach. Ist es auch.
Bevor ihr anfangt, achtet bitte darauf, dass ihr der Datei, in die euer Skript schreiben soll, die richtigen Chmod Rechte gebt. Sonst wundert ihr euch am Ende, warum es nicht geht und sucht wahrscheinlich Stunden nach den Fehler.
<
;?php
/* Options */
$data = "gsweb.dat";
$trusted = array("127.0.0.1", "721.0.0.1");
In unserem PHP Skript werden wir die Optionen direkt festlegen, da eine Konfigurationsdatei hier überflüssig wäre. (Und ich ehrlich gesagt keine Ahnung hab, wie ich in PHP Konfigurationsdateien schreibe & auslese.)
Also, die “$data”-Variable gibt an, in welcher Datei die Daten abgelegt werden. Dieser solltet ihr die entsprechenden Chmod Rechte geben.
Bei der “$trusted”-Variable solltet ihr die IP Adresse von dem Server angeben, auf dem euer Glastopf läuft. Hier könnt ihr mehrere IP Adressen eintragen, die berechtigt sind, Daten zu speichern.
// Checking if the accessing IP address is a trusted source.
foreach ($trusted as $trust) {
if ($_SERVER['REMOTE_ADDR'] == $trust) {
$safe = TRUE;
break;
}
$safe = FALSE;
}
Hier fragen wir in einer Schleife ab, ob die IP Adresse die auf das Skript zugreift überhaupt berechtigt ist. Wenn das zutrifft, wird die “$safe”-Variable auf TRUE gesetzt und die Schleife unterbrochen.
// If the source is safe, proceed.
if ($safe) {
// Getting the data
$submit = $_POST['update'];
// Checking if the file is writable
if (is_writable($data)) {
if (!$submit) {
print "ERROR|NO_DATA";
exit;
}
if (!$handle = fopen($data, 'w')) {
print "ERROR|404";
exit;
}
// Can't write to file
if (fwrite($handle, $submit) == FALSE) {
print "ERROR|403";
exit;
}
// If wrote, SUCCESS and close the file
print "SUCCESS";
fclose($handle);
}
} else { print "<a href="/">back to home"; }
?
>
;
Wenn die Abfrage geklärt ist und alles in Ordnung ist, erhalten wir die gePOSTeteten Daten, checken ob die Datei schreibbar ist und falls dies so ist, geben wir “SUCCESS” zurück.
Falls unsere IP nicht berechtigt ist, wird einfach ein Link eingeblendet, der zurück auf die Hauptseite verweist. Damit wäre unser Skript, was die Daten entgegennimmt fertig.
» Ausgeben der Daten in PHP
Wir sind jetzt fast fertig mit unserem Plugin. Bevor wir uns dem Test widmen, müssen wir aber noch das Skript schreiben, dass den ganzen Kram ausgibt.
Das ist aber eine Sache von 5 Minuten.
<?php
/* Options */
$data = "gsweb.dat";
Auch hier haben wir wieder eine kurze Optionen-Sektion. Diese Gibt an, welche Datei den gelesen werden soll.
if (!$handle = fopen($data, 'r')) {
print "Error reading glastopf informations.";
exit;
}
else {
$line = fgets($handle, 1024);
$line = explode('|', $line);
fclose($handle);
print "$line[0] IPs in database
$line[1] Paths in database
$line[2] Attacks in the last hour
<span style="font
-size
:9px
;">(last updated: $line[3])";
}
?
>
;
Wir öffnen die Datei, splitten die Daten nach “|” und geben diese dann aus.
Fertig!
Jetzt müssen wir nur noch die “glastopf.cfg” bearbeiten und unter “dataplugins” unser Plugin “gsweb.py” einfügen.
Die “gsweb.cfg” wird ebenfalls im Config Ordner des Glastopfes abgelegt und unser “gsweb.py” in den Pluginordner.
Unsere beiden PHP Skripte legen wir jetzt auf unserem Webserver, auf dem auch ein Blog oder eine sonstige Webseite liegt ab. Unser Skript, dass die Daten ausliest, kann über einen PHP include leicht eingebunden werden. Sollte alles erledigt sein, kann der Glastopf wie gewohnt gestartet werden und die Meldung, dass unser Plugin geladen wurde, sollte zu sehen sein.
» Schlusswort
Ich hoffe das dass Tutorial verständlich rübergekommen ist und ihr euch einen Überblick darüber verschaffen konntet, wie ihr eigene Plugins für den Glastopf schreiben könnt.
Falls ihr noch Fragen habt oder irgendeinen Fehler im Artikel findet, schreibt einfach einen Kommentar und ich schaue, dass ich euch helfen kann.
Wie gesagt, das Tutorial deckt nur einen kleinen Teil ab und zeigt, wie man anfangen kann, den Glastopf selber zu erweitern. Wenn man es richtig nimmt, ist dieses Plugin eigentlich nur eine Modifikation des Twitter Plugins. Man kann mit den Daten natürlich noch viel mehr anstellen, unteranderem könnte man ein Plugin schreiben, dass die Angriffe Live protokolliert und automatisiert Abuse Meldungen an die Hoster sendet usw.
Falls der Code im Tutorial nicht funktionieren solltet, wieso auch immer, für denjenigen habe ich das Plugin nochmal gezippt hochgeladen.
Download hier => gsweb.zip
So, viel Spaß mit diesem Plugin, wenn ihr mal oben links schaut, seht ihr, dass ich dieses Plugin schon benutze und es soweit 1a funktioniert. Wenn ich Lust habe, werde ich den Artikel eventuell auch noch ins Englische übersetzen.
