diff options
author | Matt Johnston <matt@ucc.asn.au> | 2020-10-25 22:52:36 +0800 |
---|---|---|
committer | Matt Johnston <matt@ucc.asn.au> | 2020-10-25 22:52:36 +0800 |
commit | 4e8a1da551db44e5865f6a1cc28e24fc2c2bdd0f (patch) | |
tree | 409aebeb7bd0f03248dc0716d01a408cc09ed878 /fuzz | |
parent | 754dc50b933c76ca39660fe2d9e65e35f9aef428 (diff) |
Add first try at fuzzing custom mutator
Diffstat (limited to 'fuzz')
-rw-r--r-- | fuzz/fuzz-common.c | 4 | ||||
-rw-r--r-- | fuzz/fuzz-harness.c | 6 | ||||
-rw-r--r-- | fuzz/fuzz-sshpacketmutator.c | 175 | ||||
-rw-r--r-- | fuzz/fuzzer-client_mutator.c | 8 |
4 files changed, 193 insertions, 0 deletions
diff --git a/fuzz/fuzz-common.c b/fuzz/fuzz-common.c index 065b7d9..8dddacb 100644 --- a/fuzz/fuzz-common.c +++ b/fuzz/fuzz-common.c @@ -256,10 +256,12 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) { int fakesock = wrapfd_new(); m_malloc_set_epoch(1); + fuzz.do_jmp = 1; if (setjmp(fuzz.jmp) == 0) { svr_session(fakesock, fakesock); m_malloc_free_epoch(1, 0); } else { + fuzz.do_jmp = 0; m_malloc_free_epoch(1, 1); TRACE(("dropbear_exit longjmped")) /* dropbear_exit jumped here */ @@ -302,10 +304,12 @@ int fuzz_run_client(const uint8_t *Data, size_t Size, int skip_kexmaths) { int fakesock = wrapfd_new(); m_malloc_set_epoch(1); + fuzz.do_jmp = 1; if (setjmp(fuzz.jmp) == 0) { cli_session(fakesock, fakesock, NULL, 0); m_malloc_free_epoch(1, 0); } else { + fuzz.do_jmp = 0; m_malloc_free_epoch(1, 1); TRACE(("dropbear_exit longjmped")) /* dropbear_exit jumped here */ diff --git a/fuzz/fuzz-harness.c b/fuzz/fuzz-harness.c index ced707c..36905d6 100644 --- a/fuzz/fuzz-harness.c +++ b/fuzz/fuzz-harness.c @@ -46,3 +46,9 @@ int main(int argc, char ** argv) { return 0; } + +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { + printf("standalone fuzzer harness shouldn't call LLVMFuzzerMutate"); + abort(); + return 0; +} diff --git a/fuzz/fuzz-sshpacketmutator.c b/fuzz/fuzz-sshpacketmutator.c new file mode 100644 index 0000000..fa0ed5f --- /dev/null +++ b/fuzz/fuzz-sshpacketmutator.c @@ -0,0 +1,175 @@ +#include "fuzz.h" + +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); + +static void fuzz_get_packets(buffer *inp, buffer **out_packets, unsigned int *num_out_packets) { + /* Skip any existing banner. Format is + SSH-protoversion-softwareversion SP comments CR LF + so we look for SSH-2. then a subsequent LF */ + unsigned char* version = memmem(inp->data, inp->len, "SSH-2.", strlen("SSH-2.")); + if (version) { + buf_incrpos(inp, version - inp->data); + unsigned char* newline = memchr(&inp->data[inp->pos], '\n', inp->len - inp->pos); + if (newline) { + buf_incrpos(inp, newline - &inp->data[inp->pos]); + } else { + /* Give up on any version string */ + buf_setpos(inp, 0); + } + } + + const unsigned int max_out_packets = *num_out_packets; + *num_out_packets = 0; + while (1) { + if (inp->pos + 4 > inp->len) { + /* End of input */ + break; + } + + if (*num_out_packets >= max_out_packets) { + /* End of output */ + break; + } + + /* Read packet */ + unsigned int packet_len = buf_getint(inp); + if (packet_len <= RECV_MAX_PACKET_LEN) { + /* Bad length, try skipping a single byte */ + buf_decrpos(inp, 3); + continue; + } + packet_len = MIN(packet_len, inp->len - inp->pos); + + /* Copy to output buffer */ + buffer* new_packet = buf_new(RECV_MAX_PACKET_LEN); + buf_putint(new_packet, packet_len); + buf_putbytes(new_packet, buf_getptr(inp, packet_len), packet_len); + buf_incrpos(inp, packet_len); + + out_packets[*num_out_packets] = new_packet; + (*num_out_packets)++; + } + +} + +/* Mutate in-place */ +void buf_llvm_mutate(buffer *buf) { + /* Position it after packet_length and padding_length */ + const unsigned int offset = 5; + if (buf->len < offset) { + return; + } + buf_setpos(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); +} + + +static const char* FIXED_VERSION = "SSH-2.0-dbfuzz\r\n"; +static const size_t MAX_FUZZ_PACKETS = 500; +/* XXX This might need tuning */ +static const size_t MAX_OUT_SIZE = 50000; + +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) { + int i; + unsigned short randstate[3] = {0,0,0}; + memcpy(randstate, &Seed, sizeof(Seed)); + + /* 1% chance straight llvm mutate */ + if (nrand48(randstate) % 100 == 0) { + return LLVMFuzzerMutate(Data, Size, MaxSize); + } + + buffer inp_buf = {.data = Data, .size = Size, .len = Size, .pos = 0}; + buffer *inp = &inp_buf; + + /* Parse packets */ + buffer* packets[MAX_FUZZ_PACKETS] = {0}; + unsigned int num_packets = MAX_FUZZ_PACKETS; + 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); + } + + /* Start output */ + buffer *oup = buf_new(MAX_OUT_SIZE); + /* Put a new banner to output */ + buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION)); + + /* Iterate output */ + for (i = 0; i < num_packets+1; i++) { + // These are pointers to output + buffer *out_packetA = NULL, *out_packetB = NULL; + // These need to be freed + buffer *alloc_packetA = NULL, *alloc_packetB = NULL; + + /* 5% chance each */ + const int optA = nrand48(randstate) % 20; + const int other = nrand48(randstate) % num_packets; + if (optA == 0) { + /* Copy another */ + out_packetA = packets[nrand48(randstate) % num_packets]; + } + if (optA == 1) { + /* Mutate another */ + alloc_packetA = buf_new(RECV_MAX_PACKET_LEN); + buffer *from = packets[nrand48(randstate) % num_packets]; + buf_putbytes(alloc_packetA, from->data, from->len); + out_packetA = alloc_packetA; + buf_llvm_mutate(out_packetA); + } + + /* 10% chance each of mutate or drop */ + if (i < num_packets) { + int optB = nrand48(randstate) % 10; + if (optB == 0) { + /* Copy as-is */ + out_packetB = packets[i]; + } + if (optB == 1) { + /* Drop it */ + } + if (optB == 2) { + /* Mutate it */ + alloc_packetB = buf_new(RECV_MAX_PACKET_LEN); + buffer *from = packets[nrand48(randstate) % num_packets]; + buf_putbytes(alloc_packetB, from->data, from->len); + out_packetB = alloc_packetB; + buf_llvm_mutate(out_packetB); + } + } + + if (out_packetA && oup->len + out_packetA->len <= oup->size) { + buf_putbytes(oup, out_packetA->data, out_packetA->len); + } + if (out_packetB && oup->len + out_packetB->len <= oup->size) { + buf_putbytes(oup, out_packetB->data, out_packetB->len); + } + if (alloc_packetA) { + buf_free(alloc_packetA); + alloc_packetA = NULL; + } + if (alloc_packetB) { + buf_free(alloc_packetB); + alloc_packetB = NULL; + } + } + + for (i = 0; i < num_packets; i++) { + buf_free(packets[i]); + } + + size_t ret_len = MIN(MaxSize, oup->len); + memcpy(Data, oup->data, ret_len); + buf_free(oup); + return ret_len; +} + diff --git a/fuzz/fuzzer-client_mutator.c b/fuzz/fuzzer-client_mutator.c new file mode 100644 index 0000000..df57314 --- /dev/null +++ b/fuzz/fuzzer-client_mutator.c @@ -0,0 +1,8 @@ +#include "fuzz.h" + +#include "fuzz-sshpacketmutator.c" + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + return fuzz_run_client(Data, Size, 0); +} + |