[Info] Wie funktioniert eigentlich das Signieren der AVM-Firmware?

PeterPawn

IPPF-Urgestein
Mitglied seit
10 Mai 2006
Beiträge
15,300
Punkte für Reaktionen
1,760
Punkte
113
Nachdem nun bei der 7390 der Plugin-Mechanismus wiederauferstanden ist (das war er bei der internationalen Version ja schon für die 06.20, weil die Language-Datenbanken ausgelagert wurden) und auch die (sicheren) Updates der DECT-Komponenten immer wichtiger werden, weil sie meist ohne Interaktion mit dem Benutzer ablaufen, wird es Zeit, den AVM-Mechanismus zum Signieren der Image-Dateien mal etwas genauer zu untersuchen (bzw. eher zu erläutern). Auch die Tatsache, daß neue Firmware nur noch korrekt signierte Firmware-Images installieren will, trägt natürlich dazu bei, daß dieses Thema einmal mehr in den Mittelpunkt des Interesses (zumindest des meinigen) gerückt ist.

Das ist hier wieder eher "als Story" erzählt ... wer es kürzer und "als Referenz" will, muß es eben selbst erkunden.

Nicht zuletzt spielt die Verwendung einer wirklich originalen Firmware auch dann ein Rolle, wenn man mein modfs-Skript verwenden will, um seine NAND-basierte FRITZ!Box an die eigenen Wünsche anzupassen. Dieses Skript kann ja auf Wunsch auch eine aktuelle Firmware direkt vom FTP-Server von AVM laden ... bisher wird dabei allerdings nicht getestet, ob es sich wirklich um eine unveränderte Version von AVM handelt oder ob da schon jemand manipuliert hat.

Nun kann so eine Image-Datei ja auf verschiedenen Wegen in eine FRITZ!Box gelangen, wer schon einmal einen Blick in das Shell-Skript /sbin/start_dect_update.sh einer aktuellen Firmware-Version gewagt hat, dem wird früher oder später unweigerlich die Zeile
Rich (BBCode):
tr069fwupdate packet ${url} 2>>/var/tmp/dect_update_error.log >>/var/tmp/dect_update_out.log
ins Auge springen und nach etwas Analyse des Shell-Codes in dieser Datei stellt man dann schnell fest, daß damit offenbar ein solches Image (das ja nur ein TAR-Archiv mit einem "komischen Namen" ist) sowohl geladen (je nach URL) als auch geprüft und entpackt werden kann, denn nach dem Aufruf dieses Programms liegen die einzelnen Bestandteile so einer Image-Datei in /var vor, sofern die Datei korrekt signiert ist. Das gilt auch für die Datei /sbin/plugin_start.sh in der neuesten Version für die 7390, dort wird ebenfalls tr069fwupdate zur Prüfung benutzt:
Rich (BBCode):
install_plugins() {
[...]
echo "$0[$$]: Extract SOFTWARE-PLUGIN ${imountdir}/${plugin_updatefile}"
if [ -n "${isavefile}" ]; then
echo "$0[$$]: ... option savefile '${isavefile}' found"
tr069fwupdate packet ${imountdir}/${plugin_updatefile} ${isavefile}
else
tr069fwupdate packet ${imountdir}/${plugin_updatefile}
fi
if ! fw_error_text $? ; then
echo "$0[$$]: ACHTUNG: es ist ein Fehler aufgetreten."
rm -f ${isavefile}
return
fi
[...]
}
... offenbar sind wir also bei diesem Programm schon vor der richtigen Schmiede.

Für eine genauere Analyse der Vorgänge dort, müssen wir uns also erst einmal eine passende Datei besorgen, die wir als Eingabe für unsere Tests benutzen können. Ein komplettes Firmware-Image paßt hier schon deshalb nicht so richtig, weil es eine erkleckliche Größe hat und wir absehbar mit ein paar Kommandos wie hexdump, dd und auch strings umgehen müssen, die bei einer größeren Datei dann auch größere Ausgaben produzieren und in der Regel auch länger brauchen bei der Abarbeitung.

Daher böte es sich an, für diese Untersuchungen ein Update-Image für ein DECT-Gerät von AVM zu verwenden, denn dieses ist normalerweise etwas kleiner - allerdings eignet sich seit dem Erscheinen der 84.06.51 das dort enthaltene plugins.update (auch ein verkapptes TAR-Archiv) noch viel besser für die ersten Schritte, da im Gegensatz zu den für andere Images verwendeten öffentlichen Schlüsseln:
Rich (BBCode):
# ls -l /etc/avm_firmware_public_key*
-rw-r-----    1 root     root           266 May  8 22:44 /etc/avm_firmware_public_key1
-rw-r-----    1 root     root           266 May  8 22:44 /etc/avm_firmware_public_key2
-rwxr-xr-x    1 root     root           266 May  8 22:44 /etc/avm_firmware_public_key3
es für das Signieren der Plugins offenbar ein anderes Schlüsselpaar gibt, dessen öffentlicher Teil in einer weiteren Datei enthalten ist:
Rich (BBCode):
# cat /etc/plugin_global_key.pem
00bed5268d38b33fe9876f4ae22a5970657c3501adcb84879654def6fc83c1303667b12a031025782cb6490fed946ec81c3968ebc5d50697af9a2475339692eb5c84240cac09b2b3ca2a419efb6ae206e782209fc5a405054630634d4a4bb0f3c053c72547f2fb95add232929a7f722db94d873e02cbb2985106d6dd66dfa5592f
010001
Das hat also gleichzeitig den Vorteil, daß wir nicht erst raten müssen, welcher öffentliche Schlüssel denn nun zu einer Signatur gehören könnte ... die Annahme, daß es sich hier um den oben gezeigten handelt, ist ja naheliegend.

Also nehmen wir mal diese Datei plugins.update auf einer 7390 genauer unter die Lupe (sie wird ja beim Update auf die 06.51 automatisch an einer Stelle im Dateisystem abgelegt, auf die man notfalls auch mit den NAS-Funktionen zugreifen könnte), zuerst sehen wir uns einmal an, was dort an Dateien enthalten ist:
Rich (BBCode):
# ls -l /var/media/ftp/FRITZ/plugins/
-rwx--xr-x    1 root     root       1239040 Jan  1  1970 plugins.update
# tar -t -v -f /var/media/ftp/FRITZ/plugins/plugins.update
drwxr-x--- 0/0         0 2016-04-25 12:45:48 ./var/
-rwxrwxrwx 0/0       179 2016-04-25 12:45:48 ./var/install
-rwx------ 0/0   1228800 2016-04-25 12:45:48 ./var/plugin-wlan.image
-rwx------ 0/0      4096 2016-04-25 12:45:48 ./var/plugin-webcm_interpreter.image
-rw-r----- 0/0       128 2016-04-25 12:45:48 ./var/signature
So eine Datei mit dem Namen signature in der Image-Datei legt ja einen Zusammenhang mit unserem Thema nahe, auf den ersten Blick wirkt es natürlich etwas seltsam, wenn die Signatur-Datei selbst Bestandteil der signierten Datei ist. Das kann normalerweise ja nicht funktionieren ... beim Berechnen der Signatur (die i.d.R. ja eine mit einem privaten (RSA-)Schlüssel verschlüsselte DER-Struktur ist, die einen kryptographisch erzeugten Hash-Wert über die signierte Datei enthält) kann ja das endgültige Ergebnis (also der Inhalt dieser Signatur) noch nicht in deren Berechnung einfließen; da muß AVM offenbar irgendwelche "Kunstgriffe" verwenden, damit das am Ende funktioniert.

Nun steht zu vermuten, daß AVM auch nicht jedesmal das Rad neu erfindet und daher wird die Berechnung so einer Signatur sicherlich in irgendeiner Bibliothek enthalten sein, da bietet sich eine solche mit dem Namen libfwsign.so ja geradezu als "Verdächtige" an. Schauen wir dort also einmal nach, was diese Bibliothek einem Aufrufer so zu bieten hätte (das erfolgt jetzt auf einer 7490 und in dem dort installierten Labor-Image (33361 - mit modfs angepaßt), weil ich die Toolchain nur auf der 7490 verwende und sie daher auch nur für die 7490 erstellt habe, die 7390 ist dafür dann doch zu schwachbrüstig - daher können die Offsets in der Bibliothek von denen in der 7390 abweichen):
Rich (BBCode):
root@FB7490:~ $ /var/bintools/usr/bin/nm -D /lib/libfwsign.so
         U BN_hex2bn
         U ERR_get_error
         U MD5Final
         U MD5Init
         U MD5Update
         U RSA_free
         U RSA_new
         U RSA_verify
[...]
000013b0 T hexdump
[...]
000021f4 T my_BN_hex2bn
0000215c T my_RSA_free
00002174 T my_RSA_md5_verify
00002100 T my_RSA_new
00001f60 T signature_add_dirty_marker
00001d28 T signature_check
00001bdc T signature_get_crc
00001c68 T signature_set_crc
00001b10 T signature_stream_exit
00001438 T signature_stream_init
00001528 T signature_stream_write
Diese Bibliothek stellt also einige Funktionen zum Umgang mit einer Signatur zur Verfügung (die roten) und verwendet dazu offenbar kryptographische Standardfunktionen: MD5 - sicherlich für den Hash-Wert und RSA - womit man schon mal Daten bis zur maximalen Länge des Schlüsselpaars direkt signieren kann.

AVM verwendet 1024-Bit-RSA-Schlüssel, wie man oben anhand des Inhalts der /etc/plugin_global_key.pem ja sehen kann, die erste Zeile ist der Modulus und die zweite der Exponent des öffentlichen Schlüssel, das sehen wir später auch noch einmal in den Protokoll-Dateien von AVM.

Auf der anderen Seite paßt das auch hervorragend zu der Größe der Datei signature aus unserem Image, denn diese 1024 Bit sind ja genau die 128 Byte, die diese Datei groß ist und auch die von der libfwsign.so importierte Funktion BN_hex2bn paßt zu unserem Szenario, denn aus der Zeile mit den beiden Faktoren in der Datei mit dem öffentlichen Schlüssel müssen ja irgendwann wieder "big numbers" in der internen Darstellung werden, damit die Kryptographie-Funktionen damit umgehen können.

Auch die ebenfalls von der Bibliothek importierte Funktion RSA_verify paßt nahtlos zu den bisherigen Feststellungen (wobei man für das grundlegende Verständnis des AVM-Mechanismus nun nicht unbedingt auch mit den Kryptographie-Funktionen von OpenSSL programmieren können muß, das ist nur als "Nachweis" verlinkt).

Damit können wir (als Arbeitsthese) mal zwei Punkte festhalten:

1. Die Datei signature in so einer Image-Datei ist eine mit einem privaten RSA-Schlüssel erzeugte Signatur-Datei, der signierte Hash-Wert dürfte ein MD5-Hash über die Eingabedatei sein.
2. Dieses Verhalten müßte man auch mit "normalen Tools" nachbilden können, denn für das Signieren so einer Datei gibt es im OpenSSL-Paket auch entsprechende Programme.

Das läßt sich ja nun recht einfach mittels OpenSSL überprüfen, die dazu notwendigen Binaries kann man z.B. mit dem Freetz-Projekt erstellen.

Wenn es sich bei der signature um das vermutete Format handeln sollte, dann müßte diese Datei ja einfach zu "entschlüsseln" sein.

Dazu brauchen wir - neben einem möglichst kompletten OpenSSL-Programm, das eigentlich nur ein CLI für die libcrypto.so und libssl.so ist - noch die Signatur-Datei und den öffentlichen Schlüssel zu ihrer Überprüfung.

Erstere ist schnell extrahiert:
Rich (BBCode):
# mkdir /var/media/ftp/sigtest
# tar -x -f /var/media/ftp/FRITZ/plugins/plugins.update -O ./var/signature >/var/media/ftp/sigtest/signature
# cat /etc/plugin_global_key.pem >/var/media/ftp/sigtest/plugin_avm.key
# ls -l /var/media/ftp/sigtest
-rw-r--r--    1 root     root           266 Jun  6 17:22 plugin_avm.key
-rw-r--r--    1 root     root           128 Jun  6 17:20 signature
# cat /var/media/ftp/sigtest/plugin_avm.key
00bed5268d38b33fe9876f4ae22a5970657c3501adcb84879654def6fc83c1303667b12a031025782cb6490fed946ec81c3968ebc5d50697af9a2475339692eb5c84240cac09b2b3ca2a419efb6ae206e782209fc5a405054630634d4a4bb0f3c053c72547f2fb95add232929a7f722db94d873e02cbb2985106d6dd66dfa5592f
010001
Nun ist die Datei /etc/plugin_global_key.pem in der AVM-Firmware ja trotz ihres Namens keine Datei in PEM-Kodierung, wie man sie gewöhnlich im Umgang mit Kryptographie-Tools findet, dort handelt es sich beim PEM-Format um die Base64-kodierte DER-Struktur - also ASN.1-kodierte einzelne Werte in binärer Darstellung.

Für das Umwandeln einer Datei aus dem von AVM verwendeten Format in eine "richtige" PEM-Datei habe ich schon vor langer Zeit ein Shell-Skript erstellt und dieses (auch schon eine Weile her) in meinem GitHub-Repository veröffentlicht: https://github.com/PeterPawn/YourFritz/blob/master/signimage/avm_pubkey_to_pkcs8

Mit diesem Skript ist dann der öffentliche Schlüssel von AVM schnell in das passende Format konvertiert und einem Test der oben stehenden Hypothese zum Inhalt der Datei signature steht nichts mehr im Wege:
Rich (BBCode):
# cd /var/media/ftp/sigtest
# ./avm_pubkey_to_pkcs8 <plugin_avm.key >plugin_avm.pem
# openssl rsa -pubin -in plugin_avm.pem -text -noout
Public-Key: (1024 bit)
Modulus:
    00:be:d5:26:8d:38:b3:3f:e9:87:6f:4a:e2:2a:59:
    70:65:7c:35:01:ad:cb:84:87:96:54:de:f6:fc:83:
    c1:30:36:67:b1:2a:03:10:25:78:2c:b6:49:0f:ed:
    94:6e:c8:1c:39:68:eb:c5:d5:06:97:af:9a:24:75:
    33:96:92:eb:5c:84:24:0c:ac:09:b2:b3:ca:2a:41:
    9e:fb:6a:e2:06:e7:82:20:9f:c5:a4:05:05:46:30:
    63:4d:4a:4b:b0:f3:c0:53:c7:25:47:f2:fb:95:ad:
    d2:32:92:9a:7f:72:2d:b9:4d:87:3e:02:cb:b2:98:
    51:06:d6:dd:66:df:a5:59:2f
Exponent: 65537 (0x10001)
# openssl rsautl -in signature -verify -inkey plugin_avm.pem -pubin -asn1parse
    0:d=0  hl=2 l=  32 cons: SEQUENCE
    2:d=1  hl=2 l=  12 cons:  SEQUENCE
    4:d=2  hl=2 l=   8 prim:   OBJECT            :md5
   14:d=2  hl=2 l=   0 prim:   NULL
   16:d=1  hl=2 l=  16 prim:  OCTET STRING
      0000 - ee 4e f7 45 2f 76 b8 b9-51 31 43 72 f5 78 3d 8b   .N.E/v..Q1Cr.x=.
