/* * Program: ident-snort-bo-exploit.c * * Author: Kyle Haugsness (contact via http://isc.sans.org/contact.php) * http://handlers.sans.org/khaugsness * * Date: October 21, 2005 * * Version: 0.1 * * Purpose: Standalone program to sniff the network (or read a pcap file) * to identify and decode exploit attempts against the Snort * Back Orifice pre-processor vulnerability. * * References: http://www.snort.org/pub-bin/snortnews.cgi#99 * http://www.kb.cert.org/vuls/id/175500 * http://www.osvdb.org/displayvuln.php?osvdb_id=20034 * http://xforce.iss.net/xforce/alerts/id/207 * * Affected: Snort 2.4.0 - 2.4.2 are the vulnerable Snort versions. * * * Unaffected: Snort 2.4.3 and above * * Platforms: Debian Linux 3.1 stable (Sarge) * RedHat 9 * SuSE 10 * FreeBSD 5.3 (uncomment the #define BSD_PCAP below!) * OpenBSD 3.7 * NetBSD 2.0.2 * Solaris 8 (uncomment the #define SOLARIS) * * Requires: Recent lipcap (http://www.tcpdump.org/) * * Compile: gcc -Wall -lpcap -o ident-snort-bo-exploit ident-snort-bo-exploit.c * * On Solaris, you might need to add "-lsocket -lnsl" flags * (after -lpcap) to the above command. * * Testing: You should be able to test this program by reading or * replaying the 1025A.cap, which can be found in the same * place as this program (http://handlers.sans.org/khaugsness/). * Two testing options: 1) read the file using the -r switch * 2) sniff the network and replay the file using tcpreplay. * * Documentation: * * This program sniffs an interface looking for exploit attempts against * the Snort Back Orifice pre-processor vulnerability. Since all of the * packets that would exploit this vulnerability are "encrypted" with * the Back Orifice encryption, this program has to decrypt the packets * and try to determine whether it is a Back Orifice packet. * * So the order of operations for this program is as follows: * * 1) Open the interface with libpcap (or read an existing file). * 2) Set the filter to capture all UDP packets over 1024 bytes. * 3) Ignore packets with a source or destination port of 31337. * These packets cannot trigger the vulnerability in Snort. * 4) Attempt to decrypt the packet using the brute force code * from Snort. This is taking advantage of the fact that you * can brute force the key used by Back Orifice. * 5) Once decrypted, look for the magic value in the Back Orifice * packet. It is 8 bytes: "*!*QWTY?" * 6) If the above test is positive, then we have a potential * exploit attempt. Log the key that was used to encrypt the * packet and the packet contents. * * An example of the program's output is at the very bottom of * this file. * * If you are super paranoid (and don't trust this program running as * root on your box), then just capture suspicious packets using tcpdump * and then feed them into this program with -r. Here's the filter * string to use: "udp and udp[4:2] >= 1032 and not port 31337" * * This program seems to successfully identify exploits, regardless * of the key/password used to encrypt the Back Orifice packets. * * * Some stuff was copied from the BOclient program, which has no * license. Other information about the BO protocol from: * * http://www.magnux.org/~flaviovs/boproto.html * * Some functions and variables (I tried to document which ones) were * copied straight from Snort (the fixed version). The Snort copyright: * * ** Copyright (C) 1998-2005 Martin Roesch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ /* #define BSD_PCAP */ /* #define SOLARIS */ #include /* Standard UNIX */ #include /* Standard UNIX */ #include /* String functions */ #include /* Signal handling */ #include /* Date/time functions */ #include /* Standard UNIX */ #include /* Networking socket functions */ #include /* Time functions */ #include /* System data types */ #include /* Signal handling */ #include /* Signal handling */ #include /* Address conversion functions */ #include /* Address structures */ #include /* Libpcap library */ #ifdef BSD_PCAP #include /* Ioctl call for BSD to set immediate BPF return */ #endif /* Typedefs for Solaris */ #ifdef SOLARIS typedef uint64_t u_int64_t; typedef uint32_t u_int32_t; typedef uint16_t u_int16_t; typedef uint8_t u_int8_t; #endif #define PCAP_OUTPUT_DEFAULT "snort-bo-exploit" #define MAGICSTRING "*!*QWTY?" #define BACKORIFICE_MAGIC_SIZE 8 /* Global variables */ jmp_buf env_int; /* For signal handling */ unsigned char verbose = 0; /* For verbose error reporting */ unsigned long packets_decoded = 0; /* Number of reply packets decoded */ unsigned long packets_alerted = 0; /* Number of reply packets alerted */ pcap_dumper_t *pcap_out_handle = NULL; /* For writing out pcap data */ /* The following variables are from Snort 2.4.3 */ static long holdrand = 1L; /* global keyvalue for the BoRand() function */ u_int16_t lookup1[65536][3]; u_int16_t lookup2[65536]; /* Function definitions */ int packet_sniff(char *dev, char *pcap_in_file, char *pcap_out_file); void packet_decode(u_char *args, const struct pcap_pkthdr* pkthdr, const u_char* packet); void hex_output(const unsigned int length, const char *data); /* The following two functions are from Snort 2.4.3 */ void PrecalcPrefix(); char BoRand(); /* * Define a structure for decoding an IP header. It is possible to use code * in /usr/include/netinet/ip.h, but most example code out there seems to * explicitly declare the following structure for an IP header (from tcpdump). */ struct ip_hdr { u_int8_t ip_vhl; /* Header length, version */ #define IP_V(ip) (((ip)->ip_vhl & 0xf0) >> 4) /* Extract IP version */ #define IP_HL(ip) ((ip)->ip_vhl & 0x0f) /* Extract IP header length */ u_int8_t ip_tos; /* Type of service */ u_int16_t ip_len; /* Total length */ u_int16_t ip_id; /* Identification */ u_int16_t ip_off; /* Fragment offset field */ #define IP_DF 0x4000 /* Dont fragment flag */ #define IP_MF 0x2000 /* More fragments flag */ #define IP_OFFMASK 0x1fff /* Mask for fragmenting bits */ u_int8_t ip_ttl; /* Time to live */ u_int8_t ip_p; /* Protocol */ u_int16_t ip_sum; /* Checksum */ struct in_addr ip_src, ip_dst; /* Source and dst IP address */ }; /* Structure for UDP header */ struct udphdr { u_int16_t source; u_int16_t dest; u_int16_t len; u_int16_t check; }; /* Structure for decoding the Back Orifice header */ struct bo_hdr { u_int32_t size; /* Size of the packet */ u_int32_t packet_num; /* The packet number */ u_int8_t type; /* Packet type */ char *arg1; /* Data field 1 */ char *arg2; /* Data field 2 */ u_int8_t crc; /* CRC checksum */ }; void usage(char *prog) { fprintf(stderr, "\nUsage: %s [options below]\n\n", prog); fprintf(stderr, " -a - alert text file [default: stdout]; recommended to use tee instead\n"); fprintf(stderr, " -h - usage\n"); fprintf(stderr, " -i - interface to sniff; chosen automatically if not specified\n"); fprintf(stderr, " -r - read input pcap instead of sniffing\n"); fprintf(stderr, " -v - debug\n"); fprintf(stderr, " -w - output pcap file [default: snort-bo-exploit-.cap]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Note1: This program will overwrite the output files (alert\n" " and pcap) if they already exist.\n\n"); fprintf(stderr, "Note2: The -v option will log all packets that match the BPF\n" " filter (not just ones that are exploit attempts) to the\n" " alert log and the output pcap file. This is probably not\n" " what you want to do in a production environment.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Recommended usage: %s -i | tee alerts.txt\n\n", prog); exit(1); } void sig_int(int signum) { /* * This function is the signal handler for when the * user hits Ctrl-C or issues a kill command from the * command line. It returns back to the setjmp() so * it can close the output files and shutdown the sniffer */ /* Return back to the program (where setjmp() is) */ longjmp(env_int, 1); } int main(int argc, char *argv[]) { char cur_arg; /* Used for getopt processing */ char *alert_file = NULL; /* Output file for text alerts */ char *pcap_in_file = NULL; /* Input file - pcap */ char *pcap_out_file = NULL; /* Output file - filename */ unsigned int pcap_out_file_len = 0; /* Dynamically generated file name length */ char dev[32] = ""; /* Device to sniff */ char *dev_temp; /* Temporary device name */ time_t mytime; /* Used to calculate local time */ struct tm *loc_time; /* Used to calculate local time */ char date[20]; /* String containing YYYY-MM-DD-HH:MM:SS */ char errbuf_pcap[PCAP_ERRBUF_SIZE]; /* Used in this function to get the default network interface */ FILE *foutput = NULL; /* Output file - file pointer */ /* Setup the signal handler for keyboard interrupts */ signal(SIGINT, sig_int); signal(SIGTERM, sig_int); /* Parse command-line options */ while ((cur_arg = getopt(argc, argv, "ha:i:r:w:v")) != EOF) { switch (cur_arg) { case 'a': alert_file = optarg; break; case 'h': usage(argv[0]); break; case 'i': if (strlen(optarg) > 31) { break; /* ignore it */ } else { snprintf(dev, sizeof(dev), "%s", optarg); dev[sizeof(dev) - 1] = '\0'; /* just in case */ } break; case 'r': pcap_in_file = optarg; break; case 'w': pcap_out_file = optarg; break; case 'v': verbose = 1; break; default: usage(argv[0]); } } /* end of while(getopt) */ if (pcap_in_file != NULL) { /* Clear the device name */ memset(dev, 0, sizeof(dev)); } else { /* We are going to be sniffing, so verify root privileges */ if(geteuid() != 0) { printf("This program requires root access to run.\n"); exit(0); } /* Determine which interface to use */ if (strlen(dev) == 0) { if ((dev_temp = pcap_lookupdev(errbuf_pcap)) == NULL) { fprintf(stderr, "Couldn't lookup default ethernet device " "with pcap_lookupdev: %s\n", errbuf_pcap); exit(0); } snprintf(dev, sizeof(dev), "%s", dev_temp); dev[sizeof(dev) - 1] = '\0'; /* just in case */ } } /* end of if (pcap_in_file != NULL) { */ /* If the user didn't specify a capture file, generate a filename that includes the date/time */ if (pcap_out_file == NULL) { mytime = time(NULL); loc_time = localtime(&mytime); strftime(date, sizeof(date), "%Y-%m-%d-%H:%M:%S", loc_time); date[20] = '\0'; /* null terminate just in case */ pcap_out_file_len = sizeof(PCAP_OUTPUT_DEFAULT) + sizeof(date) + 5; pcap_out_file = malloc(pcap_out_file_len); if (pcap_out_file == NULL) { fprintf(stderr, "Unable to allocate memory!\n"); exit(1); } snprintf(pcap_out_file, pcap_out_file_len, "%s-%s.cap", PCAP_OUTPUT_DEFAULT, date); pcap_out_file[pcap_out_file_len - 1] = '\0'; /* just in case */ } /* If requested, open the alert file */ if (alert_file != NULL) { foutput = freopen(alert_file, "w", stdout); if (foutput == NULL) { fprintf(stderr, "Unable to open alert file: %s\n", alert_file); exit (73); } } else { foutput = stdout; } /* Print my arguments */ if (pcap_in_file != NULL) { fprintf(stdout, "# Using pcap input file: %s\n", pcap_in_file); } else{ fprintf(stdout, "# Using interface: %s\n", dev); } if (alert_file != NULL) { fprintf(stdout, "# Using alert output file: %s\n", alert_file); } else { fprintf(stdout, "# Using alert output file: stdout\n"); } fprintf(stdout, "# Using pcap output file: %s\n", pcap_out_file); /* Build the table for brute-forcing the BO key */ PrecalcPrefix(); /* Call the sniffer function */ if (pcap_in_file != NULL) { if (packet_sniff(NULL, pcap_in_file, pcap_out_file) == -1) { /* If we receive -1 from packet_sniff() the pcap_loop failed */ exit(1); } } else { if (packet_sniff(dev, NULL, pcap_out_file) == -1) { /* If we receive -1 from packet_sniff() the pcap_loop failed */ exit(1); } } /* Close input/output files */ if (alert_file != NULL) fclose(foutput); exit(0); } /* end of main() */ int packet_sniff(char *dev, char *pcap_in_file, char *pcap_out_file) { /* * This function sets up pcap for sniffing. It accepts a device * name to sniff and strings to input/output files. This * function calls packet_decode() when it gets a new packet from * pcap. */ char errbuf_pcap[PCAP_ERRBUF_SIZE]; /* Error buffer for messages */ pcap_t *pcap_handle; /* Context for pcap */ bpf_u_int32 mask; /* The netmask of our sniffing device */ bpf_u_int32 net; /* The IP of our sniffing device */ struct bpf_program filter; /* The compiled filter expression */ u_char* args = NULL; /* Used for callback function in pcap_loop */ #ifdef BSD_PCAP unsigned int v = 1; /* Used for BPF ioctl */ #endif /* Setup the bpf packet filter string - using 1032 bytes to account for 8 bytes of the UDP header; the buffer overflow is 1024 bytes */ char bpf_string[] = "udp and udp[4:2] >= 1032 and not port 31337"; /* Determine if we are using an input file or sniffing the device */ if (dev == NULL) { /* Verify that we have a filename */ if (pcap_in_file == NULL) { fprintf(stderr, "No device name specified for " "sniffing or no input pcap given\n"); return -1; } /* Open the pcap input file */ if ((pcap_handle = pcap_open_offline(pcap_in_file, errbuf_pcap)) == NULL) { fprintf(stderr, "Unable to start sniffer session " "on interface %s: %s\n", dev, errbuf_pcap); return -1; } /* * Note that we can't setup the BPF filter here because we * have no device name. So we won't have libpcap filtering * packets for us and will have to do it in packet_decode(). * This isn't necessarily a problem, but it's the reason * that filtering seems to be done in two places. */ } else { /* Open the sniffer session in promiscuous mode */ if ((pcap_handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf_pcap)) == NULL) { fprintf(stderr, "Unable to start sniffer session " "on interface %s: %s\n", dev, errbuf_pcap); return -1; } #ifdef BSD_PCAP /* * If you don't do the following on FreeBSD, pcap buffers the packets * until it feels like giving the packet to the application * which is not desirable. */ if (ioctl(pcap_fileno(pcap_handle), BIOCIMMEDIATE, &v) == -1) { fprintf(stderr, "Unable to set immediate mode on bpf!\n"); } #endif /* Lookup the subnet and mask for the device */ if (pcap_lookupnet(dev, &net, &mask, errbuf_pcap) == -1) { fprintf(stderr, "Unable to lookup network info. %s\n", errbuf_pcap); return -1; } /* Compile the BPF filter expression */ if (pcap_compile(pcap_handle, &filter, bpf_string, 0, net) == -1) { pcap_perror(pcap_handle, "Unable to compile pcap expression"); return -1; } /* Apply the BPF filter */ if (pcap_setfilter(pcap_handle, &filter) == -1) { pcap_perror(pcap_handle, "Unable to set pcap filter"); return -1; } } /* end of if(dev == NULL) { */ /* Open the pcap output file */ if (pcap_out_file != NULL) { pcap_out_handle = pcap_dump_open(pcap_handle, pcap_out_file); if (pcap_out_handle == NULL) { fprintf(stderr, "Unable to open output pcap file!\n"); exit(1); } } /* Here is where the signal handler jumps to after receiving interrupt */ if (setjmp(env_int) != 0) { /* Print out final messages */ fprintf(stdout, "#\n#\n# Received interrupt. Cleaning up.\n"); fprintf(stdout, "# Number of packets matching UDP filter: " "%ld packets\n", packets_decoded); fprintf(stdout, "# Number of Snort Back Orifice exploit attempts: " "%ld packets\n", packets_alerted); /* Cleanup */ pcap_close(pcap_handle); pcap_dump_close(pcap_out_handle); /* pcap output file */ return 0; } else { /* Run the sniffer (looping over each packet) */ if (pcap_loop(pcap_handle, -1, packet_decode, args) == -1) { fprintf(stderr, "Error in pcap_loop\n"); return -1; } } return 0; } /* end of packet_sniff() */ void hex_output(const unsigned int length, const char *data) { /* This function prints data tcpdump-style */ unsigned int i = 0, j = 0; /* loop counters */ unsigned int line_addr = 0; /* address printed on the left */ char out_line[16]; /* buffer for the end of line ascii. this doesn't need a null terminator because we print each character individually (with %c, not %s) */ if (length == 0) return; if (data == NULL) return; memset(out_line, 0, sizeof(out_line)); /* Loop through every input byte */ for (i = 0, line_addr = 0; i < length; i++, line_addr++) { /* Print the line numbers at the beginning of the line */ if ((i % 16) == 0) { fprintf(stdout, "0x%04x: ", line_addr); memset(out_line, 0, sizeof(out_line)); } /* Copy the byte into the ascii part of the output string */ out_line[i % 16] = (unsigned char) data[i]; /* Add whitespace before the final ascii */ if ((i % 16) == 8) fprintf(stdout, " "); /* Print the ascii at the end of the line */ if ((i % 16) == 15) { fprintf(stdout, "%02X ", (unsigned char) data[i]); fprintf(stdout, " "); /* Print the ascii at the end of the line */ for (j = 0; j < 16; j++) { /* Print only ascii characters */ if (out_line[j] > 31 && out_line[j] < 127) { fprintf(stdout, "%c", out_line[j]); } else { fprintf(stdout, "."); } } fprintf(stdout, "\n"); } else { fprintf(stdout, "%02X ", (unsigned char) data[i]); } } /* end of for() loop that handles complete lines */ /* Handle the ascii for the final line, which may not be completely filled. */ if (i % 16 > 0) { /* Add an extra space to account for the extra space in the middle of the line */ if (i % 16 < 8) { fprintf(stdout, " "); } /* Add whitespace for every missing hex byte */ for (j = 0; j < 16 - (i % 16); j++) { fprintf(stdout, " "); } /* Add whitespace before the final ascii */ fprintf(stdout, " "); /* Print the ascii at the end of the line */ for (j = 0; j < (i % 16); j++) { /* Print only ascii characters */ if (out_line[j] > 31 && out_line[j] < 127) { fprintf(stdout, "%c", out_line[j]); } else { fprintf(stdout, "."); } } } /* end of if() statement that handles the final line */ fprintf(stdout, "\n"); } /* end of hex_output() */ void packet_decode(u_char *args, const struct pcap_pkthdr* pkthdr, const u_char* packet) { /* * This function receives the packet header and packet as arguments * and decodes it to extract the BO data. Successfully decoded * packets are printed to the output file, otherwise the function * just returns to wait for a new packet. * * If the user gave us a pcap file (instead of sniffing the network), * then the packets won't be filtered with the bpf filter. So this * function has to double-check that the packet is UDP and the length * is long enough and the port isn't 31337. */ const struct ip_hdr* ip; /* IP packet header (here) */ const struct udphdr* udp; /* UDP packet header /usr/include/netinet/udp.h */ const struct bo_hdr* bo; /* Back Orifice packet header */ u_int length = pkthdr->len; /* Packet length from the wire */ u_int hlen, off, version; /* Header length, ip offset, ip version */ int len; /* Length reported in the packet */ unsigned int i = 0; /* Loop counter */ u_char *bo_encrypted; /* Back Orifice data prior to decryption */ char *bo_decrypted; /* Back Orifice data after decryption */ time_t tm; /* Date/time */ unsigned int exploit = 0; /* Flag: is current packet an exploit */ unsigned int logged = 0; /* Flag: has this packet already been logged to pcap */ /* Snort variables */ u_int16_t cyphertext_referent; u_int16_t cyphertext_suffix; u_int16_t key; char *magic_cookie = "*!*QWTY?"; char *pkt_data; char *magic_data; char *end; char plaintext; /* Get the current time */ tm = time(NULL); /* The ethernet header is 14 bytes */ /* Jump past the ethernet header */ ip = (struct ip_hdr*)(packet + 14); length -= 14; /* Check to see we have a packet of valid length */ if (length < sizeof(struct ip_hdr)) { fprintf(stderr, "Truncated IP datagram of length: %d bytes", length); return; } /* Get some of the data from the IP header */ len = ntohs(ip->ip_len); /* Length reported in the packet */ hlen = IP_HL(ip); /* Header length */ version = IP_V(ip); /* IP version */ /* Check the IP version */ if(version != 4) { fprintf(stderr, "Unknown IP version: %d\n", version); return; } /* Check the IP header length */ if(hlen < 5 ) { fprintf(stderr, "Bad header length: %d\n", hlen); return; } /* Check to see if actual packet is as long as reported in the header */ if(length < len) fprintf(stderr, "Truncated IP datagram: %d bytes missing\n", len - length); /* Check to see if this packet was fragmented */ off = ntohs(ip->ip_off); if((off & 0x1fff) != 0 ) /* No 1's in first 13 bits */ return; /* Verify that we are dealing with UDP */ if (ip->ip_p != 17) { return; } /* Jump past the ethernet header + IP header */ udp = (struct udphdr*)(packet + 14 + sizeof(struct ip_hdr)); length -= sizeof(struct ip_hdr); /* Verify the actual packet length matches the header */ if (length < ntohs(udp->len)) return; /* Verify that the UDP packet length is long enough to trigger * the vulnerability (includes 8 bytes of the UDP header) */ if (ntohs(udp->len) < 1032) { return; } /* Verify that the port number is not 31337 because the bug * isn't triggered when either source or destination is set to that */ if (ntohs(udp->source) == 31337 || ntohs(udp->dest) == 31337) { return; } /* Set the pointer to to the DNS portion of the packet */ bo_encrypted = (u_char *)(packet + 14 + sizeof(struct ip_hdr) + sizeof(struct udphdr)); length -= sizeof(struct udphdr); /* Print the encrypted BO packet */ if (verbose) { /* Copy the packet to the output pcap file */ if (pcap_out_handle != NULL) { pcap_dump((u_char *) pcap_out_handle, pkthdr, packet); logged = 1; } fprintf(stdout, "#\n##############################################\n"); fprintf(stdout, "#\n"); fprintf(stdout, "# UDP packet below matched filter...\n"); fprintf(stdout, "# The encrypted decode is below.\n"); fprintf(stdout, "# Going to decrypt it next.\n"); fprintf(stdout, "#\n"); fprintf(stdout, "##############################################\n"); fprintf(stdout, "#\n"); hex_output(length, bo_encrypted); } /* Snort code here... * take the first two characters of the packet and generate the * first reference that gives us a reference key */ cyphertext_referent = (u_int16_t) (bo_encrypted[0] << 8) & 0xFF00; cyphertext_referent |= (u_int16_t) (bo_encrypted[1]) & 0x00FF; /* * generate the second referent from the last two characters * of the cyphertext */ cyphertext_suffix = (u_int16_t) (bo_encrypted[6] << 8) & 0xFF00; cyphertext_suffix |= (u_int16_t) (bo_encrypted[7]) & 0x00FF; for(i=0;i<3;i++) { /* get the key from the cyphertext */ key = lookup1[cyphertext_referent][i]; /* * if the lookup from the proposed key matches the cyphertext reference * then we've probably go the right key and can proceed to full * decryption using the key * * moral of the story: don't use a lame keyspace */ if(lookup2[key] == cyphertext_suffix) { holdrand = key; pkt_data = (char*)bo_encrypted; end = (char*)bo_encrypted + BACKORIFICE_MAGIC_SIZE; magic_data = magic_cookie; while(pkt_dataip_src)); fprintf(stdout, "# Dest IP: %s\n", inet_ntoa(ip->ip_dst)); fprintf(stdout, "# Source port: %d\n", ntohs(udp->source)); fprintf(stdout, "# Dest port: %d\n", ntohs(udp->dest)); fprintf(stdout, "# UDP data len: %d\n", ntohs(udp->len)-8); fprintf(stdout, "# BO key (dec): %d\n", key); fprintf(stdout, "# BO key (hex): 0x%04X\n", key); /* Copy the packet to the output pcap file */ if ((pcap_out_handle != NULL) && (logged == 0)) { pcap_dump((u_char *) pcap_out_handle, pkthdr, packet); } packets_alerted++; /* Decrypt the BO packet */ bo_decrypted = malloc(length - 8); /* -8 because the Magic value has been removed */ if (bo_decrypted == NULL) { fprintf(stdout, "Unable to malloc!\n"); exit(1); } for (i = 0; i < length - 8; i++, pkt_data++) { bo_decrypted[i] = (char) (*pkt_data ^ BoRand()); } /* Decode the BO header */ bo = (struct bo_hdr*)(bo_decrypted); /* Print some info about the BO header */ fprintf(stdout, "# BO data len: %d (UDP len - 17 byte BO header)\n", bo->size - 17); fprintf(stdout, "# BO pkt id: %d\n", bo->packet_num); fprintf(stdout, "# BO pkt type: 0x%02X (0x01 = PING)\n", bo->type); /* Finally, print the decrypted packet data - this will be the shellcode! */ fprintf(stdout, "#\n# Decrypted BO data:\n#\n"); hex_output(length, bo_decrypted); fprintf(stdout, "#\n"); } } fflush(stdout); packets_decoded++; fprintf(stderr, "# Decoded packet num: %lu; Exploit: ", packets_decoded); if (exploit == 1) { fprintf(stderr, "yes; "); } else { fprintf(stderr, " no; "); } fprintf(stderr, "Timestamp: %s", ctime(&tm)); return; } /* end of packet_decode() */ /* * From Snort 2.4.3: * * Function: BoRand() * * Purpose: Back Orifice "encryption" algorithm * * Arguments: None. * * Returns: key to XOR with current char to be "encrypted" */ char BoRand() { holdrand = holdrand * 214013L + 2531011L; return (char) (((holdrand >> 16) & 0x7fff) & 0xFF); } /* * From Snort 2.4.3 * * Precalculate the known cyphertext into a prefix and suffix lookup table * to recover the key. Using this in the BoFind() function below is much * faster than the old brute force method */ void PrecalcPrefix() { u_int8_t cookie_cyphertext[BACKORIFICE_MAGIC_SIZE]; char *cookie_plaintext = "*!*QWTY?"; int key; int cookie_index; char *cp_ptr; /* cookie plaintext indexing pointer */ u_int16_t cyphertext_referent; for(key=0;key<65536;key++) { /* setup to generate cyphertext for this key */ holdrand = key; cp_ptr = cookie_plaintext; /* convert the plaintext cookie to cyphertext for this key */ for(cookie_index=0;cookie_index