It has been in my mind for quite some time to learn Golang and write some pentesting-oriented tools lately. I’ve finally made up my mind and wrote a tool to inject fake RIPv2 routes in a network in Go that I called Golang RIP Injection Program (or GRIP for short). This is still in early development but it already works flawlessly so you can use it.

First things first, let’s give credit where credit is due. Most (if not all) of the heavy lifting had already been done by my good friend Lorenzo Nicolodi who explained in great details how to perform these kind of attacks against common routing protocols (namely RIPv2, OSPF and BGP). He also owns a repository where he hosts python code to do so.

NOTE: Yes, I know that RIPv2 is considered legacy by some and yes, I know my code sucks, I'm not a programmer. But there's still a ton of organizations which use RIPv2 as their internal routing protocol and this code works even though it seems like it was written by a monkey, so deal with it ¯\_(ツ)_/¯

The code I wrote is fairly commented so it shouldn’t need a lot of explanation. I’ll only go through the key parts.

The code starts by setting up some constants and a structure containing the header of the UDP datagram:

type udpHeader struct {
  SrcPort  uint16
  DstPort  uint16
  Length   uint16
  Checksum uint16
}

I then declared a function called forgeDatagram() that will forge the IPv4 datagram. It takes as input the UDP payload (later created in the main function), the destination IP address and the (spoofable) source IP address.

CAUTION: since IOS version 15 Cisco started setting as true the valide-update-source option in routers by default. This options prohibits route updates that are not from the same subnet so it's impossible to make the routers accepts routes with spoofed sources unless this option was disabled.

The function proceeds to create the UDP header by setting the source and destination port and the length of the UDP datagram. As Lorenzo pointed out multiple times in our conversations, RIP is one of the few protocols that actually requires a precise source port and not just a random one:

header := udpHeader{
    SrcPort: 520,
    DstPort: 520,
    Length:  uint16(udpHeaderLen + len(data)),
  }

After that it appends the payload to the UDP header and then forges the IPv4 header:

ipv4Header := &ipv4.Header{
    Version:  ipv4.Version,
    Len:      ipv4.HeaderLen,
    TotalLen: ipv4.HeaderLen + udpHeaderLen + len(data),
    ID:       0x0000,
    Protocol: ipv4Protocol,
    Dst:      dstIP.To4(),
    Src:      srcIP.To4(),
    TTL:	1,
    TOS:	0xc0,
    Flags:	0x4000,
  }

Once this is done the function proceeds to convert everything to bytes and pack everything up, returning a valid IPv4 datagram.

The main function is responsible for handling the user input and preparing the stuff required for the program to work so I won’t bother explaining it too much. One part that needs some explaination is the one responsible for forging the RIPv2 payload:

// Prepare input data to be used to create the packet
routeTag := uint16(0)
netAddr := net.ParseIP(*netPtr)
dstAddr := net.ParseIP(*dstPtr)
// Parse subnet mask as uint32
netmask := net.ParseIP(*mskPtr)
netmaskInt := binary.BigEndian.Uint32(netmask[12:16])
metric := uint32(*metPtr)

// Setup the fake route and create the packet
nextHop := net.ParseIP("0.0.0.0")
rtePtr := rip.NewRoute(netAddr, nextHop, netmaskInt, metric, routeTag)
pktPtr := rip.New(2, 2)
pktPtr.Routes = append(pktPtr.Routes, rtePtr)
packet, _ := pktPtr.Pack()

Here the program takes the pointers to the values of the various flags and parses them accordingly to the type needed. It then forges a route through rip.NewRoute() using the address of the network, the next hop (set to 0.0.0.0 to signify the route is directly connected) the subnet mask, the metric and the route tag as arguments. After that it creates the UDP payload using the Pack() method of the packet. The resulting payload is sent to the aforementioned forgeDatagram() function as argument together with the source and destination addresses.

The rest of the program is about setting up the socket and sending the packets through the network so it’s not of much interest.

And now it’s time for some shitty drawings (y’all’ve been waiting for this, right?) This is our scenario: attack1

The attacker wants to hijack packets meant for 10.0.0.100 so he uses GRIP to inject a fake route in the network:

$ sudo grip --network 10.0.0.100 --src 172.16.0.100

And this is what happens in reality (okay, this may be a bit fictionalized for the sake of simplicity) attack2

The router receives the fake route and advertises it to its neighbours (your friendly neighbour spiderman router) attack3

And obviously now the victim feels the need to connect to 10.0.0.100 and tell him something (those victims have always a perfect timing when it comes to hacking!) attack4

As soon as I finish the tool to attack OSPF I will integrate it with GRIP. See you at the next post!