allcfgconv - Ersatz / Gedanken zu Verschlüsselung

sweetie-pie

Neuer User
Mitglied seit
5 Feb 2005
Beiträge
118
Punkte für Reaktionen
0
Punkte
16
Hallo,

irgendwie stört es mich auch, dass die Funktion zum einfachen Decrypten auf der Console aus der Box verschwindet.
Sicherlich ist es einerseits ein Sicherheitsgewinn, aber andererseits ist das PW ja weiterhin irgendwie verschlüsselt auf der Box gespeichert.
Im Allgemeinen verstehe ich nicht, wieso AVM dies so macht.
Für Provider-Zugansdaten kann ich das nachvollziehen, aber nicht für die Userpasswörter, die kann man hashen.

Wie dem auch sei, ich habe dazu mal folgendes probiert:
Eine "alte" allcfgconv in die Fritzbox (7270_V2 FRITZ!OS 06.05) kopiert.
Verschiedene Passwörter (1,2,3,4) gesetzt.
ar7 original und decodiert ausgelesen.

Hier die Ergebnisse:
Code:
/# cat /var/flash/ar7.cfg  | grep -A 2 "id = 100"
id = 100;
name     = "$$$$BUJURNILLOYD1LAPYRMF1A5RCMIS3CDS23TYKHM4L1GP6M1CFLQG5GZM3I3QJXLI6JE1L3YIELH4AQFM";
password = "$$$$QAHUOG1OIIKV2E15ADH3L2SRMSUFDZRTIK6JXYO5VOOEOH3NO32CLTDWHFPAH5DBNC4LXXTXD4GD5QFM";
/# /var/mod/root/allcfgconv -C ar7 -c -o - | grep -A 2 "id = 100"
id = 100;
name = "@CompatMode";
password = "1";

/# cat /var/flash/ar7.cfg  | grep -A 2 "id = 100"
id = 100;
name     = "$$$$QDXWB36PMRVMSAHAX6CLQIPYRKH2UZFSVRZB5SRUY1T2KY2MAOW26FB2XCY66E4WV2X5F61UP1LCGQFM";
password = "$$$$IPGX2D2TJSHUE411N3IKTIRXI6CD1CYO3X6EG3HJCXFYT1NGSFYVBRRYDG662OEH2HSJWGMVPN36CQFM";
/# /var/mod/root/allcfgconv -C ar7 -c -o - | grep -A 2 "id = 100"
id = 100;
name = "@CompatMode";
password = "2";

/# cat /var/flash/ar7.cfg  | grep -A 2 "id = 100"
id = 100;
name     = "$$$$PJUV3LLRQC3LAWDOLICZJV4BNUR5ZPTURBRRZCO4JIKNMKQII5IQJZYTUVTOKJARBCNEDTBEV5DDGQFM";
password = "$$$$A4UD2MDK5RCL46H2QIYLUDHXNTTSJDLXQH2OWAIXVCPI2KRFRNXODGDYSWTVPJPANEGEJQNZCYY31QFM";
/# /var/mod/root/allcfgconv -C ar7 -c -o - | grep -A 2 "id = 100"
id = 100;
name = "@CompatMode";
password = "3";

/# /var/mod/root/allcfgconv -C ar7 -c -o - | grep -A 2 "id = 100"
id = 100;
name = "@CompatMode";
password = "4";
/# cat /var/flash/ar7.cfg  | grep -A 2 "id = 100"
id = 100;
name     = "$$$$F266PXQ3EHWVOGVMIV21KNMVZCYERGZTV6P13SZ63VANX3PURRRYYV3JNMETY1RAUVWGWUOJSILSKQFM";
password = "$$$$LKP1HO24FSLSNFRGNTMHL4OBESV6VHDTXTRKYR3PANQ1A1V3SGUZ4JLLKEDJCQ26D4HRBNX2NP3NAQFM";

Was mir so aufgefallen ist:
80 Zeichen - die letzten 3 ändern sich nicht
Nutzt man das gleiche PW bei einem anderen Dienst sind alle Zeichen verschieden, es ändern sich auch die letzten 3 Zeichen.
Ändert man das PW wird auch der User anders verschlüsselt.
Auch habe ich probiert fremde PW-Strings auf dem Netz auf meiner Box zu dekodieren - ohne Erfolg.

Ich stelle mal folgende Thesen auf:

Das encryptete PW ...
... kann kein Hash sein (Hash=Einwegfunktion -> keine Berechung des Passworts möglich.)
... ist immer an die Box gebunden (mit MAC, S/N oder CERT oder was auch immer gekoppelt).
... ist an den Dienst gebunden (webui, voip...).
Benutzernamen werden u.a. unter Zuhilfenahme des Passworts verschlüsselt.

Ich würde gerne mal mit IDA pro in die Binaries (z.B. allcfgconv neu/alt, libar7cfg.so ...) gucken, aber AFAIK geht das nur mit der Bezahlvariante.
Evtl. könnte man auch mal einige ENV-Variablen (z.B. MACx) ändern und testen ob ar7.cfg noch decrypted werden kann.

Vielleicht gibt es ja hier den einen oder anderen der sich darüber mit mit austauschen möchte (auch per PM).
Auch würden mich Ergebnisse der obigen Versuche auf anderen Boxen interessieren.
Ansonsten bin ich offen für alle Gedanken und Kommentare zur Verschlüsselung.

