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