

Combined with our hardware acceleration, our Ruby-backed AppSim engine can easily pump out traffic at 10 or more gigabits per second.
In this library, protocol information is represented by Ruby objects called Blocks. These blocks can contain protocol-specific configuration options (like URIs, usernames or passwords), protocol state information, and can even contain other block objects. Block objects inherit still more information from their parent classes.
By creating custom protocol libraries with nested blocks, the end result is a simple to use way to create complex network messages. Also, using custom nested blocks leaves the resulting static strings with a multitude of valuable type information that we can use. I will cover more on how we can use that with my next blog post.
The Block Object
The Block object is the core of the Block library. It is the base class of every single block type that we create to define our protocols. Using this class as a base, a developer can create a specific block type like "HTTP::Request::Get", for example.
By combining lists of custom block types, a developer can typically reduce a protocol action (like in the AppManager UI) into a single command using Block. This is aided by convention that any options that don't get specified by a user will be randomly generated to look as realistic as possible.
# Simple FTP USER Command
b(FTP::Commands::USER).gen # => "USER akQA12KN\r\n"
# Simple HTTP GET Request
b(HTTP::Request::Get, :uri => "/index.html").gen # => "GET /index.html HTTP/1.1\r\nHost: a21naasd\r\n\r\n"
Block Syntax
This is the basic way to call into the Block library. The 'b' function is a globally accessible object factory that builds any type of your choosing. You can nest blocks within your block, and give them attributes or parameters.
The syntax used draws heavily on use of Ruby code block syntax. While it looks hairy at first, it simplifys things a bit. It reads a lot like XML, in that you have a element type and then that type can contain any number of other types.
Eventually you will take the object you just created and call the "gen" method on it. This lets Block decide how to best create a string matching the parameters that have been specified.
# Basic form
# b(TypeClass,:attribute=>'value') { |d| [BLOCK VALUE] }
# Static string generation
b(ByteString){|d| "hello"}.gen # => "hello"
# Static integer generation
b(Int::UINT16LE){|d| 0x01020304}.gen # => "\x04\x03\x02\x01"
# Random generation
b(String::AlphaNumeric, :min => 4).gen # => "mUw9"
# Compound Lists
b(ByteString){|d| ["one","two"]}.gen # => "onetwo"
# Deeply nested structure
b(Block){|d|[ # anon function returns array, note array commas
b(Block){|d| attr(:stored_object)},
b(Repeat,:times=>2){|b|[ # can use static attributes
b(Char){|d| "H"},
b(Char){|d| "E"},
b(Char){|d| "L"},
b(Char){|d| "O"},
]},
b(EOL) # uses default or random value for EOL ("\r\n")
]}.gen # => "(StoredObject)HELOHELO\r\n"
Custom Block Types
The real power of the Block library is in creating your own custom Block types. When a full suite of custom Block types are implemented for a protocol, generating realistic traffic becomes straight-forward and simple.
class FTP < Block
module Commands
class USER < Command
@operation = b(Operation){|d|"USER"}
@username = b(AlphaNum,:min=>1,:max=>24)
def encode(data)
b(FTP::Command::Line,:operation=>attr(:operation),:arguments=>[attr(:username)])
end
end
end
end
Block + AppSim
The BreakingPoint AppSim architecture exists to provide high-performance realistic application traffic with the easiest possible user interface. The user is able to supply a multitude of options that fine tune all of this network traffic.
Block was orginally created as an aid for the BreakingPoint Labs to help deal with the often very complex sets of options coming in from the users. Since Block can handle very complex options and auto-generate content, just passing on the AppSim options to a simple command like "b(HTTP::Request::Gen,@user_options)" can transform a simple HTTP message into a very realistic looking one. Easy:
GET /~sean/authd/index.html HTTP/1.1
Host: localhost
Connection: close
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip,deflate
UA-CPU: x86
Authorization: Digest username="httpuser",realm="private",nonce="408b6c576464477d675f04eb86609d90",uri="/~sean/authd/index.html",qop=auth,nc=00000001,cnonce="c9ea9ac0",response="da59792735e59ab97ddf3db1c7a5acde",opaque="5b168ba031c8cfa782b94ae587b79f67"
In the previous two posts on TCL, we've gone from running canned components like Routing Robot and Session Sender to running custom Attack Series with the Security component.
Today, I want to take a look at Security Evasions, explain what they are, and then show how users of the TCL API can apply evasions in their testing.
Evasions are ways an attacker modifies the traffic generated during an attack to cause network security devices to fail in the detection of the attack. They can be applied in different layers; for instance, you can perform layer-3 (IP) evasions by using IP fragmentation, setting your maximum fragment size to 64 bytes. Configuration for IP fragmentation can also accomodate changing the order the fragments are sent, overlapping the fragments in a variety of ways, having the Security component reassemble the frames according to the policy of a given operating system, and even having the client side fragments be a different size than those from the server side.
Of course, IP fragmentation isn't the only way an attacker can attempt evading detection. Evasions exist in each layer of the protocol stack, and they can be applied to Security attacks in the BreakingPoint products individually or in concert to help users find where their IPS fails to detect attacks.
Configuring IP Fragmentation in the Attack Manager

