2 puan yazan GN⁺ 2025-01-10 | 1 yorum | WhatsApp'ta paylaş
  • Windows’un Best-Fit karakter dönüşümü, UTF-16 dizelerini ANSI kod sayfasına çevirirken benzer görünen karakterlerle değiştirir; bu davranış Path Traversal, Argument Injection ve RCE’ye yol açabilen WorstFit saldırı yüzeyini oluşturur
  • Sorun; ANSI API, C/C++ çalışma zamanı, derleyicinin eklediği başlangıç kodu ve geliştiricilerin wide olmayan karakter API’lerini kullanmasının üst üste geldiği yapıda ortaya çıkar; GetCommandLineA, GetEnvironmentVariableA, getenv, int main() yolları bundan etkilenir
  • CVE-2024-4577’de Chinese/Japanese kod sayfalarında U+00AD - karakterine dönüşerek PHP-CGI yamasını baypas etti; Filename Smuggling’de ise ¥, ve fullwidth slash karakterleri / veya \ karakterine dönüşerek yol karışıklığı yaratır
  • Argument Splitting, fullwidth çift tırnak veya Yen/Won işaretinin komut satırı ayrıştırma karakterlerine dönüşmesiyle wget.exe, tar.exe, openssl.exe, java.exe gibi CLI araçlarına argüman enjekte edilmesini sağlayabilir; PHP, Python, Node.js ve Rust’taki yaygın argüman escape yöntemleri tek başına bunu engellemekte zorlanır
  • Azaltma için Windows’un UTF-8 seçeneği açılmalı ya da geliştiriciler Wide Character API ile _wgetcwd, _wgetenv, wmain() gibi wide karakter yollarını kullanmalıdır; Microsoft tüm Windows sürümlerinde UTF-8’i varsayılan olarak açana kadar benzer sorunlar tekrarlanabilir

Windows kodlama yapısı ve Best-Fit

  • Windows başlangıçta ANSI kod sayfalarını kullanıyordu ve dil bölgesine göre 1252, 932, 936, 949, 950 gibi kod sayfaları farklıydı
    • ACP (ANSI Code Page), dosya işlemleri ve ortam değişkenleri gibi çoğu uygulama ve sistem ayarında kullanılır
    • OEMCP (Original Equipment Manufacturer Code Page), ağırlıklı olarak konsolda okuma/yazma gibi cihaz iletişimlerinde kullanılır
    • chcp, ACP’yi değil OEMCP’yi gösterdiği için bu araştırmanın odağındaki ACP’yi doğrulamak için uygun bir araç değildir
  • Windows 1990’ların ortasında Unicode’a geçti ve günümüzde çekirdek API’ler UTF-16 tabanlı wide character kullanır
    • Dosya sistemi, sistem bilgisi ve metin işleme gibi çekirdek API’ler wide karakter API’lerine dönüştürüldü
    • UTF-8 özelliği mevcut olsa da çoğu dilde varsayılan olarak açık değildir; yazıda beta aşamasında olarak ifade edilir
  • Geriye dönük uyumluluk nedeniyle Windows API, ANSI ve Unicode sürümlerini birlikte sunar
    • ANSI API’ler GetEnvironmentVariableA gibi A sonekine sahiptir
    • Unicode API’ler GetEnvironmentVariableW gibi W sonekine sahiptir
    • ANSI API çağrıldığında Windows, dahili UTF-16 dizeleri RtlUnicodeStringToAnsiString veya WideCharToMultiByte ile ANSI dizelere dönüştürür

Best-Fit’in WorstFit’e dönüşme biçimi

  • Best-Fit, UTF-16 karakterleri hedef ANSI kod sayfasında tam olarak ifade edilemediğinde bunları benzer görünen veya benzer hissedilen karakterlere eşleyen davranıştır
    • Örneğin Windows-1252’de U+221E, 8 olarak eşlenir
    • √π⁷≤∞, ANSI API’den geçtiğinde "vp7=8" gibi değişebilir
  • Eşleme her kod sayfasında farklı çalışır
    • ¥ U+00A5, Japanese 932 kod sayfasında \ olarak eşlenir
    • Central European 1250 kod sayfasında Y olarak eşlenir
    • Diğer kod sayfalarının çoğunda değişmeden kalır
  • Yalnızca Windows API’nin doğrudan çağrılmasında değil, CRT fonksiyonlarında ve yaygın main fonksiyonu yollarında da aynı dönüşüm gerçekleşir
    • getenv gibi wide olmayan CRT fonksiyonlarında Best-Fit dönüşümü uygulanır
    • Argümanlar ve ortam değişkenleri int main(int argc, char* argv[], char* envp[]) biçiminde alındığında da dönüşüm devreye girer
    • Bunun nedeni, derleyicinin eklediği CRT başlangıç kodu ile ANSI Windows API kullanımının birleşmesidir
  • Eşlemeleri kontrol etmek için Best-fit Mapping Grepper ve Unicode.org’un WindowsBestFit ham eşleme verileri referans alınabilir