# ./avm_pubkey_to_pkcs8 </etc/avm_firmware_public_key1 >avm1.pem
# openssl rsautl -in signature -verify -inkey avm1.pem -pubin -asn1parse
RSA operation error
1996924008:error:0407006A:lib(4):func(112):reason(106):NA:0:
1996924008:error:04067072:lib(4):func(103):reason(114):NA:0:
Nach der Umwandlung des öffentlichen Schlüssels noch einmal schnell den korrekten Aufbau verifiziert (wenn OpenSSL den akzeptiert, kann da nichts falsch sein, außerdem stimmt der angezeigte Modulus mit dem Hexdump im AVM-File überein) und dann erst einmal mit diesem öffentlichen Schlüssel den Inhalt der signature geprüft ... das hat noch nichts mit der Prüfung der Image-Datei zu tun, es stellt nur sicher, daß die Datei signature mit dem zugehörigen privaten Schlüssel erzeugt wurde und die oben zu sehende ASN.1-Struktur "enthüllt" auch, daß die Annahme eines MD5-Hashes richtig war - der rote Teil ist der mit der Signatur gesicherte Hash-Wert der Eingabedatei. Die Gegenprobe mit einem anderen Schlüssel ergibt dann den erwarteten Fehler - bis hier sollte also alles halbwegs stimmig sein.

Schauen wir uns jetzt das Ergebnis so eines Aufrufs von tr069fwupdate mal etwas genauer an:
Rich (BBCode):
# tr069fwupdate packet file:///var/media/ftp/FRITZ/plugins/plugins.update 2>/var/media/ftp/sigtest/check.err >/var/media/ftp/sigtest/check.out;echo "rc=$?"
rc=0
Neben dem Return-Code erzeugt so ein Aufruf ja noch einiges an Dateien, schauen wir einmal, welche das sind:
Rich (BBCode):
# ls -lrt /var/tmp
[...]
-rw-r--r--    1 root     root            60 Jun  6 18:05 firmware_stream_result
-rw-r--r--    1 root     root           109 Jun  6 18:05 install_out.log
-rw-r--r--    1 root     root            58 Jun  6 18:05 install_error.log
-rw-r--r--    1 root     root          1222 Jun  6 18:05 fwsign.log
# ls -l /var/media/ftp/sigtest/
-rw-r--r--    1 root     root             0 Jun  6 18:05 check.err
-rw-r--r--    1 root     root             0 Jun  6 18:05 check.out
-rw-r--r--    1 root     root           266 Jun  6 17:22 plugin_avm.key
-rw-r--r--    1 root     root           128 Jun  6 17:20 signature
In unseren umgeleiteten Ausgabedateien steht offensichtlich nichts (die haben die Länge 0) - da werden tatsächlich nur Fehler beim Aufruf (z.B. ungültige Dateinamen) protokolliert. Das Ergebnis der Signaturprüfung wird wohl am ehesten in den Dateien in /var/tmp stehen, die Liste wurde oben nach Datum sortiert und nur die relevanten Einträge werden dargestellt. Sehen wir uns zunächst die (vermutlich) relevantesten Dateien an:
Rich (BBCode):
# cat /var/tmp/firmware_stream_result
total=1239040 ret=0 sigcrc=ee4ef7452f76b8b951314372f5783d8b
# cat /var/tmp/fwsign.log
md5: ee 4e f7 45 2f 76 b8 b9 51 31 43 72 f5 78 3d 8b
public num='00ab54b73f000e9fc5bf3c0d229e56ae1644507877ca1eaf364708975de1e50236754fdc8577bd9e9ec4c94bd595c22195a9cfa2ac57840c507b483ccf1c5d4d1448c6d8c8bdab629df4e5bcd65a52695064819d1f5157afeb8fea43dedd9c7b091c344cfb42434f5f7bc77bbf2c0469400d10a29d04d6c1b2807fd3be68800eaf'
public exp='010001'
public num='00c923d6cde5ca1780e84b6383c6c24b03a56532149f0a210541f16b1698d5761dd90ffd77500ff5dd2c9269710dad5ebcb1f6fbf318993429fcb228c043cc0980ec09b85b8a393c96b3e52f647b898ddff37aa9f662771aa87cee8686d3e2e3970a38e25bdc13f591344a2f6a39647a6555696fca21423e90c987e990ad64ff81'
public exp='010001'
public num='00f2ee9ffd8556211f5644da48a252b107124b330d4c20dcf3b9bac892924cabaa4df4f53e1c62e3f2aa12a23eb1d770df1520a998078738407e6a71b077f73ba976363836b880b0dd88741bc3b83ab061691226e823404b7fc88ed278d8130fe5336eb925c78f2f8ad7cb87d9586286f768ab3236fa8fb51ae7c4bbe1e041d849'
public exp='010001'
public num='00bed5268d38b33fe9876f4ae22a5970657c3501adcb84879654def6fc83c1303667b12a031025782cb6490fed946ec81c3968ebc5d50697af9a2475339692eb5c84240cac09b2b3ca2a419efb6ae206e782209fc5a405054630634d4a4bb0f3c053c72547f2fb95add232929a7f722db94d873e02cbb2985106d6dd66dfa5592f'
public exp='010001'
Hier finden wir also unseren MD5-Hash in den Protokolldateien wieder ... die fwsign.log enthält darüber hinaus noch weitere Einträge zu anderen öffentlichen Schlüsseln (erst der grüne ist der hier verwendete), wenn man die Daten vergleicht, stellt man schnell fest, daß es sich hier um den Inhalt der Dateien für das Signieren von anderen Images handelt:
Rich (BBCode):
# for d in /etc/avm_firmware_public_key*;do echo $d; cat $d; done
/etc/avm_firmware_public_key1
00ab54b73f000e9fc5bf3c0d229e56ae1644507877ca1eaf364708975de1e50236754fdc8577bd9e9ec4c94bd595c22195a9cfa2ac57840c507b483ccf1c5d4d1448c6d8c8bdab629df4e5bcd65a52695064819d1f5157afeb8fea43dedd9c7b091c344cfb42434f5f7bc77bbf2c0469400d10a29d04d6c1b2807fd3be68800eaf
010001
/etc/avm_firmware_public_key2
00c923d6cde5ca1780e84b6383c6c24b03a56532149f0a210541f16b1698d5761dd90ffd77500ff5dd2c9269710dad5ebcb1f6fbf318993429fcb228c043cc0980ec09b85b8a393c96b3e52f647b898ddff37aa9f662771aa87cee8686d3e2e3970a38e25bdc13f591344a2f6a39647a6555696fca21423e90c987e990ad64ff81
010001
/etc/avm_firmware_public_key3
00f2ee9ffd8556211f5644da48a252b107124b330d4c20dcf3b9bac892924cabaa4df4f53e1c62e3f2aa12a23eb1d770df1520a998078738407e6a71b077f73ba976363836b880b0dd88741bc3b83ab061691226e823404b7fc88ed278d8130fe5336eb925c78f2f8ad7cb87d9586286f768ab3236fa8fb51ae7c4bbe1e041d849
010001
Anscheinend wird also bei der Verwendung der libfwsign.so (oder auch von tr069fwupdate selbst) nach mehr als einem Kandidaten für den öffentlichen Schlüssel geschaut ... wenn keiner gefunden wird, gibt tr069fwupdate allerdings einen Return-Code ungleich 0 aus, hier wurde also (s.o.) offenbar ein passender Schlüssel zum Prüfen der Signatur gefunden.

(Wenn man das später weiter testet, stellt man fest, daß da die Dateien /etc/avm_firmware_public_key[1-9] der Reihe nach probiert werden (so sie denn vorhanden sind) und dann erst kommt die /etc/plugin_global_key.pem dran - das merken wir uns mal für später als interessantes Detail.)

Nun ist offenbar beim Erzeugen der fwsign.log die Prüfung schon erfolgt bzw. in vollem Gange, das wirft dann die Frage auf, was da in der /var/tmp/firmware_stream_result stehen mag.
Dem Augenschein nach handelt es sich bei total=1239040 ja um die Größe der Image-Datei (viel weiter oben mal markiert in einem CODE-Kasten), ret=0 ist sicherlich irgendein Return-Code und die Angabe nach sigcrc ist ja ohne Zweifel der MD5-Hash, der uns hier immer wieder über den Weg läuft.

Bleibt die Frage, wer diese Datei eigentlich erzeugt ... da tr069fwupdate bereits selbst die Signaturen überprüft, macht so eine "intermediate"-Datei für dieses Programm ja nur begrenzten Sinn, es ist also anzunehmen, daß sie von einer anderen durch tr069fwupdate aufgerufenen Komponente erzeugt wurde als Mittel der Kommunikation zwischen Prozessen. Stimmt diese Annahme, wird der Dateiname irgendwo in dieser Komponente auftauchen und somit auch per grep zu finden sein.

Das machen wir jetzt wieder an einer entpackten Dateisystemstruktur für die 7490 (und auf der 7490), weil so eine Suche über das procfs auf der FRITZ!Box nicht funktioniert und das dann eine ziemlich nervige Zeile für den Aufruf von grep würde. Also verwende ich dafür die auf der Box entpackte Struktur der 113.06.55-33361 - irgendwo habe ich mal das Skript extract_7490_new_filesystem hier veröffentlicht, mit dem man so eine Struktur auf einer 7490 selbst auspacken kann (wenn man kein Freetz nutzen will) - allerdings muß man auf der FRITZ!Box dann sicherstellen, daß sich keine "directory loops" ergeben, wenn man rekursiv suchen läßt.
Rich (BBCode):
root@fb7490:/var/media/ftp/system/FB7490/firmware/113.06.55-33361 $ grep -r firmware_stream_result *
Binary file usr/www/cgi-bin/firmwarecfg matches
Binary file usr/bin/tr069fwupdate matches
Das Ergebnis dürfte nicht allzuviel Interpretationsspielraum lassen ... ganz offensichtlich kommunizieren nur diese beiden Komponenten über diese Datei.

Also liegt die Vermutung nahe, daß tr069fwupdate (welches wir ja aufgerufen haben) seinerseits firmwarecfg aufruft. firmwarecfg ist nun ein CGI-Binary, das auch an diversen anderen Stellen im GUI für den Upload von Dateien verwendet wird, insofern wäre auch die Annahme, daß dort ebenfalls Funktionen zur Prüfung von Images enthalten sind (z.B. für das manuelle Update über eine Image-Datei), auch nicht so abwegig. Das war lange Zeit so etwas wie die eierlegende Wollmilchsau bei den binären Komponenten für das GUI - im Prinzip ist das sogar heute noch so, daß da alle möglichen Funktionen aus unterschiedlichen "Funktionsblöcken" enthalten sind.

Sehen wir jetzt mit strings mal in das Programm tr069fwupdate hinein, findet sich auch schnell der Aufruf von firmwarecfg:
Rich (BBCode):
# strings /usr/bin/tr069fwupdate | grep firmwarecfg
wget %s -O - "ftp://%s" 2>/var/dl_err | /usr/www/cgi-bin/firmwarecfg stream %s | tar xvf -
wget %s -O - "ftp://%s:%s@%s" 2>/var/dl_err | /usr/www/cgi-bin/firmwarecfg stream | tar xvf -
httpsdl%s -O - "%s" 2>/var/dl_err | /usr/www/cgi-bin/firmwarecfg stream %s | tar xvf -
httpsdl%s -O - "%s" "%s" "%s" 2>/var/dl_err | /usr/www/cgi-bin/firmwarecfg stream | tar xvf -
cat "%s" 2>/dev/null | /usr/www/cgi-bin/firmwarecfg stream %s | tar xvf -
Das hatten wir zwar früher schon festgestellt (bei der TR069-Geschichte) ... aber man sieht auch deutlich, daß offenbar irgendwie die Image-Datei auf verschiedenen Wegen bereitgestellt werden kann, aber am Ende landet sie immer in stdin für firmwarecfg, während die Ausgabe dann gleich per Pipe an das tar-Applet weitergeleitet und von diesem entpackt wird.

Spätestens seit der Version 06.30 der Firmware wissen wir auch, daß AVM da den Mechanismus beim Auspacken von Firmware-Images in firmwarecfg noch einmal geändert hat (bei der Verwendung von tr069fwupdate war da schon länger ein anderes Verzeichnis beim Aufruf eines der o.a. Kommandos aktiv, nämlich /var/packet), damit das Auspacken nicht mehr direkt nach /var erfolgt und dabei dann alle möglichen Dateien (im einzigen regulär beschreibbaren Verzeichnis unterhalb von /) überschrieben werden können.

Zwar wurde auch vorher schon sichergestellt, daß nur relativ zu ./var/ eingepackte Dateien überhaupt ausgepackt werden können (andere Pfade werden stur in /var/tmp/ignored_tar_content geändert von firmwarecfg stream, ebenso alle Dateien, die kein normales Verzeichnis/keine normale Datei sind oder das /../ für das übergeordnete Verzeichnis im Namen haben - obwohl das schon vom tar-Applet (inzwischen) verhindert würde), aber es gab immer wieder neue Ideen, wie man diesen Mechanismus zum Überschreiben von FRITZ!OS-Dateien verwenden könnte - schon früher wurde nach jedem der oben stehenden tar-Aufrufe die Datei /var/post_install aus dem SquashFS-Image erneuert, weil sie einfach durch so eine Image-Datei überschrieben werden konnte und dann beim Neustart des Routers Kommandos ausgeführt werden konnten. Seit der Verschärfung der "Sicherheitsbestimmungen" ist da jedenfalls (fast) alles wirklich sicherer geworden, weil nicht mehr mit dem Wurzelverzeichnis als Basis entpackt wird und die entpackten Dateien erst nach erfolgreicher Signaturprüfung nach / (bzw. /var) verschoben werden. Für die 06.30 hat dann auch AVM irgendwann mal diese beseitigte Schwachstelle beschrieben, nachdem der Finder sie auf der Full Disclosure-Mailingliste veröffentlicht hatte (bzw. parallel dazu, denn das war wohl abgestimmtes Vorgehen).

Ausgehend davon, daß der Aufruf von firmwarecfg das einzige Programm ist, was in den oben gefundenen Zeilen zwischen dem Download einer Datei und dem Auspacken steht, kann eigentlich nur innerhalb von firmwarecfg diese Umorganisation des Firmware-Images stattfinden und in der Tat stellt man bei direktem Aufruf von firmwarecfg fest, daß dort am TAR-Archiv "herumgepfuscht" wird:
Rich (BBCode):
# tar -t -v -f /var/media/ftp/FRITZ/plugins/plugins.update
drwxr-x--- 0/0         0 2016-04-25 12:45:48 ./var/
-rwxrwxrwx 0/0       179 2016-04-25 12:45:48 ./var/install
-rwx------ 0/0   1228800 2016-04-25 12:45:48 ./var/plugin-wlan.image
-rwx------ 0/0      4096 2016-04-25 12:45:48 ./var/plugin-webcm_interpreter.image
-rw-r----- 0/0       128 2016-04-25 12:45:48 ./var/signature
# rm /var/tmp/firmware_stream_result
# cat /var/media/ftp/FRITZ/plugins/plugins.update | /usr/www/cgi-bin/firmwarecfg stream | tar -t -v
drwxr-x--- 0/0         0 2016-04-25 12:45:48 ./var/unpack/
-rwxrwxrwx 0/0       179 2016-04-25 12:45:48 ./var/unpack/install
-rwx------ 0/0   1228800 2016-04-25 12:45:48 ./var/unpack/plugin-wlan.image
-rwx------ 0/0      4096 2016-04-25 12:45:48 ./var/unpack/plugin-webcm_interpreter.image
-rw-r----- 0/0       128 2016-04-25 12:45:48 ./var/unpack/signature
# cat /var/tmp/firmware_stream_result
total=1239040 ret=0 sigcrc=ee4ef7452f76b8b951314372f5783d8b
Wie man sehen kann, fügt firmwarecfg dem Pfad so einer Datei einfach eine weitere Komponente unpack hinzu, dieses Verzeichnis wird auch vorher ordentlich rekursiv gelöscht. Damit ist das Schreiben außerhalb von /var/unpack durch das tar-Applet recht schwierig, auch wenn das Wurzelverzeichnis das aktuelle wäre für den tar-Aufruf - bei tr069fwupdate ist das ohnehin nicht der Fall, dort wird ja /var/packet beim Aufruf der Kommandokette verwendet.

