Route to Bugs: Analyzing the Security of BGP Message Parsing

Download Slides

Original article: https://www.blackhat.com/us-23/briefings/schedule/index.html#route-to-bugs-analyzing-the-security-of-bgp-message-parsing-32162

This talk discusses an often-overlooked aspect of Border Gateway Protocol (BGP) security: vulnerabilities in its software implementations. More specifically, vulnerabilities in BGP message parsing.

Software suites implementing BGP are nowadays relied upon for Internet routing and for functions such as internal routing in most large data centers as well as MPLS L3 VPNs. Following the Network Function Disaggregation (NFD) trend, many leading implementations are nowadays open source.

A lot of (deserved) attention is given in the community to aspects of BGP protocol security discussed in RFC4272, which can be mitigated with the use of RPKI and BGPsec. However, recent BGP incidents show that it might take only a malformed packet to cause a large disruption.

We will present a quantitative analysis of previously known vulnerabilities in both open and closed-source popular BGP implementations and focus the talk on an extensive new analysis of seven modern implementations. There are two main findings in this research:
• Some implementations process parts of OPEN messages (e.g., decapsulating optional parameters), before validating the BGP ID and ASN fields of the originating router. This means that only TCP spoofing (instead of a complete takeover of a configured peer) is required to inject malformed packets.
• We found three new vulnerabilities in a leading open-source implementation, FRRouting, which could be exploited to achieve denial of service on vulnerable BGP peers, thus dropping all BGP sessions and routing tables and rendering the peer unresponsive. These vulnerabilities were found using a fuzzer we developed and will release to the community.

Our research shows that many modern BGP implementations still have low hanging fruit that can still be abused by attackers.

Tcpdump and Docker

Often there is a need to run tcpdump when debugging connectivity issues. It might be challenging to install tcpdump on the host or its useful to tap into a container and run tcpdump there.

Tcpdump docker container can be built using the following file:

tcpdump $ cat Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y tcpdump net-tools
CMD /usr/bin/tail -f

“CMD /usr/bin/tail -f” is used just as a command to run on the container startup. Any other similar command can be used.

Build a container:

$ docker build . -t tcpdump --tag tcpdump
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM ubuntu
 ---> 58db3edaf2be
Step 2/3 : RUN apt-get update && apt-get install -y tcpdump net-tools
 ---> Using cache
 ---> f2114ffe1a4a
Step 3/3 : CMD /usr/bin/tail -f
 ---> Using cache
 ---> 973f5bd0a573
Successfully built 973f5bd0a573
Successfully tagged tcpdump:latest

Run tcpdump container, exec into it and run tcpdump:

tcpdump $ docker run -d --network host  tcpdump
0a76ea051cb184d18e32136a583555014c9891361f96f2c0d3014824c0267937
rromanyak@sh-edge-instance-group-1-35w7 ~/docker-composer/tcpdump $ docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS        PORTS     NAMES
0a76ea051cb1   tcpdump          "/bin/sh -c '/usr/bi…"   3 seconds ago   Up 1 second             priceless_dirac
~ $ docker exec -it priceless_dirac /bin/bash
root@sh-edge-instance-group-1-35w7:/#
#  tcpdump -ni any host 192.168.1.100
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
16:29:41.546456 eth0  In  IP 192.168.1.100.17685 > 34.75.75.75.8080: Flags [S], seq 3065893830, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 3518285460 ecr 0,sackOK,eol], length 0
16:29:41.546508 eth0  Out IP 34.75.75.75.8080 > 192.168.1.100.17685: Flags [S.], seq 200885289, ack 3065893831, win 64768, options [mss 1420,sackOK,TS val 1251209852 ecr 3518285460,nop,wscale 7], length 0
16:29:41.546637 eth0  In  IP 192.168.1.100.7845 > 34.75.75.75.8080: Flags [S], seq 418846665, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 151752502 ecr 0,sackOK,eol], length 0
16:29:41.546651 eth0  Out IP 34.75.75.75.8080 > 192.168.1.100.7845: Flags [S.], seq 3540253616, ack 418846666, win 64768, options [mss 1420,sackOK,TS val 1251209852 ecr 151752502,nop,wscale 7], length

Using Docker’s ability to run a container that attaches to the network of another with the --network=container:<container_name> option we can tap into the network of another container:

$ docker ps
CONTAINER ID   IMAGE            COMMAND   CREATED      STATUS      PORTS     NAMES
4e8a23069308   flowerpot/goif   "/main"   5 days ago   Up 5 days             klt-instance-template-sedge-backend-5-qysi
 $ docker run -d --tty --net=container:klt-instance-template-sedge-backend-5-qysi tcpdump
