2 poin oleh GN⁺ 2025-01-10 | 1 komentar | Bagikan ke WhatsApp
  • Konversi karakter Best-Fit di Windows mengganti string UTF-16 menjadi karakter yang tampak mirip saat diubah ke halaman kode ANSI, dan perilaku ini menjadi permukaan serangan WorstFit yang dapat berujung pada Path Traversal, Argument Injection, dan RCE
  • Masalah ini muncul dari tumpang tindih antara ANSI API, runtime C/C++, kode startup yang disisipkan compiler, serta penggunaan API karakter non-wide oleh pengembang; jalur GetCommandLineA, GetEnvironmentVariableA, getenv, dan int main() terdampak
  • CVE-2024-4577 membypass patch PHP-CGI ketika U+00AD berubah menjadi - pada halaman kode Chinese/Japanese, sementara Filename Smuggling menciptakan kebingungan jalur saat ¥, , dan fullwidth slash berubah menjadi / atau \
  • Argument Splitting dapat membuat karakter parsing command line dari fullwidth double quote atau tanda Yen/Won, sehingga argumen bisa diinjeksi ke tool CLI seperti wget.exe, tar.exe, openssl.exe, dan java.exe; escaping argumen umum di PHP, Python, Node.js, dan Rust saja sulit untuk mencegahnya
  • Untuk mitigasi, aktifkan opsi UTF-8 di Windows, atau pengembang perlu memakai Wide Character API serta jalur karakter wide seperti _wgetcwd, _wgetenv, dan wmain(); masalah serupa dapat terus berulang sampai Microsoft mengaktifkan UTF-8 sebagai default di semua edisi Windows

Struktur encoding Windows dan Best-Fit

  • Pada awalnya Windows menggunakan halaman kode ANSI, dan halaman kode berbeda-beda menurut wilayah bahasa, seperti 1252, 932, 936, 949, dan 950
    • ACP (ANSI Code Page) digunakan untuk sebagian besar aplikasi dan pengaturan sistem, seperti operasi file dan variabel lingkungan
    • OEMCP (Original Equipment Manufacturer Code Page) terutama digunakan untuk komunikasi perangkat seperti baca/tulis konsol
    • chcp menampilkan OEMCP, bukan ACP, sehingga bukan cara untuk memeriksa ACP yang menjadi fokus riset ini
  • Windows beralih ke Unicode pada pertengahan 1990-an, dan API inti saat ini menggunakan wide character berbasis UTF-16
    • API inti seperti sistem file, informasi sistem, dan pemrosesan teks beralih ke API karakter wide
    • Fitur UTF-8 memang ada, tetapi belum aktif secara default di sebagian besar bahasa, dan dalam tulisan ini disebut berada pada tahap beta
  • Karena kompatibilitas ke belakang, Windows API menyediakan versi ANSI dan Unicode secara bersamaan
    • ANSI API memiliki sufiks A, seperti GetEnvironmentVariableA
    • Unicode API memiliki sufiks W, seperti GetEnvironmentVariableW
    • Saat ANSI API dipanggil, Windows mengonversi string UTF-16 internal menjadi string ANSI dengan RtlUnicodeStringToAnsiString atau WideCharToMultiByte

Bagaimana Best-Fit menjadi WorstFit

  • Best-Fit adalah perilaku yang memetakan karakter UTF-16 ke karakter yang tampak atau terasa mirip ketika karakter tersebut tidak dapat direpresentasikan secara tepat di halaman kode ANSI tujuan
    • Misalnya, pada Windows-1252, U+221E dipetakan menjadi 8
    • Jika √π⁷≤∞ melewati ANSI API, hasilnya dapat berubah menjadi seperti "vp7=8"
  • Mapping bekerja berbeda di tiap halaman kode
    • ¥ U+00A5 dipetakan menjadi \ pada halaman kode Japanese 932
    • Pada halaman kode Central European 1250, karakter itu dipetakan menjadi Y
    • Pada sebagian besar halaman kode lain, karakter itu tidak berubah
  • Konversi yang sama terjadi bukan hanya pada pemanggilan Windows API secara langsung, tetapi juga pada fungsi CRT dan jalur fungsi main yang umum
    • Konversi Best-Fit diterapkan pada fungsi CRT non-wide seperti getenv
    • Konversi juga ikut terlibat ketika argumen dan variabel lingkungan diterima dalam bentuk int main(int argc, char* argv[], char* envp[])
    • Hal ini terjadi karena kode startup CRT yang disisipkan compiler berpadu dengan penggunaan ANSI Windows API
  • Untuk memeriksa mapping, rujuk Best-fit Mapping Grepper dan data mapping mentah WindowsBestFit dari Unicode.org