Auf der anderen Seite kann man auch sehen, daß die Datei /var/tmp/firmware_stream_result tatsächlich von firmwarecfg (oder einer dort aufgerufenen Komponente) erzeugt wird, denn sie ist nach vorherigem Löschen und Ausführung von firmwarecfg ja wieder da.

Wenn wir jetzt etwas weiter oben noch einmal nach den Dateien schauen, die so ein Aufruf von tr069fwupdate offensichtlich erzeugt, stechen einem die Dateien /var/tmp/install_out.log und /var/tmp/install_error.log ins Auge, die waren vorher ebenfalls nicht vorhanden.
Rich (BBCode):
# cat /var/tmp/install_out.log
install plugins ...
PLUGINS=/var/packet/var/plugin-webcm_interpreter.image /var/packet/var/plugin-wlan.image
# cat /var/tmp/install_error.log
mkdir: can't create directory '/var/plugins': File exists
Hier handelt es sich augenscheinlich um die Ausgabedateien bei der Abarbeitung der im Image enthaltenen Skript-Datei ./var/install ... wenn die Signatur paßt, ruft tr069fwupdate offenbar auch gleich noch die im Image enthaltene Skript-Datei auf. Das disqualifiziert dann tr069fwupdate irgendwie als Mittel der Wahl für eine eigene Prüfung eines AVM-Images - es ist sicherlich nicht im Sinne des Erfinders, wenn ein Firmware-Image mit gültiger Signatur im Rahmen von modfs auch gleich noch installiert wird. Selbst wenn das in die inaktive Partition erfolgen würde und sich durch das Umsetzen von linux_fs_start auf den ursprünglichen Wert wieder rückgängig machen ließe, ist das eine unnötige Belastung des Flash-Speichers, denn sicherlich soll eher eine modifizierte Firmware nach modfs verwendet werden.

Also müssen wir uns vielleicht selbst um die Auswertung des Ergebnisses von firmwarecfg stream in der /var/tmp/firmware_stream_result kümmern, wenn wir nur rein die gültige Signatur eines AVM-Images überprüfen wollen. Probieren wir doch einfach einmal aus, ob bereits der Aufruf von firmwarecfg stream zur Ausführung der enthaltenen ./var/install führt:
Rich (BBCode):
# rm /var/tmp/install_* /var/install
rm: can't remove '/var/install': No such file or directory
# ls -lrt /var/tmp
[...]
-rw-r--r--    1 root     root          1222 Jun  6 18:05 fwsign.log
-rw-r--r--    1 root     root            60 Jun  6 19:16 firmware_stream_result
-rw-r--r--    1 root     root            28 Jun  6 19:26 dnsd_servers
-rw-r--r--    1 root     root            27 Jun  6 19:26 avm-resolv.conf
-rw-r--r--    1 root     root            42 Jun  6 19:31 chrony.drift
# cat /var/media/ftp/FRITZ/plugins/plugins.update | /usr/www/cgi-bin/firmwarecfg stream | tar -t -v
drwxr-x--- 0/0         0 2016-04-25 12:45:48 ./var/unpack/
-rwxrwxrwx 0/0       179 2016-04-25 12:45:48 ./var/unpack/install
-rwx------ 0/0   1228800 2016-04-25 12:45:48 ./var/unpack/plugin-wlan.image
-rwx------ 0/0      4096 2016-04-25 12:45:48 ./var/unpack/plugin-webcm_interpreter.image
-rw-r----- 0/0       128 2016-04-25 12:45:48 ./var/unpack/signature
# ls -lrt /var/tmp
[...]
-rw-r--r--    1 root     root          1222 Jun  6 18:05 fwsign.log
-rw-r--r--    1 root     root            28 Jun  6 19:26 dnsd_servers
-rw-r--r--    1 root     root            27 Jun  6 19:26 avm-resolv.conf
-rw-r--r--    1 root     root            42 Jun  6 19:31 chrony.drift
-rw-r--r--    1 root     root            60 Jun  6 19:55 firmware_stream_result
Der Vergleich der Zeitstempel der erzeugten Protokolldateien macht schnell deutlich, daß von firmwarecfg tatsächlich nur die /var/tmp/firmware_stream_result geschrieben wird (neben der Ausgabe des modifizierten Archivs auf stdout). Für weitere Tests können wir uns also auch gleich auf den Aufruf von firmwarecfg stream beschränken.

Nun kommen wir wieder zu der Thematik zurück, wie eine Signatur gleichzeitig Bestandteil der signierten Datei sein könnte ... ohne besondere Vorkehrungen ist das schlicht nicht möglich, wie ich weiter oben schon mal angedeutet habe. Auch offenbart die Verwendung eines stinknormalen Programms zur Berechnung des MD5-Hashes für die plugins.update schnell, daß da offenbar der MD5-Hash nicht 1:1 über die Datei - so wie sie im Dateisystem liegt - gebildet wird, weder vor noch nach der Modifikation mit dem zusätzlichen unpack-Verzeichnis im Pfad:
Rich (BBCode):
# md5sum /var/media/ftp/FRITZ/plugins/plugins.update
c8cc0c757610a86c7bd26db526c7ea88  /var/media/ftp/FRITZ/plugins/plugins.update
# cat /var/media/ftp/FRITZ/plugins/plugins.update | /usr/www/cgi-bin/firmwarecfg stream | md5sum;cat /var/tmp/firmware_stream_result
08f680b247fc60bb0b4f24d6cfedeb69  -
total=1239040 ret=0 sigcrc=ee4ef7452f76b8b951314372f5783d8b
Die Änderung an den Pfaden bewirkt zwar auch eine Änderung des Hashes, aber der ist immer noch meilenweit von dem entfernt, was da firmwarecfg für die Datei berechnet hat.

