From 01765e6f2b95c8766853bc8b1d5761f96916bafe Mon Sep 17 00:00:00 2001 From: DavidKorczynski Date: Fri, 25 Jun 2021 17:01:09 +0100 Subject: [PATCH] Openvpn: improve proxy fuzzer and fuzzing infrastructure (#5971) * openvpn: improve buffer fuzzer. * Improve state of openvpn fuzzing, the proxy fuzzer in particular. --- projects/openvpn/Dockerfile | 1 + projects/openvpn/build.sh | 23 ++++++++++- projects/openvpn/fuzz.h | 47 ++++++++++++++++++++++ projects/openvpn/fuzz_base64.cpp | 2 + projects/openvpn/fuzz_buffer.cpp | 12 +----- projects/openvpn/fuzz_dhcp.cpp | 2 + projects/openvpn/fuzz_header.h | 46 ++++++++++++++++++++++ projects/openvpn/fuzz_misc.cpp | 3 +- projects/openvpn/fuzz_proxy.cpp | 67 ++++++++++++++++++++++++-------- 9 files changed, 174 insertions(+), 29 deletions(-) create mode 100644 projects/openvpn/fuzz.h create mode 100644 projects/openvpn/fuzz_header.h diff --git a/projects/openvpn/Dockerfile b/projects/openvpn/Dockerfile index 93a5bf887..22fcef862 100644 --- a/projects/openvpn/Dockerfile +++ b/projects/openvpn/Dockerfile @@ -20,3 +20,4 @@ RUN git clone --depth 1 https://github.com/OpenVPN/openvpn openvpn WORKDIR openvpn COPY build.sh $SRC/ COPY fuzz*.cpp $SRC/ +COPY fuzz*.h $SRC/openvpn/src/openvpn/ diff --git a/projects/openvpn/build.sh b/projects/openvpn/build.sh index a0fb60e48..88bb61048 100755 --- a/projects/openvpn/build.sh +++ b/projects/openvpn/build.sh @@ -15,14 +15,33 @@ # ################################################################################ +# Changes in the code so we can fuzz it. +echo "" >> $SRC/openvpn/src/openvpn/openvpn.c +echo "ssize_t fuzz_get_random_data(void *buf, size_t len) { return 0; }" >> $SRC/openvpn/src/openvpn/fake_fuzz_header.h +echo "#include \"fake_fuzz_header.h\"" >> $SRC/openvpn/src/openvpn/openvpn.c + +sed -i 's/read(/fuzz_read(/g' ./src/openvpn/console_systemd.c +sed -i 's/fgets(/fuzz_fgets(/g' ./src/openvpn/console_builtin.c +sed -i 's/fgets(/fuzz_fgets(/g' ./src/openvpn/misc.c +sed -i 's/, tz/, (struct timezone*)tz/g' ./src/openvpn/otime.h +sed -i 's/#include "forward.h"/#include "fuzz_header.h"\n#include "forward.h"/g' ./src/openvpn/proxy.c +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 + +# Build openvpn autoreconf -ivf ./configure --disable-lz4 -make V=1 +make + +# Make openvpn object files into a library we can link fuzzers to cd src/openvpn rm openvpn.o ar r libopenvpn.a *.o + +# Compile the fuzzers for fuzzname in fuzz_base64 fuzz_dhcp fuzz_proxy fuzz_misc fuzz_buffer; do - $CXX -I../../src/compat/ -I../../ -I./ $CXXFLAGS -c $SRC/${fuzzname}.cpp -o ${fuzzname}.o + $CXX -I../../src/compat/ -I../../ -I./ $CXXFLAGS -c $SRC/${fuzzname}.cpp -o ${fuzzname}.o $CXX ${CXXFLAGS} ${LIB_FUZZING_ENGINE} ./${fuzzname}.o -o $OUT/${fuzzname} \ 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 \ diff --git a/projects/openvpn/fuzz.h b/projects/openvpn/fuzz.h new file mode 100644 index 000000000..10e33c3ee --- /dev/null +++ b/projects/openvpn/fuzz.h @@ -0,0 +1,47 @@ +/* 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 + +// Returns a NULL-terminated C string that should be freed by the caller. +char *get_modifiable_string(FuzzedDataProvider &provider) { + std::string s1 = provider.ConsumeRandomLengthString(); + char *tmp = (char *)malloc(s1.size() + 1); + memcpy(tmp, s1.c_str(), s1.size()); + tmp[s1.size()] = '\0'; + return tmp; +} + +FuzzedDataProvider *prov = NULL; + + +extern "C" ssize_t fuzz_get_random_data(void *buf, size_t len) { + size_t ret_val; + char *cbuf = (char*)buf; + + if (prov->remaining_bytes() == 0) { + return -1; + } + + double prob = prov->ConsumeProbability(); + if (prob < 0.05) { + return 0; + } + + if (len == 1) { + ret_val = prov->ConsumeData(buf, 1); + return ret_val; + } + ret_val = prov->ConsumeData(buf, len); + return ret_val; +} + diff --git a/projects/openvpn/fuzz_base64.cpp b/projects/openvpn/fuzz_base64.cpp index 48838acd3..83f40c3eb 100644 --- a/projects/openvpn/fuzz_base64.cpp +++ b/projects/openvpn/fuzz_base64.cpp @@ -13,6 +13,8 @@ limitations under the License. #include #include +#include "fuzz.h" + extern "C" { #include "base64.h" } diff --git a/projects/openvpn/fuzz_buffer.cpp b/projects/openvpn/fuzz_buffer.cpp index 1d0a8322c..d99a64c11 100644 --- a/projects/openvpn/fuzz_buffer.cpp +++ b/projects/openvpn/fuzz_buffer.cpp @@ -9,7 +9,8 @@ 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 + +#include "fuzz.h" extern "C" { #include "config.h" @@ -18,15 +19,6 @@ extern "C" { #include "buffer.h" } -// Returns a NULL-terminated C string that should be freed by the caller. -char *get_modifiable_string(FuzzedDataProvider &provider) { - std::string s1 = provider.ConsumeRandomLengthString(); - char *tmp = (char *)malloc(s1.size() + 1); - memcpy(tmp, s1.c_str(), s1.size()); - tmp[s1.size()] = '\0'; - return tmp; -} - extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { FuzzedDataProvider provider(data, size); diff --git a/projects/openvpn/fuzz_dhcp.cpp b/projects/openvpn/fuzz_dhcp.cpp index ebaaf5f3d..36ac5d91c 100644 --- a/projects/openvpn/fuzz_dhcp.cpp +++ b/projects/openvpn/fuzz_dhcp.cpp @@ -13,6 +13,8 @@ limitations under the License. #include #include +#include "fuzz.h" + extern "C" { #include "config.h" #include "syshead.h" diff --git a/projects/openvpn/fuzz_header.h b/projects/openvpn/fuzz_header.h new file mode 100644 index 000000000..5bfaf8331 --- /dev/null +++ b/projects/openvpn/fuzz_header.h @@ -0,0 +1,46 @@ +/* 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 +#include + +// Forward declared because we want to use FuzzedDataProvider, +// which requires CPP. +extern ssize_t fuzz_get_random_data(void *buf, size_t len); +ssize_t fuzz_recv(int sockfd, void *buf, size_t len, int flags){ + return fuzz_get_random_data(buf, len); +} + +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') { + s[0] = 'A'; + } + s[size-1] = '\0'; + return s; +} + + +int fuzz_select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout) { + char val; + ssize_t c = fuzz_get_random_data(&val, 1); + return c; +} + + +ssize_t fuzz_send(int sockfd, const void *buf, size_t len, int flags) { + return len; +} diff --git a/projects/openvpn/fuzz_misc.cpp b/projects/openvpn/fuzz_misc.cpp index 67025797b..1340938aa 100644 --- a/projects/openvpn/fuzz_misc.cpp +++ b/projects/openvpn/fuzz_misc.cpp @@ -9,7 +9,8 @@ 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 + +#include "fuzz.h" extern "C" { #include "config.h" diff --git a/projects/openvpn/fuzz_proxy.cpp b/projects/openvpn/fuzz_proxy.cpp index 270cbb54c..26fbfef43 100644 --- a/projects/openvpn/fuzz_proxy.cpp +++ b/projects/openvpn/fuzz_proxy.cpp @@ -9,31 +9,59 @@ 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 + +#include "fuzz.h" extern "C" { +#include #include "config.h" #include "syshead.h" #include "proxy.h" +#include "interval.h" #include #include } + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + CRYPTO_malloc_init(); + SSL_library_init(); + ERR_load_crypto_strings(); + + OpenSSL_add_all_algorithms(); + OpenSSL_add_ssl_algorithms(); + OpenSSL_add_all_digests(); + + SSL_load_error_strings(); + return 1; +} + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *tmp = NULL; + char *tmp2 = NULL; + + if (size < 500) { + return 0; + } FuzzedDataProvider provider(data, size); + prov = &provider; struct gc_arena gc = gc_new(); struct http_proxy_info pi; ssize_t generic_ssizet; int signal_received = 0; struct buffer lookahead = alloc_buf(1024); + struct event_timeout evt; + memset(&evt, 0, sizeof(struct event_timeout)); + memset(&pi, 0, sizeof(struct http_proxy_info)); memset(&pi, 0, sizeof(pi)); - pi.proxy_authenticate = NULL; generic_ssizet = 0; std::string username = provider.ConsumeBytesAsString( - (provider.ConsumeIntegralInRange(1, 100))); + (provider.ConsumeIntegralInRange(1, USER_PASS_LEN))); strcpy(pi.up.username, username.c_str()); if (strlen(pi.up.username) == 0) { gc_free(&gc); @@ -42,7 +70,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { } std::string pass = provider.ConsumeBytesAsString( - (provider.ConsumeIntegralInRange(1, 100))); + (provider.ConsumeIntegralInRange(1, USER_PASS_LEN))); strcpy(pi.up.password, pass.c_str()); if (strlen(pi.up.password) == 0) { gc_free(&gc); @@ -56,16 +84,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { pi.auth_method = HTTP_AUTH_NONE; break; case 1: - pi.auth_method = HTTP_AUTH_NONE; - // pi.auth_method = HTTP_AUTH_BASIC; + pi.auth_method = HTTP_AUTH_BASIC; break; case 2: - // pi.auth_method = HTTP_AUTH_DIGEST; - pi.auth_method = HTTP_AUTH_NONE; + pi.auth_method = HTTP_AUTH_DIGEST; break; case 3: - // pi.auth_method = HTTP_AUTH_NTLM; - pi.auth_method = HTTP_AUTH_NONE; + pi.auth_method = HTTP_AUTH_NTLM; break; case 4: pi.auth_method = HTTP_AUTH_NTLM2; @@ -86,16 +111,26 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { break; } - std::string proxy_authenticate = provider.ConsumeBytesAsString( - (provider.ConsumeIntegralInRange(1, 100))); - char *tmp_authenticate = (char *)malloc(proxy_authenticate.size()); - memcpy(tmp_authenticate, proxy_authenticate.c_str(), - proxy_authenticate.size()); + char *tmp_authenticate = get_modifiable_string(provider); pi.proxy_authenticate = tmp_authenticate; - establish_http_proxy_passthru(&pi, 0, "1.2.3.4", "777", NULL, &lookahead, + + if (provider.ConsumeProbability() < 0.5) { + + tmp = get_modifiable_string(provider); + pi.options.custom_headers[0].name = tmp; + if (provider.ConsumeProbability() < 0.5) { + tmp2 = get_modifiable_string(provider); + pi.options.custom_headers[0].content = tmp2; + } + } + + establish_http_proxy_passthru(&pi, 0, "1.2.3.4", "777", &evt, &lookahead, &signal_received); free(pi.proxy_authenticate); gc_free(&gc); free_buf(&lookahead); + + if (tmp != NULL) free(tmp); + if (tmp2 != NULL) free(tmp2); return 0; }