The full list of Security evasion options are available in a couple of ways. They are listed in the BreakingPoint User Guide, which is available from BreakingPoint Systems Documentation page on StrikeCenter (authentication required). Take a look at chapter 6 under Attack Group Options for further information. An alternate way is to use the Attack Manager in the BreakingPoint GUI. The options that are available depend on the specific strikes and strikesets that you have added to your Attack Series.
Getting Back to the Code
So, now's the time to fire up the latest version of harness.tcl to follow along. This is the script that performs the initial connection to your BreakingPoint device and puts you into the interactive TCL shell. Make sure that you're running the script from the directory in which you've extracted lib.tcl, which holds some utility functions that we will need in these examples.
As you have noticed, Security Evasions are grouped together according to what protocols they affect. We've already looked at IP fragmentation, but you can also set options for Ethernet, TCP, UDP, higher-level protocols like HTTP, and even HTML.
So, lets load up the TCL harness and get started:
[*] Connected...
% $bps configure -name "TCL Security Evasion Test"
% set security [$bps createComponent security security-evade]
% $security configure -attackPlan "Clientside Strikes"
% $security setDomain client 1 default
% $security setDomain server 2 default
Here, we've named our test, and selected to use an Attack Series that targets clientside vulnerabilities. If you want to run an existing Attack Series, but are unsure the name to use, you can get a listing with the following command:
% $bps listAttackSeries
When using TCL, we setup the evasion options using nested TCL dictionaries. Most other languages call this data type a hash. Conceptually, the options we will set in this example are hierarchically ordered like this:
This configuration will cause all clientside connections to be made to the server's TCP port 9999, which is useful in testing that the IPS can detect HTTP attacks that are on a port other than the standard port 80. Further, the server's responses will use HTTP chunked encoding, with a chunk size of 100 bytes. This is a good test of the IPS normalization or reassembly engine.
In TCL, this configuration is done as follows:
% set params { TCP { DestinationPortType static DestinationPort 9999 } HTTP { ServerChunkedTransfer true ServerChunkedTransferSize 100 } }
% set xml [_paramOverridesXml [$security id] $params]
% $bps _execXml $xml
% $bps save
% $bps run
The set params line is just the TCL way to define a dictionary, which are key/value pairs. Again, I'm nesting multiple dictionaries. You alternately could use the TCL construct dict create. The second line makes a call to a function I've defined in lib.tcl, _paramOverridesXml. This function generates the XML that we pass to the _execXml call.
With the ability to automate tests, and now with the ability to automate evasions, you can really put an IPS through its paces. You can hone in on a particular filter, and test different evasion techniques to see when detection of a particular attack fails. If you see that a filter fails when non-standard ports are used for HTTP, and you have an HTTP server that listens on a non-standard port, it might make sense to reconfigure the applicable filters to match on that port, and rerun the test again to see how detection rates change. It also makes sense to add background traffic to your test to see how performance changes with the reconfiguration of your filters.
This post shows an API function we haven't used previously, namely the _execXml method. This method is in the TCL API to allow the TCL UI to take advantage of new features that did not exist at the time the TCL API was developed. The GUI communicates with the BreakingPoint device using XML messages. If you're the kind that likes to look at lower-level stuff like this, take your favorite copy of tcpdump and filter on port 8880. You can take XML messages sent from the client, save them in a string in TCL, and use _execXML. If there is some function of the BreakingPoint GUI that doesn't exist in the TCL API, you can extend your scripts using this method call and the appropriate XML.
The code archive for this post updates the previous examples by adding the ability to add evasions to either a single strike, or a set of strikes. Please download, experiment, and post back with questions or examples of your of TCL-fu!
Last week we started to excerpt from our paper, "7 Ways to Reduce Time-to-Test", talking about automation, resource sharing, access to stateful traffic protocols and more. The final three tips in the paper look at removing a layer of abstraction between your network configuration and IP addresses and routing information, easily reproducing bugs for QA and how to stop babysitting your test equipment.
Download the full paper here (no registration required).
5) The Devil is in the Details
Re-configuring and developing new scripts every time you need to change a testing requirement is a large waste of resources. By creating a layer of abstraction between your network configuration and IP addresses and routing information you can eliminate the need to re-configure and develop new scripts every time you need to change your testing requirements. This step can eliminate days of configuration, allowing you to change an existing test in a matter of minutes.
Whether changing a test between a switched, routed, NAT or VLAN environment or changing IP address ranges, by creating this layer of abstraction you can quickly and easily designate addressing information for each test interface. You will also be able to create network domains in moments that will be reflected instantly across all tests and eliminate the need to tear down and reconfigure equipment. Some test gear excel at this, while others fall short. Other ways to create layers of abstraction is by leveraging physical layer switching devices such as the APCON®. These will remove the need to rewire your lab for each test.
6) Get Your Developers What They Need
Reproducing bugs for developers can sometimes be a chore, even as we realize the importance to the overall production. During these times you will want to simply provide an instant snapshot of any bugs for the developer. Most test devices allow you to capture and replay traffic in order to see what is causing any problems, however these captures are often too shallow and do not represent a true sample. Furthermore you want the traffic sample to be usable outside of your own equipment in an open format, e.g. PCAP.
When reviewing testing tools look that you are getting enough capture history per interface and that you can export the traffic sample as an open format. This will accelerate the debugging of devices as you witness the effects of real traffic by importing live traffic packet captures and coverting it automatically for stateful replay.
Additionally, device under test (DUT) Automation, which we wrote about above, also helps automate the regression testing your developers will need. DUT automation helps to eliminate manual power-cycle reboots or the need for a separate switch and allows a test engineer to connect the DUT, run a pre-saved test configuration, view the report and send it to the developer.
7) Unlock Yourself from the Lab
Babysitting test equipment is never a good use of your time. Why not monitor tests remotely and give yourself a chance to leave the lab on time for once! Before you get the idea that this is just about convenience, consider this: how many times have you come in to the surprise that your test failed in the middle of the night? This results in lengthy delays and costs.
Look for devices that do not require a client and have remote management capabilities, including the ability to securely log into the unit from a remote location to start, monitor, and conclude a test. Furthermore you need the ability to manage the DUT or SUT remotely, for example to cycle power and verify that it has been reset between test runs. Combine this remote functionality with automated testing and the ability to send results as email attachments and you might never have to go to the lab on a Saturday again.
Last week, Dustin wrote about some tools that aid in reversing network protocols, outlining three tools in particular: Protocol Informatics, PDB, and Discoverer. As Dustin lamented, nothing out there offers a simple push-button analysis, and I'm quick to agree. Accurate, rapid analysis of proprietary binary protocols is pretty hard, requiring a familiarity in usual socket programming practices, a determined patience, and a whole lot of note-taking. Closed binary protocols are rarely as simple as ICMP ping, they often have no real documentation, and the people who do know how they work usually keep their mouths shut.
With that said, I'd like to share some screenshots and discuss some of the techniques I've developed to deal with this arcane art.
A Simple Select Statement

