aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaik Betka <9078425+voidptr127@users.noreply.github.com>2023-04-17 17:09:48 +0200
committerMaik Betka <9078425+voidptr127@users.noreply.github.com>2023-04-17 17:09:48 +0200
commit450dbae8cd2ae278e0b381d172cb7694f40278cb (patch)
tree3235b4a6263c756d435139a6b3fd2d03d66bba1e
parenta3bc8d3440acefb7168c849337e4a4841bf11c3d (diff)
downloadafl++-450dbae8cd2ae278e0b381d172cb7694f40278cb.tar.gz
first version with unix domain sockets is ready for testing
-rw-r--r--custom_mutators/atnwalk/atnwalk.c254
-rw-r--r--src/afl-fuzz-run.c11
2 files changed, 232 insertions, 33 deletions
diff --git a/custom_mutators/atnwalk/atnwalk.c b/custom_mutators/atnwalk/atnwalk.c
index cc9f9618..584add61 100644
--- a/custom_mutators/atnwalk/atnwalk.c
+++ b/custom_mutators/atnwalk/atnwalk.c
@@ -1,5 +1,4 @@
-#include "../../include/afl-fuzz.h"
-
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -7,10 +6,10 @@
#include <sys/un.h>
#include <unistd.h>
-
#define INIT_BUF_SIZE 4096
#define SOCKET_NAME "/tmp/atnwalk.socket"
+
// handshake constants
const uint8_t SERVER_ARE_YOU_ALIVE = 42;
const uint8_t SERVER_YES_I_AM_ALIVE = 213;
@@ -23,8 +22,10 @@ const uint8_t SERVER_ENCODE_BIT = 0b00001000;
typedef struct atnwalk_mutator {
- uint8_t *decoded_buf;
- size_t decoded_size;
+ uint8_t *fuzz_buf;
+ size_t fuzz_size;
+ uint8_t *post_process_buf;
+ size_t post_process_size;
} atnwalk_mutator_t;
@@ -55,6 +56,32 @@ int write_all(int fd, uint8_t *buf, size_t buf_size) {
return 1;
}
+void put_uint32(uint8_t *buf, uint32_t val) {
+ buf[0] = (uint8_t) (val >> 24);
+ buf[1] = (uint8_t) ((val & 0x00ff0000) >> 16);
+ buf[2] = (uint8_t) ((val & 0x0000ff00) >> 8);
+ buf[3] = (uint8_t) (val & 0x000000ff);
+}
+
+uint32_t to_uint32(uint8_t *buf) {
+ uint32_t val = 0;
+ val |= (((uint32_t) buf[0]) << 24);
+ val |= (((uint32_t) buf[1]) << 16);
+ val |= (((uint32_t) buf[2]) << 8);
+ val |= ((uint32_t) buf[3]);
+ return val;
+}
+
+void put_uint64(uint8_t *buf, uint64_t val) {
+ buf[0] = (uint8_t) (val >> 56);
+ buf[1] = (uint8_t) ((val & 0x00ff000000000000) >> 48);
+ buf[2] = (uint8_t) ((val & 0x0000ff0000000000) >> 40);
+ buf[3] = (uint8_t) ((val & 0x000000ff00000000) >> 32);
+ buf[4] = (uint8_t) ((val & 0x00000000ff000000) >> 24);
+ buf[5] = (uint8_t) ((val & 0x0000000000ff0000) >> 16);
+ buf[6] = (uint8_t) ((val & 0x000000000000ff00) >> 8);
+ buf[7] = (uint8_t) (val & 0x00000000000000ff);
+}
/**
* Initialize this custom mutator
@@ -67,19 +94,21 @@ int write_all(int fd, uint8_t *buf, size_t buf_size) {
* There may be multiple instances of this mutator in one afl-fuzz run!
* Return NULL on error.
*/
-atnwalk_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
+atnwalk_mutator_t *afl_custom_init(void *afl, unsigned int seed) {
srand(seed);
atnwalk_mutator_t *data = (atnwalk_mutator_t *) malloc(sizeof(atnwalk_mutator_t));
if (!data) {
perror("afl_custom_init alloc");
return NULL;
}
- data->decoded_buf = (uint8_t *) malloc(INIT_BUF_SIZE);
- data->decoded_size = INIT_BUF_SIZE;
+ data->fuzz_buf = (uint8_t *) malloc(INIT_BUF_SIZE);
+ data->fuzz_size = INIT_BUF_SIZE;
+ data->post_process_buf = (uint8_t *) malloc(INIT_BUF_SIZE);
+ data->post_process_size = INIT_BUF_SIZE;
return data;
}
-// TODO: implement
+
/**
* Perform custom mutations on a given input
*
@@ -100,13 +129,12 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
uint8_t *add_buf, size_t add_buf_size, size_t max_size) {
struct sockaddr_un addr;
int fd_socket;
- ssize_t n;
- uint8_t buffer[5];
+ uint8_t ctrl_buf[8];
+ uint8_t wanted;
// initialize the socket
fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd_socket == -1) {
- perror("socket");
*out_buf = NULL;
return 0;
}
@@ -114,26 +142,127 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
if (connect(fd_socket, (const struct sockaddr *) &addr, sizeof(addr)) == -1) {
- perror("atnwalk server is down");
+ close(fd_socket);
+ *out_buf = NULL;
+ return 0;
+ }
+
+ // TODO: how to set connection deadline? maybe not required if server already closes the connection?
+
+ // TODO: there should be some kind of loop retrying with different seeds and ultimately giving up on that input?
+ // maybe this is not necessary, because we may also just return a single byte in case of failure?
+
+ // ask whether the server is alive
+ ctrl_buf[0] = SERVER_ARE_YOU_ALIVE;
+ if (!write_all(fd_socket, ctrl_buf, 1)) {
+ close(fd_socket);
*out_buf = NULL;
return 0;
}
- if (!write_all(fd_socket, buffer, 5)) {
- perror("write to atnwalk server failed");
+ // see whether the server replies as expected
+ if (!read_all(fd_socket, ctrl_buf, 1) || ctrl_buf[0] != SERVER_YES_I_AM_ALIVE) {
+ close(fd_socket);
+ *out_buf = NULL;
+ return 0;
+ }
+
+ // tell the server what we want to do
+ wanted = SERVER_MUTATE_BIT | SERVER_ENCODE_BIT;
+
+ // 50% chance to perform a crossover if there is an additional buffer available
+ if ((add_buf_size > 0) && (rand() % 2)) {
+ wanted |= SERVER_CROSSOVER_BIT;
+ }
+
+ // tell the server what we want and how much data will be sent
+ ctrl_buf[0] = wanted;
+ put_uint32(ctrl_buf + 1, (uint32_t) buf_size);
+ if (!write_all(fd_socket, ctrl_buf, 5)) {
+ close(fd_socket);
*out_buf = NULL;
return 0;
}
- if (read_all(fd_socket, buffer, 5)) {
- perror("read to atnwalk server failed");
- exit(EXIT_FAILURE);
+ // send the data to mutate and encode
+ if (!write_all(fd_socket, buf, buf_size)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+
+ if (wanted & SERVER_CROSSOVER_BIT) {
+ // since we requested crossover, we will first tell how much additional data is to be expected
+ put_uint32(ctrl_buf, (uint32_t) add_buf_size);
+ if (!write_all(fd_socket, ctrl_buf, 4)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+
+ // send the additional data for crossover
+ if (!write_all(fd_socket, add_buf, add_buf_size)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+
+ // lastly, a seed is required for crossover so send one
+ put_uint64(ctrl_buf, (uint64_t) rand());
+ if (!write_all(fd_socket, ctrl_buf, 8)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+ }
+
+ // since we requested mutation, we need to provide a seed for that
+ put_uint64(ctrl_buf, (uint64_t) rand());
+ if (!write_all(fd_socket, ctrl_buf, 8)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+
+ // obtain the required buffer size for the data that will be returned
+ if (!read_all(fd_socket, ctrl_buf, 4)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+ size_t new_size = (size_t) to_uint32(ctrl_buf);
+
+ // if the data is too large then we ignore this round
+ if (new_size > max_size) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+
+ if (new_size > buf_size) {
+ // buf is too small, need to use data->fuzz_buf, let's see whether we need to reallocate
+ if (new_size > data->fuzz_size) {
+ data->fuzz_size = new_size << 1;
+ data->fuzz_buf = (uint8_t *) realloc(data->fuzz_buf, data->fuzz_size);
+ }
+ *out_buf = data->fuzz_buf;
+ } else {
+ // new_size fits into buf, so re-use it
+ *out_buf = buf;
+ }
+
+ // obtain the encoded data
+ if (!read_all(fd_socket, *out_buf, new_size)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
}
close(fd_socket);
+ return new_size;
}
-// TODO: implement
+
/**
* A post-processing function to use right before AFL writes the test case to
* disk in order to execute the target.
@@ -151,23 +280,90 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
* A return of 0 indicates an error.
*/
size_t afl_custom_post_process(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf) {
- data->decoded_buf[0] = 'p';
- data->decoded_buf[1] = 'u';
- data->decoded_buf[2] = 't';
- data->decoded_buf[3] = 's';
- data->decoded_buf[4] = ' ';
- data->decoded_buf[5] = ';';
- data->decoded_buf[6] = '\n';
- return 7;
+ struct sockaddr_un addr;
+ int fd_socket;
+ uint8_t ctrl_buf[8];
+
+ // initialize the socket
+ fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd_socket == -1) {
+ *out_buf = NULL;
+ return 0;
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
+ if (connect(fd_socket, (const struct sockaddr *) &addr, sizeof(addr)) == -1) {
+ close(fd_socket);
+ *out_buf = NULL;
+ return 0;
+ }
+
+ // ask whether the server is alive
+ ctrl_buf[0] = SERVER_ARE_YOU_ALIVE;
+ if (!write_all(fd_socket, ctrl_buf, 1)) {
+ close(fd_socket);
+ *out_buf = NULL;
+ return 0;
+ }
+
+ // see whether the server replies as expected
+ if (!read_all(fd_socket, ctrl_buf, 1) || ctrl_buf[0] != SERVER_YES_I_AM_ALIVE) {
+ close(fd_socket);
+ *out_buf = NULL;
+ return 0;
+ }
+
+ // tell the server what we want and how much data will be sent
+ ctrl_buf[0] = SERVER_DECODE_BIT;
+ put_uint32(ctrl_buf + 1, (uint32_t) buf_size);
+ if (!write_all(fd_socket, ctrl_buf, 5)) {
+ close(fd_socket);
+ *out_buf = NULL;
+ return 0;
+ }
+
+ // send the data to decode
+ if (!write_all(fd_socket, buf, buf_size)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+
+ // obtain the required buffer size for the data that will be returned
+ if (!read_all(fd_socket, ctrl_buf, 4)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+ size_t new_size = (size_t) to_uint32(ctrl_buf);
+
+ // need to use data->post_process_buf, let's see whether we need to reallocate
+ if (new_size > data->post_process_size) {
+ data->post_process_size = new_size << 1;
+ data->post_process_buf = (uint8_t *) realloc(data->post_process_buf, data->post_process_size);
+ }
+ *out_buf = data->post_process_buf;
+
+ // obtain the decoded data
+ if (!read_all(fd_socket, *out_buf, new_size)) {
+ close(fd_socket);
+ *out_buf = buf;
+ return buf_size;
+ }
+
+ close(fd_socket);
+ return new_size;
}
-// TODO: implement
+
/**
* Deinitialize everything
*
* @param data The data ptr from afl_custom_init
*/
void afl_custom_deinit(atnwalk_mutator_t *data) {
- free(data->decoded_buf);
+ free(data->fuzz_buf);
+ free(data->post_process_buf);
free(data);
}
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index ee4a3298..26a1ea36 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -130,12 +130,15 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) {
new_size = afl->max_length;
}
-
- if (new_mem != *mem) { *mem = new_mem; }
+ // TODO: think about how to enable the change without breaking other implementations
+ // if (new_mem != *mem) { *mem = new_mem; }
/* everything as planned. use the potentially new data. */
- afl_fsrv_write_to_testcase(&afl->fsrv, *mem, new_size);
- len = new_size;
+ // TODO: think about how to enable the change without breaking other implementations
+ afl_fsrv_write_to_testcase(&afl->fsrv, new_mem, new_size);
+
+ // TODO: think about how to enable the change without breaking other implementations
+ // len = new_size;
} else {