fuzzing.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #include "fuzzing.h"
  2. #include <tcpedit/tcpedit.h>
  3. #include <assert.h>
  4. #include <stdint.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. static unsigned int fuzz_seed;
  8. static unsigned int fuzz_factor;
  9. static unsigned int fuzz_running;
  10. void
  11. fuzzing_init(uint32_t _fuzz_seed, uint32_t _fuzz_factor)
  12. {
  13. assert(_fuzz_factor);
  14. fuzz_seed = _fuzz_seed;
  15. fuzz_factor = _fuzz_factor;
  16. fuzz_running = 1;
  17. }
  18. #define SGT_MAX_SIZE 16
  19. static inline int
  20. fuzz_get_sgt_size(uint32_t r, uint32_t caplen)
  21. {
  22. if (0 == caplen)
  23. return 0;
  24. if (caplen <= SGT_MAX_SIZE)
  25. /* packet too small, fuzzing only one byte */
  26. return 1;
  27. /* return random value between 1 and SGT_MAX_SIZE */
  28. return (1 + (r % (SGT_MAX_SIZE - 1)));
  29. }
  30. static inline int
  31. fuzz_reduce_packet_size(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr, uint32_t new_len)
  32. {
  33. if (pkthdr->len < pkthdr->caplen) {
  34. tcpedit_seterr(tcpedit, "Packet length %u smaller than capture length %u", pkthdr->len, pkthdr->caplen);
  35. return -1;
  36. }
  37. if (new_len > pkthdr->caplen) {
  38. tcpedit_seterr(tcpedit, "Cannot fuzz packet of capture length %u to length %u", pkthdr->caplen, new_len);
  39. return -1;
  40. }
  41. if (new_len == pkthdr->caplen) {
  42. return 0;
  43. }
  44. pkthdr->len = new_len;
  45. pkthdr->caplen = pkthdr->len;
  46. /* do not fix lengths in ip/tcp/udp layers.
  47. * fixlen option already does so, and can be called with fuzzing option. */
  48. return 1;
  49. }
  50. int
  51. fuzzing(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr, u_char **pktdata)
  52. {
  53. int chksum_update_required = 0;
  54. uint32_t r, s;
  55. uint16_t l2proto;
  56. uint8_t l4proto;
  57. u_char *packet, *l3data, *l4data, *end_ptr;
  58. tcpeditdlt_plugin_t *plugin;
  59. int l2len, l4len;
  60. tcpeditdlt_t *ctx;
  61. assert(tcpedit);
  62. assert(pkthdr);
  63. assert(*pktdata);
  64. if (!fuzz_running)
  65. goto done;
  66. assert(fuzz_factor);
  67. /*
  68. * Determine if this is one of the packets that is going to be altered.
  69. * No fuzzing for the other 7 out of 8 packets
  70. */
  71. r = tcpr_random(&fuzz_seed);
  72. if ((r % fuzz_factor) != 0)
  73. goto done;
  74. /* initializations */
  75. ctx = tcpedit->dlt_ctx;
  76. packet = *pktdata;
  77. end_ptr = packet + pkthdr->caplen;
  78. plugin = tcpedit->dlt_ctx->encoder;
  79. l2len = plugin->plugin_l2len(ctx, packet, pkthdr->caplen);
  80. l2proto = ntohs(plugin->plugin_proto(ctx, packet, pkthdr->caplen));
  81. if (l2len == -1 || (int)pkthdr->caplen < l2len)
  82. goto done;
  83. /*
  84. * Get a pointer to the network layer
  85. *
  86. * Note that this pointer may be in a working buffer and not on directly
  87. * to '*pktdata'. All alterations are done in this buffer, which later
  88. * will be copied back to '*pktdata', if necessary
  89. */
  90. l3data = plugin->plugin_get_layer3(ctx, packet, pkthdr->caplen);
  91. if (!l3data)
  92. goto done;
  93. switch (l2proto) {
  94. case (ETHERTYPE_IP): {
  95. l4data = get_layer4_v4((ipv4_hdr_t *)(packet + l2len), end_ptr);
  96. if (!l4data)
  97. goto done;
  98. l4len = l4data - packet;
  99. l4proto = ((ipv4_hdr_t *)l3data)->ip_p;
  100. break;
  101. }
  102. case (ETHERTYPE_IP6): {
  103. l4data = get_layer4_v6((ipv6_hdr_t *)(packet + l2len), end_ptr);
  104. if (!l4data)
  105. goto done;
  106. l4len = l4data - packet;
  107. l4proto = ((ipv6_hdr_t *)l3data)->ip_nh;
  108. break;
  109. }
  110. default:
  111. /* apply fuzzing on unknown packet types */
  112. l4len = pkthdr->caplen - l2len;
  113. l4data = packet + l2len;
  114. l4proto = IPPROTO_RAW;
  115. }
  116. /* adjust payload length based on layer 3 protocol */
  117. switch (l4proto) {
  118. case IPPROTO_TCP:
  119. l4len -= sizeof(tcp_hdr_t);
  120. l4data += sizeof(tcp_hdr_t);
  121. break;
  122. case IPPROTO_UDP:
  123. l4len -= sizeof(udp_hdr_t);
  124. l4data += sizeof(udp_hdr_t);
  125. break;
  126. }
  127. if (l4len <= 1 || l4data > end_ptr)
  128. goto done;
  129. /* add some additional randomization */
  130. r ^= r >> 16;
  131. s = r % FUZZING_TOTAL_ACTION_NUMBER;
  132. switch (s) {
  133. case FUZZING_DROP_PACKET: {
  134. /* simulate dropping the packet */
  135. if (fuzz_reduce_packet_size(tcpedit, pkthdr, 0) < 0)
  136. /* could not change packet size, so packet left unchanged */
  137. goto done;
  138. break;
  139. }
  140. case FUZZING_REDUCE_SIZE: {
  141. /* reduce packet size */
  142. uint32_t new_len = (r % (l4len - 1)) + 1;
  143. if (fuzz_reduce_packet_size(tcpedit, pkthdr, new_len) < 0)
  144. /* could not change packet size, so packet left unchanged */
  145. goto done;
  146. chksum_update_required = 1;
  147. break;
  148. }
  149. case FUZZING_CHANGE_START_ZERO: {
  150. /* fuzz random-size segment at the beginning of the packet with 0x00 */
  151. uint32_t sgt_size = fuzz_get_sgt_size(r, l4len);
  152. memset(l4data, 0x00, sgt_size);
  153. chksum_update_required = 1;
  154. break;
  155. }
  156. case FUZZING_CHANGE_START_RANDOM: {
  157. /*
  158. * fuzz random-size segment at the beginning of the packet payload
  159. * with random bytes
  160. */
  161. size_t i;
  162. uint32_t sgt_size = fuzz_get_sgt_size(r, l4len);
  163. if (!sgt_size)
  164. goto done;
  165. for (i = 0; i < sgt_size; i++)
  166. l4data[i] = l4data[i] ^ (u_char)(r >> 4);
  167. chksum_update_required = 1;
  168. break;
  169. }
  170. case FUZZING_CHANGE_START_FF: {
  171. /*
  172. * fuzz random-size segment at the beginning of the packet
  173. * payload with 0xff
  174. */
  175. uint32_t sgt_size = fuzz_get_sgt_size(r, l4len);
  176. if (!sgt_size)
  177. goto done;
  178. memset(l4data, 0xff, sgt_size);
  179. chksum_update_required = 1;
  180. break;
  181. }
  182. case FUZZING_CHANGE_MID_ZERO: {
  183. /* fuzz random-size segment inside the packet payload with 0x00 */
  184. if (l4len <= 2)
  185. goto done;
  186. uint32_t offset = ((r >> 16) % (l4len - 1)) + 1;
  187. uint32_t sgt_size = fuzz_get_sgt_size(r, l4len - offset);
  188. if (!sgt_size)
  189. goto done;
  190. memset(l4data + offset, 0x00, sgt_size);
  191. chksum_update_required = 1;
  192. break;
  193. }
  194. case FUZZING_CHANGE_MID_FF: {
  195. /* fuzz random-size segment inside the packet payload with 0xff */
  196. if (l4len <= 2)
  197. goto done;
  198. uint32_t offset = ((r >> 16) % (l4len - 1)) + 1;
  199. uint32_t sgt_size = fuzz_get_sgt_size(r, l4len - offset);
  200. if (!sgt_size)
  201. goto done;
  202. memset(l4data + offset, 0xff, sgt_size);
  203. chksum_update_required = 1;
  204. break;
  205. }
  206. case FUZZING_CHANGE_END_ZERO: {
  207. /* fuzz random-sized segment at the end of the packet payload with 0x00 */
  208. int sgt_size = fuzz_get_sgt_size(r, l4len);
  209. if (!sgt_size || sgt_size > l4len)
  210. goto done;
  211. memset(l4data + l4len - sgt_size, 0x00, sgt_size);
  212. chksum_update_required = 1;
  213. break;
  214. }
  215. case FUZZING_CHANGE_END_RANDOM: {
  216. /* fuzz random-sized segment at the end of the packet with random Bytes */
  217. int i;
  218. int sgt_size = fuzz_get_sgt_size(r, l4len);
  219. if (!sgt_size || sgt_size > l4len)
  220. goto done;
  221. for (i = (l4len - sgt_size); i < l4len; i++)
  222. l4data[i] = l4data[i] ^ (u_char)(r >> 4);
  223. chksum_update_required = 1;
  224. break;
  225. }
  226. case FUZZING_CHANGE_END_FF: {
  227. /* fuzz random-sized segment at the end of the packet with 0xff00 */
  228. int sgt_size = fuzz_get_sgt_size(r, l4len);
  229. if (!sgt_size || sgt_size > l4len)
  230. goto done;
  231. memset(l4data + l4len - sgt_size, 0xff, sgt_size);
  232. chksum_update_required = 1;
  233. break;
  234. }
  235. case FUZZING_CHANGE_MID_RANDOM: {
  236. /* fuzz random-size segment inside the packet with random Bytes */
  237. size_t i;
  238. uint32_t offset = ((r >> 16) % (l4len - 1)) + 1;
  239. int sgt_size = fuzz_get_sgt_size(r, l4len - offset);
  240. if (!sgt_size || sgt_size > l4len)
  241. goto done;
  242. for (i = offset; i < offset + sgt_size; i++)
  243. l4data[i] = l4data[i] ^ (u_char)(r >> 4);
  244. chksum_update_required = 1;
  245. break;
  246. }
  247. default:
  248. assert(false);
  249. }
  250. dbgx(3, "packet %llu fuzzed : %d", tcpedit->runtime.packetnum, s);
  251. done:
  252. return chksum_update_required;
  253. }