about summary refs log tree commit diff
path: root/src/afl-fuzz-redqueen.c
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2021-01-20 13:51:57 +0100
committervan Hauser <vh@thc.org>2021-01-20 13:51:57 +0100
commitf380487bb49a66b1fac513cad344f1be5df10959 (patch)
treebc4544b4f38a39134d01b9ca60422131caf124d2 /src/afl-fuzz-redqueen.c
parent9dff3495d54c0bd3da59ef43ca25df06c6d9f2c2 (diff)
downloadafl++-f380487bb49a66b1fac513cad344f1be5df10959.tar.gz
wip
Diffstat (limited to 'src/afl-fuzz-redqueen.c')
-rw-r--r--src/afl-fuzz-redqueen.c518
1 files changed, 452 insertions, 66 deletions
diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c
index bf41863e..7ac0290a 100644
--- a/src/afl-fuzz-redqueen.c
+++ b/src/afl-fuzz-redqueen.c
@@ -517,6 +517,149 @@ static int strntoull(const char *str, size_t sz, char **end, int base,
 
 }
 
+static u8 hex_table_up[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                              '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+static u8 hex_table_low[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                               '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+static u8 hex_table[] = {0, 1, 2, 3,  4,  5,  6,  7,  8,  9,  0,  0,  0, 0,
+                         0, 0, 0, 10, 11, 12, 13, 14, 15, 0,  0,  0,  0, 0,
+                         0, 0, 0, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0,
+                         0, 0, 0, 0,  0,  0,  0,  10, 11, 12, 13, 14, 15};
+
+// tests 2 bytes at location
+static int is_hex(const char *str) {
+
+  u32 i;
+
+  for (i = 0; i < 2; i++) {
+
+    switch (str[i]) {
+
+      case '0' ... '9':
+      case 'A' ... 'F':
+      case 'a' ... 'f':
+        break;
+      default:
+        return 0;
+
+    }
+
+  }
+
+  return 1;
+
+}
+
+// tests 4 bytes at location
+static int is_base64(const char *str) {
+
+  u32 i;
+
+  for (i = 0; i < 4; i++) {
+
+    switch (str[i]) {
+
+      case '0' ... '9':
+      case 'A' ... 'Z':
+      case 'a' ... 'z':
+      case '+':
+      case '/':
+        break;
+      default:
+        return 0;
+
+    }
+
+  }
+
+  return 1;
+
+}
+
+static u8 base64_encode_table[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static u8 base64_decode_table[] = {
+    62, 0,  0,  0,  63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0,
+    0,  0,  0,  0,  0,  0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
+    10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+    0,  0,  0,  0,  0,  0,  26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
+
+static u32 from_base64(u8 *src, u8 *dst, u32 dst_len) {
+
+  u32 i, j, v;
+  u32 len = ((dst_len / 3) << 2);
+  u32 ret = 0;
+
+  for (i = 0, j = 0; i < len; i += 4, j += 3) {
+
+    v = base64_decode_table[src[i] - 43];
+    v = (v << 6) | base64_decode_table[src[i + 1] - 43];
+    v = src[i + 2] == '=' ? v << 6
+                          : (v << 6) | base64_decode_table[src[i + 2] - 43];
+    v = src[i + 3] == '=' ? v << 6
+                          : (v << 6) | base64_decode_table[src[i + 3] - 43];
+
+    dst[j] = (v >> 16) & 0xFF;
+    ++ret;
+
+    if (src[i + 2] != '=') {
+
+      dst[j + 1] = (v >> 8) & 0xFF;
+      ++ret;
+
+    }
+
+    if (src[i + 3] != '=') {
+
+      dst[j + 2] = v & 0xFF;
+      ++ret;
+
+    }
+
+  }
+
+  return ret;
+
+}
+
+static void to_base64(u8 *src, u8 *dst, u32 dst_len) {
+
+  u32 i, j, v;
+  u32 len = (dst_len >> 2) * 3;
+
+  for (i = 0, j = 0; i < len; i += 3, j += 4) {
+
+    v = src[i];
+    v = i + 1 < len ? v << 8 | src[i + 1] : v << 8;
+    v = i + 2 < len ? v << 8 | src[i + 2] : v << 8;
+
+    dst[j] = base64_encode_table[(v >> 18) & 0x3F];
+    dst[j + 1] = base64_encode_table[(v >> 12) & 0x3F];
+    if (i + 1 < len) {
+
+      dst[j + 2] = base64_encode_table[(v >> 6) & 0x3F];
+
+    } else {
+
+      dst[j + 2] = '=';
+
+    }
+
+    if (i + 2 < len) {
+
+      dst[j + 3] = base64_encode_table[v & 0x3F];
+
+    } else {
+
+      dst[j + 3] = '=';
+
+    }
+
+  }
+
+}
+
 #endif
 
 static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
@@ -525,29 +668,6 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
                               u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len,
                               u8 do_reverse, u8 lvl, u8 *status) {
 
-  //  (void)(changed_val); // TODO
-  //  we can use the information in changed_val to see if there is a
-  //  computable i2s transformation.
-  //  if (pattern != o_pattern && repl != changed_val) {
-
-  //    u64 in_diff = pattern - o_pattern, out_diff = repl - changed_val;
-  //    if (in_diff != out_diff) {
-
-  //      switch(in_diff) {
-
-  //        detect uppercase <-> lowercase, base64, hex encoding, etc.:
-  //        repl = reverse_transform(TYPE, pattern);
-  //      }
-  //    }
-  //  }
-  //  not 100% but would have a chance to be detected
-
-  // fprintf(stderr,
-  //         "Encode: %llx->%llx into %llx(<-%llx) at idx=%u "
-  //         "taint_len=%u shape=%u attr=%u\n",
-  //         o_pattern, pattern, repl, changed_val, idx, taint_len,
-  //         h->shape + 1, attr);
-
   u64 *buf_64 = (u64 *)&buf[idx];
   u32 *buf_32 = (u32 *)&buf[idx];
   u16 *buf_16 = (u16 *)&buf[idx];
@@ -559,6 +679,12 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
 
   u32 its_len = MIN(len - idx, taint_len);
 
+  // fprintf(stderr,
+  //         "Encode: %llx->%llx into %llx(<-%llx) at idx=%u "
+  //         "taint_len=%u shape=%u attr=%u\n",
+  //         o_pattern, pattern, repl, changed_val, idx, taint_len,
+  //         h->shape + 1, attr);
+
 #ifdef TRANSFORM
   // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3
   if (lvl & LVL3) {
@@ -1581,71 +1707,215 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
 #ifndef COMBINE
   (void)(cbuf);
 #endif
+#ifndef TRANSFORM
+  (void)(changed_val);
+#endif
 
-  u32 i = 0;
+  u8  save[40];
+  u32 saved_idx = idx, pre, from = 0, to = 0, i;
   u32 its_len = MIN((u32)32, len - idx);
   its_len = MIN(its_len, taint_len);
-  u8 save[32];
-  memcpy(save, &buf[idx], its_len);
+  u32 saved_its_len = its_len;
 
-  if (lvl == LVL1) {
+  if (lvl & LVL3) {
 
-    for (i = 0; i < its_len; ++i) {
+    u32 max_to = MIN(4U, idx);
+    if (!(lvl & LVL1) && max_to) { from = 1; }
+    to = max_to;
 
-      if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) ||
-          *status == 1) {
+  }
 
-        break;
+  memcpy(save, &buf[saved_idx - to], its_len + to);
 
-      }
+#ifdef _DEBUG
+  fprintf(stderr, "RTN TRANSFORM idx=%u lvl=%02x", idx, lvl);
+  for (j = 0; j < 8; j++)
+    fprintf(stderr, "%02x", orig_buf[idx + j]);
+  fprintf(stderr, " -> ");
+  for (j = 0; j < 8; j++)
+    fprintf(stderr, "%02x", o_pattern[j]);
+  fprintf(stderr, " <= ");
+  for (j = 0; j < 8; j++)
+    fprintf(stderr, "%02x", repl[j]);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "            ");
+  for (j = 0; j < 8; j++)
+    fprintf(stderr, "%02x", buf[idx + j]);
+  fprintf(stderr, " -> ");
+  for (j = 0; j < 8; j++)
+    fprintf(stderr, "%02x", pattern[j]);
+  fprintf(stderr, " <= ");
+  for (j = 0; j < 8; j++)
+    fprintf(stderr, "%02x", changed_val[j]);
+  fprintf(stderr, "\n");
+#endif
 
-      buf[idx + i] = repl[i];
+  // Try to match the replace value up to 4 bytes before the current idx.
+  // This allows matching of eg.:
+  //   if (memcmp(user_val, "TEST") == 0)
+  //     if (memcmp(user_val, "TEST-VALUE") == 0) ...
+  // We only do this in lvl 3, otherwise we only do direct matching
 
-      if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+  for (pre = from; pre <= to; pre++) {
+
+    if (*status != 1 && (!pre || !memcmp(buf + saved_idx - pre, repl, pre))) {
+
+      idx = saved_idx - pre;
+      its_len = saved_its_len + pre;
+
+      for (i = 0; i < its_len; ++i) {
+
+        if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) ||
+            *status == 1) {
+
+          break;
+
+        }
+
+        buf[idx + i] = repl[i];
+
+        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
 
 #ifdef COMBINE
-      if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); }
+        if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); }
 #endif
 
