ali@pwnworld$

Exploring kernel exploitation.

9 March 2024

CVE-2024-22857: Arbitrary Code Execution in zlog

by Ali Raza

CVE-2024-22857

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

zlog is 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++. Using printf can work, but it cannot be easily redirected or reformatted. syslog is slow and designed for system use. So, zlog was created. It is faster, safer, and more powerful than log4c, 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:

They specify different kinds of log entries. In the zlog source code, a category is a (zlog_category_t *) variable.

They describe log patterns, such as with or without timestamps, source files, and source lines.

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

References

— locus_x64

tags: Vulnerability Research - Exploitation