CVE-2024-22857: Arbitrary Code Execution in zlog
by Ali Raza
Vulnerability
Heap based buffer flow in zlog v1.1.0
to v1.2.17
in zlog_rule_new()
. Size of record_name
is MAXLEN_PATH(1024) + 1 but file_path
may have data upto MAXLEN_CFG_LINE(MAXLEN_PATH*4) + 1. So a check was missing in zlog_rule_new()
while 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 get arbitrary code execution or potentially cause remote code execution (RCE).
Now let’s take a deeper look into the zlog library and then we will see how we can exploit this vulnerability.
zlog Library
Quoted from zlog User’s Guide
zlog
is a reliable, high-performance, thread safe, flexible, clear-model, pure C logging library. Actually, in the C world there was NO good logging library for applications likelogback
in java orlog4cxx
in c++. Usingprintf
can work, but can not be redirected or reformatted easily.syslog
is slow and is designed for system use. Sozlog
is there. 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 to not install any target to my host machine, so I will use the compiled library from the source directory.
How to use
Zlog is is a simple but more customizable logging library. You can use it in your application by simply crafting a configuration file of your need and then using the library functions to log the messages. A detailed documentation is available on 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, category is a (zlog_cateogory_t *
) variable.
- Formats
They describe log patterns, such as: with or without time stamps, source files, and source lines.
- Rules
Rules consist of 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. Still, there is a complex match range of categories. Rule decouples variable conditions.
Let’s have 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:
$ ./main
Hello, zlog
This displays the log on stdout as defined in the configuration file. zlog supports various output methods. The syntax is:
(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 user-defined output. We use the following template to define 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 config will be:
[formats]
simple = "%m%n"
[rules]
mycat.* $my_func, "~/zlog_out.txt"; simple
Exploitation
The vulnerability is triggered while 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 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 reserved CVE number.
- 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
~Ali Raza Mumtaz (arm)
tags: Vulnerability Research - Exploitation