b7ef3cafa6d82fd5682d7be65c59f1e24868c9ef618d40bbb1404c0fac168739
 ~ $ docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS     NAMES
b7ef3cafa6d8   tcpdump          "/bin/sh -c '/usr/bi…"   6 seconds ago   Up 5 seconds             ecstatic_archimedes
4e8a23069308   flowerpot/goif   "/main"                  5 days ago      Up 5 days                klt-instance-template-sedge-backend-5-qysi
$ docker exec -it b7ef3cafa6d8 /bin/bash
# tcpdump -ni any host 1.1.1.1


When to use pointers in Go (by Dylan Meeus)

clockwise/spiral rule for pointers in C

One of my pet peeves is finding places in Go code where pointers are being used, when it’d be better if they weren’t. I think one of the major misconceptions of where you want to use pointers comes from the idea that a pointer in Go is pretty much like a pointer in C.

Yet, it’s not. Pointers in Go don’t function the way they do in C/C++ (thankfully, the clockwise spiral rule as in the image above still gives me nightmares).

Mythbuster: use pointers for performance

A common idea is that when you use pointers your application will be faster, because you’ll avoid copying data around all the time. When Java came around, one of the complaints was that Java was slow because you’d be doing a pass-by-value all the time. Hence perhaps it’s no surprise that in Go we find the same idea persisting.

Yet, Passing pointers in Go is often slower than passing values. This is a result of Go being a garbage collected language. When you pass a pointer to a function, Go needs to perform Escape Analysis to figure out if the variable should be stored on the heap or the stack. This already adds a bit of overhead, but in addition the variable could be stored on the heap. When you store a variable on the heap, you also lose time when the GC is running.

One neat feature of Go is that you can check what the escape analysis is doing by running with go build -gcflags="-m". If you run this, Go will tell you if a variable will escape to the heap or not:

./main.go:44:20: greet ... argument does not escape
./main.go:44:21: greeting escapes to heap
./main.go:44:21: name escapes to heap

If a variable does not escape to the heap, it lives on the stack. And a stack does not require a garbage collector to clean up the variables, it’s just a push/pop operation.

If you just pass everything by value, you always run on the stack and don’t have to incur the overhead of garbage collection. (The GC will still run by default. But having less on the heap will make the GC have less work to do).

Now you know that using pointers could negatively impact your performance, but when do you want to use pointers?

Copying large structs

So are pointers never more performant than passing the values? Actually, that would be a false statement as well. Pointers can have a benefit when you have structs containing lots of data. When you have these, the overhead of the garbage collector might be negated by the overhead you’d get when copying large amounts of data.

Something I almost invariable get asked when I mentioned this, is ‘how much kb / mb should the struct be’?

I don’t think there’s a magic number, as with anything performance related, you should probably benchmark this. Go has great benchmarking tools built-in, so might as well make use of them 😃

Mutability

The only way to mutate a variable that you pass to a function is by passing a pointer. By default, the pass-by-value means that changes you make are on the copy you’re working on. Thus, they are not reflected in the calling function.

If you have this code:

type person struct {
 name string
}func main() {
 p := person{"Richard"}
 rename(p)
 fmt.Println(p)
}func rename(p person) {
 p.name = "test"
}

The output will be Richard because the change you made to person was made to the copy of person that rename got. Not on the underlying object, but if you want to mutate person you could just accept a pointer.

func main() {
 p := person{"Richard"}
 rename(&p)
 fmt.Println(p)
}func rename(p *person) {
 p.name = "test"
}

Now the output will be test. This is probably the main use case for pointers in Go, when you want mutability. Whether or not being mutable is a good thing I’ll leave open to debate.

API consistency

It’s a good idea to use a pointer receiver everywhere if you need at least one. This will keep your API consistent, even though not all methods might mutate your struct.

Hence, prefer this:

func (p *person) rename(s string) {
   p.name = s 
}func (p *person) printName() {
  fmt.Println(p.name)
}

over

func (p *person) rename(s string) {
   p.name = s 
}func (p person) printName() {
  fmt.Println(p.name)
}

Even though you would not need the pointer in the printName you'd do it for the sake of consistency. This makes your API easier to use, and you do avoid having to remember where to (de)reference and where not to do so.

To signify true absence

If you’re using values, you’ll always get the default-zero value. In some cases, you might want to really know if something is absent or just filled out. For example, if you have a struct representing exams with scores that a student took, if a struct is empty and has a score of 0, does this mean the student did a really bad job or does this mean the student was absent?

