[Snort-sigs] MSSQL False Neg

Matt Olney molney at ...435...
Tue Dec 1 19:14:57 EST 2009


So, I thought it might be interesting for folks to see how I
approached the false neg report, I have my full notes below for those
who want to see how we work, but here is the meat of my findings (also
forgive any formatting issues, I C&P alot of this from VI, and there
is a ton of data in here):

OK, so after our jump, we land at offset 98 from the begining of the
payload.  This puts us 8 bytes behind the begining of the username
field (this is because of our TDS packet header, which is 8 bytes).
We then do the content check 8 bytes in distance from the doe pointer.
 The DOE pointer is set by the byte_jump, so is at 0x62 (98).  8 bytes
from there is 0x6a (106).  The payload begins at packet location 0x36,
if we add 0x36 to 0x6a, we get A0.  At A0 we have:

00a0  73 00 61 00 b3 a5 xx 00 xx 00 xx 00 xx 00 xx 00   s.a...x.x.x.x.x.

A perfect match for "s|00|a|00" aka "|73 00 61 00|".

The rule seems to be correct, I'm thinking there is a thresholding
issue somewhere.  Looking at it, it looks like it should alert if you
see 5 packets in a 2 second period that match this rule.  If you want
to check the non-thresholding portion of the rule, try the following
local rule:

alert tcp $EXTERNAL_NET any -> $SQL_SERVERS 1433 (msg:"SQL SA brute
force login attempt TDS v7/8"; flow:to_server,established;
content:"|10|"; depth:1; content:"|00 00|"; depth:2; offset:34;
content:"|00 00 00 00|"; depth:4; offset:64;
pcre:"/^.{12}(\x00|\x01)\x00\x00(\x70|\x71)/smi";
byte_jump:2,48,little,from_beginning; content:"s|00|a|00|"; within:4;
distance:8; nocase; reference:bugtraq,4797; reference:cve,2000-1209;
reference:nessus,10673; classtype:suspicious-login; sid:111113543;)

OK, stop reading if you're not interested in gory details, including
an oversight on my part:

First, I had to break the packet just into the payload section.  Then
I ran through the packet and established what it was the rule was
looking for.  You'll note that I failed to grab the within/distance
modifier to the final content match, so I decided to hand decode the
packet.  Bill's reference he was using was perfect for protocol
decoding.  At first I didn't know what the first 8 bytes were, but I
found the header details further up in Bill's reference.  Once I
decoded the patcket, I understood better what we were looking for.
Then I was able to see that we ended up 8 bytes short of the actual
location of the username.  It was immediately obvious that this was
because of the 8 byte header.  When I rechecked the rule, I saw that
the correct modifiers were already in place: distance 8: within: 4
(order not important for these modifiers).  The distance/within pair
indicates that the move is relative to the pointer, and the distance
value is what moves the pointer prior to the content check.  The check
must then pass within the number of bytes in the within field.  In
this case s|00|a|00| fits exactly in the 4 byte parameter, so you are
essentially saying these 4 bytes MUST occur 8 bytes away.  I even
wondered if the port was wrong, so I decoded the eth/ip/tcp headers
(By the way, Bill, very clever obfuscating the checksum so that your
IP obfuscation was more effective...NICE)  But everything checked out
there as well.  So, I was pretty confident we were good.