The above is a simple SQL statement (starting at offset 0x6a), repeated ten times in fairly quick succession, then churned through a very simplistic packet payload difference engine "payload_diffgen.rb." It's written in Ruby, using PacketFu to pick out packets from a recorded session, and some simple heuristics to flag the bytes that change from action to action; those bytes are red and masked with an arbitrary 0xbd byte ("bd" because it kind of looks like a pair of upside-down glasses, and because the lines are on the outside so the kerning is nice). As you can see, the whole payload is 188 bytes, 31 of which are easily recognizable as text, giving a total binary "bloat" factor of about 6x.
The first ten bytes, colored black, are part of the known packet header for this particular protocol, so I'm not too interested in those (other people have already documented that part of this protocol). Among these ten queries, the lone byte at 0x0c is the only one that's changed. The mutant_diffs() function pulls that byte out of the session history, and we can see at a glance that it's steadily incrementing by four on each successive run of the command. Thus, we can postulate that this byte is responsible for some kind of counter. Of course, we can't tell if the surrounding bytes have anything to do with it with such a small sample -- what happens when this value rolls over after it hits 0xfe? Byte 0x0b might increment, or 0x0d, or maybe nothing at all. It all depends on the endianness of the value, or if it's just a single char.
The Response

Here, we have the response packet to my initial select. Again, there's some text -- 17 bytes' worth -- and even more binary overhead (288 bytes), giving an almost 17:1 binary:text ratio. Clearly, a lot is going on in this packet, and there's a lot more than one byte of difference between these two ostensibly identical responses. For brevity, I'll just concentrate on a couple of the more unusual features. Byte 0xba, as revealed by mutant_diffs(), is not a serial counter, but is more of a cyclical value -- and more importantly, seems to have no correlation to the initial request. This value is keeping track of something internal to the server, and we will only be able to tell if the client does anything with this value later on in the session. So, it's something to keep an eye on for later (this is part of that note-taking aspect required for protocol analysis).
Elsewhere in the payload, byte 0xd7 is incrementing at a rate of four per response, so it's surely related to byte 0x0c in the initial request -- even though each iteration is two less than the initial request (0xc2 produces 0xc0, 0xc6 produces 0xc4, etc). Unlike the request's 0x0c byte, though, this byte is surrounded by nulls. This implies that those nulls are just padding characters, and the original sequence number is just char. By the way, it's this kind of contextual awareness that today's automated analysis tools are notoriously bad at doing.
Sessions Separated by Time

