diff options
author | Matt Johnston <matt@ucc.asn.au> | 2020-11-01 23:44:58 +0800 |
---|---|---|
committer | Matt Johnston <matt@ucc.asn.au> | 2020-11-01 23:44:58 +0800 |
commit | 121e6e620269ff3eef7ef46d6ab7057e56317588 (patch) | |
tree | 029784932a0b530cc753da437528d69f6027fdb1 | |
parent | 1b6e16ae7c9105e35dd14464b8ce785c613ab6e2 (diff) |
Fuzzing - get rid of "prefix" for streams
Improved packet generation with sshpacketmutator
-rw-r--r-- | dbrandom.c | 16 | ||||
-rw-r--r-- | fuzz/fuzz-common.c | 41 | ||||
-rw-r--r-- | fuzz/fuzz-sshpacketmutator.c | 168 |
3 files changed, 123 insertions, 102 deletions
@@ -151,17 +151,11 @@ static void write_urandom() #if DROPBEAR_FUZZ void fuzz_seed(const unsigned char* dat, unsigned int len) { - static unsigned char keep_pool[SHA1_HASH_SIZE]; - static int once = 0; - if (!once) { - once = 1; - hash_state hs; - sha1_init(&hs); - sha1_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz")); - sha1_process(&hs, dat, len); - sha1_done(&hs, keep_pool); - } - memcpy(hashpool, keep_pool, sizeof(keep_pool)); + hash_state hs; + sha1_init(&hs); + sha1_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz")); + sha1_process(&hs, dat, len); + sha1_done(&hs, hashpool); counter = 0; donerandinit = 1; } diff --git a/fuzz/fuzz-common.c b/fuzz/fuzz-common.c index 20f4287..3f8d081 100644 --- a/fuzz/fuzz-common.c +++ b/fuzz/fuzz-common.c @@ -64,6 +64,7 @@ int fuzz_set_input(const uint8_t *Data, size_t Size) { memset(&svr_ses, 0x0, sizeof(svr_ses)); memset(&cli_ses, 0x0, sizeof(cli_ses)); wrapfd_setup(fuzz.input); + // printhex("input", fuzz.input->data, fuzz.input->len); fuzz_seed(fuzz.input->data, MIN(fuzz.input->len, 16)); @@ -187,6 +188,7 @@ static void load_fixed_hostkeys(void) { void fuzz_kex_fakealgos(void) { ses.newkeys->recv.crypt_mode = &dropbear_mode_none; + ses.newkeys->recv.algo_mac = &dropbear_nohash; } void fuzz_get_socket_address(int UNUSED(fd), char **local_host, char **local_port, @@ -236,23 +238,8 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) { return 0; } - /* - get prefix, allowing for future extensibility. input format is - string prefix - uint32 wrapfd seed - ... to be extended later - [bytes] ssh input stream - */ - - /* be careful to avoid triggering buffer.c assertions */ - if (fuzz.input->len < 8) { - return 0; - } - size_t prefix_size = buf_getint(fuzz.input); - if (prefix_size != 4) { - return 0; - } - uint32_t wrapseed = buf_getint(fuzz.input); + uint32_t wrapseed; + genrandom(&wrapseed, sizeof(wrapseed)); wrapfd_setseed(wrapseed); int fakesock = wrapfd_new(); @@ -284,23 +271,11 @@ int fuzz_run_client(const uint8_t *Data, size_t Size, int skip_kexmaths) { return 0; } - /* - get prefix, allowing for future extensibility. input format is - string prefix - uint32 wrapfd seed - ... to be extended later - [bytes] ssh input stream - */ + // Allow to proceed sooner + ses.kexstate.donefirstkex = 1; - /* be careful to avoid triggering buffer.c assertions */ - if (fuzz.input->len < 8) { - return 0; - } - size_t prefix_size = buf_getint(fuzz.input); - if (prefix_size != 4) { - return 0; - } - uint32_t wrapseed = buf_getint(fuzz.input); + uint32_t wrapseed; + genrandom(&wrapseed, sizeof(wrapseed)); wrapfd_setseed(wrapseed); int fakesock = wrapfd_new(); diff --git a/fuzz/fuzz-sshpacketmutator.c b/fuzz/fuzz-sshpacketmutator.c index 3a513b0..4ad60ef 100644 --- a/fuzz/fuzz-sshpacketmutator.c +++ b/fuzz/fuzz-sshpacketmutator.c @@ -14,7 +14,10 @@ size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); static const char* FIXED_VERSION = "SSH-2.0-dbfuzz\r\n"; -static const size_t MAX_FUZZ_PACKETS = 500; +static const char* FIXED_IGNORE_MSG = + "\x00\x00\x00\x10\x06\x02\x00\x00\x00\x00\x11\x22\x33\x44\x55\x66"; +static const unsigned int FIXED_IGNORE_MSG_LEN = 16; +#define MAX_FUZZ_PACKETS 500 /* XXX This might need tuning */ static const size_t MAX_OUT_SIZE = 50000; @@ -62,30 +65,51 @@ static void fuzz_get_packets(buffer *inp, buffer **out_packets, unsigned int *nu } packet_len = MIN(packet_len, inp->len - inp->pos); - /* Copy to output buffer. We're reusing buffers */ - buffer* new_packet = out_packets[*num_out_packets]; - (*num_out_packets)++; - buf_setlen(new_packet, 0); - buf_putint(new_packet, packet_len); - buf_putbytes(new_packet, buf_getptr(inp, packet_len), packet_len); + /* Check the packet length makes sense */ + if (packet_len >= MIN_PACKET_LEN-4) { + /* Copy to output buffer. We're reusing buffers */ + buffer* new_packet = out_packets[*num_out_packets]; + (*num_out_packets)++; + buf_setlen(new_packet, 0); + // packet_len doesn't include itself + buf_putint(new_packet, packet_len); + buf_putbytes(new_packet, buf_getptr(inp, packet_len), packet_len); + } buf_incrpos(inp, packet_len); } } -/* Mutate a packet buffer in-place */ -static void buf_llvm_mutate(buffer *buf) { +/* Mutate a packet buffer in-place. +Returns DROPBEAR_FAILURE if it's too short */ +static int buf_llvm_mutate(buffer *buf) { + int ret; /* Position it after packet_length and padding_length */ const unsigned int offset = 5; - if (buf->len < offset) { - return; - } - buf_setpos(buf, offset); + buf_setpos(buf, 0); + buf_incrwritepos(buf, offset); size_t max_size = buf->size - buf->pos; size_t new_size = LLVMFuzzerMutate(buf_getwriteptr(buf, max_size), buf->len - buf->pos, max_size); - buf_setpos(buf, 0); - buf_putint(buf, new_size); - buf_setlen(buf, offset + new_size); + size_t new_total = new_size + 1 + 4; + // Round down to a block size + new_total = new_total - (new_total % dropbear_nocipher.blocksize); + + if (new_total >= 16) { + buf_setlen(buf, new_total); + // Fix up the length fields + buf_setpos(buf, 0); + // packet_length doesn't include itself, does include padding_length byte + buf_putint(buf, new_size+1); + // always just put minimum padding length = 4 + buf_putbyte(buf, 4); + ret = DROPBEAR_SUCCESS; + } else { + // instead put a fake packet + buf_setlen(buf, 0); + buf_putbytes(buf, FIXED_IGNORE_MSG, FIXED_IGNORE_MSG_LEN); + ret = DROPBEAR_FAILURE; + } + return ret; } @@ -93,8 +117,8 @@ static void buf_llvm_mutate(buffer *buf) { static buffer *oup; static buffer *alloc_packetA; static buffer *alloc_packetB; -buffer* packets1[MAX_FUZZ_PACKETS]; -buffer* packets2[MAX_FUZZ_PACKETS]; +static buffer* packets1[MAX_FUZZ_PACKETS]; +static buffer* packets2[MAX_FUZZ_PACKETS]; /* Allocate buffers once at startup. 'constructor' here so it runs before dbmalloc's interceptor */ @@ -122,15 +146,18 @@ size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, buf_setlen(oup, 0); unsigned int i; + size_t ret_len; unsigned short randstate[3] = {0,0,0}; memcpy(randstate, &Seed, sizeof(Seed)); // printhex("mutator input", Data, Size); /* 0.1% chance straight llvm mutate */ - if (nrand48(randstate) % 1000 == 0) { - return LLVMFuzzerMutate(Data, Size, MaxSize); - } + // if (nrand48(randstate) % 1000 == 0) { + // ret_len = LLVMFuzzerMutate(Data, Size, MaxSize); + // // printhex("mutator straight llvm", Data, ret_len); + // return ret_len; + // } buffer inp_buf = {.data = Data, .size = Size, .len = Size, .pos = 0}; buffer *inp = &inp_buf; @@ -141,9 +168,13 @@ size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, fuzz_get_packets(inp, packets, &num_packets); if (num_packets == 0) { - // gotta do something - memcpy(Data, FIXED_VERSION, MIN(strlen(FIXED_VERSION), MaxSize)); - return LLVMFuzzerMutate(Data, Size, MaxSize); + // Make up a packet, writing direct to the buffer + inp->size = MaxSize; + buf_setlen(inp, 0); + buf_putbytes(inp, FIXED_VERSION, strlen(FIXED_VERSION)); + buf_putbytes(inp, FIXED_IGNORE_MSG, FIXED_IGNORE_MSG_LEN); + // printhex("mutator no input", Data, inp->len); + return inp->len; } /* Start output */ @@ -157,40 +188,52 @@ size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, buf_setlen(alloc_packetA, 0); buf_setlen(alloc_packetB, 0); - /* 5% chance each */ - const int optA = nrand48(randstate) % 20; + /* 2% chance each */ + const int optA = nrand48(randstate) % 50; if (optA == 0) { /* Copy another */ unsigned int other = nrand48(randstate) % num_packets; out_packetA = packets[other]; + // printf("copy another %d / %d len %u\n", other, num_packets, out_packetA->len); } if (optA == 1) { /* Mutate another */ unsigned int other = nrand48(randstate) % num_packets; - buffer *from = packets[other]; - buf_putbytes(alloc_packetA, from->data, from->len); out_packetA = alloc_packetA; - buf_llvm_mutate(out_packetA); + buffer *from = packets[other]; + buf_putbytes(out_packetA, from->data, from->len); + if (buf_llvm_mutate(out_packetA) == DROPBEAR_FAILURE) { + out_packetA = NULL; + } + // printf("mutate another %d / %d len %u -> %u\n", other, num_packets, from->len, out_packetA->len); } if (i < num_packets) { - int optB = nrand48(randstate) % 10; + int optB = nrand48(randstate) % 100; if (optB == 1) { - /* 10% chance of drop */ + /* small chance of drop */ /* Drop it */ - // printf("%d drop\n", i); - } else if (optB <= 6) { - /* Mutate it, 50% chance */ - // printf("%d mutate\n", i); - buffer *from = packets[nrand48(randstate) % num_packets]; - buf_putbytes(alloc_packetB, from->data, from->len); - out_packetB = alloc_packetB; - buf_llvm_mutate(out_packetB); - } else { - /* Copy as-is */ - out_packetB = packets[i]; - // printf("%d as-is\n", i); - } + //printf("%d drop\n", i); + } else { + /* Odds of modification are proportional to packet position. + First packet has 20% chance, last has 100% chance */ + int optC = nrand48(randstate) % 1000; + int mutate_cutoff = MAX(200, (1000 * (i+1) / num_packets)); + if (optC < mutate_cutoff) { + // // printf("%d mutate\n", i); + out_packetB = alloc_packetB; + buffer *from = packets[i]; + buf_putbytes(out_packetB, from->data, from->len); + if (buf_llvm_mutate(out_packetB) == DROPBEAR_FAILURE) { + out_packetB = from; + } + // printf("mutate self %d / %d len %u -> %u\n", i, num_packets, from->len, out_packetB->len); + } else { + /* Copy as-is */ + out_packetB = packets[i]; + // printf("%d as-is len %u\n", i, out_packetB->len); + } + } } if (out_packetA && oup->len + out_packetA->len <= oup->size) { @@ -201,7 +244,7 @@ size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, } } - size_t ret_len = MIN(MaxSize, oup->len); + ret_len = MIN(MaxSize, oup->len); memcpy(Data, oup->data, ret_len); // printhex("mutator done", Data, ret_len); return ret_len; @@ -225,30 +268,39 @@ size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, unsigned int num_packets2 = MAX_FUZZ_PACKETS; fuzz_get_packets(inp2, packets2, &num_packets2); + // fprintf(stderr, "input 1 %u packets\n", num_packets1); + // printhex("crossover input1", Data1, Size1); + // fprintf(stderr, "input 2 %u packets\n", num_packets2); + // printhex("crossover input2", Data2, Size2); + buf_setlen(oup, 0); /* Put a new banner to output */ buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION)); - for (i = 0; i < num_packets1+1; i++) { - if (num_packets2 > 0 && nrand48(randstate) % 10 == 0) { - /* 10% chance of taking another packet at each position */ - int other = nrand48(randstate) % num_packets2; - // printf("inserted other packet %d at %d\n", other, i); - buffer *otherp = packets2[other]; - if (oup->len + otherp->len <= oup->size) { - buf_putbytes(oup, otherp->data, otherp->len); + if (num_packets1 == 0 && num_packets2 == 0) { + buf_putbytes(oup, FIXED_IGNORE_MSG, FIXED_IGNORE_MSG_LEN); + } else { + unsigned int min_out = MIN(num_packets1, num_packets2); + unsigned int max_out = num_packets1 + num_packets2; + unsigned int num_out = min_out + nrand48(randstate) % (max_out-min_out+1); + + for (i = 0; i < num_out; i++) { + int choose = nrand48(randstate) % (num_packets1 + num_packets2); + buffer *p = NULL; + if (choose < num_packets1) { + p = packets1[choose]; + } else { + p = packets2[choose-num_packets1]; } - } - if (i < num_packets1) { - buffer *thisp = packets1[i]; - if (oup->len + thisp->len <= oup->size) { - buf_putbytes(oup, thisp->data, thisp->len); + if (oup->len + p->len <= oup->size) { + buf_putbytes(oup, p->data, p->len); } } } size_t ret_len = MIN(MaxOutSize, oup->len); memcpy(Out, oup->data, ret_len); + // printhex("crossover output", Out, ret_len); return ret_len; } |