Seite 1 von 1

ACHTUNG: AdressLittle ist NICHT SO SICHER WIE BEWORBEN!

Verfasst: 19.05.2025, 19:00
von itsicherheit
Vorwort vom Team und Purgatory:

Moin,

es geht hier um ein etwas prikäreres Thema das ein wenig Vorlauf und Erklärungsbedarf des Teams (und meinerseits) benötigt um es verständlich zu machen. Es geht um das Programm Adress Little für das wir @Joachim-68VB einen exklusiven Bereich gegeben haben und ihm voller Vertrauen auch erweiterte Forenrechte für diesen Bereich gegeben haben. Vor etwa zwei Wochen wurde in dem Bereich ein Beitrag gepostet den ich, vor allem um Joachim-68VB zu schützen, erst einmal offline genommen habe und in unseren internen Bereich verschoben habe. Der Ersteller war @itsicherheit. Der Grund war, dass einige Aussagen getroffen wurden die ich sehr grenzwertig (wenn auch nicht direkt angreifend) fand und schlussendlich im Team auch so Anklang für meine Entscheidung fand. Ich editierte den Beitrag, und zwar so, dass er fundiert und nicht angreifend war, oder so verstanden werden konnte. Ich war im Dialog mit dem Ersteller des Beitrags als auch dem Programmierer von Adress Little. Gerade letzterem ließ ich meine editierte Version via PM zukommen mit der Bitte um eine Stellungsnahme via PM dazu, gab ihm also genug Zeit das prüfen zu können. Die Antwort war kurz, knapp und kam schnell. Zitat: "Könnt Ihr veröffentlichen." Zitat Ende.

Also tat ich das und brachte den von mir editierten Beitrag online. Es dauerte nicht einmal eine Stunde und Joachim-68VB löschte den Beitrag, ohne jegliche Antwort. Dazu ein Auszug aus unserem Moderationsprotokoll:
Screenshot.jpg
Unten ist zu sehen zu welcher Uhrzeit ich den Beitrag verschoben habe und oben wann er gelöscht wurde, auch der Grund wurde angegeben. Nun zum Grund warum ich den Beitrag editiert habe.
Es stand da, dass über diverse Foren, auch in PMs, mit dem Programmierer kommuniziert wurde, dass sein Programm nicht so sicher sei wie von ihm angegeben. Weiterhin wurde geschrieben, dass der Programmierer entweder überhaupt nicht reagiert oder nur mit Beschimpfungen. Da sowas bei uns im Forum überhaupt nichts zu suchen hat habe ich dementsprechende Textzeilen entfernt. Aber der Programmierer spiegelt mit seinem Verhalten ja wieder, dass zumindest irgendwas daran wahr zu sein scheint.

Wir lassen solches Verhalten nicht zu, nicht bei unseren Usern, selbst nicht bei uns. Wenn wir eine Plattform dazu geben ein Programm zu bewerben muss der jeweilige Programmierer auch bereit dazu sein mit Kritik, ob relevant oder nicht, umgehen zu können. Aus diesem Grunde haben wir Joachim-68VB, zumindest erst einmal temporär, die Sonderrechte entzogen und uns entschlossen diesen Beitrag nochmal zu posten. Leider fehlt uns das erste Drittel des Beitrags in dem der Ersteller auf die AES256 Verschlüsselung (mit Codezeilen) eingeht und sie zunächst einmal für korrekt einstuft. Aber dann kommt das Aber...
Originalbeitrag @itsicherheit

Er wirbt damit, dass eure Daten alle AES256 verschlüsselt sind.
Ja, stimmt. Sie wurden mit dem Algorithmus umgewandelt; schaut man sich die Datei im Detail an, sieht das auf den ersten Blick auch gut aus:

Code: Alles auswählen

41 44 52 4C 36 34 30 30 53 2D 4A 53 20 12 B5 24             ADRL6400S-JS µ$
Am Anfang der Datei sehen wir Magic, was erstmal nichts ungewöhnliches ist. Ist gerne gesehen, wenn man sich auf den Inhalt der Datei verlässt, statt der Datei-Endung, die eigentlich nichtsaussagend ist.
Schauen wir mal, was das genau ist:

Code: Alles auswählen

public static readonly string CryptDefault = "ADRL6400S-JS ";
Alles klar, wird also irgend ein Default-Wert sein. Die Details, wie die Datei geöffnet und gespeichert werden sind hier nicht sonderlich von Relevanz; dafür umso mehr, wie eure Daten "verschlüsselt" und "entschlüsselt" werden.
Es wird jetzt brisant und man muss kein Softwareentwickler sein, um das zu verstehen.

Ich erkläre es zuerst für jedermann:
Der Key ist quasi der Schlüssel zu euren Daten und der Init Vector ist eine Startinformation, ab der der Algo weitergehen kann.
Wenn die Verschlüsselung das Ziel ist, ist der Init Vector die Startadresse, ähnlich wie das bei Maps wäre, wenn ihr wohin navigiert.

Nun ist es aber so, nun ist es aber so, (Edit Purgatory:) dass man sich gedacht hat, man (Edit Purgatory Ende) könne die Generierung dieser Daten, die PRO VERSCHLÜSSELUNG EINMALIG ZU SEIN HABEN(!), umgehen kann, indem er diese aus dem Passwort der Benutzer generiert. Geniale Idee, könnte man meinen...

Code: Alles auswählen

public static string Text_AES_Codieren(string TextInput, string TextPassword) {
    string str = "";
    if (TextPassword.Length > 0 & TextInput.Length > 0) {
        using (AesManaged aesManaged = new AesManaged()) {
            byte[] salt = new byte[8] { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6,  (byte) 7,  (byte) 8 };
            using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(TextPassword, salt)) {
                aesManaged.Key = rfc2898DeriveBytes.GetBytes(aesManaged.Key.Length);
                aesManaged.IV = rfc2898DeriveBytes.GetBytes(aesManaged.IV.Length);
            }
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, aesManaged.CreateEncryptor(), CryptoStreamMode.Write);
            byte[] bytes = Encoding.UTF8.GetBytes(TextInput);
            cryptoStream.Write(bytes, 0, bytes.Length);
            cryptoStream.Close();
            str = Convert.ToBase64String(memoryStream.ToArray());
        }
    }
    return str;
}
Egal ob beim ver- oder entschlüsseln, es wird immer derselbe Salt angewandt und dann ist das auch noch so ein einfacher Salt (1, 2, 3, 4, 5, 6, 7, 8).
Das bedeutet ganz konkret, dass jeder Angreifer mit Mustererkennung und Rainbow-Tables kinderleicht an die gespeicherten Daten kommt.

Noch dazu ist der verwendete Algorithmus mit modernen Grafikkarten relativ schnell in seiner Standardkonfiguration, die hier angewandt wird, knackbar. Minuten sollten hier haushoch ausreichen.

Eine bessere Implementierung wäre z.B.:

Code: Alles auswählen

new Rfc2898DeriveBytes(password, salt, 200_000, HashAlgorithmName.SHA256);
Wobei der Salt mit einem RNG aus System.Cryptography generiert wurde. Entweder pro Datenbank oder pro Eintrag, was natürlich deutlich sicherer wäre.
Weiterhin ist der verwendete Generator (AesManaged) obsolet und sollte auf gar keinen Fall mehr verwendet werden! Hier wäre

Code: Alles auswählen

Aes.Create()
die korrekte Wahl.

Selbiges geht übrigens auch für die Entschlüsselung dieser Daten:

Code: Alles auswählen

public static string Text_AES_Decodieren(string TextInput, string TextPassword) {
    string str = "";
    if (TextPassword.Length > 0 & TextInput.Length > 0) {
        using (AesManaged aesManaged = new AesManaged()) {
            byte[] salt = new byte[8]
            {
      (byte) 1,
      (byte) 2,
      (byte) 3,
      (byte) 4,
      (byte) 5,
      (byte) 6,
      (byte) 7,
      (byte) 8
            };
            using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(TextPassword, salt)) {
                aesManaged.Key = rfc2898DeriveBytes.GetBytes(aesManaged.Key.Length);
                aesManaged.IV = rfc2898DeriveBytes.GetBytes(aesManaged.IV.Length);
            }
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, aesManaged.CreateDecryptor(), CryptoStreamMode.Write);
            try {
                byte[] buffer = Convert.FromBase64String(TextInput);
                cryptoStream.Write(buffer, 0, buffer.Length);
                cryptoStream.Close();
                str = Encoding.UTF8.GetString(memoryStream.ToArray());
            } catch (Exception ex) {
                ProjectData.SetProjectError(ex);
                str = "";
                ProjectData.ClearProjectError();
            }
        }
    }
    return str;
}
Ich weiß nicht, seit wann dieser Code in der Anwendung ist, allerdings ist er genau so qualitativ hochwertig, wie das was z.B. ChatGPT ausgeben würde.