This screenshot shows the differences between the same select statement being sent on different days. The requests are identical -- both ask for the same data, are 188 bytes long, etc. -- but we can see that there are a lot of differences between the first ten (issued on day 1) and the next ten (issued on day 2). We can hypothesize that there are variables in the request that are responsible for time-stamping. The byte at 0x18 increments by one between yesterday and today, so we can expect it to increment by one tomorrow -- unless this is a coincidence.
Coincidental correlation is a common error in protocol analysis (both automated and manual). However, in this case, we have a little more support for the time theory. Byte 0x54 is identical to byte 0x18, so it's more likely they both are tracking the same event. It may still be unrelated to time -- it may be a process ID marker, say -- but at least we've ruled out the idea that it's a constant magic value, or a length, or an offset, or anything else related to to the structure of the request itself.
Time-Sensitive Responses

The response between yesterday and today similarly reflects more differences. Again, the data is identical, but we can see a series of bytes, 0x1f through 0x22, that change between sessions. In socket programming, 4 byte groups are very common, since it indicates a value with a width of at least 32 bits, a typical long. In this case, the value at 0x1f[0,4] yesterday was 0x0d0e232d, while today's is 0x0f0c2102. Depending on endianness, they might also be 0x2d230e0d vs 0x02210c0f. However, it seems unlikely that this is a millisecond or even a second counter -- remember, we're looking at a set of ten responses, each of which are separated by about two seconds. If it's a timestamp with at least second fidelity, we should have seen this in just yesterday's sample. Besides, bytes 0x0f[0,3] all seem awfully close together -- byte 0x1f is up two, and bytes 0x20 and 0x21 are both down two. So, they do change over time, but they're not measuring time. Maybe things will get clearer when we look at other aspects of the session.
Two Different Selects

In my final example, I have two different select statements. One is for, "select region_name from regions;" and the other is, "select region_name,abbr from regions;". As you'd expect, the bytes where the data starts to diverge are blanked out. However, byte 0x1f, immediately preceding the select statement, pops out immediately as a length variable. The first statement is 31 bytes long (if you drop the terminating semicolon), which is 0x1f, while the second statement is 36 bytes, aka, hex 0x24. And because if the differing lengths, and a lack of padding characters in the protocol, any analysis after this divergence point is going to be off; my first-pass analyzer here is not smart enough to adjust out the differences accordingly. If we want to experiment select statements of differing data, we should probably stick with a single length common to both so as to eliminate that sort of self-induced interference.
Two Different Responses