Kasus WorstFit pertama: PHP-CGI CVE-2024-4577

  • CVE-2024-4577 adalah contoh serangan WorstFit yang memungkinkan server PHP-CGI dengan halaman kode Chinese/Japanese dikompromikan hanya melalui request ?%ADs
    • Halaman kode yang terdampak adalah 932 (Japanese), 936 (Simplified Chinese), dan 950 (Traditional Chinese)
    • Karakter ancamannya adalah ­ U+00AD
  • Kerentanan PHP-CGI tahun 2012 adalah argument injection yang terjadi ketika Apache otomatis memperlakukan query string sebagai argumen pertama program CGI
    • Dengan menambahkan ?-s, kebocoran source code halaman dan RCE menjadi mungkin
    • Patch PHP menghentikan parsing argumen jika query string diawali dash
  • Karena Best-Fit, soft hyphen U+00AD dikonversi menjadi - pada halaman kode Chinese/Japanese sehingga patch lama dapat dibypass
    • Dari sudut pandang PHP-CGI, ?%ADs dapat berperilaku seperti -s
    • Melalui kasus ini, tim riset pertama kali berhadapan dengan istilah Best-Fit

Filename Smuggling: masalah konversi karakter jalur

  • Filename Smuggling adalah serangan yang dapat menciptakan path traversal ketika karakter Unicode dalam nama file berubah menjadi / atau \ di jalur ANSI API
    • API terkait mencakup GetCurrentDirectoryA, getcwd, FindFirstFileA, findfirst*, GetFullPathNameA, dan lainnya
    • Halaman kode yang terdampak adalah 874, 125x, 932 (JP), dan 949 (KR)
    • Karakter ancamannya adalah U+FF0F, U+FF3C, ¥ U+00A5 (JP), dan U+20A9 (KR)
  • d8.exe, Developer Shell milik Chrome V8, menggunakan GetCurrentDirectoryA() dalam implementasi internalnya untuk mendapatkan direktori kerja saat ini
    • Jika direktori kerja yang berisi karakter Unicode berbahaya dapat dibuat, karakter tersebut berubah menjadi payload path traversal saat diakses melalui ANSI API
    • Sebagai contoh, akses tak disengaja ke C:/windows/win.ini menjadi mungkin
  • Implementasi Windows dari Dir.getwd() di mruby bergantung pada fungsi ANSI CRT _getcwd()
    • Nilai kembaliannya dapat terkontaminasi dan dapat berujung pada Path Traversal

Cuckoo Sandbox: dari Path Traversal hingga RCE

  • Akses sistem file Windows di Python sebelumnya dapat menggunakan wide API atau ANSI API, tergantung apakah string-nya wide atau narrow
    • Setelah PEP 529, encoding sistem file Windows distandardisasi menjadi UTF-8
    • Python 2 dan Python 3 sebelum Python 3.6 tetap rentan terhadap serangan WorstFit
  • Cuckoo Sandbox adalah platform analisis malware otomatis, dan versi resmi terbarunya bergantung pada Python 2.7
    • Cuckoo terdiri dari Cuckoo Host dan VM Cluster
    • Sampel yang diunggah dijalankan secara terisolasi di VM, lalu paket jaringan, file drop, dan log disinkronkan melalui mekanisme internalnya
  • Jika malware membuat file drop dengan nama file Unicode, Path Traversal dapat terjadi dalam pemrosesan path Python di Cuckoo Host
    • Contoh PoC membuat path AAAA\u00a5..\u00a5..\u00a5..\u00a5..\u00a5..\u00a5conf\u00a5cuckoo.conf
    • Setelah analisis selesai, saat pengguna menekan tombol unduh di antarmuka web, operasi file Python terpicu
    • Cuckoo Host dapat memproses path hasil konversi yang berisi ../ dan mengirim data sensitif kepada penyerang
  • Penyerang dapat mengunduh cuckoo.conf, mengumpulkan informasi sensitif yang diperlukan untuk menghitung Flask PIN, lalu mencapai RCE di Sandbox Host

