Using LD_PRELOAD to cheat, inject features and investigate programs
rafalcieslak.wordpress.com
rafalcieslak.wordpress.com
“Dynamic Programming is mainly an optimization over plain recursion. Wherever we see a recursive solution that has repeated calls for same inputs, we can optimize it using Dynamic Programming. The idea is to simply store the results of subproblems, so that we do not have to re-compute them when needed later”
Most people aren't that fortunate. There are good reasons why the word "job" is not synonymous with "fun".
The wiki describes a bunch of ways around this, but they all seemed kinda finicky and annoying, so instead I LD_PRELOADed a shim that made getpwent(3) or whatever it used always return "wizard" as the user name.
[0] https://github.com/haad/proxychains/blob/master/src/proxycha...
[1] https://github.com/haad/proxychains/blob/master/src/libproxy...
- fakeroot: Gives the running program the impression that it is running as root, often used for example for building debian packages. This will let the build script create a directory tree which it believes is owned by root and then when that directory tree is packed by tar, tar will also see root as the owner and the .tar-archive will have root as the user/group for the files in the final debian package.
- faketime: Gives the running program the impression that it is running at some specific time. Usefull for testing code during specific events like leap-years, etc.
- eatmydata: Will ignore all fsync() and related system calls which ensures files are written to permanent storage. I have used this once for running a database during a testsuite, and databases are much faster when they do not have to wait for the data to reach permanent storage.
The implementation is completely bonkers. The tool sets some environmental variables, then spawns a child process with LD_PRELOAD set to load a library (libstdbuf.so) which has a some initialization code that runs when the library is loaded, and that based on the environmental variables, calls setvbuf() from inside the child process to override the buffering behavior.
You could also use LD_PRELOAD to get a different MAC address. This would've work with FlexLM.
Though its probably easier with a bit of hexediting or disassembling to modify the binary personally, I get fuzzy feelings of love from the type of software cracking which does not require modified binaries.
frida uses it to wrap and inject... anything.
These programs have all been around for quite a long time. I think libnaw has been around since early 2000s at least.
Technically, it uses LD_PRELOAD to hook the necessary libc functions. As, at least on x86-64, a syscall is just a CPU instruction like any other, you can't hook into it through LD_PRELOAD or any other tricks that don't involve the kernel (apart from rewriting the program before you execute it). That's also why it doesn't work on e.g. Go programs, as they don't use libc.
On most Unix systems, you can use ptrace to intercept system calls from another process. This is how tools like rr or strace work.
The gVisor docs list 3 ways: KVM, systrap, ptrace https://gvisor.dev/docs/architecture_guide/platforms/ :
> systrap: The systrap platform relies seccomp’s SECCOMP_RET_TRAP feature in order to intercept system calls. This makes the kernel send SIGSYS to the triggering thread, which hands over control to gVisor to handle the system call. For more details, please see the systrap README file.
> systrap replaced ptrace as the default gVisor platform in mid-2023. If you depend on ptrace, and systrap doesn’t fulfill your needs, please voice your feedback.
> ptrace: The ptrace platform uses PTRACE_SYSEMU to execute user code without allowing it to execute host system calls. This platform can run anywhere that ptrace works (even VMs without nested virtualization), which is ubiquitous.
> Unfortunately, the ptrace platform has high context switch overhead, so system call-heavy applications may pay a performance penalty. For this reason, systrap is almost always the better choice.
The Falco docs list 3 syscall event drivers: Kernel module, Classic eBPF probe, and Modern eBPF probe: https://falco.org/docs/event-sources/kernel/
Dynamic linker > Systems using ELF: https://en.wikipedia.org/wiki/Dynamic_linker#Systems_using_E...
1: https://github.com/gittup/tup/blob/master/src/ldpreload/ldpr...
Today, one would use Docker for this exact purpose, putting each test run into its own container (or even multiple containers).
My favorite nasty hack along these lines was to inject a new implementation of gethostname via LD_PRELOAD as the simplest path to prevent a CI server from surfacing a hostname in a place it shouldn't be.
But of course it's all splitting hairs. A sufficiently dedicated / motivated / funded person can investigate even the most hardened static position independent binary. Dynamic linking with LD_PRELOAD is like propping the front door open in comparison.
Which is unambiguously not the case, they merely slow it down by a small margin.
When you are using shared libaries, it is fairly trivial to hook into the library calls and replace them with whatever you want. When you are using static libraries, the linker and optimizer could for example inline the machine code directly in the application code. What tools do you have to do similar tricks with statically compiled binaries?
That pointless remark misses the whole point. Even though ideally an attack vector would be eliminated, it's already good enough if it becomes unexploitable by the vast majority of potential attackers. That's why there is a whole field called "app hardening" as in a sliding scale instead of "perfect app protection".
Consider a situation in which there is a new vulnerability in openssl. you can treat this as a hypothetical question or just.. remember any of your past experiences of any of the many openssl vulns.
How many binaries on your server use the vulnerable version? If all binaries are dynamically linked you can answer this fairly trivially with a shell script to enumerate binaries, pass them to ldd, and a little grepping. If all of your binaries are statically linked what do you do? Ideally pull the build info from your build server that shows you every version of everything that went into the binary.. which is data that just doesn't exist for most people
Maybe you scan the binaries to do some kind of signature analysis... but I would not be confident in the results not having false positives and false negatives.
Now let's patch it. How quickly can you recompile every static binary on your server? Can you even easily cut new builds of these existing versions but with a small patch increment or will your dev teams just rush a new release of any changes they're working on?
or with dynamically libraries, you update the library on your server and be done with it
... or so you thought. you didn't check what processed were running with the old library still open in memory and restart them so you're still vulnerable :)
I don't think it is. You start from an irrational and unsubstantiated belief that ignores any of the basic usecases of shared libraries.
> I believe dynamic used to make sense but no longer does in the vast majority of cases.
It's your personal belief, and one that's unsubstantiated and os based on ignorance.
> Static binaries have their costs, but are so much easier to reason about.
That assertion is completely irrelevant, as it fails to address any of the usecases for shared libraries. Being able to run code, and other dubious claims of simplicity, don't even qualify as questioning the purpose of shared libraries.
That's just your personal assertion, which is entirely baseless and unsubstantiated. It's ok to have beliefs, but instead of pushing them as truths you should at least start by doing some cursory research to see if they are even plausible. And yours isn't.
I'm very interested in your assertion that my impression is implausible though. What evidence do you have?
I had to deal with the same problem a few years ago, and used the exact same solution.
Funnily enough, I've seen much stronger obfuscation from reverse engineering from my cheap Tuya IoT devices app than from my bank app.
IMHO, if the client-side of an IoT service is obfuscated, I'd take that as a sign that they're trying to hide some really insecure API endpoints.
ptrace or SECCOMP_RET_TRAP can be used to do syscall interception. But that would be somewhat complex in comparison to the ease of use of LD_PRELOAD.
gVisor[1] is a project which intercepts every syscall with the method above and services the syscall by itself (no passthrough) for the purpose of sandboxing.
You can also use eBPF to audit and meddle with syscalls.
[1]https://maskray.me/blog/2021-05-16-elf-interposition-and-bsy...
Even their server libraries are obfuscated, and hooking open() turned out to be just easier than trying to patch the binaries themselves.
[0] https://medium.com/@prizmant/hacking-punkbuster-e22e6cf2f36e
[1] E.g. https://bugzilla.redhat.com/show_bug.cgi?id=1722181
We also forbid dlopen - so even if some third-party library commits such an offense, it will be blocked: https://github.com/ClickHouse/ClickHouse/blob/master/program...
/lib64/ld-linux-x86-64.so.2 --preload myevil.so ./your_program
there we go, bypassedThe creativity of people to break things by misusing them is unbound. Fix your bugs, fix non-bugs that seem like "foreseeable misuse". Don't fix "my users are in outer space" non-bugs.
Why the fuck do Azure and Google Collab mess with LD_PRELOAD. Why do Clickhouse crashes when it does? Does it rely on unspecified behavior or are the preload libraries problematic? Is the preloaded library buggy?
It looks like an arms race where stome piece of software forces use of a particular version of library instead of using what's in the system (but not static linking), then someone else use LD_PRELOAD to force back the use of the system library, and then other software ban LD_PRELOAD to counter the counter. I understand that some ugly things are sometimes needed to make software work, but think of the collateral damage.
This won't work for everything, and it probably does need SIP to be off (also make a backup!) but it might be a way to get something to work.
It doesn't actually block it from 3rd parties.
The examples are some authentication plugins.
one extra argument to the linker when they compile their plugin
LD_PRELOAD can also be used to turn an executable binary into a library. You have to intercept __libc_start_main(), provide your own custom main(), then call whatever functions from the binary your heart desires. You may need to use raw function addresses taken from some IDA or other Ghidra, as the binary is not required to export symbols.
https://web.archive.org/web/20161007013145/http://pewpewthes...
IIRC it has been further tightened since the above was written. DYLD_INTERPOSE is now a thing, DYLD_LIBRARY_PATH and DYLD_INSERT_LIBRARIES may be silently ignored and dropped from the env.
https://apple.stackexchange.com/questions/421306/exporting-d...
This assumes there’s some copy protection on it.
https://github.com/gordol/ld_preload-sounds
this started out as like 10-20 lines of terrible code originally, and a few people sent merge requests to improve it