Given that I'm eliciting a very different response, it's unsurprising that payload_diffgen splashes red all over my data. We've run into a similar problem as the last packet; not only is the result set different, but more importantly, they're of different lengths. However, up until the divergence, we can still make some use of the payload.
Notice bytes 0x0c through 0x1b. Like four, sixteen is a magical length. Looking at these bytes side-by-side, we can see that they appear randomly changed -- and here's where some background in other protocols comes in handy. Whenever I see 16 bytes a) near the top of a payload that b) stays static when the payload's static and changes wildly when you have different content, I immediately think, "MD5 hash." Unfortunately, simply hashing the payload after the suspected hash doesn't produce a match, nor do permutations on hashing the data -- but I'm still quite confident that this is a hash of something.
Eventually, experimentation proved this theory out. Turns out, if you change the select statement in a way that's syntactically meaningless (in this case, adding an extra space), this MD5 hash value changes as you'd expect, which is a solid indicator that the theory is correct. Sadly, though, it's not a straight hash of the select statement. That's okay, though. Elsewhere in this protocol, I've noticed that it has a tendency to xor values together and then produce a hash of that operation, so I suspect something similar is going on here.
And repeat, and repeat, and repeat...
We've looked at a single query-response pair, and really have only scratched the surface of the protocol. All together, I'd say there are about a hundred-ish unknowns across the whole session that were investigated using these highly manual techniques. I wish there was more automation. Flagging bytes as length fields, and padding accordingly, is the next big trick. Today, there really isn't a lot out there that replaces this classical scientific method. Toolchains like PDB are helpful (for fuzzing), as are stats engines like PI (for simple protocols). I'm sure Microsoft's Discoverer is fantastic, but alas, its implementation is also quite secret.
At any rate, if there's enough public interest in this sort of thing, I'm hopeful I'll have something beyond mere screenshots to share. Believe me, I'm not keeping my code under wraps because it's too awesome for mere mortals -- it's really just incredibly messy and hackish, and certainly not ready for public scrutiny. If and when I do release the next automated analysis whatsamajigger, you'll certainly read about it here.
Yesterday we started to highlight some of our tips from the "7 Ways to Reduce Time-to-Test" paper, starting with how to simplify your testing environment and having access to stateful traffic to speed up the process. Today we excerpt numbers three and four. Number three has my favorite heading in the paper, "Domo Arigato Mr. Roboto", and talks about what to look for when automating testing including the importance of DUT automation, agile testing tools and even the testing tool GUI. The fourth tip gives some practical ways to gather and share testing knowledge with your community, whether internal or external. This includes using a wiki, sites like PerfTesting.org and even reports from organizations such as Sandvine.
Here is the excerpt or head over and download the paper here (no registration required) and let us know what you think, either here or on Twitter.
3) Domo Arigato Mr. Roboto
Manual testing will always be time-consuming and lead to more errors, not to mention that it will never scale to your lab’s needs. At the same time, legacy testing tools are complicated and difficult to maintain, often relying on dated scripting languages. Replace any testing tools that do not provide several levels of automation to help you “write once, test often.”
Automation should be intrinsic throughout your testing platform, particularly today when we all need to do more with less. When it comes to testing tools, some of the top time-saving automation capabilities include:
4) Communal Thinking
“Knowledge is of two kinds. We know a subject ourselves, or we know where we can find information upon it.”
- Samuel Johnson
Gathering testing knowledge from a community of experts can help you reduce time-to-test. Your community of testing experts includes your own staff, the testing vendor, industry forums and more. Understanding and utilizing these stores of information can have a positive impact on your time-to-test. As you begin to gather this knowledge from other areas, one suggestion is to organize it for future use by storing tests and test data in a centralized repository, such as a wiki.
Some great examples are the reports from companies such as Sandvine, which tell you the make-up of traffic patterns that are out on the Internet. This data changes from quarter to quarter, so it’s best to keep a running history. Certain test vendors also provide this data so you won’t have to guess about traffic patterns when testing. Finally, there are sites such as perftesting.org where QA engineers from a variety of companies post information on the latest test methodologies, tools and techniques. Getting involved in these online communities will not only improve your testing, but also help you network.
Tags: