JOSTALY TECHNOLOGIES 12/09/2024 142f308c2c67b97f47a397a3f08bd652ca94a73b5bfaef4d98dc80aba41f6553 ./xz.article.git.tgz d91c0e8d6acca91a8fef877666f4985f07427d1f5899502bd3115a42c2501994 ./xz.article.git.tgz.asc "Our lives begin to end the day we become silent about things that matter." - Martin Luther King 0) NEWS OF INTEREST 30 Aug : CVE-2023-22527 - Atlassian Confluence 04 Sep : Haliburton data breach (CVE-2024-3094): Backdoor in XZ utils configure 1) ARTICLE This article digs into augmenting xz and then replacement of standard libc. Xz was a slightly larger endeavor as it's split into liblzma, lzmainfo, xz, xzdec, and lzmadec. The article was sort of a race against time to generate content for the company website, double as a development task/testing, triple for testing/bug-hunting, but there's only so much time. The standard configuration for most software packages is complex to say the least, and gets even more complicated from nefarious actors adding backdoors in configuration scripts, the build system, and (in)advertent security issues baked into the software itself. The complexity of these systems makes finding the issues akin to the proverbial needle in the haystack; sometimes even bordering on the line of obfuscation if not crossing it (under the hood at least). After running a configuration, build, and standard make install to a modified --prefix, the entire build system was ripped out and replaced with shell scripts of what is actually getting built and modifying some compiler flags. The resulting install files then linked back into the source tree, the default system versions uninstalled / removed. See the makefiles in each directory. They run a build.sh shell script. XZ has testing, next was to ensure that all tests work with the simplified build. A lot of work goes into open source software, most times it's used without an appreciation or understanding the mechanics. Obsessive on code is something JOSTALY TECHNOLOGIES is good at. Our libc variant hooks into our patent pending design and while the prototypes remain the same to be compliant with the standard, there's a number of differences that narrow in on code sections that are interesting. Complexity is generally the enemy of something being straight-forward and easy to understand, simple sticks. A top down approach to start the augmentation/replacement was used initially due to the smaller number of source files. Almost immediately however, a segfault was encountered with the application layer releasing memory allocated by the liblzma library, causing a conflict between the two libc memory managers, introducing a chicken and the egg problem. A temporary disable of linking with the library and direct library source file compilation was required to bypass the problem until all components had been augmented. The lzmainfo application was first, xzdec/lzmadec second, and finally xz itself. It's also worthwhile to point out that lzmadec is really just xzdec. Immediately with augmentation/replacement a missing null check was found in lzma_free: extern void lzma_free(void *ptr, const lzma_allocator *allocator) { if (allocator != NULL && allocator->free != NULL) allocator->free(allocator->opaque, ptr); -> else if (ptr != NULL) free(ptr); return; } Not very interesting. JOSTALY TECHNOLOGIES believes in pedantic error checking though, the NULL page is a popular exploitation, checking pointers everywhere can uncover interesting behavior. The stdio.h header is questionable, it provides essentially a middle man interface around the unistd.h raw syscalls. It's very common to see the standard f(write|read|close|error|open) calls, etc. so this interface is useful to implement avoiding translation to unistd.h variants. Our implementation is essentially a thin direct pass-through to the raw syscalls to avoid the "middle-man" as much as possible. Unless required by the standard, the use of global variables are not used, and modifications made to accommodate this. String manipulation is also a popular exploitation known for causing issues. Most programmers or anyone familiar with the standard C formatting will notice the output has raw % formatting and the arguments printed after the format string. This avoids string manipulation code paths at the expense of aesthetics and possible detection of manipulation failures within the formatting. It's always interesting to dig into an unfamiliar code base, and the disorientation that comes along with it. This assembler file caught my eye: LZMA_CRC64: _CET_ENDBR ... pushl %ebx pushl %esi pushl %edi pushl %ebp movl 0x14(%esp), %esi /* buf */ movl 0x18(%esp), %edi /* size */ movl 0x1C(%esp), %eax /* crc LSB */ While you would have to configure xz with a 32-bit environment (host_cpu=i?86) to turn on getting this built in, and then use a 64-bit application to call that function, the address would be cut in half. It was questionable, took a little bit of time to track down, so it was just removed along with other sections. Particularly the BCJ (Branch/Conditional/Jump) coders/decoders, these are sort of interesting as they're modifying instructions for particular architectures for better compression results. Tests were modified to accommodate these changes. Raw assembly files were ripped out, arguments ripped out that are more development type of tools (--list, --block-size, --block-list) that are probably better off as an independent tool. A number of OS specific macros were additionally removed to simplify down to generic (ENABLE_SANDBOX, etc.). Multi-threading encoders/decoders were also removed. Given the speed of current processors, the assembler and threading have a feel of premature optimization and extra complexity. All in all, this xz variant is a bit less complex, doesn't require a build system, and is augmented with our operating system / library routines. Each of the build scripts have two important toggles: * CFLAGS : Toggle between our backend implementation of libc, or use the development system libc. * LZMA_OBJS : Toggle between direct liblzma sources, or use static linking of liblzma. Interesting limitations on our end that were found in addition to above: -fstack-protector strong: A simple implementation of the hidden stack_chk_fail emission call is not sufficient. Internally Thread Local Storage (TLS) is used with segment registers to achieve this (on the x86). This is essentially hooking into a backend compiler feature. This may differ between compilers, and results may vary. Local variables of relevant size are generally avoided, a more granular approach would be nice in addition to our other security features.