Heap-based buffer overflow in the glibc's syslog() [CVE-2023-6246]

We discussed this vulnerability during Episode 240 on 06 February 2024

Qualys at it again this time with a skipped initialization code path leading to a small allocation and a buffer overflow deep in glibc’s syslog.

The normal flow of this function is relatively simple. It initializes a few variables: starting with bufs the default buffer that will be used as a char[1024]. It also creates char* buf and size_t bufsize these start being set to NULL and 0 respectively. Then the code attempt to write just the standard syslog header/prefix to bufs. If the entire header was able to fit inside of bufs then it tries to write the actual message into the rest of bufs. Again is just checks if message was able to fit, if it was it sets buf to bufs. Regardless of the success of that write it will also update bufsize at this point now that it knows the length of the header + log message.

The next block of code checks if buf is still NULL which indicates at one of the two attempted writes it failed and couldn’t write the entire message indicating that it must need to allocate a larger buffer. It will attempt to allocate a bufsize + 1 buffer, and that is where everything goes wrong. The only time bufsize is updated is once it knows the header + log message length value. If the first write of only the header fails, it won’t fall into the conditional block that ultimately sets bufsize. This means when it tries to do this allocation, bufsize will still be 0.

The trick to reaching this case is that there must be a way to make the syslog header be more than the 1024 bytes of the initial buffer. Normally it’ll be much smaller than this, however if syslog is used without explicitly opening a log and setting a LogTag the tag used will effectively be the basename(argv[0]) which is entirely attacker controlled.

Qualys was able to exploit this issue against su on multiple distributions and they do document the exploitation strategy within the post.

Episode Correction: During the episode I believe we might have stated this error path didn’t really work at all, which is perhaps not a fair assessment since it does work when the failure is because of the message taking it over the buffer length. It only fails in this insecure way when the header specifically. I also think I might have mentioned an integer overflow, not exactly sure what I was thinking about there but this gets you a small allocation without any overflow.