Der Winter mit seinen langen Nächten kommt... ;)
 
sweetie-pie schrieb:
Ich würde gerne mal mit IDA pro in die Binaries (z.B. allcfgconv neu/alt, libar7cfg.so ...) gucken ...
Die Nutzungsbedingungen für das, was Du da "auseinandernehmen" möchtest, kennst Du aber schon (Auszug - am Ende):

AVM räumt das nicht ausschließliche Recht ein, dieses AVM Firmware-Paket zu
nutzen, welches ausschließlich im Object Code Format überlassen wird. Der
Lizenznehmer darf von der Software nur eine Vervielfältigung erstellen, die
ausschließlich für Sicherungszwecke verwendet werden darf (Sicherungskopie).
AVM behält sich alle Rechte vor, die nicht ausdrücklich eingeräumt werden. Ohne
vorheriges schriftliches Einverständnis und ausser in den gesetzlich gestatteten
Fällen darf dieses AVM Firmware-Paket insbesondere weder
- vervielfältigt, verbreitet oder in sonstiger Weise öffentlich zugänglich
gemacht werden, noch
- bearbeitet, disassembliert, reverse engineered, übersetzt, dekompiliert oder
in sonstiger Weise ganz oder teilweise "geöffnet"
und in der Folge weder
vervielfältigt, verbreitet noch in sonstiger Weise öffentlich zugänglich
gemacht werden.


sweetie-pie schrieb:
Der Winter mit seinen langen Nächten kommt...
Tue, was Du nicht lassen kannst - aber lasse es (hier) niemanden wissen.

G., -#####o:
 
Die Verschlüsselung hängt meines Wissens von mac a und dem Standard - WLAN-key ab.
 
Das Reverse Engineering zu Forschung (Sicherheit) und privaten Zwecken (lernen) nicht strafbar ist,
will icht nicht weiter thematisieren, ansonsten würden ganze Lehrstühle im Knast sitzen... daran
ändern auch einseitge Lizenzbedingungen nichts (die vermutlich rechtlich eh keinen Bestand haben).

Fakt dagegen ist, dass durch das ledigliche Entfernen eines Kommandozeilenparameters eine augenscheinliche
Sicherheit vorgegaugelt wird die defacto nicht da ist, wenn ich schon allein durch das Kopieren einer alten Binary
wieder alle Passwörter auslesen kann.

Beim nächsten Sicherheitsproblem (Wer schützt uns davor, dass die Busybox demnächst nicht sowas wie der
Shellschock trifft?) funktionieren dann wieder alte Angriffszenarien...

