From 1c54b327c8a0aa4605637d00ff256dd0d495e744 Mon Sep 17 00:00:00 2001 From: DavidKorczynski Date: Mon, 28 Jun 2021 21:45:31 +0100 Subject: [PATCH] openvpn: three new fuzzers and improved proxy fuzzer (#5979) * add list fuzzer * add header file inclusion order. * added an mroute fuzzer. * add packet id fuzzer. * refactor list, mroute and packet_id fuzzers. * set it up so fgets always returns a string with an ASCII char. * refactor build script. * hook fopen and fclose in builtin_console. --- projects/openvpn/build.sh | 9 +- projects/openvpn/fuzz_header.h | 18 +++- projects/openvpn/fuzz_list.c | 135 ++++++++++++++++++++++++++++++ projects/openvpn/fuzz_mroute.c | 72 ++++++++++++++++ projects/openvpn/fuzz_packet_id.c | 77 +++++++++++++++++ 5 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 projects/openvpn/fuzz_list.c create mode 100644 projects/openvpn/fuzz_mroute.c create mode 100644 projects/openvpn/fuzz_packet_id.c diff --git a/projects/openvpn/build.sh b/projects/openvpn/build.sh index 2ca701b90..1eac4f373 100755 --- a/projects/openvpn/build.sh +++ b/projects/openvpn/build.sh @@ -28,6 +28,9 @@ sed -i 's/select(/fuzz_select(/g' ./src/openvpn/proxy.c sed -i 's/send(/fuzz_send(/g' ./src/openvpn/proxy.c sed -i 's/recv(/fuzz_recv(/g' ./src/openvpn/proxy.c +sed -i 's/fopen/fuzz_fopen/g' ./src/openvpn/console_builtin.c +sed -i 's/fclose/fuzz_fclose/g' ./src/openvpn/console_builtin.c + sed -i 's/fp = (flags/fp = stdout;\n\/\//g' ./src/openvpn/error.c # Build openvpn @@ -44,13 +47,13 @@ ar r libopenvpn.a *.o $CXX $CXXFLAGS -g -c $SRC/fuzz_randomizer.cpp -o $SRC/fuzz_randomizer.o # Compile the fuzzers -for fuzzname in fuzz_dhcp fuzz_misc fuzz_base64 fuzz_proxy fuzz_buffer fuzz_route; do +for fuzzname in fuzz_dhcp fuzz_misc fuzz_base64 fuzz_proxy fuzz_buffer fuzz_route fuzz_packet_id fuzz_mroute fuzz_list; do $CC -DHAVE_CONFIG_H -I. -I../.. -I../../include -I../../include -I../../src/compat \ -DPLUGIN_LIBDIR=\"/usr/local/lib/openvpn/plugins\" -Wall -std=c99 $CFLAGS \ - -c $SRC/${fuzzname}.c -o ${fuzzname}.o + -c $SRC/${fuzzname}.c -o $SRC/${fuzzname}.o # Link with CXX - $CXX ${CXXFLAGS} ${LIB_FUZZING_ENGINE} ./${fuzzname}.o -o $OUT/${fuzzname} $SRC/fuzz_randomizer.o \ + $CXX ${CXXFLAGS} ${LIB_FUZZING_ENGINE} $SRC/${fuzzname}.o -o $OUT/${fuzzname} $SRC/fuzz_randomizer.o \ libopenvpn.a ../../src/compat/.libs/libcompat.a /usr/lib/x86_64-linux-gnu/libnsl.a \ /usr/lib/x86_64-linux-gnu/libresolv.a /usr/lib/x86_64-linux-gnu/liblzo2.a \ -lssl -lcrypto -ldl diff --git a/projects/openvpn/fuzz_header.h b/projects/openvpn/fuzz_header.h index 2b9ae18f9..566baa757 100644 --- a/projects/openvpn/fuzz_header.h +++ b/projects/openvpn/fuzz_header.h @@ -25,9 +25,14 @@ ssize_t fuzz_read(int sockfd, void *buf, size_t len){ return fuzz_get_random_data(buf, len); } + + char *fuzz_fgets(char *s, int size, FILE *stream) { ssize_t v = fuzz_get_random_data(s, size-1); - if (s[0] == '\0') { + // We use fgets to get trusted input. As such, assume we have + // an ascii printable char at the beginning. + printf("Calling into fgets\n"); + if (s[0] <= 0x21 || s[0] >= 0x7f) { s[0] = 'A'; } s[size-1] = '\0'; @@ -45,3 +50,14 @@ int fuzz_select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, s ssize_t fuzz_send(int sockfd, const void *buf, size_t len, int flags) { return len; } + +FILE *fp_p = NULL; +FILE *fuzz_fopen(const char *pathname, const char *mode) { + if (mode == NULL) return fp_p; + return fp_p; +} + +int fuzz_fclose(FILE *stream) { + if (stream == NULL) return 1; + return 2; +} diff --git a/projects/openvpn/fuzz_list.c b/projects/openvpn/fuzz_list.c new file mode 100644 index 000000000..1e8fe621c --- /dev/null +++ b/projects/openvpn/fuzz_list.c @@ -0,0 +1,135 @@ +/* Copyright 2021 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "config.h" +#include "syshead.h" +#include "list.h" + +#include "fuzz_randomizer.h" + +#define KEY_SIZE 23 + +/* Required for hash_init() */ +static uint32_t word_hash_function(const void *key, uint32_t iv) { + return hash_func(key, KEY_SIZE, iv); +} + +/* Required for hash_init() */ +static bool word_compare_function(const void *key1, const void *key2) { + return ((size_t)key1 & 0xFFF) == ((size_t)key1 & 0xFFF); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + struct gc_arena gc; + struct hash *hash = NULL; + ssize_t generic_ssizet, generic_ssizet2, num_loops; + + fuzz_random_init(data, size); + + gc = gc_new(); + + int total_to_fuzz = fuzz_randomizer_get_int(1, 20); + for (int i = 0; i < total_to_fuzz; i++) { + generic_ssizet = fuzz_randomizer_get_int(0, 8); + + switch (generic_ssizet) { + case 0: + if (hash == NULL) { + int n_buckets = fuzz_randomizer_get_int(1, 1000); + uint32_t iv; + + hash = + hash_init(n_buckets, iv, word_hash_function, word_compare_function); + } + break; + case 1: + if (hash) { + hash_free(hash); + hash = NULL; + } + break; + case 2: + if (hash) { + struct hash_iterator hi; + struct hash_element *he; + hash_iterator_init(hash, &hi); + while ((he = hash_iterator_next(&hi))) { + void *w = he->value; + } + hash_iterator_free(&hi); + } + break; + case 3: + if (hash) { + void *key; + void *value; + char arr[KEY_SIZE]; + memset(arr, 0, KEY_SIZE); + fuzz_get_random_data(arr, KEY_SIZE); + key = (void *)arr; + if (!hash_lookup(hash, key)) { + generic_ssizet = fuzz_randomizer_get_int(0, 0xfffffff); + value = (void *)generic_ssizet; + hash_add(hash, key, value, false); + } + } + break; + case 4: + if (hash) { + hash_n_elements(hash); + } + break; + case 5: + if (hash) { + hash_n_buckets(hash); + } + break; + case 6: + if (hash) { + uint32_t hv; + generic_ssizet = fuzz_randomizer_get_int(0, 0xfffffff); + hv = generic_ssizet; + hash_bucket(hash, hv); + } + break; + case 7: + if (hash) { + void *key; + char arr[KEY_SIZE]; + memset(arr, 0, KEY_SIZE); + fuzz_get_random_data(arr, KEY_SIZE); + key = (void *)arr; + hash_remove(hash, key); + } + break; + case 8: + if (hash) { + void *value; + generic_ssizet = fuzz_randomizer_get_int(0, 0xfffffff); + value = (void *)generic_ssizet; + hash_remove_by_value(hash, value); + } + default: + break; + } + } + + if (hash) { + hash_free(hash); + } + + gc_free(&gc); + + fuzz_random_destroy(); + + return 0; +} diff --git a/projects/openvpn/fuzz_mroute.c b/projects/openvpn/fuzz_mroute.c new file mode 100644 index 000000000..2f996d490 --- /dev/null +++ b/projects/openvpn/fuzz_mroute.c @@ -0,0 +1,72 @@ +/* Copyright 2021 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "config.h" +#include "syshead.h" +#include "init.h" +#include "mroute.h" + +#include "fuzz_randomizer.h" + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + fuzz_random_init(data, size); + struct buffer buf; + struct gc_arena gc; + + gc = gc_new(); + + char *tmp = get_random_string(); + buf = string_alloc_buf(tmp, &gc); + free(tmp); + + struct mroute_addr src_addr; + struct mroute_addr dst_addr; + mroute_addr_init(&src_addr); + mroute_addr_init(&dst_addr); + unsigned int ret = mroute_extract_addr_ip(&src_addr, &dst_addr, &buf); + + if (ret & MROUTE_EXTRACT_SUCCEEDED) { + mroute_addr_mask_host_bits(&src_addr); + mroute_addr_print(&src_addr, &gc); + mroute_learnable_address(&src_addr, &gc); + } + + uint16_t vid; + struct mroute_addr a1, a2, a3, a4; + mroute_addr_init(&a1); + mroute_addr_init(&a2); + mroute_addr_init(&a3); + mroute_addr_init(&a4); + mroute_extract_addr_ether(&a1, &a2, &a3, &a3, vid, &buf); + + if (size > sizeof(struct openvpn_sockaddr)) { + struct openvpn_sockaddr local_sock; + memcpy(&local_sock, data, sizeof(struct openvpn_sockaddr)); + mroute_extract_openvpn_sockaddr(&a1, &local_sock, true); + mroute_extract_openvpn_sockaddr(&a1, &local_sock, false); + } + + struct mroute_helper *mhelper = NULL; + mhelper = mroute_helper_init(fuzz_randomizer_get_int(0, 0xfffffff)); + if (mhelper != NULL) { + mroute_helper_add_iroute46(mhelper, fuzz_randomizer_get_int(0, MR_HELPER_NET_LEN-1)); + mroute_helper_free(mhelper); + } + + gc_free(&gc); + + fuzz_random_destroy(); + return 0; +} + diff --git a/projects/openvpn/fuzz_packet_id.c b/projects/openvpn/fuzz_packet_id.c new file mode 100644 index 000000000..e550da0c0 --- /dev/null +++ b/projects/openvpn/fuzz_packet_id.c @@ -0,0 +1,77 @@ +/* Copyright 2021 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "config.h" +#include "syshead.h" +#include "init.h" +#include "packet_id.h" + +#include "fuzz_randomizer.h" + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzz_random_init(data, size); + + struct packet_id pid; + struct packet_id_net pin; + const int seq_backtrack = 10; + const int time_backtrack = 10; + + packet_id_init(&pid, seq_backtrack, time_backtrack, "name", 0); + + int total_sends = fuzz_randomizer_get_int(0, 10); + for (int i = 0; i < total_sends; i++) { + update_time(); + pin.time = fuzz_randomizer_get_int(0, 0xfffffff); + pin.id = fuzz_randomizer_get_int(0, 0xfffffff); + + packet_id_reap_test(&pid.rec); + bool test = packet_id_test(&pid.rec, &pin); + if (test) { + packet_id_add(&pid.rec, &pin); + } + } + packet_id_free(&pid); + + struct gc_arena gc; + gc = gc_new(); + struct buffer buf; + char *tmp = get_random_string(); + buf = string_alloc_buf(tmp, &gc); + free(tmp); + packet_id_read(&pid, &buf, false); + packet_id_read(&pid, &buf, true); + gc_free(&gc); + + char filename[256]; + sprintf(filename, "/tmp/libfuzzer.%d", getpid()); + + FILE *fp = fopen(filename, "wb"); + if (!fp) { + return 0; + } + fwrite(data, size, 1, fp); + fclose(fp); + + struct packet_id_persist p; + memset(&p, 0, sizeof(struct packet_id_persist)); + packet_id_persist_init(&p); + packet_id_persist_load(&p, filename); + gc = gc_new(); + packet_id_persist_print(&p, &gc); + gc_free(&gc); + packet_id_persist_close(&p); + + fuzz_random_destroy(); + return 0; +} +