WorstFit: Die verborgenen Transformers von Windows ANSI enthüllt
(blog.orange.tw)- Die Best-Fit-Zeichenkonvertierung von Windows ersetzt beim Umwandeln von UTF-16-Strings in ANSI-Codepages Zeichen durch ähnlich aussehende Zeichen; dieses Verhalten bildet die WorstFit-Angriffsfläche für Path Traversal, Argument Injection und RCE
- Das Problem entsteht aus dem Zusammenspiel von ANSI-APIs, C/C++-Runtime, vom Compiler eingefügtem Startcode und der Nutzung nicht-breiter Zeichen-APIs durch Entwickler; betroffen sind die Pfade
GetCommandLineA,GetEnvironmentVariableA,getenvundint main() - Bei CVE-2024-4577 wurde U+00AD in chinesischen/japanischen Codepages zu
-, wodurch der PHP-CGI-Patch umgangen wurde; beim Filename Smuggling werden¥,₩und der Fullwidth Slash zu/oder\\und erzeugen Pfadverwirrung - Argument Splitting kann mit Fullwidth-Doppelanführungszeichen oder Yen-/Won-Zeichen Befehlszeilen-Trennzeichen erzeugen und so Argumente in CLI-Tools wie
wget.exe,tar.exe,openssl.exeundjava.exeeinschleusen; mit den üblichen Argument-Escaping-Methoden in PHP, Python, Node.js und Rust ist das schwer zu verhindern - Zur Abmilderung sollte man die UTF-8-Option von Windows aktivieren oder als Entwickler die Wide Character API sowie Wide-Character-Pfade wie
_wgetcwd,_wgetenvundwmain()verwenden; bis Microsoft UTF-8 nicht auf allen Windows-Editionen standardmäßig aktiviert, könnten ähnliche Probleme weiter auftreten
Windows-Encoding-Struktur und Best-Fit
- Windows nutzte anfangs ANSI-Codepages, und je nach Sprachraum unterschieden sich die Codepages wie 1252, 932, 936, 949 und 950
- ACP (ANSI Code Page) wird für die meisten Anwendungen und Systemeinstellungen wie Dateizugriffe und Umgebungsvariablen verwendet
- OEMCP (Original Equipment Manufacturer Code Page) wird vor allem für Gerätekommunikation wie das Lesen/Schreiben in der Konsole verwendet
chcpzeigt nicht die ACP, sondern die OEMCP an und ist daher kein Mittel zur Prüfung der ACP, um die es in dieser Untersuchung geht
- Windows wechselte Mitte der 1990er Jahre zu Unicode, und die zentralen APIs basieren heute auf UTF-16 und Wide Characters
- Kern-APIs für Dateisystem, Systeminformationen und Textverarbeitung wurden auf Wide-Character-APIs umgestellt
- UTF-8-Unterstützung existiert zwar, ist in den meisten Sprachen aber nicht standardmäßig aktiviert und wird im Artikel als Beta-Stadium beschrieben
- Aus Gründen der Abwärtskompatibilität bietet die Windows-API sowohl ANSI- als auch Unicode-Versionen an
- ANSI-APIs tragen den Suffix
A, etwaGetEnvironmentVariableA - Unicode-APIs tragen den Suffix
W, etwaGetEnvironmentVariableW - Wird eine ANSI-API aufgerufen, konvertiert Windows den internen UTF-16-String mit
RtlUnicodeStringToAnsiStringoderWideCharToMultiBytein einen ANSI-String
- ANSI-APIs tragen den Suffix
Wie Best-Fit zu WorstFit wird
- Best-Fit bezeichnet das Verhalten, UTF-16-Zeichen, die sich in der Ziel-ANSI-Codepage nicht exakt darstellen lassen, auf ähnlich aussehende oder ähnlich wirkende Zeichen abzubilden
- So wird etwa
∞U+221E in Windows-1252 auf8abgebildet √π⁷≤∞kann nach dem Durchlaufen einer ANSI-API zu\"vp7=8\"werden
- So wird etwa
- Das Mapping verhält sich je nach Codepage unterschiedlich
¥U+00A5 wird in der japanischen Codepage 932 auf\\abgebildet- In der mitteleuropäischen Codepage 1250 wird es zu
Y - In den meisten anderen Codepages bleibt es unverändert
- Dieselbe Konvertierung tritt nicht nur bei direkten Windows-API-Aufrufen auf, sondern auch in CRT-Funktionen und über den gewöhnlichen
main-Funktionspfad- In nicht-breiten CRT-Funktionen wie
getenvwird die Best-Fit-Konvertierung angewendet - Auch wenn Argumente und Umgebungsvariablen in der Form
int main(int argc, char* argv[], char* envp[])empfangen werden, greift die Konvertierung ein - Grund ist die Kombination aus vom Compiler eingefügtem CRT-Startcode und der Nutzung von ANSI-Windows-APIs
- In nicht-breiten CRT-Funktionen wie
- Zur Prüfung der Mappings kann man auf Best-fit Mapping Grepper und die rohen WindowsBestFit-Mapping-Daten von Unicode.org verweisen
Erster WorstFit-Fall: PHP-CGI CVE-2024-4577
- CVE-2024-4577 ist ein WorstFit-Angriffsfall, bei dem sich PHP-CGI-Server mit auf chinesische/japanische Codepages gesetzter Konfiguration allein mit einer Anfrage
?%ADskompromittieren ließen- Betroffene Codepages sind 932 (Japanisch), 936 (Vereinfachtes Chinesisch) und 950 (Traditionelles Chinesisch)
- Das problematische Zeichen ist
U+00AD
- Die PHP-CGI-Schwachstelle von 2012 war eine Argument-Injection, die dadurch entstand, dass Apache den Query-String automatisch als erstes Argument des CGI-Programms behandelte
- Mit angehängtem
?-swaren Quellcode-Leakage und RCE möglich - Der PHP-Patch stoppte das Parsen von Argumenten, wenn der Query-String mit einem Bindestrich begann
- Mit angehängtem
- Durch Best-Fit wird das Soft Hyphen U+00AD in chinesischen/japanischen Codepages in
-umgewandelt, wodurch der bestehende Patch umgangen wird?%ADskann aus Sicht von PHP-CGI wie-sfunktionieren- Dieser Fall brachte das Forschungsteam erstmals mit dem Begriff Best-Fit in Kontakt
Filename Smuggling: Wenn Pfadzeichen umgewandelt werden
- Filename Smuggling ist ein Angriff, bei dem in Dateinamen enthaltene Unicode-Zeichen im ANSI-API-Pfad zu
/oder\\werden und so Path Traversal ermöglichen können- Relevante APIs sind unter anderem
GetCurrentDirectoryA,getcwd,FindFirstFileA,findfirst*undGetFullPathNameA - Betroffene Codepages sind 874, 125x, 932 (JP) und 949 (KR)
- Bedrohliche Zeichen sind
/U+FF0F,\U+FF3C,¥U+00A5 (JP) und₩U+20A9 (KR)
- Relevante APIs sind unter anderem
d8.exe, die Developer Shell von Chrome V8, verwendet internGetCurrentDirectoryA(), um das aktuelle Arbeitsverzeichnis zu ermitteln- Kann ein Arbeitsverzeichnis mit schädlichen Unicode-Zeichen erzeugt werden, wird es beim Zugriff über die ANSI-API in ein Path-Traversal-Payload umgewandelt
- So ist beispielsweise ein unbeabsichtigter Zugriff auf
C:/windows/win.inimöglich
- Die Windows-Implementierung von mrubys
Dir.getwd()hängt von der ANSI-CRT-Funktion_getcwd()ab- Der Rückgabewert kann kontaminiert werden
- Daraus kann Path Traversal folgen
Cuckoo Sandbox: Von Path Traversal zu RCE
- Der Zugriff von Python auf das Windows-Dateisystem konnte je nachdem, ob Zeichenketten wide oder narrow waren, die Wide API oder die ANSI API verwenden
- Seit PEP 529 ist die Windows-Dateisystemkodierung auf UTF-8 standardisiert
- Python 2 und Python 3 vor Python 3.6 blieben für WorstFit-Angriffe anfällig
- Cuckoo Sandbox ist eine automatisierte Plattform zur Malware-Analyse, und die aktuelle offizielle Version hängt von Python 2.7 ab
- Cuckoo besteht aus dem Cuckoo Host und einem VM-Cluster
- Hochgeladene Samples werden isoliert in VMs ausgeführt, und Netzwerkpakete, abgelegte Dateien und Logs werden über eigene Mechanismen synchronisiert
- Wenn Malware eine abgelegte Datei mit einem Unicode-Dateinamen erzeugt, kann bei der Pfadverarbeitung in Python auf dem Cuckoo Host Path Traversal auftreten
- Das beispielhafte PoC erzeugt den Pfad
AAAA\u00a5..\u00a5..\u00a5..\u00a5..\u00a5..\u00a5conf\u00a5cuckoo.conf - Nach Abschluss der Analyse wird ein Python-Dateizugriff ausgelöst, wenn der Nutzer in der Weboberfläche auf den Download-Button klickt
- Der Cuckoo Host verarbeitet dann den konvertierten Pfad mit
../und kann dem Angreifer sensible Daten senden
- Das beispielhafte PoC erzeugt den Pfad
- Der Angreifer kann
cuckoo.confherunterladen und sensible Informationen sammeln, die zur Berechnung der Flask-PIN nötig sind, um RCE auf dem Sandbox Host zu erreichen- Das Demo-Video ist als Video 11 verfügbar
Argument Splitting: Best-Fit verändert das Parsing der Kommandozeile
- Argument Splitting ist ein Angriff, bei dem die Kommandozeichenfolge über die Ausgabe von
GetCommandLineAoder über den non-Unicode-int main()-Pfad verändert wird, sodass Argumente getrennt werden- Relevante APIs und Pfade sind
GetCommandLineA,int main() - Betroffene Codepages sind 874, 125x, 932(JP), 949(KR)
- Kritische Zeichen sind
"U+FF02,\U+FF3C,¥U+00A5(JP),₩U+20A9(KR)
- Relevante APIs und Pfade sind
- Der Beispielcode in PHP führt
wget.exe -qaus, nachdem die URL mitescapeshellarg()sicher eingefasst wurde, aber mit der Eingabe" --use-askpass=calc "lässt sichcalc.exestarten- Dieselbe Eingabe wird auch dann nicht abgewehrt, wenn man auf Node.js, Rust oder Python wechselt
- Es funktioniert sogar im Beispiel mit aktuellen Python-Versionen und
subprocess.run(["wget", "-q", ...])
- Windows übergibt an einen neuen Prozess die komplette Kommandozeile als einzelne Zeichenkette, die das ausführbare Programm selbst parst
- Anders als bei UNIX-artigen Systemen wird nicht immer ein Argument-Array übergeben
- Die API
CreateProcessnimmt den ParameterlpCommandLinedirekt entgegen
- Bei üblichem Kommandozeilen-Parsing sind Leerzeichen und Tabs, doppelte Anführungszeichen und Backslashes entscheidend
- Leerzeichen und Tabs trennen Argumente, wenn nicht der quote mode aktiv ist
"schaltet den quote mode um\maskiert in bestimmten Sequenzen doppelte Anführungszeichen und Backslashes
- Die Standardbibliotheken der meisten Sprachen maskieren Benutzerargumente gemäß diesen Regeln, aber das Escaping endet vor der Best-Fit-Konvertierung
- PHP
escapeshellargersetzt doppelte Anführungszeichen durch Leerzeichen, setzt das Argument in Anführungszeichen und verarbeitet Backslashes - Python
subprocessmaskiert mitlist2cmdlinegemäß den Microsoft-CRT-Regeln für das Kommandozeilen-Parsing - Wenn danach bei der ANSI-Konvertierung
"U+FF02 in"U+0022 umgewandelt wird, ändert sich die ursprüngliche Kommandozeilensyntax
- PHP
- Auch Programme, die nur
int main()verwenden, können anfällig sein- Der Compiler erzeugt
mainCRTStartupim Binärprogramm, und diese Startfunktion ist mit der CRT-Bibliothek verbunden - Wenn die CRT intern die Kommandozeile über die ANSI API holt und parst, greift die Best-Fit-Konvertierung ein
- Deshalb ist es selbst mit den Standardbibliotheken bestimmter Programmiersprachen schwer, den Angriff vollständig zu verhindern
- Der Compiler erzeugt
Reale Fälle von Argument Splitting
- ElFinder ist ein Open-Source-Web-Dateimanager mit PHP-Backend, der standardmäßig Windows-Server sowie das Erstellen und Entpacken von Archiven unterstützt
- Die Archivverarbeitung ist über die Ausführung von Shell-Kommandos implementiert, und die Argumente werden mit
escapeshellargmaskiert - Für das tar-Format wird das in Windows integrierte
tar.exeverwendet - Mit einem tar-Dateinamen wie
aaa" "--use-compress-program=calc" "bbb.tarlässt sich das Argument--use-compress-programeinschleusen und beliebiger Befehlscode ausführen - Die Demo basiert auf einem auf Englisch konfigurierten Windows-Server mit Code Page 1252, und laut Zusammenfassung sollte sie auch auf 125x-Codepages und Code Page 874 funktionieren
- Das Demo-Video ist als Video 12 verfügbar
- Die Archivverarbeitung ist über die Ausführung von Shell-Kommandos implementiert, und die Argumente werden mit
- Im Fall der in TortoiseGit verwendeten modifizierten
plink.exekann die Eingabe einer bösartigen URI in das Clone-Feld Codeausführung auslösen- Details finden sich in der curated list
- Das Demo-Video ist Video 13
- RStudio unterstützt SVN-Versionsverwaltung; wenn sich in einem bösartig präparierten Ordner ein SVN-Projekt befindet, kann mit einem einzigen Klick der Rechner gestartet werden
- Details finden sich in der curated list
- Das Demo-Video ist Video 14
- Beim Fall von Microsoft Excel handelt es sich um CVE-2024-49026, das Argument Splitting mit der Windows-Funktion „Open-With“ kombiniert
- Windows verwaltet für Dateierweiterungen eine Handler-Tabelle, die sich mit
ftypeundassocprüfen lässt - Dateinamen werden Teil der Argumente des Handler-Programms, sodass sich der Angriff über den Dateinamen anwenden lässt
- Ein Dateiname, bei dem Punkte, Slash, Backslash und doppelte Anführungszeichen in ihre fullwidth-Formen umgewandelt wurden, verursacht bei
Excel.exeeine Argument-Injection - Excel selbst hat keine zusätzlichen Argumente, die sich gut für eine weitere Ausnutzung eignen, daher wurden NTLM Relay und RBCD/ADCS zusammen genutzt, um RCE zu erreichen
- Das Demo-Video ist als Video 15 verfügbar
- Windows verwaltet für Dateierweiterungen eine Handler-Tabelle, die sich mit
Verwirrung bei Umgebungsvariablen
- Environment Variable Confusion entsteht dadurch, dass
GetEnvironmentVariableA,GetEnvironmentStringsAundchar *getenv()Best-Fit-konvertierte Versionen von Umgebungsvariablen zurückgeben- Betroffene Codepages und problematische Zeichen sind nicht abschließend bestimmt
- Im Fall von Apache HTTPd ist der Bereich 0x00-0xFF relevant
- Damit dieser Angriff funktioniert, müssen Umgebungsvariablen vom Nutzer kontrollierbar sein
- Das ist etwa der Fall, wenn ein Elternprozess Informationen an einen von ihm erzeugten Kindprozess weitergibt
- Bei CGI werden viele HTTP-Anfragedaten wie Query-String und HTTP-Header als Umgebungsvariablen übergeben
- Das Beispiel zur Umgehung einer WAF behandelt den Fall, dass ein CGI-Skript wie ein Routing-Service arbeitet
- In der Apache-Konfiguration gibt es eine Regel, die
REQUEST_URImit/adminablehnt, um den Remote-Zugriff auf/cgi.pl/adminzu blockieren - Wegen des WorstFit-Verhaltens von Windows Perl ist eine Umgehung möglich, wenn Teile von
admindurch Best-Fit-Äquivalente ersetzt werden - In Code Page 1250 wird
àU+00E0 bei der ANSI-Konvertierung zua - Die Anfrage
/cgi.pl/%E0dminerscheint serverseitigen Regeln als anderer Pfad, wird aber vom Perl-CGI-Skript bei der Auswertung vonPATH_INFOüber ANSI-APIs als/adminverarbeitet
- In der Apache-Konfiguration gibt es eine Regel, die
- Bei PHP-CGI unter Windows wurden in bestimmten Konfigurationen ein Oracle zur Dateiexistenzprüfung und potenziell LFI bestätigt
- Ursache ist die Art, wie
PATH_INFOund andere pfadbezogene Umgebungsvariablen verarbeitet werden - Eine Anfrage wie
/index.php/foo/barwird aus Sicht von Apache als Umgebungsvariablen wieREDIRECT_URL,REQUEST_URI,PATH_INFOundPATH_TRANSLATEDübergeben - Allein anhand dieser Informationen lässt sich die Grenze zwischen dem PHP-Dateinamen und zusätzlichem
PATH_INFOnicht eindeutig bestimmen;php-cgi.exeinterpretiert dies
- Ursache ist die Art, wie
- In japanischen Codepages führt die Nutzung von
¥dazu, dass Webserver und PHP-CGI Pfade unterschiedlich interpretieren- Der Webserver behandelt
/..¥..¥windows/win.ini/foovollständig als zusätzlichesPATH_INFO - PHP-CGI erhält einen konvertierten Wert wie
REQUEST_URI=/index.php/..\\..\\windows/win.ini/foound gerät bei der Trennung zwischen tatsächlicher PHP-Datei undPATH_INFOin Verwirrung - Unter Apache ermöglicht der Unterschied in den Antworten für nicht vorhandene und vorhandene Dateien ein File-Existence-Oracle
- Wenn unter IIS die Direktive
doc_rootgesetzt ist, ermöglicht ein Pfad wie/index.php/..¥..¥..¥windows/win.ini/ein Include vonC:\\Windows\\win.iniund damit LFI - Ist die eingebundene Datei ausführbar oder enthält vom Nutzer kontrollierbaren Code, kann dies potenziell zu RCE führen; dieses Szenario wird in realen Anwendungen jedoch eher als seltener Bug eingestuft
- Der Webserver behandelt
Schwierigkeiten bei Offenlegung und Behebung
- Das Forschungsteam meldete mehrere Probleme in Programmiersprachen, Open-Source-Projekten und eingebauten Windows-CLI-Programmen an die jeweiligen Upstream-Maintainer
- Die meisten Diskussionen gab es bei Argument Splitting
- Einige Anbieter betrachten bereits die Weitergabe von Nutzereingaben an die Kommandozeile selbst als Schwachstelle
- Unklare Zuständigkeiten waren ebenfalls ein Problem
- Der problematische Code verteilt sich auf das beim Kompilieren automatisch eingefügte
mainCRTStartup()und interne ANSI-API-Aufrufe in MSVCRT/UCRT - Es ist schwer zu trennen, ob das Problem darin liegt, dass Entwickler kein
wmain()verwenden, oder ob die CRT die Kommandozeile falsch aufteilt undmain()dadurch falsche Argumente übergibt - Einige Projekte liefern nur Quellcode aus, während vorgebaute Windows-Executables von freiwilligen Dritten im Internet verteilt werden
- Der problematische Code verteilt sich auf das beim Kompilieren automatisch eingefügte
- Eine Behebung ist nicht so einfach, wie
main()nur durch eine Wide-Character-Version zu ersetzen- Wenn sich die Funktionssignatur ändert, müssen Variablendefinitionen und die Argument-Parsing-Logik von
char *aufwchar_t *umgeschrieben werden - Dieser Prozess ist mühsam und fehleranfällig
- Wenn sich die Funktionssignatur ändert, müssen Variablendefinitionen und die Argument-Parsing-Logik von
- Curl antwortete, es handle sich um Windows-Funktionalität und man plane keine Behebung; die von Microsoft portierte Curl-Version ändert den Entry Point auf
wmain(), sodass das in Windows integriertecurl.exenicht betroffen ist- Die offiziellen Build-Binaries von Curl sind von Argument-Splitting-Angriffen betroffen
- Der vollständige Bericht ist auf HackerOne veröffentlicht
- OpenSSL kann Argumente über die Umgebungsvariable
OPENSSL_WIN32_UTF8im Wide-Character-Format verarbeiten- Ursprünglich sollte das Probleme bei der UTF-8-Anzeige in der UI beheben, mildert aber auch Argument-Splitting-Angriffe
- Bei der Standardnutzung von OpenSSL wissen Entwickler oft nicht, dass sie diese Umgebungsvariable setzen müssen; über das Argument
-engineist beliebige Codeausführung möglich
- Die offizielle Perl-Distribution stellt keine vorgebauten Windows-Executables bereit; häufig genutzt werden Drittanbieter-Installer wie Strawberry Perl und ActiveState Perl
- Beide Distributionen sind von Argument-Splitting-Angriffen betroffen
- Nach Gesprächen mit den Perl-Maintainern lautete das Fazit, es handle sich eher um einen Microsoft- als um einen Perl-Bug; der Fall ist derzeit ungelöst
- An Microsoft wurden drei Fälle an das MSRC gemeldet, die zunächst alle wegen Nichterfüllung der Schweregradkriterien abgelehnt wurden
- Erst nach mehreren Wiederaufnahmen wurde der Excel-Fall im dritten Anlauf akzeptiert
- Die anderen Fälle sind bis heute ungelöst
- Das MSRC antwortete, dass diese Fälle auf einer Schwachstelle beruhen, bei der eine separate Anwendung nicht vertrauenswürdige Eingaben in die Kommandozeile einfügt und ausführt; die Technik selbst, die eine Ausnutzung ermöglicht, erfülle nicht die Kriterien für eine Schwachstelle
- Auch CERT/CC wurde um Hilfe gebeten; einige Monate später ergänzte Microsoft die Dokumentation von
GetCommandLineAum eine Sicherheitswarnung- Die Warnung wurde nur bei
GetCommandLineAergänzt, obwohl es weitere ANSI-APIs gibt, bei denen Vorsicht nötig ist
- Die Warnung wurde nur bei
Gemeldete betroffene Ziele und Status
- Im Verlauf der Offenlegung wurden folgende Punkte bestätigt und gemeldet
- 2024/05/07: PHP
php-cgi.exe— CVE-2024-4577 - 2024/06/13: Curl Official Build — Won’t Fix
- 2024/06/13: Apache Subversion
svn.exe— CVE-2024-45720 - 2024/06/16: Microsoft Tar
tar.exe— Won’t Fix - 2024/06/19: Microsoft Excel
excel.exe— CVE-2024-49026 - 2024/06/19: Microsoft PhoneBook
rasphone.exe— Won’t Fix - 2024/06/19: Oracle Java
java.exe— Pending Fix - 2024/06/19: Perl
perl.exe— Won’t Fix - 2024/07/15: Perforce
p4.exe— CVE-2024-8067 - 2024/08/05: PostgreSQL
psql.exe— Won’t Fix - 2024/08/08: Putty
plink.exe— Fixed - 2024/08/19: OpenSSL
openssl.exe— Other - 2024/08/19: wkhtmltopdf
wkhtmltopdf.exe— EOL - 2024/08/19: GNU Wget — No Reply
- 2024/05/07: PHP
Gegenmaßnahmen und verbleibende Angriffsfläche
- Da WorstFit ein Problem auf Betriebssystemebene ist, könnten ähnliche Probleme weiterhin immer wieder auftauchen, bis Microsoft UTF-8 in allen Windows-Editionen standardmäßig aktiviert
- Nutzer können prüfen, ob die UTF-8-Option in Windows aktiviert ist, und sie einschalten
- Diese Funktion ist noch als Beta gekennzeichnet, und ob sie Nebenwirkungen hat, ist nicht sicher
- Entwickler sollten nach Möglichkeit die Wide Character API verwenden
- Auch die CRT bietet Wide-Character-Versionen wie
_wgetcwdund_wgetenvan - Wenn weiterhin Nicht-Wide-Pfade verwendet werden, könnte die interne Implementierung ANSI-APIs aufrufen und dadurch für WorstFit-Angriffe anfällig sein
- Auch die CRT bietet Wide-Character-Versionen wie
- Aufgrund der Abwärtskompatibilität von Windows könnte es weitere Stellen geben, an denen sich ANSI-APIs verbergen
- Beispielsweise könnten Windows-Registry-Abfragen wie
RegQueryValueAbetroffen sein, allerdings müssten dafür konkrete Angriffsszenarien gefunden werden - Das Forschungsteam beobachtete Best-Fit-Verhalten auch in Active Directory
- Beispielsweise könnten Windows-Registry-Abfragen wie
1 Kommentare
Meinungen auf Hacker News
Das ist ein ziemlich kniffliges Problem. Microsofts „Best Fit“-Code-Mapping ist ein öffentlicher, aber im Grunde „gefühlsbasierter“ Mapper, der den weiten Unicode-Raum nach ASCII überführt, und er steckt im ganzen System
Dieser Mapper wird standardmäßig an sehr vielen Stellen eingebunden, und angesichts dessen, wie Microsoft Abwärtskompatibilität versteht, scheint er auch weiterhin enthalten bleiben zu müssen. Exploits entstehen meist daraus, dass ungewöhnliche Codepoints „gefühlt“ auf Slashs, Bindestriche oder Anführungszeichen gemappt werden. Innerhalb moderner Sprachen werden sie als korrektes Unicode geprüft, aber sobald sie an Shell-Befehle oder die Win32-API übergeben werden, findet nach der Übergabe der Kontrolle eine andere Art der reduzierenden Konvertierung statt. Wie der curl-Maintainer sagte: „curl ist hier das Opfer“; die Frage ist, wer der Täter ist. Wenn ein Server Nutzereingaben beim Validieren anders glattbügelt als beim Übergeben an Systembibliotheken, entsteht am Ende ein Problem. Eine Option, die Best-Fit-Konvertierung auf Win32-Seite abzuschalten, könnte eine Lösung sein, aber ich bin kein Windows-Experte, also ist das nur eine Vermutung. Selbst dann interagiert man weiter mit offiziellen APIs oder Software, die das noch nicht abgeschaltet hat
"w"enden, nicht auf"a". Dieser Ansatz löst, wenn man das Präfix"\\?\"verwendet oder das Manifest korrekt setzt, zugleich auch das Problem mit Pfaden über 260 Zeichen; das ist seit Windows XP möglich und wird seitdem empfohlenIch weiß nicht recht, warum Nicht-Unicode-APIs immer noch so verbreitet sind. Es ist schwer vorstellbar, dass das aus dem Wunsch heraus geschieht, Windows 98 oder Windows 2000 zu unterstützen
Was außerdem nötig ist, ist eine Art Linting. In modernen Anwendungen gibt es normalerweise keinen Grund, ANSI-WinAPI-Funktionen aufzurufen. Man könnte auch die Locale auf UTF-8 setzen und nur die 8-Bit-Funktionen verwenden, aber ich weiß nicht, wie gut das funktioniert. Soweit ich weiß, gibt es auch einige Einstellungen und Header, die dafür sorgen, dass
argv,printfundstd::coutmit UTF-8 arbeiten und dass nur UTF-8/UTF-16-Konvertierungsfunktionen für die WinAPI verwendet werden, ohne seltsame Konvertierungen. Microsoft sollte diesen Ablauf an einer Stelle dokumentierenDas ist bis zu einem gewissen Grad absehbar, war aber selbst für mich neu, obwohl ich ungefähr zehn Jahre lang Windows-Entwicklung und Wine-API-Hacking gemacht habe, als die W/A-Verwirrung aufkam
Windows ist wie das Kartenspiel Munchkin: Wenn mehrere Features zufällig ineinandergreifen, können sie sich zu unglaublich zufälligen und mächtigen Exploits verbinden. Es ist erfreulich, dass das ANSI-Subsystem auf UTF-8 umgestellt wird; theoretisch kann das viele solcher Probleme entschärfen. Ich frage mich, ob das Rust-Team an der Prozess-Erzeugungs-API noch eine weitere Korrektur vornehmen muss
Natürlich kann Rust nicht kontrollieren, was jenseits von Prozessgrenzen passiert. Wenn eine von Rust gestartete Anwendung ANSI-APIs verwendet, entsteht das Problem dort, aber das liegt in der Verantwortung dieser Anwendung
„ANSI schrittweise abzuschaffen und die Verwendung der Wide-Character-API zu empfehlen“ war, wenn ich mich richtig erinnere, seit NT 3.5 Microsofts offizielle Position
Leider ist eines der großen Hindernisse die Implementierung von Microsofts C/C++-Runtime-Bibliothek
msvcrt.dll. Nichtstandardisierte Wide-Funktionen wie_wfopen()und_wgetenv()verwenden intern die W-Funktionen der WinAPI, aber die standardmäßigen Narrow-Funktionen wiefopen()undgetenv()verwenden einfach die A-Funktionen, statt in die Wide-Versionen zu konvertieren. Und die A-Funktionen melden Unicode-Konvertierungsfehler normalerweise nicht, sondern überschreiben sie per Best-Fit. Wer in C geschriebene Software nach Windows portiert, möchte nicht alle Standardfunktionen durch Microsoft-spezifische, nicht portable Funktionen ersetzen. Ab diesem Punkt ist es praktisch eine komplette NeuschreibungactiveCodePageauf UTF-8 setzen und nur „ANSI“-Funktionen verwendenmainundfopenper#defineauf ihre Wide-Gegenstücke umDann kann man
char*und unverzierte String-Literale nicht einfach so verwenden, also definiert man einentchar-Typ, der unter Linuxcharund unter Windowswchar_tist, sowie das Makro_T()für String-Literale. Im Großen und Ganzen funktioniert das ohne viel Nachdenken gut-A-Variante, nicht die-W-Variante. Ich weiß nicht, ob inrobots.txtirgendetwas Seltsames steht, aber es ist merkwürdig, dass eine API, bei der für neuen Code die-W-Variante empfohlen wird, standardmäßig die Legacy-API zurückliefertmsvcrt.dllwurde durch die Universal C Runtime (UCRT)[1] ersetzt, und die UCRT ist C99-konformEs gibt zwei Möglichkeiten, in selbst geschriebenen Anwendungen oder gepatchten EXE-Dateien die „Ansi“-Codepage tatsächlich auf UTF-8 zu erzwingen
Die eine ist die Verwendung einer Manifestdatei; das funktioniert ab bestimmten Builds von Windows 10. Man kann es nach dem Build auch auf beliebige EXE-Dateien anwenden und so UTF-8-Unterstützung in ein Programm hineinzwängen. Besonders nützlich ist das für Programme im Konsolenmodus. Die andere Möglichkeit ist, den Hack zu verwenden, den „App Locale“-artige Tools nutzen. Eine Methode umfasst den Aufruf undokumentierter Funktionen in NTDLL. Ich weiß nicht genau, welche Funktionen nötig sind, aber
RtlInitNlsTablesundRtlResetRtlTranslationskönnten damit zusammenhängenIch bin mir nicht sicher, ob Microsoft UTF-8 in allen Windows-Editionen standardmäßig aktivieren wird. Es gibt viele alte Anwendungen, die eine bestimmte Codepage oder 1 Byte pro Zeichen voraussetzen und dadurch kaputtgehen könnten.
Subtiler ist, dass es auch Anwendungen gibt, die beim Konvertieren von Wide-Zeichen nach ANSI annehmen, dass die Bytezahl nicht zunimmt, und deshalb vorhandene Buffer wiederverwenden. Bei UTF-8 stimmt das nicht; bei den meisten bisherigen Codepages stimmte es weitgehend, wodurch neue Schwachstellen entstehen könnten. Viel weniger dürfte kaputtgehen, wenn man stattdessen in den Win32-
xxxA-APIs die Best-Fit-Logik entfernt und nicht abbildbare Zeichen durch ein Zeichen wiexersetzt, das keine übliche Meta-Bedeutung hat.[0] https://tambre.ee/blog/adobe_after_effects_windows_utf-8/
Angesichts der Probleme, die Best-Fit-Mappings verursachen, wäre es auch sinnvoll, das zum Standard zu machen; Microsoft müsste Nutzern aber dabei helfen, alte Software weiterhin einfach auszuführen. Eine weniger sinnvolle Methode wäre, in den Best-Fit-Mappings alle Zuordnungen zu „speziellen“ ASCII-Zeichen zu entfernen, doch das hilft Apps mit statisch gelinkter CRT nicht. Da es die Schwachstellen auch nicht behebt, ist das keine gute Lösung. Manchmal sind Sicherheitslücken der Antrieb, um Brüche der Abwärtskompatibilität durchzusetzen.
Microsoft kennt dieses Problem mindestens seit einem Jahr. Denn sie haben mit CA2101[1] eine spezielle Codeanalyse-Regel veröffentlicht, die ausdrücklich von der Nutzung von Best-Fit-Mapping abrät.
In der Regelbeschreibung wurden Sicherheitslücken erwähnt, die Details aber absichtlich vage gehalten.
[1] https://learn.microsoft.com/en-us/dotnet/fundamentals/code-a...
Man muss nicht alles von
char *aufwchar *umstellen. Man kann die erhaltenen Wide-Zeichen nach UTF-8 konvertieren oder, wenn man auch ungültige Sequenzen wie ungepaarte Surrogate erlauben will, in etwas wie Rusts WTF-8 umwandeln und anschließend weitercharverwenden.Natürlich muss man aufpassen, ANSI- oder OEMCP-Strings nicht mit UTF-8-Strings zu vermischen; wenn man einfach nur UTF-8 verwendet, ist es leicht. Genau diesen Ansatz empfiehlt die klassische Website https://utf8everywhere.org/.
Auf meinem privaten Windows-Rechner hatte ich den UTF-8-Modus schon seit einigen Jahren aktiviert und bin diesem Bug dadurch zufällig entgangen. Es ist die Einstellung, die unten im Artikel genannt wird.
Ich hatte sie eingeschaltet, weil alte ausländische Spiele kaputte Zeichen angezeigt haben; obwohl sie als „Beta“ markiert ist, habe ich keine Bugs oder Nebenwirkungen bemerkt.
Ich hatte mich gefragt, ob die Beta-Checkbox dasselbe ist wie
ActiveCodePageim Manifest auf UTF-8 zu setzen, aber die Dokumentation[0] stellt klar, dass GDI nicht der prozessspezifischen Codepage folgt, sondern nur der einzigen globalen Codepage, die diese Checkbox setzt.Es ist etwas schade, dass man in der eigenen App über die
*A-APIs nicht vollständig in UTF-8 opt-in kann. Trotzdem halte ich es für die im Artikel hervorgehobenen Probleme weiterhin für einen brauchbaren Workaround oder eine Maßnahme der Defense in Depth.[0] https://learn.microsoft.com/en-us/windows/apps/design/global...
Meine Güte. Ich wusste zwar, dass die Windows-API solche Best-Fit-Konvertierungen anbietet, aber nicht, dass das bei meiner Standard-Codepage 949[1] das Standardverhalten mehrerer ANSI-Funktionen ist.
An diesem Punkt sollte man es wie
getseinfach verbieten. [1] Ich weiß, dass es die UTF-8-Codepage 65001 gibt. Sie war lange Zeit wirklich praktisch unbenutzbar und hat auch heute noch Kompatibilitätsprobleme.