Ich finde genau aus solchen Gründen muss man sich mit diesen essentiellen Dingen auseinander setzen, denn
wie AVM die Passwörter auf der Box speichert hat sich in den letzen 10 Jahren augenscheinlich nicht geändert...
aus eigenem Antrieb kommt man dort wohl nicht darauf... :(
 
Zuletzt bearbeitet:
sweetie-pie schrieb:
... eine augenscheinliche Sicherheit vorgegaugelt wird die defacto nicht da ist ...
Die Sicherheitslücke schaffst Du (per kopieren). Ohne Passwörter kopierst Du (auf eine fremde Box) gar nichts.

sweetie-pie schrieb:
Wer schützt uns davor, dass die Busybox demnächst nicht sowas wie der Shellschock trifft?
Das wirst dann Du sein.

sweetie-pie schrieb:
aus eigenem Antrieb kommt man dort wohl nicht darauf...
Das ist auch gut so.

G., -#####o:
 
Wenn Du ein wenig tiefer einsteigen willst, noch ein paar Stichpunkte:

1. Es gibt keinen Zusammenhang zwischen gespeicherten Benutzernamen und Kennwörtern. Zu verschlüsselnde Daten werden bei jedem Schreibzugriff auf die Dateien, in denen sie gespeichert sind, neu verschlüsselt und dann geschrieben. Das geht soweit, daß vorher per Editor im Klartext in einer Datei abgelegte Daten (es gibt ein "Caching" des ctlmgr, das man berücksichtigen muß) bei einem Schreibzugriff (da wird wg. des Cachings die gesamte Datei neu geschrieben) dann verschlüsselt werden.

2. Dabei wird eine AES256-CBC-Verschlüsselung mit einem Salt (das ist Bestandteil der binären kodierten Daten) und einem IV (der sich von maca und wlan_key - bei "normalen" Boxen - ableitet) verwendet.

3. Die resultierenden Binärdaten werden mit einer Base32-Kodierung mit einer abgewandelten "Übersetzungstabelle" (A-Z und 1-6 für die 32 möglichen 5-Bit-Kodierungen) dargestellt. Daher muß man bei der Beschreibung des Vorgangs auch zwischen der "Verschlüsselung" (AES) und der Kodierung (Base32) unterscheiden.

4. Deine Versuche mit der Länge des Chiffrats sind zwar anfänglich richtig, aber gehen nicht weit genug. Ab einer Klartextlänge von 23 Zeichen verlängert sich dann das Chiffrat (also der Buffer, der dort "kodiert" wird) um 24 Zeichen für jeweils 16 zusätzliche Zeichen Klartext -> angesichts der Kodierung ziemlich nachvollziehbar (es gibt Ausnahmen, wenn das Verhältnis Garbage zu Nutzlast mal extrem ungünstig ausgeht, wie bei der Klartextlänge von 55 bis 70 Zeichen, es gibt meiner Meinung auch einen Fehler bei der Berechnung der Länge der auszugebenden Binärdaten). Es werden ja für jeweils 5 zusätzliche Zeichen im "Klartext" (a 8 Bit = 40 Bit) 8 zusätzliche Zeichen (bei 5 Bit "Information" pro Buchstabe/Ziffer) benötigt. Klartext meint hier allerdings nicht wirklich Klartext, sondern "AES encoded buffer", mir fällt bloß kein besseres Wort für "vor Base32-Kodierung" ein. Die identischen Zeichen am Ende des Chiffrats entstehen durch das "krumme" Verhältnis bei der Kodierung und sind - vermutlich - der vorherige Speicherinhalt hinter dem AES-Buffer, der nicht überschrieben wird und dann, so wie er da steht, in Base32 kodiert wird. Ziemlich deutlich wird das beim "Padding" von 55 bis 71 Zeichen Klartext, wo dann im Chiffrat 6x 'A' = 6x (5 Null-Bits) = 30 Bits ausschließlich mit 0 am Ende stehen, womit noch die führenden 2 Bits der letzten 4 kodierten Bytes "Informationen" enthalten (zumindest bei mir standen bei 2 Boxen da nachvollziehbar diese Nullbytes).

5. Wenn man "schnell" oder auch "automatisiert" das zu einem Klartext gehörende Chiffrat (es gibt aber eben keine 1:1-Zuordnung, aus demselben Klartext entsteht durch das Salt immer ein anderes Chiffrat - jedenfalls bei verschiedenen Salts) ermitteln will, ist der Zugriff über 'ctlmgr_ctl' mit anschließender Sicherung des Wertes aus der cfg-Datei der bessere Weg und nur so kriegt man auch ohne langwierigste "Handarbeit" eine halbwegs analysierbare Liste in vertretbarer Zeit. Allerdings findet dabei eine Art "Caching" statt, man muß also den Schreibzugriff des ctlmgr auf die betreffende Datei abwarten, bevor man dort liest.

Erlaubt oder nicht ?
Kommt ganz drauf an ... ich behaupte - bis zu einer gegenteiligen Entscheidung eines anzurufenden Gerichts - ja.

Die erwähnten Nutzungsbedingungen sind ja so auch nur unvollständig zitiert:
http://download.avm.de/fritz.box/fritzbox.fon_wlan_7270_v2/firmware/deutsch/info.txt schrieb:
Das vorliegende AVM Firmware-Paket enthält Dateien, die unter verschiedenen
Lizenzen verbreitet werden, insbesondere unter AVM-proprietärer Lizenz oder
unter einer Open Source Lizenz (nämlich GNU General Public License,
GNU Lesser General Public License oder FreeBSD License). Einzelheiten zu
verschiedenen Lizenzen enthält die Datei "license.txt"
(ftp://ftp.avm.de/fritz.box/license.txt).
Das ist sogar der "wichtigere" Teil des Textes, denn der in #2 zitierte Auszug könnte suggerieren, daß es in jedem Falle nicht erlaubt wäre. Zu dieser Behauptung versteigt sich allerdings nicht einmal die Firma AVM selbst. In der vorstehend angesprochenen Datei steht nämlich dann folgendes:
ftp://ftp.avm.de/fritz.box/license.txt schrieb:
Der Lizenznehmer ist außer in den gesetzlich gestatteten Fällen (insbesondere nach § 69e deutsches Urheberrechtsgesetz, Dekompilierung) nicht berechtigt, die Software zu ändern, zurückzuentwickeln, zu übersetzen oder Teile herauszulösen.
Die Regelungen des UrhG werden hier also ausdrücklich auch als Ausnahme festgelegt (selbst wenn das nicht so wäre, würde das Gesetz die Regelung in der Lizenz brechen).

Zur Analyse (und dem Verständnis) der AVM-Verschlüsselung braucht es aber gar nicht unbedingt einen Disassembler. Etwas Kryptoanalyse (Eingangswerte und daraus resultierende Chiffrate) reicht - neben dem ebenfalls unstrittig erlaubten Analysieren von Beziehungen zwischen Bibliotheken und den dort enthaltenen Strings - vollkommen, um den Algorithmus nachzubilden.

Ich finde es auch immer wieder etwas putzig, wenn bei solchen Analysen dann der "Sicherheitsverlust" beklagt wird. Einen Black-Hat interessieren die Lizenzbestimmungen einen Bummi und das sind beileibe keine Dummköpfe, die mit dem Buddeleimer um den Weihnachtsbaum tanzen. Insofern ist - meine Meinung, die ich gerne bereit bin auch kontrovers zu diskutieren - die Kenntnis der verwendeten Algorithmen sogar für den legitimen Besitzer genauso wichtig wie für einen Angreifer, da der Besitzer nur so - wenn er sich dafür interessieren sollte - das Gefahrenpotential realistisch(!) einschätzen kann und sich nicht nur auf die - nicht immer zuverlässigen - Aussagen irgendwelcher Hersteller verlassen muß.

Schon durch die Aktion mit der Abschaffung des c-Switches bei den AVM-Utilities wird doch am Ende nur der Sandmann bedient. Wer sich nur ein ganz klein wenig mit der Verschlüsselung beschäftigt, ist gar nicht auf die Existenz dieser Optionen angewiesen und selbst wenn man im Moment noch keine "eigene" Implementierung veröffentlichen will/muß, baut AVM ja selbst wieder entsprechende Hintertüren zur Entschlüsselung in die eigene Firmware ein. Solange das dann noch so einfach zu verwenden ist, muß man kein eigenes Programm dafür veröffentlichen, da das andere Voraussetzungen benötigt bis es auf der Box ist und bei weitem nicht so leicht zu verifizieren ist, wie ein Shell-Skript.

Interessenten seien an das Freetz-Ticket 2558 verwiesen ... im dort enthaltenen Kommentar des Shell-Skripts kann man auch etwas zu den "boxspezifischen" Komponenten des IV finden.

Edit: Es kommt oben nicht klar zum Ausdruck: AES verschlüsselt mit einer Blocksize von 128 Bit = 16 Byte, d.h. die "Verschlüsselung" erfordert "Sprünge" von 16 Byte beim verschlüsselten Inhalt, wenn der Buffer bei der Verschlüsselung zu klein wird (dessen Größe ändert sich ja nicht). Diese 16 Byte benötigen dann 16 x 8 = 128 Bit bei der Base32-Kodierung, das wären aber 25,6 Zeichen. Aus diesem Mißverhältnis ergibt sich das identische "Padding" bei der Base32-Kodierung über mehrere Klartext-Längen, die letzten Zeichen gehören schlicht nicht zum AES-Buffer.
 
Zuletzt bearbeitet:
Wenn Du ein wenig tiefer einsteigen willst, noch ein paar Stichpunkte...

Chapeau! Das ist ja mal ein Beitrag... *DANKE*

Ich glaube dies und insbesondere das Freetz-Ticket hilft mir zum Verständnis gewaltig weiter...
 
Zuletzt bearbeitet:
1. Es gibt keinen Zusammenhang zwischen gespeicherten Benutzernamen und Kennwörtern.
Nun, ich hatte festgestellt, dass bei Änderung eines Passwortes sich das Chiffrat des zugehörigen Benutzernamens ebenfalls ändert... darin sah ich eine Verknüpfung.

Zu meinem Verständnis habe ich das jetz mal skizziert.... fb-encryption_.png

...eine AES256-CBC-Verschlüsselung mit einem Salt (das ist Bestandteil der binären kodierten Daten) und einem IV (der sich von maca und wlan_key ... ableitet) verwendet.
Also deine Aussage mit dem Salt ist mir noch unklar... was wird deiner Meinung nach gesalted, der zu verschlüsselende Text?

Ab einer Klartextlänge von 23 Zeichen verlängert sich dann das Chiffrat (also der Buffer, der dort "kodiert" wird) um 24 Zeichen für jeweils 16 zusätzliche Zeichen Klartext...

Wie kommst Du auf die 23 Zeichen bzw. 22 Zeichen? Ich gehe davon aus Du hast dies im Rahmen deiner Analyse festgestellt? Wenn ich AES-CBC richtig verstanden habe, hätte ich dieses Verhalten nach 16 .ten , bzw. ab den 17. ten Zeichen erwartet. Wenn natürlich der Klartext schon gesalzen ist, würde mir das einleuchten...

Daraus könnte ich dann folgende Überlegung ableiten:
Wenn ich 80 Zeichen codierten String habe, brauche ich mindest 80 Base32-Zeichen * 5 bit / 8 Bit = 50 Byte Klartext (evtl. inkl. Salt).
Oder anders herum gerechnet: 3 * 16 Byte-Blockchiffren = 48 Byte * 8 Bit / 5 Bit Base32-Kodierung geben = 76,5 Zeichen. Macht also Sinn mit den 3(,5) Bytes "Müll" am Ende.

Bei all deinen Ausführungen hast Du dich aber nicht zum eigentlichen Schlüssel geäußert... woher nimmst Du die Gewissheit das es ein 256 Bit-Schlüssel ist?
Mir ist nicht klar wie Du das allein aus den Chiffraten herleiten kannst... oder ist das nur eine Annahme, libavmcipher implemetiert ja offensichtlich auch 128 und 192 Bit...!?

Interessenten seien an das Freetz-Ticket 2558 verwiesen ... im dort enthaltenen Kommentar des Shell-Skripts kann man auch etwas zu den "boxspezifischen" Komponenten des IV finden.

Das ist sehr Cool, ich ziehe meinen Hut vor soviel Shell-Code... :rolleyes:
Ich habe das erfolgreich auf einer 7270_v2 mit SW6.05 getestet, sowohl mit der lokalen, als auch mit einer Konfig einer 7170.
BTW: Bei der 7170 steht der wlan_key nicht im mdt3|4..!?

Für mich wäre es interessant dies auf meiner lokalen Maschine zum laufen zu bekommen.
Hast Du bei deinen Untersuchungen bereits mal über eine Versuchsreihe in QEMU nachgedacht?
 
Zuletzt bearbeitet:
Zu meinem Verständnis habe ich das jetz mal skizziert
Sehr schön, blicke ich zwar nicht durch (habe auch nicht den Ehrgeiz, das zu wollen) ... aber solange es Dir hilft, ist das ja in Ordnung.

Wie kommst Du auf die 23 Zeichen bzw. 22 Zeichen? Ich gehe davon aus Du hast dies im Rahmen deiner Analyse festgestellt?
Richtig, mach das doch einfach mal selbst. Du kennst ja jetzt schon die "kritischen" Stellen, an denen sich die Länge des Chiffrats erhöht, wenn der Klartext die Grenze überschreitet. Wenn es bei 23 Zeichen startet (da werden aus 80 Zeichen Chiffrat dann 104), sollte die mathematische Reihe ja einfach zu finden sein und Du mußt nicht unbedingt die diversen Zwischenwerte auch testen (wobei es natürlich nicht schadet und für den einfachen Überblick ist eine lückelose Liste besser).

Wenn ich AES-CBC richtig verstanden habe, hätte ich dieses Verhalten nach 16 .ten , bzw. ab den 17. ten Zeichen erwartet.
Die Folge der 128-Bit-Blöcke bei AES (nicht zwingend bei Rijndael) ? Daß dort nicht nur der "reine" AES-verschlüsselte Teil enthalten ist im Base32-kodierten String, ergibt sich ja schon daraus, daß Du bei einem Zeichen Klartext (auch bei 0 Zeichen Klartext) ja schon 80 Zeichen Base32-Kodierung hast. Deine nachfolgenden Berechnungen zur Länge sind ja nachvollziehbar, daraus kannst Du auch schlußfolgern, daß dort schon bei 0/1 Zeichen Klartext jede Menge zusätzliche Information enthalten sein muß.

Wenn erst bei 23 Zeichen eine "Verlängerung" notwendig wird, ist offensichtlich bei 0-6 Zeichen Klartext aber auch schon zuviel im Chiffrat enthalten, das ginge (wenn man will) kürzer.

Ich hatte mir als erstes mal eine Liste von bekannten Klartexten (0-105 Zeichen nur Punkte) erstellt, diese kodieren lassen (anschließend zur Sicherheit dekodiert, um zu prüfen, ob die Liste korrekt ist) und dann mit dieser Liste weiter gearbeitet.

Die Länge des Base32-kodierten Strings ist jedenfalls dem Vielfachen von 8 (das ist die kürzeste Base32-Länge, bei der komplette Bytes kodiert werden - nämlich 5 an der Zahl) geschuldet, die Länge der zu kodierende binären Daten dann wieder der zugrunde liegenden Verschlüsselung (Vielfache des 16 Byte-Buffers aus AES-CBC) plus den zusätzlichen Initialisierungswerten für diese konkrete Anwendung der Verschlüsselung auf die Klartextdaten.

Die Länge der AES-verschlüsselten Daten ändert sich dann ja auch in den von Dir erwarteten 16-Byte-Sprüngen. Aus den zwangsläufigen Längen-Differenzen ergibt sich das "Padding" bei der Base32-Kodierung. Bei 39-55 Zeichen Klartext ist das Padding nicht vorhanden (Chiffrat ist 128 Zeichen lang), was könnte das jetzt für die Aufteilung der binären Daten in AES-Buffer und "andere" heißen ? Rein rhetorisch, will ich nicht wirklich diskutieren ...

Bei all deinen Ausführungen hast Du dich aber nicht zum eigentlichen Schlüssel geäußert... woher nimmst Du die Gewissheit das es ein 256 Bit-Schlüssel ist?
Mir ist nicht klar wie Du das allein aus den Chiffraten herleiten kannst... oder ist das nur eine Annahme, libavmcipher implemetiert ja offensichtlich auch 128 und 192 Bit...!?
Die Annahme, daß ein Programm beim Entschlüsseln auch dieselbe kryptographische Funktion verwendet, ist sicherlich legitim. Wenn Du das "decode_passwords"-Skript kennst, weißt Du auch, welches Programm von AVM dort von mir zweckentfremdet wird. Wenn Du dort mal ganz simpel nach Zeichenketten siehst, wirst Du mir sicherlich zustimmen. Das geht bis hier noch alles vollkommen ohne Disassembler/Debugger ... man muß halt nur an den richtigen Stellen suchen.

Wenn Du es ganz genau wissen willst, nimm Dir einen binären Puffer mit einem bekannten Inhalt (weil Du diesen selbst erzeugt hast, s.o.) und einen C-Compiler zusammen mit der OpenSSL-Developer-Dokumentation und suche Dir aus den binären Daten die passenden "Versatzstücke" für den Aufruf heraus (wenn Du Dir die Dokumentation der entsprechenden Funktionen ansiehst, klären sich sicherlich auch die Fragen zu "Salt" und "IV"/"Key"). Weiter gehe ich - solange AVM andere Wege offen läßt - mit Hinweisen nicht ... wenn Du mit einem Disassembler/Debugger umgehen kannst/willst, schau nach "config_set_pwfuncs" (setzt die CB-Funktionen zum Entschlüsseln beim Konfig-Zugriff) und welche Routinen dort eingetragen werden. Hier ist jetzt wirklich Schluß ... zumal mich Deine allerletzte Überlegung dann doch etwas irritiert.

BTW: Bei der 7170 steht der wlan_key nicht im mdt3|4..!?
Wo habe ich je davon gesprochen, daß man den wlan_key in MTD3 oder MTD4 findet ? Da steht er zwar meistens noch einmal, hat aber mit dem "Urlader-Environment" (Teil von MTD2) nur sekundär zu tun. Einzelheiten dazu findet man in den Kernel-Quellen beim TFFS-Treiber. Wie das bei der 7170 konkret im TFFS aussieht, weiß ich gar nicht genau, so weit wollte ich nie zurück. Andererseits glaube ich (angesichts des Alters der TFFS-Quellen) nicht an prinzipielle Unterschiede.

Hast Du bei deinen Untersuchungen bereits mal über eine Versuchsreihe in QEMU nachgedacht?
Ne, warum sollte ich ? Dein Interesse daran kann ich auch nicht wirklich nachvollziehen. Was bringt Dir das ?

Wenn es nur um's Lernen geht, nimm die Freetz-Toolchain, einen Editor und einen Browser (für das Lesen im Netz) und teste Deine Erkenntnisse auf Deiner FRITZ!Box.

Ich will jedenfalls keine Webseite o.ä., auf der man nur noch fremde Daten zusammen mit den passenden "secrets" eingeben muß und die einem dann den Klartext ausspuckt. Das sehe ich dann auch wieder kritisch ... da ist dann auch das Ende von Forschung und Lernen erreicht.
 
Sehr schön, blicke ich zwar nicht durch (habe auch nicht den Ehrgeiz, das zu wollen) ... aber solange es Dir hilft, ist das ja in Ordnung.

Okay, deshalb vermute ich reden wir vielleicht ein bißchen aneinander vorbei... ;)

Wenn Du es ganz genau wissen willst, nimm Dir einen binären Puffer mit einem bekannten Inhalt (weil Du diesen selbst erzeugt hast, s.o.) und einen C-Compiler zusammen mit der OpenSSL-Developer-Dokumentation und suche Dir aus den binären Daten die passenden "Versatzstücke" für den Aufruf heraus (wenn Du Dir die Dokumentation der entsprechenden Funktionen ansiehst, klären sich sicherlich auch die Fragen zu "Salt" und "IV"/"Key").

Also ich habe mal mit Python-Modul Crypto.Cipher.AES rumgespielt und kann das m.E. nachvollziehen, dahingehend meine Zeichung aus dem vorherigen Post.
Dort sieht man oben den zu verschlüsselnden Buffer der bei AES ja immer maximal 128 Bit, also 16 Zeichen beträgt.
Da die zu verschlüsselenden Daten ja mehr als 16 Zeichen sind, brauchen wir min. 3 Blöcke á 16 Zeichen.
Ich glaube da sind wir auf einer Wellenlänge.

Nun zum IV und Key:

Ich habe mir die von dir im Skript verwendete Binary mit den elfutils angeguckt. Da wird tatsächlich nur die 256-Bit-Funktion importiert, das war wohl bei den Binaries die ich angeuckt hatte nicht so...
Jetzt schreibst Du, immer vom IV und nicht vom Key.

Python implementiert die AES-Funktion wir folgt (geborgt von hier):
Code:
>>> from Crypto.Cipher import AES
>>> obj = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')
>>> message = "The answer is no"
>>> ciphertext = obj.encrypt(message)
>>> ciphertext
'\xd6\x83\x8dd!VT\x92\xaa`A\x05\xe0\x9b\x8b\xf1'
>>> obj2 = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')
>>> obj2.decrypt(ciphertext)
'The answer is no'
Dazu steht noch erläuternd:
MODE_CBC
Cipher-Block Chaining (CBC). Each of the ciphertext blocks depends on the current and all previous plaintext blocks. An Initialization Vector (IV) is required.
The IV is a data block to be transmitted to the receiver. The IV can be made public, but it must be authenticated by the receiver and it should be picked randomly.
Das habe ich probiert oben im Bild darzustellen. Der IV wird bei der Codierung des ersten Blocks mit dem ersten Buffer verknüpft und bedingt, dass der IV eine Länge von 128 Bit haben muss (links oben dargestellt).
Dann kommt erst der Key zum Tragen, hier mit 256-Bit und die eigentliche Verschlüsselung des ersten Blocks startet. Das Ergebnis habe ich dann (im auch als AES-encrypted-Buffer bezeichneten) Block, auch mit 16-Byte.
Als nächstes werden dann die Klartextzeichen 17-32 auf gleiche Weise verschlüsselt, aber dessen IV ist das Ergebnis des vorherigen Blocks, dargestellt duch den Pfeil von unten nach oben. Der Key bleibt logischerweise der Gleiche.
Genauso gehts mit dem dritten Block (Zeichen 33-48). Am Ende werden dann die einzelnen AES-encrypted-Buffer á 16 Zeichen aneinander gehängt, also im einfachen Fall 48 Zeichen und Base32-kodiert und zu 76,5 bzw. 80 Zeichen aufgebläht.

In deinem Skript bindest Du ja die env-Boxumgebung ins proc-Filesystem ein, so dass sie in der chroot zur Verfügung steht.

Wo ich dir jetzt nicht folgen kann, ist wie Du zur Annahme kommst, dass MAC und WLAN ausgerechnet in den IV einfließen?
- MAC und WLAN könnten genauso gut in den Key einfließen, oder in beides....

Direkte AES-Keys in den Binaries dabe ich nicht gesehen, aber ich glaube das wolltest Du mir auch nicht sagen... :rolleyes:

Als einen fixen Ansatz hätte ich da noch eine Speicheranlayse. Ich hatte schon probiert mal ein Memdump zu machen um mit den Tool der Coldbootjungs einen Schlüssel im RAM zu suchen, aber /dev/mem liefert nur 4kB und /dev/kmem versagt den Dienst dort gänzlich. (BTW: Bei Fritzbox ist MIPS auch in Little-Endian? Klar, oder nicht?)

Ansonsten werde ich mir mal ein paar Tabellen basteln... aber bevorzugt auf meinem Hauptrechner... der macht nicht so häufig einen Neustart... ;)
 
