From c2d9a153cb0f4e3a3f86bb9eec47c5e61fa1561a Mon Sep 17 00:00:00 2001 From: Felipe Zimmerle Date: Tue, 22 Dec 2015 19:21:57 -0300 Subject: [PATCH] Adds support to afl fuzzer in the build system --- configure.ac | 51 +++- examples/simple_example_using_c/Makefile.am | 9 +- src/Makefile.am | 1 + test/Makefile.am | 13 +- test/benchmark/Makefile.am | 1 + test/fuzzer/Makefile.am | 37 +++ test/fuzzer/afl_fuzzer.cc | 264 ++++++++++++++++++++ 7 files changed, 370 insertions(+), 6 deletions(-) create mode 100644 test/fuzzer/Makefile.am create mode 100644 test/fuzzer/afl_fuzzer.cc diff --git a/configure.ac b/configure.ac index 81a5c95f..d19d3d63 100644 --- a/configure.ac +++ b/configure.ac @@ -218,6 +218,20 @@ if test "$debugLogs" != "true"; then fi +# Fuzzer +AC_ARG_ENABLE(afl-fuzz, + [AC_HELP_STRING([--enable-afl-fuzz],[Turn on the afl fuzzer compilation utilities])], + + [case "${enableval}" in + yes) aflFuzzer=true ;; + no) aflFuzzer=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-afl-fuzz) ;; + esac], + + [aflFuzzer=false] + ) + + # Decide if we want to build the tests or not. buildTestUtilities=false if test "x$YAJL_FOUND" = "x1"; then @@ -234,10 +248,21 @@ AM_CONDITIONAL([TEST_UTILITIES], [test $buildTestUtilities = true]) # General link options if test "$PLATFORM" != "MacOSX"; then - GLOBAL_LDADD="-lrt" - AC_SUBST(GLOBAL_LDADD) + GLOBAL_LDADD="-lrt " fi +if test "$aflFuzzer" == "true"; then + FUZZ_CPPCFLAGS="-fsanitize=address -fsanitize-coverage=edge,indirect-calls,8bit-counters " + GLOBAL_LDADD="$GLOBAL_LDADD -fsanitize=address " + GLOBAL_CPPFLAGS="$GLOBAL_CPPFLAGS $FUZZ_CPPCFLAGS" +fi +AC_SUBST(GLOBAL_LDADD) +AC_SUBST(GLOBAL_CPPFLAGS) + +AM_CONDITIONAL([AFL_FUZZER], [test $aflFuzzer = true]) + +GLOBAL_CFLAGS="" +AC_SUBST(GLOBAL_CFLAGS) # Files to be generated via autotools. AC_CONFIG_FILES([\ @@ -247,6 +272,7 @@ AC_CONFIG_FILES([\ others/Makefile \ test/Makefile \ test/benchmark/Makefile \ + test/fuzzer/Makefile \ examples/Makefile \ examples/simple_example_using_c/Makefile \ ]) @@ -373,4 +399,25 @@ else echo " + SecDebugLog ....disabled" fi +if test "$aflFuzzer" = "true"; then + echo " + afl fuzzer ....enabled" + echo " ($FUZZ_CPPCFLAGS)" +else + echo " + afl fuzzer ....disabled" +fi + echo " " + + +if test "$aflFuzzer" = "true"; then + echo "WARNING: afl fuzzer was enabled. Make sure you are using the" + echo " 'afl-clang-fast' as the compiler, otherwise the compilation" + echo " will fail." + echo " " + echo " You can set the compiler using:" + echo " " + echo " $ export CXX=afl-clang-fast++ " + echo " $ export CC=afl-clang-fast " + echo " " +fi + diff --git a/examples/simple_example_using_c/Makefile.am b/examples/simple_example_using_c/Makefile.am index 7d92396a..4f166817 100644 --- a/examples/simple_example_using_c/Makefile.am +++ b/examples/simple_example_using_c/Makefile.am @@ -6,11 +6,16 @@ test_SOURCES = \ test.c test_LDADD = \ - -L$(top_builddir)/src/.libs/ -lmodsecurity $(YAJL_LDFLAGS) $(GEOIP_LDFLAGS) + -L$(top_builddir)/src/.libs/ \ + -lmodsecurity \ + $(YAJL_LDFLAGS) \ + $(GEOIP_LDFLAGS) \ + $(GLOBAL_LDADD) test_CFLAGS = \ -I$(top_builddir)/headers \ - -I$(top_builddir) + -I$(top_builddir) \ + $(GLOBAL_CFLAGS) MAINTAINERCLEANFILES = \ Makefile.in diff --git a/src/Makefile.am b/src/Makefile.am index eee795a5..630af92b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -211,6 +211,7 @@ libmodsecurity_la_CPPFLAGS = \ -O3 \ -I ../headers \ $(GEOIP_CFLAGS) \ + $(GLOBAL_CPPFLAGS) \ $(MODSEC_NO_LOGS) \ $(PCRE_CFLAGS) diff --git a/test/Makefile.am b/test/Makefile.am index 30d89fa1..8d5be76a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,7 +1,13 @@ +if AFL_FUZZER +export MAYBE_AFL_FUZZER = fuzzer +endif + SUBDIRS = \ - benchmark + benchmark \ + $(MAYBE_AFL_FUZZER) + # make clean CLEANFILES = @@ -11,7 +17,6 @@ MAINTAINERCLEANFILES = \ Makefile.in - bin_PROGRAMS = unit-tests regression-tests rules-optimization @@ -39,6 +44,7 @@ unit_tests_CPPFLAGS = \ $(CURL_CFLAGS) \ $(MODSEC_NO_LOGS) \ $(GEOIP_CFLAGS) \ + $(GLOBAL_CPPFLAGS) \ $(PCRE_CFLAGS) \ $(YAJL_CFLAGS) @@ -68,6 +74,7 @@ regression_tests_CPPFLAGS = \ $(CURL_CFLAGS) \ $(MODSEC_NO_LOGS) \ $(GEOIP_CFLAGS) \ + $(GLOBAL_CPPFLAGS) \ $(PCRE_CFLAGS) \ $(YAJL_CFLAGS) @@ -95,5 +102,7 @@ rules_optimization_CPPFLAGS = \ $(CURL_CFLAGS) \ $(MODSEC_NO_LOGS) \ $(GEOIP_CFLAGS) \ + $(GLOBAL_CPPFLAGS) \ $(PCRE_CFLAGS) \ $(YAJL_CFLAGS) + diff --git a/test/benchmark/Makefile.am b/test/benchmark/Makefile.am index b3066d6f..a7bfe304 100644 --- a/test/benchmark/Makefile.am +++ b/test/benchmark/Makefile.am @@ -16,6 +16,7 @@ benchmark_LDADD = \ benchmark_CPPFLAGS = \ -std=c++11 \ -I$(top_builddir)/headers \ + $(GLOBAL_CPPFLAGS) \ $(PCRE_CFLAGS) diff --git a/test/fuzzer/Makefile.am b/test/fuzzer/Makefile.am new file mode 100644 index 00000000..1350caea --- /dev/null +++ b/test/fuzzer/Makefile.am @@ -0,0 +1,37 @@ + + +# make clean +CLEANFILES = + +# make maintainer-clean +MAINTAINERCLEANFILES = \ + Makefile.in + + +bin_PROGRAMS = afl_fuzzer + +afl_fuzzer_SOURCES = \ + afl_fuzzer.cc + +afl_fuzzer_LDADD = \ + $(GLOBAL_LDADD) \ + $(top_builddir)/src/.libs/libmodsecurity.a \ + $(CURL_LDADD) \ + $(GEOIP_LDFLAGS) $(GEOIP_LDADD) \ + $(PCRE_LDADD) \ + $(YAJL_LDFLAGS) $(YAJL_LDADD) + + +afl_fuzzer_CPPFLAGS = \ + -std=c++11 \ + -Icommon \ + -I../ \ + -O0 \ + -g \ + -I$(top_builddir)/headers \ + $(CURL_CFLAGS) \ + $(MODSEC_NO_LOGS) \ + $(GEOIP_CFLAGS) \ + $(GLOBAL_CPPFLAGS) \ + $(PCRE_CFLAGS) \ + $(YAJL_CFLAGS) diff --git a/test/fuzzer/afl_fuzzer.cc b/test/fuzzer/afl_fuzzer.cc new file mode 100644 index 00000000..9c913fa6 --- /dev/null +++ b/test/fuzzer/afl_fuzzer.cc @@ -0,0 +1,264 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * 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 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + + +#include + +#include "modsecurity/modsecurity.h" +#include "actions/transformations/transformation.h" + +/** + * for i in $(ls -l *h | awk {'print $9'}); do echo "#include \"actions/transformations/$i\""; done; + * + */ +#include "actions/transformations/base64_decode_ext.h" +#include "actions/transformations/base64_decode.h" +#include "actions/transformations/cmd_line.h" +#include "actions/transformations/compress_whitespace.h" +#include "actions/transformations/css_decode.h" +#include "actions/transformations/escape_seq_decode.h" +#include "actions/transformations/hex_decode.h" +#include "actions/transformations/hex_encode.h" +#include "actions/transformations/html_entity_decode.h" +#include "actions/transformations/js_decode.h" +#include "actions/transformations/length.h" +#include "actions/transformations/lower_case.h" +#include "actions/transformations/md5.h" +#include "actions/transformations/none.h" +#include "actions/transformations/normalise_path.h" +#include "actions/transformations/normalise_path_win.h" +#include "actions/transformations/parity_even_7bit.h" +#include "actions/transformations/parity_odd_7bit.h" +#include "actions/transformations/parity_zero_7bit.h" +#include "actions/transformations/remove_comments_char.h" +#include "actions/transformations/remove_comments.h" +#include "actions/transformations/remove_nulls.h" +#include "actions/transformations/remove_whitespace.h" +#include "actions/transformations/replace_comments.h" +#include "actions/transformations/replace_nulls.h" +#include "actions/transformations/sha1.h" +#include "actions/transformations/sql_hex_decode.h" +#include "actions/transformations/transformation.h" +#include "actions/transformations/trim.h" +#include "actions/transformations/trim_left.h" +#include "actions/transformations/trim_right.h" +#include "actions/transformations/url_decode.h" +#include "actions/transformations/url_decode_uni.h" +#include "actions/transformations/url_encode.h" +#include "actions/transformations/utf8_to_unicode.h" + + +/** + * for i in $(ls -l *h | awk {'print $9'}); do echo "#include \"operators/$i\""; done; + * + */ +#include "operators/begins_with.h" +#include "operators/contains.h" +#include "operators/contains_word.h" +#include "operators/detect_sqli.h" +#include "operators/detect_xss.h" +#include "operators/ends_with.h" +#include "operators/eq.h" +#include "operators/fuzzy_hash.h" +#include "operators/ge.h" +#include "operators/geo_lookup.h" +#include "operators/gsblookup.h" +#include "operators/gt.h" +#include "operators/inspect_file.h" +#include "operators/ip_match_f.h" +#include "operators/ip_match_from_file.h" +#include "operators/ip_match.h" +#include "operators/le.h" +#include "operators/lt.h" +#include "operators/no_match.h" +#include "operators/operator.h" +#include "operators/pm_f.h" +#include "operators/pm_from_file.h" +#include "operators/pm.h" +#include "operators/rbl.h" +#include "operators/rsub.h" +#include "operators/rx.h" +#include "operators/str_eq.h" +#include "operators/str_match.h" +#include "operators/validate_byte_range.h" +#include "operators/validate_dtd.h" +#include "operators/validate_hash.h" +#include "operators/validate_schema.h" +#include "operators/validate_url_encoding.h" +#include "operators/validate_utf8_encoding.h" +#include "operators/verify_cc.h" +#include "operators/verify_cpf.h" +#include "operators/verify_ssn.h" +#include "operators/within.h" + + +using namespace modsecurity::actions::transformations; +using namespace modsecurity::operators; +using namespace modsecurity; + +#include +#include +#include +#include +#include + + +int main(int argc, char** argv) { + size_t count; + uint8_t buf[128]; + ssize_t read_bytes; + + std::string lastString; + + while (__AFL_LOOP(1000)) { + // (re-) initialize the library and read new input + read_bytes = -1; + memset(buf, 0, 128); + read_bytes = read(STDIN_FILENO, buf, 128); + + std::string currentString = std::string(read_bytes, 128); + std::string s = currentString; + std::string z = lastString; + + ModSecurity *ms = new ModSecurity(); + Rules *rules = new Rules(); + Assay *assay = new Assay(ms, rules, NULL); + + + /** + * Transformations, generated by: + * + * for i in $(grep "class " -Ri * | grep " :" | grep -v "InstantCache" | awk {'print $2'}); do echo $i *$(echo $i | awk '{print tolower($0)}') = new $i\(\"$i\"\)\; $(echo $i | awk '{print tolower($0)}')-\>evaluate\(s, NULL\)\; delete $(echo $i | awk '{print tolower($0)}')\;; done; + * + */ +#if 1 +Base64DecodeExt *base64decodeext = new Base64DecodeExt("Base64DecodeExt"); base64decodeext->evaluate(s, NULL); delete base64decodeext; +Base64Decode *base64decode = new Base64Decode("Base64Decode"); base64decode->evaluate(s, NULL); delete base64decode; +CmdLine *cmdline = new CmdLine("CmdLine"); cmdline->evaluate(s, NULL); delete cmdline; +CompressWhitespace *compresswhitespace = new CompressWhitespace("CompressWhitespace"); compresswhitespace->evaluate(s, NULL); delete compresswhitespace; +CssDecode *cssdecode = new CssDecode("CssDecode"); cssdecode->evaluate(s, NULL); delete cssdecode; +EscapeSeqDecode *escapeseqdecode = new EscapeSeqDecode("EscapeSeqDecode"); escapeseqdecode->evaluate(s, NULL); delete escapeseqdecode; +HexDecode *hexdecode = new HexDecode("HexDecode"); hexdecode->evaluate(s, NULL); delete hexdecode; +HexEncode *hexencode = new HexEncode("HexEncode"); hexencode->evaluate(s, NULL); delete hexencode; +//HtmlEntityDecode *htmlentitydecode = new HtmlEntityDecode("HtmlEntityDecode"); htmlentitydecode->evaluate(s, NULL); delete htmlentitydecode; +JsDecode *jsdecode = new JsDecode("JsDecode"); jsdecode->evaluate(s, NULL); delete jsdecode; +Length *length = new Length("Length"); length->evaluate(s, NULL); delete length; +LowerCase *lowercase = new LowerCase("LowerCase"); lowercase->evaluate(s, NULL); delete lowercase; +Md5 *md5 = new Md5("Md5"); md5->evaluate(s, NULL); delete md5; +None *none = new None("None"); none->evaluate(s, NULL); delete none; +NormalisePath *normalisepath = new NormalisePath("NormalisePath"); normalisepath->evaluate(s, NULL); delete normalisepath; +NormalisePathWin *normalisepathwin = new NormalisePathWin("NormalisePathWin"); normalisepathwin->evaluate(s, NULL); delete normalisepathwin; +ParityEven7bit *parityeven7bit = new ParityEven7bit("ParityEven7bit"); parityeven7bit->evaluate(s, NULL); delete parityeven7bit; +ParityOdd7bit *parityodd7bit = new ParityOdd7bit("ParityOdd7bit"); parityodd7bit->evaluate(s, NULL); delete parityodd7bit; +ParityZero7bit *parityzero7bit = new ParityZero7bit("ParityZero7bit"); parityzero7bit->evaluate(s, NULL); delete parityzero7bit; +RemoveCommentsChar *removecommentschar = new RemoveCommentsChar("RemoveCommentsChar"); removecommentschar->evaluate(s, NULL); delete removecommentschar; +RemoveComments *removecomments = new RemoveComments("RemoveComments"); removecomments->evaluate(s, NULL); delete removecomments; +RemoveNulls *removenulls = new RemoveNulls("RemoveNulls"); removenulls->evaluate(s, NULL); delete removenulls; +RemoveWhitespace *removewhitespace = new RemoveWhitespace("RemoveWhitespace"); removewhitespace->evaluate(s, NULL); delete removewhitespace; +ReplaceComments *replacecomments = new ReplaceComments("ReplaceComments"); replacecomments->evaluate(s, NULL); delete replacecomments; +ReplaceNulls *replacenulls = new ReplaceNulls("ReplaceNulls"); replacenulls->evaluate(s, NULL); delete replacenulls; +Sha1 *sha1 = new Sha1("Sha1"); sha1->evaluate(s, NULL); delete sha1; +SqlHexDecode *sqlhexdecode = new SqlHexDecode("SqlHexDecode"); sqlhexdecode->evaluate(s, NULL); delete sqlhexdecode; +Transformation *transformation = new Transformation("Transformation"); transformation->evaluate(s, NULL); delete transformation; +Trim *trim = new Trim("Trim"); trim->evaluate(s, NULL); delete trim; +TrimLeft *trimleft = new TrimLeft("TrimLeft"); trimleft->evaluate(s, NULL); delete trimleft; +TrimRight *trimright = new TrimRight("TrimRight"); trimright->evaluate(s, NULL); delete trimright; +UrlDecode *urldecode = new UrlDecode("UrlDecode"); urldecode->evaluate(s, NULL); delete urldecode; +//UrlDecodeUni *urldecodeuni = new UrlDecodeUni("UrlDecodeUni"); urldecodeuni->evaluate(s, NULL); delete urldecodeuni; +UrlEncode *urlencode = new UrlEncode("UrlEncode"); urlencode->evaluate(s, NULL); delete urlencode; +Utf8Unicode *utf8unicode = new Utf8Unicode("Utf8Unicode"); utf8unicode->evaluate(s, NULL); delete utf8unicode; +#endif + + + /** + * Operators, generated by: + * + * for i in $(grep "class " -Ri * | grep " :" | grep -v "InstantCache" | awk {'print $2'}); do echo $i *$(echo $i | awk '{print tolower($0)}') = new $i\(\"$i\"\)\; $(echo $i | awk '{print tolower($0)}')-\>evaluate\(s, NULL\)\; delete $(echo $i | awk '{print tolower($0)}')\;; done; + * + */ +#if 1 +BeginsWith *beginswith = new BeginsWith("@BeginsWith", z, false); beginswith->evaluate(assay, s); delete beginswith; +Contains *contains = new Contains("@Contains", z, false); contains->evaluate(assay, s); delete contains; +ContainsWord *containsword = new ContainsWord("@ContainsWord", z, false); containsword->evaluate(assay, s); delete containsword; +DetectSQLi *detectsqli = new DetectSQLi("@DetectSQLi", z, false); detectsqli->evaluate(assay, s); delete detectsqli; +DetectXSS *detectxss = new DetectXSS("@DetectXSS", z, false); detectxss->evaluate(assay, s); delete detectxss; +EndsWith *endswith = new EndsWith("@EndsWith", z, false); endswith->evaluate(assay, s); delete endswith; +Eq *eq = new Eq("@Eq", z, false); eq->evaluate(assay, s); delete eq; +FuzzyHash *fuzzyhash = new FuzzyHash("@FuzzyHash", z, false); fuzzyhash->evaluate(assay, s); delete fuzzyhash; +Ge *ge = new Ge("@Ge", z, false); ge->evaluate(assay, s); delete ge; +GeoLookup *geolookup = new GeoLookup("@GeoLookup", z, false); geolookup->evaluate(assay, s); delete geolookup; +GsbLookup *gsblookup = new GsbLookup("@GsbLookup", z, false); gsblookup->evaluate(assay, s); delete gsblookup; +Gt *gt = new Gt("@Gt", z, false); gt->evaluate(assay, s); delete gt; +InspectFile *inspectfile = new InspectFile("@InspectFile", z, false); inspectfile->evaluate(assay, s); delete inspectfile; +IpMatchF *ipmatchf = new IpMatchF("@IpMatchF", z, false); ipmatchf->evaluate(assay, s); delete ipmatchf; +IpMatchFromFile *ipmatchfromfile = new IpMatchFromFile("@IpMatchFromFile", z, false); ipmatchfromfile->evaluate(assay, s); delete ipmatchfromfile; +IpMatch *ipmatch = new IpMatch("@IpMatch", z, false); ipmatch->evaluate(assay, s); delete ipmatch; +Le *le = new Le("@Le", z, false); le->evaluate(assay, s); delete le; +Lt *lt = new Lt("@Lt", z, false); lt->evaluate(assay, s); delete lt; +NoMatch *nomatch = new NoMatch("@NoMatch", z, false); nomatch->evaluate(assay, s); delete nomatch; +PmF *pmf = new PmF("@PmF", z, false); pmf->evaluate(assay, s); delete pmf; +PmFromFile *pmfromfile = new PmFromFile("@PmFromFile", z, false); pmfromfile->evaluate(assay, s); delete pmfromfile; +Pm *pm = new Pm("@Pm", z, false); pm->evaluate(assay, s); delete pm; +Rbl *rbl = new Rbl("@Rbl", z, false); rbl->evaluate(assay, s); delete rbl; +Rsub *rsub = new Rsub("@Rsub", z, false); rsub->evaluate(assay, s); delete rsub; +Rx *rx = new Rx("@Rx", z, false); rx->evaluate(assay, s); delete rx; +StrEq *streq = new StrEq("@StrEq", z, false); streq->evaluate(assay, s); delete streq; +StrMatch *strmatch = new StrMatch("@StrMatch", z, false); strmatch->evaluate(assay, s); delete strmatch; +ValidateByteRange *validatebyterange = new ValidateByteRange("@ValidateByteRange", z, false); validatebyterange->evaluate(assay, s); delete validatebyterange; +ValidateDTD *validatedtd = new ValidateDTD("@ValidateDTD", z, false); validatedtd->evaluate(assay, s); delete validatedtd; +ValidateHash *validatehash = new ValidateHash("@ValidateHash", z, false); validatehash->evaluate(assay, s); delete validatehash; +ValidateSchema *validateschema = new ValidateSchema("@ValidateSchema", z, false); validateschema->evaluate(assay, s); delete validateschema; +ValidateUrlEncoding *validateurlencoding = new ValidateUrlEncoding("@ValidateUrlEncoding", z, false); validateurlencoding->evaluate(assay, s); delete validateurlencoding; +ValidateUtf8Encoding *validateutf8encoding = new ValidateUtf8Encoding("@ValidateUtf8Encoding", z, false); validateutf8encoding->evaluate(assay, s); delete validateutf8encoding; +VerifyCC *verifycc = new VerifyCC("@VerifyCC", z, false); verifycc->evaluate(assay, s); delete verifycc; +VerifyCPF *verifycpf = new VerifyCPF("@VerifyCPF", z, false); verifycpf->evaluate(assay, s); delete verifycpf; +VerifySSN *verifyssn = new VerifySSN("@VerifySSN", z, false); verifyssn->evaluate(assay, s); delete verifyssn; +Within *within = new Within("@Within", z, false); within->evaluate(assay, s); delete within; +#endif + + + /** + * ModSec API + * + */ +#if 0 + assay->processConnection(s.c_str(), 123, s.c_str(), 123); + assay->processURI(s.c_str(), z.c_str(), z.c_str()); + assay->addRequestHeader(s, z); + assay->addRequestHeader(s, s); + assay->addRequestHeader(z, z); + assay->addRequestHeader(z, s); + assay->processRequestHeaders(); + assay->appendRequestBody((const unsigned char *)s.c_str(), s.length()); + assay->processRequestBody(); + assay->addResponseHeader(s, z); + assay->addResponseHeader(s, s); + assay->addResponseHeader(z, z); + assay->addResponseHeader(z, s); + assay->processResponseHeaders(); + assay->appendResponseBody((const unsigned char *)s.c_str(), s.length()); + assay->processResponseBody(); +#endif + + + delete assay; + delete rules; + delete ms; + + lastString = currentString; + } + return 0; +} +