İlk WorstFit vakası: PHP-CGI CVE-2024-4577

  • CVE-2024-4577, Chinese/Japanese kod sayfalarına ayarlanmış PHP-CGI sunucularının yalnızca ?%ADs isteğiyle ele geçirilebilmesini sağlayan bir WorstFit saldırı örneğidir
    • Etkilenen kod sayfaları 932 (Japanese), 936 (Simplified Chinese), 950 (Traditional Chinese)
    • Tehdit karakteri ­ U+00AD
  • 2012’deki PHP-CGI zafiyeti, Apache’nin sorgu dizesini CGI programının ilk argümanı olarak otomatik işlemesiyle ortaya çıkan bir argument injection sorunuydu
    • ?-s eklendiğinde sayfa kaynak kodunun sızdırılması ve RCE mümkün oluyordu
    • PHP yaması, sorgu dizesi dash ile başlıyorsa argüman ayrıştırmayı durduracak şekildeydi
  • Best-Fit nedeniyle U+00AD soft hyphen, Chinese/Japanese kod sayfalarında - karakterine dönüştü ve mevcut yama baypas edildi
    • ?%ADs, PHP-CGI açısından -s gibi davranabilir
    • Bu vaka sayesinde araştırma ekibi Best-Fit terimiyle ilk kez karşılaştı

Filename Smuggling: yol karakterlerinin dönüşmesi sorunu

  • Filename Smuggling, dosya adında bulunan Unicode karakterlerinin ANSI API yolunda / veya \ karakterine dönüşerek path traversal oluşturabildiği bir saldırıdır
    • İlgili API’ler arasında GetCurrentDirectoryA, getcwd, FindFirstFileA, findfirst*, GetFullPathNameA vb. bulunur
    • Etkilenen kod sayfaları 874, 125x, 932 (JP), 949 (KR)
    • Tehdit karakterleri U+FF0F, U+FF3C, ¥ U+00A5 (JP), U+20A9 (KR)
  • Chrome V8’in Developer Shell’i olan d8.exe, iç uygulamasında geçerli çalışma dizinini almak için GetCurrentDirectoryA() kullanır
    • Kötü amaçlı Unicode karakterleri içeren bir çalışma dizini oluşturulabiliyorsa, ANSI API erişimi sırasında path traversal payload’una dönüşür
    • Örnek olarak istenmeyen C:/windows/win.ini erişimi mümkün olabilir
  • mruby’nin Dir.getwd() Windows uygulaması, ANSI CRT fonksiyonu _getcwd()’ye dayanır
    • Dönüş değeri kirlenebilir ve Path Traversal’a yol açabilir

Cuckoo Sandbox: Path Traversal’dan RCE’ye

  • Python’ın Windows dosya sistemine erişimi, dizgenin wide mı yoksa narrow mı olduğuna bağlı olarak wide API veya ANSI API kullanabiliyordu
    • PEP 529 sonrasında Windows dosya sistemi kodlaması UTF-8 olarak standartlaştırıldı
    • Python 2 ve Python 3.6 öncesi Python 3, WorstFit saldırılarına açık durumda kaldı
  • Cuckoo Sandbox otomatikleştirilmiş bir zararlı yazılım analiz platformudur ve en güncel resmî sürümü Python 2.7’ye bağımlıdır
    • Cuckoo, Cuckoo Host ve VM Cluster’dan oluşur
    • Yüklenen örnek VM’de izole şekilde çalıştırılır; ağ paketleri, bırakılan dosyalar ve loglar kendi mekanizmasıyla senkronize edilir
  • Zararlı yazılım Unicode dosya adına sahip bırakılan bir dosya oluşturursa, Cuckoo Host’un Python yol işlemesinde Path Traversal oluşabilir
    • Örnek PoC, AAAA\u00a5..\u00a5..\u00a5..\u00a5..\u00a5..\u00a5conf\u00a5cuckoo.conf yolunu oluşturur
    • Analiz bittikten sonra kullanıcı web arayüzünde indirme düğmesine bastığında Python dosya işlemi tetiklenir
    • Cuckoo Host, dönüştürülmüş ../ içeren yolu işleyerek saldırgana hassas veriler gönderebilir
  • Saldırgan cuckoo.conf dosyasını indirip Flask PIN hesaplaması için gereken hassas bilgileri toplayarak Sandbox Host üzerinde RCE elde edebilir
    • Demo videosu Video 11 olarak sunulmuştur