Als Ergänzung zum Bild oben nochmal kurz in Python:
Code:
key = 'This is a key123'
IV = 'This is an IV456'
message = "plainpassword_p1" + "plainpassword_p2" + "plainpassword_p3"
#Typischer Funktionsaufruf
obj = AES.new(key, AES.MODE_CBC, IV)
ciphertext = obj.encrypt(message)
print "C", binascii.hexlify(ciphertext[:16]),binascii.hexlify(ciphertext[16:32]),binascii.hexlify(ciphertext[32:])

#So wie oben aufgezeichnet
obj = AES.new(key, AES.MODE_CBC, IV)
ciphertext1 = obj.encrypt(message[:16])
obj = AES.new(key, AES.MODE_CBC, ciphertext1)
ciphertext2 = obj.encrypt(message[16:32])
obj = AES.new(key, AES.MODE_CBC, ciphertext2)
ciphertext3 = obj.encrypt(message[32:])

print "C", binascii.hexlify(ciphertext1),binascii.hexlify(ciphertext2),binascii.hexlify(ciphertext3)

#Und noch AVMlike codiert
print "U", avm_b32encode(ciphertext)
Ausgabe:
Code:
C 514315f2ac63a87a1764774b4ed5551b 4b2761798d8dcbe08230d255c698d489 b2ec8ecc033efef96d6490f27045b429
C 514315f2ac63a87a1764774b4ed5551b 4b2761798d8dcbe08230d255c698d489 b2ec8ecc033efef96d6490f27045b429
U KFBRL3VMMOUHUF2EO4FU4VKVDNFSOYLZRWG3XYECGDJFLRUY1SE2F2EOZQBT46XZNVSJB3TQIW1CS===
 