I also pulled our old notes on this bug and grabbed the test pcap we
had (generated by Alex, actually using a sql brute force tool).  Since
this is rev: 4, I was hoping to see some evolution of the rule, but
the rule in its current state is actually how it originally written.
All of the updates have been to add to the documentation.  I reran the
pcap with our rules and got a positive result.  I then wanted to check
the thresholding, so I pulled the thresholding out of the rule, and
placed them both in a local rules file.  Here was the output (sid 1
was the unthresholded rule:

Snort Test Suite v.0.3.0

Alerts:
1:1:0           SQL SA brute force login attempt TDS v7/8
          Alerts: 470
1:3543:4        SQL SA brute force login attempt TDS v7/8
          Alerts: 94

So right now, I >>THINK<< we're good, both on detection and on the
thresholding....so now we have to figure out why you aren't working.
Let me know how your tests go.

====BEGIN NOTES====
[Layer 2/3 HEADERS]
Eth
00 14 bf 52 fe 40   dst
00 d0 2b 77 75 01   src
08 00               type
IP
45                  Ver 4, Header size
20                  TOS
00 bc               Total length
1e 56               ID
40 00               Flags and frag info
6c                  TTL
06                  Protocol (TCP)
xx xx               Checksum
79 0b 50 ce         121.11.80.206 (Chinanet, full /24 block...I'm
guessing this was live attack traffic)
xx xx xx 7a         xx.xx.xx.122
TCP
08 2b               src 2091
05 99               dst 1433  (Correct port for rule)
a4 51 cc 4d         checksum
b1 be 2b 43         ack number
50 18               hdr len/reservered/flags (ack/psh set flags
consistent with stream state)
ff ff               window size
3d 81               tcp checksum
00 00               urgent pointer

[Detection Breakdown]

content:"|10|"; depth: 1;
10

content:"|00 00|"; depth: 2; offset: 34;
00 00

content:"|00 00|"; depth: 4; offset: 64;
00 00 00 00

pcre:"/^.{12}(\x00|\x01)\x00\x00(\x70|\x71)/smi";
10 01 00 94 00 00 01 00 8c 00 00 00 01 00 00 71

byte_jump:2,48,little,from_beginning;
62 00  [Read little endian, decimal: 98]

content:"s|00|a|00|";
44 00 57 00
"D" 00 "W" 00

Original packet data, serilalized:
10 01 00 94 00 00 01 00 8c 00 00 00 01 00 00 71 00 00 00 00 00 00 00
07 d0 19 00 00 00 00 00 00 e0 03 00 00 20 fe ff ff 04 08 00 00 56 00
06 00 62 00 02 00 66 00 01 00 68 00 00 00 68 00 0e 00 00 00 00 00 84
00 04 00 8c 00 00 00 8c 00 00 00 00 1c 25 5b 6f ff 00 00 00 00 8c 00
00 00 44 00 57 00 44 00 57 00 34 00 44 00 73 00 61 00 b3 a5 xx 00 xx
00 2e 00 xx 00 xx 00 xx 00 2e 00 xx 00 xx 00 xx 00 2e 00 31 00 32 00
32 00 4f 00 44 00 42

Original packet data, protocol decoded:

[TDS Packet Header]
Packet type:                10  (TDS 7.0 login packet)
Last Packet indicator:      01
Packet Size:                00 94
Unknown:                    00 00 01 00

[Login Packet Decode]
Total Packet Size [4]:                          8c 00 00 00

      4
TDS Version [4]:                                01 00 00 71

      8
Packet Size [4]:                                00 00 00 00

      12
Client Version Program [4]:                     00 00 00 07

      16
PID of Client [4]:                              d0 19 00 00

      20
Connection ID [4]:                              00 00 00 00

      24
Option Flags 1 [1]:                             e0

      25
Option Flags 2 [1]:                             03

      26
Sql Type Flags [1]:                             00

      27
reserved flags [1, mbz]:                        00

      28
time zone [4]:                                  20 fe ff ff

      32
Collation Info [4]:                             04 08 00 00

      36
Position of client hostname [2]                 56 00 [86 decimal]

      38
Hostname length [2]                             06 00

      40
Position of username [2]:                       62 00 [98 decimal]

      42
Username length [2]:                            02 00

      44
Position of password [2]:                       66 00 [102 decimal]

      46
Password length [2]:                            01 00

      48
Position of app name [2]:                       68 00 [104 decimal]

      50
Length of app name [2]:                         00 00

      52
Position of server name [2]:                    68 00 [104 decimal]

      54
Length of server name [2]:                      0e 00

      56
Int16 [2, mbz] [2]:                             00 00

      58
Int16 [2, mbz] [2]:                             00 00

      60
Position of library name [2]:                   84 00 [132 decimal]

      62
Length of library name [2]:                     04 00

      64
Position of language [2]:                       8c 00 [132 decimal]

      66
Length of language [2]:                         00 00

      68
Position of database name [2]:                  8c 00 [132 decimal]

      70
Length of database name [2]:                    00 00

      72
Mac address of the client [6]:                  00 1c 25 5b 6f ff

      78
Position of auth portion [2]:                   00 00

      80
NT Auth Length [2]:                             00 00

      82
Next position [2]:                              8c 00 [132 decimal]

      84
Int16 [2, mbz]:                                 00 00

      86
Hostname [n(6)]:                                44 00 57 00 44 00 57
00 34 00 44 00
        98      (DWDW4D)
Username [n(2)]:                                73 00 61 00

      102     (sa)
Password [n(1)]:                                b3 a5

      104     (encrypted)
Server Name [n(14)]:                            xx 00 xx 00 2e 00 xx
00 xx 00 xx 00 2e 00 xx 00 xx 00 xx 00 2e 00 31 00 32 00 32 00
        132     (xx.xxx.xxx.122)
Library Name [n(4)]:                            4f 00 44 00 42 00 43
00
        140     (ODBC)

OK...so we check:

content:"|10|"; depth: 1;                           [Not immediately
apparent what this is, as it is part of the undescribed header]
content:"|00 00|"; depth: 2; offset: 34;            [Checking the Sql
Flags and the Reserved flags are 00 00]
content:"|00 00 00 00|"; depth:4; offset:64;        [Checking the 4
must-be-zero bytes at offset 58 and 60]
pcre:"/^.{12}(\x00|\x01)\x00\x00(\x70|\x71)/smi";   [Verifying that we
have an appropriate version field at offset 8]
byte_jump:2,48,little,from_beginning;               [Grab the offset
of Username, jump the offset from begining of packet value here is 62
hex, 98 decimal]
content:"s|00|a|00|"; within 4; distance: 8;        [Check for username "sa"]



             OK, so after our jump, we land at offset 98 from the
begining of the payload.  This puts us 8 bytes behind the begining of
the username field (this is because of our TDS packet header, which is
8 bytes).  We then do the content check 8 bytes in distance from the
doe pointer.  The DOE pointer is set by the byte_jump, so is at 0x62
(98).  8 bytes from there is 0x6a (106).  The payload begins at packet
location 0x36, if we add 0x36 to 0x6a, we get A0.

At A0 we have:

00a0:  73 00 61 00 b3 a5 xx 00 xx 00 xx 00 xx 00 xx 00   s.a...x.x.x.x.x.

A perfect match for "s|00|a|00" aka "|73 00 61 00|".

The rule seems to be correct, I'm thinking there is a thresholding
issue somewhere.

alert tcp $EXTERNAL_NET any -> $SQL_SERVERS 1433 (msg:"SQL SA brute
force login attempt TDS v7/8"; flow:to_server,established;
content:"|10|"; depth:1; content:"|00 00|"; depth:2; offset:34;
content:"|00 00 00 00|"; depth:4; offset:64;
pcre:"/^.{12}(\x00|\x01)\x00\x00(\x70|\x71)/smi";
byte_jump:2,48,little,from_beginning; content:"s|00|a|00|"; within:4;
distance:8; nocase; threshold:type threshold, track by_src, count 5,
seconds 2; reference:bugtraq,4797; reference:cve,2000-1209;
reference:nessus,10673; classtype:suspicious-login; sid:3543; rev:4;)




More information about the Snort-sigs mailing list