Argument Splitting: Komut satırı ayrıştırmasını değiştiren Best-Fit

  • Argument Splitting, GetCommandLineA çıktısında veya Unicode olmayan int main() yolunda komut satırı dizgesinin değişerek argümanların ayrıldığı bir saldırıdır
    • İlgili API ve yollar: GetCommandLineA, int main()
    • Etkilenen code page’ler: 874, 125x, 932(JP), 949(KR)
    • Tehdit oluşturan karakterler: U+FF02, U+FF3C, ¥ U+00A5(JP), U+20A9(KR)
  • Örnek PHP kodu, URL’yi escapeshellarg() ile güvenli şekilde sarıp wget.exe -q çalıştırsa da " --use-askpass=calc " girdisiyle calc.exe çalıştırmak mümkündür
    • Aynı girdi Node.js, Rust, Python’a çevrilse de savunma sağlanmaz
    • Python’ın güncel sürümündeki subprocess.run(["wget", "-q", ...]) örneğinde de çalışır
  • Windows, yeni sürece tüm komut satırını tek bir dizge olarak geçirir ve çalıştırılabilir dosya bunu doğrudan ayrıştırır
    • UNIX türevlerinde olduğu gibi argüman dizisinin her zaman aktarıldığı bir yapı değildir
    • CreateProcess API’si lpCommandLine parametresini doğrudan alır
  • Genel komut satırı ayrıştırmasında önemli karakterler boşluk·tab, double quote ve backslash’tir
    • Boşluk ve tab, quote mode’da değilken argümanları ayırır
    • " quote mode’u değiştirir
    • \ belirli dizilerde double quote ve backslash’i escape eder
  • Çoğu dilin standart kütüphanesi kullanıcı argümanlarını bu kurallara göre escape eder, ancak escape işlemi Best-Fit dönüşümünden önce biter
    • PHP escapeshellarg, double quote’u boşluğa çevirir, argümanı quote içine alır ve backslash’i işler
    • Python subprocess, Microsoft CRT komut satırı ayrıştırma kurallarına uygun şekilde list2cmdline ile escape eder
    • Sonraki ANSI dönüşümünde U+FF02, " U+0022’ye dönüşürse özgün komut satırı sözdizimi değişir
  • Yalnızca int main() kullanan programlar da savunmasız olabilir
    • Derleyici ikili dosyada mainCRTStartup oluşturur ve bu başlangıç fonksiyonu CRT kütüphanesine bağlanır
    • CRT içi komut satırını ANSI API ile alıp ayrıştırırsa Best-Fit dönüşümü devreye girer
    • Bu davranış nedeniyle saldırıyı yalnızca belirli bir programlama dilinin standart kütüphanesiyle tamamen engellemek zordur

Argument Splitting gerçek vakaları

  • ElFinder, PHP backend tabanlı açık kaynaklı bir web dosya yöneticisidir ve varsayılan olarak Windows sunucularını ve arşiv oluşturma/çıkarma işlemlerini destekler
    • Arşiv işlemesi shell command çalıştırarak uygulanmıştır ve argümanlar escapeshellarg ile escape edilir
    • tar biçimi işlemede Windows’un yerleşik tar.exe aracı kullanılır
    • aaa" "--use-compress-program=calc" "bbb.tar gibi bir tar dosya adıyla --use-compress-program argümanı enjekte edilerek keyfî komut çalıştırma mümkündür
    • Demo, İngilizce yapılandırılmış Windows server ve Code Page 1252 temel alınarak yapılmıştır; 125x code page’leri ve Code Page 874’te de çalışması gerektiği belirtilmiştir
    • Demo videosu Video 12 olarak sunulmuştur
  • TortoiseGit’te kullanılan değiştirilmiş plink.exe vakasında, clone girdisine kötü amaçlı URI verilirse kod çalıştırma tetiklenebilir
  • RStudio, SVN sürüm kontrolünü destekler; kötü amaçlı hazırlanmış bir klasörde SVN projesi varsa tek tıklamayla hesap makinesi çalıştırmak mümkündür
  • Microsoft Excel vakası, Argument Splitting ile Windows’un “Open-With” özelliğini birleştiren CVE-2024-49026’dır
    • Windows, dosya uzantılarına göre handler table tutar; bu, ftype ve assoc ile kontrol edilebilir
    • Dosya adı handler programının argümanlarının bir parçası hâline geldiğinden, saldırı dosya adı üzerinden uygulanabilir
    • Nokta, slash, backslash ve double quote karakterleri fullwidth biçimlerine çevrilmiş bir dosya adıyla Excel.exe üzerinde argument injection tetiklenir
    • Excel’in kendisinde ek sömürüye uygun argümanlar bulunmadığından RCE elde etmek için NTLM Relay ve RBCD/ADCS birlikte kullanılır
    • Demo videosu Video 15