By using a pointer, the default-zero value is a nil pointer, which could be used to signify the absence of a value. There’s other ways of doing this, you could create a struct such as:

type exam struct {
    score   int
    present bool
}

Where you’d use the present field to indicate an exam was actually taken by a student.

Why I prefer values?

I realize this next bit is at least a bit subjective. Different people will feel differently about how code should be written, so if you disagree with the next part that’s perfectly fine. You do you. 😃

I believe that it makes sense to write Go in a “values-by-default” way. This could not be the correct approach for your situation, but it does avoid one major issue from my point of view. When you use use values rather than pointers, you can’t run into Tony Hoare’s “billion dollar mistake”, the dreaded nil-pointer.

It eliminates a lot of guard statements, because often the default-zero value is perfectly usable.

Another benefit is that mutability causes more headaches than it solves. It makes functions prone to side-effects and makes it in general harder to debug. It’s also easy to avoid mutable functions by just having the function return a modified version of the struct rather than doing an in-place mutation.

Our earlier renaming example could have been written in this way

func main() {
 p := person{"richard"}
 p = rename(p)
 fmt.Println(p)
}func rename(p person) person {
 p.name = "test"
 return p
}

This is also how append works, so it’s not that alien.

x := []int{1,2}
x = append(x, 3)
x = append(x, 4)

The safety of using of not using pointers, in combination with values often being faster than pointers, makes me think that when you want to use a pointer you should pause and really consider if you need one.

Original article available at medium.

Broken references in Virtualenvs

After trying a few things, this worked for me:

