- Unicode’un variation selector’ları art arda eklenerek, ekranda görünmeyen ama kopyala-yapıştırla birlikte taşınan bir bayt dizisi tek bir karakterin arkasına gizlenebilir
- VS-1’den VS-256’ya kadar 256 variation selector vardır; bu da 1 baytlık aralıkla bire bir eşleşen bir eşleme oluşturmayı mümkün kılar
😊 arkasına hello’nun baytları olan [0x68, 0x65, 0x6c, 0x6c, 0x6f] eklense bile dışarıdan bakıldığında sıradan tek bir emoji gibi görünür
- Çözme işlemi
U+FE00..U+FE0F ve U+E0100..U+E01EF aralıklarını bulup yeniden bayta çevirir; temel karakterin emoji olması gerekmez
- Bu yöntem Unicode’un kötüye kullanımıdır ve insan denetimli içerik filtrelerini aşmak ya da metne filigran gömmek için kötüye kullanılabilir
Görünmez verinin tek bir karaktere eklenme biçimi
- Unicode metin, kod noktası (codepoint) dizileriyle temsil edilir ve genellikle
U+XXXX biçiminde gösterilir
- Basit Latin karakterlerinde kod noktası ile ekranda görünen karakter bire bir eşleşir
- Örnek:
U+0067, g karakterini temsil eder
- Diğer yazı sistemlerinde ekrandaki tek bir karakter birden fazla kod noktasından oluşabilir
- Örnek: Devanagari’de
ki diye okunan karakter, U+0915 ve U+0940 ardışık çiftiyle temsil edilir
Variation selector’ları veri deposu gibi kullanmak
- Unicode, VS-1’den VS-256’ya kadar adlandırılan 256 variation selector kod noktası tanımlar
- Variation selector’ın kendisi ekranda gösterilmez; önceki karakterin gösterim biçimini değiştirmek için kullanılır
- Çoğu Unicode karakterinin ilişkili bir varyasyonu yoktur; ancak Unicode geleceğe dönük uyumluluğu hedeflediğinden, anlamını bilmeyen işleme kodlarının da variation selector’ı koruması gerekir
U+0067 (g) arkasına U+FE01 (VS-2) eklense bile ekranda küçük harf g gibi görünür
- Kopyala-yapıştır yapıldığında variation selector da birlikte taşınır
- 256 variation selector, tam olarak 1 baytı temsil etmeye yetecek sayıdadır; bu nedenle herhangi bir Unicode kod noktasının arkasına 1 bayt veri gizlenebilir
- Unicode belirtimi, birden fazla variation selector’ın art arda geldiği dizileri ayrıntılı olarak ele almaz ve işleme sırasında bunların yok sayılması gerektiğini ima eder
- Birden fazla variation selector art arda eklenirse, rastgele bir bayt dizisi tek bir karakterin arkasında temsil edilebilir
Baytları variation selector olarak kodlama
- Variation selector’lar iki kod noktası aralığına ayrılır
U+FE00 .. U+FE0F: ilk 16’sı
U+E0100 .. U+E01EF: kalan 240’ı
- Bir baytı variation selector’a çevirme kuralı basittir
- Bayt 16’dan küçükse
0xFE00 + byte
- Aksi halde
0xE0100 + (byte - 16)
- Kodlama, önce bir temel karakter (base character) ekleyip ardından her baytı bir variation selector’a dönüştürerek peş peşe ekler
fn byte_to_variation_selector(byte: u8) -> char {
if byte < 16 {
char::from_u32(0xFE00 + byte as u32).unwrap()
} else {
char::from_u32(0xE0100 + (byte - 16) as u32).unwrap()
}
}
fn encode(base: char, bytes: &[u8]) -> String {
let mut result = String::new();
result.push(base);
for byte in bytes {
result.push(byte_to_variation_selector(*byte));
}
result
}
hello’yu temsil eden [0x68, 0x65, 0x6c, 0x6c, 0x6f] baytları 😊 arkasına eklenirse, dışarıdan sıradan bir emoji gibi görünen bir dizge oluşur
- Normal çıktıda gizli karakterler görünmez; ancak Rust’ın debug formatıyla yazdırıldığında
\u{e0158} gibi gizli kod noktaları ortaya çıkar
"😊\u{e0158}\u{e0155}\u{e015c}\u{e015c}\u{e015f}"
Gizli baytları yeniden okuma yöntemi
- Çözme işlemi, karakterleri dolaşırken variation selector aralıklarındaki kod noktalarını yeniden bayta dönüştürür
U+FE00..U+FE0F aralığı variation_selector - 0xFE00 ile geri yüklenir
U+E0100..U+E01EF aralığı variation_selector - 0xE0100 + 16 ile geri yüklenir
- İlk variation selector’a kadar olan normal karakterler temel karakter kabul edilip yok sayılır
- Variation selector olmayan bir karakterle karşılaşılır ve halihazırda sonuç varsa çözme işlemi sonlandırılır
fn variation_selector_to_byte(variation_selector: char) -> Option<u8> {
let variation_selector = variation_selector as u32;
if (0xFE00..=0xFE0F).contains(&variation_selector) {
Some((variation_selector - 0xFE00) as u8)
} else if (0xE0100..=0xE01EF).contains(&variation_selector) {
Some((variation_selector - 0xE0100 + 16) as u8)
} else {
None
}
}
- Aynı kodlama sonucu çözüldükten sonra UTF-8 olarak yorumlandığında
"hello" elde edilir
- Temel karakterin emoji olması gerekmez; normal karakterlerde de variation selector işleme biçimi aynıdır
- Emoji kullanılmasının nedeni yalnızca daha eğlenceli olmasıdır
Kötüye kullanım olasılığı
- Bu yöntem Unicode’un kötüye kullanımıdır ve kullanılmamalıdır
- İşlenmiş sonuçta veri görünmediği için insan moderatörlerin veya inceleyicilerin gizli verinin varlığını fark etmesi zordur
- İnsan denetimli içerik filtrelerinden geçerek veri gizleme yöntemi olarak kötüye kullanılabilir
- Metin filigranlama için de kullanılabilir
- Mesaj birden fazla kişiye gönderildikten sonra sızdırılırsa asıl alıcı takip edilebilir
- Variation selector dizileri çoğu kopyala-yapıştır işleminden sağ çıkar
- İstenilen veri yoğunluğuna izin verir; istenirse her karaktere filigran eklenebilir
LLM gizli veriyi işleyebilir mi
- Hacker News’te yayımlandıktan sonra, LLM’lerin bu tür gizli verileri nasıl ele aldığına dair bir soru ortaya çıktı
- Genel olarak tokenizer’ların variation selector’ları token olarak koruduğu ve teorik olarak modelin bunlara erişebildiği görülüyor
- OpenAI tokenizer, bunu doğrulamak için kullanılabilecek bir kontrol aracıdır
- Genel olarak modellerin dahili olarak doğrudan çözmeyi denemediği görülüyor
- Kod yorumlayıcıyla birlikte kullanıldığında bazı modeller gizli veriyi çözebiliyor
1 yorum
Hacker News yorumları
Unicode’un kötüye kullanımında bu sadece buzdağının görünen kısmı. Benzer tekniklerle Unicode dizgesi alan pek çok sistemde tampon taşırılabiliyor; genelde hata ya da çökme ile sonuçlanıyor ama şanslıysanız epey ilginç davranışlar da çıkabiliyor.
Python 3 öncesi dönemde sızma testi yaparken, yalnızca aksan/ayırıcı işaretlerle tek bir karakteri çok bayta genişletip arka uç web sunucusunun tamponunu taşırmıştım. O zaman sonuç sadece çökme ve otomatik yeniden başlatmaydı, ama yeterince kurcalanırsa belirli sistem ya da yazılım exploit’lerinde kullanılabilecek gibi görünüyor.
Modern formlarda bile yalnızca JavaScript’i kapatarak benzer durumlar tetiklenebilir; en iyi ihtimalle debug açıktır da stack trace veya sorgular yazdırılır ve biraz bilgi sızar. Bir diğer yaygın hata da metin dizgilerinde
\nile\r\nuzunluğunu yanlış saymaktır; JavaScript genelde carriage return’ü 1 bayt sayar, ama HTTP spesifikasyonu 2 bayt ister.unescape(encodeURIComponent("ç")).length, JavaScript’te bayt uzunluğunu hızlıca kontrol etmek için kabaca kullanılan bir yöntemdir;\r\nsorunu da uzunluğu saymadan önce dizgeyi temizleyerek çözülür.Bu sevimli ama aslında gerekli değil. Unicode’da PUA (private use area) denen geniş bir aralık var; bu aralıktaki kodlar hiçbir karaktere eşlenmez ve gelecekte de eşlenmeyecekleri için dahili/kullanıcı tanımlı amaçlarla kullanılır.
Örneğin fish-shell’de token’ları güvenli biçimde dizge olarak ayrıştırırken, escape edilmemiş özel karakterleri dizge içindeki başka Unicode kod noktaları gibi değiştirir ama PUA bölgesine koyar ve daha sonra pipeline’da yakalar. API sınırının dışına sızdırılmamalıdır, ama karşılaşıldığında aynen geçirilmesi önerilir; çoğu sistem ve kütüphane de böyle yapar. Bariz bir sızıntı yolu olabilir; çünkü pek çok sıradan geliştirici Unicode hakkında “uluslararasılaştırma sorunlarından kaçınmak için her zaman Unicode kullan”dan fazlasını bilmediğinden bu yol çoğu zaman açık kalır.
). Buradaki ana nokta, kopyala-yapıştır sırasında gizli kalacak ve başka bir karakterin “parçası” gibi ele alınacak şekilde kodlamak.https://news.ycombinator.com/item?id=42791378
API’nin yalnızca emoji kabul etmesi kısıtı nedeniyle suç amaçlı kullanım ihtimali hemen tartışılmaya başlandı. O kullanımda PUA kullanılamaz; emojinin içine kodlamak gerekir.
Atanmış noncharacter’lar arasında
0xFFFF,0xFFFEve her düzlemin son iki kod noktası olduğu gibi, Arabic Presentation Forms’un ortasında bir bölge de var. İnsanların bu şekilde kullanabileceği daha fazla noncharacter olsun diye listenin sonradan genişletildiğini biliyorum.Tanınmayan PUA karakterleriyle rastgele karakterleri görünmez şekilde watermark’lamak mümkün değil. Çünkü bunlar birleşen karakter olarak ele alınmaz. Bunun yerine ayrı render edilen yer tutucu kutular görünür. Örn:
— tabii private use area’yı gerçekten kendi özel kullanımınız için kullanıyorsanız kutu olmayabilir.Yaklaşık 10 yıl önce Windows dosya adının ortasına U+202D LEFT-TO-RIGHT OVERRIDE koyarak iş arkadaşlarımı şaşırtmıştım.
funnypicturegnp.exe,funnypictureexe.pnggibi görünüyordu.Fotoğraf önizlemesi gibi görünen özel bir ikon da ekleyince epey inandırıcı oluyordu.
.exeçoğu zaman otomatik engellenir, ama günümüzde zararlı uzantı çoğunlukla .html oluyor ve obfuscate edilmişwindow.locationyönlendirmesiyle sahte giriş sayfası açıyorlar.cute-cat-lmth.pnggibi RTL kötüye kullanımları nispeten yaygındı ama tespitleri de çok kolaydı; böyle e-postaları anında phishing olarak işaretliyorduk.https://trojansource.codes/
Temelde yorum gibi görünen ama derlendiğinde kod olarak çalışan kodu gizleyebiliyorsunuz. Yine de birçok metin editörü bu tür şüpheli yorumları zaten görünür kıldığı için CVE statüsünün tartışmalı olduğunu hatırlıyorum.
guitar_tab.txtadlı bir bat dosyası oluşturmuştumGerçek bir kullanım örneği olarak Sanity, bu numarayı kullanarak Content Source Maps’i “önizleme modu”nda web sayfasına sunulan gerçek metnin içine kodlamış0. Editörler yalnızca ilgili metne veya içeriğe tıklayarak derin içerik yapısı içindeki özgün konuma kolayca iz sürebiliyor
Dezavantajları ve sınırlamaları da var. Örneğin tarihler, zaman damgaları, URL’ler, ID’ler gibi olduğu gibi ayrıştırılması veya kullanılması gereken değerlere eklenmesini engellemek gerekiyor. Yine de oldukça eğlenceli bir numara
0 https://www.sanity.io/docs/stega
[1] https://github.com/sanity-io/content-source-maps
Bunu LLM çıktısı filigranlama için kullanma fikri hoşuma gitti. Tam doğru nokta. Zaten sadece kopyala-yapıştır yapan düşük kaliteli üreticilerin %99’u mecburen yakalanır; diğer temel kullanım senaryolarını da neredeyse hiç etkilemez
Her karaktere ya da çıktı token’ına ne kadar yerleştirileceğini de merak ediyorum. Kullanıcı ID’si, prompt referansı, tarih, token numarası gibi şeyler mi? Terminalde nasıl yorumlandığını da merak ediyorum; gerçekten harika
Gerçek yapay zeka savunması, tüm insan etkileşimlerinde gerçek kimlikle doğrulanmış anahtar imzası istemekten ibaret; o da A: asla gerçekleşmeyecek, B: yozlaşmış hükümetlerin olduğu ülkelerde veya özel sektörün güçlü etkisi altındaki yozlaşmış hükümetlerin olduğu ülkelerde, örneğin ABD’de, kötüye kullanılabilir
Elbette cümleyi
xxd’ye gönderirseniz görünür. Şu anki en üst yorumdaki PUA önerisi ise hemen görünür olmasından farklıEk test yaptığımda, terminale yapıştırdıktan sonra
xxdiçinde mesaj tamamen bozulmadan geçiyor; ancak terminalde seçip tekrar yapıştırınca mate terminal ve konsole’un X selection’ında yalnızca birkaç kelime kalıp kesildi. Kesilmenin terminalden mi X’ten mi kaynaklandığını bilmiyorum. xterm’de sonedeğişti ve seçilen içerik daha da fazla kesildiDosyaya cümle bozulmadan yazılıyor. Bu yüzden sorun, terminal dışına kopyalarken bazı verilerin düşmesine daha yakın görünüyor. Cümleyi bir test dosyasına
echoedip tarayıcıda açarak metni kopyalayıp kontrol ettimÜretim sırasında örnekleme yöntemine bir oyun eklerseniz, daha sonra LLM’i tekrar çalıştırıp çıktı desenini gözlemleyerek parmak izini tespit edebilirsiniz. Örneğin yüksek olasılıklı token’lar ile düşük olasılıklı token’ları dönüşümlü seçmek gibi. Gerçek uygulama elbette çok daha incelikli olurdu, ama fikir bu yönde
İlginç bir nokta olarak, ekran okuyucular karakter karakter ilerlerken bu variation selector’ları algılayabiliyor. Örneğin üzerinde ok tuşlarıyla ilerleyince “Smiling face with smiling eyes”, “Symbol e zero one five five”, “Symbol e zero one five c” diye okuyor
Ancak bu, kullanılan konuşma sentezleyicisine göre değişiyor ve belgeyi normal biçimde okurken böyle karakterler olup olmadığını anlayamadığınız için genel olarak büyük bir avantaj değil
StegCloak0 da benzer aileden ve gizli payload’u AES-256-CTR ile şifreleyerek bu fikri bir adım daha ileri taşıyor. Oldukça hoş küçük bir numara
0 https://github.com/KuroLabs/stegcloak
Ancak karşı tarafın çözebilmesi için parola sırrını paylaşmanız gerekiyor
Başlık biraz yanıltıcı. “Temel karakterin emoji olması gerekmez; variation selector işleme normal karakterlerde de aynıdır. Emojiyle yapmak sadece daha eğlencelidir” deniyor
Bu yöntemi emoji olmayan karakterlerde kullanırsanız daha gizli ve daha rahatsız edici olur
Basit LLM çıktısı filigranlamasından ziyade, bu logprobs verisini birlikte paketlemenin temiz bir yolu olabilir gibi görünüyor
Temelde, üretilen tüm token’ların olasılık bilgisini dahil ederek üretim sürecine bir miktar şeffaflık katmak. OpenAI API spesifikasyonunda da var ve
llama.cppgibi birçok motor da bu bilgiyi sağlıyor. Genellikle ayrı bir alan olarak ekleniyor, ama mikupad0 gibi görselleştirme yöntemleri de varMuhtemelen kötü bir fikir, ama yine de aklı kurcalayan bir düşünce
Harika bir teknik. ASCII’yi yansıtıyor ve UI öğeleri, özellikle de web uygulamalarında pek sık görünmeyen Unicode Tag karakterleri de var
Tag karakterlerinin ilginç yanı, bazı LLM’lerin gizli metni ASCII olarak yorumlayıp talimatları izlemesi; hatta bunların doğrudan yazılabilmesi
https://embracethered.com/blog/posts/2024/hiding-and-finding...
Microsoft’un Copilot’ta düzelttiği gerçek bir exploit kavram kanıtı da var
https://embracethered.com/blog/posts/2024/m365-copilot-promp...