Ortam Değişkeni Karmaşası

  • Ortam Değişkeni Karmaşası, GetEnvironmentVariableA, GetEnvironmentStringsA, char *getenv() ortam değişkenlerinin Best-Fit dönüşümünden geçmiş sürümünü döndürdüğünde ortaya çıkar
    • Etkilenen kod sayfası ve tehdit karakterleri belirli değildir
    • Apache HTTPd örneğinde 0x00-0xFF aralığı devreye girer
  • Bu saldırının mümkün olması için ortam değişkenlerinin kullanıcı tarafından kontrol edilebilmesi gerekir
    • Üst sürecin oluşturduğu alt sürece bilgi aktardığı durumlar buna örnektir
    • CGI’de query string, HTTP header gibi HTTP isteği bilgilerinin önemli bir kısmı ortam değişkenleri olarak aktarılır
  • WAF atlatma örneği, CGI betiğinin bir routing service gibi davrandığı durumu ele alır
    • Apache yapılandırmasında, REQUEST_URI içinde /admin geçen istekleri reddederek /cgi.pl/admin için uzaktan erişimi engelleyen bir kural bulunur
    • Windows Perl’in WorstFit davranışı nedeniyle admin içindeki bazı karakterler Best-Fit equivalent ile değiştirilirse kural atlatılabilir
    • Code Page 1250’de à U+00E0, ANSI dönüşümü sırasında a karakterine dönüşür
    • /cgi.pl/%E0dmin isteği sunucu tarafındaki kurala göre farklı bir yol gibi görünür; ancak Perl CGI betiği PATH_INFO değerini ANSI API ile okuduğunda /admin olarak işlenir
  • Windows üzerinde PHP-CGI’de belirli yapılandırmalarda dosya varlığı doğrulama oracle’ı ve potansiyel LFI doğrulanmıştır
    • Bunun nedeni PATH_INFO ve diğer path ile ilgili ortam değişkenlerinin işlenme biçimidir
    • /index.php/foo/bar isteği Apache açısından REDIRECT_URL, REQUEST_URI, PATH_INFO, PATH_TRANSLATED gibi ortam değişkenleri olarak aktarılır
    • Yalnızca bu bilgiyle PHP dosya adı ile ek PATH_INFO sınırını net biçimde ayırmak zordur; php-cgi.exe bunu yorumlar
  • Japonca kod sayfasında ¥ kullanıldığında web sunucusu ile PHP-CGI’nin path yorumlaması farklılaşır
    • Web sunucusu /..¥..¥windows/win.ini/foo değerinin tamamını ek PATH_INFO olarak işler
    • PHP-CGI ise REQUEST_URI=/index.php/..\..\windows/win.ini/foo gibi dönüştürülmüş bir değer alır ve gerçek PHP dosyası ile PATH_INFO değerini ayırma sürecinde karışıklık yaşar
    • Apache’de var olmayan dosyalar ile var olan dosyaların yanıt farkından file existence oracle mümkün olur
    • IIS’te doc_root directive’i ayarlanmışsa /index.php/..¥..¥..¥windows/win.ini/ gibi bir path ile C:\Windows\win.ini dosyasını include edip okumaya dayalı LFI mümkün olur
    • Dahil edilen dosya çalıştırılabilir durumdaysa veya kullanıcının kontrol edebildiği kod içeriyorsa potansiyel RCE’ye yol açabilir; ancak bu senaryo gerçek uygulamalarda nadir görülen bir bug’a daha yakın sınıflandırılır

