CVE-2024-22857: Arbitrary Code Execution in zlog
by Ali Raza

Vulnerability
Heap-based buffer flow in zlog versions v1.1.0 to v1.2.17 in zlog_rule_new(). The size of record_name is MAXLEN_PATH(1024) + 1, but file_path may have data up to MAXLEN_CFG_LINE(MAXLEN_PATH*4) + 1. A check was missing in zlog_rule_new() when copying the record_name from file_path + 1, which caused the buffer overflow. An attacker can exploit this vulnerability to overwrite the zlog_record_fn record_func function pointer to achieve arbitrary code execution or potentially cause remote code execution (RCE).
Now, let’s take a deeper look at the zlog library, and then we will see how to exploit this vulnerability.
zlog Library
Quoted from the zlog User’s Guide
zlogis a reliable, high-performance, thread-safe, flexible, clear-model, pure C logging library. In the C world, there was no good logging library for applications like Logback in Java or Log4cxx in C++. Usingprintfcan work, but it cannot be easily redirected or reformatted.syslogis slow and designed for system use. So, zlog was created. It is faster, safer, and more powerful thanlog4c, so it can be widely used.
Download and Build zlog
You can download the zlog library from GitHub (https://github.com/HardySimpson/zlog/). After downloading the library, you can build it using the following commands:
cd zlog
make
I prefer not to install any target on my host machine, so I will use the compiled library from the source directory.
How to use
Zlog is a simple but highly customizable logging library. You can use it in your application by crafting a configuration file for your needs and then using the library functions to log messages. Detailed documentation is available in the zlog User’s Guide. I will explain a few important aspects of the library here.
Configure
zlog uses a user-provided configuration file to initialize the logging objects. It has a specific format. There are 3 important concepts in zlog:
- Categories
They specify different kinds of log entries. In the zlog source code, a category is a (zlog_category_t *) variable.
- Formats
They describe log patterns, such as with or without timestamps, source files, and source lines.
- Rules
Rules consist of a category, level, output file (or other channel), and format. In brief, if the category string in a rule in the configuration file equals the name of a category variable in the source, then they match. However, there is a complex match range of categories. The rule decouples variable conditions.
Let’s look at a “Hello, Zlog” program that uses zlog.
Config file:
[formats]
simple = "%m%n"
[rules]
mycat.INFO >stdout; simple
Driver Program:
int main(){
const char * config_file = "zlog.conf";
int rv = zlog_init(config_file);
if (rv){
printf("init failed\\n");
return -1;
}
zlog_category_t *zc;
zc = zlog_get_category("mycat");
if (!zc) {
printf("get cat fail\\n");
zlog_fini();
return -2;
}
zlog_info(c, "Hello, zlog");
zlog_fini();
return 0;
}
This will simply display the info log as follows:
$ ./main
Hello, zlog
This displays the log on stdout, as defined in the configuration file. zlog supports various output methods. The syntax is as follows:
(output action), (output option); (format name, optional)
| Output | Output Action | Output Option | |
|---|---|---|---|
| to standard out | >stdout | no meaning | |
| to standard error | >stderr | no meaning | |
| to syslog | >syslog | syslog facility, can be: LOG_USER(default), LOG_LOCAL[0-7] This is required. | |
| pipeline output | cat | no meaning | |
| to file | “(file path)” | rotation. see 5.6 for detail 10MB * 3 ~ “press.#r.log” | |
| synchronous I/O file | -“(file path)” | ||
| user-defined output | $name | “path” (dynamic or static) of record function |
Our interest is in the user-defined output. We use the following template to define a user-defined output.
int in_program_func(zlog_msg_t *msg){
// Do whatever you want to, with the output
}
int main(void){
int rc = zlog_init("zlog.conf");
if (rc){
printf("init failed\\n");
return -1;
}
zlog_category_t *zc = zlog_get_category("mycat");
// set in_program_func as output function
if(zlog_set_record("my_func", in_program_func) != 0){ // [1]
printf("set record func failed\\n");
zlog_fini();
return -2;
}
zlog_info(zc, "Hello, zlog"); // [2]
zlog_fini();
return 0;
}
and the related configuration will be:
[formats]
simple = "%m%n"
[rules]
mycat.* $my_func, "~/zlog_out.txt"; simple
Exploitation
The vulnerability is triggered when parsing the configuration file. The zlog_rule_new() function is responsible for parsing the configuration file and creating a new rule.
I have posted a detailed explanation of the vulnerability, along with the exploit code, on my company’s (Ebryx) blog. You can read the full post here.
Timeline
- 11-22-2023: Vulnerability discovered and reported to the vendor.
- 01-10-2024: Reported to MITRE, and a CVE number was reserved.
- 03-07-2024: Public disclosure and CVE published.
References
- https://www.ebryx.com/blogs/arbitrary-code-execution-in-zlog-cve-2024-22857
- https://www.cybersecurity-help.cz/vdb/SB2024022842
- https://www.openwall.com/lists/oss-security/2024/02/28/8
- https://github.com/HardySimpson/zlog/pull/251
— locus-x64
tags: Vulnerability Research - Exploitation