der weg ist mir egal (der weg ist das ziel)
Die Firmware enthält in der Datei /etc/init.d/S44-hostname eine "command injection"-Schwachstelle. Diese läßt sich nicht extern ausnutzen, trotzdem beginnt AVM nach der Meldung dieses Problems mit seiner Behebung. Diese Stelle ist aber in der vorliegenden Firmware noch geeignet, eigene Kommandos in den Start-Prozess einzubauen.
Dazu muß nur in der ar7.cfg im "hostname"-Eintrag eine passende Zeichenkette abgelegt werden. Das Schema wäre:
hostname = "fritz.box$(...eigene_kommandos ...)";
Allerdings muß man beachten, daß diese Datei ziemlich früh im Bootprozess aufgerufen wird und es nicht unbedingt sinnvoll ist, dort bereits die Modifikationen am System vorzunehmen. Besser ist es, wenn man dort nur die Vorbereitungen trifft, damit die debug.cfg oder irgendein anderes benutzerdefiniertes Skript irgendwo wieder in den Bootprozess einbezogen wird. In etwa so:
Code:
n=97;v=/var/$n;mkconfigfile $v $n;zcat $v|sh;rm $v
Diese wenigen Zeichen (der Platz im "hostname"-Statement ist begrenzt) reichen aus, um die in einer anderen Datei (diese hat keinen "Namen", es ist auch nur ein TFFS-Node wie die debug.cfg es normalerweise ist, nur mit einer anderen "Nummer") hinterlegten Kommandos im Rahmen der Abarbeitung der S44-hostname beim Start auch noch auszuführen. Das "zcat" schadet dabei normalerweise auch dann nicht, wenn die Datei gar nicht komprimiert ist. Die Komprimierung ist auch nicht unbedingt notwendig, das TFFS komprimiert selbst beim Schreiben ... manchmal ist aber ein 'gzip -9' doch etwas besser in der Kompression, besonders bei sehr großen Skripten. Wer beim Erstellen des TFFS-Node nicht komprimiert, kann auch 'cat' anstelle von 'zcat' verwenden.
Bevor wir uns jetzt dem Inhalt dieser Datei zuwenden, noch eine Bemerkung zum modifizierten Hostnamen. Diese Änderung ist über das Webinterface nicht möglich, da dort ordentlich geprüft wird, ob nur sinnvolle Zeichen enthalten sind. Allerdings funktioniert es mit 'ctlmgr_ctl' (auf anderen Geräten, ich habe selbst keine 7360 und schon gar nicht von diesem Provider) problemlos, solange man die notwendigen "escapes" einbaut:
Code:
# ctlmgr_ctl w box settings/hostname "fb\$(n=97;v=/var/\$n;mkconfigfile \$v \$n;zcat \$v|sh;rm \$v)"
# grep "^[ \t]*hostname =" /var/flash/ar7.cfg
hostname = "fb$(n=97;v=/var/$n;mkconfigfile $v $n;zcat $v|sh;rm $v)";
#
Aus diesem geänderten Hostnamen ergibt sich allerdings später (je nach Box und den verwendeten Funktionen) noch ein kosmetisches - leider an einigen Stellen unter Umständen offenbar auch funktionelles - Problem, dem widmen wir uns aber später. Wichtig ist allerdings noch das Wissen, daß natürlich jede Änderung des Hostnamens über das Webinterface die Änderung wieder überschreiben würde.
Den Inhalt einer beliebigen - vorbereiteten - Shell-Datei kann man z.B. so in die oben verwendete Datei schreiben:
Code:
# mkconfigfile /var/tmp/97 97
# cat [I]sourcefile[/I] >/var/tmp/97 oder
# gzip -9 [I]sourcefile[/I] | cat >/var/tmp/97 wenn man selbst komprimieren will
# rm /var/tmp/97
Diese Datei könnte jetzt - in Deinem Falle, wo keine Dateien auf USB-Speicher und/oder Netzwerk benötigt werden - schon die richtigen Kommandos enthalten. Aber es ist eben noch "sehr früh" im Startvorgang und mit einiger Wahrscheinlichkeit steht noch nicht einmal das lokale Netz zur Verfügung, der USB-Speicher schon gar nicht. Was man an dieser Stelle auch vermeiden sollte, sind bind-Mounts für Verzeichnisse nach /var, damit kommen einige Versionen der Firmware nicht immer klar, wenn sie dort später nach Mounts für USB-Geräte suchen.
Man kann natürlich auch darauf verzichten, die Kommandos bereits im Rahmen der S44-hostname abzuarbeiten und sich - mit einem etwas "intelligenteren" Skript - einfach an das "onlinechanged" anhängen, indem man das Kommando im Hostname-Eintrag so ändert:
Code:
n=97;v=/var;t=$v/$n;o=$v/tmp/onlinechanged;mkconfigfile $t $n;mkdir $o;zcat $t>$o/rc.user;rm $t
Dann muß man in den Zeilen in der "Datei 97" nur berücksichtigen, daß dieses Skript mehrmals (mit entsprechenden Parametern) aufgerufen wird/aufgerufen werden kann und der erste Aufruf aber erst erfolgt, wenn sich ein Wechsel des Online-Status vollzogen hat. Je nach den konkreten Umständen kann das sogar sinnvoller sein, als sich auf die debug.cfg zu verlassen, da bei deren Abarbeitung i.d.R. auch noch keine Onlineverbindung besteht (z.B. für einen OpenVPN-Client) und selbst die USB-Geräte nicht zwangsweise schon vorhanden sein müssen.
Eine mögliche Kommandofolge, um ganz profan die debug.cfg wieder auszuführen (am Ende der rc.tail.sh, wer das unbedingt an der "alten" Stelle haben will, muß eben das insert-Kommando ändern), wäre:
Code:
v=/var
e=/etc/
i=init.d
n=98
sed -e "/^exit 0/s_^.*_mkconfigfile $v/$n $n\ngunzip -c $v/$n|sh\nrm $v/${n}\n&_" -i $e$i/rc.tail.sh
Auch hier gilt natürlich wieder, daß man beim Speichern im TFFS ohne 'gzip' dann das 'gunzip' durch 'cat' ersetzen sollte.
Wenn alles gut geht (warum sollte es das nicht ?), wird damit die letzte Zeile der /etc/init.d/rc.tail.sh durch
Code:
mkconfigfile /var/98 98
gunzip -c /var/98|sh
rm /var/98
exit 0
ersetzt. Das ist ziemlich dicht dran an dem originalen AVM-Aufruf, wem das noch zu wenig ist, der kann sich ja selbst etwas ausdenken.
Bleibt noch das Problem, daß der modifizierte Hostname im GUI unschön aussieht und an einigen Stellen auch zu Problemen führt. Dafür gibt es sicherlich auch noch andere Lösungen, aber zwei will ich kurz anreißen.
Die erste Möglichkeit wäre es, die Änderung in der ar7.cfg noch in S44-hostname (also in "Datei 97", die ja dort abgearbeitet wird) zurückzunehmen und den Rest des Init-Prozesses dann wieder mit dem "richtigen" Hostnamen ablaufen zu lassen. Das beseitigt (wieder bei mir, die Einschränkung muß ich machen) das kosmetische Problem im GUI und auch ein mögliches Problem mit dem Namen der DECT-Basis (tritt bei mir nicht mit allen DECT-Endgeräten auf). Dann muß man nur zusätzlich einen Prozess einrichten, der die Schreibzugriffe des ctlmgr auf die ar7.cfg überwacht und dann einfach - quasi hinter dem Rücken des ctlmgr - direkt in der Datei den Eintrag wieder ergänzt, damit beim nächsten Booten dort wieder der richtige Inhalt (also inkl. Kommando) steht. Da der ctlmgr die Einstellungen aus seinem eigenen internen Abbild der ar7.cfg neu schreibt, muß das leider nach jedem Schreibzugriff erneut erfolgen und das belastet das NOR-Flash für's TFFS zusätzlich. Wer das mit dem internen Abbild einfach testen will, braucht bloss mal ein "echo -n >/var/flash/ar7.cfg" machen, sich die Datei ansehen (oops, nichts drin) und dann z.B. den Hostnamen wie oben per ctlmgr_ctl setzen ... die anderen Einstellungen sind dann auch wieder da; wer das ohne eigene Sicherung der ar7.cfg macht, ist trotzdem "nicht ganz dicht".
Auch wenn man jetzt annehmen könnte, daß Schreibzugriffe auf die ar7.cfg ja nicht so häufig erfolgen, befindet man sich da durchaus im Irrtum. Dort finden immer wieder mal Schreibzugriffe statt, wie man mit einer entsprechenden Überwachung etwas erstaunt feststellen wird. Daher ist das in meinen Augen die schlechtere Lösung, wenn man für jeden Schreibzugriff des ctlmgr dann gleich noch einen weiteren draufsetzt.
Da bietet sich dann eher der zweite Weg an. Die ar7.cfg wird einfach "virtualisiert", das meint, anstelle des char-Device für die ar7.cfg in /var/flash wird eine reguläre Datei mit demselben Inhalt erzeugt (der Hostname wird natürlich "zurückgesetzt") und diese dann vom ctlmgr benutzt. Dann muß man nur noch einen Mechanismus schaffen, der in regelmäßigen Abständen oder - wenn man es richtig "richtig" machen will - nur bei Schreibzugriffen durch den ctlmgr die Änderungen in den TFFS-Node überträgt. Wenn dieser Mechanismus dann auch noch eine Verzögerung beim "richtigen Schreiben" realisiert, kann er gleich noch die Schreibzugriffe im TFFS weiter reduzieren, da der ctlmgr durchaus auch schon mal 4 Zugriffe in der ersten Minute beim Starten der Box ausführt (je nachdem, was da alles passiert). Wenn man also erst 60 Sekunden nach der letzten Änderung schreibt, erspart man seinem NOR-Flash einige Zugriffe (und die ar7.cfg ist ja nicht gerade klein).
Für die Überwachung der Schreibzugriffe kann man auf die "inotify-tools" (bei Interesse hätte ich einen Patch zum Erstellen der statischen Binaries mit Freetz) oder auch eine Busybox mit inotifyd zurückgreifen, ansonsten macht man es halt mit Polling.
Das liest sich sicherlich alles erst einmal fürchterlich kompliziert, wenn es erst einmal umgesetzt ist, läuft es aber ganz easy und zuverlässig. Für eine Firmware, bei der man das root-FS nicht modifizieren kann/will, ist es eben - solange die CI-Lücke vorhanden ist - ein Ansatzpunkt.