Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Posted Apr 2, 2024 21:38 UTC (Tue) by dezgeg (subscriber, #92243)In reply to: Free software's not-so-eXZellent adventure by ms
Parent article: Free software's not-so-eXZellent adventure
As far as I understood, ifuncs itself are not the method to achieve this. It's just a way to gain execution whenever a library is loaded, just like .init (which is used for things like calling global constructons). The actual method to hook RSA_public_decrypt() is via audit hook (see man rtld-audit).
Now, it's still possible that using the ifunc mechanism made things somewhat simpler for the backdoor developer (perhaps ifunc resolvers are called in an earlier stage than .init functions, or something), but fact is .init still would have been a way to get liblzma code to execute in the sshd process (and .init can't be removed without breaking metric tons of stuff).
Posted Apr 2, 2024 22:15 UTC (Tue)
by andresfreund (subscriber, #69562)
[Link] (8 responses)
It'd have been harder / noisier, I suspect. From what I can tell .init etc do get processed after symbol resolution and thus after the GOT has been remapped read-only. Of course you could still do nasty stuff, but it'll either be harder to have sufficient information about the client (and thus whether to execute a payload), or noisier (remapping the GOT RW).
Posted Apr 3, 2024 1:48 UTC (Wed)
by dezgeg (subscriber, #92243)
[Link]
Another idea that comes to mind is fopencookie() could be used to hook stdout/stderr and use that to deliver the payload (assuming sshd includes enough info there about failed connection attempts)... there's just so many different opportunities. Also detection chances could probably be reduced by simply disabling the backdoor for say, 90% of the connections (pthread_atfork() perhaps) while still remaining useful.
Posted Apr 3, 2024 13:59 UTC (Wed)
by jejb (subscriber, #6654)
[Link] (6 responses)
Posted Apr 3, 2024 14:35 UTC (Wed)
by farnz (subscriber, #17727)
[Link] (5 responses)
The problem with "shouldn't call the ifunc to perform the choice test until we actually need to resolve the symbol" is that an attacker can just redirect a symbol that matters to you; for example, if I supply an IFUNC for memcpy, then my IFUNC is called as soon as your program calls memcpy. This is why I believe that there's two ELF design decisions that interact to surprise you - the other one is that the symbol import doesn't tell the linker which library it expects the symbol to come from, so any symbol can be indirected by any dependency.
Posted Apr 3, 2024 14:51 UTC (Wed)
by jejb (subscriber, #6654)
[Link] (4 responses)
The way ifuncs currently work for dynamic libraries is that you can only do one for your own symbols, so liblzma can't use an ifunc to intercept memcpy, only glibc can do that.
So to look at this concrete example: liblzma couldn't use an ifunc to redirect RSA_public_decrypt (the object of its attack), it had to use an ifunc on one of its own functions to install an audit hook to redirect the symbol resolution for RSA_public_decrypt that allowed it to hijack the symbol in another library.
Posted Apr 3, 2024 15:21 UTC (Wed)
by ms (subscriber, #41272)
[Link]
That seems a really good example: I have no doubt it would be technically possible for the dynamic linker / elf loader to allow ifuncs for symbols that are defined elsewhere - sure you might have to do it lazily because maybe that symbol hasn't been defined yet, but I'm sure it could be done. But instead, it has been _chosen_ that you can only put ifuncs it on your own functions. So it doesn't seem that much of a leap to also choose "you can only install audit hooks on your own symbols", or, more generally, "pre main, you can't do anything to symbols you've not defined yourself".
Posted Apr 3, 2024 15:22 UTC (Wed)
by farnz (subscriber, #17727)
[Link] (2 responses)
liblzma can, however, supply a memcpy symbol, and with a bit of careful squirrelling, be set up so that its IFUNC causes it to redirect to the normal memcpy. This sort of symbol redirection game is designed into ELF (although it's not necessary for a dynamic linker to do this - Apple's Mach-O format has symbol tell you which library to import from).
So liblzma can use an IFUNC to redirect RSA_public_decrypt; it's just that in doing so, it makes RSA_public_decrypt visible in the exports list of liblzma, which would make the attack less stealthy.
Posted Apr 3, 2024 16:15 UTC (Wed)
by jejb (subscriber, #6654)
[Link] (1 responses)
No, it can't. If you get two identical global scope non weak symbols the dynamic linker will throw a duplicate symbol error. If you try to do a local scope instead (which actually indirect dependencies get by default), it won't get resolved until used in that scope (so the resolution in sshd would always pick up the global scope symbol in glibc). You could possibly get around this using versioning, but, assuming ifunc on resolution is implemented, your ifunc wouldn't get called until something requests that specific version of memcpy ... which wouldn't happen for the binary in question.
Posted Apr 3, 2024 16:17 UTC (Wed)
by farnz (subscriber, #17727)
[Link]
It can - it supplies a weak symbol, and at least in my experiments, that's enough to trigger IFUNC resolution from my attacking binary. Maybe this is a bug, but it's still a problem.
Posted Apr 2, 2024 22:28 UTC (Tue)
by fenncruz (subscriber, #81417)
[Link]
Posted Apr 3, 2024 12:03 UTC (Wed)
by smurf (subscriber, #17840)
[Link] (3 responses)
They might well be, but that's irrelevant since once you have an adversarial foothold in some library or other, running as root, you can do pretty much anything anyway, ifunc or .init or hijacked symbol.
More to the point: using an ifunc in order to choose the best implementation is fairly standard for crypto or compression code, thus doesn't jump at you when you look at the library's metadata. On the other hand, .init is used to build static objects and similar stuff which a reasonably-coded multi-thread-capable library should have no business requiring. The fact that it's easier to get gdb to trace code in .init sections than in ifuncs might be relevant too.
Posted Apr 3, 2024 12:44 UTC (Wed)
by ms (subscriber, #41272)
[Link] (2 responses)
(ignoring the "running as root" clause)
I think this is maybe getting to the nub of it: if you link with a library, are you implicitly trusting it, to the extent that you're willing for it to write all over "your" memory, including code?
It's possible that for some of us who are used to thinking in terms of microservices, containers, etc etc, it doesn't seem hard to imagine a world where the answer is "no, I'm not trusting it that far - it can have its own memory, just like it has its own namespace, and its own scopes, but it doesn't get to interfere with mine". To me, it seems pretty crazy that all these languages typically enforce lexical scoping rules, but apparently glibc, with a combination of ifuncs and audit hooks, allows complete violation of the compartmentalisation that lexical scoping creates.
For some of us who are (I'm guessing) more experienced C/C++/kernel devs, there's both tradition, and good reason as to why believing/hoping/pretending the trust isn't absolute, is misguided.
Posted Apr 3, 2024 15:32 UTC (Wed)
by farnz (subscriber, #17727)
[Link] (1 responses)
You don't even need to be thinking in terms of microservices etc; Apple's dynamic linker requires imported symbols to be annotated with the library that's expected to supply the symbol, and thus most of the time, when you link against a library, you're only bringing it in for its symbols. I don't know if it has an equivalent of IFUNC, nor when static constructors are run (when a library is loaded, or when you first access a symbol from the library - both would be fine within the letter of C++).
Posted Apr 7, 2024 17:31 UTC (Sun)
by mathstuf (subscriber, #69389)
[Link]
They are run when a symbol from the *object* containing the static object resides is accessed.
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure
Free software's not-so-eXZellent adventure