go to your virtualenv directory (but don't run workon):

cd ~/.virtualenv/name_of_broken_venv

Now delete these files:

rm -rf .Python bin/python* lib/python2.7/* include/python2.7

Then to rebuild your venv, run:

virtualenv .
workon name_of_broken_venv
pip freeze

Awesome BGP Notes

Nonstop Forwarding (NSF) or Graceful Restart allows a router to continue forwarding with the existing information (retained from the previous session) while the session is being reset.

Several measures are available to increase BGP scalability. These measures reduce either the number of routes/paths to be maintained or the number of updates to be generated.

 

To enforce policies, BGP uses a three-step process:
1 Input policy engine
2 Path selection
3 Output policy engine

As updates are received from a peer, they are stored in a Routing Information Base (RIB) for that peer (Adj-RIB-In). The updates are filtered by the Input Policy Engine. A path selection algorithm is then performed to determine the best path for each prefix. The resulting best paths are stored in the local BGP RIB (Loc-RIB) and then are submitted to the local IP routing table (IP-RIB) for installation consideration. When multipath is enabled, the best path plus all equal-cost paths are submitted for IP-RIB consideration.

In addition to the best paths received from peers, the Loc-RIB also contains BGP prefixes injected by the current router (called locally sourced) that are selected as the best paths. The
content of the Loc-RIB must pass through the Output Policy Engine before being advertised to other peers. The routes that successfully pass through the Output Policy Engine are
installed in the output RIB (Adj-RIB-Out).

The primary function of the BGP Scanner process is BGP housekeeping. Specifically, the BGP Scanner performs periodic scans of the BGP RIB to determine if prefixes and
attributes should be deleted and if route map or filter caches should be flushed. This process also scans the IP RIB to ensure that all the BGP next hops are still valid. If the next hop is
unreachable, all BGP entries using that next hop are removed from the BGP RIB

BGP Capabilities
As defined in RFC 1771, BGP supports the following four types of messages:
• Open—This type of message is used to set up the initial BGP connections.
• Update—These messages are used between peers to exchange network layer reachability information.
• Notification—These messages are used to communicate error conditions.
• Keepalive—These messages are exchanged periodically between a pair of peers to keep the session up.

Here are some of the capabilities that are supported in Cisco IOS software:
• Capability code 1, Multiprotocol extension
• Capability code 2, Route refresh
• Capability code 64, Graceful restart
• Capability code 128, Old form of route refresh
• Capability code 130, Outbound Route Filter (ORF)

 

Tuning BGP Performance
TCP MSS - The two main parameters that affect TCP’s performance are the maximum segment size (MSS) and the TCP window size. The TCP MSS controls the size of the TCP segment, or
packet, and the TCP window size controls the rate at which packets can be sent.

Queue Optimization - The purpose of queue optimization is to minimize packet loss. This most often occurs on a router with a large fan-out of BGP sessions. The root cause is the stream of acknowledgments that are received from a large number of peers simultaneously. The router is unable to process all the TCP ACKs, causing the input queues to overflow, resulting in packet loss.
The packet reception process for BGP packets has three major components:

  • Input hold queue—This is not an actual queue, but a counter that is assigned to an interface. When a packet bound for the processor is received on an interface, the input hold queue is incremented by 1. After that packet has been processed, the input hold queue is decremented to reflect that the packet is no longer in the queue. Each input queue has a maximum queue depth.
  •  Selective Packet Discard (SPD) Headroom—SPD Headroom is a counter that allows the input hold queues to exceed their configured maximum size. The total value of the SPD Headroom is shared by all the interfaces. This headroom is used to store high-priority packets, such as routing control traffic, above and beyond the input hold queue. The SPD feature is discussed in detail in the section “Selective Packet Discard.”
  •  System buffers—The system buffers store the incoming packets being sent to the process level. A packet destined for the processor is removed from the interface buffer and is put in the system buffer. These buffers can be seen with the show buffers command.

SPD - The SPD feature is a queue-management mechanism that operates on the input hold queues for traffic destined for the route processor. The SPD process can distinguish between highand normal-priority traffic, allowing it to better manage system resources in the input queue. The SPD function is specifically for managing input queue congestion.

BGP Network Performance Features
BGP Fast External Fallover - The default behavior for tearing down a BGP session is to require the hold timer to expire, which by default is 180 seconds. The BGP fast external fallover function triggers the teardown of an eBGP session immediately when the link to that eBGP peer fails. This feature is only for external peers.

BGP Non-Stop Forwarding - The BGP Non-Stop Forwarding (NSF) or graceful restart (BGP-GR) feature takes advantage of the independence of the data plane and control plane processing. The concept of BGP NSF is that the data plane can continue forwarding for a period of time while BGP restarts.

End-of-RIB Marker - The end-of-RIB marker indicates to a BGP peer that the initial routing update has completed after session initiation. This feature is valuable for BGP convergence independently of BGP-GR

Graceful Restart Capability - This capabilityalso contains the Restart State, Restart Time in the Restart Flags, and Forwarding State foreach AFI/SAFI, as part of the Address Family flags.

BGP Soft Reconfiguration - Soft reconfiguration outbound does not require any additional resource. The BGP router can process the Adj-RIB-Loc through the outbound policy for the particular peer, creating a new Adj-RIB-Out. The remote peer can be updated by any changes with BGP Update messages.
The soft reset of an inbound connection presents more of a difficulty. When prefix information for a remote peer is rejected because of inbound policy, that prefix information is not
maintained in the BGP table. This is intended to optimize resource utilization on a BGP router that has a large number of prefixes. The BGP soft reconfiguration feature lets a BGP peer maintain all prefix information learned from the remote peer, even if it is rejected because of inbound policy filtering. This feature increases the memory resource requirements; however, the router can reprocess all inbound prefixes through an updated inbound configuration.

Route Refresh Feature - The route refresh feature is a replacement for the soft reconfiguration feature. Route refresh is a capability that is negotiated at session initiation. The route refresh feature allows a BGP BGP Network Performance Features 95 router to request that a remote peer resend its BGP Adj-RIB-Out. This allows the BGP router to reapply the inbound policy.

 

BIRD: Filters and Functions example

BIRD contains a simple programming language. There are two objects in this language: filters and functions. Filters are interpreted by BIRD core when a route is being passed between protocols and routing tables. The filter language contains control structures such as if's and switches, but it allows no loops.
BIRD supports functions, so that you don't have to repeat the same blocks of code over and over. Functions can have zero or more parameters and they can have local variables. Recursion is not allowed.

In this post we will configure an inbound filter that will set MED to a specific value for a specific ASN and also set Local Preference attribute for a specific prefix from the specific upstream ISP.

Lets set MED 50 to all routes originated in Twitter ASN 13414. Also lets set local preffor prefix 4.0.0.0/9 received via Level3. The simple and non-elegant way to do that is define the following filter:

Read more

The difference between an Aggregate and a Generate route in Junos

Aggregate routes are quite like generate routes in that they become active when a contributing route is present from a peer or neighbour.  What’s different is that the aggregate route always has a discard or reject, so isn’t used to forward traffic.  A generate route has a preferred contributing route, and this is used to forward the traffic even in the event that there isn’t a contributing route matching the packet’s destination.

In real-world usage, aggregates are used by people to summarise their address space into a single block to keep the Internet routing table smaller.   Generate routes are usually used at the edge of a network to generate a conditional default route into the IGP if there are any routes received from the upstream ISP.

Junos on GNS3 + VirtualBox

1. Install GNS3 all in onepackage. It is straight forward process described in many tutorials and videos available on internet. 

2. Install Oracle VM VirtualBox.

3. Download Junos vmdk files (JunOS Olive-disk1.vmdk)

4. Create a VM in VirtualBox using junos vmdk files.

5. Change settings of junosVM as following:
- disable floppy/CD;
- set 'Base Memory' to 512MB;
- Network Settings: there are 4 adapters, each adapter should be set to as 'Not attached' and Advanced => Adapter Type: Paravirtualized Network;

Read more

Bird Internet Routing Daemon Configuration and Administration

Show Commands:

bird> show symbols - list of symbols (filters, protocols, tables, etc...)
bird> show protocols [all] - list of protocols [including details]
birdc> show route for “prefix/ip” [all] - list route for given prefix/ip address [including details]
birdc> show route filter “filtr” [all] - list routes according given filter [including details]
birdc> show route where bgp_path ~ [= * 1234 * =] - list routes with given bgp path
birdc> show route where 127.0.0.5 ~ net - the same as “for prefix” (?)
birdc> show route filter { if 10.100.1.0 ~ net then accept; } - debug filter
birdc> show route where bgp_path.last = 15685 routes with origin 15685
birdc> show route tableT1 where bgp_path ~[ =* 701 *=] all count - list a number of routes that go via Verizon
birdc> show route table T1 all filter { if ( 701 ~ bgp_path ) then accept; reject } count - does the same thing as the previous command
birdc> show route table T1 where bgp_path_first = 2828 && bgp_path.len = 2 all count - list a number of routes originating in XO and have AS-PATH length 2
birdc> show route export EDGE_R1 -  equivalent of show route advertising-protocol bgp in junos. 'EDGE_R1' is a name of the bgp group in bird conf
birdc> show route table T1 primary count - show only best routes

Read more

Create two loopback interfaces on juniper MX router

In Junos it is not possible to create two Loopback interfaces in one routing instance, so the configuration example below would be invalid:

root@router# show interfaces lo0 unit 0 { family inet { address 10.1.1.1/32; } } unit 1 { family inet { address 10.1.1.2/32; } } [edit] root@ny-edge-r1# commit check [edit interfaces lo0] 'unit 1' if_instance: Multiple loopback interfaces not permitted in master routing instance error: configuration check-out failed [edit] root@router#

 

Instead, we have to create a separate routing instance, create Loopback interface there, and import a direct route of the second loopback to the main routing instance inet.0:

root@router> show configuration interfaces lo0 unit 0 { family inet { address 10.1.1.1/32; } } unit 2 { family inet { address 10.1.1.2/32; } } ------------------------------------------------------------------------------------------ root@router> show configuration routing-options interface-routes { rib-group inet GROUP1; } rib-groups { GROUP1 { import-rib [ RI01.inet.0 inet.0 ]; import-policy LO2-2-INET; } } ------------------------------------------------------------------------------------------ root@router> show configuration policy-options policy-statement LO2-2-INET term 10 { from instance RI01; then accept; } term 100 { then reject; } ------------------------------------------------------------------------------------------ root@router> show configuration routing-instances RI01 { instance-type virtual-router; interface lo0.2; routing-options { interface-routes { rib-group inet GROUP1; } } }





LAB: Load sharing internet traffic in a small branch office using PBR

Scenario:
Office network consists of two internet facing SRX firewalls (FW1 and FW2) and L3 main switch (CORE-SW1). Core switch can be from any vendor, in our case, its a cisco device. Firewalls are connected to two different ISPs, FW1 is connected to ISP1 and FW2 is connected to FW2. CORE-SW1 has L3 uplinks to each SRX and has a couple of different VLAN L3 interfaces where users live, it is a default gateway for LAN.
Both firewall have IPsecVPN links to the datacenter network, which consists of actually two datacenters connected via 10g DCI. Clients/Users of the remote office need to be able to connect to the internet and also to the datacenter. We need to be able to fail over to the secondary ISP should the primary fail. Active/Active scenario is preferred.

Read more

SPADVROUTE - Multicast LAB

In this lab activity, you will implement and verify the operations of IGMP and MLD as well as
observe multicast flooding on the LAN when IGMP snooping is implemented.

You will work on different Cisco routers that are running Cisco IOS (c2900), Cisco IOS XE
(asr1001) and Cisco IOS XR (asr9k) Software. After completing this activity, you will be able
to meet these objectives:
 Configure IP multicast support and monitoring for IGMP and MLD
 Monitor the network without and with IGMP snooping

 

Read more