Argument Splitting: Best-Fit yang mengubah parsing command line

  • Argument Splitting adalah serangan ketika string command line berubah dan argumen terpecah pada output GetCommandLineA atau jalur non-Unicode int main()
    • API dan jalur terkait adalah GetCommandLineA, int main()
    • Code page yang terdampak adalah 874, 125x, 932(JP), 949(KR)
    • Karakter ancaman adalah U+FF02, U+FF3C, ¥ U+00A5(JP), U+20A9(KR)
  • Contoh kode PHP membungkus URL dengan aman memakai escapeshellarg() lalu menjalankan wget.exe -q, tetapi input " --use-askpass=calc " memungkinkan eksekusi calc.exe
    • Input yang sama tetap tidak tertangkal meski diganti ke Node.js, Rust, atau Python
    • Ini juga bekerja pada contoh subprocess.run(["wget", "-q", ...]) di versi terbaru Python
  • Windows meneruskan seluruh command line ke proses baru sebagai satu string, dan executable mem-parsing-nya sendiri
    • Strukturnya bukan seperti keluarga UNIX yang selalu meneruskan array argumen
    • API CreateProcess menerima parameter lpCommandLine secara langsung
  • Dalam parsing command line umum, karakter pentingnya adalah spasi dan tab, double quote, serta backslash
    • Spasi dan tab memisahkan argumen saat bukan dalam quote mode
    • " mengalihkan quote mode
    • \ meng-escape double quote dan backslash pada urutan tertentu
  • Standard library di sebagian besar bahasa meng-escape argumen pengguna sesuai aturan ini, tetapi escaping selesai sebelum konversi Best-Fit
    • PHP escapeshellarg mengganti double quote menjadi spasi, membungkus argumen dengan quote, dan menangani backslash
    • Python subprocess meng-escape dengan list2cmdline agar sesuai aturan parsing command line Microsoft CRT
    • Setelah itu, jika dalam konversi ANSI U+FF02 berubah menjadi " U+0022, sintaks command line aslinya berubah
  • Program yang hanya menggunakan int main() juga bisa rentan
    • Compiler membuat mainCRTStartup di dalam binary, dan fungsi startup ini ditautkan dengan library CRT
    • Jika bagian internal CRT mengambil command line melalui ANSI API lalu mem-parsing-nya, konversi Best-Fit ikut terlibat
    • Karena perilaku ini, sulit mencegah serangan sepenuhnya hanya dengan standard library dari bahasa pemrograman tertentu

Kasus nyata Argument Splitting

  • ElFinder adalah manajer file web open source berbasis backend PHP, dan secara default mendukung server Windows serta pembuatan dan ekstraksi arsip
    • Pemrosesan arsip diimplementasikan dengan eksekusi shell command, dan argumen di-escape dengan escapeshellarg
    • Untuk pemrosesan format tar digunakan tar.exe bawaan Windows
    • Dengan nama file tar seperti aaa" "--use-compress-program=calc" "bbb.tar, argumen --use-compress-program dapat disisipkan untuk menjalankan perintah arbitrer
    • Demo dilakukan pada Windows server berkonfigurasi bahasa Inggris dengan Code Page 1252, dan dirangkum bahwa ini seharusnya juga bekerja pada code page 125x dan Code Page 874
    • Video demo tersedia sebagai Video 12
  • Kasus plink.exe yang dimodifikasi dan digunakan di TortoiseGit dapat memicu eksekusi kode jika URI berbahaya dimasukkan sebagai input clone
  • RStudio mendukung version control SVN, dan jika sebuah proyek SVN berada di folder yang dibuat secara berbahaya, kalkulator dapat dijalankan dengan satu klik
  • Kasus Microsoft Excel adalah CVE-2024-49026 yang menggabungkan Argument Splitting dengan fitur “Open-With” Windows
    • Windows mempertahankan handler table untuk tiap ekstensi file, yang dapat diperiksa dengan ftype dan assoc
    • Karena nama file menjadi bagian dari argumen program handler, serangan dapat diterapkan melalui nama file
    • Nama file yang mengubah dot, slash, backslash, dan double quote ke bentuk fullwidth memicu argument injection ke Excel.exe
    • Karena Excel sendiri tidak memiliki argumen yang cocok untuk eksploitasi lanjutan, RCE dicapai dengan menggunakan NTLM Relay bersama RBCD/ADCS
    • Video demo adalah Video 15