Fehlt halt avm_b32encode ... wird wohl etwas wie das hier sein (das ist meine Variante gewesen bei den ersten Untersuchungen):
Code:
#! /usr/bin/python3
import sys
import os

class Base32Exception(Exception):
        pass

class Base32:
        def Encode(self, input):
                if type(input).__name__ != 'bytes':
                        raise Base32Exception("Input data has to be of type 'bytes'.")
                if (len(input) % 5) != 0:
                        raise Base32Exception('Invalid length of binary input data.')
                output = b''
                index_input = 0
                while index_input < len(input):
                        binary = bytes(input[index_input:index_input+5])
                        p = int.from_bytes(binary, byteorder='big', signed=False)
                        s = b''
                        for j in range(8):
                                k = (p >> (j * 5)) & 31
                                c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456'[k]
                                s = bytes(c, 'ascii') + s
                        output += s
                        index_input += 5
                return output

        def Decode(self, input):
                if type(input).__name__ != 'bytes':
                        raise Base32Exception("Input data has to be of type 'bytes'.")
                if (len(input) % 8) != 0:
                        raise Base32Exception('Invalid length of text input data.')
                output = bytearray()
                index_input = 0
                while index_input < len(input):
                        out = bytearray(5)
                        val = 0
                        for j in range(8):
                                c = input[index_input + j]
                                try:
                                        v = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456'.index(chr(c))
                                except ValueError:
                                        raise Base32Exception('Invalid character found at text input data.')
                                val = (val << 5) + v
                        for k in range(5):
                                o = val & 255
                                out[4 - k] = o
                                val = val >> 8
                        output.extend(out)
                        index_input += 8
                return output

