├── host_stop.sh ├── rage.h ├── makefile ├── libmutant.h ├── .gitignore ├── host_rage.sh ├── todo.txt ├── README.md ├── libmutant.c ├── rage.c └── master_packets.txt /host_stop.sh: -------------------------------------------------------------------------------- 1 | killall -9 rage 2 | -------------------------------------------------------------------------------- /rage.h: -------------------------------------------------------------------------------- 1 | 2 | extern int debug; 3 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(CC) -Wall -o rage rage.c libmutant.c 3 | -------------------------------------------------------------------------------- /libmutant.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | unsigned char* do_fuzz_random(unsigned char *databuf, int data_buffer_len); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | # mine 35 | seeds.log 36 | rage 37 | -------------------------------------------------------------------------------- /host_rage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set +v 4 | if [ -n "$1" ] 5 | then 6 | echo 'about to rage against host: ' $1 7 | command -v nmap > /dev/null 2>&1 || { echo "You dont appear to have nmap. exiting"; exit 1; } 8 | for portnum in `nmap $1 -sT | grep open | cut -d '/' -f 1`; 9 | do 10 | echo $portnum 11 | screen -S ragePort$portnum -d -m ./rage -p $portnum -f ./master_packets.txt -t $1 12 | done; 13 | else 14 | echo 'must give me a target host. exiting' 15 | exit 16 | fi 17 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | todo 2 | 3 | - exit after N packets/seconds (to enable re-seed) 4 | 5 | - tidy up/refactor: consistent style, fix shitty code, refactor 6 | - debug should be -D 7 | 8 | - randomize which packet is chosen to randomly string together handshake sequences 9 | 10 | --libmutant-- 11 | - specific byte-range-offset mutation - target specific part of proto (libmutant) 12 | - configurable percent-mutate 13 | - different mutate strategies (radamsa)/libmutant +inject +bitflip +nullbyte 14 | 15 | - event/poll IO 16 | 17 | - remote ssh process/log watch 18 | 19 | - metrics, performance stats 20 | - more L3,L4 network 21 | - server coverage tracing 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rage against the network 2 | ======================== 3 | 4 | a dumb protocol-unaware packet fuzzer/replayer 5 | ---------------------------------------------- 6 | 7 | ## Basic ideas: 8 | * ridiculously easy to use (near-zero setup cost) 9 | * protocol-unaware; blind brute-force fuzzing 10 | * fairly quick (todo: proper epoll) 11 | * comes with a collection of packets for common protocols ready to go 12 | 13 | ## Current status: 14 | * IP only (TCP and UDP) 15 | * Client-side only 16 | * not particularly fast (single-thread blocking) 17 | * deterministic; fuzz-runs can be repeated given the seed of a prior run 18 | * target process monitoring is left as an excersize to the reader 19 | * naive unconfigurable mutations 20 | 21 | ## Usage 22 | ``` 23 | Usage: rage [-d] -p -t -f 24 | -f filename file to read packet zoo from 25 | -d enable debug [excessive] 26 | -l print out all packets in file 27 | -p portnum specify target port for fuzzing 28 | -t host specify target host for fuzzing 29 | -s milliseconds specify a send delay 30 | -b don't fuzz, send original packets and exit 31 | -r provide a seed for srand (repeat a fuzz run) 32 | -c number of packets sent before forced reconnect 33 | -z a dummy arg to ID different fuzzers on same host 34 | 35 | ``` 36 | ## packet file format 37 | todo 38 | 39 | ## doing a fuzz run 40 | todo 41 | 42 | ## bugs found 43 | todo 44 | -------------------------------------------------------------------------------- /libmutant.c: -------------------------------------------------------------------------------- 1 | /* basic mutation stuff */ 2 | 3 | #include 4 | #include 5 | #include "rage.h" 6 | 7 | #define FUZZ_RATIO_PC 0.05 8 | #define CHUNK_DUPE_MAX_PC 0.25 9 | 10 | unsigned char* do_byte_percent_mutate(unsigned char *databuf, unsigned int data_buffer_len) 11 | { 12 | unsigned int bytes_to_fuzz, i, location; 13 | unsigned char mutatedbyte; 14 | bytes_to_fuzz = (data_buffer_len * FUZZ_RATIO_PC); 15 | for (i=0; i data_buffer_len) 39 | { 40 | location = data_buffer_len - chunk_len; 41 | } 42 | retbuf=NULL; 43 | return retbuf; 44 | } 45 | 46 | unsigned char* do_fuzz_random(unsigned char *databuf, unsigned int data_buffer_len) 47 | { 48 | unsigned char *retbuf; 49 | retbuf = do_byte_percent_mutate(databuf, data_buffer_len); 50 | return retbuf; 51 | } 52 | -------------------------------------------------------------------------------- /rage.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "libmutant.h" 14 | #include "rage.h" 15 | 16 | #define MAX_SOCKET_ERRORS 10 17 | #define MAX_CONNECT_ERRORS 10 18 | 19 | #if defined(__APPLE__) || defined(__MACH__) 20 | # ifndef MSG_NOSIGNAL 21 | # define MSG_NOSIGNAL SO_NOSIGPIPE 22 | # endif 23 | #endif 24 | 25 | int debug =0; 26 | int udpmode=0; 27 | int send_delay=0; 28 | int print_packets=0; 29 | int modify_payload=1; 30 | int packet_loop_counter=0; 31 | int packet_loop_counter_max=10; 32 | int socket_errors=0; 33 | int connect_errors =0; 34 | float FUZZ_RATIO = 0.05; 35 | 36 | int sockfd = (uintptr_t)NULL; 37 | 38 | void init_sock(); 39 | 40 | struct packetDescription 41 | { 42 | char l3[4]; 43 | char l4[4]; 44 | int sport; 45 | int dport; 46 | char direction[4]; 47 | char* hexdata; 48 | char* comment; 49 | struct packetDescription *next; 50 | }; 51 | 52 | struct packetDescription *head = NULL; 53 | struct packetDescription *current = NULL; 54 | 55 | void usage() 56 | { 57 | printf("Usage: rage [-d] -p -t -f \n"); 58 | printf(" -f filename file to read packet zoo from\n"); 59 | printf(" -d enable debug [excessive]\n"); 60 | printf(" -u UDP mode\n"); 61 | printf(" -l print out all packets in file\n"); 62 | printf(" -p portnum specify target port for fuzzing\n"); 63 | printf(" -t host specify target host for fuzzing\n"); 64 | printf(" -s milliseconds specify a send delay \n"); 65 | printf(" -b don't fuzz, send original packets and exit \n"); 66 | printf(" -r seed provide a seed for srand (repeat a fuzz run)\n"); 67 | printf(" -c number of packets sent before forced reconnect\n"); 68 | printf(" -z a dummy arg to ID different fuzzers on same host\n"); 69 | printf(" -h this help screen\n"); 70 | exit(1); 71 | } 72 | 73 | 74 | void addToList(char *line) 75 | { 76 | struct packetDescription *newpkt 77 | = (struct packetDescription*)malloc(sizeof(struct packetDescription)); 78 | char *token; 79 | int field=0; 80 | if (line[0]=='#') 81 | { 82 | if (debug) {printf("Bailing on line with comment\n");} 83 | free(newpkt); 84 | return; 85 | } 86 | char *c; 87 | c = strchr(line,'\n'); 88 | *c = '\0'; 89 | if (debug) {printf("Full Line: ----%s----\n",line);} 90 | token = strtok(line,":"); 91 | while (token !=NULL) 92 | { 93 | if (field==0) { 94 | strcpy(newpkt->l3,token); 95 | if (debug) {printf("l3: %s\n",token);} 96 | } else if (field==1) { 97 | strcpy(newpkt->l4,token); 98 | if (debug) {printf("l4: %s\n",token);} 99 | } else if (field==2) { 100 | newpkt->sport=atoi(token); 101 | if (debug) {printf("sport: %d\n",newpkt->sport);} 102 | } else if (field==3) { 103 | newpkt->dport=atoi(token); 104 | if (debug) {printf("dport: %d\n",newpkt->dport);} 105 | } else if (field==4) { 106 | strcpy(newpkt->direction,token); 107 | if (debug) {printf("direction: %s\n",token);} 108 | } else if (field==5) { 109 | newpkt->hexdata = malloc(strlen(token)+1); 110 | strcpy(newpkt->hexdata,token); 111 | if (debug) {printf("hexdata: %s\n",token);} 112 | } else if (field==6) { 113 | newpkt->comment = malloc(strlen(token)+1); 114 | strcpy(newpkt->comment,token); 115 | if (debug) {printf("comment: %s\n",token);} 116 | } 117 | token = strtok(NULL,":"); 118 | field++; 119 | } 120 | if (head==NULL) 121 | { 122 | head = newpkt; 123 | newpkt->next=NULL; 124 | } else { 125 | newpkt->next = head; 126 | head = newpkt; 127 | } 128 | return; 129 | } 130 | 131 | int ascii_char_to_num(char c) 132 | { 133 | int i = strtol((char []){c,0},NULL,16); 134 | return i; 135 | } 136 | 137 | void get_raw_from_ascii_hex(char *input, unsigned char *output) 138 | { 139 | if (debug) {printf("debug: get_raw_from_ascii_hex\n");} 140 | int bytelen; 141 | int i; 142 | bytelen = strlen(input); 143 | if (debug) {printf("debug: input len: %d\n",bytelen);} 144 | 145 | char *ptr; 146 | ptr = input; 147 | 148 | for (i=0;idport==portNo) 167 | { 168 | printf("PacketComment port %d: %s\n",portNo,current->comment); 169 | } 170 | current=current->next; 171 | } 172 | return; 173 | } 174 | 175 | void getPacketDescriptions(FILE *fp) 176 | { 177 | char line[12000];//todo wtf 178 | void *r; 179 | int total_lines=0; 180 | r=fgets(line,12000,fp); 181 | while (r!=NULL) 182 | { 183 | addToList(line); 184 | r=fgets(line,12000,fp); 185 | total_lines++; 186 | } 187 | printf("[+] reading %d lines complete\n",total_lines); 188 | return; 189 | } 190 | 191 | void print_all_packets(int portnum) 192 | { 193 | if (debug) {printf("Printing all packets with portnum %d\n",portnum);} 194 | current = head; 195 | while (current != NULL) 196 | { 197 | char sport_text[8]; 198 | char dport_text[8]; 199 | char direction[4]; 200 | char outtext[128]; 201 | int outlen=0; 202 | int i; 203 | if (portnum!=0 && portnum!=current->dport) 204 | { 205 | current=current->next; 206 | continue; 207 | } 208 | if (current->sport==0) 209 | { 210 | strcpy(sport_text,"*"); 211 | } 212 | else 213 | { 214 | sprintf(sport_text,"%d",current->sport); 215 | } 216 | if (current->dport==0) 217 | { 218 | strcpy(dport_text,"*"); 219 | } 220 | else 221 | { 222 | sprintf(dport_text,"%d",current->dport); 223 | } 224 | if (strcmp(current->direction,"CS")) 225 | { 226 | strcpy(direction,"<--"); 227 | } else 228 | { 229 | strcpy(direction,"-->"); 230 | } 231 | sprintf(outtext,"%s/%s [ %s %s %s ]",current->l3,current->l4,sport_text,direction,dport_text); 232 | outlen = strlen(outtext); 233 | for (i=24;i>outlen;i--) strcat(outtext," "); 234 | strcat(outtext,current->comment); 235 | printf("%s\n",outtext); 236 | current=current->next; 237 | } 238 | return; 239 | } 240 | 241 | unsigned char * ascii_to_binary(char *input) 242 | { 243 | if (debug) {printf("debug: called into ascii_to_binary with %u bytes\n",(unsigned int)strlen(input));} 244 | if (debug) {printf("debug: ascii_to_binary in: %s\n",input);} 245 | unsigned char *output; 246 | output = malloc((strlen(input)/2)+1); 247 | get_raw_from_ascii_hex(input,output); 248 | return output; 249 | } 250 | 251 | 252 | void send_packet(unsigned char *databuf,int portnum,char *target_host, int data_buffer_len) 253 | { 254 | struct sockaddr_in dest; 255 | int sendval; 256 | int sendtoerr; 257 | 258 | bzero((char *)&dest, sizeof(dest)); 259 | dest.sin_family = AF_INET; 260 | dest.sin_port = htons(portnum); 261 | 262 | if (inet_aton(target_host, (struct in_addr *)&dest.sin_addr.s_addr)==0) 263 | { 264 | printf("Error with address\n"); 265 | exit(errno); 266 | } 267 | if (debug) {printf("Addr: %s\n",target_host);} 268 | if (debug) {printf("Send()ing %d bytes thru sock: 0x%x\n",data_buffer_len,sockfd); } 269 | if (udpmode) 270 | { 271 | sendtoerr = sendto(sockfd,databuf,data_buffer_len,0,(struct sockaddr*)&dest,sizeof(dest)); 272 | if (sendtoerr < 0) 273 | { 274 | perror("sendto() failed"); 275 | exit(1); 276 | } 277 | } else { 278 | sendval = send(sockfd, databuf, data_buffer_len, MSG_NOSIGNAL); 279 | if (sendval == -1) 280 | { 281 | if (debug) {printf("send() failed: %d, reconnecting\n",sendval);} 282 | if (connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) !=0) 283 | { 284 | if (debug) {printf("\n\nConnect() error, try new socket");} 285 | init_sock(); 286 | if (connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) !=0) 287 | { 288 | connect_errors++; 289 | if (connect_errors>MAX_CONNECT_ERRORS) 290 | { 291 | printf("\n\nConnect() error, exiting\n\n"); 292 | exit(errno); 293 | } 294 | usleep(1000); 295 | return; 296 | } 297 | } 298 | sendval = send(sockfd, databuf, data_buffer_len, 0); 299 | if (debug) {printf("send() retval after reconnect: %d\n",sendval);} 300 | } 301 | } 302 | if (debug) {printf("appears to have send() successfully\n");} 303 | packet_loop_counter++; 304 | if (packet_loop_counter>packet_loop_counter_max) 305 | { 306 | packet_loop_counter=0; 307 | close(sockfd); 308 | init_sock(); 309 | } 310 | return; 311 | } 312 | 313 | void init_sock() 314 | { 315 | if (udpmode) 316 | { 317 | sockfd = socket(AF_INET,SOCK_DGRAM, IPPROTO_UDP); 318 | } else { 319 | sockfd = socket(AF_INET,SOCK_STREAM,0); 320 | } 321 | if (sockfd <0) 322 | { 323 | socket_errors +=1; 324 | usleep(1000); 325 | if (socket_errors>MAX_SOCKET_ERRORS) 326 | { 327 | printf("\n%d Socket errors, exiting\n",MAX_SOCKET_ERRORS); 328 | exit(errno); 329 | } 330 | } 331 | } 332 | 333 | int port_count(int portnum) 334 | { 335 | int count=0; 336 | current = head; 337 | while (current->next != NULL) 338 | { 339 | if (current->dport == portnum && 340 | ((udpmode && strcmp("UDP",current->l4)==0) || 341 | (!udpmode && strcmp("TCP",current->l4)==0))) 342 | { 343 | count++; 344 | } 345 | current = current->next; 346 | } 347 | return count; 348 | } 349 | 350 | void begin_fuzzer(int portnum, char *target_host) 351 | { 352 | char port_print[8]; 353 | unsigned char *data_buffer; 354 | unsigned int data_buffer_len; 355 | int outbuflen=0; 356 | char outbuffer[64]; 357 | int packets_sent=0; 358 | if (portnum==0) 359 | { 360 | strcpy(port_print,"ALL"); 361 | } else { 362 | sprintf(port_print,"%d",portnum); 363 | } 364 | init_sock(); 365 | printf("[+] beginning fuzz run against: %s:%s\n\n",target_host,port_print); 366 | if (port_count(portnum)==0) 367 | { 368 | printf("ERR: we don't have any packets for this port. Exiting\n"); 369 | exit(1); 370 | } 371 | while (1) 372 | { 373 | current = head; 374 | while (current!=NULL) 375 | { 376 | if (current->dport!=portnum || 377 | (udpmode && strcmp(current->l4,"UDP")!=0) || 378 | (!udpmode && strcmp(current->l4,"TCP")!=0)) 379 | { 380 | if (debug) printf("Skipping this packet..\n"); 381 | current=current->next; 382 | continue; 383 | } 384 | data_buffer = ascii_to_binary(current->hexdata); 385 | data_buffer_len = (strlen(current->hexdata)/2); 386 | if (packets_sent%100==0) 387 | { 388 | for(;outbuflen>0;outbuflen--) 389 | { 390 | printf("\b"); 391 | } 392 | sprintf(outbuffer,"Sent %d packets",packets_sent); 393 | outbuflen = strlen(outbuffer); 394 | printf("%s",outbuffer); 395 | fflush(stdout); 396 | } 397 | if (modify_payload) 398 | { 399 | data_buffer = do_fuzz_random(data_buffer,data_buffer_len); 400 | } 401 | if (debug) {printf("Attempting to send data\n");} 402 | usleep(send_delay*1000); 403 | send_packet(data_buffer,portnum,target_host,data_buffer_len); 404 | packets_sent++; 405 | free(data_buffer); 406 | current=current->next; 407 | } 408 | if (modify_payload==0) 409 | { 410 | printf("\n\nSent all packets unmodified, exiting\n\n"); 411 | exit(0); 412 | } 413 | } 414 | } 415 | 416 | void save_seed(int seed, char *fullCmdLine) 417 | { 418 | time_t sec; 419 | char *time_str; 420 | sec = time(NULL); 421 | time_str = ctime(&sec); 422 | time_str[strlen(time_str)-1] = '\0'; 423 | FILE *seedFile = fopen("seeds.log","a"); 424 | fprintf(seedFile,"%s, cmd:%s seed was: %d\n",time_str,fullCmdLine,seed); 425 | fclose(seedFile); 426 | } 427 | 428 | int main(int argc, char **argv) 429 | { 430 | FILE *fp; 431 | printf("rage network dumbfuzzer\n"); 432 | char *fileName = NULL; 433 | char *target_host = NULL; 434 | unsigned int supplied_seed =0; 435 | int portnum=0; 436 | int c; 437 | char *fullCmdLine[128]; //todo 438 | strcpy((char *)fullCmdLine,argv[0]); 439 | int i; 440 | for (i=1;i