Die Bourne-Shell ist nichts Ungewöhnliches. Andere Befehlsinterpreter oder sogar eine Mischung davon, typischerweise Shell und etwas anderes, können verwendet werden. Natürlich führt das Hinzufügen von Sprachen zu einem komplexeren Quoting; jede Interpretationsebene fügt eine weitere Ebene des Quotings hinzu.
Als Beispiel erstellen wir ein kleines Skript, das verwendet wird, um die IP-Adressen zu finden, von denen aus ein ungültiger Anmeldeversuch unternommen wurde. Die Ausgabe sollte das iptables-Format haben. Als Skript, das auf sich selbst aufgerufen werden kann, ist die Aufgabe fast trivial. Das Skript könnte so aussehen:
#!/bin/bash lastb -i -f /var/log/btmp | awk ' $3 ~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ { cnt[$3]++ } END { for (ip in cnt) if (cnt[ip] > '"$NUM"') print ip "\t" cnt[ip] } ' | ( while read foo bar; do if ! grep -q " $foo/" /etc/sysconfig/iptables; then if [ $VERBOSE == "Y" ]; then echo "# $foo $bar"; fi echo "-A INPUT -s $foo/32 -j DROP" echo "-A OUTPUT -d $foo/32 -j DROP" fi; done ) | sort
In dem Skript wird davon ausgegangen, dass die beiden Variablen NUM and VERBOSE definiert wurden. Die erste Variable definiert einen Schwellenwert. Das Skript erkennt ungültige Anmeldeversuche, wenn die Anzahl der Versuche einer einzelnen IP-Adresse den Schwellenwert überschreitet, wird die IP Adresse gemeldet. Die VERBOSE Variable ergänzt den Output um weitere Informationen. Im Beispiel gehen wir davon aus, dass die beiden Variablen im Scheduling System gesetzt wurden.
Der erste Teil des Skripts verwendet awk, um die Anzahl der Einbruchsversuche pro IP-Adresse zu zählen. Es gibt sowohl die IP-Adresse als auch die Anzahl der Versuche aus. Dies wird wiederum von der Shell gelesen, die dann überprüft, ob die IP-Adresse nicht bereits blockiert ist. Ist dies nicht der Fall, liefert es die gewünschte Ausgabe.
Dieses Skript, wenn auch etwas ausgefeilter, wird tatsächlich verwendet, um Firewall-Regeln auf mehreren Systemen zu verwalten. Die Ausgabe für ein System sieht zum Beispiel so aus:
172.58.99.157 20 77.247.110.118 50 -A INPUT -s 172.58.99.157/32 -j DROP -A INPUT -s 77.247.110.118/32 -j DROP -A OUTPUT -d 172.58.99.157/32 -j DROP -A OUTPUT -d 77.247.110.118/32 -j DROP
Die ersten beiden Zeilen zeigen die Anzahl der Einbruchsversuche (20 und 50) von zwei Sources. Die nächsten 4 Zeilen zeigen die Zeilen, die der iptables-Konfiguration hinzugefügt werden können, um den gesamten Datenverkehr von diesen Sources zu blockieren.
Versuchen wir, das Skript in ein Ausführungsprogramm zu konvertieren. Ein guter Anfang besteht darin, einfach das Skript zu kopieren und dann die zusätzliche Ebene des Quotings hinzuzufügen. Der erste, aber syntaktisch falsche Schritt wäre:
run program = /bin/bash -c ' lastb -i -f /var/log/btmp | awk ' $3 ~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ { cnt[$3]++ } END { for (ip in cnt) if (cnt[ip] > '"$NUM"') print ip "\t" cnt[ip] } ' | ( while read foo bar; do if ! grep -q " $foo/" /etc/sysconfig/iptables; then if [ $VERBOSE == "Y" ]; then echo "# $foo $bar"; fi echo "-A INPUT -s $foo/32 -j DROP" echo "-A OUTPUT -d $foo/32 -j DROP" fi; done ) | sort '
Bei genauerer Betrachtung stellen wir fest, dass es hauptsächlich der awk-Code ist, der ein Problem verursacht, da er in einfache Anführungszeichen eingeschlossen ist. Um die Komplexität zu erhöhen, wird auch der Parameter NUM angesprochen, der vom Scheduling-Server aufgelöst werden muss.
Am Ende möchten wir einen String haben, der das Skript enthält, das von der Shell ausgeführt werden muss. Wir könnten also auch temporär /bin/echo anstelle von /bin/sh verwenden. Wenn der Job ausgeführt wird, wird das auszuführende Skript ausgegeben, anstatt das Skript auszuführen. Dies ermöglicht es uns, das Ausführungsprogramm zu testen, ohne den “heißen” Code tatsächlich auszuführen.
Als Zweites ändern wir das einfache Anführungszeichen nach awk und das Anführungszeichen nach dem awk-Code, sodass ein einfaches Anführungszeichen übrig bleibt. Dies kann erreicht werden, indem die Zeichenfolge in einfachen Anführungszeichen beendet und ein einfaches Anführungszeichen verkettet wird, das der Inhalt einer Zeichenfolge in doppelten Anführungszeichen ist:
run program = /bin/echo -c ' lastb -i -f /var/log/btmp | awk '"'"' $3 ~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ { cnt[$3]++ } END { for (ip in cnt) if (cnt[ip] > '"$NUM"') print ip "\t" cnt[ip] } '"'"' | ( while read foo bar; do if ! grep -q " $foo/" /etc/sysconfig/iptables; then if [ $VERBOSE == "Y" ]; then echo "# $foo $bar"; fi echo "-A INPUT -s $foo/32 -j DROP" echo "-A OUTPUT -d $foo/32 -j DROP" fi; done ) | sort '
Wenn wir dieses Script ausführen, sieht das Ganze schon viel besser aus:
-c lastb -i -f /var/log/btmp | awk ' $3 ~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ { cnt[$3]++ } END { for (ip in cnt) if (cnt[ip] > 25) print ip "\t" cnt[ip] } ' | ( while read foo bar; do if ! grep -q " $foo/" /etc/sysconfig/iptables; then if [ $VERBOSE == "Y" ]; then echo "# $foo $bar"; fi echo "-A INPUT -s $foo/32 -j DROP" echo "-A OUTPUT -d $foo/32 -j DROP" fi; done ) | sort
Der Parameter NUM wurde bereits korrekt ersetzt, nur der Parameter VERBOSE wird noch nicht richtig behandelt. Durch Hinzufügen einiger Anführungszeichen nach dem gleichen Prinzip wie im Fall des awk-Skriptteils erhalten wir dieses Run Program:
run program = /bin/bash -c ' lastb -i -f /var/log/btmp | awk '"'"' $3 ~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ { cnt[$3]++ } END { for (ip in cnt) if (cnt[ip] > '"$NUM"') print ip "\t" cnt[ip] } '"'"' | ( while read foo bar; do if ! grep -q " $foo/" /etc/sysconfig/iptables; then if [ "'$VERBOSE'" == "Y" ]; then echo "# $foo $bar"; fi echo "-A INPUT -s $foo/32 -j DROP" echo "-A OUTPUT -d $foo/32 -j DROP" fi; done ) | sort '
Und wenn der Job läuft (NUM ist auf 19, VERBOSE auf Y gesetzt), erzeugt er diesen Report:
103.147.3.118 20 118.100.180.76 20 155.94.145.191 20 160.124.49.170 20 164.90.133.183 20 185.235.43.158 20 187.170.254.186 20 39.118.192.132 20 58.96.209.38 20 81.70.149.90 20 95.210.130.95 20 -A INPUT -s 103.147.3.118/32 -j DROP -A INPUT -s 118.100.180.76/32 -j DROP -A INPUT -s 155.94.145.191/32 -j DROP -A INPUT -s 160.124.49.170/32 -j DROP -A INPUT -s 164.90.133.183/32 -j DROP -A INPUT -s 185.235.43.158/32 -j DROP -A INPUT -s 187.170.254.186/32 -j DROP -A INPUT -s 39.118.192.132/32 -j DROP -A INPUT -s 58.96.209.38/32 -j DROP -A INPUT -s 81.70.149.90/32 -j DROP -A INPUT -s 95.210.130.95/32 -j DROP -A OUTPUT -d 103.147.3.118/32 -j DROP -A OUTPUT -d 118.100.180.76/32 -j DROP -A OUTPUT -d 155.94.145.191/32 -j DROP -A OUTPUT -d 160.124.49.170/32 -j DROP -A OUTPUT -d 164.90.133.183/32 -j DROP -A OUTPUT -d 185.235.43.158/32 -j DROP -A OUTPUT -d 187.170.254.186/32 -j DROP -A OUTPUT -d 39.118.192.132/32 -j DROP -A OUTPUT -d 58.96.209.38/32 -j DROP -A OUTPUT -d 81.70.149.90/32 -j DROP -A OUTPUT -d 95.210.130.95/32 -j DROP
Teil 1: Einführung und einfache Anwendung
Teil 2: Fortgeschrittene Anwendung
Teil 3: Backticks
Teil 4: Andere Interpreter
Teil 5: Umgehung von Einschränkungen