HACKER Q&A
📣 somemisopaste

How does the xz backdoor replace RSA_public_decrypt?


At this point I pretty much understand the entire process on how the xz backdoor came to be: its execution stages, extraction from binary "test" files etc. But one thing puzzles me: how can the ifunc mechanism be used to replace something like RSA_public_decrypt? Granted this probably stems from my lack of understanding of ifunc, but I was under the impression that in order for the ifunc mechanism to work in your code, you have to explicitly mark specific function with multiple implementations with __attribute__ ((ifunc ("the_resolver_function"))). Looking at the source code of the RSA function in question, ifunc attribute isn't present:

https://github.com/openssl/openssl/blob/master/crypto/rsa/rsa_crpt.c#L51

So how does the backdoor actually replace the call? Does this means that the ifunc mechanism can be used to override pretty much anything on the system?


  👤 lifthrasiir Accepted Answer ✓
The initial discoverer has briefly mentioned this (and any error here will be mine), but ifunc resolver here has been used to execute some code much earlier than everything else, because the resolver has to be called before the symbol gets resolved. So the resolver itself doesn't have to be put to the replaced function; the actual hooking instead happened in crc32_resolve etc. in the liblzma itself, which is justifiable by its own because ifunc is often used for performance-sensitive code.

The resolver isn't always guaranteed to be called if the symbol itself doesn't get used, but sshd had a particular linker option that resolves all symbols during the dynamic linkage phase, so that resolver happened to be always called, and importantly, before `RSS_public_decrypt` could been resolved. So the resolver could tamper the subsequent dynamic loading process to replace that symbol.