Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Out-of-bounds write caused by incorrect error handling of calloc in get_options (mmdblookup.c:298) #252

Closed
cve-reporting opened this issue Feb 13, 2021 · 0 comments

Comments

@cve-reporting
Copy link

mmdblookup tool is vulnerable to OOB write attack via execution with maliciously crafted parameter list.

Incorrect handling of the value returned by calloc in get_options may lead to:

  • out-of-bound write attempt and segmentation fault error in case of restrictive memory protection,
  • near NULL out-of-bound overwrite in case of limited memory restrictions (e.g. in embedded environments).

Memory allocation is performed to handle input parameters, which are controlled by the attacker.
Number of input parameters is limited by an operating system restrictions - e.g. in Linux by ARG_MAX:
https://unix.stackexchange.com/questions/120642/what-defines-the-maximum-size-for-a-command-single-argument
mmdblookup allocates nr_params * sizeof(const char * ) bytes - e.g. on 64-bit Linux system following command allocated 1.6 MB block:

./bin/mmdblookup --file x --ip 1 $(python3 -c "print(209000*'0 ')")

If the allocation be unsuccessful (which may happen on systems with limited resources or strict limits) and calloc returns NULL, mmdblookup will fill the memory block starting from 0 with addresses of input parameters (argv).

Vulnerable code (mmdblookup.c):

297:    const char **lookup_path =
298:        calloc((argc - optind) + 1, sizeof(const char *));
299:    int i;
300:    for (i = 0; i < argc - optind; i++) {
301:        lookup_path[i] = argv[i + optind];
302:        (*lookup_path_length)++;
303:    }
304:    lookup_path[i] = NULL;

See following recommendations for details (especially the calloc example):
https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors

The issue can be reproduced and tested using ErrorSanitizer (https://gitlab.com/ErrorSanitizer/ErrorSanitizer).

Reproduction steps:

  1. Install gdb

  2. Download and unpack code of ErrorSanitizer (https://gitlab.com/ErrorSanitizer/ErrorSanitizer)

  3. Perform compilation of ErrorSanitizer according to the manual (https://gitlab.com/ErrorSanitizer/ErrorSanitizer#compilation)

    cd ErrorSanitizer; make

  4. Set ESAN to the path of ErrorSanitizer directory

    export ESAN=/opt/...

  5. Download and unzip attached map temp_0.cur_input
    temp_0.cur_input.zip

  6. Run mmdblookup with ErrorSanitizer in gdb using:

    gdb -batch -ex='run' -ex='backtrace' --args env LD_PRELOAD="$ESAN/error_sanitizer_preload.so" ./bin/.libs/mmdblookup --file tmp --ip 0 temp_0.cur_input

You should receive similar output:

process 31222 is executing new program: libmaxminddb/bin/.libs/mmdblookup
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x0000555555555414 in get_options (ip_file=<synthetic pointer>, thread_count=<synthetic pointer>, lookup_path_length=<synthetic pointer>, iterations=<synthetic pointer>, verbose=<synthetic pointer>, ip_address=<synthetic pointer>, mmdb_file=<synthetic pointer>, argv=0x7fffffffdae8, argc=<optimized out>) at mmdblookup.c:301
301	        lookup_path[i] = argv[i + optind];
#0  0x0000555555555414 in get_options (ip_file=<synthetic pointer>, thread_count=<synthetic pointer>, lookup_path_length=<synthetic pointer>, iterations=<synthetic pointer>, verbose=<synthetic pointer>, ip_address=<synthetic pointer>, mmdb_file=<synthetic pointer>, argv=0x7fffffffdae8, argc=<optimized out>) at mmdblookup.c:301
#1  main (argc=<optimized out>, argv=0x7fffffffdae8) at mmdblookup.c:103
#0  0x0000555555555414 in get_options (ip_file=<synthetic pointer>, thread_count=<synthetic pointer>, lookup_path_length=<synthetic pointer>, iterations=<synthetic pointer>, verbose=<synthetic pointer>, ip_address=<synthetic pointer>, mmdb_file=<synthetic pointer>, argv=0x7fffffffdae8, argc=<optimized out>) at mmdblookup.c:301
	program = <optimized out>
	lookup_path = 0x0
	i = <optimized out>
	help = 0
	version = 0
	program = <optimized out>
	lookup_path = <optimized out>
	i = <optimized out>
	options = {{name = 0x5555555565fe "file", has_arg = 1, flag = 0x0, val = 102}, {name = 0x5555555565d6 "ip", has_arg = 1, flag = 0x0, val = 105}, {name = 0x5555555565d9 "verbose", has_arg = 0, flag = 0x0, val = 118}, {name = 0x5555555565e1 "version", has_arg = 0, flag = 0x0, val = 110}, {name = 0x5555555565e9 "benchmark", has_arg = 1, flag = 0x0, val = 98}, {name = 0x5555555565f3 "threads", has_arg = 1, flag = 0x0, val = 116}, {name = 0x5555555565fb "ip-file", has_arg = 1, flag = 0x0, val = 73}, {name = 0x555555556603 "help", has_arg = 0, flag = 0x0, val = 104}, {name = 0x5555555564d8 "?", has_arg = 0, flag = 0x0, val = 1}, {name = 0x0, has_arg = 0, flag = 0x0, val = 0}}
	opt_index = <optimized out>
	optstring = <optimized out>
	opt_char = <optimized out>
#1  main (argc=<optimized out>, argv=0x7fffffffdae8) at mmdblookup.c:103
	mmdb_file = 0x7fffffffdf0f "tmp"
	ip_address = 0x7fffffffdf18 "0"
	verbose = 0
	iterations = 0
	lookup_path_length = <optimized out>
	thread_count = 0
	ip_file = 0x0
	lookup_path = <optimized out>
	mmdb = {flags = 25638685, filename = 0x7ffff7bacae6 "esan_enable_map_based_failure", file_size = 140737349601192, file_content = 0x7ffff7bac1dc "\253\064\306KG\261\202>\222~\307֕\364ec", data_section = 0x7ffff7bacbd4 "strtoul", data_section_size = 0, metadata_section = 0x7fffffffd5d0 "\250Ǻ\367\377\177", metadata_section_size = 3, full_record_byte_size = 32767, depth = 0, ipv4_start_node = {netmask = 54720, node_value = 32767}, metadata = {node_count = 0, record_size = 0, ip_version = 0, database_type = 0x7ffff7ff7a40 "p\341\377\367\377\177", languages = {count = 0, names = 0x7}, binary_format_major_version = 1, binary_format_minor_version = 0, build_epoch = 140737341165800, description = {count = 1640875860, descriptions = 0x7ffff7ff6358}}}
@horgh horgh closed this as completed in ec946c1 Feb 18, 2021
horgh added a commit that referenced this issue Feb 18, 2021
Check all calloc/malloc return values. Closes #252.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant