Another Path to Exploiting CVE-2024-1212 in Progress Kemp LoadMaster
Intro
Rhino Labs discovered a pre-authentication command injection vulnerability in the Progress Kemp LoadMaster. LoadMaster is a load balancer product that comes in many different flavors and even has a free version. The flaw exists in the LoadMaster API. When an API request is received to either the β/accessβ or β/accessv2β endpoint, the embedded min-httpd server calls a script which in turn calls the access binary with the HTTP request info. The vulnerability works even when the API is disabled.
Rhino Labs showed that attacker controlled data is read by the access binary when sending an enableapi command to the /access endpoint. The attacker controlled data exists as the βusernameβ in the Authorization header. The username value is put into the REMOTE_USER environment variable. The value stored in REMOTE_USER is retrieved by the access binary and ends up as part of a string passed to a system() call. The system call executes the validuser binary and a carefully crafted payload allows us to inject commands into the bashΒ shell.
We also found that the REMOTE_PASS environment variable is exploitable in the same way here via the Authorization header.
This command execution is possible via any API command if the API is enabled. As Rhino Labs points out, When sending a GET request to the access API indicating the enableapi command, the access binary skips checking whether the API is enabled first or not, and the Authorization header is checked rightΒ away.
APIv2
While investigating this vulnerability, I noticed that LoadMaster has two APIs, the v1 API indicated above, and a v2 API that functions via the /accessv2 endpoint and JSON data. The access binary still processes these requests, but a slightly different path is followed. The logic of the main function is largely duplicated as a new function and called if the APIv2 is requested. That function then performs the same checks as above, with the slight exception that it will decode the API and pass the values of the apiuser and apipass keys to the same system call. So, we have another path to the same exposure:
While we can still control the password variable, itβs no longer exploitable here. Somewhere along the path the password string gets converted to base64 before being passed through the system() call, nullifying any injectedΒ quotes.
We can see below that the verify_perms function calls validu() with REMOTE_USER and REMOTE_PASS data in the APIv1 implementation; in the API v2 implementation the apiuser and apipass data is passed to validu() from the APIv2Β JSON.
Patch
The patch solves these flaws quite simply by examining the username and password strings in the Authorization header for single quotes. If they contain a single quote, the patched function will truncate them just before the first single quote. Decompiling the patched access binary with Ghidra, we can seeΒ this:
Here we see the addition of the new function call for both username and password. The function loops over each character in the input string and if it is a single quote, itβs changed to a \0, null terminating theΒ string.
Another Way to Test: Emulation
Even though weβve got x86 linux binaries, we canβt run them natively on another linux machine due to potential library and ABI issues. Regardless, we can extract the filesystem and use a chroot and qemu to emulate the environment. Once weβve extracted the filesystem, we can mount the ext2 filesystem ourselves:
sudo mount -t ext2 -o loop,exec unpatched.ext2 mnt/
Now we can explore the filesystem and execute binaries.
This provides us with a quick offline method to test our assumptions around injection. For instance, as we mentioned, the access binary is exploitable via the REMOTE_USER parameter:
First, weβve copied the qemu-x86_64-static binary into our mounted filesystem. Weβre using that with the -E flag to pass in a bunch of environment variables found via reversing access, one of which is the injectable REMOTE_USER. The whole thing is wrapped in chroot so that symbolic links and relative paths work correctly. We give /bin/access several flags which weβve lifted straight from the CGI script that calls theΒ binary
exec /bin/${0/*\//} -C $CLUST -F $FIPS -H $HW_VERSION
and from checking the ps debugging feature in the LoadMaster UI. Pro tip: check ps while running another longer running debug command like top or tcpdump in order to see betterΒ results.
root 13333 0.0 0.0 6736 1640 ? S 15:54 0:00 /sbin/httpd -port 8080 -address 127.0.0.1
root 16733 0.0 0.0 6736 112 ? S 15:59 0:00 /sbin/httpd -port 8080 -address 127.0.0.1
bal 16734 0.0 0.0 12064 2192 ? S 15:59 0:00 /bin/access -C 0 -F 0 -H 3
bal 16741 0.2 0.0 11452 2192 ? S 15:59 0:00 /usr/bin/top -d1 -n10 -b -o%CPU
bal 16845 0.0 0.0 7140 1828 ? R 15:59 0:00 ps auxwww
While this doesnβt provide us the complete method to exploit externally, it is a nice quick method to try out different injection strings and test assumptions. We can also pass a -g <port> parameter to qemu and then attach gdb to the process to get even closer to whatβs happening.
Conclusion
This was a really cool find by Rhino Labs. Here I add one additional exploitation path and some additional ways to test for this vulnerability.
Tenableβs got you covered and can detect this vulnerability as part of your VM program with Tenable VM, Tenable SC, and Tenable Nessus. The direct check plugin for this vulnerability can be found at CVE-2024-1212. The plugin tests test both APIv1 and APIv2 paths for this command execution exposure.
Resources
Another Path to Exploiting CVE-2024-1212 in Progress Kemp LoadMaster was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.