Als Letztes möchte ich hier noch den Hinweis geben, dass das Programm neben dem unsicheren Code auch noch verwendete Sicherheitsschlüssel und Passwörter enthält:

Code: Alles auswählen

public static readonly string KeyCtr1 = "<1j<Wm§2T]B&|S59XJ3qa62uu?CWdHo-]";
public static readonly string KeyCtr2 = "M<v/E/q9&+wmh-B\\JP!(6prAdF/d)3Qz";
public static readonly string KeyCtr3 = "KQ\\j-)J$RH7€Y7Sy06%2/-7r4q+q&XY[";
public static readonly string KeyCtr4 = "§tDGez~%iFfg~q%-/Qzy|HG€4if4o§[";
public static readonly string KeyCtr5 = "gM1YEu%\\/3$a%j)J[rZoPL*d?VJ4CcPY";
public static readonly string KeyCtr6 = "y5]kszi%-mx[$*g1üugx1~oWd~=b]M4=D1";
public static readonly string KeyCtrDark1 = "dgBbDQAF?/KmoEr5h4-wTZs#%Ce*P)(3";
public static readonly string KeyCtrDark2 = "$Z21Ksnp8g)yHa0(Wf5w!ECbRxtrF?o6";
public static readonly string KeyCtrDark3 = "Dya1G9tgnV0+L4$(BXR%p6q2ONd7/PE=";
public static readonly string KeyCtrDark4 = "XPqsYvd3A\\rZ5%OkoiuDHnM0Gf*/NaS&";
public static readonly string KeyCtrDark5 = ")*CjxEMrGiS-pd=c%$0LshFa7#K+nA23";
public static readonly string DataCryptReg = "Gp6pdwRyNmGiZqPIkT7jJtIxTlSp9I2MqBimeQLWGfkNpEW69wkXPT58dFTIxB";
public static readonly string Adrl6400Pass = "ADRL6400";
public static readonly string DefaultAESPass = "Hg#pUr85/%)$Achim";
Wobei man hier sagen muss, dass die resultierenden Werte selten genutzt werden - so scheint es zumindest.

Beispiel:

Code: Alles auswählen

Grund.Text_AES_Codieren(Grund.KeyCtr1 + Grund.KeyCtrDark1, Grund.KeyCtrDark1);
Hier wird der Schlüssel zwar verwendet, aber nicht zwischengespeichert.

Anders sieht es bei der Win32-Interop aus, die er gebaut hat:

Code: Alles auswählen

public static readonly string Key10 = "uzt64vugi~6r654";
public static readonly string Key20 = "jhvgiu6r76u+guzgu";
public static readonly string Key30 = "hg567/&($/&&§%";
public static readonly string Key40 = "hguzv#56787ubhjAAAj";
public static readonly string Key50 = "QwPaS!*s/bk)ygx?";
public static readonly string Key60 = "vEn*Nr7gJLRGX4z-";
public static readonly string Key70 = "5N4\\zXUeOTfZF&WM";
public static readonly string Key80 = "nuMJ1#p(s4P3Y&8z\\Sx-GdZh+Rb6XAfK";
public static string KeyAll0 = ")p(bMX%hM1~T+J5!§xksgo\\&ywtDL>e5~Z87vdz-ELR$+a§8m-0aU=su7K_z_#XT";
Ich habe den Code nicht zu 100% durchblickt, aber bei der Katastrophe, die der Decompiler mir auf den Tisch geworfen hat, ist das auch kein Wunder. Die Kernimplementierung der Anwendung bleibt erhalten ...

Originalbeitrag @itsicherheit Ende
Schlussworte vom Team und Purgatory:

Wir möchten, dass alle Beteiligten die meinen hier eine Meinung abgeben zu wollen oder vielmehr zu können, fundiert und pragmatisch antworten. Anfeindungen, von welcher Seite auch immer, werden von uns konsequent, und ohne Vorwarnung, editiert oder auch gelöscht.