This vulnerability was analyzed 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.