Java SignedObject-gated Deserialization and Pre-auth Reachability via Error Paths
Reading time: 6 minutes
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
Bu sayfa, java.security.SignedObject etrafında kurulmuş yaygın bir "guarded" Java deserialization desenini ve görünüşte ulaşılamaz olan sink'lerin hata işleme akışları aracılığıyla nasıl pre-auth erişilebilir hale gelebileceğini belgeler. Teknik Fortra GoAnywhere MFT (CVE-2025-10035) içinde gözlemlendi fakat benzer tasarımlara da uygulanabilir.
Tehdit modeli
- Attacker bir HTTP endpoint'ine ulaşabilir; bu endpoint sonuçta serileştirilmiş bir SignedObject olması amaçlanan ve saldırgan tarafından sağlanan byte[]'i işler.
- Kod, dıştaki türü SignedObject (veya byte[]) ile sınırlamak için bir validating wrapper (ör. Apache Commons IO ValidatingObjectInputStream veya özel bir adapter) kullanır.
- SignedObject.getObject() tarafından döndürülen iç nesne, gadget chains'in tetiklenebileceği yerdir (ör. CommonsBeanutils1), ancak yalnızca bir signature verification gate'ten sonra.
Tipik vulnerable pattern
com.linoma.license.gen2.BundleWorker.verify'e dayanan basitleştirilmiş bir örnek:
private static byte[] verify(byte[] payload, KeyConfig keyCfg) throws Exception {
String sigAlg = "SHA1withDSA";
if ("2".equals(keyCfg.getVersion())) {
sigAlg = "SHA512withRSA"; // key version controls algorithm
}
PublicKey pub = getPublicKey(keyCfg);
Signature sig = Signature.getInstance(sigAlg);
// 1) Outer, "guarded" deserialization restricted to SignedObject
SignedObject so = (SignedObject) JavaSerializationUtilities.deserialize(
payload, SignedObject.class, new Class[]{ byte[].class });
if (keyCfg.isServer()) {
// Hardened server path
return ((SignedContainer) JavaSerializationUtilities.deserializeUntrustedSignedObject(
so, SignedContainer.class, new Class[]{ byte[].class }
)).getData();
} else {
// 2) Signature check using a baked-in public key
if (!so.verify(pub, sig)) {
throw new IOException("Unable to verify signature!");
}
// 3) Inner object deserialization (potential gadget execution)
SignedContainer inner = (SignedContainer) so.getObject();
return inner.getData();
}
}
Temel gözlemler:
- Doğrulayan deserializer (1) rastgele üst seviye gadget sınıflarını engeller; yalnızca SignedObject (veya raw byte[]) kabul edilir.
- RCE primitive, (3)'te SignedObject.getObject() ile gerçeğe dönen iç nesnede bulunur.
- Bir imza kontrolü (2) SignedObject'un ürünle ilişkilendirilmiş bir açık anahtar karşısında verify() olması gerektiğini zorunlu kılar. Saldırgan geçerli bir imza üretemezse, iç gadget asla deserialize edilmez.
Sömürüye ilişkin hususlar
Kod yürütmesi elde etmek için, saldırganın iç nesne olarak kötü amaçlı bir gadget zincirini saran doğru şekilde imzalanmış bir SignedObject teslim etmesi gerekir. Bu genellikle aşağıdakilerden birini gerektirir:
- Özel anahtarın ele geçirilmesi: ürünün lisans nesnelerini imzalamak/doğrulamak için kullandığı eşleşen özel anahtarı ele geçirmek.
- Signing oracle: satıcıyı veya güvenilir bir imzalama servisini, saldırgan kontrollü serileştirilmiş içeriği imzalamaya zorlamak (ör. bir lisans sunucusu istemci girdisinden gömülü rastgele bir nesneyi imzalıyorsa).
- Alternatif erişilebilir yol: iç nesneyi verify() uygulamadan deserialize eden veya belirli bir modda imza kontrollerini atlayan bir sunucu tarafı yol bulmak.
Bunlardan biri yoksa, imza doğrulaması deserialization sink'in varlığına rağmen sömürüyü engeller.
Hata işleme akışları yoluyla pre-auth erişilebilirlik
Bir deserialization uç noktası kimlik doğrulama veya oturum-bağlı bir token gerektiriyor gibi görünse bile, hata işleme kodu yanlışlıkla token'ı bir kimliksizin oturumuna üretebilir ve iliştirebilir.
Örnek erişilebilirlik zinciri (GoAnywhere MFT):
- Hedef servlet: /goanywhere/lic/accept/
oturum-bağlı bir lisans istek tokenı gerektirir. - Hata yolu: /goanywhere/license/Unlicensed.xhtml’e sonek gereksiz veri (trailing junk) ve geçersiz JSF durumu ile erişmek AdminErrorHandlerServlet'i tetikler; bu servlet şunları yapar:
- SessionUtilities.generateLicenseRequestToken(session)
- bundle=<...> içinde imzalı bir lisans isteği ile vendor lisans sunucusuna yönlendirir
- Bundle, GUID'i geri kazanmak için çevrimdışı çözülebilir (hard-coded keys). Aynı oturum çerezini koruyup, saldırgan kontrollü bundle baytları ile /goanywhere/lic/accept/
'e POST yaparak SignedObject sink'ine pre-auth olarak ulaşılır.
Erişilebilirlik kanıtı (etkisiz) probe:
GET /goanywhere/license/Unlicensed.xhtml/x?javax.faces.ViewState=x&GARequestAction=activate HTTP/1.1
Host: <target>
- Güncellenmemiş: 302 Location header to https://my.goanywhere.com/lic/request?bundle=... and Set-Cookie: ASESSIONID=...
- Yama uygulanmış: redirect without bundle (no token generation).
Blue-team tespiti
stack traces/logs'taki göstergeler SignedObject-gated sink'e ulaşma girişimlerini kuvvetle işaret ediyor:
java.io.ObjectInputStream.readObject
java.security.SignedObject.getObject
com.linoma.license.gen2.BundleWorker.verify
com.linoma.license.gen2.BundleWorker.unbundle
com.linoma.license.gen2.LicenseController.getResponse
com.linoma.license.gen2.LicenseAPI.getResponse
com.linoma.ga.ui.admin.servlet.LicenseResponseServlet.doPost
Sertleştirme rehberi
- Her getObject() çağrısından önce imza doğrulamasını sürdürün ve doğrulamanın amaçlanan açık anahtar/algoritma ile yapıldığından emin olun.
- Doğrudan SignedObject.getObject() çağrılarını, iç stream üzerinde filtrelemeyi yeniden uygulayan sertleştirilmiş bir wrapper ile değiştirin (örn. deserializeUntrustedSignedObject using ValidatingObjectInputStream/ObjectInputFilter izin listeleri).
- Yetkilendirilmemiş kullanıcılar için oturum-bağlı token veren hata işleyici akışlarını kaldırın. Hata yollarını saldırı yüzeyi olarak değerlendirin.
- Hem dış hem iç deserializasyonlar için katı izin listelerine sahip Java serialization filters (JEP 290) tercih edin. Örnek:
ObjectInputFilter filter = info -> {
Class<?> c = info.serialClass();
if (c == null) return ObjectInputFilter.Status.UNDECIDED;
if (c == java.security.SignedObject.class || c == byte[].class) return ObjectInputFilter.Status.ALLOWED;
return ObjectInputFilter.Status.REJECTED; // outer layer
};
ObjectInputFilter.Config.setSerialFilter(filter);
// For the inner object, apply a separate strict DTO allow-list
Örnek saldırı zinciri özeti (CVE-2025-10035)
- Pre-auth token minting via error handler:
GET /goanywhere/license/Unlicensed.xhtml/watchTowr?javax.faces.ViewState=watchTowr&GARequestAction=activate
bundle=... ve ASESSIONID=... içeren 302 al; bundle'ı çevrimdışında deşifre ederek GUID'i kurtar.
- Aynı cookie ile pre-auth aşamasında sink'e ulaş:
POST /goanywhere/lic/accept/<GUID> HTTP/1.1
Cookie: ASESSIONID=<value>
Content-Type: application/x-www-form-urlencoded
bundle=<attacker-controlled-bytes>
- RCE için, gadget chain'i saran doğru şekilde imzalanmış bir SignedObject gerekir. Araştırmacılar imza doğrulamasını atlayamadı; istismar, eşleşen bir private key'e veya bir signing oracle'a erişime bağlı.
Düzeltme sürümleri ve davranış değişiklikleri
- GoAnywhere MFT 7.8.4 and Sustain Release 7.6.3:
- İç deserialization'ı, SignedObject.getObject() yerine bir wrapper (deserializeUntrustedSignedObject) kullanarak sertleştirildi.
- Hata-handler token üretimi kaldırıldı; pre-auth erişilebilirliği kapatıldı.
JSF/ViewState hakkında notlar
The reachability trick, bir JSF sayfası (.xhtml) ve geçersiz javax.faces.ViewState kullanarak ayrıcalıklı bir error handler'a yönlendirir. Bu bir JSF deserialization sorunu olmasa da, tekrarlayan bir pre-auth paterni: ayrıcalıklı işlemler yapan ve güvenlikle ilgili session özniteliklerini ayarlayan error handler'lara sızmak.
References
- watchTowr Labs – Is This Bad? This Feels Bad — GoAnywhere CVE-2025-10035
- Fortra advisory FI-2025-012 – Deserialization Vulnerability in GoAnywhere MFT's License Servlet
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.