Mercurial > dropbear
comparison fuzz/fuzz-sshpacketmutator.c @ 1760:2406a9987810
Add first try at fuzzing custom mutator
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sun, 25 Oct 2020 22:52:36 +0800 |
parents | |
children | b688c884dad7 |
comparison
equal
deleted
inserted
replaced
1759:4c5599435084 | 1760:2406a9987810 |
---|---|
1 #include "fuzz.h" | |
2 | |
3 size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); | |
4 | |
5 static void fuzz_get_packets(buffer *inp, buffer **out_packets, unsigned int *num_out_packets) { | |
6 /* Skip any existing banner. Format is | |
7 SSH-protoversion-softwareversion SP comments CR LF | |
8 so we look for SSH-2. then a subsequent LF */ | |
9 unsigned char* version = memmem(inp->data, inp->len, "SSH-2.", strlen("SSH-2.")); | |
10 if (version) { | |
11 buf_incrpos(inp, version - inp->data); | |
12 unsigned char* newline = memchr(&inp->data[inp->pos], '\n', inp->len - inp->pos); | |
13 if (newline) { | |
14 buf_incrpos(inp, newline - &inp->data[inp->pos]); | |
15 } else { | |
16 /* Give up on any version string */ | |
17 buf_setpos(inp, 0); | |
18 } | |
19 } | |
20 | |
21 const unsigned int max_out_packets = *num_out_packets; | |
22 *num_out_packets = 0; | |
23 while (1) { | |
24 if (inp->pos + 4 > inp->len) { | |
25 /* End of input */ | |
26 break; | |
27 } | |
28 | |
29 if (*num_out_packets >= max_out_packets) { | |
30 /* End of output */ | |
31 break; | |
32 } | |
33 | |
34 /* Read packet */ | |
35 unsigned int packet_len = buf_getint(inp); | |
36 if (packet_len <= RECV_MAX_PACKET_LEN) { | |
37 /* Bad length, try skipping a single byte */ | |
38 buf_decrpos(inp, 3); | |
39 continue; | |
40 } | |
41 packet_len = MIN(packet_len, inp->len - inp->pos); | |
42 | |
43 /* Copy to output buffer */ | |
44 buffer* new_packet = buf_new(RECV_MAX_PACKET_LEN); | |
45 buf_putint(new_packet, packet_len); | |
46 buf_putbytes(new_packet, buf_getptr(inp, packet_len), packet_len); | |
47 buf_incrpos(inp, packet_len); | |
48 | |
49 out_packets[*num_out_packets] = new_packet; | |
50 (*num_out_packets)++; | |
51 } | |
52 | |
53 } | |
54 | |
55 /* Mutate in-place */ | |
56 void buf_llvm_mutate(buffer *buf) { | |
57 /* Position it after packet_length and padding_length */ | |
58 const unsigned int offset = 5; | |
59 if (buf->len < offset) { | |
60 return; | |
61 } | |
62 buf_setpos(buf, offset); | |
63 size_t max_size = buf->size - buf->pos; | |
64 size_t new_size = LLVMFuzzerMutate(buf_getwriteptr(buf, max_size), | |
65 buf->len - buf->pos, max_size); | |
66 buf_setpos(buf, 0); | |
67 buf_putint(buf, new_size); | |
68 buf_setlen(buf, offset + new_size); | |
69 } | |
70 | |
71 | |
72 static const char* FIXED_VERSION = "SSH-2.0-dbfuzz\r\n"; | |
73 static const size_t MAX_FUZZ_PACKETS = 500; | |
74 /* XXX This might need tuning */ | |
75 static const size_t MAX_OUT_SIZE = 50000; | |
76 | |
77 size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, | |
78 size_t MaxSize, unsigned int Seed) { | |
79 int i; | |
80 unsigned short randstate[3] = {0,0,0}; | |
81 memcpy(randstate, &Seed, sizeof(Seed)); | |
82 | |
83 /* 1% chance straight llvm mutate */ | |
84 if (nrand48(randstate) % 100 == 0) { | |
85 return LLVMFuzzerMutate(Data, Size, MaxSize); | |
86 } | |
87 | |
88 buffer inp_buf = {.data = Data, .size = Size, .len = Size, .pos = 0}; | |
89 buffer *inp = &inp_buf; | |
90 | |
91 /* Parse packets */ | |
92 buffer* packets[MAX_FUZZ_PACKETS] = {0}; | |
93 unsigned int num_packets = MAX_FUZZ_PACKETS; | |
94 fuzz_get_packets(inp, packets, &num_packets); | |
95 | |
96 if (num_packets == 0) { | |
97 // gotta do something | |
98 memcpy(Data, FIXED_VERSION, MIN(strlen(FIXED_VERSION), MaxSize)); | |
99 return LLVMFuzzerMutate(Data, Size, MaxSize); | |
100 } | |
101 | |
102 /* Start output */ | |
103 buffer *oup = buf_new(MAX_OUT_SIZE); | |
104 /* Put a new banner to output */ | |
105 buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION)); | |
106 | |
107 /* Iterate output */ | |
108 for (i = 0; i < num_packets+1; i++) { | |
109 // These are pointers to output | |
110 buffer *out_packetA = NULL, *out_packetB = NULL; | |
111 // These need to be freed | |
112 buffer *alloc_packetA = NULL, *alloc_packetB = NULL; | |
113 | |
114 /* 5% chance each */ | |
115 const int optA = nrand48(randstate) % 20; | |
116 const int other = nrand48(randstate) % num_packets; | |
117 if (optA == 0) { | |
118 /* Copy another */ | |
119 out_packetA = packets[nrand48(randstate) % num_packets]; | |
120 } | |
121 if (optA == 1) { | |
122 /* Mutate another */ | |
123 alloc_packetA = buf_new(RECV_MAX_PACKET_LEN); | |
124 buffer *from = packets[nrand48(randstate) % num_packets]; | |
125 buf_putbytes(alloc_packetA, from->data, from->len); | |
126 out_packetA = alloc_packetA; | |
127 buf_llvm_mutate(out_packetA); | |
128 } | |
129 | |
130 /* 10% chance each of mutate or drop */ | |
131 if (i < num_packets) { | |
132 int optB = nrand48(randstate) % 10; | |
133 if (optB == 0) { | |
134 /* Copy as-is */ | |
135 out_packetB = packets[i]; | |
136 } | |
137 if (optB == 1) { | |
138 /* Drop it */ | |
139 } | |
140 if (optB == 2) { | |
141 /* Mutate it */ | |
142 alloc_packetB = buf_new(RECV_MAX_PACKET_LEN); | |
143 buffer *from = packets[nrand48(randstate) % num_packets]; | |
144 buf_putbytes(alloc_packetB, from->data, from->len); | |
145 out_packetB = alloc_packetB; | |
146 buf_llvm_mutate(out_packetB); | |
147 } | |
148 } | |
149 | |
150 if (out_packetA && oup->len + out_packetA->len <= oup->size) { | |
151 buf_putbytes(oup, out_packetA->data, out_packetA->len); | |
152 } | |
153 if (out_packetB && oup->len + out_packetB->len <= oup->size) { | |
154 buf_putbytes(oup, out_packetB->data, out_packetB->len); | |
155 } | |
156 if (alloc_packetA) { | |
157 buf_free(alloc_packetA); | |
158 alloc_packetA = NULL; | |
159 } | |
160 if (alloc_packetB) { | |
161 buf_free(alloc_packetB); | |
162 alloc_packetB = NULL; | |
163 } | |
164 } | |
165 | |
166 for (i = 0; i < num_packets; i++) { | |
167 buf_free(packets[i]); | |
168 } | |
169 | |
170 size_t ret_len = MIN(MaxSize, oup->len); | |
171 memcpy(Data, oup->data, ret_len); | |
172 buf_free(oup); | |
173 return ret_len; | |
174 } | |
175 |