Kebingungan Variabel Lingkungan

  • Kebingungan Variabel Lingkungan terjadi ketika GetEnvironmentVariableA, GetEnvironmentStringsA, dan char *getenv() mengembalikan versi variabel lingkungan yang telah dikonversi dengan Best-Fit
    • Code page yang terdampak dan karakter ancamannya tidak disebutkan secara spesifik
    • Dalam kasus Apache HTTPd, 0x00-0xFF terkait
  • Agar serangan ini berhasil, variabel lingkungan harus dapat dikendalikan oleh pengguna
    • Ini berlaku ketika proses induk meneruskan informasi ke proses anak yang dibuatnya
    • Pada CGI, sebagian besar informasi permintaan HTTP seperti query string dan HTTP header diteruskan sebagai variabel lingkungan
  • Contoh bypass WAF membahas situasi ketika skrip CGI bertindak seperti routing service
    • Konfigurasi Apache memiliki aturan yang menolak REQUEST_URI yang berisi /admin untuk mencegah akses jarak jauh ke /cgi.pl/admin
    • Karena perilaku WorstFit pada Windows Perl, bypass dimungkinkan jika sebagian dari admin diganti dengan padanan Best-Fit
    • Pada Code Page 1250, à U+00E0 berubah menjadi a selama konversi ANSI
    • Permintaan /cgi.pl/%E0dmin terlihat sebagai jalur berbeda oleh aturan sisi server, tetapi ketika skrip Perl CGI membaca PATH_INFO melalui ANSI API, permintaan itu diproses sebagai /admin
  • Pada PHP-CGI di Windows, dalam konfigurasi tertentu telah dikonfirmasi adanya oracle pengecekan keberadaan file dan potensi LFI
    • Penyebabnya adalah cara pemrosesan PATH_INFO dan variabel lingkungan terkait path lainnya
    • Permintaan /index.php/foo/bar diteruskan, dari sudut pandang Apache, sebagai variabel lingkungan seperti REDIRECT_URL, REQUEST_URI, PATH_INFO, dan PATH_TRANSLATED
    • Dengan informasi ini saja, sulit membedakan secara jelas batas antara nama file PHP dan tambahan PATH_INFO, sehingga php-cgi.exe menafsirkannya
  • Jika memanfaatkan ¥ pada code page Jepang, interpretasi path antara web server dan PHP-CGI menjadi berbeda
    • Web server memperlakukan keseluruhan /..¥..¥windows/win.ini/foo sebagai tambahan PATH_INFO
    • PHP-CGI menerima nilai yang telah dikonversi seperti REQUEST_URI=/index.php/..\..\windows/win.ini/foo dan mengalami kebingungan dalam proses membedakan file PHP sebenarnya dari PATH_INFO
    • Pada Apache, file existence oracle dimungkinkan melalui perbedaan respons antara file yang tidak ada dan file yang ada
    • Pada IIS, jika directive doc_root disetel, LFI yang meng-include dan membaca C:\Windows\win.ini dimungkinkan melalui path seperti /index.php/..¥..¥..¥windows/win.ini/
    • Jika file yang disertakan dapat dieksekusi atau berisi kode yang dapat dikendalikan pengguna, hal ini berpotensi berujung pada RCE, tetapi skenario tersebut diklasifikasikan lebih sebagai bug yang jarang terjadi pada aplikasi nyata