Açıklama ve Düzeltme Sürecindeki Zorluklar

  • Araştırma ekibi, programlama dilleri, açık kaynak projeleri ve Windows’un yerleşik CLI programlarındaki çeşitli sorunları ilgili upstream maintainer’lara bildirdi
    • En fazla tartışma Argument Splitting konusunda yaşandı
    • Bazı vendor’lar kullanıcı girdisini komut satırına aktarmanın başlı başına bir zafiyet olduğunu değerlendirdi
  • Sorumluluğun kime ait olduğunun belirsiz olması da sorun yarattı
    • Sorunlu kod, derleme sırasında otomatik eklenen mainCRTStartup() ile MSVCRT/UCRT içindeki ANSI API çağrılarına yayılıyor
    • Bunun geliştiricinin wmain() kullanmamasından mı, yoksa CRT’nin komut satırını yanlış bölüp main() fonksiyonuna hatalı argümanlar iletmesinden mi kaynaklandığını ayırmak zor
    • Bazı projeler yalnızca kaynak kod sağlıyor; Windows prebuilt executable dosyaları ise internetteki üçüncü taraf gönüllüler tarafından dağıtılıyor
  • Düzeltme, yalnızca main() fonksiyonunu wide-character sürüme çevirmekten ibaret değil
    • Fonksiyon imzası değiştiğinde değişken tanımları ve argüman ayrıştırma mantığı char * temelinden wchar_t * temeline yeniden yazılmalı
    • Bu süreç zahmetli ve hataya açık
  • Curl, bunun bir Windows özelliği olduğunu söyleyerek düzeltme planı olmadığını belirtti; Microsoft’un port ettiği Curl ise entry’yi wmain() olarak düzelttiği için Windows’un yerleşik curl.exe dosyası etkilenmiyor
    • Curl resmi build binary’leri Argument Splitting saldırısından etkileniyor
    • Tam rapor HackerOne üzerinde yayımlandı
  • OpenSSL, OPENSSL_WIN32_UTF8 ortam değişkeniyle argümanları wide character biçiminde işleyebiliyor
    • Asıl amaç UI’da UTF-8 görüntüleme sorununu düzeltmekti, ancak Argument Splitting saldırısını da hafifletiyor
    • Varsayılan OpenSSL kullanımında geliştiriciler çoğu zaman bu ortam değişkenini ayarlamaları gerektiğini bilmiyor; -engine argümanı kullanılarak keyfi kod çalıştırma mümkün olabiliyor
  • Perl’in resmi dağıtımı Windows prebuilt executable sunmuyor; Strawberry Perl ve ActiveState Perl gibi üçüncü taraf installer’lar yaygın olarak kullanılıyor
    • İki dağıtım da Argument Splitting saldırısından etkileniyor
    • Perl maintainer’ı ile yapılan görüşmeler sonucunda bunun “Perl bug’ından çok Microsoft bug’ına yakın” olduğu sonucuna varıldı ve konu şu anda çözülmemiş durumda
  • Microsoft’a MSRC üzerinden üç bildirim yapıldı ve hepsi başlangıçta ciddiyet kriterlerini karşılamadığı gerekçesiyle reddedildi
    • Birkaç kez yeniden açıldıktan sonra yalnızca Excel örneği üçüncü denemeden sonra kabul edildi
    • Diğer örnekler hâlâ çözülmemiş durumda
    • MSRC, bunun ayrı bir uygulamanın güvenilmeyen girdiyi komut satırına koyup çalıştırmasına dayanan bir zafiyete bağlı olduğunu; bunu istismar edilebilir kılan technique’in kendisinin zafiyet şartlarını karşılamadığını belirtti
  • CERT/CC’den de yardım istendi ve Microsoft birkaç ay sonra GetCommandLineA belgelerine güvenlik uyarısı ekledi
    • Uyarı yalnızca GetCommandLineA için eklendi; dikkat gerektiren başka ANSI API’ler de var

Bildirilen Etkilenen Hedefler ve Durumları

  • Açıklama sürecinde doğrulanıp bildirilen kalemler şunlardır
    • 2024/05/07: PHP php-cgi.exeCVE-2024-4577
    • 2024/06/13: Curl Official BuildWon’t Fix
    • 2024/06/13: Apache Subversion svn.exeCVE-2024-45720
    • 2024/06/16: Microsoft Tar tar.exe — Won’t Fix
    • 2024/06/19: Microsoft Excel excel.exeCVE-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.exeCVE-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

Azaltma önlemleri ve kalan saldırı yüzeyi

  • WorstFit saldırısı işletim sistemi düzeyinde bir sorun olduğundan, Microsoft tüm Windows sürümlerinde UTF-8’i varsayılan olarak etkinleştirene kadar benzer sorunlar tekrar ortaya çıkmaya devam edebilir
  • Kullanıcıların yapabileceği şey, Windows’un UTF-8 seçeneğini kontrol edip etkinleştirmektir
    • Bu özellik hâlâ beta aşamasında olarak işaretleniyor ve yan etkilerinin olup olmadığı kesin değil
  • Geliştiriciler mümkün olduğunca Wide Character API kullanmalıdır
    • CRT de _wgetcwd, _wgetenv gibi wide character sürümlerini sunar
    • non-wide yollar kullanılmaya devam edilirse iç uygulama ANSI API’yi çağırabilir ve WorstFit saldırılarına açık kalabilir
  • Windows’un geriye dönük uyumluluğu nedeniyle ANSI API’nin gizli olduğu daha fazla yer olabilir
    • Örneğin RegQueryValueA gibi Windows Registry sorguları etkilenebilir, ancak savunmasız senaryoların bulunması gerekir
    • Araştırma ekibi Active Directory’de de Best-Fit davranışı gözlemledi