if __name__ == '__main__':
        rc = 1
        if sys.argv[1] == 'test':
                if Base32().Encode(b'\xFF\xFF\xFF\xFF\xFF') != b'66666666':
                        raise Base32Exception('Error encoding all-ones buffer.')
                if Base32().Encode(b'\x00\x00\x00\x00\x00') != b'AAAAAAAA':
                        raise Base32Exception('Error encoding all-zeros buffer.')
                if Base32().Encode(b'\x00\x44\x32\x14\xC7') != b'ABCDEFGH':
                        raise Base32Exception("Error encoding to 'ABCDFEGH'.")
                if Base32().Encode(b'\x39\x8A\x41\x88\x20') != b'HGFEDCBA':
                        raise Base32Exception("Error encoding to 'HGFEDCBA'.")
                if Base32().Decode(bytes('66666666', 'ascii')) != b'\xFF\xFF\xFF\xFF\xFF':
                        raise Base32Exception('Error decoding to all-ones buffer.')
                if Base32().Decode(bytes('AAAAAAAA', 'ascii')) != b'\x00\x00\x00\x00\x00':
                        raise Base32Exception('Error decoding to all-zeros buffer.')
                if Base32().Decode(bytes('ABCDEFGH', 'ascii')) != b'\x00\x44\x32\x14\xC7':
                        raise Base32Exception("Error decoding from 'ABCDFEGH'.")
                if Base32().Decode(bytes('HGFEDCBA', 'ascii')) != b'\x39\x8A\x41\x88\x20':
                        raise Base32Exception("Error decoding from 'HGFEDCBA'.")
                for i in range(10):
                        binary = bytes(os.urandom(i*5))
                        encoded = Base32().Encode(binary)
                        decoded = Base32().Decode(encoded)
                        if binary != decoded:
                                raise Base32Exception("Error comparing original byte array contents and result after encoding/decoding calls.")
                print('Tests done.', file=sys.stderr)
                rc = 0
        elif sys.argv[1] == 'encode':
                input = sys.stdin.buffer.read()
                output = Base32().Encode(input)
                sys.stdout.write(output.decode('ascii'))
                rc = 0
        elif sys.argv[1] == 'decode':
                input = sys.stdin.buffer.read()
                sys.stdout.buffer.write(Base32().Decode(input))
                rc = 0
        else:
                print("Unknown mode '{0:s}'.".format(sys.argv[1]), file=sys.stderr)
        sys.exit(rc)