Kesulitan dalam Proses Pengungkapan dan Perbaikan

  • Tim peneliti melaporkan berbagai masalah pada bahasa pemrograman, proyek open source, dan program CLI bawaan Windows kepada masing-masing upstream maintainer
    • Perdebatan paling banyak terjadi pada Argument Splitting
    • Sebagian vendor menganggap tindakan meneruskan input pengguna ke command line itu sendiri sebagai kerentanan
  • Ketidakjelasan tanggung jawab juga menjadi masalah
    • Kode bermasalah tersebar di mainCRTStartup() yang disisipkan otomatis saat kompilasi dan pemanggilan ANSI API internal MSVCRT/UCRT
    • Sulit membedakan apakah masalahnya karena developer tidak menggunakan wmain(), atau karena CRT salah membagi command line lalu meneruskan argumen yang keliru ke main()
    • Sebagian proyek hanya menyediakan source code, sementara Windows prebuilt executable didistribusikan oleh relawan pihak ketiga di internet
  • Perbaikannya tidak sesederhana mengganti main() menjadi versi wide-character
    • Jika function signature berubah, definisi variabel dan logika parsing argumen harus ditulis ulang dari berbasis char * menjadi berbasis wchar_t *
    • Proses ini menyakitkan dan rentan kesalahan
  • Curl menjawab bahwa ini adalah fitur Windows dan tidak berencana memperbaikinya, sedangkan Curl yang di-port oleh Microsoft memperbaiki entry menjadi wmain() sehingga curl.exe bawaan Windows tidak terdampak
    • Binary build resmi Curl terdampak serangan Argument Splitting
    • Laporan lengkap dipublikasikan di HackerOne
  • OpenSSL dapat memproses argumen dalam format wide character melalui variabel lingkungan OPENSSL_WIN32_UTF8
    • Tujuan awalnya adalah memperbaiki masalah tampilan UTF-8 di UI, tetapi ini juga memitigasi serangan Argument Splitting
    • Dalam penggunaan OpenSSL default, banyak developer tidak tahu bahwa mereka harus menyetel variabel lingkungan ini, dan eksekusi kode arbitrer dimungkinkan menggunakan argumen -engine
  • Distribusi resmi Perl tidak menyediakan Windows prebuilt executable, dan installer pihak ketiga seperti Strawberry Perl dan ActiveState Perl umum digunakan
    • Kedua distribusi tersebut terdampak serangan Argument Splitting
    • Setelah berdiskusi dengan maintainer Perl, disimpulkan bahwa ini “lebih mirip bug Microsoft daripada bug Perl”, sehingga saat ini masih belum terselesaikan
  • Tiga kasus dilaporkan ke Microsoft melalui MSRC, dan semuanya awalnya ditolak karena tidak memenuhi kriteria tingkat keparahan
    • Setelah dibuka kembali beberapa kali, hanya kasus Excel yang diterima setelah percobaan ketiga
    • Kasus lainnya masih belum terselesaikan hingga saat ini
    • MSRC menjawab bahwa kasus ini bergantung pada kerentanan aplikasi terpisah yang menjalankan input tidak tepercaya dengan memasukkannya ke command line, dan technique yang membuat eksploitasi itu memungkinkan tidak memenuhi syarat sebagai kerentanan
  • Bantuan juga diminta kepada CERT/CC, dan beberapa bulan kemudian Microsoft menambahkan peringatan keamanan ke dokumentasi GetCommandLineA
    • Peringatan hanya ditambahkan pada GetCommandLineA, sementara masih ada ANSI API lain yang perlu diwaspadai

Target Terdampak yang Dilaporkan dan Statusnya

  • Item yang dikonfirmasi dan dilaporkan selama proses pengungkapan adalah sebagai berikut
    • 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

Mitigasi dan Permukaan Serangan yang Tersisa

  • Serangan WorstFit adalah masalah di tingkat sistem operasi, sehingga hingga Microsoft mengaktifkan UTF-8 sebagai default di semua edisi Windows, isu serupa dapat terus muncul kembali
  • Tindakan yang dapat dilakukan pengguna adalah memeriksa dan mengaktifkan opsi UTF-8 di Windows
    • Fitur ini masih ditandai berada pada tahap beta, dan belum jelas apakah ada efek samping
  • Developer sebaiknya menggunakan Wide Character API sebisa mungkin
    • CRT juga menyediakan versi wide character seperti _wgetcwd dan _wgetenv
    • Jika terus menggunakan jalur non-wide, implementasi internal dapat memanggil ANSI API dan terekspos pada serangan WorstFit
  • Karena kompatibilitas mundur Windows, kemungkinan masih ada tempat lain tempat ANSI API tersembunyi
    • Sebagai contoh, query Windows Registry seperti RegQueryValueA dapat terdampak, tetapi skenario kerentanannya perlu dicari
    • Tim peneliti juga mengamati perilaku Best-Fit di Active Directory

