Openvpn: improve proxy fuzzer and fuzzing infrastructure (#5971)

* openvpn: improve buffer fuzzer.

* Improve state of openvpn fuzzing, the proxy fuzzer in particular.
This commit is contained in:
DavidKorczynski 2021-06-25 17:01:09 +01:00 committed by GitHub
parent 13a076b275
commit 01765e6f2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 174 additions and 29 deletions

View File

@ -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/

View File

@ -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 \

47
projects/openvpn/fuzz.h Normal file
View File

@ -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 <fuzzer/FuzzedDataProvider.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;
}
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<double>();
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;
}

View File

@ -13,6 +13,8 @@ limitations under the License.
#include <stdlib.h>
#include <string.h>
#include "fuzz.h"
extern "C" {
#include "base64.h"
}

View File

@ -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 <fuzzer/FuzzedDataProvider.h>
#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);

View File

@ -13,6 +13,8 @@ limitations under the License.
#include <stdlib.h>
#include <string.h>
#include "fuzz.h"
extern "C" {
#include "config.h"
#include "syshead.h"

View File

@ -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 <sys/types.h>
#include <sys/socket.h>
// 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;
}

View File

@ -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 <fuzzer/FuzzedDataProvider.h>
#include "fuzz.h"
extern "C" {
#include "config.h"

View File

@ -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 <fuzzer/FuzzedDataProvider.h>
#include "fuzz.h"
extern "C" {
#include <sys/time.h>
#include "config.h"
#include "syshead.h"
#include "proxy.h"
#include "interval.h"
#include <openssl/err.h>
#include <openssl/ssl.h>
}
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<uint32_t>(1, 100)));
(provider.ConsumeIntegralInRange<uint32_t>(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<uint32_t>(1, 100)));
(provider.ConsumeIntegralInRange<uint32_t>(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<uint32_t>(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<double>() < 0.5) {
tmp = get_modifiable_string(provider);
pi.options.custom_headers[0].name = tmp;
if (provider.ConsumeProbability<double>() < 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;
}