(keine Tabs, da C&P aus einer Konsole)

Dann noch so etwas zum Analysieren meiner "probes" aus bekanntem Klartext, insbesondere zum Erkunden der "Padding-Bits":
Code:
#! /usr/bin/python3
import sys
import string
import codecs
import math
from base32 import Base32

class Encoding_Line(object):
        """members:
        encoded_bytes  - bytes(<encoded part of line>)
        encoded_binary - bytes(<base32 decoded data of encoded_text>)
        encoded_length - length of encoded_text
        text_length    - length of plain text encoded to encoded_text"""
        def __init__(self, line):
                parts = str.split(line, '\t')
                self.text_length = int(parts[0])
                self.encoded_length = int(parts[1])
                self.encoded_bytes = bytes(parts[2].rstrip('\n'), 'ascii')
                self.encoded_binary = Base32().Decode(self.encoded_bytes)

def binstring(input):
        out = ''
        for byte in input:
                bits = bin(byte)
                aligned = str('000000000' + bits[bits.index('0b')+2::])[-8::]
                out += aligned
        out = out[::-1]
        return out

def equal_start(bins):
        prefix = bins[0]
        while len(prefix) > 0:
                for b in bins:
                        next_b = True
                        for i in range(len(prefix)):
                                if b[i] != prefix[i]:
                                        prefix = prefix[:i]
                                        next_b = False
                                        break
                        else:
                                if next_b == False:
                                        break
                else:
                        return prefix
        return prefix

