summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2020-11-01 23:44:58 +0800
committerMatt Johnston <matt@ucc.asn.au>2020-11-01 23:44:58 +0800
commit121e6e620269ff3eef7ef46d6ab7057e56317588 (patch)
tree029784932a0b530cc753da437528d69f6027fdb1
parent1b6e16ae7c9105e35dd14464b8ce785c613ab6e2 (diff)
Fuzzing - get rid of "prefix" for streams
Improved packet generation with sshpacketmutator
-rw-r--r--dbrandom.c16
-rw-r--r--fuzz/fuzz-common.c41
-rw-r--r--fuzz/fuzz-sshpacketmutator.c168
3 files changed, 123 insertions, 102 deletions
diff --git a/dbrandom.c b/dbrandom.c
index 7aaa42b..3f21593 100644
--- a/dbrandom.c
+++ b/dbrandom.c
@@ -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;
}