+      }
+
+      memcpy(&buf[idx], save + to - pre, i);
+
     }
 
   }
 
+#ifdef TRANSFORM
+
   if (*status == 1) return 0;
 
   if (lvl & LVL3) {
 
-    u8  toupper = 0, tolower = 0, xor = 0, arith = 0;
-    u8  xor_val[32], arith_val[32];
     u32 j;
+    u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, tob64 = 0;
+    u32 fromhex = 0, fromb64 = 0;
+    u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_lf = 0,
+        from_cr = 0, from_up = 0;
+    u32 to_0 = 0, to_x = 0, to_slash = 0, to_lf = 0, to_cr = 0, to_up = 0;
+    u8  xor_val[32], arith_val[32], tmp[48];
+
+    idx = saved_idx;
+    its_len = saved_its_len;
+
+    memcpy(save, &buf[idx], its_len);
 
-#ifdef _DEBUG
-    fprintf(stderr, "RTN TRANSFORM idx=%u ", idx);
-    for (j = 0; j < 8; j++)
-      fprintf(stderr, "%02x", orig_buf[idx + j]);
-    fprintf(stderr, " -> ");
-    for (j = 0; j < 8; j++)
-      fprintf(stderr, "%02x", o_pattern[j]);
-    fprintf(stderr, " <= ");
-    for (j = 0; j < 8; j++)
-      fprintf(stderr, "%02x", repl[j]);
-    fprintf(stderr, "\n");
-    fprintf(stderr, "            ");
-    for (j = 0; j < 8; j++)
-      fprintf(stderr, "%02x", buf[idx + j]);
-    fprintf(stderr, " -> ");
-    for (j = 0; j < 8; j++)
-      fprintf(stderr, "%02x", pattern[j]);
-    fprintf(stderr, " <= ");
-    for (j = 0; j < 8; j++)
-      fprintf(stderr, "%02x", changed_val[j]);
-    fprintf(stderr, "\n");
-#endif
     for (i = 0; i < its_len; ++i) {
 
       xor_val[i] = pattern[i] ^ buf[idx + i];
       arith_val[i] = pattern[i] - buf[idx + i];
 
+      if (i == 0) {
+
+        if (orig_buf[idx] == '0') {
+
+          from_0 = 1;
+
+        } else if (orig_buf[idx] == '\\') {
+
+          from_slash = 1;
+
+        }
+
+        if (repl[0] == '0') {
+
+          to_0 = 1;
+
+        } else if (repl[0] == '\\') {
+
+          to_slash = 1;
+
+        }
+
+      } else if (i == 1) {
+
+        if (orig_buf[idx + 1] == 'x') {
+
+          from_x = 1;
+
+        } else if (orig_buf[idx + 1] == 'X') {
+
+          from_X = from_x = 1;
+
+        }
+
+        if (repl[1] == 'x' || repl[1] == 'X') { to_x = 1; }
+
+      } else {
+
+        if (orig_buf[idx + i] == '\n') { ++from_lf; }
+        if (orig_buf[idx + i] == '\r') { ++from_cr; }
+        if (repl[i] == '\n') { ++to_lf; }
+        if (repl[i] == '\r') { ++to_cr; }
+
+      }
+
+      if (i) {
+
+        if (!(i % 2)) {
+
+          if (i < 16 && is_hex(repl + (i << 1))) {
+
+            tohex += 2;
+
+            if (!to_up) {
+
+              if (repl[i] >= 'A' && repl[i] <= 'F')
+                to_up = 1;
+              else if (repl[i] >= 'a' && repl[i] <= 'f')
+                to_up = 2;
+              if (repl[i - 1] >= 'A' && repl[i - 1] <= 'F')
+                to_up = 1;
+              else if (repl[i - 1] >= 'a' && repl[i - 1] <= 'f')
+                to_up = 2;
+
+            }
+
+          }
+
+          if (len > idx + i && is_hex(orig_buf + idx + i)) {
+
+            fromhex += 2;
+
+            if (!from_up) {
+
+              if (orig_buf[idx + i] >= 'A' && orig_buf[idx + i] <= 'F')
+                from_up = 1;
+              else if (orig_buf[idx + i] >= 'a' && orig_buf[idx + i] <= 'f')
+                from_up = 2;
+              if (orig_buf[idx + i - 1] >= 'A' && orig_buf[idx + i - 1] <= 'F')
+                from_up = 1;
+              else if (orig_buf[idx + i - 1] >= 'a' &&
+                       orig_buf[idx + i - 1] <= 'f')
+                from_up = 2;
+
+            }
+
+          }
+
+        }
+
+        if (!(i % 3) && i + to_lf + to_cr < 24) {
+
+          if (is_base64(repl + i + to_lf + to_cr)) tob64 += 3;
+
+        }
+
+        if (!(i % 4) && i < 24) {
+
+          if (is_base64(orig_buf + idx + i)) fromb64 += 4;
+
+        }
+
+      }
+
       if ((o_pattern[i] ^ orig_buf[idx + i]) == xor_val[i] && xor_val[i]) {
 
         ++xor;
@@ -1672,10 +1942,124 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
 
       }
 
-#ifdef _DEBUG
-      fprintf(stderr, "RTN loop=%u %u %u %u %u (%u %u)\n", i, xor, arith,
-              tolower, toupper, arith_val[i], xor_val[i]);
-#endif
+      //  #ifdef _DEBUG
+      fprintf(
+          stderr,
+          "RTN loop=%u xor=%u arith=%u tolower=%u toupper=%u tohex=%u tob64=%u "
+          "fromhex=%u fromb64=%u to_0=%u to_slash=%u to_x=%u to_lf=%u to_cr=%u "
+          "from_0=%u from_slash=%u from_x=%u from_lf=%u from_cr=%u\n",
+          i, xor, arith, tolower, toupper, tohex, tob64, fromhex, fromb64, to_0,
+          to_slash, to_x, to_lf, to_cr, from_0, from_slash, from_x, from_lf,
+          from_cr);
+      //  #endif
+
+      // input is base64 and converted to binary? convert repl to base64!
+      if (i && !(i % 4) && i < 24 && fromb64 > i) {
+
+        to_base64(repl, tmp, i);
+        memcpy(buf + idx, tmp, i);
+        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+        fprintf(stderr, "RTN ATTEMPT fromb64 %u result %u\n", fromb64, *status);
+
+      }
+
+      // input is converted to base64? decode repl with base64!
+      if (i && !(i % 3) && i < 24 && tob64 > i) {
+
+        u32 olen = from_base64(repl, tmp, i);
+        memcpy(buf + idx, tmp, olen);
+        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+        fprintf(stderr, "RTN ATTEMPT tob64 %u result %u\n", tob64, *status);
+
+      }
+
+      // input is converted to hex? convert repl to binary!
+      if (i && !(i % 2) && i < 16 && tohex && tohex > i) {
+
+        u32 off;
+        if (to_slash + to_x + to_0 == 2) {
+
+          off = 2;
+
+        } else {
+
+          off = 0;
+
+        }
+
+        for (j = 0; j < i / 2; j++)
+          tmp[j] = (hex_table[repl[off + (j << 1)] - '0'] << 4) +
+                   hex_table[repl[off + (j << 1) + 1] - '0'];
+
+        memcpy(buf + idx, tmp, i / 2);
+        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+        fprintf(stderr, "RTN ATTEMPT tohex %u result %u\n", tohex, *status);
+
+      }
+
+      // input is hex and converted to binary? convert repl to hex!
+      if (i && !(i % 2) && i < 16 && fromhex &&
+          fromhex + from_slash + from_x + from_0 > i) {
+
+        u8 off = 0;
+        if (from_slash && from_x) {
+
+          tmp[0] = '\\';
+          if (from_X) {
+
+            tmp[1] = 'X';
+
+          } else {
+
+            tmp[1] = 'x';
+
+          }
+
+          off = 2;
+
+        } else if (from_0 && from_x) {
+
+          tmp[0] = '0';
+          if (from_X) {
+
+            tmp[1] = 'X';
+
+          } else {
+
+            tmp[1] = 'x';
+
+          }
+
+          off = 2;
+
+        }
+
+        if (to_up == 1) {
+
+          for (j = 0; j <= i; j++) {
+
+            tmp[off + (j << 1)] = hex_table_up[repl[j] >> 4];
+            tmp[off + (j << 1) + 1] = hex_table_up[repl[j] % 16];
+
+          }
+
+        } else {
+
+          for (j = 0; j <= i; j++) {
+
+            tmp[off + (j << 1)] = hex_table_low[repl[j] >> 4];
+            tmp[off + (j << 1) + 1] = hex_table_low[repl[j] % 16];
+
+          }
+
+        }
+
+        memcpy(buf + idx, tmp, (i << 1) + off);
+        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+        fprintf(stderr, "RTN ATTEMPT fromhex %u result %u\n", fromhex, *status);
+        memcpy(buf + idx, save, (i << 1) + off);
+
+      }
 
       if (xor > i) {
 
@@ -1715,9 +2099,9 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
 
       }
 
-#ifdef COMBINE
+  #ifdef COMBINE
       if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); }
-#endif
+  #endif
 
       if ((i >= xor&&i >= arith &&i >= tolower &&i >= toupper) ||
           repl[i] != changed_val[i] || *status == 1) {
@@ -1728,9 +2112,11 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
 
     }
 
+    memcpy(&buf[idx], save, i);
+
   }
 
-  memcpy(&buf[idx], save, i);
+#endif
 
   return 0;