Anatomy of DDoS attacks
As part of the Soteria research project at THG, we needed to look at DDoS attacks, their features and how to generate the amount of traffic required to simulate an actual attack.
There are few resources available for folks researching cyber attacks and DDoS attacks in general. Attack data corpus are few and far between and typically very small samples or synthetic/generated data.
There are many approaches to DDoS mitigation, from simple rate-limiting and dropping traffic, to manipulating iptables/host firewall rules in reaction to signalling, to upstream routers to initiate congestion-control-like approaches.
Which of these approaches warrants further investigation depends upon factors such as where in the network the mitigation will sit; from directly on attacked host [iptables], to between the datacentre and the “wild internet” [BGP and routing based]. Large solution vendors (e.g. Cloudflare, Akamai Prolexic) in this space typically favour being between the attack and the customer datacentre.
In order to verify approaches to DDoS mitigation and alongside literature review of prior-art around the problem, we needed to create a DDoS simulation test-bed.
Simulating a real attack
By definition a DDoS attack has the capability to cause harm to a network and attached services. As such even creating a controlled simulation of an attack should be a task taken with great care.
To ensure that the attack software didn’t impact our operations, the first step was to build a separate network fabric and devices which would be decoupled from anything production related. However for the simulation to provide useful information for us, the design of the test network had to match the production network — same hardware devices, switches, same SDN, same NIC provider, same drivers etc.
Other aspects of the network environment to take into consideration are;
- the DDoS traffic generation software should not be limited by any hardware bottlenecks
- the switches etc should not ‘mitigate’ the attack due to memory constraints or other configuration limits
Essentially we need to ensure that the traffic being generated as the ‘attack’ is all received at the ‘attacked device’ network card and packets are not being lost along the way due to incorrect config (poorly sized packet buffers etc).
With a test environment / laboratory configured we had a safe platform to test the DDoS packet generation software. Next we had to evaluate the various tools and software packages for packet generation at scale.
hping3, trex & bonesi
After an initial evaluation period we focused our efforts on the following; hping3, trex and bonesi. Each of these could generate packets in a fashion that could simulate various types of attacks. As we required a scriptable or progammable solution, it was important that each of these have an API which we can program against.
DDoS Botnet Simulator is a Tool to simulate Botnet Traffic in a testbed environment on the wire — https://github.com/Markus-Go/bonesi
We considered bonesi as it is a C project and that fits well with the rest of the code the team is working with and, it has features around TCP traffic that are harder to find in other packages. It’s also explicitly designed to simulate a botnet-based attack which is a good foundation for learning about how a botnet could be constructed and used.
hping is a command-line oriented TCP/IP packet assembler/analyzer — https://tools.kali.org/information-gathering/hping3
Hping3 is again a C project, but this time the API is a Tcl/Tk script interface which allows for more rapid prototyping of ideas. It’s fairly old at this point and there is a decent amount of documentation around it.
While hping3 and bonesi are designed to create Denial of Service style attacks, trex is a little different in that it is designed around generating (realistic) packets and is less focused purely on security testing and more on generating packets quickly and efficiently:
TRex is an open source, low cost, stateful and stateless traffic generator fuelled by DPDK. — https://trex-tgn.cisco.com/
For our DDoS simulation we required the following additional characteristics:
- repeatable
- controllable
- the packet generator must be able to saturate the network interface
Reproducibility
A key focus for the team was to ensure that the simulated DDoS packets/traffic although similar in content, flow rate etc as a real attack, would be 100% reproducible. This meant that we had to remove source of randomness from the generation and instead use seeded random-number-generators in the code.
Controllable
It was of utmost importance that the packet generation was under control at all times and the process could easily be killed if needed — for fairly obvious reasons! We also had a requirement to be able to feed “industry standard” pcap files into the packet generator so we could use these files as the corpus of packets that we wished to model our simulated or generated packets on.
Network interface saturation
As the network lab has limited hardware capacity, it was important from an efficiency (and cost) point of view that we could generate sufficient traffic to simulate an attack using the least resources possible. Ideally we should be limited only by the network interface capability and the software stack should scale to use the resources available.
Crafting the ‘attack’
With a set of requirements defined and short-list of candidate tools to use, the team started testing the tooling and modifying where required.
Each packet “stream” takes a field engine (udp_fe
in this case) which uses the random seed to generate values in the range specified by the field engine. As one of our requirements is reproducibility, it was important to us to specify a random_seed
value so we could guarantee that each stream would generate the same data.
This required a small adjustment to TRex to allow us to manipulate the udp field engine to our liking. We also modified the code to allow us to feed in a pcap file and use that along with the normally generated packets. Using a pcap as the source of some of the packets allows us to determine the distribution of different types of packets.
With the changes to TRex implemented, we could fire-up the test and see what the performance was.
As we started the test, we recorded the key statistics from the packet generation device. Here we can see that we are generating about 53 million packets per second. This initial attack consumed 16 CPU cores on the packet generation device.
However on the device under test, we are only receiving 34 million packets per second. Despite this drop in packets received (something we need to investigate further), the test proved that with the correct configuration, and some modifications to how the packets are generated for our usecase, we are able to generate enough packets to simulate an attack which would cause disruption in a real-world scenario.
These stats from the device under test, were retrieved via some XDP code that was used to record the received packets.
Next steps
With the initial testing of the setup completed the team came up with some refinements and ideas for improving the testing.
First we needed to capture the stats of packets received at various places through the network fabric so we could discover the source of the packet losses. This is essential to allow us to have a robust testing platform for the future.
After we have managed to debug and resolve all the packet losses in the test network, our plans involve packet fingerprinting and how we can adjust packet features to avoid fingerprinting.
The code for packet generation can be found in the Soteria Research github repository.