8 ms·
ldd arbitrary code execution (2009)
- KolmogorovComp 3y agoArticle is dated 2009. Previous discussions: - [2009] https://news.ycombinator.com/item?id=902958 https://news.ycombinator.com/item?id=902958 - [2015] https://news.ycombinator.com/item?id=9629667 https://news.ycombinator.com/item?id=9629667 - [2022] https://news.ycombinator.com/item?id=30033807 https://news.ycombinator.com/item?id=30033807 Also formatting inside code-block is broken
- a1o 3y agoIs the mentioned issue in the article still true today?
- eqvinox 3y agoYes. So is the (IMHO) best workaround of using lddtree (pax-utils) instead. Available on pretty much all distros, though probably not installed by default.
- anthk 3y agoobjdump -x /path/to/bin | grep NEEDED
- eqvinox 3y agoDoesn't tell you if/where the library is found on your system.
- janAkali 3y agofor i in $(objdump -p /path/to/bin | grep NEEDED | awk '{print $2}'); do ldconfig -p | awk -v var="$i" '$1==var{print $0}'; done edit: even simpler, for i in $(objdump -p /path/to/bin | grep NEEDED | awk '{print $2}'); do ldconfig -p | grep $i; done
- eqvinox 3y agoCool, but I think I'll stick to lddtree ;) (… it also recurses down on the dependencies, presumably you can extend your shell script to do that too but at some point why not just use the existing tools?)
- mid-kid 3y agoNot in glibc at least[1], and I reckon many other implementations don't have this problem either. [1]: https://manpages.debian.org/bookworm/manpages/ldd.1.en.html#Security https://manpages.debian.org/bookworm/manpages/ldd.1.en.html#...
- thriftwy 3y agoI believe I saw a simple shell script based on nm or binutils which could do a safe ldd on any executable, including executables for different platforms. Update: Here seems to be one example: https://gist.github.com/jerome-pouiller/c403786c1394f53f44a3b61214489e6f https://gist.github.com/jerome-pouiller/c403786c1394f53f44a3...
- nwellnhof 3y agoThis seems to be based on readelf.
- yakubin 3y agoIt's readelf, but it will only list libs which are declared in the ELF of the executable. It won't list libraries loaded at startup with dlopen(3), unlike ldd. I'm also not sure, if this script will recurse, but I suspect not. ldd will print libraries loaded recursively.
- Joker_vD 3y ago> It won't list libraries loaded at startup with dlopen(3) Well, that'd require actually executing the startup code.
- 1vuio0pswjnm7 3y agoNone of the previous discussions on HN mentioned using readelf instead of ldd. Whenever I want to quickly check for dynamically linked libraries in a program, I use readelf -d program The use case I have is that I want to know what are the dependencies the program needs from the host system. If I wanted to know what are the dependencies of a particular shared library I can use readelf -d library
- wyldfire 3y agoThat's great, but unlike ldd, readelf won't walk the list of dependencies recursively.
- deleted 3y ago[deleted]
- mycall 3y agonever fear, recursive and concat unix piping to the rescue
- stingraycharles 3y agoAt that point, why not just use ldd?
- saagarjha 3y agoBecause it executes the binary
- jagrsw 3y agoIf you're worried (like really worried) about a binary, then it can easily fool readelf (btw, it can fool ldd too), so eg the data from the ELF header is never used. Probably running `strace` or `perf record -e intel_pt` in a confined environ is the best option to see what a binary really does, short of using ida/disasm/gdb - but then a binary can discover the use of strace too, and behave differently :). It's turtles all the way down.
- evgpbfhnr 3y ago(the various implementations of) lddtree also don't have this "feature" as they all use readelf or similar (EDIT: and unlike readelf -d, will recurse through dependencies like ldd does) I considered that rather well known though...
- djbusby 3y agoI just learned today! Thanks.
- greatfilter251 3y ago[dead]
- throwawaaarrgh 3y agoI get that it's important that we call out exploitable situations like this, but it's also kind of moot. There are so many ways to exploit modern systems you can just rustle around in a bag of exploits and one will work. Known executable, unknown, doesn't matter. Not even using a VM will keep you safe. Not even RBAC. What's easier and more reliable is to work in terms of risks. It's fine if you get exploited, as long as the access you have to sensitive systems is limited, and those sensitive systems have backups, and you can't delete those backups, you can re-deploy systems from scratch using automation if you get compromised, automatically rotate credentials, etc. Lower the overall risk by setting everything up so the worst case scenario isn't that bad. Then you don't have to worry so much about an "unknown executable" because even if it gets exploited the attacker can't cause too much damage.
- causality0 3y agoThe number one method of security, at least for power users, is user behavior. Look at URLs you might visit critically. Don't run random-ass code. Keep an eye out for being taken advantage of.
- nextaccountic 3y ago> Not even using a VM will keep you safe. Do you browse the Internet? Ever visited an unknown website? Whatever the browser uses to run JS and wasm code on the web ought to be enough.
- yjftsjthsd-h 3y agoBrowsers do in fact get exploited; I would have called that an example of it not being a solved problem.
- nextaccountic 3y agoThey get exploited (and then the exploit is patched), but we're still using a browser to talk about this, right?
- dwheeler 3y agoThis not new info, but it's good to tell others. My "Program Librarues HOWTO" says this: "Beware: do not run ldd on a program you don't trust. As is clearly stated in the ldd(1) manual, ldd works by (in certain cases) by setting a special environment variable (for ELF objects, LD_TRACE_LOADED_OBJECTS) and then executing the program. It may be possible for an untrusted program to force the ldd user to run arbitrary code (instead of simply showing the ldd information). So, for safety's sake, don't use ldd on programs you don't trust to execute." https://dwheeler.com/program-library/Program-Library-HOWTO/x36.html https://dwheeler.com/program-library/Program-Library-HOWTO/x... I believe that doc dates from 2000. This info wasn't new then either, it was specifically documented in its man page.
- gpvos 3y agoIt's not general knowledge among people who only sometimes drop down to root (such as me). Although it is mentioned in an early paragraph of the man page, it's easy to miss. It could use an attention-drawing "IMPORTANT SECURITY NOTICE" there.
- mananaysiempre 3y agoCan’t resist mentioned the funny, but perhaps less than adequately prominent note to “beware of gift horses” on the Plan 9 manual page for bundle(1) [pack files into a self-extracting shell script, more or less equivalent to shar(1) on a normal Unix]. https://9p.io/magic/man2html/1/bundle https://9p.io/magic/man2html/1/bundle
- IshKebab 3y agoThat's the wrong way to solve this. You have to assume people don't read the manual, especially for security issues. The correct solution is that `ldd` should be safe by default, and require a `--allow-unsafe-execution` flag if it needs to actually execute code. I think this is pretty well known these days but probably not well known enough!
- gpvos 3y agoToo backwards incompatible maybe, although you could add a check on whether the output is a tty, like ls does; I don't know how often ldd is used in scripts.
- Joker_vD 3y agoMaybe combining the linker and the loader into a single program is a bad idea. I guess they kinda do similar job, if you look from the kernel point of view...
- raggi 3y agobesides being slower, splitting them into two would mean they’d need to agree on abi and evolution. Having them ship in the same binary skips over those concerns
- Joker_vD 3y agoOkay, I've just checked, and apparently the ld and ld.so are two completely different executables: the loader, ld.so, is 236K large and statically linked while ld is 1.7M large and dynamically linked. And now that I think of it, I believe that alternative linkers such as gold, or mold, or lld, don't come with alternative loaders so... they have to manually follow the GNU's ld.so ABI and evolution?
- LegionMammal978 3y agoThe terminology is a bit confusing: the ordinary linker used at compile time is gold, or mold, or lld, that outputs an ELF executable. Then, the dynamic linker, or dynamic loader, is provided by the libc and usually called ld-whatever.so. It takes the loaded ELF program as input when you start it, and processes relocations and whatnot before yielding control to the program's entry point. The interface between the compile-time linker and the dynamic linker is mostly defined by the ELF file format, System V ABI, and processor-specific ABI: many of the details can be found in the elf(5) man page. So in principle, there isn't really a bespoke "ld.so ABI". However, in practice, this can break down a bit on the consumer's side; there was a ruckus a while back when glibc started only including the undocumented DT_GNU_HASH and not the documented DT_HASH for its shared libraries, under the premise that only libc should be concerned with interpreting ELF headers. At least from the producer's side, the story is better, since dynamic linkers will generally at least accept the standard headers, and ignore any nonstandard headers they don't understand.
- userbinator 3y agoOne particular case when a program won't be handled by ld-linux.so is when it has a different loader than the system's default specified in it's .interp ELF section. That's the whole idea in executing arbitrary code with ldd -- load the executable via a different loader that does not handle LD_TRACE_LOADED_OBJECTS environment variable but instead executes the program. The way dynamic linking is done on UNIX-like systems has always seemed odd and a bit of an afterthought to me, and that explains why --- the actual loader in the OS kernel itself knows nearly nothing about dynamic linking. Instead it "redirects" execution to a loader (which is itself not that different from any other static binary) to do that.
- throwawaylinux 3y agoWell dynamic linking and most things in unix is an afterthought by definition because the first unix system did not have them. That doesn't seem to be what you mean though in context, but I'm not sure what you do mean. You don't like it or don't think it was designed well because dynamic linking is done in userspace?
- userbinator 3y agoThe fact that an executable needs to explicitly specify the "interpreter", instead of that being something under control of the OS loader, doesn't seem like a good way of doing it; and then the implementation of ldd discussed here just takes it in an even weirder direction.
- dap 3y agoWhat distinction are you making? Do you think it should be part of the kernel? I ask because ld.so (the runtime linker/loader) is part of the OS on many systems.
- userbinator 3y agoI mean not explicitly hardcoding the path to the interpreter inside every binary. If the dynamic linker is part of the OS, then surely the OS should already know where it is?
- eqvinox 3y agorecommended workaround: use lddtree (part of pax-utils) The output is actually more informative too: $ lddtree `which ssh` /usr/bin/ssh (interpreter => /lib64/ld-linux-x86-64.so.2) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 libgssapi_krb5.so.2 => /lib/x86_64-linux-gnu/libgssapi_krb5.so.2 libkrb5.so.3 => /lib/x86_64-linux-gnu/libkrb5.so.3 libkeyutils.so.1 => /lib/x86_64-linux-gnu/libkeyutils.so.1 libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 libk5crypto.so.3 => /lib/x86_64-linux-gnu/libk5crypto.so.3 libcom_err.so.2 => /lib/x86_64-linux-gnu/libcom_err.so.2 libkrb5support.so.0 => /lib/x86_64-linux-gnu/libkrb5support.so.0 libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 vs. $ ldd `which ssh` linux-vdso.so.1 (0x00007fff7972e000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f1a0030c000) libgssapi_krb5.so.2 => /lib/x86_64-linux-gnu/libgssapi_krb5.so.2 (0x00007f1a002ba000) libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f19ffe00000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f1a0029b000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f19ffc1e000) libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f19ffb84000) /lib64/ld-linux-x86-64.so.2 (0x00007f1a00499000) libkrb5.so.3 => /lib/x86_64-linux-gnu/libkrb5.so.3 (0x00007f19ffaaa000) libk5crypto.so.3 => /lib/x86_64-linux-gnu/libk5crypto.so.3 (0x00007f19ffa7d000) libcom_err.so.2 => /lib/x86_64-linux-gnu/libcom_err.so.2 (0x00007f1a00293000) libkrb5support.so.0 => /lib/x86_64-linux-gnu/libkrb5support.so.0 (0x00007f1a00285000) libkeyutils.so.1 => /lib/x86_64-linux-gnu/libkeyutils.so.1 (0x00007f19ffa76000) libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f19ffa65000) (lddtree opens the files as plain binaries and reads the ELF linking data, it does not execute any code from the files being inspected.)
- School-Cotton 3y agoThat’s incredible. Is there any reason not to fix ldd to just fail if the program is using a different loader?
- raggi 3y agoDifferent than what loader?
- School-Cotton 3y agoThe GNU one.
- GuB-42 3y agoBecause you may want ldd to work when the program is using a different loader. Not all nonstandard loaders are malicious. If you want to avoid this risk, use something else, like readelf or lddtree.
- eek2121 3y agoThis attitude is why this issue exists in the first place.
- planede 3y agoldd's implementation relies on the assumption that the loader respects the LD_TRACE_LOADED_OBJECTS environment variable. Do all non-malicious loaders need to respect this environment variable and implement GNU ld.so's behavior?
- xelxebar 3y agoIf you take a look at the the ldd shell script, current versions no longer run the raw executable, always executing like the author's 3rd command example.
- Etrnl_President 3y agoNever run IDD without also running QD, which enables a state that prevents damage or death, aka. "God Mode".
- Dwedit 3y agoWouldn't have thought of 'ldd', would have gone straight to objdump.
- rapi 3y ago[flagged]
- rapi 3y agohttps://news.ycombinator.com/item?id=36840748 https://news.ycombinator.com/item?id=36840748
- deleted 3y ago[deleted]
- jameshart 3y agoAm I the only one who read this and thought 'wow, if I can get LD_TRACE_LOADED_OBJECTS to be set I can block anything from being executed'? That seems like a scary powerful environment variable.
- sp1rit 3y agoNot really. Realistically you can set PATH to an empty string, and block execution everything that isn't invoked using a hardcoded path.
- jameshart 3y agoYeah, I thought about that, but this would kill even directly invoked executables, right?
- sp1rit 3y agoYes, this is certainly more "far-reaching" than other env vars. But If you have the ability to wreak havoc even if ld-linux wouldn't check for this (obv. PATH being the prime example, but unsetting HOME will almost certainly break some software written by novices (not falling back to the home specified in /etc/passwd)). And I also could imagine that tools that allow setting arbitrary env vars might filter common troublemakers like PATH, but almost certainly not niche env vars that change the bahviour of ld-linux.
- immibis 3y agoYou can also set LD_PRELOAD to the path of a shared library whose constructor segfaults, or set LD_LIBRARY_PATH to the path of a directory containing a libc that doesn't work.
- niggerrape1488 3y ago[dead]
- stabbles 3y agoAnother fun `ldd` fact: some executables can be run, even when ldd says its libraries cannot be located. This is thanks to $ORIGIN rpaths. 1. Create an executable in `./bin/exe` with say `$ORIGIN/../lib` rpath, and link it to some library `./lib/libfoo.so` 2. Create a symlink one dir up: `ln -s ./bin/exe exe` 3. `ldd exe` will tell you `libfoo.so` is not found 4. `./exe` runs just fine. This is because the kernel resolves the symlink path, and the dynamic loader receives that, and uses it for $ORIGIN. `ldd` on the other hand will interpolate $ORIGIN with the symlink location, not its target. This only "works" for the executable, not for dependencies of dependencies: if a library is located as a symlink in a different directory from its target, $ORIGIN is relative to the symlink, and it's likely to break. This can be problematic if you wanna put symlinks in say `/usr/local/lib`.