comparison fuzz/fuzz-sshpacketmutator.c @ 1774:833bf9947603

Fuzzing - get rid of "prefix" for streams Improved packet generation with sshpacketmutator
author Matt Johnston <matt@ucc.asn.au>
date Sun, 01 Nov 2020 23:44:58 +0800
parents af9ed0815818
children 8179eabe16c9
comparison
equal deleted inserted replaced
1773:c3ca130d193a 1774:833bf9947603
12 #include "dbutil.h" 12 #include "dbutil.h"
13 13
14 size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); 14 size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
15 15
16 static const char* FIXED_VERSION = "SSH-2.0-dbfuzz\r\n"; 16 static const char* FIXED_VERSION = "SSH-2.0-dbfuzz\r\n";
17 static const size_t MAX_FUZZ_PACKETS = 500; 17 static const char* FIXED_IGNORE_MSG =
18 "\x00\x00\x00\x10\x06\x02\x00\x00\x00\x00\x11\x22\x33\x44\x55\x66";
19 static const unsigned int FIXED_IGNORE_MSG_LEN = 16;
20 #define MAX_FUZZ_PACKETS 500
18 /* XXX This might need tuning */ 21 /* XXX This might need tuning */
19 static const size_t MAX_OUT_SIZE = 50000; 22 static const size_t MAX_OUT_SIZE = 50000;
20 23
21 /* Splits packets from an input stream buffer "inp". 24 /* Splits packets from an input stream buffer "inp".
22 The initial SSH version identifier is discarded. 25 The initial SSH version identifier is discarded.
60 buf_decrpos(inp, 3); 63 buf_decrpos(inp, 3);
61 continue; 64 continue;
62 } 65 }
63 packet_len = MIN(packet_len, inp->len - inp->pos); 66 packet_len = MIN(packet_len, inp->len - inp->pos);
64 67
65 /* Copy to output buffer. We're reusing buffers */ 68 /* Check the packet length makes sense */
66 buffer* new_packet = out_packets[*num_out_packets]; 69 if (packet_len >= MIN_PACKET_LEN-4) {
67 (*num_out_packets)++; 70 /* Copy to output buffer. We're reusing buffers */
68 buf_setlen(new_packet, 0); 71 buffer* new_packet = out_packets[*num_out_packets];
69 buf_putint(new_packet, packet_len); 72 (*num_out_packets)++;
70 buf_putbytes(new_packet, buf_getptr(inp, packet_len), packet_len); 73 buf_setlen(new_packet, 0);
74 // packet_len doesn't include itself
75 buf_putint(new_packet, packet_len);
76 buf_putbytes(new_packet, buf_getptr(inp, packet_len), packet_len);
77 }
71 buf_incrpos(inp, packet_len); 78 buf_incrpos(inp, packet_len);
72 } 79 }
73 } 80 }
74 81
75 /* Mutate a packet buffer in-place */ 82 /* Mutate a packet buffer in-place.
76 static void buf_llvm_mutate(buffer *buf) { 83 Returns DROPBEAR_FAILURE if it's too short */
84 static int buf_llvm_mutate(buffer *buf) {
85 int ret;
77 /* Position it after packet_length and padding_length */ 86 /* Position it after packet_length and padding_length */
78 const unsigned int offset = 5; 87 const unsigned int offset = 5;
79 if (buf->len < offset) { 88 buf_setpos(buf, 0);
80 return; 89 buf_incrwritepos(buf, offset);
81 }
82 buf_setpos(buf, offset);
83 size_t max_size = buf->size - buf->pos; 90 size_t max_size = buf->size - buf->pos;
84 size_t new_size = LLVMFuzzerMutate(buf_getwriteptr(buf, max_size), 91 size_t new_size = LLVMFuzzerMutate(buf_getwriteptr(buf, max_size),
85 buf->len - buf->pos, max_size); 92 buf->len - buf->pos, max_size);
86 buf_setpos(buf, 0); 93 size_t new_total = new_size + 1 + 4;
87 buf_putint(buf, new_size); 94 // Round down to a block size
88 buf_setlen(buf, offset + new_size); 95 new_total = new_total - (new_total % dropbear_nocipher.blocksize);
96
97 if (new_total >= 16) {
98 buf_setlen(buf, new_total);
99 // Fix up the length fields
100 buf_setpos(buf, 0);
101 // packet_length doesn't include itself, does include padding_length byte
102 buf_putint(buf, new_size+1);
103 // always just put minimum padding length = 4
104 buf_putbyte(buf, 4);
105 ret = DROPBEAR_SUCCESS;
106 } else {
107 // instead put a fake packet
108 buf_setlen(buf, 0);
109 buf_putbytes(buf, FIXED_IGNORE_MSG, FIXED_IGNORE_MSG_LEN);
110 ret = DROPBEAR_FAILURE;
111 }
112 return ret;
89 } 113 }
90 114
91 115
92 /* Persistent buffers to avoid constant allocations */ 116 /* Persistent buffers to avoid constant allocations */
93 static buffer *oup; 117 static buffer *oup;
94 static buffer *alloc_packetA; 118 static buffer *alloc_packetA;
95 static buffer *alloc_packetB; 119 static buffer *alloc_packetB;
96 buffer* packets1[MAX_FUZZ_PACKETS]; 120 static buffer* packets1[MAX_FUZZ_PACKETS];
97 buffer* packets2[MAX_FUZZ_PACKETS]; 121 static buffer* packets2[MAX_FUZZ_PACKETS];
98 122
99 /* Allocate buffers once at startup. 123 /* Allocate buffers once at startup.
100 'constructor' here so it runs before dbmalloc's interceptor */ 124 'constructor' here so it runs before dbmalloc's interceptor */
101 static void alloc_static_buffers() __attribute__((constructor)); 125 static void alloc_static_buffers() __attribute__((constructor));
102 static void alloc_static_buffers() { 126 static void alloc_static_buffers() {
120 buf_setlen(alloc_packetA, 0); 144 buf_setlen(alloc_packetA, 0);
121 buf_setlen(alloc_packetB, 0); 145 buf_setlen(alloc_packetB, 0);
122 buf_setlen(oup, 0); 146 buf_setlen(oup, 0);
123 147
124 unsigned int i; 148 unsigned int i;
149 size_t ret_len;
125 unsigned short randstate[3] = {0,0,0}; 150 unsigned short randstate[3] = {0,0,0};
126 memcpy(randstate, &Seed, sizeof(Seed)); 151 memcpy(randstate, &Seed, sizeof(Seed));
127 152
128 // printhex("mutator input", Data, Size); 153 // printhex("mutator input", Data, Size);
129 154
130 /* 0.1% chance straight llvm mutate */ 155 /* 0.1% chance straight llvm mutate */
131 if (nrand48(randstate) % 1000 == 0) { 156 // if (nrand48(randstate) % 1000 == 0) {
132 return LLVMFuzzerMutate(Data, Size, MaxSize); 157 // ret_len = LLVMFuzzerMutate(Data, Size, MaxSize);
133 } 158 // // printhex("mutator straight llvm", Data, ret_len);
159 // return ret_len;
160 // }
134 161
135 buffer inp_buf = {.data = Data, .size = Size, .len = Size, .pos = 0}; 162 buffer inp_buf = {.data = Data, .size = Size, .len = Size, .pos = 0};
136 buffer *inp = &inp_buf; 163 buffer *inp = &inp_buf;
137 164
138 /* Parse packets */ 165 /* Parse packets */
139 unsigned int num_packets = MAX_FUZZ_PACKETS; 166 unsigned int num_packets = MAX_FUZZ_PACKETS;
140 buffer **packets = packets1; 167 buffer **packets = packets1;
141 fuzz_get_packets(inp, packets, &num_packets); 168 fuzz_get_packets(inp, packets, &num_packets);
142 169
143 if (num_packets == 0) { 170 if (num_packets == 0) {
144 // gotta do something 171 // Make up a packet, writing direct to the buffer
145 memcpy(Data, FIXED_VERSION, MIN(strlen(FIXED_VERSION), MaxSize)); 172 inp->size = MaxSize;
146 return LLVMFuzzerMutate(Data, Size, MaxSize); 173 buf_setlen(inp, 0);
174 buf_putbytes(inp, FIXED_VERSION, strlen(FIXED_VERSION));
175 buf_putbytes(inp, FIXED_IGNORE_MSG, FIXED_IGNORE_MSG_LEN);
176 // printhex("mutator no input", Data, inp->len);
177 return inp->len;
147 } 178 }
148 179
149 /* Start output */ 180 /* Start output */
150 /* Put a new banner to output */ 181 /* Put a new banner to output */
151 buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION)); 182 buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION));
155 // These are pointers to output 186 // These are pointers to output
156 buffer *out_packetA = NULL, *out_packetB = NULL; 187 buffer *out_packetA = NULL, *out_packetB = NULL;
157 buf_setlen(alloc_packetA, 0); 188 buf_setlen(alloc_packetA, 0);
158 buf_setlen(alloc_packetB, 0); 189 buf_setlen(alloc_packetB, 0);
159 190
160 /* 5% chance each */ 191 /* 2% chance each */
161 const int optA = nrand48(randstate) % 20; 192 const int optA = nrand48(randstate) % 50;
162 if (optA == 0) { 193 if (optA == 0) {
163 /* Copy another */ 194 /* Copy another */
164 unsigned int other = nrand48(randstate) % num_packets; 195 unsigned int other = nrand48(randstate) % num_packets;
165 out_packetA = packets[other]; 196 out_packetA = packets[other];
197 // printf("copy another %d / %d len %u\n", other, num_packets, out_packetA->len);
166 } 198 }
167 if (optA == 1) { 199 if (optA == 1) {
168 /* Mutate another */ 200 /* Mutate another */
169 unsigned int other = nrand48(randstate) % num_packets; 201 unsigned int other = nrand48(randstate) % num_packets;
202 out_packetA = alloc_packetA;
170 buffer *from = packets[other]; 203 buffer *from = packets[other];
171 buf_putbytes(alloc_packetA, from->data, from->len); 204 buf_putbytes(out_packetA, from->data, from->len);
172 out_packetA = alloc_packetA; 205 if (buf_llvm_mutate(out_packetA) == DROPBEAR_FAILURE) {
173 buf_llvm_mutate(out_packetA); 206 out_packetA = NULL;
207 }
208 // printf("mutate another %d / %d len %u -> %u\n", other, num_packets, from->len, out_packetA->len);
174 } 209 }
175 210
176 if (i < num_packets) { 211 if (i < num_packets) {
177 int optB = nrand48(randstate) % 10; 212 int optB = nrand48(randstate) % 100;
178 if (optB == 1) { 213 if (optB == 1) {
179 /* 10% chance of drop */ 214 /* small chance of drop */
180 /* Drop it */ 215 /* Drop it */
181 // printf("%d drop\n", i); 216 //printf("%d drop\n", i);
182 } else if (optB <= 6) { 217 } else {
183 /* Mutate it, 50% chance */ 218 /* Odds of modification are proportional to packet position.
184 // printf("%d mutate\n", i); 219 First packet has 20% chance, last has 100% chance */
185 buffer *from = packets[nrand48(randstate) % num_packets]; 220 int optC = nrand48(randstate) % 1000;
186 buf_putbytes(alloc_packetB, from->data, from->len); 221 int mutate_cutoff = MAX(200, (1000 * (i+1) / num_packets));
187 out_packetB = alloc_packetB; 222 if (optC < mutate_cutoff) {
188 buf_llvm_mutate(out_packetB); 223 // // printf("%d mutate\n", i);
189 } else { 224 out_packetB = alloc_packetB;
190 /* Copy as-is */ 225 buffer *from = packets[i];
191 out_packetB = packets[i]; 226 buf_putbytes(out_packetB, from->data, from->len);
192 // printf("%d as-is\n", i); 227 if (buf_llvm_mutate(out_packetB) == DROPBEAR_FAILURE) {
193 } 228 out_packetB = from;
229 }
230 // printf("mutate self %d / %d len %u -> %u\n", i, num_packets, from->len, out_packetB->len);
231 } else {
232 /* Copy as-is */
233 out_packetB = packets[i];
234 // printf("%d as-is len %u\n", i, out_packetB->len);
235 }
236 }
194 } 237 }
195 238
196 if (out_packetA && oup->len + out_packetA->len <= oup->size) { 239 if (out_packetA && oup->len + out_packetA->len <= oup->size) {
197 buf_putbytes(oup, out_packetA->data, out_packetA->len); 240 buf_putbytes(oup, out_packetA->data, out_packetA->len);
198 } 241 }
199 if (out_packetB && oup->len + out_packetB->len <= oup->size) { 242 if (out_packetB && oup->len + out_packetB->len <= oup->size) {
200 buf_putbytes(oup, out_packetB->data, out_packetB->len); 243 buf_putbytes(oup, out_packetB->data, out_packetB->len);
201 } 244 }
202 } 245 }
203 246
204 size_t ret_len = MIN(MaxSize, oup->len); 247 ret_len = MIN(MaxSize, oup->len);
205 memcpy(Data, oup->data, ret_len); 248 memcpy(Data, oup->data, ret_len);
206 // printhex("mutator done", Data, ret_len); 249 // printhex("mutator done", Data, ret_len);
207 return ret_len; 250 return ret_len;
208 } 251 }
209 252
223 unsigned int num_packets1 = MAX_FUZZ_PACKETS; 266 unsigned int num_packets1 = MAX_FUZZ_PACKETS;
224 fuzz_get_packets(inp1, packets1, &num_packets1); 267 fuzz_get_packets(inp1, packets1, &num_packets1);
225 unsigned int num_packets2 = MAX_FUZZ_PACKETS; 268 unsigned int num_packets2 = MAX_FUZZ_PACKETS;
226 fuzz_get_packets(inp2, packets2, &num_packets2); 269 fuzz_get_packets(inp2, packets2, &num_packets2);
227 270
271 // fprintf(stderr, "input 1 %u packets\n", num_packets1);
272 // printhex("crossover input1", Data1, Size1);
273 // fprintf(stderr, "input 2 %u packets\n", num_packets2);
274 // printhex("crossover input2", Data2, Size2);
275
228 buf_setlen(oup, 0); 276 buf_setlen(oup, 0);
229 /* Put a new banner to output */ 277 /* Put a new banner to output */
230 buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION)); 278 buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION));
231 279
232 for (i = 0; i < num_packets1+1; i++) { 280 if (num_packets1 == 0 && num_packets2 == 0) {
233 if (num_packets2 > 0 && nrand48(randstate) % 10 == 0) { 281 buf_putbytes(oup, FIXED_IGNORE_MSG, FIXED_IGNORE_MSG_LEN);
234 /* 10% chance of taking another packet at each position */ 282 } else {
235 int other = nrand48(randstate) % num_packets2; 283 unsigned int min_out = MIN(num_packets1, num_packets2);
236 // printf("inserted other packet %d at %d\n", other, i); 284 unsigned int max_out = num_packets1 + num_packets2;
237 buffer *otherp = packets2[other]; 285 unsigned int num_out = min_out + nrand48(randstate) % (max_out-min_out+1);
238 if (oup->len + otherp->len <= oup->size) { 286
239 buf_putbytes(oup, otherp->data, otherp->len); 287 for (i = 0; i < num_out; i++) {
288 int choose = nrand48(randstate) % (num_packets1 + num_packets2);
289 buffer *p = NULL;
290 if (choose < num_packets1) {
291 p = packets1[choose];
292 } else {
293 p = packets2[choose-num_packets1];
240 } 294 }
241 } 295 if (oup->len + p->len <= oup->size) {
242 if (i < num_packets1) { 296 buf_putbytes(oup, p->data, p->len);
243 buffer *thisp = packets1[i];
244 if (oup->len + thisp->len <= oup->size) {
245 buf_putbytes(oup, thisp->data, thisp->len);
246 } 297 }
247 } 298 }
248 } 299 }
249 300
250 size_t ret_len = MIN(MaxOutSize, oup->len); 301 size_t ret_len = MIN(MaxOutSize, oup->len);
251 memcpy(Out, oup->data, ret_len); 302 memcpy(Out, oup->data, ret_len);
303 // printhex("crossover output", Out, ret_len);
252 return ret_len; 304 return ret_len;
253 } 305 }
254 306