1 yorum

 
GN⁺ 2025-01-10
Hacker News yorumları
  • Bu oldukça çetrefilli bir sorun. Microsoft’un “best fit” kod eşlemesi, geniş Unicode’u ASCII’ye dönüştüren, açıkta olsa da fiilen “hissiyata dayalı” çalışan bir eşleyici ve sistemin geneline yayılmış durumda.
    Bu eşleyici pek çok yerde varsayılan olarak bağlanıyor ve Microsoft’un geriye dönük uyumluluğa bakış biçimi nedeniyle dâhil edilmeye devam edecek gibi görünüyor. Exploit’ler çoğunlukla sıra dışı kod noktalarının eğik çizgi, tire, tırnak işareti gibi karakterlere “hissiyata göre” eşlenmesinden çıkıyor. Modern dil içinde doğru Unicode olarak denetleniyorlar, ama shell komutuna veya Win32 API’ye geçince denetimi devrettikten sonra farklı bir şekilde daraltılarak dönüştürülüyorlar. curl bakımcısının dediği gibi burada “curl mağdur”; mesele suçlunun kim olduğu. Sunucu, kullanıcı girdisini doğrularken ve sistem kütüphanesine geçirirken onu farklı biçimde ezip büzüyorsa sonunda sorun çıkar. Win32 tarafında best fit dönüşümünü kapatmaya yarayan bir seçenek çözüm olabilir, ama Windows uzmanı olmadığım için bu bir tahmin. Öyle olsa bile resmi API’lerle ya da bunu hâlâ kapatmamış yazılımlarla etkileşim sürer.

    • Opt-out yöntemi Unicode Windows API kullanmak, yani "a" ile değil "w" ile biten işlevleri kullanmaktır. Bu yöntem "\\?\" öneki eklenirse ya da manifest doğru ayarlanırsa 260 karakteri aşan yol sorununu da beraber çözer; Windows XP’den beri mümkündür ve önerilmektedir.
      Unicode olmayan API’nin hâlâ neden bu kadar yaygın kullanıldığını pek bilmiyorum. Bunun Windows 98 veya Windows 2000’i destekleme isteğinden kaynaklandığını hayal etmek zor.
    • Windows’ta Windows XP’den beri eski davranışı kapatmanın bir yolu olan manifest dosyası var. Yanlış hatırlamıyorsam manifest yoksa GetWindowsVersion bile güncel sürümü döndürmüyordu. Buna opt-out eklemek ve bir gün Visual Studio varsayılanı yapmak çok zor görünmüyor.
      Ayrıca gereken şey bir tür linting. Modern uygulamalarda ANSI WinAPI işlevlerini çağırmak için genelde bir neden yok. Locale’i UTF-8’e ayarlayıp yalnızca 8 bitlik işlevleri kullanma yolu da olabilir, ama ne kadar iyi çalıştığını bilmiyorum. argv, printf, std::cout’un UTF-8 ile çalışmasını ve tuhaf dönüşümler olmadan yalnızca WinAPI için UTF-8/UTF-16 dönüştürme işlevlerinin kullanılmasını sağlayan bazı ayarlar ve header’lar olduğunu da biliyorum. Microsoft’un bu prosedürü tek bir yerde belgelemesi gerekiyor.
    • Güvenlik açığı olsun ya da olmasın, Windows’ta Unicode argümanları düzgün işleyemiyorsa bu curl’ün de hatasıdır.
    • Kod noktalarını karakterlere gevşek biçimde eşleme yaklaşımı Unicode’da her zaman canımı sıkmıştır.
  • Bu bir dereceye kadar tahmin edilebilir, ama W/A karmaşasının ortaya çıktığı dönemlerde yaklaşık 10 yıl Windows geliştirme ve Wine API hack’leri yapmış biri olarak benim için de yeniydi.
    Windows, Munchkin kart oyunu gibi: çeşitli özellikler tesadüfen birbirine denk gelince inanılması güç derecede rastgele ve güçlü exploit’lerde birleşebiliyor. ANSI alt sistemini UTF-8’e dönüştürüyor olmaları sevindirici; teoride bu tür sorunların çoğunu hafifletebilir. Rust ekibinin süreç oluşturma API’sine bir düzeltme daha yapmak zorunda kalıp kalmayacağını da merak ediyorum.

    • Rust standart kütüphanesi varsayılan olarak ANSI API’yi neredeyse hiç kullanmıyor. Yazıda Rust üzerinde işe yarayan bir saldırı gösterilmemiş; böyle bir saldırı varsa kesinlikle bildirilmesi iyi olur.
      Elbette Rust süreç sınırının ötesinde olup biteni kontrol edemez. Rust’ın çalıştırdığı uygulama ANSI API kullanırsa sorun o tarafta ortaya çıkar, ama bu o uygulamanın sorumluluğudur.
  • “ANSI’yi aşamalı olarak kaldırıp Wide Character API kullanımını önermek”, doğru hatırlıyorsam NT 3.5 zamanından beri Microsoft’un resmi tutumuydu.
    Ne yazık ki büyük engellerden biri Microsoft’un C/C++ çalışma zamanı kütüphanesi msvcrt.dll’nin uygulanış biçimi. _wfopen(), _wgetenv() gibi standart dışı wide işlevler içeride Win API’nin W işlevlerini kullanıyor; fakat fopen(), getenv() gibi standart narrow işlevler wide sürüme dönüştürmek yerine doğrudan A işlevlerini kullanıyor. A işlevleri de genelde Unicode dönüşüm hatasını bildirmek yerine best-fit yöntemiyle üstünü örtüyor. C ile yazılmış yazılımı Windows’a port eden biri, standart işlevlerin tamamını Microsoft’a özgü taşınabilir olmayan işlevlerle değiştirmek istemez. O noktadan sonra bu fiilen baştan yazmak olur.

    • Son 2 yılda Microsoft belgelerini okuyunca bende oluşan izlenim bunun tersiydi. Uygulama manifestinde activeCodePage değerini UTF-8 yapıp yalnızca “ANSI” işlevleri kullanın deniyor gibiydi.
    • Taşınabilir kodda, Windows derlemesi için main ve fopen gibi standart işlevler wide karşılıklarına #define edilir.
      Böyle olunca char* ve süslemesiz string literal’ları öylece kullanamazsınız; bu yüzden Linux’ta char, Windows’ta wchar_t olan tchar türünü ve string literal’lar için _T() makrosunu tanımlarsınız. Genellikle fazla düşünmeden iyi çalışır.
    • Bugünlerde gerçekten sinir bozucu olan şey, Google’da Win32 API aratınca her zaman -W varyantı değil -A varyantının önce çıkması. robots.txt içinde tuhaf bir şey mi var bilmiyorum, ama yeni kodda -W varyantını kullanmayı öneren bir API’nin varsayılan olarak legacy API’yi döndürmesi garip.
    • Microsoft’un C/C++ çalışma zamanı msvcrt.dll, Universal C Runtime (UCRT)[1] ile değiştirildi ve UCRT C99 ile uyumlu.
    • Windows, yol adlarını böyle aptalca encoding işlemleri olmadan doğrudan bayt dizileri olarak ele alan bir API sunmalıydı. UNC yollarını tanıtırken bunu yapabilirmiş gibi geliyor.
  • Kendi yazdığınız uygulamalarda veya patch’lediğiniz EXE’lerde “Ansi” kod sayfasını fiilen UTF-8’e zorlamanın iki yolu var.
    Biri manifest dosyası kullanmak; Windows 10’un belirli bir build’inden itibaren çalışıyor. Derlemeden sonra herhangi bir EXE’ye de uygulanabildiği için bir programa zorla UTF-8 desteği ekleyebilirsiniz. Özellikle konsol modu programları için kullanışlı. Diğeri “App Locale” türü araçların kullandığı hack’i kullanmak. Yöntemlerden biri NTDLL’nin belgelenmemiş bir işlev çağrısını içeriyor. Tam olarak hangi işlevin gerektiğini bilmiyorum ama RtlInitNlsTables ve RtlResetRtlTranslations ilgili olabilir.

  • Microsoft'un tüm Windows sürümlerinde UTF-8'i varsayılan olarak açma ihtimalinden pek emin değilim. Belirli bir kod sayfasını ya da karakter başına 1 bayt varsayan çok sayıda eski uygulama olduğu için bozulabilirler.
    Daha incelikli olarak, wide karakterden ANSI'ye dönüştürürken bayt sayısının artmayacağını varsayıp mevcut tamponu yeniden kullanan uygulamalar da var. UTF-8'de bu doğru değil; mevcut kod sayfalarının çoğunda ise genelde doğru olduğu için yeni güvenlik açıkları ortaya çıkabilir. Bence Win32 xxxA API'lerinden Best-Fit mantığını kaldırıp, eşlenemeyen karakterleri ortak meta anlamı olmayan x gibi bir karakterle değiştirmek çok daha az kırıcı olur.

    • Bu tür uygulamalara örnek olarak Adobe After Effects verilebilir[0]. En azından eskiden öyleydi; artık Windows kullanmıyorum.
      [0] https://tambre.ee/blog/adobe_after_effects_windows_utf-8/
    • Henüz yoksa bir OS API sürümü getirip, yeni API sürümünü ya da yeni SDK'yı hedefleyen yeni/güncellenmiş uygulamaların varsayılan olarak UTF-8 varsaymasını sağlamak mümkün olmaz mı diye düşünüyorum. Belirli bir API sürümünün altı legacy modda emüle edilebilir. Windows'ta zaten çeşitli Windows sürümlerinin davranışını taklit eden shim kavramı var.
    • UTF-8 öncesi Windows'ta da varsayılan kod sayfasını değiştirince uygulamaların garipleşmesi sorunu zaten vardı. Bu yüzden kullanıcıya UTF-8 seçeneği sunmak makul.
      Best-Fit eşlemenin yol açtığı sorunlara bakınca bunu varsayılan yapmak da makul, ama Microsoft'un kullanıcıların eski kodu kolayca çalıştırmanın bir yolunu bulmasına yardım etmesi gerekir. Daha az makul bir yöntem, Best-Fit eşlemede “özel” ASCII karakterlerine giden tüm eşlemeleri kaldırmak olabilir; ancak bu, CRT'yi statik bağlayan uygulamalara yardımcı olmaz. Güvenlik açığını da gidermediği için iyi bir çözüm değil. Bazen güvenlik açıkları geriye dönük uyumluluğu bozmayı zorlamak için motivasyon olur.
  • Microsoft bu sorundan en az 1 yıldır haberdardı. Çünkü best-fit eşleme kullanımını açıkça önermeyen CA2101[1] adlı özel bir kod analizi kuralı yayımladı.
    Kural açıklamasında güvenlik açıklarından söz ediliyordu, ancak ayrıntılar bilinçli olarak muğlak bırakılmıştı.
    [1] https://learn.microsoft.com/en-us/dotnet/fundamentals/code-a...

  • Her şeyi char *'dan wchar *'a çevirmek gerekmiyor. Alınan wide karakterleri UTF-8'e dönüştürüp ya da eşleşmemiş surrogate gibi geçersiz dizilere bile izin vermek istiyorsanız Rust'taki WTF-8 benzeri bir şeye dönüştürüp char kullanmaya devam edebilirsiniz.
    Elbette ANSI veya OEMCP dizgelerini UTF-8 dizgeleriyle karıştırmamaya dikkat etmek gerekir; ama sadece UTF-8 kullanırsanız iş kolay. Klasik https://utf8everywhere.org/ sitesinin önerdiği yaklaşım da bu.

  • Kişisel Windows bilgisayarımda birkaç yıldır UTF-8 modunu açmış olduğum için bu hatadan tesadüfen kaçınmışım. Yazının alt kısmında gösterilen ayar.
    Eski yabancı oyunlar bozuk karakterler gösterdiği için açmıştım; “Beta” olarak işaretli olmasına rağmen herhangi bir hata ya da yan etki hissetmedim.

    • İlginç, ama benim durumumda o onay kutusu rastgele uygulamaları fazlasıyla çökertmekten başka bir işe yaramadı. Kapalıyken kullanıcının varsayılan kod sayfasının ne olduğuna bağlı olarak düzgün çalışıp çalışmadığı değişiyor gibi.
    • Az önce “Beta: Use Unicode UTF-8 for worldwide language support” seçeneğini açtım. Kaç uygulamanın bozulacağını görmek ilginç olacak.
  • Beta onay kutusunun manifest'te ActiveCodePage değerini UTF-8 olarak ayarlamakla aynı olup olmadığını merak ediyordum; belgelere[0] bakınca GDI'nin süreç başına kod sayfasını izlemediği, yalnızca onay kutusunun ayarladığı tek bir global kod sayfasını izlediği açıkça belirtilmiş.
    Kendi uygulamanızda *A API'leri için tamamen UTF-8'e opt-in yapamamak biraz üzücü. Yine de yazıda vurgulanan sorunlar için bunun hâlâ geçerli bir geçici çözüm ya da savunmayı derinleştirme aracı olabileceğini düşünüyorum.
    [0] https://learn.microsoft.com/en-us/windows/apps/design/global...

  • Aman Tanrım. Windows API'nin bu tür best-fit dönüşümü sağladığını biliyordum, ama varsayılan kod sayfam olan 949'da[1] bunun çeşitli ANSI işlevlerinin varsayılan davranışı olduğunu bilmiyordum.
    Bu noktada gets gibi düpedüz yasaklanmalı. [1] UTF-8 kod sayfası 65001'in var olduğunu biliyorum. Uzun süre gerçekten kullanılamayacak düzeydeydi ve hâlâ uyumluluk sorunları yaşıyor.