Series · 3 partsBrowser-to-Native Trust BoundariesPart 1 · You are here
- 1Extension Sandbox'taydı. Native Host Değildi.You are here
- 2Sayfanın İzni Yoktu. Extension'ın Vardı.
- 3Paket İmzalıydı. Güncelleme Yine de Bir Güvenlik Kararıydı.
60 saniyede browser güven sınırı
Bir browser extension doğru şekilde paketlenmiş, imzalanmış, incelenmiş ve web sayfasından izole edilmiş olabilir. Buna rağmen işletim sistemine uzanan tehlikeli bir yetki yolu açabilir.
Native Messaging bilinçli olarak güçlü tasarlanmıştır. Browser, kayıtlı native uygulamayı ayrı bir süreç olarak başlatır ve standart girdi/çıktı üzerinden uzunluk bilgisi eklenmiş JSON mesajları aktarır. Native host bundan sonra sıradan web içeriğinin ulaşamadığı dosyalara, cihazlara, kimlik bilgilerine, socket’lere, child process’lere ve yerel yönetim arayüzlerine erişebilir.
Bu mimari tek başına zafiyet değildir. Zafiyet, ayrı soruları cevaplayan geçerli kararların tek bir authorization kararı gibi değerlendirilmesiyle ortaya çıkar:
- Bir sayfada content script çalışabilir.
- Content script extension service worker’a mesaj gönderebilir.
- Service worker kayıtlı native host’a bağlanabilir.
- Native host manifest ilgili extension ID’ye izin verir.
- Host bir JSON operasyonunu kabul eder.
- İşletim sistemi ortaya çıkan etkiye izin verir.
Bu kararların hiçbiri, isteği başlatan sayfanın, frame’in, kullanıcı hareketinin, nesnenin, dosya yolunun veya operasyonun yetkili olduğunu kanıtlamaz.
Temel araştırma sorusu şudur:
Web tarafından kontrol edilen niyet hangi noktada native işletim sistemi etkisine dönüşüyor ve nihai authorization kararını hangi bileşen veriyor?
Bu çalışma, yerel bir kanıtı kontrolsüz gerçek dünya istismarına dönüştürmeden bu soruyu cevaplayabilen tekrarlanabilir bir vulnerability research yöntemi kurar.
Tek özellik, beş ayrı principal
Chrome, content script’leri sayfanın JavaScript ortamından izole çalışan bileşenler olarak tanımlar. Bu bir execution isolation özelliğidir. Chrome’un mesajlaşma güvenliği rehberi ise content script’lerin service worker’dan daha az güvenilir kabul edilmesini ve onlardan gelen mesajların saldırgan tarafından hazırlanmış olabileceğinin varsayılmasını ister.
Bu ayrım kritiktir. İzole bir content script yine de sayfa kontrollü DOM değerlerini okuyabilir,
window.postMessage olaylarını alabilir veya ele geçirilmiş bir origin üzerinde tetiklenebilir.
Service worker sender bilgisini ve operasyonu doğrulamadan mesajı iletirse isolated world bir
güvenlik sınırı değil, yalnızca relay haline gelir.
Native Messaging gerçekte neyi garanti eder?
Platform kontrolleri değerlidir; ancak her biri dar bir soruyu yanıtlar.
| Mekanizma | Kanıtladığı şey | Kanıtlamadığı şey |
|---|---|---|
Extension nativeMessaging izni | Extension Native Messaging API’yi kullanabilir | Her extension bileşeninin her native operasyonu çağırabileceği |
Host allowed_origins / allowed_extensions | Belirtilen extension ID host’u başlatabilir | İsteğin hangi sayfa, frame, tenant veya kullanıcı akışından geldiği |
| Isolated content-script world | Sayfa JavaScript’i content script değişkenlerini doğrudan okuyamaz | Sayfadan türetilen verinin güvenilir olduğu |
sender.id ve sender.url | Browser’ın gözlemlediği sender context | Operasyonun, yolun veya nesnenin yetkili olduğu |
| Length-prefixed JSON | Mesaj sınırları olan bir transport formatı | Güvenli schema, semantic authorization veya replay koruması |
| Native süreç UID’si | Host’un çalıştığı OS kimliği | Browser caller’ın bu kimliğin tüm erişimini devralması gerektiği |
| Extension mağaza incelemesi | Dağıtım ve politika kontrol noktası | Ayrı kurulan native uygulamanın doğru olduğu |
Chrome native host’u ayrı bir süreç olarak başlatır. Mesaj UTF-8 JSON’dur ve önüne native byte order ile 32-bit uzunluk eklenir. Belgelenmiş browser-to-host sınırı 64 MiB, host-to-browser sınırı ise 1 MiB’dir. Bunlar güvenli uygulama limitleri değil, transport tavanlarıdır. 2 KiB’lık command envelope bekleyen bir host kendi limitini 2 KiB olarak uygulamalıdır.
Chrome host’a ilk argüman olarak extension origin’ini geçirir. Firefox benzer ancak farklı bir contract ve manifest alanı kullanır. Cross-browser uyumluluk kodu bu kimlikleri sessizce birleştirmemelidir. Chrome extension mesajlaşması JSON serialization kullanırken diğer implementasyonlarda structured clone davranışı görülebilir.
Zafiyet kalıbı: origin laundering
En yaygın hata memory corruption değil, origin laundering davranışıdır:
güvenilmeyen sayfa değeri
→ content-script nesnesi
→ extension mesajı
→ native JSON request
→ yerel dosya veya süreç etkisi
Her geçişte sonraki bileşen daha güvenilir görünen bir principal’dan mesaj alır:
- service worker kendi content script’ini görür;
- browser kurulu extension’ı görür;
- native host izin verilen extension origin’ini görür;
- işletim sistemi yerel native süreci görür.
Orijinal sayfa ve resource authorization bilgisi korunup yeniden doğrulanmazsa context kaybolurken güven seviyesi yükselir.
| Zafiyet sınıfı | Tehlikeli tasarım | Olası sonuç |
|---|---|---|
| Confused deputy | Her content-script mesajı host’a iletilir | Sayfa extension ve host yetkisini ödünç alır |
| Eksik object authorization | Caller keyfi path, device ID, profil veya hesap seçer | Cross-user veya cross-tenant erişim |
| Argument injection | Host mesaj alanlarını shell string’e ekler | Host kimliğiyle native code execution |
| Path traversal / link following | Son nesne doğrulanmadan yol birleştirilip açılır | Yetkili root dışından okuma veya yazma |
| Replay | Ayrıcalıklı request’te nonce, expiry veya state binding yoktur | Önceki etki tekrar oluşturulur |
| Parser asymmetry | Browser ve host tipleri veya boyutları farklı yorumlar | Doğrulanan nesneyle çalıştırılan nesne farklılaşır |
| Lifecycle confusion | Long-lived port navigation sonrasında state taşır | Yeni sayfa eski authorization context’i devralır |
| Installation boundary failure | Yazılabilir manifest veya host path binary’yi yönlendirir | Persistence veya privilege boundary geçişi |
Sınırlandırılmış sentetik laboratuvar
Üretim extension’ına veya üçüncü taraf hedefe ihtiyaç yoktur. Laboratuvar şunlardan oluşur:
- unpacked bir Manifest V3 extension;
- loopback üzerinde veya
https://lab.invalidorigin’inde bir test sayfası; - yalnızca laboratuvar extension ID için kayıtlı native host;
- hassas olmayan canary dosyalarının bulunduğu dizin;
- isimlendirilmiş bir canary okuma operasyonu;
- gerçekte hangi dosyanın açıldığını kaydeden bağımsız observer.
Güvensiz service worker güven context’inin nasıl kaybolduğunu gösterir:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type !== "native") return;
chrome.runtime.sendNativeMessage(
"com.jankesec.native_lab",
request.payload,
(response) => sendResponse(response),
);
return true;
});
Kod yalnızca routing label kontrol eder. Sender bileşeni, origin, frame, operasyon, nesne, boyut, state ve kullanıcı hareketi doğrulanmaz.
Sentetik native host ikinci hatayı görünür hale getirir:
from pathlib import Path
def handle(message):
if message.get("op") == "read_file":
return {"data": Path(message["path"]).read_text()}
return {"error": "unknown operation"}
Bu tam bir exploit değildir; minimal authorization oracle’dır. Positive test
/tmp/nmh-lab/canary.txt dosyasını ister. Negative test izin verilen root dışındaki farklı bir
canary’yi ister. Finding, yalnızca ikinci request host’a ulaştığında ve bağımsız file observer yanlış
nesnenin açıldığını kanıtladığında vardır.
Gerçek browser verileri, SSH anahtarları, password store veya kullanıcı belgeleriyle etki gösterilmemelidir. Zafiyet ödünç alınan dosyanın hassasiyeti değil, yetkisiz nesne geçişidir.
Etkileşimden önce envanter
Mesaj göndermeden önce extension ve native host kurulum durumunu statik kanıtlarla kaydedin.
Extension envanteri
| Alan | Kanıt |
|---|---|
| Extension ID ve kurulum kaynağı | Browser extension sayfası, enterprise policy, imzalı paket metadata |
| Manifest ve update | Manifest version, update URL ve managed-extension policy |
| İzinler | permissions, optional_permissions, host_permissions |
| Content-script erişimi | Match pattern, excluded match, frame ve execution world |
| Harici caller’lar | externally_connectable ID ve page pattern’leri |
| Message listener’lar | onMessage, onConnect ve external varyantları |
| Native operasyonlar | Tüm connectNative ve sendNativeMessage call site’ları |
| Sender kontrolleri | sender.id, URL origin, tab, frame, document, incognito state |
| State binding | Gesture, nonce, expiry, selected object, active tab, tenant |
Native host envanteri
| Alan | Kanıt |
|---|---|
| Manifest konumu ve sahibi | OS path veya registry key, ownership ve ACL |
| Host executable | Absolute ve resolved path, son dosya kimliği |
| Allowed extension ID’leri | allowed_origins veya allowed_extensions |
| Runtime kimliği | UID, token, integrity level, sandbox veya service account |
| Parser | Length handling, UTF-8, JSON type ve maximum size |
| Dispatcher | Operation allowlist ve default-deny davranışı |
| Object authorization | Resource ID’nin path, device veya account’a dönüşümü |
| Process creation | Shell kullanımı, argument array, environment ve working directory |
| Output | Veri sınıflandırması, truncation, error ve stderr logging |
| Update path | Installer identity, signature verification ve yazılabilir parent path |
Bu envanter false positive’leri de kapatır. Ayrıcalıklı görünen bir host yalnızca sabit durum sorgusu sunabilir. Buna karşılık user-level bir host browser profilleri, developer credential’ları, cloud CLI’ları ve local socket’lere erişerek yüksek etkili bir yol oluşturabilir.
Browser context’i request içinde koruyun
Service worker ilk güvenilir politika noktasıdır. Page-defined nesneyi forward etmek yerine browser’ın gözlemlediği gerçekleri küçük bir request contract’a dönüştürmelidir.
const ALLOWED_ORIGINS = new Set(["https://lab.invalid"]);
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
const origin = sender.url ? new URL(sender.url).origin : "";
const validSender =
sender.id === chrome.runtime.id &&
sender.tab?.id !== undefined &&
sender.frameId === 0 &&
ALLOWED_ORIGINS.has(origin);
if (!validSender || message?.type !== "read-canary") {
sendResponse({ ok: false, error: "denied" });
return false;
}
const request = {
version: 1,
operation: "canary.read",
resource: String(message.resource),
origin,
tabId: sender.tab.id,
documentId: sender.documentId,
nonce: crypto.randomUUID(),
expiresAt: Date.now() + 5_000,
};
chrome.runtime.sendNativeMessage(
"com.jankesec.native_lab",
request,
sendResponse,
);
return true;
});
Bu daha güçlüdür ancak tek başına yeterli değildir. Native host, alanları extension oluşturduğu için körlemesine güvenemez. Contract sabit schema, bounded value, kısa lifetime, replay tracking ve operation-to-resource authorization tablosuna ihtiyaç duyar.
Browser contract içinde kullanıcı kontrollü path taşımayın. canary-a gibi sabit resource ID
kullanın ve path’i host içinde çözümleyin:
RESOURCES = {
"canary-a": Path("/tmp/nmh-lab/canary-a.txt"),
"canary-b": Path("/tmp/nmh-lab/canary-b.txt"),
}
def handle(message):
if set(message) != {
"version", "operation", "resource", "origin",
"tabId", "documentId", "nonce", "expiresAt",
}:
return {"ok": False, "error": "schema"}
if message["version"] != 1 or message["operation"] != "canary.read":
return {"ok": False, "error": "operation"}
path = RESOURCES.get(message["resource"])
if path is None:
return {"ok": False, "error": "resource"}
return {"ok": True, "data": path.read_text()}
Yüksek etkili operasyonlarda origin yazan JSON alanı kanıt değildir. Browser-observed state ile
brokered session oluşturun; approval’ı çözümlenmiş operation ve object’e bağlayın; native
capability’yi host’un ambient OS identity’sinden daha dar tutun.
Transport’u hostile input olarak parse edin
Native host parser tam dört header byte okumalı, allocation öncesi oversized mesajı reddetmeli, ilan edilen body boyutunu eksiksiz okumalı, strict UTF-8 decode etmeli, JSON object zorunlu kılmalı, tipleri doğrulamalı ve tek framed response üretmelidir.
import json
import struct
import sys
MAX_REQUEST = 16 * 1024
def read_exact(stream, count):
chunks = bytearray()
while len(chunks) < count:
part = stream.read(count - len(chunks))
if not part:
raise EOFError("truncated native message")
chunks.extend(part)
return bytes(chunks)
def read_message():
header = read_exact(sys.stdin.buffer, 4)
length = struct.unpack("@I", header)[0]
if length == 0 or length > MAX_REQUEST:
raise ValueError("invalid message length")
payload = read_exact(sys.stdin.buffer, length)
value = json.loads(payload.decode("utf-8", errors="strict"))
if not isinstance(value, dict):
raise TypeError("message must be an object")
return value
Parser testleri zero/truncated/oversized header, hatalı body length, invalid UTF-8, extra field, yanlış tip, aşırı nested JSON, arka arkaya frame, mid-frame EOF ve stdout contamination senaryolarını içermelidir. Debug çıktısı stderr’e yazılmalıdır; stdout’a düşen tek bir debug satırı framing kanalını bozar.
Authorization her hop boyunca yaşamalıdır
Kanıt matrisi
Savunulabilir sonuç; observed fact, inference, positive execution, negative control ve belirsizliği birbirinden ayırır.
| İddia | Positive evidence | Negative control | Güçlü sonuç |
|---|---|---|---|
| Sayfa extension listener’a ulaşabiliyor | Tab, frame, document ve origin ile gözlenen mesaj | Eşleşmeyen origin’den aynı mesaj reddedilir | Reachability kaydedilen sender kümesiyle sınırlıdır |
| Extension host’u başlatabiliyor | Request ID ile korele host process creation | Listede olmayan extension ID access denial alır | Host manifest extension identity filtreler |
| Host operasyon sunuyor | Geçerli schema beklenen canary sonucunu döndürür | Unknown operation ve extra field reddedilir | Dispatcher explicit ve default-deny çalışır |
| Resource authorization çalışıyor | Allowed ID mapped canary’yi açar | Traversal, absolute path, symlink ve unknown ID başarısızdır | Caller test edilen varyantlarla keyfi nesne seçemez |
| Replay kontrol altında | Fresh nonce yalnızca bir kez başarılıdır | Aynı nonce ve expired request etki oluşturmaz | Request lifetime içinde single-use’dur |
| Native authority sınırlıdır | Runtime identity ve system-call trace beklenen object’e uyar | Korunan ve ilgisiz nesnelerin değişmediği doğrulanır | Etki test edilen capability ile sınırlıdır |
| Cleanup tamamdır | Manifest, extension, canary, process ve log kaldırılır | Cleanup sonrası reconnect başarısızdır | Laboratuvar bridge’i artık erişilebilir değildir |
Denied JSON response tek başına “etki oluşmadı” kanıtı değildir. Canary, filesystem event, process tree veya device state bağımsız doğrulanmalıdır.
Test matrisi
| Katman | Test | Güvenli sonuç |
|---|---|---|
| Web origin | Allowed ve unallowed origin’den aynı mesaj | Yalnızca exact allowed origin ilerler |
| Frame | Top frame, embedded frame ve stale frame | Uygun olmayan frame/document reddedilir |
| Extension sender | Content script, extension page, external extension, external page | Her sender sınıfı için ayrı explicit policy vardır |
| Schema | Missing, extra, wrong-type, nested ve oversized değer | Operation resolution öncesi reddedilir |
| Operation | Known read, unknown operation, deprecated version | Yalnızca güncel allowlisted operasyon vardır |
| Resource | Known ID, arbitrary path, traversal, symlink, race target | Açılan son object authorized set içinde kalır |
| Process | Argument array ve shell string | Attacker-controlled shell interpretation oluşmaz |
| Lifecycle | Navigation, reconnect, worker ve browser restart | Authorization bağlı olduğu context’ten uzun yaşamaz |
| Replay | Duplicate nonce ve expired message | İkinci etki yoktur |
| Installation | Writable manifest, parent ve redirected host path | ACL veya integrity control substitution’ı engeller |
| Output | Oversized response, stdout noise, malformed JSON | Protocol desync olmadan bounded failure |
| Cleanup | Extension veya host registration kaldırılır | Browser host’u tekrar başlatamaz |
Sertleştirme sırası
- Gereksiz Native Messaging erişimini kaldırın. Kurulu olmayan capability gelecekteki extension bug’ından çağrılamaz.
- Content script ve host erişimini daraltın. Match pattern, frame, host permission, external caller ve optional permission kapsamını azaltın.
- Browser-observed sender context’i doğrulayın. Page-provided origin veya tab claim kabul etmeyin.
- Versioned operation schema tanımlayın. Unknown key, yanlış tip, büyük boyut ve deprecated operation fail closed olmalıdır.
- Identifier’ları trusted component içinde çözün. Page-supplied path, command, URL ve device handle authority injection’dır.
- Policy ve execution’ı ayırın. Dispatcher küçük implementation’ı çağırmadan önce operation ve resource’u yetkilendirmelidir.
- Shell ve ambient administrator identity kullanmayın. Fixed executable, argument array, sanitized environment, controlled working directory ve least privilege kullanın.
- Yüksek etkili işlemleri state’e bağlayın. Gesture, selected object, nonce, expiry ve approval aynı request’i tanımlamalıdır.
- Etkiyi bağımsız doğrulayın. Beklenen object değişimini ve komşu object’lerin değişmediğini kaydedin.
- Installation path’i koruyun. Manifest, registry key, executable path, update package ve parent directory güvenlik sınırının parçasıdır.
Detection ve forensic sinyaller
- extension install source, ID, version, permission ve update değişiklikleri;
connectNative/sendNativeMessageoperation name ve request ID;- native host process parent, command line, origin argümanı, UID, hash ve signature;
- Native Messaging manifest veya registry değişiklikleri;
- host tarafından başlatılan child process’ler;
- host’a atfedilen file, socket, device, credential veya configuration etkileri;
- gate ve reason bazında denied operation;
- anormal message size, parse failure, reconnect rate ve replay detection;
- browser idle iken veya approved workflow yokken host execution.
Log’lar secret payload değil identifier ve decision kaydetmelidir. Native mesajlar token, file content, account name veya local path içerebilir. Evidence collection ikinci bir veri sızıntısı yolu oluşturmamalıdır.
Finding nasıl raporlanmalı?
Güçlü başlık kaybolan sınırı açıkça söyler:
Browser content script, extension attacker-controlled path’i forward ettiği ve host object authorization uygulamadığı için Native Messaging host’a approved resource set dışındaki bir nesneyi okutabiliyordu.
Rapor şunları içermelidir:
- extension ve native host version;
- installation ve runtime identity;
- erişilebilir sender context’leri;
- minimal request schema;
- approved ve unauthorized canary object’leri;
- positive effect evidence;
- negative control;
- gerçek hassas veri kullanmadan gösterilen maksimum etki;
- extension ve host tarafındaki root cause;
- string filter değil operation contract’ı değiştiren remediation;
- cleanup kanıtı.
Severity, nativeMessaging izninin varlığından değil reachable effect’ten gelir. Yalnızca status
döndüren host informational olabilir. Developer credential okuyabilen user-level host high impact
yaratabilir. Broad dispatcher üzerinden erişilen system service privilege boundary aşabilir.
Son model
Native Messaging, extension ve host tek bir dar, versioned authorization protocol gibi davrandığında güvenlidir:
browser-observed sender
→ allowed operation
→ trusted resource resolution
→ bounded native identity
→ independently verified effect
Browser sandbox değerlidir. Extension ID değerlidir. Host manifest değerlidir. Ancak bunların hiçbiri keyfi native etkiyi yetkilendirmez.
Bridge yalnızca orijinal web context native tarafın deliberate, testable ve observable karar vermesine yetecek kadar uzun yaşadığında güvenilirdir.
Bu teknik not ne kadar güncel?
En son kaynak incelemesi, içerik güncellemesi veya yayın tarihi gösterilmektedir.
Birincil kamu kayıtları kontrol edildi. Ortama özgü davranış, ayrıca yeniden üretilmedikçe iddianın dışındadır.
