[Snort-devel] [PATCH 2/5]: byte_extract: Add bitmasking support for calculated bytes
Joshua.Kinard at ...3108...
Joshua.Kinard at ...3108...
Fri Apr 29 00:42:39 EDT 2011
In researching ways to do content matching on NS records in the
authority section of a DNS Response packet, I ran up against the
compression pointer that DNS uses to reduce the size of response
packets. Some reading states that out of the two bytes, only 14 bits
actually encode the offset from the start of the payload to the repeated
DNS label. The first two bits are set to "11" when a pointer is used
(in little-endian order). With current byte_extract (and byte_jump),
there is no way to extract the full value of the offset. One can only
read the second byte, which gives a maximum offset of 256 bytes from the
start of the payload. If one reads both bytes, the presence of "11" in
the first two bits of the first byte will throw the calculation off.
So I have added a "mask" parameter to byte_extract (and byte_jump; see
follow-on patch) to be able to read in two full bytes and then mask out
the first two bits, so that the full 14-bit pointer is extracted. This
gives a new maximum offset of 16384 bytes from the start of the payload.
Encoded in the packet, we will read:
Answer: |CB 00 71 2A|
Authority: |03|ns7|06|bazbar|C0 18|
If one tried to use a rule with this content match:
content:"|00 02 00 01|"; rawbytes; content:"|03|ns7|06|bazbar|03|org";
It will not match because "|03|org" was already repeated in the query
section. One would write the below instead with current Snort:
content:"|00 02 00 01|"; rawbytes; content:"|03|ns7|06|bazbar|C0|";
rawbytes; distance:6; byte_extract:1,0,var_bazbar_org_ptr,relative;
content:"|03|org"; rawbytes; offset:var_bazbar_org_ptr;
This works, because we are already sitting on the "|18|" value,
regardless of endianess. But in the case of a packet where that
compression pointer is going to be refering to some value >256 bytes, we
need those remaining six bits in "|C0|" in order for our calculation to
work. Let's assume that first "|03|org" reference is 983 bytes from the
start of the payload. In binary, that's 0000001111010111, but DNS needs
the first two bits to be "11" to mark the presence of a pointer value.
Now we have 1100001111010111 (hex is |C3 D7|). But if we read those two
bytes into byte_extract in little-endian order, we're going to wind up
with a calculated value of 50,135. Which is a rather long walk off of a
The attached patch will allow the following to remedy this:
content:"|00 02 00 01|"; rawbytes; content:"|03|ns7|06|bazbar";
rawbytes; distance:6; byte_extract:2,0,var_bazbar_org_ptr,relative,mask
16383,little; content:"|03|org"; rawbytes; offset:var_bazbar_org_ptr;
Now we can read in the full 14 bits of this pointer, because '16383' in
decimal is 0x3fff in hex, or 0011111111111111 in binary. The value is
properly calculated when bitwise ANDed against |C3 D7|, and the
follow-on content match can use that as an offset from the start of the
payload to match the remaining piece of the DNS name.
I didn't set it to parse in hex because I couldn't find a good chunk of
existing code to do that. Feel free to tweak if needed.
-------------- next part --------------
A non-text attachment was scrubbed...
Size: 4867 bytes
More information about the Snort-devel