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 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
|
/*
New Custom Mutator for AFL++
Written by Khaled Yakdan <yakdan@code-intelligence.de>
Andrea Fioraldi <andreafioraldi@gmail.com>
Shengtuo Hu <h1994st@gmail.com>
Dominik Maier <mail@dmnk.co>
*/
// You need to use -I/path/to/AFLplusplus/include -I.
#include "afl-fuzz.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define DATA_SIZE (100)
static const char *commands[] = {
"GET",
"PUT",
"DEL",
};
typedef struct my_mutator {
afl_state_t *afl;
// any additional data here!
size_t trim_size_current;
int trimmming_steps;
int cur_step;
u8 *mutated_out, *post_process_buf, *trim_buf;
} my_mutator_t;
/**
* Initialize this custom mutator
*
* @param[in] afl a pointer to the internal state object. Can be ignored for
* now.
* @param[in] seed A seed for this mutator - the same seed should always mutate
* in the same way.
* @return Pointer to the data object this custom mutator instance should use.
* There may be multiple instances of this mutator in one afl-fuzz run!
* Return NULL on error.
*/
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
srand(seed); // needed also by surgical_havoc_mutate()
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
if (!data) {
perror("afl_custom_init alloc");
return NULL;
}
if ((data->mutated_out = (u8 *)malloc(MAX_FILE)) == NULL) {
perror("afl_custom_init malloc");
return NULL;
}
if ((data->post_process_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
perror("afl_custom_init malloc");
return NULL;
}
if ((data->trim_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
perror("afl_custom_init malloc");
return NULL;
}
data->afl = afl;
return data;
}
/**
* Perform custom mutations on a given input
*
* (Optional for now. Required in the future)
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[in] buf Pointer to input data to be mutated
* @param[in] buf_size Size of input data
* @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on
* error.
* @param[in] add_buf Buffer containing the additional test case
* @param[in] add_buf_size Size of the additional test case
* @param[in] max_size Maximum size of the mutated output. The mutation must not
* produce data larger than max_size.
* @return Size of the mutated output.
*/
size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
u8 **out_buf, uint8_t *add_buf,
size_t add_buf_size, // add_buf can be NULL
size_t max_size) {
// Make sure that the packet size does not exceed the maximum size expected by
// the fuzzer
size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size;
memcpy(data->mutated_out, buf, buf_size);
// Randomly select a command string to add as a header to the packet
memcpy(data->mutated_out, commands[rand() % 3], 3);
if (mutated_size > max_size) { mutated_size = max_size; }
*out_buf = data->mutated_out;
return mutated_size;
}
/**
* A post-processing function to use right before AFL writes the test case to
* disk in order to execute the target.
*
* (Optional) If this functionality is not needed, simply don't define this
* function.
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[in] buf Buffer containing the test case to be executed
* @param[in] buf_size Size of the test case
* @param[out] out_buf Pointer to the buffer containing the test case after
* processing. External library should allocate memory for out_buf.
* The buf pointer may be reused (up to the given buf_size);
* @return Size of the output buffer after processing or the needed amount.
* A return of 0 indicates an error.
*/
size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf,
size_t buf_size, uint8_t **out_buf) {
if (buf_size + 5 > MAX_FILE) { buf_size = MAX_FILE - 5; }
memcpy(data->post_process_buf + 5, buf, buf_size);
data->post_process_buf[0] = 'A';
data->post_process_buf[1] = 'F';
data->post_process_buf[2] = 'L';
data->post_process_buf[3] = '+';
data->post_process_buf[4] = '+';
*out_buf = data->post_process_buf;
return buf_size + 5;
}
/**
* This method is called at the start of each trimming operation and receives
* the initial buffer. It should return the amount of iteration steps possible
* on this input (e.g. if your input has n elements and you want to remove
* them one by one, return n, if you do a binary search, return log(n),
* and so on...).
*
* If your trimming algorithm doesn't allow you to determine the amount of
* (remaining) steps easily (esp. while running), then you can alternatively
* return 1 here and always return 0 in post_trim until you are finished and
* no steps remain. In that case, returning 1 in post_trim will end the
* trimming routine. The whole current index/max iterations stuff is only used
* to show progress.
*
* (Optional)
*
* @param data pointer returned in afl_custom_init for this fuzz case
* @param buf Buffer containing the test case
* @param buf_size Size of the test case
* @return The amount of possible iteration steps to trim the input.
* negative on error.
*/
int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf,
size_t buf_size) {
// We simply trim once
data->trimmming_steps = 1;
data->cur_step = 0;
memcpy(data->trim_buf, buf, buf_size);
data->trim_size_current = buf_size;
return data->trimmming_steps;
}
/**
* This method is called for each trimming operation. It doesn't have any
* arguments because we already have the initial buffer from init_trim and we
* can memorize the current state in *data. This can also save
* reparsing steps for each iteration. It should return the trimmed input
* buffer, where the returned data must not exceed the initial input data in
* length. Returning anything that is larger than the original data (passed
* to init_trim) will result in a fatal abort of AFLFuzz.
*
* (Optional)
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[out] out_buf Pointer to the buffer containing the trimmed test case.
* External library should allocate memory for out_buf.
* AFL++ will not release the memory after saving the test case.
* Keep a ref in *data.
* *out_buf = NULL is treated as error.
* @return Pointer to the size of the trimmed test case
*/
size_t afl_custom_trim(my_mutator_t *data, uint8_t **out_buf) {
*out_buf = data->trim_buf;
// Remove the last byte of the trimming input
return data->trim_size_current - 1;
}
/**
* This method is called after each trim operation to inform you if your
* trimming step was successful or not (in terms of coverage). If you receive
* a failure here, you should reset your input to the last known good state.
*
* (Optional)
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param success Indicates if the last trim operation was successful.
* @return The next trim iteration index (from 0 to the maximum amount of
* steps returned in init_trim). negative ret on failure.
*/
int32_t afl_custom_post_trim(my_mutator_t *data, int success) {
if (success) {
++data->cur_step;
return data->cur_step;
}
return data->trimmming_steps;
}
/**
* Perform a single custom mutation on a given input.
* This mutation is stacked with the other muatations in havoc.
*
* (Optional)
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[in] buf Pointer to the input data to be mutated and the mutated
* output
* @param[in] buf_size Size of input data
* @param[out] out_buf The output buffer. buf can be reused, if the content
* fits. *out_buf = NULL is treated as error.
* @param[in] max_size Maximum size of the mutated output. The mutation must
* not produce data larger than max_size.
* @return Size of the mutated output.
*/
size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size,
u8 **out_buf, size_t max_size) {
*out_buf = buf; // in-place mutation
if (buf_size <= sizeof(size_t)) { return buf_size; }
size_t victim = rand() % (buf_size - sizeof(size_t));
(*out_buf)[victim] += rand() % 10;
return buf_size;
}
/**
* Return the probability (in percentage) that afl_custom_havoc_mutation
* is called in havoc. By default it is 6 %.
*
* (Optional)
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @return The probability (0-100).
*/
uint8_t afl_custom_havoc_mutation_probability(my_mutator_t *data) {
return 5; // 5 %
}
/**
* Determine whether the fuzzer should fuzz the queue entry or not.
*
* (Optional)
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param filename File name of the test case in the queue entry
* @return Return True(1) if the fuzzer will fuzz the queue entry, and
* False(0) otherwise.
*/
uint8_t afl_custom_queue_get(my_mutator_t *data, const uint8_t *filename) {
return 1;
}
/**
* Allow for additional analysis (e.g. calling a different tool that does a
* different kind of coverage and saves this for the custom mutator).
*
* (Optional)
*
* @param data pointer returned in afl_custom_init for this fuzz case
* @param filename_new_queue File name of the new queue entry
* @param filename_orig_queue File name of the original queue entry
* @return if the file contents was modified return 1 (True), 0 (False)
* otherwise
*/
uint8_t afl_custom_queue_new_entry(my_mutator_t *data,
const uint8_t *filename_new_queue,
const uint8_t *filename_orig_queue) {
/* Additional analysis on the original or new test case */
return 0;
}
/**
* Deinitialize everything
*
* @param data The data ptr from afl_custom_init
*/
void afl_custom_deinit(my_mutator_t *data) {
free(data->post_process_buf);
free(data->mutated_out);
free(data->trim_buf);
free(data);
}
|