43 ms·
Don't run 'strings' on untrusted files
- guns 12y agoCrap, so if `objdump` is likely vulnerable to overflows, and `ldd` is a simple bash script ripe for abuse¹, is there a safe and easy way to determine dynamic library dependencies in an executable? ¹ http://www.catonmat.net/blog/ldd-arbitrary-code-execution/ http://www.catonmat.net/blog/ldd-arbitrary-code-execution/
- JoshTriplett 12y agoFor an executable you don't trust? Probably running objdump as an unprivileged user inside a VM, and working on future hardening of libbfd.
- jacquesm 12y agoYou can run ldd safely as a normal user.
- guns 12y agoldd actually executes the program being probed; it is not a static analyzer. It's only safe if you trust the program in the first place. From my /usr/bin/ldd: try_trace() ( output=$(eval $add_env '"$@"' 2>&1; rc=$?; printf 'x'; exit $rc) rc=$? printf '%s' "${output%x}" return $rc ) … # If the program exits with exit code 5, it means the process has been # invoked with __libc_enable_secure. Fall back to running it through # the dynamic linker. try_trace "$file"
- vezzy-fnord 12y agoIt depends, but in general, ldd(1) should be considered unsafe. See under Security: http://man7.org/linux/man-pages/man1/ldd.1.html http://man7.org/linux/man-pages/man1/ldd.1.html This is an insightful bug report: https://bugzilla.redhat.com/show_bug.cgi?id=531160 https://bugzilla.redhat.com/show_bug.cgi?id=531160
- lgeek 12y ago> ldd actually executes the program being probed; It's insecure, but it's actually a bit more nuanced. What I'm describing applies to up-to-date glibc, it might have been even more insecure in the past. ldd first calls the dynamic linker with --verify, i.e.: /lib/ld-linux.so.2 --verify $file This is the system's dynamic linker, which should be considered safe. The --verify parameter tells the dynamic linker to check if the file is a valid dynamically linked file. Assuming that the file exists and it is an ELF file, the dynamic linker exits with status 1 if the file doesn't have a DYNAMIC segment, with 2 if the file doesn't have a INTERP segment or with 0 if it has both (the typical case). Execution is never passed to the file with this flag. Depending on the exit status of the loader, ldd does different things: * If it was 0, it calls try_trace "$file", meaning that the file is invoked directly. However, remember that for the status to be 0, the file must have an INTERP segment, meaning that the kernel will call the interpreter, typically the dynamic linker, i.e. /lib/ld-linux.so.2. A well behaved linker doesn't pass control to the file if LD_TRACE_LOADED_OBJECTS is set. This is the insecure case. The interpreter can be set to any other application (it must be statically linked or it must dynamically link itself). However, if the attacker doesn't control the file system, there typically aren't any files which match this condition and either pass control to the application or do anything harmful when invoked with no arguments. The alternative is to set the interpreter field to be the file itself. The problem with that is the full path to the ELF file must be known by the attacker (or it must be known that the victim will call ldd from the directory in which the file resides). /proc/self/exe will not work because at the time the kernel reads the entry, it will still point to the executable of the calling process, usually bash in this case. * If the exit status was 1, ldd quits (because it's an invalid file). * If the exit status was 2 (no INTERP segment), the dynamic loader is invoked directly, i.e. try_trace "$RTLD" "$file" These two latter cases are safe. A bug which can significantly simplify the attack would be a mismatch between the dynamic loader and the kernel, where the dynamic linker thinks that the ELF file has an INTERP filed and returns 0 for --verify and then the kernel doesn't find the INTERP field and passes control directly to the application. I've poked a bit around and as far as I can tell, there's no such bug in the latest kernel and glibc releases. In addition, the kernel aborts the loading process for any errors related to loading the interpreter. This 30C3 talk[0] discusses potential security issues arising because of mismatches between the various ELF parsers. [0] http://www.youtube.com/watch?v=1-tUo6RUzBU http://www.youtube.com/watch?v=1-tUo6RUzBU
- cesarb 12y agoThere's also readelf: $ readelf -d /bin/ls [...] Tag Type Name/Value 0x0000000000000001 (NEEDED) Shared library: [libselinux.so.1] 0x0000000000000001 (NEEDED) Shared library: [libcap.so.2] 0x0000000000000001 (NEEDED) Shared library: [libacl.so.1] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] [...] I don't know how careful readelf is with its input validation.
- fabulist 12y agoThe article mentions readelf as using the same buggy library :(
- kayamon 12y agoreadelf specifically doesn't use BFD - one of it's majors reasons for existence is to validate libbfd.
- fabulist 12y agoHmm, my mistake. You're completely correct. From the readelf man page: This program performs a similar function to objdump but it goes into more detail and it exists independently of the BFD library, so if there is a bug in BFD then readelf will not be affected.
- bjackman 12y agoI'm probably going to sound painfully naive now... but why is that a security risk? So libbfd reads past the end of a buffer and segfaults.. so what? It's not writing or executing anything untoward, so who cares?
- JoshTriplett 12y agoSegfaults frequently indicate exploitable security holes, and in particular, the fuzz test shown in the article shows a segfault at a user-controlled address (0x41414141 is AAAA).
- deleted 12y ago[deleted]
- pumpkinross 12y agoHaving any segfault is historically a strong indicator that there are worse things you have yet to discover.
- jrockway 12y agoSegfault: interesting. Segfault at 0x41414141 when your input is "AAAA": alarming.
- jsaxton86 12y agoConsider the case of a buffer overrun, a vulnerability that can often cause segfaults. A clever attacker could write a bunch of nasty code to that buffer, then use the buffer overrun to insert a jump statement back to that malicious code. Maybe they added the equivalent "rm -rf $HOME`" or something. Or maybe they decided to dial home with a reverse shell on your system. You have no idea, since you have no control over what code is being executed.
- panzi 12y agoHow will they do this if it's a read buffer overflow, not a writer overflow?
- avian 12y agoI'm a regular user of this utility and this came as a complete surprise to me. So much so that I checked the source myself before believing the article. I thought "strings" was just a dumb scan over the file. Does this mean that with a properly crafted binary it is also possible to hide strings from a quick check with "strings"?
- gwern 12y agoBased on the man page, I'm going to guess yes: -a --all - Do not scan only the initialized and loaded sections of object files; scan the whole files. Both the name and description imply that by default it won't scan the whole thing.
- eli 12y agoYes, though a properly crafted binary has always been able to hide from strings with even a minimal amount of obfuscation or encryption. There's no way to know all the strings a program can output without running it.
- avian 12y agoOf course, a properly crafted program can always arbitrarily obfuscate strings. But if you manage to trick libbfd into thinking it's looking at a particular format, you can hide plain text from a simple "strings" invocation even in files that are not executable. I've been using "strings" on all kinds of files, not only executables, and assumed that it will always display all sequences of printable characters present in the file.
- eli 12y agoAh, I see. Fair enough, but I don't think it was ever a great assumption that "strings" would uncover all the text in a file. There are so many ways to screw with a file at the byte level that could confuse "strings" but still appear fine when read by an application.
- revelation 12y ago
- _delirium 12y ago> the Linux version of strings is an integral part of GNU binutils... I think almost everyone ships that version of strings and objdump, fwiw. FreeBSD and NetBSD ship an almost verbatim GNU binutils; OpenBSD's seems to have more local changes (partly b/c it's based on an older binutils they've diverged from), but its 'strings' still uses libbfd. The only exception I ran across in some quick digging is that Illumos ships the Solaris version of 'strings', and doesn't ship an 'objdump'. This 'strings' seems to have come via Sun via Microsoft via AT&T via UC Berkeley: https://github.com/illumos/illumos-gate/blob/master/usr/src/cmd/strings/strings.c https://github.com/illumos/illumos-gate/blob/master/usr/src/.... Whether it's safer I haven't investigated; it also parses ELF files, but via its own libelf rather than GNU libbfd.
- saurik 12y agoApple also does not ship this tool from binutils (though as they aren't dealing with ELF this may be out of the scope of your analysis). They have used their own version of strings which also descended from BSD (like the one you point to from Illumos), though more indirectly (they used some of BSD's code, but I think mostly rewrote it while they were NeXT).
- edwintorok 12y agoOpenBSD's strings outputs this: BFD: strings-bfd-badptr: invalid string offset 1179403647 >= 0 for section `'
- kjetil 12y agoEven without such vulnerabilities, I would be wary of printing out stuff from any untrusted files in a terminal. Most terminal emulators have been vulnerable to escape character attacks at some point. http://marc.info/?l=bugtraq&m=104612710031920 http://marc.info/?l=bugtraq&m=104612710031920 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-2383 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-2383
- panzi 12y agoBut I don't think that strings will print escape characters. It's the point of strings to extract printable ASCII characters. Or am I mistaken?
- theoh 12y agoNo, you're right, I think the parent comment was referring to using cat or grep on binaries.
- malka 12y agoeven on 'valid' binaries, it still tends to mess up your terminal. I noticed that pretty quickly when I started working with Linux. Are there really people that work with cat and grep on binaries files ?
- theoh 12y agoPerhaps not intentionally, but cat has valid uses for concatenating binary files and sometimes they end up going to the terminal just by accident. As far as grep goes, the answer to your question is "yes": http://stackoverflow.com/questions/9988379/how-to-grep-a-text-file-which-contains-some-binary-data http://stackoverflow.com/questions/9988379/how-to-grep-a-tex...
- ams6110 12y agoGrep even explicitly supports it with the -a (--text) option that forces it to treat a binary file as text.
- Bentech 12y agoI wonder if this counts for 'file' as well
- greenyoda 12y agoI don't think so. "file" only needs to read the first few bytes at the beginning of a file to guess what type of file it is, so it isn't likely to have any buffer overflow problems.
- _delirium 12y agoYou might think so, but 'file' has its share of buffer overflows, integer overflows, attacker-inducible infinite loops, etc. It does some more extensive parsing for some kinds of files, and some of those end up with edge cases. Here's one from two days ago, a buffer overrun in parsing ELF files: https://access.redhat.com/security/cve/CVE-2014-3710 https://access.redhat.com/security/cve/CVE-2014-3710. Others: https://security-tracker.debian.org/tracker/source-package/file https://security-tracker.debian.org/tracker/source-package/f...
- mihai_ionic 12y agoYes, for example this bug affects 'file': https://sourceware.org/bugzilla/show_bug.cgi?id=16825 https://sourceware.org/bugzilla/show_bug.cgi?id=16825 The code in 'file' tries to parse the given file with every built-in format loader, so there are likely many more vulnerabilities like this one.
- Animats 12y agoIt's time to start converting the low-level Linux/UNIX utilities to a language with subscript checking. Go, or Rust (if and when it's finished), or D, or something. We have some good options now.
- blinkingled 12y agoIt's not the utilities themselves that are the bigger problem - rather a library the utility uses. libbfd in this case. Problem with writing libraries in anything other than C of course is that they are not very usable by a large variety of software.
- gizmo686 12y agoMost languages have some type of "extern C" as part of their FFI. It should be possible to use C as a langua franca between two non C languages.
- blinkingled 12y ago> It should be possible to use C Ugh, wouldn't that fall in "just because you can do something doesn't mean you actually should" category? :) Suppose I wrote the library in Go and exported some type of C wrapper and then I loaded that in Rust using Rust->C mechanism - that will load the Golang runtime in Rust! And you still got non-trivial C code to deal with anyways!
- gizmo686 12y agoIf you want to interface two languages, then I do not see a way arround involving the runtime of both languages (in the same way that linking to C code still uses the C runtime). However, using my method does not actually involve any C code. "extern C" makes the compiler produce object code that can be called as if it was produced by C code; it does not produce any C code that would compile to the given object code. Of course, it will also produce C headers to give the type information of the exported symbols, but I wouldn't call that code.
- kens 12y agoAm I the only person who thinks there's something fundamentally wrong with computing if running "strings" could let someone take over your computer? (I'm not being snarky; I seriously think the whole approach to security need to be redone somehow.)
- _ak 12y agoI think this is an indicator how fundamentally over-engineered all of the GNU tools are. strings was supposed to be a simple tool that finds bits of data that look like human-readable strings. It wasn't meant to parse ELF binaries and suddenly be a security risk, especially since it's one of the first tools you would use in computer forensics.
- _delirium 12y agoI don't think that's the root of the problem. While you could make a simpler strings(1), which would help people who only use that one, more complex stuff like objdump(1) really does need to parse binaries. And that should be possible to do without worrying about security problems: you're just reading a file and extracting some information, which even in the worst case should be possible to do without accidentally executing arbitrary code. It's just that libbfd seems to have a lot of bugs, and because it's written in an unsafe language, such bugs can not only cause incorrect information or crashes, but sometimes attacker-controlled code execution. But if you don't do it via libbfd, you're going to need some library that can parse binaries, since many utilities end up needing to do it, and it shouldn't be impossible to safely do so. An alternative is to implement a subset of full parsing specifically tailored to each utility. In the case of strings(1) that's very simple; in the case of some other utilities it's of intermediate complexity; all the way up to some that need to parse every corner of ELF. Whether that produces a bigger or smaller attack surface depends on a lot of factors: each parser might be simpler, but there are many more of them. FreeBSD was contemplating centralizing more of that into a common libelf, so I don't think it's only GNU who think that's a good idea in principle: https://wiki.freebsd.org/LibElf https://wiki.freebsd.org/LibElf
- cbd1984 12y agoAlthough perhaps not in this case, it's generally true that one person's bloat is another person's functionality. BSD tools are simpler. They're also generally less useful.
- roghummal 12y agoIs 'cat foo|strings' immune to the problems of libbfd?
- ams6110 12y agoI wouldn't think so. The data being processed are the same.
- roghummal 12y agoI thought that reading from stdin might bypass the file type analysis.
- ANTSANTS 12y agoHaven't checked but I'm pretty sure the binutils just look for "magic numbers" (ie are the first four bytes "0x7F 'E' 'L' 'F'") and don't look at any filesystem information.
- mistaken 12y agoUsing cat and piping it to string actually works without crashing. It seems that libbfd style parsing is disabled on stdin.
- deleted 12y ago[deleted]
- 101914 12y agoDespite what the blog comments suggest, gdb does not have to link to libbfd. But what about objcopy? Much more important utility than strings(1), in my opinion. I admit I rely on it and do not a have a substitute at the ready. At some stage we need a BSD alternative to the GNU binutils (aside from gcc alternatives). I have seen it discussed several times over the years, but as far as I know it does not exist?
- rajivkomar 12y agoCan't this bug be fixed
- mihai_ionic 12y agoThe binutils maintainers aren't exactly responsive when it comes to following up on security-impacting bug reports: https://sourceware.org/bugzilla/show_bug.cgi?id=16825 https://sourceware.org/bugzilla/show_bug.cgi?id=16825
- pronoiac 12y agoSo, can anyone suggest a safer alternative? Or should I use this as an excuse to pick up a new language?