10 ms·
Using LD_PRELOAD to cheat, inject features and investigate programs (2013)
- pentestercrab 9y agoThere is a nice list of useful utilities called preeny that can be downloaded here[1]. 1. https://github.com/zardus/preeny https://github.com/zardus/preeny
- zbentley 9y agoIs this possible when trying to intercept and/or otherwise mess with system calls or stdlib function calls in a language that statically links all/most of its libraries? Are Go binaries affected? Statically-linked Rust binaries? If not, this is (depending on how you look at it) either a security point in favor of those languages or a hindrance to patching/debuggability.
- AnIdiotOnTheNet 9y agoIf your ELF is static, there is no ELF interpreter (ld.so), so LD_PRELOAD means nothing.
- jwilk 9y agoIf the program was linked statically to libfoo, you won't be able to preempt any libfoo symbols using LD_PRELOAD. There are other interception techniques that work against any binaries, even fully static ones: http://man7.org/linux/man-pages/man2/ptrace.2.html http://man7.org/linux/man-pages/man2/ptrace.2.html http://man7.org/linux/man-pages/man2/seccomp.2.html http://man7.org/linux/man-pages/man2/seccomp.2.html
- IncRnd 9y agoThis fun local hack is related to binary planting vulnerabilities, where a dynamic resource can be planted on-disk prior to program execution for later loading due to being in the load path.
- joshumax 9y agoInterestingly, I had an article featured om HN related to how easy this is to exploit: https://news.ycombinator.com/item?id=14518356 https://news.ycombinator.com/item?id=14518356
- IncRnd 9y agoI just read through that. Nice debugging. Binary planting, incidentally, was one of those persistent bugs in all Windows versions, since the local directory in Windows generally is in the beginning of the path. That variant is called DLL planting or a DLL loading vulnerability. That single design decision to execute from the local folder has caused untold havoc, including half a dozen APIs for how to handle DLL loading.
- kuschku 9y agoAnd this is especially bad when your browser drops all downloaded .DLLs and .EXEs in the same Downloads folder
- IncRnd 9y agoor when your email program downloads files you go to a network share and click java loads the configuration from the current directory or, or, or You're absolutely right :(
- AnIdiotOnTheNet 9y agoI don't see what the big deal is. That behavior allowed a ton of flexibility at the cost of DLL planting problems iff someone can already drop arbitrary files in the application's directory.
- IncRnd 9y ago> I don't see what the big deal is. Let's say that a Windows user wants to use sandboxie to isolate web components when browsing, until Aug of this year. You're Pwned: https://nvd.nist.gov/vuln/detail/CVE-2017-12480 https://nvd.nist.gov/vuln/detail/CVE-2017-12480
- joshumax 9y agoI actually had to use this trick several months ago in order to install Nessus on my Debian box. The installer was written in C and it called uname() to check if I was running a "supported kernel version" despite not actuality installing any kernel module to the system. It simply refused to install until my fake uname() returned the 3.10.x series kernel in the utsname structure...
- angrygoat 9y agoI did exactly the same thing in the early 2000s to get an ancient Java application working under Linux 2.4 -- it was calling some syscall via libc and getting a slightly different result than it had under Linux 2.2, and dying for no useful reason. I'm pretty sure that LD_PRELOAD hack ran in prod for years :-) Very handy trick.
- katastic 9y agoI used the same trick to modify how X11 server sends keyboard messages. There's a boolean flag that denotes whether or not a key event is "real" or generated artificially. For some !@$!@'d up reason, 99% of apps actually discard all synthetic keyboard events. So doing something as innocent as mapping joystick to keyboard commands fails. It also took HOURS to find out why my keyboard generating test code wouldn't work until I piped it to an Ubuntu postit note program and hazzah... it worked with no code changes. Every other app was just blocking the events.
- johncolanduoni 9y agoIt sounds like this wasn't the case in this instance, but applications could depend on kernel features without loading any kernel modules. The fact that they required 3.10 is interesting because my first example would be user namespaces, which were completed around that point (I think they were mostly done by 3.8, but IIRC there were some significant features that weren't complete until 3.9-3.10). Edit - Here's some details about Chromium's dependency on these features: https://chromium.googlesource.com/chromium/src/+/lkcr/docs/linux_sandboxing.md https://chromium.googlesource.com/chromium/src/+/lkcr/docs/l...
- 9y ago
- tfmatt 9y agoIs there a similar Java centric type override? Would it be as simple as dropping a jar in the right location or is there an environment variable that can be over written? Also it would be neat to know the java code to chain the "open" system call like he did.
- jmalicki 9y agoYou can override the ClassLoader (and use other reflection tricks after that) to do almost anything, see e.g. http://blog.cyberborean.org/2007/07/04/custom-classloaders-the-black-art-of-java http://blog.cyberborean.org/2007/07/04/custom-classloaders-t...
- nitwit005 9y agoProbably not with an environment variable, but you can always replace the java executable with another executable that adds an option like -Xbootclasspath.
- half-kh-hacker 9y agoYou can write a Java agent to overwrite the classfiles as they're loaded. At startup, you can use the `-javaagent:<path/to/jar>` option, and you can also bind an agent to other JVMs, programmatically, at runtime with a bit of hackery.
- self_awareness 9y agoFull sandboxing is not possible with LD_PRELOAD, because the application can still issue raw system calls (syscalls) by using the 'int 0x80' instruction, skipping any library call.
- webkike 9y agoIn order to wrap system calls, the syscall ptrace can be used: https://en.m.wikipedia.org/wiki/Ptrace https://en.m.wikipedia.org/wiki/Ptrace
- Graziano_M 9y agoThe proper way to do this is to use seccomp.
- microcolonel 9y agoHow exactly does one use seccomp to alter/intercept syscalls instead of simply blocking or allowing them? I can't find anything which uses it like that with a search.
- jmgao 9y agoGP is misinformed. seccomp is an optimization to ptrace-based syscall interception, when you're only interested in intercepting a subset of a process's syscalls. Instead of getting an event and processing every single syscall a process makes, you can register a seccomp filter that fires a ptrace event in only specific cases (which can be super coarse like "the syscall number is open", or more fine grained like "the syscall number is open and the file is being opened with O_WRONLY or O_RDWR"). Without something like this, ptrace based interception would be almost unusably slow for many cases.
- Hello71 9y agoalthough unfortunately it can't do "the syscall number is open and the path is five characters long".
- sandGorgon 9y agothe state of art here is Microsoft's Detours (https://www.microsoft.com/en-us/research/project/detours/ https://www.microsoft.com/en-us/research/project/detours/) . I think it is commercially used by security software, printer interceptors, etc
- pmalynin 9y agoCan confirm; Used it while working on Azure Security Forensics team, however sometimes we also used EasyHook for C#
- banthar 9y agoI've used both LD_PRELOAD and Detours and I like LD_PRELOAD so much better. LD_PRELOAD just needs single standard shared library and one environment variable. With Detours you have to: inject code into executable, stop threads, worry about races, protecting/unprotecting memory. The way it's implemented also feels like a big hack. The hooks are injected into first few instructions of hooked methods. Some system functions even deliberately leave blank space there. I'm yet to encounter a use case where all this complexity is useful.
- nwellnhof 9y agoThat's because ELF allows symbol interposition and PE (Portable Executable) doesn't have a comparable feature. Symbol interposition doesn't come without a price, though, and is often criticized: https://www.macieira.org/blog/2012/01/sorry-state-of-dynamic-libraries-on-linux/ https://www.macieira.org/blog/2012/01/sorry-state-of-dynamic... | https://www.airs.com/blog/archives/307 https://www.airs.com/blog/archives/307 | https://news.ycombinator.com/item?id=8029564 https://news.ycombinator.com/item?id=8029564
- sandGorgon 9y ago>The ELF dynamic linking mechanism is designed to emulate static linking. That's like designing cars to neigh and occasionally kick people to death with robot legs that exist only for this purpose. That comment wins thread.
- sebcat 9y agoLD_PRELOAD is a pretty decent tool to have in your toolbox when debugging, and you can't fire up lldb/gdb. I've used it in the past to get an overview of what gets passed to SSL_write, SSL_read: https://github.com/sebcat/openssl-hook https://github.com/sebcat/openssl-hook
- craigds 9y agoGDAL has a vsipreload.so which you can preload to replace all the libc file handling functions with ones that understand special filesystem paths, like /vsizip/path/to/zipfile.zip/myfile.csv We used it for years to do remote inspection of zip files without downloading the whole thing. Now there's a proper API for virtual filesystems in GDAL and we don't have to preload any more.
- Slartie 9y agoWe used (and actually still use) this to override hostname lookup in an automated test scenario, where we want to run multiple test suites of the same software (consisting of several processes which all communicate with each other via networking) on the same build server in parallel. We ended up just configuring everything to bind/connect to "localhost", so it runs fine on developer machines, but when executed on the build server - which is a lot more beefier than a dev laptop - the exact same configuration can be run 20 times, executing different test suites in parallel. The trick is that there's one independent loopback network interface for each run on that server, and by overriding the hostname lookup of the software via a small library provided in LD_PRELOAD, we replace the default lookup to 127.0.0.1 with these other loopback devices' IPs. You could of course achieve the same thing (and even more, like filesystem isolation) in a way more elegant and better-understandable way via Docker containers, but we started doing this in 2011 - there was no Docker back then, so we built our own ultra-low-overhead network virtualization. It'll probably be replaced by Docker someday, but for the moment it's still being used as it "just works" and requires practically no maintenance after being set up once.
- jamespo 9y agoCouldn't you just bind to different addresses under 127.0.0.1/8 ? eg 127.0.0.1, 127.0.0.2 etc
- Slartie 9y agoNot if there isn't an interface. If I create one, sure, but then I would have to do that on all dev computers as well and I'd have to forward the address somehow into all server and client configs and/or command-lines of tools started as part of the build process. Just overriding "localhost" spares me from both (the address to resolve is provided as an env variable, just like LD_PRELOAD itself - and env variables are inherited by child processes, which is great in my scenario).
- jamespo 9y agoBut the loopback interface is up & available as standard? Only advantage I can see is if localhost is hardcoded everywhere.
- Xophmeister 9y agoColleagues of mine use LD_PRELOAD for roll-you-own profiling. They supply their own `malloc`, for example, that does the same as regular-`malloc` but also writes to a log file that can be analysed later.
- sebcat 9y agoMassif is a good tool for memory profiling: http://valgrind.org/docs/manual/ms-manual.html http://valgrind.org/docs/manual/ms-manual.html
- convery 9y agoThis may be a good time to ask since I've been recommended to use LD_PRELOAD but it's always caused issues. Imagine this, a 'Bootstrap.so' is injected into a process via LD_PRELOAD. This bootstrapper places a jump on the programs entrypoint that jumps to a bootstrap::callback function. This function wants to load other shared libraries though. So when it starts loading another library the bootstrapper gets unloaded and reloaded, which ofcourse breaks execution. Would anyone know how to properly deal with this?
- saurik 9y agoWhy is the bootstrap.so being unloaded? It should be loaded into the process pretty permanently. If you have something calling dlopen/dlclose on it, maybe give it an extra dlopen.
- convery 9y agoThat's the issue, it shouldn't be unloaded. Yet when bootstrap.so calls dlopen(other.so); bootstrap.so gets unloaded and reloaded. I'll try adding a dlopen(bootstap.so) in the callback to increase the refcount though.
- Erwin 9y agoI experienced something similar in my data-driven permission restriction DSO. Turns out I was calling my initialization function multiple times because the system calls I overrode were called before my attribute((constructor)) function. I'd recommend running your program under gdb with a breakpoint in your function and checking out what the stack is like. Maybe your function is getting called from an unexpected place. In my case I was overriding fopen64 but glibc wanted to open some NSS related files before my init function was called.
- steinuil 9y agoIf I'm not mistaken putting DLLs in the same directory as the executable lets you do the same on Windows. Mods and cracks for videogames usually plant a fake DirectX dll in the game folder.
- tomyws 9y agoAbsolutely correct. Although this was intentional behaviour, it was given a security advisory[1]. In practice it allowed all sorts of exploits, including dumping packed executables for cracking, as you say. [1]: https://docs.microsoft.com/en-us/security-updates/SecurityAdvisories/2010/2269637 https://docs.microsoft.com/en-us/security-updates/SecurityAd...
- iancarroll 9y agoThough to be clear, this is like one of a billion ways to inject code into processes on Windows.
- AnIdiotOnTheNet 9y agoYeah, but UNIX developers love namespace conflicts and hardcoded paths, so something as simple as "look for libs in the directory your binary is in" either never occurred to them or was rejected under various arbitrary concerns.
- DavidBuchanan 9y agoI wouldn't categorise security as an "arbitrary concern".
- AnIdiotOnTheNet 9y agoIn order to exploit this you need to be able to write to the directory where the binary is. "Security" is trotted out as an arbitrary concern quite often because a lot of security people don't have any concept of risk analysis or cost/benefit. If it was up to them no one would ever do anything because that way they can't make a mistake.
- jwilk 9y agoInteresting projects that employ LD_PRELOAD: https://github.com/stewartsmith/libeatmydata https://github.com/stewartsmith/libeatmydata https://github.com/wolfcw/libfaketime https://github.com/wolfcw/libfaketime https://cwrap.org/ https://cwrap.org/ https://github.com/dex4er/fakechroot https://github.com/dex4er/fakechroot https://blitiri.com.ar/p/libfiu/ https://blitiri.com.ar/p/libfiu/ https://github.com/paultag/tmperamental https://github.com/paultag/tmperamental
- eigengrau 9y agoAddendum: https://github.com/sickill/stderred https://github.com/sickill/stderred - colorizing wrapper for stderr
- Fnoord 9y agolibfaketime can be used for nefarious things such as using software past its trial stage (same with using serials attached to MAC addresses + mac spoofing) or returning future TOTP data. Its generally useful for QA as well. It could've been used by Apple to avoid the reset bug in iOS 11.1.2 fakechroot is used when you build your own *.deb file. libeatmydata can be used in environments where you'd destroy the data afterwards anyway, or where you don't need logging or keep state after a reboot. You can also mount a bunch of filesystems such as /tmp and /var/log as tmpfs though.
- rrix2 9y agolibfaketime is also still crucial for reproduceable builds with a lot of compiers embedding compilation date, too, right?
- jwilk 9y agoAt least Debian folks didn't have to resort to using libfaketime. Instead, they patch the embedding tools to use timestamp from environment: https://reproducible-builds.org/specs/source-date-epoch/ https://reproducible-builds.org/specs/source-date-epoch/
- jwilk 9y ago
- timtosi 9y agoAs C language teacher assistants in Uni, we used LD_PRELOAD during project defences to replace the lib standard malloc by one of our own in order to be sure that students allocated & freed memory correclty. Never saw so much Segmentation faults in such a short time window.
- wyldfire 9y agoElectric Fence is one such library. [1] [1] https://elinux.org/Electric_Fence https://elinux.org/Electric_Fence
- d33 9y agoHow about libdislocator? https://github.com/mirrorer/afl/tree/master/libdislocator https://github.com/mirrorer/afl/tree/master/libdislocator
- jwilk 9y agoIf you can afford rebuilding from source, using AddressSanitizer (perhaps also UndefinedBehaviorSanitizer) should be better at uncovering memory safety bugs than any LD_PRELOAD trick.
- photonios 9y agoI wrote up a little hack a while ago to work around a limitation in gcov: https://github.com/Photonios/fgcov https://github.com/Photonios/fgcov It also uses `LD_PRELOAD` to overwrite a function in gcov and hash a filename to prevent exceeding a limit. Even more interesting, it comes with a stand-alone binary. The shared library is copied into the binary and when the binary runs, it extracts the shared library and runs the real gcov and injects the shared library using `LD_PRELOAD`. I had fun :)
- oleavr 9y agoAnother approach is using LD_PRELOAD with Frida to be able to write the instrumentation logic in JavaScript: https://www.frida.re/docs/gadget/ https://www.frida.re/docs/gadget/ This also supports monitoring the .js file for changes and reloading the instrumentation logic live, which is great for game tweaks. Just save the file and instantly see the results.
- nivviv 9y agohttps://nwnx.io/ https://nwnx.io/ A project that injects via LD_PRELOAD into a game called Neverwinter Nights (now Enhanced Edition). It lets content authors/admins add things like database support, more scripting languages, extensive ruleset changes, and whatever else you want to the game.
- BearOso 9y agoI've used LD_PRELOAD to thunk calls to glXSwapBuffers, adding a call to glFinish right afterwards. This keeps the OpenGL driver from getting too far ahead in rendering, since both the buffer swap and command queue are asynchronous, and reduces input lag somewhat.
- jusssi 9y agoAny chance you have this on Github? For some reason my card/driver combo creates a huge output lag when it can't keep up with with framerate cap. I'd like to try if something like this would help with it. On topic of things that use LD_PRELOAD, there's this fps capper (not my work): https://github.com/torkel104/libstrangle https://github.com/torkel104/libstrangle
- przemoc 9y ago~8 years ago I was using LD_PRELOAD to be able to (s)fdisk fixed-size VDI image (VirtualBox Disk Image) outside of virtual machine. https://gist.github.com/przemoc/571086 https://gist.github.com/przemoc/571086 Don't know if it still builds and works flawlessly.
- Shorel 9y agoThis is both an argument in favour of static linking, and an argument against static linking. Rust and go programmers will claim this is an attack they are not vulnerable to it, and other people will say this is an useful feature and static linking prevents its use.