def print_changes(enc_bins, old_len, new_len):
        bins = []
        for e in enc_bins:
                bins.append(binstring(e))
        unused_bits = equal_start(sorted(bins))
        print('======================================================')
        print('encoded text length changed from {0} to {1}'.format(old_len, new_len))
        print('encoded text padded with {0} unused bits ({1} bytes)'.format(len(unused_bits),math.ceil(len(unused_bits) / 8)))
        print('======================================================')
        return

file=sys.argv[1]
Lines = []

with open(file) as input:
        for line in input:
                Lines.append(Encoding_Line(line))

input.close

# count characters within the encoded strings
# chars = dict()
# for el in Lines:
#       for c in el.encoded_text:
#               if c in chars:
#                       chars[c]+=1
#               else:
#                       chars[c]=1
# for e in sorted(chars.keys(),key=lambda x: x if not str.isdigit(x) else 'Z'+x):
#       print '{0} {1:d}'.format(e,chars[e])

# print length of plain text and encoded_data and append hexadecimal content of encoded_data
enc_len = 0
enc_bins = []
for el in Lines:
        if el.encoded_length != enc_len:
                if enc_len != 0:
                        print_changes(enc_bins, enc_len, el.encoded_length)
                enc_len = el.encoded_length
                enc_bins = []
        enc_bins.append(el.encoded_binary[-8::])
        needed_bits = el.text_length * 8
        needed_bytes = math.ceil(needed_bits / 5)
        print('{0:3d} {1:3d} {2:3d} {3:3d} {4:s}'.format(el.text_length, needed_bits, needed_bytes, el.encoded_length, ''.join(map(lambda x: format(x, '02X'), el.encoded_binary))))
print_changes(enc_bins, enc_len, -1)
So, jetzt ist aber wirklich Schluß und ich mische mich hier raus ... viel Erfolg.

Ich hatte halt niemanden zum Diskutieren und habe deshalb viel selbst probiert ... aber auch der Disassmbler (oder gdb) kann ja nicht schaden.

Wenn Du den Prozess nachvollziehen willst (ohne groß zu probieren), nimm Dir webdavcfginfo vor (das beschränkt sich auf das Wesentliche, auf config_set_pwfuncs hatte ich schon hingewiesen), disable den Watchdog (sonst fällt Dir die Box wg. gesperrter Resourcen vor die Füße) und schau Dir die Funktion an der Adresse bei Offset 12 (wenn ich mich richtig erinnere, dafür würde ich aber die Hand nicht ins Feuer legen) in der Parameterliste der genannten Funktion an. Solltest Du dabei nebenbei noch den gdb auf der 7490 zum Laufen bekommen, kannst Du mir einen Gefallen tun und mir erklären, wie Dir das gelungen ist.

So, sei nicht sauer, aber das war jetzt wirklich die letzte Nachricht meinerseits zu diesem Thema. Die Zusammensetzung der Eingangswerte für den AES-Aufruf kriegst Du alleine heraus, meinen Vorschlag, wie man das machen kann, habe ich schon unterbreitet.
 
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.