Ausgehend davon, daß es eben nicht möglich ist, die Signatur selbst in die Berechnung der Signatur einzubeziehen, liegt ja die Vermutung nahe, daß da von firmwarecfg auch einfach bei der Berechnung des Hashes diese Datei ausgeblendet wird ... wenn diese Annahme stimmt, müßte ja auch die Berechnung des Hashes durch firmwarecfg dasselbe Ergebnis liefern, wenn man diese Datei im "Ausgangsmaterial" ändert - eine solche Änderung fällt dann logischerweise nicht auf, wenn der Hash ohne diese Datei berechnet wird. Das läßt sich ja schnell überprüfen:
Rich (BBCode):
# mkdir /var/tmp/sigtest
# cd /var/tmp/sigtest
# cp -a /var/media/ftp/FRITZ/plugins/plugins.update .
# hd plugins.update | grep -B 5 -A 40 "./var/signature"
0012ccf0  00 00 02 1d 00 00 00 00  00 00 00 00 00 00 02 e6  |................|
0012cd00  00 11 00 00 6a 06 7c f5  ac 51 6c 0f c2 1f ff fb  |....j.|..Ql.....|
0012cd10  7c 40 00 00 00 00 00 00  00 03 00 00 00 00 00 00  ||@..............|
0012cd20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012da00  2e 2f 76 61 72 2f 73 69  67 6e 61 74 75 72 65 00  |./var/signature.|
0012da10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012da60  00 00 00 00 30 31 30 30  36 34 30 00 30 30 30 30  |....0100640.0000|
0012da70  30 30 30 00 30 30 30 30  30 30 30 00 30 30 30 30  |000.0000000.0000|
0012da80  30 30 30 30 32 30 30 00  31 32 37 30 37 33 37 32  |0000200.12707372|
0012da90  35 33 34 00 30 31 32 30  35 34 00 20 30 00 00 00  |534.012054. 0...|
0012daa0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012db00  00 75 73 74 61 72 20 20  00 00 00 00 00 00 00 00  |.ustar  ........|
0012db10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012db40  00 00 00 00 00 00 00 00  00 30 30 30 30 30 30 30  |.........0000000|
0012db50  00 30 30 30 30 30 30 30  00 00 00 00 00 00 00 00  |.0000000........|
0012db60  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012dc00  99 a2 19 46 60 79 f1 07  f1 a7 5e 07 27 3d 74 58  |...F`y....^.'=tX|
0012dc10  32 ff a7 fc be f0 84 4d  03 7d c4 7e b1 f7 0c 86  |2......M.}.~....|
0012dc20  1b 88 db 73 4a 4b 6f d6  e8 06 5d 30 bb 9a 7c f0  |...sJKo...]0..|.|
0012dc30  f0 f6 33 44 73 fe 7a c8  ad 4f 81 16 b7 21 cb c8  |..3Ds.z..O...!..|
0012dc40  02 0b ec 33 db e8 ae 92  d5 cf c4 25 06 43 e2 40  |...3.......%.C.@|
0012dc50  e6 ee d3 e0 7f f7 41 2c  96 96 f5 b5 8d 90 71 c3  |......A,......q.|
0012dc60  03 c0 7b 83 94 38 47 c6  b0 16 33 79 dd b2 64 3e  |..{..8G...3y..d>|
0012dc70  8d 48 b5 ca 13 2e 99 97  6c 2b b0 8b 74 59 41 61  |.H......l+..tYAa|
0012dc80  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012e800
Die Datei ./var/signature ist die letzte im Archiv, der Header mit den Metadaten der Datei beginnt an Offset 0x12da00 und der eigentliche Inhalt der Datei beginnt am Offset 0x12dc00. Wenn der Inhalt der Datei tatsächlich nicht berücksichtigt wird, dürfte sich eine Änderung an diesem Inhalt nicht auf den Hash-Wert auswirken, den firmwarecfg berechnet/berechnen läßt (denn das macht sicherlich wieder die libfwsign.so). Also überschreiben wir den Inhalt einfach mit etwas anderem, ausgehend von der Angabe im total-Wert in der firmware_stream_result sollte man erst einmal nicht annehmen, daß die Datei einfach "ausgeschnitten" wird - solche abweichenden Längen machen das am Ende nur komplizierter als nötig:
Rich (BBCode):
# cp plugins.update plugins_test.update
# dd if=/dev/zero of=plugins_test.update bs=256 seek=$(( 0x12dc )) count=2 conv=notrunc
2+0 records in
2+0 records out
512 bytes (512B) copied, 0.000353 seconds, 1.4MB/s
# hd plugins_test.update | grep -B 5 -A 40 "./var/signature"
0012ccf0  00 00 02 1d 00 00 00 00  00 00 00 00 00 00 02 e6  |................|
0012cd00  00 11 00 00 6a 06 7c f5  ac 51 6c 0f c2 1f ff fb  |....j.|..Ql.....|
0012cd10  7c 40 00 00 00 00 00 00  00 03 00 00 00 00 00 00  ||@..............|
0012cd20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012da00  2e 2f 76 61 72 2f 73 69  67 6e 61 74 75 72 65 00  |./var/signature.|
0012da10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012da60  00 00 00 00 30 31 30 30  36 34 30 00 30 30 30 30  |....0100640.0000|
0012da70  30 30 30 00 30 30 30 30  30 30 30 00 30 30 30 30  |000.0000000.0000|
0012da80  30 30 30 30 32 30 30 00  31 32 37 30 37 33 37 32  |0000200.12707372|
0012da90  35 33 34 00 30 31 32 30  35 34 00 20 30 00 00 00  |534.012054. 0...|
0012daa0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012db00  00 75 73 74 61 72 20 20  00 00 00 00 00 00 00 00  |.ustar  ........|
0012db10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012db40  00 00 00 00 00 00 00 00  00 30 30 30 30 30 30 30  |.........0000000|
0012db50  00 30 30 30 30 30 30 30  00 00 00 00 00 00 00 00  |.0000000........|
0012db60  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012e800
# rm /var/tmp/firmware_stream_result;cat plugins.update | /usr/www/cgi-bin/firmwarecfg stream >/dev/null;cat /var/tmp/firmware_stream_result
total=1239040 ret=0 sigcrc=ee4ef7452f76b8b951314372f5783d8b
# rm /var/tmp/firmware_stream_result;cat plugins_test.update | /usr/www/cgi-bin/firmwarecfg stream >/dev/null;cat /var/tmp/firmware_stream_result
total=1239040 ret=0 sigcrc=ee4ef7452f76b8b951314372f5783d8b
# md5sum plugins_test.update
1862c662233643641f506eb9df43ad70  plugins_test.update
Die Änderung des Inhalts hat also keine Auswirkung auf den durch firmwarecfg berechneten MD5-Hash, aber der Hash über die geänderte Datei stimmt immer noch nicht mit dem berechneten Wert überein, also ist da offensichtlich noch etwas anders im Archiv.

Nun kann man den Inhalt der Metadaten für die ./var/signature zwar wohl tatsächlich "vorhersagen" und könnte ihn vermutlich auch in die Signatur mit einbeziehen, aber genauso gut kann man die natürlich auch auslassen ... wir suchen ja jetzt nach der maximal möglichen Änderung am Eintrag für die ./var/signature, die noch keine Auswirkung auf den von firmwarecfg berechneten Hash hat. Die Hälfte der denkbaren Änderungen haben wir schon durchgeführt (so eine Datei in einem TAR-Archiv hat immer eine Länge, die ein Vielfaches von 512 ist und die Metadaten stecken ebenfalls in einem Block dieser Länge), also ändern wir jetzt einfach die andere Hälfte auch noch und sollte das nicht zum Erfolg führen (weil sich der Hash-Wert von firmwarecfg dann doch ändert), liegt die Wahrheit irgendwo in der Mitte zwischen den beiden Änderungen:
Rich (BBCode):
# dd if=/dev/zero of=plugins_test.update bs=256 seek=$(( 0x12da )) count=2 conv=notrunc
2+0 records in
2+0 records out
512 bytes (512B) copied, 0.000356 seconds, 1.4MB/s
# hd plugins_test.update | grep -A 45 "^0012ccf0"
0012ccf0  00 00 02 1d 00 00 00 00  00 00 00 00 00 00 02 e6  |................|
0012cd00  00 11 00 00 6a 06 7c f5  ac 51 6c 0f c2 1f ff fb  |....j.|..Ql.....|
0012cd10  7c 40 00 00 00 00 00 00  00 03 00 00 00 00 00 00  ||@..............|
0012cd20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0012e800
Wir können nun natürlich nicht länger nach dem Namen im Hexdump suchen, aber man sieht oben, daß die kompletten Metadaten und der Inhalt (2x 512 Byte) mit binären Nullen überschrieben wurden. Was sagt denn firmwarecfg zu dieser Datei?
Rich (BBCode):
# rm /var/tmp/firmware_stream_result;cat plugins.update | /usr/www/cgi-bin/firmwarecfg stream >/dev/null;cat /var/tmp/firmware_stream_result
total=1239040 ret=0 sigcrc=ee4ef7452f76b8b951314372f5783d8b
# rm /var/tmp/firmware_stream_result;cat plugins_test.update | /usr/www/cgi-bin/firmwarecfg stream >/dev/null;cat /var/tmp/firmware_stream_result
total=1239040 ret=0 sigcrc=ee4ef7452f76b8b951314372f5783d8b
# md5sum plugins_test.update
ee4ef7452f76b8b951314372f5783d8b  plugins_test.update
Na also, für firmwarecfg sind die Dateien noch identisch und sogar das "Standardprogramm" zum Berechnen eines MD5-Hashes auf der Linux-Kommandozeile kommt zu demselben Ergebnis. Damit ist also nachgewiesen, daß von firmwarecfg bei der Berechnung des MD5-Hashes über ein Firmware-Image (bzw. über irgendeine TAR-Datei, die durch firmwarecfg stream gejagt wird) der Archiv-Member mit dem Namen ./var/signature nicht berücksichtigt wird ... man darf sicherlich auch getrost annehmen, daß das in der tatsächlichen Größe dieser Datei anhand der Metadaten erfolgt und nicht einfach 1024 Byte da blind überschrieben werden.

Damit wissen wir nun aber auch, wie wir für eine Signaturprüfung ohne die Verwendung von tr069fwupdate oder firmwarecfg stream vorgehen müssen.

  1. Suchen des Offsets, an dem die Metadaten der ./var/signature in der Datei beginnen
  2. Parsen der Metadaten, um die Länge der Datei zu ermitteln
  3. Überschreiben der Metadaten und des Dateiinhalts (bis zur nächsten 512-Byte-Grenze) mit binären Nullen
Das so entstandene TAR-File kann dann ganz einfach mit dem OpenSSL-CLI-Programm getestet werden:
Rich (BBCode):
# openssl dgst -md5 -verify plugin_avm.pem -signature signature plugins_test.update
Verified OK
# openssl dgst -md5 -verify plugin_avm.pem -signature signature plugins.update
Verification Failure
Der zweite Aufruf ist nur die Gegenprobe.

Nun gilt es also, die angeführten Aktionen so in einem Shell-Skript zu kombinieren, daß damit ein vom FTP-Server geladenes AVM-Image auf seine Echtheit geprüft werden kann (für modfs) und auf der anderen Seite ist es nun auch möglich, seine eigene Firmware zu signieren, wenn man sich selbst einen passenden RSA-Schlüssel (die 1024 Bit sind nun mal "Pflicht") erzeugt und den zugehörigen öffentlichen Schlüssel im richtigen Format (das ist ja auch alles andere als kompliziert) in das erste eigene Firmware-Image erst einmal eingebunden hat.

Hier rufen wir uns dann wieder in Erinnerung, daß tr069fwupdate (bzw. eher libfwsign.so) da zuerst die Dateien /etc/avm_firmware_public_key[1-9] versucht zu lesen und daß - in den Firmware-Versionen, die ich bisher gesehen habe - dort bisher nur bis zur "3" diese Dateien in Benutzung sind. Da es auch nichts schadet, wenn es eine Lücke zwischen den vorhandenen Dateien und der eigenen gibt, kann man den eigenen öffentlichen Schlüssel problemlos als /etc/avm_firmware_public_key9 in ein eigenes Image einbinden lassen.

Die entsprechenden Skript-Dateien für das Signieren eines eigenen Images (immer daran denken, daß die Verwendung einer solchen Signatur dann voraussetzt, daß man seinen öffentlichen Schlüssel irgendwie auf die FRITZ!Box gebracht hat und zwar auch noch unter dem richtigen Namen) und für die Prüfung so einer Signatur kommen in Kürze ins GitHub-Repository ... aber das kann noch ein wenig dauern. bzw. sie stehen inzwischen unter https://github.com/PeterPawn/YourFritz/tree/master/signimage im Repository. Der Frage, wie der Aufruf da auszusehen hat und wie man eine AVM-Signatur prüft bzw. seine eigene erzeugt, die dann von einer Firmware mit dem zusätzlichen öffentlichen Schlüssel auch mit Bordmitteln getestet werden kann, widmet sich der nächste Beitrag.

Sollte jemand Fragen haben, wäre es nett, wenn die erst einmal in einem gesonderten Thread gestellt werden könnten - event. kann dann ja ein Moderator diesen zusätzlichen Thread später hier anhängen, wenn ich mit der Beschreibung fertig bin.

EDIT 2020-02-02: CODE-Blöcke als "rich BBCode" markiert, damit funktionieren die Farben wieder.
 
Zuletzt bearbeitet:
Wenn sich jemand bei einem über den "Suche Firmware"-Thread "erworbenen" Image-File nicht so ganz sicher ist, ob das eine originale AVM-Datei ist oder nicht und er das nicht über das GUI der FRITZ!Box feststellen kann oder will, könnte er künftig das Skript auf GitHub benutzen, dazu braucht er im Moment allerdings noch noch die passenden AVM-Dateien mit dem öffentlichen Schlüssel aus einer eigenen Quelle.

Allerdings denke ich, daß die von einem Copyright auch nicht betroffen sind (es fehlt einfach die "Schöpfungshöhe", denn das sind nur normale Ausgaben einer Abarbeitung eines Programms, auch wenn diese Ausgabe einmalig ist (bzw. es sein sollte)) und daher werde ich wohl die derzeit in der aktuellen Firmware der verschiedenen Modelle enthaltenen Dateien noch einmal katalogisieren lassen und daraus eine Datenbank zusammenstellen, die dann ebenfalls im Repository landen soll.

Da das ja auch generell eine Verbesserung der Sicherheit ist (selbst wenn AVM den Umgang mit älterer Firmware vielleicht nicht so lustig findet, ist der sichere Umgang damit vermutlich immer noch besser als der ohne eine solche Prüfung), rechne ich dabei nicht mit irgendwelchen Problemen. Allerdings braucht es Zeit, die Datenbank mit den Dateien für alle FRITZ!Box-Modelle aufzubauen und bis es soweit ist, müßte sich jeder selbst mit den originalen Schlüsseln von AVM für sein eigenes Modell versorgen.

Anyway ...

Was gibt es nun an Dateien unter https://github.com/PeterPawn/YourFritz/tree/master/signimage und was kann man damit anfangen?

Generell gehören zu dem ganzen Prozess des Signierens und der Überprüfung so einer Signatur ja immer zwei Schritte und für jeden dieser beiden Schritte benötigt man die passenden Eingaben.

Beim Signieren wäre das ein unsigniertes TAR-Archiv als Ausgangsdatei und ein eigener RSA-Schlüssel zur Verschlüsselung des erzeugten Hash-Wertes über diese Eingabedatei.

Für die Überprüfung benötigt man dann die signierte Image-Datei und einen passenden öffentlichen Schlüssel, um die Signatur decodieren zu können, bevor man sie mit dem erneut berechneten Hash-Wert über die TAR-Datei vergleicht.

Generell benötigen (fast) alle der hier besprochenen Skript-Dateien ein passendes OpenSSL-Binary und eine etwas erweiterte Busybox-Variante, wegen des etwas abweichenden Formats beim Erstellen einer TAR-Datei wäre die Busybox auch einer Verwendung des tar-Kommandos aus dem GNU-tar-Paket vorzuziehen.

Liegen diese Kommandos als Dateien für eine FRITZ!Box vor, arbeiten die Skripte auch direkt auf einer FRITZ!Box und man benötigt kein weiteres Linux-System für ihren Einsatz. Die passenden Binärdateien für AVM-FRITZ!Boxen mit VR9-Prozessoren (theoretisch für alle MIPS-Prozessoren, die mit Binärdateien klarkommen, welche mit "MIPS 34k" übersetzt wurden) findet man bei Bedarf im bin-Verzeichnis eines modfs-Archivs ab Version 0.3.5.

Für das Signieren benötigt man ja einen eigenen privaten RSA-Schlüssel, diesen kann man mit dem Skript generate_signing_key erstellen lassen. Damit man beim späteren Aufruf zum Signieren nicht immer noch den richtigen Schlüssel auswählen oder angeben muß, enthält die Datei image_signing_files.inc ein paar Shell-Variablen für die Definitionen der verwendeten Namen und des Ablageortes eines eigenen Schlüssels - in der Regel wird ein Benutzer dieser Skripte auch mit einem einzigen privaten RSA-Schlüssel auskommen und dann bietet sich die Verwendung eines festen Namens an, der aber eben über die Werte in dieser Datei nach eigenem Belieben festgelegt werden kann, ohne daß man dazu die eigentlichen Skript-Dateien ändern müßte. Diese Skripte lesen einfach die Definitionen aus der zusätzlichen Datei, wenn sie diese Angaben benötigen; die Datei muß sich in demselben Verzeichnis befinden wie die Skripte selbst. Hier sollte man also vor der Verwendung die Einstellungen prüfen und an die eigenen Bedingungen anpassen ... solange man keine besonderen Anforderungen hat, sollte der Standardwert $HOME/image_signing für den verwendeten Präfix aber eigentlich schon passen.

Zurück zum Aufruf von generate_signing_key ... so ein privater RSA-Schlüssel sollte beim Speichern immer mit einem Kennwort vor unbefugter Verwendung geschützt werden. Entweder man gibt beim Aufruf von generate_signing_key das gewünschte Kennwort als ersten und einzigen Kommandozeilen-Parameter an (Globbing beachten, wenn Sonderzeichen verwendet werden) oder das Skript erfragt das zu verwendende Kennwort, wenn STDIN ein Terminal-Device ist.

Bei AVM kommen ja (im Moment) nur RSA-Keys zum Einsatz, die 1024 Bit Schlüssellänge verwenden. Das ist nach BSI-Empfehlungen heutzutage eigentlich zu wenig ... hier muß man sich vorher genau überlegen, ob die selbst erzeugten Signaturen jetzt mit den AVM-Komponenten geprüft werden müssen oder ob man sich sicher sein kann, daß auch die Prüfung so einer Signatur mit den Skript-Dateien aus dem Repository erfolgen wird/soll. Ist letzteres der Fall, kann man auch RSA-Keys mit größerer Länge verwenden, bis zu 4096 Bit kann man nutzen, ohne daß sich damit das Format der signierten Archiv-Datei entscheidend ändert, denn der Block mit dem Inhalt der Signatur-Datei ist ohnehin immer 512 Byte groß. Möchte man mit dem Skript generate_signing_key einen Schlüssel größerer Länge erzeugen, dann kann man diese über eine Umgebungsvariable KEYSIZE beim Aufruf festlegen, die Auswahl ist allerdings auf die Werte 1024, 2048 und 4096 beschränkt. Damit sähe also ein Aufruf zum Generieren eines eigenen Schlüssels folgendermaßen aus:
Rich (BBCode):
root@FB7490:/var/media/ftp/system $ KEYSIZE=4096 generate_signing_key
Found OpenSSL 1.0.2g  1 Mar 2016
Check genrsa command ... OK
Enter a password for the generated key:
Generating random seed file from /dev/random (may take some time) ... OK
Generating RSA key as /var/media/ftp/root/image_signing.key ... OK
Extracting public key to /var/media/ftp/root/image_signing.pem ... OK
Extracting public key in AVM format to /var/media/ftp/root/image_signing.asc ... OK

The generated key may be used to sign own TAR archives, if they will be checked
later without AVM components. The used key size of 4096 bits isn't supported
yet by the original firmware.
root@FB7490:/var/media/ftp/system $ ls -l $HOME/image_signing.*
-rw-r--r--    1 root     root          1034 Wed Jun 15 17:37:57 2016 /var/media/ftp/root/image_signing.asc
-rw-r--r--    1 root     root          3326 Wed Jun 15 17:37:57 2016 /var/media/ftp/root/image_signing.key
-rw-r--r--    1 root     root           800 Wed Jun 15 17:37:57 2016 /var/media/ftp/root/image_signing.pem
-rw-r--r--    1 root     root           136 Wed Jun 15 17:37:13 2016 /var/media/ftp/root/image_signing.rnd
Um meiner eigenen Paranoia zu huldigen, versucht das Skript beim Fehlen einer .rnd-Datei (mit dem in image_signing_files.inc definierten Namen) seinerseits, eine 256 Byte große Datei (16 Zugriffe a 16 Byte) über /dev/random zu lesen, mit der dann der PRNG (pseudo random number generator - ein softwaregestützter Generator für Zufallszahlen) initialisiert wird. Das braucht einige Zeit und so kann/sollte man auf anderem Wege (vielleicht auch auf einer stärkeren Plattform) seine eigene Datei mit diesen Initialisierungswerten erstellen und unter dem richtigen Namen ablegen. In jedem Falle sollte damit verhindert werden können, daß wegen mangelnder Entropie mehr oder weniger identische Schlüsselpaare generiert werden, selbst wenn die Abläufe automatisiert erfolgen sollten.

Will man einen Schlüssel haben, den die AVM-Komponenten auch verkraften, sieht der Aufruf so aus:
Rich (BBCode):
root@FB7490:/var/media/ftp/system $ generate_signing_key
Found OpenSSL 1.0.2g  1 Mar 2016
Check genrsa command ... OK
Enter a password for the generated key:
Generating RSA key as /var/media/ftp/root/image_signing.key ... OK
Extracting public key to /var/media/ftp/root/image_signing.pem ... OK
Extracting public key in AVM format to /var/media/ftp/root/image_signing.asc ... OK

You should copy the file /var/media/ftp/root/image_signing.asc to your
firmware image as /etc/avm_firmware_public_key9 to use it for image
verification with AVM components.
Die hier erzeugte Datei $HOME/image_signing.asc sollte man dann unter dem Namen /etc/avm_firmware_public_key9 in die eigene Firmware einbinden, wenn man auch später in der Lage sein will, selbst-signierte Images (die an mehreren Stellen in der Firmware benutzt werden, nicht nur beim Update für das FRITZ!OS einer Box) durch die AVM-Prüfung zu bringen.

Ansonsten erstellt man ganz normal seine zu signierende TAR-Datei (das kann von einem Pseudo-Update bis zu einem Freetz-Image eigentlich alles sein) und ruft dann das Signieren dieser Datei folgendermaßen auf (zur Erinnerung, der eigene RSA-Key wird über die Einstellungen in der Include-Datei image_signing_files.asc gefunden, meiner heißt hier my_key1):
Rich (BBCode):
root@FB7490:/var/media/ftp/system $ sign_image my_image.tar >my_image.image
Found OpenSSL 1.0.2g  1 Mar 2016
Check dgst command ... OK
Check  rsa command ... OK
Verify hash algorithm  md5  is supported ... OK
Enter the password for the signing key:
Check the password for the private key file ... OK
Signing the image hash (md5) with RSA key from /var/media/ftp/root/my_key1.key ... OK
Copying resulting image to output ... OK
Man kann natürlich den privaten Schlüssel nur dann zum Signieren verwenden, wenn man dessen Kennwort weiß ... auf die Möglichkeit eines unverschlüsselt gespeicherten RSA-Schlüssels nimmt das Skript absichtlich keine Rücksicht und so erwartet es entweder dieses Kennwort wieder als zweiten Parameter beim Aufruf oder es fragt ihn über das Terminal ab.

Will man die erzeugte Datei mit AVM-Komponenten verarbeiten, ist es wichtig, daß das Einpacken der Dateien immer relativ zum Pfad ./var erfolgt. Andere Pfadangaben werden von der AVM-Firmware ausgefiltert und durch den Namen /var/tmp/ignored_tar_content ersetzt, die kriegt man also an seinem Ziel nie zu sehen.

Wichtig ist in jedem Falle die Umleitung der Ausgabe des Skriptes in eine passende Datei, denn die Eingabedatei selbst bleibt unverändert und das Ergebnis in Form des signierten Archivs wird über STDOUT geliefert ... landet das im Terminal, ist der Zeichensalat i.d.R. vorprogrammiert.

Auch hier gilt wieder, daß der von AVM immer noch verwendete Digest-Algorithmus eher schwach und schon stark veraltet ist (gerade für solche permanenten Hash-Werte sollte er schon lange nicht mehr verwendet werden, bei Datenübertragungen mit ständig wechselndem Inhalt geht er gerade noch so, weil dort die Zeit zur Manipulation und zur Berechnung einer Kollision eher knapp bemessen ist) und theoretisch ebenfalls schon lange nicht mehr in dieser Art und Weise genutzt werden sollte, wenn man den BSI-Empfehlungen folgen will.

Um für die Zukunft etwas besser gerüstet zu sein, erlaubt sign_image auch die Verwendung eines anderen Digest-Verfahrens, wobei wieder zu beachten wäre, daß dann nur das check_signed_image-Skript (oder eine andere Software, die analog arbeitet) damit etwas anfangen kann und die AVM-Prüfung damit (bisher) nicht klarkommt.

Den zu verwendenden Algorithmus legt man vor/beim Aufruf über die Umgebungsvariable USEHASH fest, die fehlende Angabe wird durch das AVM-kompatible MD5 ersetzt (wenn denn die Schlüssellänge auch noch paßt für die AVM-Komponenten).

Die möglichen Digest-Algorithmen sind allerdings im Skript auf einen aus der folgenden Liste beschränkt:
Rich (BBCode):
md5
sha1
sha224
sha256
sha384
sha512
whirlpool
Groß-/Kleinschreibung spielt dort keine Rolle (es wird alles nach klein übersetzt) und das verwendete OpenSSL-Programm muß natürlich den ausgewählten Algorithmus dann auch noch unterstützen.

Bei der Prüfung mit dem ebenfalls im Repository zu findenden Skript check_signed_image wird der verwendete Algorithmus dann anhand der OID des Hash-Feldes in der Signatur-Datei automatisch erkannt und das openssl-Kommando passend aufgerufen:
Rich (BBCode):
root@FB7490:/var/media/ftp/system $ USEHASH=SHA512 sign_image my_image.tar >my_image.image
Found OpenSSL 1.0.2g  1 Mar 2016
Check dgst command ... OK
Check rsa command ... OK
Verify hash algorithm sha512 is supported ... OK
Enter the password for the signing key:
Check the password for the private key file ... OK
Signing the image hash (sha512) with RSA key from /var/media/ftp/root/my_key1.key ... OK
Copying resulting image to output ... OK
root@FB7490:/var/media/ftp/system $ check_signed_image my_image.image -a $HOME/my_key1.asc
Found OpenSSL 1.0.2g  1 Mar 2016
Check dgst command ... OK
Check rsautl command ... OK
Checking the public key from /var/media/ftp/root/my_key1.asc ... OK
Checking support for the used hash algorithm sha512 ... OK
Verification succeeded.
Bei der Verwendung von sign_image gibt es dann auf Wunsch noch einen speziellen Fall ... manchmal steht man ja vor der Aufgabe, eine zuvor auf der FRITZ!Box erstellte Datei auch nur genau dann wieder zu entpacken, wenn sie auf diesem Gerät auch erzeugt wurde - eine auf der FRITZ!Box erzeugte Backup-Datei, die nur auf genau dieser Box auch wieder eingespielt werden darf, wäre so ein Beispiel. Nun könnte man für diesen Zweck auch alle möglichen anderen Vorkehrungen treffen, notfalls die Datei sogar komplett verschlüsseln, was dann aber auch das Nachsehen und Extrahieren für einzelne Einstellungen schwer bis unmöglich macht.

Da in der Regel im FRITZ!OS bereits ein eigener RSA-Key existiert (in Form des Schlüssels für das Zertifikat für den TLS-Zugriff auf die FRITZ!Box), braucht man dafür gar keinen zusätzlichen eigenen Schlüssel zu generieren. Dafür bietet sign_image einen speziellen Modus an, der mit der Umgebungsvariablen SIGN_ON_BOX=1 aktiviert wird und der dort dann (mithilfe von privatekeypassword, das dafür ebenfalls vorhanden sein muß) den RSA-Key aus der Datei /var/flash/websrv_ssl_key.pem für das Signieren verwendet:
Rich (BBCode):
root@FB7490:/var/media/ftp/system $ SIGN_ON_BOX=1 USEHASH=SHA512 sign_image my_image.tar >my_image.image
Found OpenSSL 1.0.2g  1 Mar 2016
Check dgst command ... OK
Check rsa command ... OK
Verify hash algorithm sha512 is supported ... OK
Signing the image hash (sha512) with RSA key from /var/flash/websrv_ssl_key.pem ... OK
Copying resulting image to output ... OK
Beim späteren Aufruf von check_signed_image kann man dann mit dem passenden Parameter auch ausschließlich den öffentlichen Schlüssel der FRITZ!Box als Kandidaten für die Signaturprüfung verwenden ... mithin kann man über eine solche Kombination sicherstellen, daß eine (ansonsten unverschlüsselte) TAR-Datei tatsächlich auf genau dieser FRITZ!Box erstellt wurde (solange der öffentliche Schlüssel paßt, der überlebt bei der AVM-Firmware aber z.B. auch kein Factory-Reset; man muß sich ggf. selbst um die Sicherung an dieser Stelle kümmern - aber das ist ein Schwachstelle des FRITZ!OS an sich und hat mit diesem Thema hier nur am Rande zu tun, weil man eben den Schlüssel selbst sichern muß).

Selbstverständlich machen diese FRITZ!OS-Sonderfunktionen nur dann Sinn, wenn die Skript-Files auch auf einer FRITZ!Box ausgeführt werden, daher erfolgt da vorher noch eine (rudimentäre) Prüfung.

Aber damit haben wir dann unsere selbst-signierte Image-Datei in den verschiedenen denkbaren "Geschmacksrichtungen" und wir können uns der Prüfung so einer Signatur widmen.


-Für diese Prüfung braucht es einerseits die signierte Datei und einen öffentlichen Schlüssel (es ginge auch ein privater (dort findet man ja auch den öffentlichen drin), aber das verbietet sich in diesem Kontext). Beim Vorgehen von AVM weiß man eigentlich nie so genau, welcher RSA-Key da nun eigentlich zum Verschlüsseln des Hash-Wertes verwendet wurde, denn es steht ja nirgendwo "dran". Entweder der (vermutlich benutzte) Schlüssel ergibt sich indirekt aus dem Kontext (z.B. bei plugins.update und /etc/plugin_global_key.pem) oder man muß eben mit allen möglichen Kandidaten so lange probieren, bis man den einen (es sollte tatsächlich nur den einen geben, das ist wie beim Highlander) gefunden hat, mit dem sich die Signatur-Datei von Byte-Brei in eine Datei mit sinnvollem Inhalt (in Form einer DER-Struktur) dekodieren läßt. Das ist dann der dazu gehörige öffentliche Schlüssel und mit dem finden dann alle weiteren Aktionen statt.

Dieser Notwendigkeit des Probierens trägt jetzt auch das Skript zur Überprüfung der Signatur Rechnung ... es erwartet neben dem Namen des zu prüfenden Images auch noch mindestens eine Angabe, wo nach den potentiell verwendeten öffentlichen Schlüsseln Ausschau gehalten werden soll.

Bei AVM sind das eigentlich immer die Dateien in dem bereits in #1 erwähnten Text-Format, wo jede Datei aus zwei Zeilen besteht, von denen die erste den Modulus (einen Bestandteil des Schlüsselpaars) enthält. Das ist eine lange Kette aus hexadezimalen Ziffern, deren Länge Rückschlüsse auf die Größe (und damit die Sicherheit) des verwendeten RSA-Key ermöglicht, wobei die Ausgabe um einmal 00 zu lang sein kann, weil dort immer eine positive Zahl gespeichert wird und deren höchstes Bit darf nicht 1 sein, wie es bei jeder Hex-Ziffer zwischen 8 und F an der ersten Stelle der Fall wäre. Die zweite Zeile ist i.d.R. nur 6 Zeichen lang und enthält fast überall den öffentlichen Exponenten mit dem Wert 65537 (0x10001).

Mit diesem AVM-Format können andere Tools jedoch nicht umgehen, daher gibt es mit dem Skript avm_pubkey_to_pkcs8 ein kommentiertes Beispiel, wie man diese "Zahlen" wieder in ein maschinenlesbares Standard- und mit OpenSSL verwendbares Format transformiert. Dieses Skript soll aber nur dem interessierten Leser das Nachvollziehen dieses Weges ermöglichen, derselbe Ablauf ist noch einmal (kürzer und "gekapselt") in check_signed_images enthalten, damit das nicht von der Existenz eines externen Skriptes abhängig sein muß.

Mehr ist zu avm_pubkey_to_pkcs8 dann auch schon nicht zu schreiben ... außer vielleicht noch dem Hinweis, daß dieses Skript als Filter angelegt ist, also die Datei im AVM-Format auf STDIN erwartet und das Ergebnis (im PEM-Format) auf STDOUT weiterreicht. Auch der Hinweis, daß die bei AVM zu findende /etc/plugin_global_key.pem trotz ihres Namens mitnichten bereits eine Datei im PEM-Format ist, sondern genauso wie die Dateien /etc/avm_firmware_public_key[1-9] das AVM-Format verwendet, sei noch einmal wiederholt, falls jemand #1 nicht gelesen hat.

Nun gibt es ja aber durchaus noch gebräuchlichere Formate für solche öffentlichen Schlüssel und auch wenn hier die AVM-Interpretation naturgemäß eine gewisse Rolle spielt, versteht das check_signed_image-Skript zusätzlich noch die gebräuchlicheren Formate und kümmert sich dann um deren Konvertierung in eine benutzbare Form.

Im Einzelnen kann das Skript mit folgenden Angaben für die Suche nach Kandidaten für öffentliche Schlüssel umgehen:

-a dateiname
Damit wird eine einzelne Datei (mit dem angegebenen Namen) der Liste hinzugefügt, diese Datei verwendet das oben beschriebene AVM-Textformat.

-f dateiname
Die hier angegebene Datei ist kein einzelner öffentlicher Schlüssel, es ist eine Liste von Dateinamen und jeder dort vorhandene Eintrag verweist auf eine Datei im AVM-Textformat. So eine Liste könnte man z.B. durch den Aufruf von:
Rich (BBCode):
ls -1 /etc/avm_firmware_public_key[1-9] plugin_global_key.pem 2>/dev/null >zieldatei
erzeugen, wo dann automatisch nur die Dateien in der Liste auftauchen, die auch tatsächlich vorhanden sind.

-b
Das ist am Ende eine Alternative zur oben gezeigten Angabe einer Dateiliste, die aber nur auf einem Gerät mit FRITZ!OS auch verfügbar ist (der Rest funktioniert ja auch auf anderen Systemen). Hier werden die oben erwähnten Dateien auf der aktuellen FRITZ!OS-Installation der Liste hinzugefügt, deshalb braucht (und akzeptiert) diese Angabe auch keinen Dateinamen.

-p dateiname oder auch
-d dateiname
Hiermit fügt man die angegebenen Dateien im "natürlichen Format" von OpenSSL (-p für PEM-Dateien und -d für solche im DER-Format) der Liste hinzu.

-c dateiname
Hier muß ich (noch) etwas weiter ausholen ... um die Behandlung der Schlüssel zu vereinheitlichen, erzeugt das Skript intern eine Textdatei, die für jeden Schlüsselkandidaten eine Zeile mit 4 Werten enthält:
Rich (BBCode):
DESC => eine (im Moment nicht verwendete) Beschreibung des Schlüssels (z.B. woher er gelesen wurde)
SRC  => der Dateiname der Quelldatei für diesen Schlüssel (so es eine solche gab)
MOD  => der Modulus dieses Schlüssels
EXP  => der öffentliche Exponent dieses Schlüssels
Alle anderen Formate werden in dieses eine umgewandelt und die hier beschriebene Option -c ermöglicht die Angabe des Namens einer Datei, die genau solche Einträge/Zeilen bereits enthält (raw format habe ich es genannt). Die Datei wird einfach an dieser Stelle an die bisher schon erzeugte Liste angehangen und später dann gelesen. Da die Zeilen dieser Datei mit der eval-Anweisung der Shell verarbeitet werden, muß man bei der Verwendung dieses Parameters extrem sicher sein, daß der Aufbau der Datei stimmt und man sollte keinesfalls eine Datei von dritter Seite an dieser Stelle verwenden (oder nur nach sehr sehr gründlicher (Sicht-)Prüfung). Dort enthaltene Shell-Kommandos werden einfach ausgeführt ... es sage niemand, ich hätte ihn nicht gewarnt. Aber dieses Format hat trotzdem seine Berechtigung, weil ich mit dem Inhalt von DESC irgendwann mal eine Liste mit Modell und Version anzeigen will, woher dieser Eintrag dann stammte (und man kann natürlich auch manuell nachsehen). Eigentlich wird dieses Format durch eine Datenbank-Abfrage erzeugt (aus einer SQLite3-Datenbank), aber die Veröffentlichung einer Version mit Datenbank-Unterstützung ist ja erst einmal verschoben - nur das "intermediate file" hat schon das richtige Format und das hier ist für das Zusammensetzen mehrerer solcher Dateien gedacht.

-s
Diese Option ist jetzt das Gegenstück zur Möglichkeit der Signatur mit dem RSA-Key der FRITZ!Box ... ist sie angegeben, darf kein anderer Kandidat für einen öffentlichen Schlüssel angegeben werden. Es wird dann nur versucht, den öffentlichen Schlüssel aus der Datei /var/flash/websrv_ssl_key.pem zu extrahieren und nur diesen einen Schlüssel zu verwenden. Scheitert das Auslesen des öffentlichen aus dem privaten Schlüssel (z.B. weil privatekeypassword fehlt oder nicht funktioniert), wird noch der Reihe nach versucht, den Schlüssel auf dem X.509-Zertifikat der Box (unter /var/flash/websrv_ssl_cert.pem bzw. /var/tmp/websrv_ssl_cert.pem) zu extrahieren, denn dafür wird das korrekte Kennwort ja eigentlich nicht benötigt.

Der letzte ist auch der einzige Parameter, bei dessen Verwendung dann ein Zertifikat berücksichtigt wird ... bei allen anderen Parametern geht es um einen "puren" öffentlichen Schlüssel. Hat man nur ein X.509-Zertifikat (oder auch ein solches im OpenPGP-Format), muß man es selbst vorher passend umwandeln bei der Verwendung der anderen Parameter.

Das war es dann auch schon, was zu check_signed_image eventuell noch zu schreiben war (oder auch nicht) ... die Fähigkeit zur Erkennung des verwendeten Digest-Algorithmus hatte ich oben erwähnt, auch hier muß natürlich das OpenSSL-Programm das unterstützen, denn die Skript-Dateien implementieren keine eigenen Algorithmen (mit der kleinen Ausnahme des Erstellens der PKCS8-Struktur, da ist etwas ASN.1-Encoding enthalten, weil es dafür keine Operationen in OpenSSL gibt).


-Bleibt noch die Feststellung, daß die fehlende Angabe bei AVM, welcher Schlüssel denn nun der richtige wäre und das in der /var/tmp/fwsign.log zu sehende "Probieren" auch bei AVM einen (kleineren) Angriffsvektor dahingehend eröffnen, daß man auch eine "falsche" Datei für verschiedene Funktionen verwenden kann.

Hier wäre besonders die Funktion zum DECT-Update von einem USB-Speicher zu erwähnen, die zwar per se ziemlich gut abgesichert ist, aber sie akzeptiert eben auch erst einmal eine korrekt signierte Firmware-Datei mit einer FRITZ!OS-Version anstelle eines "richtigen" DECT-Updates und wenn da nicht in der /var/install mit absoluten Pfaden gearbeitet würde und die Datei kernel.image somit unter dem Pfad /var/tmp/kernel.image gesucht würde, wo sie doch in /var/packet/var/tmp/kernel.image steht bei der Verwendung von tr069fwupdate, dann würde auch ein Firmware-Update über eine passend umbenannte Datei auf einem USB-Stick funktionieren, wenn die Box dort nach einem Update suchen sollte.

Den Weg des Downgrades auf diese Weise versperrt dann allerdings noch die Tatsache, daß dabei von /var/install nur über den Downgrade-Versuch berichtet wird, weil der Aufruf der /var/install dort ohne -f erfolgt. Ob das jetzt Absicht ist oder es sich eher um - mehr oder weniger glückliche - Zufälle handelt, möge jeder selbst entscheiden ... die fehlende "Zweckbestimmung" der verwendeten öffentlichen Schlüssel und die in libfwsign.so stattfindende Suche nach dem "richtigen Schlüssel" lassen dort jedenfalls erst einmal Vertauschungen zu, ohne daß die Signaturprüfung bereits darüber stolpert - was sie aber sollte, wenn da ein DECT-Update plötzlich mit dem Schlüssel für ein FRITZ!OS-Update oder dem für Plugins signiert ist (meine Meinung, die muß man nicht teilen und "et hätt noch emmer joot jejange").
 
Zuletzt bearbeitet:
  • Like
Reaktionen: Insti
Und warum ist das schade um die Zeit?

Klar, selbst ich kenne das Prinzip auch schon länger (auch die kurze, unvollständige Beschreibung in WHMF) ... eine plausible Erklärung dafür, wie es funktioniert oder eine analoge Herleitung, wie man es untersuchen/feststellen kann, habe ich trotzdem bisher nirgendwo gefunden.

Vielleicht hilfst Du mir hier ja mit einem Link auf die Sprünge?

Gibt es dort dann auch gleich noch die notwendigen Programme/Skripte, um das auch auf der FRITZ!Box selbst zu benutzen?

Das ist es nämlich, was in diesem (bzw. meinetwegen in "meinem") Kontext benötigt wird und genau deshalb beschreibe ich das hier (und nicht nur als Selbstzweck), damit jemand bei der Verwendung meiner Shell-Skripte im Rahmen von "modfs" nicht einfach "vertrauen" oder gar "glauben" muß, sondern die Funktion und den dafür verwendeten Code selbst nachvollziehen kann, wenn er das möchte.

Sollte es solche Implementierungen tatsächlich bereits geben, sollte man sie vielleicht auch im "Suche Firmware"-Thread oder anderswo mal erwähnen ... gerade dort ist ja die Gefahr, daß man auf manipulierte Firmware treffen könnte, besonders groß und auch der Download der originalen Firmware-Dateien in Freetz (der ja nicht zwangsweise von einem AVM-Server erfolgen muß, da sind ja auch Quellen bei Providern o.ä. enthalten) sollte/könnte/müßte dann ja schon lange mit diesen bereits vorhandenen Möglichkeiten zur Prüfung abgesichert sein (wobei da zumindest der Hash der Firmware-Datei im Ganzen hinterlegt ist und geprüft wird - daher fallen dort ja auch "stille Updates" durch AVM eher auf ... hier wäre dann nur die Frage, ob der Autor der Änderung im SVN dann immer sicher sein kann, daß er beim Erstellen des Referenzwertes auf eine originale Datei zugegriffen hat).

Der von Dir gezeigte Ausschnitt aus einem Pack-Vorgang mit "fwmod" muß wohl auch auf einer privaten Erweiterung von Freetz basieren (oder zumindest einer, die an einer Stelle veröffentlicht wurde, wo keine Suchmaschine beim Indizieren jemals hinkommt), denn ich kann mit "freetz" und "signing firmware" das Internet (mit Google, auch wenn das sonst nicht meine erste Wahl ist) rauf- und runtersuchen, ohne entsprechende Fundstellen auszumachen. Auch die Suche im Freetz-Trac nach der Zeichenfolge "signing firmware" findet nichts (außer "assigning firmware") - das würde ich aber erwarten, selbst wenn das nur in einem verworfenen Patch-Vorschlag auftauchen sollte (weil es zumindest im Text zum Ticket stehen sollte, in einem Beispiel, wie so ein Patch den fwmod-Ablauf dann ändert). Sollte ich mich hier zu blöd zum Suchen anstellen, hätte ich auch da ganz gerne einen Denkanstoß für die richtige Suche.

Sollte ich tatsächlich sogar eine öffentlich zugängliche Fundstelle zur korrekten(!) Dokumentation übersehen haben, verweise ich auch gerne noch darauf (ich will mich nicht mit fremden Federn schmücken, auch wenn ich tatsächlich in der Lage bin, solche Sachen selbst per RE zu finden).

Die Beschreibung in WHMF ist jedenfalls - zumindest teilweise - falsch oder (weniger drastisch) unvollständig ... wenn Du das mit "schon lange bekannt" meinst, hast Du zweifellos recht.

Aber aus der Formulierung:
WHMF schrieb:
signature ist immer der letzte Eintrag im Firmware tar-Archiv und wird aus einem Werksinternen privaten Schlüssel und der mit md5sum errechneten Checksumme des unsignierten Firmware-Archivs errechnet und dem Archiv angehangen.
kann man (zumindest ich) nicht entnehmen, daß der Platz im "unsignierten Firmware-Archiv" für die ./var/signature reserviert werden muß (mit 2x 512 Byte) und als binäre Nullen in die Berechnung des Hash-Wertes eingeht - sonst stimmt der Wert nämlich nicht und damit war es (zumindest mir) nicht möglich, mit diesen Angaben ein Shell-Skript zu erstellen, das eine AVM-Signatur erfolgreich prüfen kann (auf der Box selbst und zwar ohne die Firmware dann auch gleich noch zu installieren) bzw. eine Firmware so zu signieren, daß die originale Firmware von AVM beim Vorliegen des passenden öffentlichen Schlüssels eine solche selbst-signierte Datei akzeptiert.

Wenn das in Freetz so umgesetzt sein sollte, wie es in WHMF beschrieben ist, glaube ich auch nicht daran, daß die Firmware (zumindest aktuelle) das dann tatsächlich akzeptiert - die würde bei "wird [...] dem Archiv angehangen" schon vor dieser Datei die "end of archive"-Header sehen und gar nicht mehr bis zur ./var/signature kommen (spätestens das "tar" beim Auspacken nicht) oder - wenn man wirklich unter dem "Anhängen" den "append mode" des tar-Kommandos versteht, den aber das Busybox-Applet "tar", das von Freetz zum Packen verwendet wird, gar nicht kennt - der MD5-Hash in der Signatur stimmt nicht, denn dafür braucht es tatsächlich die leeren Blöcke als Platzhalter anstelle der "signature"-Datei und die abschließenden "end of archive"-Header - mir jedenfalls ist es nicht gelungen, für ein Image-File nur durch das "Vorziehen" der EoA-Header hinter den letzten Member (also praktisch durch das Ausschneiden der signature-Datei) eine gültige Prüfsumme zu berechnen, die mit dem Wert in der Signatur-Datei übereinstimmt.

Ich habe auch keine (zugänglichen) Quellen gefunden, daß der von AVM verwendete private Schlüssel für auch nur eine der vier enthaltenen möglichen Schlüsseldateien kompromittiert worden wäre ... auch wenn das "nur" ein 1024-Bit-Schlüsselpaar ist. Wenn das tatsächlich so wäre, könnte jeder beliebige Angreifer ja wieder eine Firmware so signieren, daß die Firmware sie auch bei einer Installation aus der Ferne klaglos akzeptiert.

Was ich mit der Formulierung
opto schrieb:
Ist praktisch um automatischen Firmware-Updates wirksam vorzubeugen
anfangen soll, weiß ich allerdings auch nicht ... denn dazu reicht es ja bereits aus, die öffentlichen Schlüssel einfach aus dem erzeugten Image zu entfernen oder durch andere zu ersetzen - dafür braucht man m.E. nicht einmal eine Vorstellung, wie die Prüfung konkret abläuft; hier reicht es ja einfach, wenn die Prüfung nicht erfolgreich ist.

Und zu guter Letzt ist die (ständig erfolgende) Änderung der Pfade bei der Verarbeitung durch "firmwarecfg stream" so neu (die kam erst in der 06.25-Laborreihe dazu), daß sie m.W. überhaupt nicht in WHMF zu finden ist ... also schadet es auch nichts, wenn das mal irgendwo erwähnt wird (und meine "Erwähnung" in den festgestellten Unterschieden beim Erscheinen dieser Labor-Reihe nehme ich da deutlich aus, denn da steht nichts zum Hintergrund oder zu den Auswirkungen dieser Änderung).

Alles in allem kann ich das "schade um die Zeit" also nicht nachvollziehen, die Anerkennung in den Worten "Schöne Analyse" nehme ich aber trotzdem dankend zur Kenntnis.

- - - Aktualisiert - - -

@opto:
Ich muß noch einmal nachhaken ... gibt es nun einen Link zu einer Quelle oder nicht?

Ich frage deshalb, weil ich mir dann das Aufbereiten eigener älterer Dateien für die Veröffentlichung im GitHub vielleicht sparen könnte, wenn da benötigte Funktionen bereits (benutzbar) implementiert wurden.

Solche eigenen Skripte als PoC hat sicherlich jeder irgendwo in der Schublade und jeder kennt auch das Problem ... man weiß selbst ganz genau, wie die aufgerufen werden sollen (notfalls schaut man einfach noch einmal hinein), daher verzichtet man auf allen Schnickschnack wie "usage screen" oder gar hilfreiche Fehlermeldungen. Das sind alles Dinge, die man bei einer Veröffentlichung dann eben nachrüsten muß und das macht richtig Arbeit, die man vielleicht nicht machen müßte, wenn es jemand anderes bereits erledigt hat.

Da ich das Thema gerade bearbeite, kann jede Stunde entscheidend sein, in der ich von so einem Link keine Kenntnis habe ... denn dann mache ich einfach weiter wie bisher und lasse mich nicht beeinflussen, selbst wenn es unnötige Arbeit sein sollte.

Wenn ich aber im Vertrauen darauf, daß irgendwann mal etwas zu finden sein wird irgendwo im Internet, die Arbeit hier ruhen lasse, geht es wieder nicht mehr weiter und wenn es dann so einen Link vielleicht gar nicht gibt, wäre das erst recht ärgerlich, wenn man so eine Verzögerung zuläßt.

Daher noch einmal ganz deutlich: Kannst Du mir eine Quelle (in Form eines Links) nennen, unter der man sowohl eine Beschreibung des Verfahrens als auch Details zu dem von Dir erwähnten "proof of concept" in/aus/für Freetz findet oder nicht?

- - - Aktualisiert - - -

12 Stunden später noch einmal ein Versuch meinerseits, die Frage "etwas dringender" zu machen ... nun könnte es natürlich sein, daß Du (@opto) nur ab und an mal im IPPF vorbeischaust, dann würde ich eigentlich auf eine E-Mail-Benachrichtigung des IPPF setzen. Heute nachmittag habe ich ohnehin keine Zeit und wenn bis zum Anpfiff keine Antwort eingetroffen sein sollte, dann mache ich einfach weiter - danach rechne ich nicht mehr mit einer Antwort in einem Zeitraum, der mir irgendetwas bringen würde.
 
Zuletzt bearbeitet:
@opto:
Mit dem dritten Key werden z.B. DECT-Firmware-Updates signiert, der erste und der zweite werden für das Signieren der eigentlichen Box-Firmware genutzt, z.B. bei der 7490:
Rich (BBCode):
root@FB7490:~ $ check_signed_image images/FRITZ.Box_7490_Labor.113.06.55-33361.image -b
Found OpenSSL 1.0.2g  1 Mar 2016
Check 'dgst' command ... OK
Check 'rsautl' command ... OK
Trying to determine the correct key now ...
Checking the public key from /etc/avm_firmware_public_key1 ... OK
Checking support for the used hash algorithm 'md5' ... OK
Verification succeeded.
root@FB7490:~ $ check_signed_image images/FRITZ.Box_7490
FRITZ.Box_7490.113.06.51.image              FRITZ.Box_7490_Labor.113.06.55-33361.image
root@FB7490:~ $ check_signed_image images/FRITZ.Box_7490.113.06.51.image -b
Found OpenSSL 1.0.2g  1 Mar 2016
Check 'dgst' command ... OK
Check 'rsautl' command ... OK
Trying to determine the correct key now ...
Checking the public key from /etc/avm_firmware_public_key1 ... OK
Checking support for the used hash algorithm 'md5' ... OK
Verification succeeded.
root@FB7490:~ $ check_signed_image 03.01.03.67.avm.de.upd -b
Found OpenSSL 1.0.2g  1 Mar 2016
Check 'dgst' command ... OK
Check 'rsautl' command ... OK
Trying to determine the correct key now ...
Checking the public key from /etc/avm_firmware_public_key1 ... FAILED
Checking the public key from /etc/avm_firmware_public_key2 ... FAILED
Checking the public key from /etc/avm_firmware_public_key3 ... OK
Checking support for the used hash algorithm 'md5' ... OK
Verification succeeded.
root@FB7490:/var/media/ftp/system $ check_signed_image FRITZ.Box_Fon_WLAN_7390.AnnexB.84.06.20.image -b
Found OpenSSL 1.0.2g  1 Mar 2016
Check 'dgst' command ... OK
Check 'rsautl' command ... OK
Trying to determine the correct key now ...
Checking the public key from /etc/avm_firmware_public_key1 ... FAILED
Checking the public key from /etc/avm_firmware_public_key2 ... FAILED
Checking the public key from /etc/avm_firmware_public_key3 ... FAILED
Checking the public key from /etc/avm_firmware_public_key9 ... FAILED
Checking the public key from plugin_global_key.pem ... FAILED
No usable public key was found.
Ich habe eine Datenbank aus den extrahierten Dateien vor einiger Zeit mal erstellt und dazu alle Firmware-Images auf dem Server durchflöhen lassen ... die habe ich in irgendeinem Backup; aber andererseits reicht vermutlich auch eine neue mit den aktuell verwendeten Keys.

Irgendwo habe ich auch mal die Verwendung des zweiten Keys gefunden, weiß gerade nicht mehr genau, wo das war ... ist auch nebensächlich. Inzwischen hat das Skript zur Signaturprüfung schon die Zusammenfassung der wesentlichen, vorher verteilten Funktionen und es fehlt nur noch die Verwendung der Datenbank, die braucht es aber auch nur dann, wenn ich ein Image außerhalb der Box oder eines für ein anderes Modell testen will, ansonsten reicht mir ja der im OS vorhandene Satz an öffentlichen Schlüsseln und das funktioniert.

Das Skript zum Signieren kann jetzt auch mit der Ausgabe des GNU-tar umgehen, solange da keine PaxHeader enthalten sind und das reicht ebenfalls. Mehr brauche ich fast nicht für meinen Bedarf ... die Sache mit der Datenbank ist mehr dafür gedacht, daß man tatsächlich auch unter einem beliebigen System (solange es ein Linux-artiges ist) die Signatur einer (fast) beliebigen Firmware prüfen kann. Die Keys sind ja nicht in allen Modellen identisch (s.o. der Test der 7390-Firmware auf einer 7490 mit den "eingebauten" Keys) ... daher braucht es eben diese Datenbank oder zumindest irgendeine Quelle, wo man auch ohne die Box ermitteln kann, welche Key-Files bei einem Modell "Standard" sind.

- - - Aktualisiert - - -

Das mit openssh kann ich nicht nachvollziehen ... zumindest nicht mit dem Stand, wie ich ihn vor dem rebase in meinem Fork hatte; denn openssh habe ich damit tatsächlich übersetzt (als dropbear-Alternative und für den SFTP-Server) und ich habe kein Problem gefunden/gehabt, weder beim Compilieren noch bei der Verwendung des SFTP-Servers (ich greife i.d.R. grundsätzlich über sshfs auf den angepaßten dropbear-Server auf der 7490 zu, wenn ich Dateien austauschen will mit der Box).

Bei Python mag das stimmen ... da ich das ohnehin nicht auf der Box einsetzen würde, habe ich es nie getestet. Die Frage wäre jetzt, ob irgendetwas bei der Integration in den Trunk schiefgegangen ist oder ob ich andere Parameter beim OpenSSH verwendet habe oder woran es sonst liegen mag.

Gerade Du weißt ja auch, daß "nicht mehr compilierbar" ein sehr weites Feld ist ...

EDIT: Wobei mir gerade einfällt, daß ich irgendwann auch mal OpenSSH "ausgemistet" habe und - da ich es auf die tatsächlich (von mir) benötigten Ciphers und KEX beschränkt habe (z.B. arcfour/RC4 rausgeworfen und auch das eigentlich nie benötigte RIPEMD160 habe ich rausgetan, auch wenn das keine bekannten Lücken hat) - irgendwann brauchte ich dann gar kein OpenSSL mehr für OpenSSH, weil alle notwendigen Algorithmen schon im OpenSSH enthalten waren (war aber der Historie in GitHub nach erst 14 Tage nach der Umstellung auf 1.0.2g bei mir). Ich hatte mal kurz überlegt, ob ich lieber auf OpenSSH mit ECDH und ed25519 wechseln sollte (seitdem PuTTY das auch kann, ist das auch bei den Kundenboxen eine denkbare Alternative), aber das beißt sich dann wieder mit der Verwaltung des Keys auf der Box, wo ich ja den RSA-Schlüssel des FRITZ!OS nutzen will (dann braucht man auch nichts gesondert für dessen Ablage und Verschlüsselung zu tun).
 
Zuletzt bearbeitet:
Ich hatte die arcfour-Algorithmen in der cipher.c auskommentiert (sind ohnehin uralter Schrott und sollten am besten gar nicht mehr benutzt werden _können_):

https://github.com/PeterPawn/freetz/commit/c5d95b4dc2f0663397a33f032fba22e9a58a3f93

und auch RIPEMD160 dann als einzige verbleibende (und m.E. sehr ungebräuchliche) Digest-Variante auch noch ausgenommen (auch wenn die nicht als überaltert gilt).

Damit konnte ich dann die Abhängigkeit von OpenSSH vom OpenSSL-Paket komplett aufheben:

https://github.com/PeterPawn/freetz/commit/9411ead384b6f085d2338dc2b03b7425c900cf7b

und auch das macht (in meinem Kontext) dann wieder Sinn.

Wenn man RIPEMD160 drin läßt und die OpenSSL-Library verwendet (da habe ich m.E. RIPEMD160 nicht ausgeklammert, weil ich an den Algorithmen eigentlich nichts geändert habe), sollte sich - so man den ersten Patch in zwei dividiert - auch OpenSSH ohne den "--without-openssl"-Patch bauen lassen.

Ich persönlich würde das tatsächlich so lösen, daß die Verwendung von RC4 in OpenSSH herausgepatcht wird und nicht wieder hingehen und RC4 in die libcrypto.so aufnehmen ... dann wird es auch wieder Leute geben, die das nicht richtig an ihrem Webserver (oder was auch immer an Paketen auf die libcrypto/libssl zugreifen will) einstellen und am Ende dann doch wieder bei der Benutzung von RC4 auch in anderem Kontext landen.

Allen Benutzern gleichzeitig kann man es ohnehin niemals recht machen und manchmal muß man die Leute auch zu ihrem Glück (hier zu sicherer Kommunikation) zwingen.

Wenn wirklich jemand unbedingt noch RC4 braucht, dann muß er sich eben eine Variante bauen, wo er selbst die Konfiguration für die Library geändert hat. Die Mainstream-Browser haben es m.W. auch alle inzwischen rausgeworfen, u.a. deshalb mußte AVM bei den älteren Modellen ja noch mal nachlegen (die 06.06-Versionen), da dort auf einmal ausschließlich nur RC4-Algorithmen gingen (seit der 06.01), weil da jemand vermutlich noch die BEAST-Attacke im Hinterkopf hatte, die zu dem Zeitpunkt aber auch schon kein Thema mehr war.

Das RIPEMD160 kann man ja drinlassen, dann darf man eben die Abhängigkeit von der OpenSSL-Library nicht weglassen (keine Ahnung, ob die AVM-Variante der libcrypto.so dann RIPEMD160 drin hätte, würde ich mich auch nicht drauf verlassen wollen).

Ich habe die trotzdem auch bewußt rausgeworfen (noch mit falschem Statement im Commit, was ich aber in einem Kommentar zum Commit noch klargestellt habe), weil dann in dem "SSH-Paket" zur Nachinstallation, welches mir vorschwebt, keine libcrypto.so gebraucht wird bzw. die von AVM definitiv ausreicht, weil beim Lesen des Box-Keys in dropbear nur solche Funktionen verwendet werden, die naturgemäß in der AVM-Version enthalten sein müssen, denn die AVM-Komponenten greifen ja genau damit auch auf das Zertifikat/den Key zu.

- - - Aktualisiert - - -

Nur mal so als "Bekräftigung" meiner Meinung, daß RC4 einfach nicht mehr in die libcrypto.so gehört: https://tools.ietf.org/html/rfc7465

- - - Aktualisiert - - -

Und noch mal zum "public key" für die Plugins ... bei der Kontrolle des Beta-Images für die 7390 ist mir aufgefallen, daß auch dort der Plugin-Key sich von dem der letzten Release-Version unterscheidet.

Nun gab es ja lange keine 7390 mit Plugins (mal von der internationalen mit den Language-DBs abgesehen, die habe ich eher links liegengelassen), daher habe ich da vorher nie so darauf geachtet - aber ich würde es tatsächlich auch für plausibel halten, wenn dieser Key erst beim Build generiert wird, denn die Plugins sollten dabei ja auch neu erstellt werden und dann wäre so eine 1:1-Beziehung zwischen dem Plugin-Image und "seiner" Firmware-Version ja auch eine zusätzliche Sicherung gegen Verwechslungen bzw. Vertauschungen (erstere versehentlich, letztere absichtlich) bei den Plugins.

- - - Aktualisiert - - -
@er13:
Ich habe gerade noch einmal in der Cipher-Konfiguration für die libcrypto.so im Freetz-Trunk nachgesehen ... da ist auch "no-rmd160" und "no-ripemd" definiert (die sind eigentlich auch noch synonym: https://github.com/openssl/openssl/blob/master/Configure#L878) und damit enthält die libcrypto.so ohnehin keine benutzbaren Funktionen dafür. Ich weiß im Moment auch nicht, ob und wie OpenSSH dann dieses Fehlen sauber behandelt (ob das dynamisch geladen wird oder ob da eine "weak reference" vor der Verwendung des Pointers noch geprüft wird), aber damit macht das Auskommentieren dieses Digests in OpenSSH ja noch mehr Sinn (so etwas wie die Abhängigkeit der Patches zweier Pakete voneinander gibt es ja wohl in Freetz bisher nicht, oder?), denn dann würde (zumindest durch rmd160, wenn es keine weiteren Abhängigkeiten gibt bei entsprechender Konfiguration) ja eine vollkommen unnötige Abhängigkeit aufrechterhalten, die aber ihr "Gegenstück" in der libcrypto.so gar nicht finden kann.

PS: Ich würde wahrscheinlich mal einen Moderator bitten, den Teil zur OpenSSL-Konfiguration irgendwie abzutrennen und in einen gesonderten Thread zu verfrachten ... es hat zwar entfernt mit der Signaturprüfung auch etwas zu tun, aber ich habe mich beim Antworten auch wieder vergallopiert und hinreißen lassen bzw. gar nicht darauf geachtet, daß das hier ja der Thread zum Signieren ist. Ich hoffe mal, niemand hat mit einer Verschiebung irgendwelche Probleme ... aber die Diskussion über OpenSSL-Konfigurationen lenkt dann doch eher vom Signatur-Thema ab, auch wenn ich sehr froh bin, wenn Freetz jetzt ebenfalls auf die LTS-Version gehen sollte (hilft ja in der Perspektive auch über das Jahresende hinaus, wenn die 1.0.1 EOS hat).
 
Zuletzt bearbeitet:
@opto:
Offenbar gibt es aber keinen "allgemeinen" RSA-Key bei AVM (zumindest nicht für das Signieren von Firmware) und in gewisser Weise ist das sogar gut - es kann kein "Generalschlüssel" kompromittiert werden ... theoretisch könnte man natürlich so ein Image auch mit mehreren Keys parallel signieren, die Frage wäre dann nur, ob man mehrere DER-Strukturen in einer gemeinsamen signature-Datei unterbringt oder ob man mehrere signature-Dateien benutzt (für jeden potentiellen Key eine eigene).

Das wird bei AVM aber erst einmal nicht funktionieren mit dem derzeitigen Stand und was da künftig passiert und wie man den Übergang zu besseren Algorithmen dann lösen will/wird, interessiert mich genauso wie jeden anderen.

So richtig sicher ist die Verwendung von MD5 bei den Signaturen ja nicht mehr - und das auch (absehbar) nicht erst seit gestern, selbst SHA1 würde man heute nicht mehr als "neue Lösung" verwenden (s. entsprechende Dokumente vom BSI: BSI TR-02102-[1-4]).
 
Nicht geprüfte Theorie: Wenn man einem korrekt signierten Image noch Dateien hinter die signatur "attachd", schneidet die stream-binary von AVM trotzdem an der gewohnten Stelle ab und prüft die zusätzlichen Daten nicht? Damit könnte man dann sogar Dateien im Image überschreiben.
Die Signatur wird eben über das gesamte Image mit Platzhalter-Blöcken anstelle der ./var/signature berechnet (bzw. der Hash, der dann "signiert" wird) und wenn man dort etwas dahinter anhängen will, stimmt wieder der Hash nicht mehr. Das tar-Kommando macht am "end of archive"-Header (2x 512 Byte mit Nullen) dann Schluß mit dem Entpacken und der wird eben noch in den Hash eingerechnet. Damit kann man da hinten anhängen, was man will, das tar-Applet sollte das nicht mehr entpacken. Also ... klappt so nicht nach meiner Erfahrung. EDIT: Nochmal deutlich ... fügt man Inhalt nach der Signatur, aber vor dem EoA-Header ein, stimmt wieder die Signatur nicht mehr ... zwar überschreibt man dann tatsächlich vorher bereits extrahierte Dateien gleichen Namens, aber es wird kein Skript mehr aufgerufen und auch nichts nach / oder /var verschoben, alles wird beim rekursiven Löschen von /var/unpack dann wieder entsorgt.

opto schrieb:
Dein Script zum prüfen von Signaturen bringt das terminal-Fenster durcheinanderen, wenn man aus versehen statt einem key ein Image angibt
Ich vermute mal, das macht "cat imagefile" aber auch und insofern sehe ich das nicht wirklich als Problem.

Mir fiele jetzt auch nicht aus dem Stand ein, wie ich (ohne weitere "sed"-Kopfstände) das Format des Keys in AVM-Schreibweise noch prüfen sollte (und ich denke mal, Du meinst nur dieses Format, die anderen werden m.E. richtig erkannt und abgelehnt) ... das ist (denke ich mal, das habe ich immer mal anders gemacht im Laufe der Zeit (s.a. avm_pubkey_to_pkcs8) und ich muß selbst erst nachsehen) jetzt schon das Auslesen der zwei Zeilen mit "read" und wenn da eben Binärdateien gelesen werden, kommt Quatsch heraus.

Da jetzt noch eine Format-Prüfung einzubauen, ist m.E. zu viel Aufwand, beim Image-File ist ja die Prüfung auf das TAR-Format schon drin und bei der Liste der Keys sind die Möglichkeiten zu vielfältig.

Mit passendem Content in einer Datei bei "-c"-Option kann man sogar die Box neu starten oder CI betreiben - aber das steht ja auch deutlich da und mein Ehrgeiz für eine "wasserdichte Lösung" an dieser Stelle tendiert gegen Null.

Es ist Sache des Aufrufers, da die richtigen Parameter anzugeben, denn ob der nun über so eine Datei das System (oder auch nur das Skript) zum Absturz bringt oder mißbraucht oder durch die direkte Eingabe eines solchen Kommandos (die Berechtigungsstrukturen im FRITZ!OS sind ja sehr übersichtlich), macht am Ende keinen wirklichen Unterschied.

- - - Aktualisiert - - -

Ansonsten ist das beim Signieren ja keine PKI, wo irgendein Schlüssel mit einem anderen beglaubigt wird ... das ist nur ein simpler RSA-Schlüssel zur Verschlüsselung der Datei, die die Ergebnisse des Hashens der Eingabedatei als ASN.1-Struktur (bzw. eben im DER-Format) enthält und aus der Tatsache, daß sich diese Datei mit dem öffentlichen Schlüssel dekodieren läßt, ergibt sich die indirekte Bestätigung, daß die Datei mit dem korrekten privaten Schlüssel kodiert wurde.

Dann wird nur noch der Hash-Wert über die "Eingabedatei" (hier das Image-File) ermittelt und mit dem dekodierten Wert verglichen und wenn der übereinstimmt, ist es dieselbe Datei, wie sie beim "Unterschreiben" vorlag (Hash-Kollisionen mal außen vorgelassen).

Da kann es keinen "übergeordneten Schlüssel" geben ... max. eben noch in einer PKI dann die Bestätigung, daß der verwendete (öffentliche) Schlüssel wirklich bei AVM für das Signieren von Firmware gedacht ist (das wäre dann ein "Zertifikat", der Verwendungszweck des privaten Schlüssels ist ja untrennbar mit dem öffentlichen verbunden).
 
Zuletzt bearbeitet:
Keine Ahnung, wie wäre es mit etwas Kontext, was Dein Ziel war?

Die Inhalte der Dateien sind ja wenig überraschend ... erst die Frage, ob der Hash in der ./var/signature auch den Wert "cef729b18b1b8e54a0caebf9ef147ca5" hat, entscheidet über die Gültigkeit.

Den müßtest Du dann noch mit "openssl rsautl -asn1parse ..." und dem richtigen Public-Key für diese Signatur ermitteln, wenn man das "optisch" vergleichen will. Wie das geht (aus tar extrahieren und dann das passende openssl-Kommando ausführen), steht irgendwo in #1.

Ansonsten müßtest Du Dir den Return-Code von tr069fwupdate noch ansehen ... die Vergleichswerte zur Interpretation findest Du am ehesten in start_dect_update.sh. Ansonsten kenne ich noch "rc=6", das steht dann für "/var/install nicht gefunden oder nicht ausführbar" (event. auch noch für weitere Probleme, aber zumindest führt eine /var/install ohne x-Bits zu diesem Return-Code, auch wenn die Signatur akzeptiert wurde).
 
So, ein hoffentlich benutzbarer Stand der Skript-Dateien ist erst einmal fertig und wird eingefroren ... die "Dokumentation" für die Verwendung habe ich in #2 untergebracht.

Sollten Fragen bestehen oder die Skript-Dateien nicht das tun, was sie lt. meiner Beschreibung tun sollten (bitte bitte kein "wie geht das, ich verstehe #2 nicht", sondern konkrete Fragen - es bringt ja auch nichts, wenn ich meine Erklärungen nur noch einmal variiert wiederhole, weil ich wieder nicht verstehe, was man da nicht verstehen sollte), dann gilt wie immer: Gerne diskutiere ich darüber, entgegen anderslautenden Gerüchten auch mit "Laien", solange es nicht auf "erklär es noch mal von vorne, was ich bei mir da jetzt genau eingeben muß" hinausläuft ... da das keine Vorlage für irgendeine konkrete Lösung ist, macht so etwas einfach auch keinen Sinn.

In modfs wird diese Möglichkeit der Signaturprüfung jedenfalls in der nächsten Version genauso Einzug halten (bzw. mit Leben erfüllt werden, die Hooks dafür sind ja schon drin), wie es ein modscript geben wird, das dem erzeugten SquashFS-Image auf Wunsch einen von mir generierten öffentlichen Schlüssel hinzufügt, mit dem ich dann künftig von mir bereitgestellte Pakete signieren werde ... von Update-Archiven für modfs selbst bis zu Binärpaketen für die Verwendung mit mod_custom_images bzw. E99-custom.
 
Hallo Peter,

inwieweit hast Du schon Deine Skripte zum Signieren/Verifizieren der Signatur auf einem x86/x64 getestet bzw. testen können?

Musste erstmal wegen den (von busybox scheinbar unterstützen) bash'ismen (source, ==, substring, read) shebang anpassen, um generate_signing_key zum Laufen zu bekommen (habe bisher nur dieses getestet). Steht es zufälligerweise noch auf Deiner ToDo-Liste oder müsste ich mich damit auseinandersetzen, wenn ich es in Freetz integriert haben möchte? ;)

Danke!

Grüße,
Gene
 
Hi,

ich weiß nicht genau, wo das Problem liegt ... ich tippe mal (wieder) auf Debian und "dash"?

Ich habe auf openSuSE mit bash 4.2 getestet, da sollte es funktionieren (beim tar muß man sich auf --format=gnu beschränken, die PaxHeader-Blöcke bringen das AVM-Zeug ohnehin durcheinander, auch Freetz packt ja vermutlich deshalb mit busybox-tar).

Code:
:~/GitHub/YourFritz/signimage # /bin/sh --version
GNU bash, version 4.2.53(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Ich kenne keinen sinnvollen Ausweg aus dem SheBang-Dilemma ... auf der Box ist /bin/sh eben busybox-ash und selbst wenn ich versuchen würde, der dash durch /bin/ash als SheBang aus dem Weg zu gehen, müßte man die Zeile unter Debian dann meist doch wieder anpassen (außer man hat tatsächlich dort die Busybox liegen). Wenn Dich das vorwärts bringen sollte, kann ich gerne auf "ash" ändern ...

Ansonsten gilt generell, daß ich die Dateien in signimage einmal unter FRITZ!OS mit BusyBox und einmal unter bash 4.2 getestet habe ... es gibt beim Signieren sogar ein paar Spezialbehandlungen, weil eben GNU-tar immer noch ein komisches Format (lt. Posix-Spec für ustar-Format) am Ende der Datei erzeugt.
 
Hi Peter,

ja, dash unter Debian bzw. Ubuntu.

Die "richtige" Lösung wäre natürlich auf die (zufälligerweise von busybox ash unterstützten) bash'ismen zu verzichten, aber die Aussage "wurde mit bash auf dem Host getestet" reicht mir völlig aus.

Ich kann dann in Freetz shebang explizit auf bash setzen.

VG,
Gene
 
Hallo Peter,

ich habe bei meinen Tests festgestellt, dass es neben der Hash-Summe wohl noch etwas geprüft wird. In jedem Fall sorgt die Aufnahme zusätzlicher Dateien ins .image-Tarball dafür, dass beim Versuch über das AVM-WebIf die Firmware zu flashen, immer noch "Die angegebene Datei enthält kein von AVM für dieses Gerät freigegebenes FRITZ!OS" angezeigt wird (getestet auf 7490.06.3x). Ich habe das Problem vorerst mittels r13794 workarounded.

Weißt Du zufälligerweise, was da noch geprüft wird? Die Anzahl der Dateiein in dem Tarball? Oder gar die Namen aller Dateien? Sonst etwas?

Danke!

VG,
Gene
 
Ich bin schon dran ... habe den Commit im Trac gesehen und baue mir gerade mal ein Image mit Freetz und dem Stand vor dem CS ... mal sehen, was "firmwarecfg stream" dazu sagt. Mein erster Gedanke war es, daß da Dateien enthalten sein könnten, die "wegen Verstoßes gegen die Regeln" zu "/var/tmp/ignored_tar_content" umbenannt werden von firmwarecfg und daß die Existenz so einer Datei dann doch getestet wird von libfwsign.so ... es gehen wirklich nur "regular files" und "directories" durch, deren Name mit "./var/" beginnt (und noch ein paar weitere Test sind vorhanden) - alles andere wird von "firmwarecfg stream" ausgefiltert. Ob da vielleicht auch "dot files" betroffen sind, kann ich noch nicht sagen ... die Anzahl der Dateien steht eigenlich nirgendwo und der Aufbau der verschiedenen signierten Dateien (von Plugins über DECT- bis zu Firmware-Images) ist eigentlich so unterschiedlich, daß es auch nichts mit "normalen Namen" zu tun haben dürfte.

Mal sehen, ob ich etwas finde ...

- - - Aktualisiert - - -

Update:
Das Packen in Freetz erzeugt als ersten Eintrag im tarball ein "./" ... das wird dann von firmwarecfg wie oben dargestellt geändert.

Offenbar ist die Existenz von /var/tmp/ignored_tar_content nach dem Auspacken dann noch eine zusätzliche Abbruchbedingung ... das würde ich jetzt als Hypothese stehen lassen und nicht weiter ergründen.

Wenn es hilft, den einzelnen Punkt am Ende des tar-Aufrufs durch "./var" zu ersetzen und das Image besteht dann die Prüfung, würde mir das als empirischer Beweis ausreichen.

EDIT: Zum Thema "FREETZ_PATCH_SIGNED" ... die Firmware trägt ja normalerweise in Node 87 noch den Grund für ihre Beschwerde ein (TELNET, SIGNED, ???), wenn das vorher (sicher) leer war, sollte man durch passendes "cat" oder "hexdump" (ggf. vorher das char-Device anlegen) erkennen können, was die eigentliche Ursache war/ist. Eine bereits bestehende Markierung wird auch durch das Flashen einer signierten Firmware natürlich nicht aufgehoben, daher ja auch die Notwendigkeit für Recovery, wenn man es mit "Hausmitteln" kurieren will. Weiß Whoopie sicherlich alles selbst, soll mehr für andere Leser als Erläuterung dienen (wobei denen dann wieder der Mail-Wechsel für den Hintergrund fehlt ... egal, ich lasse es stehen).

- - - Aktualisiert - - -

Hoppla, ich merke gerade, daß da ja noch nicht das einzige Problem liegen kann, denn das ./ existiert ja auch ohne die zusätzlichen dot-Files ... damit fällt der empirische Beweis vermutlich ohnehin flach.
 
Zuletzt bearbeitet:
Hmm, dieser "./"-Eintrag existiert doch auch dann, wenn .config und .packages nicht dazu gepackt werden - und dann funktioniert es :confused:
 
Überschneidung?

Ja, der existiert auch dann ... ich hatte zwischenzeitlich Probleme, das auf der Box zu finden, bis ich dann bemerkt habe, daß ein "strace"-Aufruf mit Ausgabe der Trace-Logs nach /tmp bei einer 38 MB-Datei nicht funktionieren kann. Wenn ich das jetzt an eine andere Stelle schreiben lasse (das Ziel für's Entpacken kann ich ja nicht festlegen) und es mit "strace" für "tr069fwupdate packet" auspacken lasse, dann akzeptiert das tr069fwupdate so ein Image klaglos (ich hatte zwar noch einmal mit ./var eingepackt, gucke aber auch gleich noch einmal nach, wie es bei ./ aussieht) und stirbt erst in der Ausführung von /var/install dann den Heldentod (weil die absoluten Pfade nicht stimmen bei tr069fwupdate packet).

Ich sehe da nicht, wo das Problem liegen sollte ... aber ich kann/will auch nicht mit einem Freetz-Image testen, da ich einmal die offizielle Release-Version von AVM und eine modfs-Version der aktuellen Labor-Reihe installiert habe.

Beim Speicherplatzproblem habe ich dann festgestellt, daß das "firmwarecfg" (was ja vermutlich sich selbst aufruft beim Upload des Images übers Web-Interface, damit das erst mal in die Signaturprüfung geht) am Ende nicht mehr in der Lage war, die /var/tmp/firmware_stream_result zu schreiben und daß deren Fehlen dann den Fehler verursachte (bei tr069fwupdate sogar als 203 - unspecified error lt. start_dect_update.sh).

Auch nach der Anzeige "nicht freigegebene Firmware" sollte es eine /var/tmp/firmware_stream_result und eine /var/tmp/fwsign.log geben (die auch stehen bleiben bis zum nächsten Versuch oder zum nächsten Start), da könnte man hilfreiche Hinweise drin finden.

Die Idee, daß da die ".irgendwas"-Dateien problematisch wären, kann man m.E. getrost verwerfen, wenn tr069fwupdate sich daran nicht stört.

- - - Aktualisiert - - -

Ne, auch mit ./ als erstem Eintrag funktioniert der Test mit "tr069fwupdate packet" ... dann wird /var/tmp/ignored_tar_content wohl wirklich ignoriert.

Das verwendete Archiv hat folgende Member:
Code:
root@FB7490:/var/media/ftp/system $ tar tvf 7490_06.51-freetz-devel-13791.de_20160625-145948.image
drwxr-xr-x root/root         0 2016-06-25 14:58:39 ./
drwxr-x--- root/root         0 2016-06-25 15:01:16 ./var/
-rw-r--r-- root/root       598 2016-06-25 15:01:16 ./var/.packages
-r-xr-x--- root/root    278552 2015-10-20 16:19:38 ./var/chksum
-r-xr-x--- root/root    283844 2015-10-20 16:19:38 ./var/regelex
-rwxr-x--- root/root     34444 2016-06-25 14:59:04 ./var/install
drwxr-x--- root/root         0 2016-06-25 15:01:15 ./var/tmp/
-rw-r----- root/root   2445832 2016-06-25 15:01:16 ./var/tmp/kernel.image
-rw-r--r-- root/root  36054272 2016-06-25 15:01:15 ./var/tmp/filesystem.image
-rwxr-x--- root/root      2795 2016-02-03 16:37:59 ./var/info.txt
-rw-r--r-- root/root     72770 2016-06-25 15:01:16 ./var/.config
-rw-r--r-- root/root       128 2016-06-25 15:01:19 ./var/signature
Was da beim Aufruf über "firmwarecfg" jetzt anders sein mag (also beim Update über das GUI), kann ich nicht selbst testen (s.o.).

- - - Aktualisiert - - -

Was mir noch aufgefallen ist ... Du hast mit der 06.30 getestet? Da wurde eigentlich der zusätzliche Pfad mit dem "unpack" ebenfalls schon verwendet - bei noch früheren Versionen wurde da ggf. noch etwas anders gefiltert, das weiß ich inzwischen nicht mehr, aber bei der 06.30 sollte das identisch sein.

Für die Nagelprobe wäre eben der Aufruf von
Code:
/usr/bin/tr069fwupdate packet file://[dateiname mit vollem Pfad, inkl. führendem Slash]
echo $?
bei Shellzugriff leicht selbst auszuführen ... ein Error 8 ist dann das Ergebnis der Ausführung der /var/install. Das kann man auch in den Dateien /var/tmp/install_* sehen ... aber schon der Start von /var/install würde die erfolgreiche Signaturprüfung belegen.

Natürlich nicht vergessen, das eigene Zertifikat den eigenen Key vorher mit bind-Mount z.B. über die key3 von AVM zu legen (wenn er nicht bereits im Image enthalten ist) - sonst gibt es ohnehin ein "wrong signed".
 
Zuletzt bearbeitet:
Danke für den Hinweis mit Node 87. Wusste ich nicht. ;-) Habe es mit "echo clear_id 87 >/proc/tffs" gelöscht.
 
Im Zusammenhang mit einer anderen Recherche bin ich tatsächlich noch auf eine Quelle gestoßen, wo es einige (korrekte) Informationen zur Funktionsweise der Signaturprüfung schon etwas länger zu lesen gibt: https://www.redteam-pentesting.de/d...-010/-avm-fritz-box-firmware-signature-bypass

Dieser damalige Fund von RedTeam war dann wohl auch der Anlass für AVM, seit der 06.20 auf "./var" als Beginn des Namens zu testen und alles andere als "ignored_tar_content" durchfallen zu lassen.
 
Ich meinte jetzt schon konkret die Änderung, daß andere Namen als "./var/signature" nicht mehr ausgeblendet werden beim Berechnen des Hash-Wertes und daß der Name "ignored_tar_content" überhaupt eingeführt wurde. Das war bei der 06.05 noch nicht der Fall - jedenfalls nach den Zeichenketten in der Firmware zu schließen. Ab welcher AVM-Revision (06.10-irgendwas) das jetzt konkret erfolgte, weiß ich auch nicht ... aber in der 06.20-Release war es dann wohl drin.

Das mit den Updates für die DOCSIS-Modelle warten wir dann doch mal ab (zumindest ich) ... ohne eine Möglichkeit, an die CVC-Datei zu kommen, braucht es auch keine Lösung, selbst eine solche (geänderte) Datei zu signieren.
 
Hallo zusammen,


ich habe mich auf einem für mich neuen Feld versucht und einen Wiki-Artikel verfasst ;-). Inhaltliche, sprachliche sowie grammatikalische Korrekturen (Deutsch ist nicht meine Muttersprache) sind willkommen.


VG, Gene
 

Zurzeit aktive Besucher

Keine Mitglieder online.

Statistik des Forums

Themen
246,366
Beiträge
2,250,916
Mitglieder
374,023
Neuestes Mitglied
jeifeioioj
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.