macOS Library Injection
Reading time: 11 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)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.
caution
dyld kodu açık kaynaklıdır ve https://opensource.apple.com/source/dyld/ adresinden bulunabilir ve şu URL gibi bir tar ile indirilebilir: https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz
Dyld Süreci
Dyld'in ikili dosyalar içinde kütüphaneleri nasıl yüklediğine bakın:
DYLD_INSERT_LIBRARIES
Bu, Linux'taki LD_PRELOAD gibidir. Bir sürecin çalıştırılacağını belirtmek için belirli bir kütüphaneyi bir yoldan yüklemesine izin verir (eğer ortam değişkeni etkinse).
Bu teknik ayrıca ASEP tekniği olarak da kullanılabilir çünkü her kurulu uygulamanın "Info.plist" adında bir plist'i vardır ve bu, LSEnvironmental
adında bir anahtar kullanarak çevresel değişkenlerin atanmasına izin verir.
note
2012'den beri Apple, DYLD_INSERT_LIBRARIES
gücünü önemli ölçüde azaltmıştır.
Koda gidin ve src/dyld.cpp
dosyasını kontrol edin. pruneEnvironmentVariables
fonksiyonunda DYLD_*
değişkenlerinin kaldırıldığını görebilirsiniz.
processRestricted
fonksiyonunda kısıtlamanın nedeni belirlenir. O kodu kontrol ederek nedenlerin şunlar olduğunu görebilirsiniz:
- İkili dosya
setuid/setgid
- Macho ikili dosyasında
__RESTRICT/__restrict
bölümünün varlığı. - Yazılımın
com.apple.security.cs.allow-dyld-environment-variables
yetkisi olmadan yetkilendirmeleri (hardened runtime) vardır.- Bir ikilinin yetkilendirmelerini kontrol etmek için:
codesign -dv --entitlements :- </path/to/bin>
- Bir ikilinin yetkilendirmelerini kontrol etmek için:
Daha güncel sürümlerde bu mantığı configureProcessRestrictions
fonksiyonunun ikinci kısmında bulabilirsiniz. Ancak, daha yeni sürümlerde yürütülen şey, fonksiyonun başlangıç kontrolleridir (iOS veya simülasyonla ilgili if'leri kaldırabilirsiniz çünkü bunlar macOS'ta kullanılmayacaktır).
Kütüphane Doğrulaması
İkili dosya DYLD_INSERT_LIBRARIES
ortam değişkenini kullanmaya izin verse bile, eğer ikili dosya yüklenecek kütüphanenin imzasını kontrol ediyorsa, özel bir kütüphaneyi yüklemeyecektir.
Özel bir kütüphaneyi yüklemek için, ikili dosyanın aşağıdaki yetkilendirmelerden birine sahip olması gerekir:
com.apple.security.cs.disable-library-validation
com.apple.private.security.clear-library-validation
ya da ikili dosya hardened runtime bayrağına veya kütüphane doğrulama bayrağına sahip olmamalıdır.
Bir ikilinin hardened runtime olup olmadığını codesign --display --verbose <bin>
ile kontrol edebilirsiniz; CodeDirectory
içinde runtime bayrağını kontrol ederek: CodeDirectory v=20500 size=767 flags=0x10000(runtime) hashes=13+7 location=embedded
Ayrıca, bir kütüphaneyi ikili dosyayla aynı sertifika ile imzalanmışsa yükleyebilirsiniz.
Bunu (kötüye) kullanma ve kısıtlamaları kontrol etme örneğini bulabilirsiniz:
macOS Dyld Hijacking & DYLD_INSERT_LIBRARIES
Dylib Ele Geçirme
caution
Önceki Kütüphane Doğrulama kısıtlamalarının Dylib ele geçirme saldırılarını gerçekleştirmek için de geçerli olduğunu unutmayın.
Windows'ta olduğu gibi, MacOS'ta da dylib'leri ele geçirebilirsiniz ve uygulamaların keyfi kod çalıştırmasını sağlayabilirsiniz (aslında, normal bir kullanıcıdan bu mümkün olmayabilir çünkü bir .app
paketinin içine yazmak ve bir kütüphaneyi ele geçirmek için bir TCC iznine ihtiyacınız olabilir).
Ancak, MacOS uygulamalarının kütüphaneleri yükleme şekli Windows'tan daha kısıtlıdır. Bu, kötü amaçlı yazılım geliştiricilerinin bu tekniği gizlilik için kullanabileceği anlamına gelir, ancak yetkileri artırmak için kötüye kullanma olasılığı çok daha düşüktür.
Öncelikle, MacOS ikililerinin kütüphaneleri yüklemek için tam yolu belirtmesi daha yaygındır. İkincisi, MacOS asla kütüphaneler için $PATH klasörlerinde arama yapmaz.
Bu işlevselliğe ilişkin kodun ana kısmı ImageLoader::recursiveLoadLibraries
içinde ImageLoader.cpp
dosyasındadır.
Bir macho ikili dosyasının kütüphaneleri yüklemek için kullanabileceği 4 farklı başlık Komutu vardır:
LC_LOAD_DYLIB
komutu, bir dylib yüklemek için yaygın komuttur.LC_LOAD_WEAK_DYLIB
komutu, önceki gibi çalışır, ancak dylib bulunamazsa, yürütme hatasız devam eder.LC_REEXPORT_DYLIB
komutu, farklı bir kütüphaneden sembolleri proxy'ler (veya yeniden ihraç eder).LC_LOAD_UPWARD_DYLIB
komutu, iki kütüphanenin birbirine bağımlı olduğu durumlarda kullanılır (bu, yukarı bağımlılık olarak adlandırılır).
Ancak, 2 tür dylib ele geçirme vardır:
- Eksik zayıf bağlantılı kütüphaneler: Bu, uygulamanın LC_LOAD_WEAK_DYLIB ile yapılandırılmış bir kütüphaneyi yüklemeye çalışacağı anlamına gelir. Sonra, bir saldırgan bir dylib'i beklenen yere yerleştirirse, yüklenir.
- Bağlantının "zayıf" olması, kütüphane bulunmasa bile uygulamanın çalışmaya devam edeceği anlamına gelir.
- Bununla ilgili kod,
ImageLoaderMachO::doGetDependentLibraries
fonksiyonundadır; buradalib->required
yalnızcaLC_LOAD_WEAK_DYLIB
doğru olduğundafalse
olur. - Zayıf bağlantılı kütüphaneleri ikililerde bulmak için (daha sonra ele geçirme kütüphaneleri oluşturma örneğiniz var):
-
otool -l </path/to/bin> | grep LC_LOAD_WEAK_DYLIB -A 5 cmd LC_LOAD_WEAK_DYLIB cmdsize 56 name /var/tmp/lib/libUtl.1.dylib (offset 24) time stamp 2 Wed Jun 21 12:23:31 1969 current version 1.0.0 compatibility version 1.0.0
- **@rpath ile yapılandırılmış**: Mach-O ikili dosyaları **`LC_RPATH`** ve **`LC_LOAD_DYLIB`** komutlarına sahip olabilir. Bu komutların **değerlerine** dayanarak, **kütüphaneler** **farklı dizinlerden** **yüklenir**.
- **`LC_RPATH`**, ikili dosya tarafından kütüphaneleri yüklemek için kullanılan bazı klasörlerin yollarını içerir.
- **`LC_LOAD_DYLIB`**, yüklenmesi gereken belirli kütüphanelerin yolunu içerir. Bu yollar **`@rpath`** içerebilir; bu, **`LC_RPATH`** içindeki değerlerle **değiştirilecektir**. Eğer **`LC_RPATH`** içinde birden fazla yol varsa, her biri yüklenmesi gereken kütüphaneyi aramak için kullanılacaktır. Örnek:
- Eğer **`LC_LOAD_DYLIB`** `@rpath/library.dylib` içeriyorsa ve **`LC_RPATH`** `/application/app.app/Contents/Framework/v1/` ve `/application/app.app/Contents/Framework/v2/` içeriyorsa. Her iki klasör de `library.dylib` yüklemek için kullanılacaktır. Eğer kütüphane `[...]/v1/` içinde yoksa, bir saldırgan oraya yerleştirerek `[...]/v2/` içindeki kütüphanenin yüklenmesini ele geçirebilir çünkü **`LC_LOAD_DYLIB`** içindeki yolların sırası takip edilir.
- **Rpath yollarını ve kütüphaneleri** ikililerde bulmak için: `otool -l </path/to/binary> | grep -E "LC_RPATH|LC_LOAD_DYLIB" -A 5`
> [!NOTE] > **`@executable_path`**: **Ana yürütülebilir dosyanın** bulunduğu dizinin **yoludur**.
>
> **`@loader_path`**: **Yükleme komutunu içeren** **Mach-O ikili dosyasının** bulunduğu **dizinin yoludur**.
>
> - Bir yürütülebilir dosyada kullanıldığında, **`@loader_path`** etkili bir şekilde **`@executable_path`** ile **aynıdır**.
> - Bir **dylib** içinde kullanıldığında, **`@loader_path`** **dylib'in** **yolunu** verir.
Bu işlevselliği kötüye kullanarak **yetkileri artırmanın** yolu, **root** tarafından **çalıştırılan** bir **uygulamanın**, saldırganın yazma izinlerine sahip olduğu bir klasörde bazı **kütüphaneleri araması** durumunda nadir bir durum olacaktır.
<div class="mdbook-alerts mdbook-alerts-tip">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
tip
</p>
Uygulamalardaki **eksik kütüphaneleri** bulmak için güzel bir **tarayıcı** [**Dylib Hijack Scanner**](https://objective-see.com/products/dhs.html) veya bir [**CLI versiyonu**](https://github.com/pandazheng/DylibHijack) vardır.\
Bu teknikle ilgili **teknik detaylar** içeren güzel bir **rapor** [**burada**](https://www.virusbulletin.com/virusbulletin/2015/03/dylib-hijacking-os-x) bulunabilir.
</div>
**Örnek**
<a class="content_ref" href="macos-dyld-hijacking-and-dyld_insert_libraries.md"><span class="content_ref_label">macOS Dyld Hijacking & DYLD_INSERT_LIBRARIES</span></a>
## Dlopen Ele Geçirme
<div class="mdbook-alerts mdbook-alerts-caution">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
caution
</p>
**Önceki Kütüphane Doğrulama kısıtlamalarının** Dlopen ele geçirme saldırılarını gerçekleştirmek için de geçerli olduğunu unutmayın.
</div>
**`man dlopen`**'dan:
- Yol **bir eğik çizgi karakteri** içermiyorsa (yani sadece bir yaprak adıysa), **dlopen() arama yapacaktır**. Eğer **`$DYLD_LIBRARY_PATH`** başlatıldığında ayarlandıysa, dyld önce **o dizinde** **bakacaktır**. Sonra, eğer çağrılan mach-o dosyası veya ana yürütülebilir dosya bir **`LC_RPATH`** belirtiyorsa, dyld **o dizinlerde** **bakacaktır**. Sonra, eğer süreç **kısıtlı değilse**, dyld **geçerli çalışma dizininde** arama yapacaktır. Son olarak, eski ikililer için, dyld bazı yedeklemeleri deneyecektir. Eğer **`$DYLD_FALLBACK_LIBRARY_PATH`** başlatıldığında ayarlandıysa, dyld **o dizinlerde** arama yapacaktır, aksi takdirde dyld **`/usr/local/lib/`** (eğer süreç kısıtlı değilse) ve sonra **`/usr/lib/`** içinde arama yapacaktır (bu bilgi **`man dlopen`**'dan alınmıştır).
1. `$DYLD_LIBRARY_PATH`
2. `LC_RPATH`
3. `CWD`(eğer kısıtlı değilse)
4. `$DYLD_FALLBACK_LIBRARY_PATH`
5. `/usr/local/lib/` (eğer kısıtlı değilse)
6. `/usr/lib/`
<div class="mdbook-alerts mdbook-alerts-caution">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
caution
</p>
Eğer isimde eğik çizgi yoksa, ele geçirme yapmak için 2 yol olacaktır:
- Eğer herhangi bir **`LC_RPATH`** **yazılabilir** ise (ancak imza kontrol edilir, bu nedenle bunun için ikilinin de kısıtlı olmaması gerekir)
- Eğer ikili dosya **kısıtlı değilse** ve o zaman CWD'den (veya belirtilen ortam değişkenlerinden birini kötüye kullanarak) bir şey yüklemek mümkündür.
</div>
- Yol **bir çerçeve** yolu gibi görünüyorsa (örneğin, `/stuff/foo.framework/foo`), eğer **`$DYLD_FRAMEWORK_PATH`** başlatıldığında ayarlandıysa, dyld önce **çerçeve kısmi yolu** için o dizinde bakacaktır (örneğin, `foo.framework/foo`). Sonra, dyld **sağlanan yolu olduğu gibi** deneyecektir (göreceli yollar için geçerli çalışma dizinini kullanarak). Son olarak, eski ikililer için, dyld bazı yedeklemeleri deneyecektir. Eğer **`$DYLD_FALLBACK_FRAMEWORK_PATH`** başlatıldığında ayarlandıysa, dyld o dizinlerde arama yapacaktır. Aksi takdirde, **`/Library/Frameworks`** (macOS'ta süreç kısıtlı değilse) ve sonra **`/System/Library/Frameworks`** içinde arama yapacaktır.
1. `$DYLD_FRAMEWORK_PATH`
2. sağlanan yol (eğer kısıtlı değilse göreceli yollar için geçerli çalışma dizinini kullanarak)
3. `$DYLD_FALLBACK_FRAMEWORK_PATH`
4. `/Library/Frameworks` (eğer kısıtlı değilse)
5. `/System/Library/Frameworks`
<div class="mdbook-alerts mdbook-alerts-caution">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
caution
</p>
Eğer bir çerçeve yolu ise, ele geçirmenin yolu:
- Eğer süreç **kısıtlı değilse**, belirtilen ortam değişkenlerinden **CWD'den göreceli yolu** kötüye kullanarak (belirtilen belgelerde söylenmese de, eğer süreç kısıtlıysa DYLD\_\* ortam değişkenleri kaldırılır)
</div>
- Yol **bir eğik çizgi içeriyorsa ama bir çerçeve yolu değilse** (yani bir tam yol veya bir dylib'e giden kısmi yol), dlopen() önce (eğer ayarlandıysa) **`$DYLD_LIBRARY_PATH`** içinde (yolun yaprak kısmıyla) bakar. Sonra, dyld **sağlanan yolu dener** (eğer kısıtlı değilse göreceli yollar için geçerli çalışma dizinini kullanarak). Son olarak, eski ikililer için, dyld yedeklemeleri deneyecektir. Eğer **`$DYLD_FALLBACK_LIBRARY_PATH`** başlatıldığında ayarlandıysa, dyld o dizinlerde arama yapacaktır, aksi takdirde dyld **`/usr/local/lib/`** (eğer süreç kısıtlı değilse) ve sonra **`/usr/lib/`** içinde arama yapacaktır.
1. `$DYLD_LIBRARY_PATH`
2. sağlanan yol (eğer kısıtlı değilse göreceli yollar için geçerli çalışma dizinini kullanarak)
3. `$DYLD_FALLBACK_LIBRARY_PATH`
4. `/usr/local/lib/` (eğer kısıtlı değilse)
5. `/usr/lib/`
<div class="mdbook-alerts mdbook-alerts-caution">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
caution
</p>
Eğer isimde eğik çizgiler varsa ve çerçeve değilse, ele geçirmenin yolu:
- Eğer ikili dosya **kısıtlı değilse** ve o zaman CWD'den veya `/usr/local/lib`'den bir şey yüklemek mümkündür (veya belirtilen ortam değişkenlerinden birini kötüye kullanarak)
</div>
<div class="mdbook-alerts mdbook-alerts-note">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
note
</p>
Not: **dlopen aramasını kontrol etmek için** **hiçbir** yapılandırma dosyası yoktur.
Not: Eğer ana yürütülebilir dosya bir **set\[ug]id ikili dosyası veya yetkilendirmelerle kod imzalanmışsa**, o zaman **tüm ortam değişkenleri yok sayılır** ve yalnızca tam yol kullanılabilir ([daha fazla bilgi için DYLD_INSERT_LIBRARIES kısıtlamalarını kontrol edin](macos-dyld-hijacking-and-dyld_insert_libraries.md#check-dyld_insert_librery-restrictions)).
Not: Apple platformları, 32-bit ve 64-bit kütüphaneleri birleştirmek için "evrensel" dosyalar kullanır. Bu, **ayrı 32-bit ve 64-bit arama yolları** olmadığı anlamına gelir.
Not: Apple platformlarında çoğu OS dylib **dyld önbelleğine** **birleştirilmiştir** ve disk üzerinde mevcut değildir. Bu nedenle, bir OS dylib'in var olup olmadığını önceden kontrol etmek için **`stat()`** çağrısı **çalışmaz**. Ancak, **`dlopen_preflight()`**, uyumlu bir mach-o dosyasını bulmak için **`dlopen()`** ile aynı adımları kullanır.
</div>
**Yolları Kontrol Et**
Aşağıdaki kod ile tüm seçenekleri kontrol edelim:
// gcc dlopentest.c -o dlopentest -Wl,-rpath,/tmp/test #include <dlfcn.h> #include <stdio.h>
int main(void) { void* handle;
fprintf("--- No slash ---\n"); handle = dlopen("just_name_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
fprintf("--- Relative framework ---\n"); handle = dlopen("a/framework/rel_framework_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
fprintf("--- Abs framework ---\n"); handle = dlopen("/a/abs/framework/abs_framework_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
fprintf("--- Relative Path ---\n"); handle = dlopen("a/folder/rel_folder_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
fprintf("--- Abs Path ---\n"); handle = dlopen("/a/abs/folder/abs_folder_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
return 0; }
Eğer bunu derleyip çalıştırırsanız, **her bir kütüphanenin nerede başarısız bir şekilde arandığını** görebilirsiniz. Ayrıca, **FS günlüklerini filtreleyebilirsiniz**:
sudo fs_usage | grep "dlopentest"
## Göreli Yol Hijacking
Eğer bir **ayrılmış ikili/uygulama** (örneğin bir SUID veya güçlü yetkilere sahip bir ikili) **göreli yol** kütüphanesi yüklüyorsa (örneğin `@executable_path` veya `@loader_path` kullanarak) ve **Kütüphane Doğrulaması devre dışıysa**, saldırganın **göreli yol yüklü kütüphaneyi** değiştirebileceği ve sürece kod enjekte etmek için bunu kötüye kullanabileceği bir durum oluşabilir.
## `DYLD_*` ve `LD_LIBRARY_PATH` çevre değişkenlerini temizle
`dyld-dyld-832.7.1/src/dyld2.cpp` dosyasında **`pruneEnvironmentVariables`** fonksiyonunu bulmak mümkündür; bu fonksiyon **`DYLD_`** ile başlayan ve **`LD_LIBRARY_PATH=`** olan herhangi bir çevre değişkenini kaldıracaktır.
Ayrıca, **suid** ve **sgid** ikilileri için **`DYLD_FALLBACK_FRAMEWORK_PATH`** ve **`DYLD_FALLBACK_LIBRARY_PATH`** çevre değişkenlerini **null** olarak ayarlayacaktır.
Bu fonksiyon, OSX hedef alındığında aynı dosyanın **`_main`** fonksiyonundan çağrılır:
#if TARGET_OS_OSX if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) { pruneEnvironmentVariables(envp, &apple);
ve bu boolean bayrakları kodda aynı dosyada ayarlanır:
#if TARGET_OS_OSX // support chrooting from old kernel bool isRestricted = false; bool libraryValidation = false; // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) { isRestricted = true; } bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0); uint32_t flags; if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { // On OS X CS_RESTRICT means the program was signed with entitlements if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && usingSIP ) { isRestricted = true; } // Library Validation loosens searching but requires everything to be code signed if ( flags & CS_REQUIRE_LV ) { isRestricted = false; libraryValidation = true; } } gLinkContext.allowAtPaths = !isRestricted; gLinkContext.allowEnvVarsPrint = !isRestricted; gLinkContext.allowEnvVarsPath = !isRestricted; gLinkContext.allowEnvVarsSharedCache = !libraryValidation || !usingSIP; gLinkContext.allowClassicFallbackPaths = !isRestricted; gLinkContext.allowInsertFailures = false; gLinkContext.allowInterposing = true;
Bu, temelde eğer ikili dosya **suid** veya **sgid** ise, ya da başlıklarda bir **RESTRICT** segmenti varsa veya **CS_RESTRICT** bayrağı ile imzalanmışsa, o zaman **`!gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache`** doğru olur ve çevre değişkenleri temizlenir.
Eğer CS_REQUIRE_LV doğruysa, o zaman değişkenler temizlenmeyecek ancak kütüphane doğrulaması, bunların orijinal ikili dosya ile aynı sertifikayı kullandığını kontrol edecektir.
## Kısıtlamaları Kontrol Et
### SUID & SGID
Make it owned by root and suid
sudo chown root hello sudo chmod +s hello
Insert the library
DYLD_INSERT_LIBRARIES=inject.dylib ./hello
Remove suid
sudo chmod -s hello
### Section `__RESTRICT` with segment `__restrict`
gcc -sectcreate __RESTRICT __restrict /dev/null hello.c -o hello-restrict DYLD_INSERT_LIBRARIES=inject.dylib ./hello-restrict
### Hardened runtime
Keychain'de yeni bir sertifika oluşturun ve bunu ikili dosyayı imzalamak için kullanın:
Apply runtime proetction
codesign -s
Apply library validation
codesign -f -s
Sign it
If the signature is from an unverified developer the injection will still work
If it's from a verified developer, it won't
codesign -f -s
Apply CS_RESTRICT protection
codesign -f -s