1 komentar

 
GN⁺ 2025-01-10
Komentar Hacker News
  • Ini masalah yang cukup rumit. Pemetaan kode “best fit” milik Microsoft adalah pemeta yang mengubah Unicode yang luas menjadi ASCII; memang terbuka, tetapi pada praktiknya berbasis “perkiraan rasa”, dan tertanam di seluruh sistem
    Pemeta ini terhubung secara default di sangat banyak tempat, dan melihat cara Microsoft menangani kompatibilitas mundur, tampaknya mau tak mau akan terus disertakan. Eksploit umumnya muncul ketika code point yang tidak biasa dipetakan secara “terasa mirip” menjadi slash, hyphen, atau tanda kutip. Di dalam bahasa modern ia diperiksa sebagai Unicode yang benar, tetapi saat diteruskan ke perintah shell atau Win32 API, setelah kontrol diserahkan ia direduksi dengan cara yang berbeda. Seperti kata maintainer curl, di sini “curl adalah korban”, tetapi pertanyaannya adalah siapa pelakunya. Jika server memvalidasi input pengguna secara berbeda dari cara ia merusaknya saat menyerahkannya ke library sistem, pada akhirnya masalah memang akan muncul. Opsi untuk mematikan konversi best fit di sisi Win32 mungkin bisa menjadi solusi, tetapi saya bukan ahli Windows jadi ini hanya dugaan. Bahkan jika begitu, tetap akan ada interaksi dengan API resmi atau perangkat lunak yang masih belum dimatikan

    • Cara opt-out adalah memakai Unicode Windows API, yaitu menggunakan fungsi yang berakhiran "w", bukan "a". Pendekatan ini juga menyelesaikan masalah path lebih dari 260 karakter jika menambahkan prefiks "\\?\" atau mengatur manifest dengan benar, dan ini sudah dimungkinkan serta direkomendasikan sejak Windows XP
      Saya tidak terlalu paham mengapa API non-Unicode masih sesering ini dipakai. Sulit membayangkan alasannya karena masih ingin mendukung Windows 98 atau Windows 2000
    • Di Windows, sejak Windows XP ada file manifest sebagai cara untuk mematikan perilaku lawas. Kalau saya tidak salah ingat, tanpa manifest bahkan GetWindowsVersion pun tidak mengembalikan versi saat ini. Menambahkan opt-out di sini, lalu suatu hari menjadikannya default di Visual Studio, tampaknya tidak akan terlalu sulit
      Hal lain yang dibutuhkan adalah semacam linting. Dalam aplikasi modern biasanya tidak ada alasan untuk memanggil fungsi ANSI WinAPI. Memang ada pendekatan mengatur locale ke UTF-8 lalu hanya memakai fungsi 8-bit, tetapi saya tidak tahu seberapa baik itu bekerja. Setahu saya ada beberapa pengaturan dan header yang membuat argv, printf, std::cout bekerja dengan UTF-8 dan memaksa kita hanya memakai fungsi konversi UTF-8/UTF-16 untuk WinAPI tanpa transformasi aneh. Microsoft perlu mendokumentasikan prosedur semacam ini di satu tempat
    • Terlepas ini celah keamanan atau bukan, jika curl tidak bisa menangani argumen Unicode dengan benar di Windows, itu juga bug di curl
    • Cara memetakan code point ke karakter secara longgar selalu terasa mengganggu di Unicode
  • Ini sampai taraf tertentu bisa diduga, tetapi bahkan bagi saya yang pernah sekitar 10 tahun berkutat dengan pengembangan Windows dan hacking API Wine pada masa kebingungan W/A, ini tetap terasa baru
    Windows itu seperti permainan kartu Munchkin: jika beberapa fitur kebetulan saling terkait, mereka bisa menyatu menjadi eksploit yang terasa acak sekaligus sangat kuat sampai sulit dipercaya. Saya senang bahwa subsistem ANSI sedang diubah ke UTF-8, dan secara teori itu bisa sangat mengurangi masalah seperti ini. Saya juga penasaran apakah tim Rust perlu melakukan perbaikan lain lagi pada API pembuatan proses

    • Library standar Rust pada dasarnya hampir tidak memakai API ANSI. Tulisan itu juga tidak menunjukkan serangan yang berlaku pada Rust, dan kalau memang ada serangan seperti itu, sebaiknya dilaporkan
      Tentu saja Rust tidak bisa mengendalikan apa yang terjadi di luar batas proses. Jika aplikasi yang dijalankan oleh Rust memakai API ANSI, masalahnya ada di sana, tetapi itu tanggung jawab aplikasi tersebut
  • Jika ingatan saya benar, “menghapus ANSI secara bertahap dan merekomendasikan penggunaan Wide Character API” sudah menjadi posisi resmi Microsoft sejak NT 3.5
    Sayangnya, salah satu hambatan besar adalah cara Microsoft mengimplementasikan library runtime C/C++ msvcrt.dll. Fungsi wide nonstandar seperti _wfopen() dan _wgetenv() secara internal memakai fungsi W dari Win API, tetapi fungsi narrow standar seperti fopen() dan getenv() tidak dikonversi ke versi wide, melainkan langsung memakai fungsi A. Dan fungsi A biasanya tidak melaporkan kegagalan konversi Unicode, melainkan menimpanya dengan pendekatan best-fit. Orang yang memindahkan perangkat lunak C ke Windows biasanya tidak ingin mengganti semua pemakaian fungsi standar menjadi fungsi Microsoft yang tidak portabel. Dari titik itu, praktiknya sudah seperti menulis ulang total

    • Kesan yang saya dapat justru sebaliknya setelah membaca dokumentasi Microsoft dalam 2 tahun terakhir. Arahnya terasa ke menetapkan activeCodePage menjadi UTF-8 di manifest aplikasi dan tetap memakai fungsi “ANSI”
    • Dalam kode yang portabel, saat build untuk Windows, fungsi standar seperti main dan fopen di-#define menjadi padanan wide-nya
      Dengan begitu kita tidak bisa begitu saja memakai char* dan string literal biasa, jadi didefinisikan tipe tchar yang menjadi char di Linux dan wchar_t di Windows, serta makro _T() untuk string literal. Biasanya ini berjalan cukup mulus tanpa banyak dipikirkan
    • Hal yang sekarang benar-benar menjengkelkan adalah saat mencari Win32 API di Google, yang selalu muncul lebih dulu adalah varian -A, bukan varian -W. Saya tidak tahu apakah ada sesuatu yang aneh di robots.txt, tetapi terasa janggal bahwa API yang direkomendasikan untuk memakai varian -W pada kode baru justru secara default menampilkan API lawas
    • Runtime C/C++ Microsoft msvcrt.dll telah digantikan oleh Universal C Runtime (UCRT)[1], dan UCRT mematuhi C99
    • Windows seharusnya menyediakan API yang memperlakukan path name sebagai deretan byte saja, tanpa penanganan encoding bodoh seperti ini. Rasanya itu mungkin saja dilakukan saat memperkenalkan path UNC
  • Ada dua cara untuk benar-benar memaksa code page “Ansi” menjadi UTF-8 pada aplikasi yang Anda tulis sendiri atau EXE yang Anda patch
    Salah satunya memakai file manifest, dan ini berfungsi mulai build tertentu di Windows 10. Ini juga bisa diterapkan setelah proses build pada EXE apa pun, jadi dukungan UTF-8 bisa dipaksakan ke suatu program. Sangat berguna khususnya untuk program mode konsol. Cara lainnya adalah memakai hack yang digunakan alat jenis “App Locale”. Salah satu metodenya mencakup pemanggilan fungsi NTDLL yang tidak terdokumentasi. Saya tidak tahu persis fungsi mana yang dibutuhkan, tetapi RtlInitNlsTables dan RtlResetRtlTranslations mungkin terkait

  • Saya ragu Microsoft bisa mengaktifkan UTF-8 sebagai default di semua edisi Windows. Ada banyak aplikasi lama yang mengasumsikan code page tertentu atau 1 byte per karakter, jadi bisa rusak.
    Yang lebih subtil, ada juga aplikasi yang memakai ulang buffer lama dengan asumsi bahwa saat mengonversi dari wide character ke ANSI jumlah byte tidak akan bertambah. Di UTF-8 itu tidak berlaku, sementara di sebagian besar code page lama asumsi itu umumnya benar, jadi ini bisa memunculkan kerentanan baru. Sepertinya akan jauh lebih sedikit merusak jika Microsoft menghapus logika Best-Fit dari API Win32 xxxA, lalu mengganti karakter yang tidak bisa dipetakan dengan karakter seperti x yang tidak punya makna meta umum

    • Contoh aplikasi seperti itu adalah Adobe After Effects[0]. Setidaknya dulu begitu, dan sekarang saya tidak lagi memakai Windows
      [0] https://tambre.ee/blog/adobe_after_effects_windows_utf-8/
    • Kalau belum ada, mungkin bisa diperkenalkan versi API OS, sehingga aplikasi baru/yang diperbarui yang menargetkan versi API baru atau SDK baru secara default mengasumsikan UTF-8. Untuk versi API di bawah ambang tertentu, tinggal diemulasikan dalam mode legacy. Di Windows sudah ada konsep shim yang meniru perilaku berbagai versi Windows
    • Bahkan sebelum UTF-8 di Windows, mengganti code page default memang sudah bisa membuat aplikasi bertingkah aneh. Jadi memberi opsi UTF-8 kepada pengguna itu masuk akal.
      Melihat masalah yang ditimbulkan oleh pemetaan Best-Fit, menjadikannya default juga masuk akal, tetapi Microsoft harus membantu pengguna menemukan cara agar kode lama tetap mudah dijalankan. Cara yang kurang masuk akal adalah menghapus semua pemetaan Best-Fit menuju karakter ASCII “khusus”, tetapi itu tidak membantu aplikasi yang menautkan CRT secara statis. Itu juga tidak benar-benar memperbaiki kerentanannya, jadi bukan solusi yang bagus. Kadang kerentanan keamanan memang menjadi pendorong untuk merusak kompatibilitas ke belakang
  • Microsoft setidaknya sudah mengetahui masalah ini sejak 1 tahun lalu. Alasannya, mereka merilis aturan analisis kode khusus bernama CA2101[1] yang secara eksplisit tidak menganjurkan penggunaan pemetaan best-fit
    Penjelasan aturannya memang menyebut kerentanan keamanan, tetapi detailnya sengaja dibuat samar
    [1] https://learn.microsoft.com/en-us/dotnet/fundamentals/code-a...

  • Tidak perlu mengubah semuanya dari char * menjadi wchar *. Cukup konversi wide character yang diterima ke UTF-8, atau jika ingin tetap mengizinkan sekuens tidak valid seperti surrogate yang tidak berpasangan, konversikan ke sesuatu seperti WTF-8 milik Rust lalu tetap gunakan char
    Tentu saja Anda harus berhati-hati agar tidak mencampur string ANSI atau OEMCP dengan string UTF-8, tetapi kalau hanya memakai UTF-8, semuanya jadi mudah. Pendekatan inilah yang direkomendasikan oleh situs klasik https://utf8everywhere.org/

  • Di komputer Windows pribadi saya, saya rupanya sudah menghindari bug ini secara tidak sengaja karena beberapa tahun terakhir mengaktifkan mode UTF-8. Itu adalah pengaturan yang disebutkan di bagian bawah artikel
    Awalnya saya mengaktifkannya karena game-game asing lama menampilkan teks rusak, dan meskipun ditandai sebagai “Beta”, saya tidak merasakan bug atau efek samping apa pun

    • Menarik, tetapi dalam kasus saya, checkbox itu tidak melakukan hal lain selain membuat terlalu banyak aplikasi acak crash. Sepertinya apakah suatu aplikasi bekerja dengan baik saat opsi itu dimatikan bergantung pada code page default pengguna
    • Saya baru saja mengaktifkan opsi “Beta: Use Unicode UTF-8 for worldwide language support”. Akan menarik melihat berapa banyak aplikasi yang bakal rusak
  • Saya penasaran apakah checkbox beta itu sama dengan mengatur ActiveCodePage ke UTF-8 di manifest, tetapi dari dokumentasi[0] terlihat jelas bahwa GDI tidak mengikuti code page per-proses dan hanya mengikuti satu code page global yang diatur oleh checkbox tersebut
    Agak disayangkan karena ini berarti aplikasi tidak bisa sepenuhnya opt-in ke UTF-8 di API *A hanya untuk aplikasinya sendiri. Meski begitu, saya rasa ini tetap bisa menjadi solusi sementara yang valid atau langkah defense-in-depth untuk masalah yang disorot artikel tersebut
    [0] https://learn.microsoft.com/en-us/windows/apps/design/global...

  • Astaga. Saya tahu API Windows menyediakan konversi best-fit semacam itu, tetapi saya tidak tahu bahwa di code page default saya, 949[1], itu adalah perilaku default dari banyak fungsi ANSI
    Pada titik ini, ini rasanya seperti gets dan seharusnya langsung dilarang. [1] Saya tahu ada code page UTF-8 65001. Dulu lama sekali benar-benar tidak layak dipakai, dan bahkan sekarang pun masih mengalami masalah kompatibilitas