thicknet: Griefing Boss Hogg

Steve_rad

Most things I do seem really awesome at the time. Like the time I was at the Italian restaurant with my wife, and I made her a heart out of spaghetti. Or that time that I jumped onto the Chicago subway right as the doors were closing and then sang the Indiana Jones theme song with the crazy guy all the way to O'Hare. Or that time I jumped off the roof.

But it never fails that time and familiarity erodes the awesomeness of these feats. I've been trying to peel the "Hang Ten" sticker off of my stereo cabinet for years. And in retrospect, the macaroni aorta was definitely a bit over the top.

In a way, the same can be said for thicknet. As great as that thing is, calling it a "framework" turned out to be a bit of a stretch. It's kind of like hiring the Dukes of Hazzard as our own personal security force. They'll do anything we want, as long as it's something in Hazzard County and it involves griefing Boss Hogg (or in our case, Oracle). If you start talking to Bo and Luke about Microsoft SQL TDS, Uncle Jesse would probably get in his truck and chase us into the next episode. I think we're starting to outgrow this analogy.

After getting accepted to the excellent BSidesCLE (they apparently fill the 2pm slot according to Pop-O-Matic Bubble) I found myself in need of a new thicknet module. After agonizing between Microsoft SQL and CC:Mail for Netware 3.12 for awhile (also decided via Pop-O-Matic), I set to work on MSSQL's TDS protocol. And wow, after taking apart Net8, TDS seems like a bedtime story. This was going to be easy.

Except that Bo and Luke can't leave Hazzard County:


thicknet::pcap::Oracle::inject($sessions{$context}, $input);

Now I finally understand this whole object-oriented thing that you computer science people are always going on about. I want to create a new module that does a lot of what the old module does, but has some key differences. Plus, I need to be able to say "inject" to whatever session I want and have it magically do the right thing. You tricky kids, with your classes and objects and FM radio.

Fine, let's sell out to the man and start making objects. thicknet now has three types:

  • Pcap objects – these are responsible for grabbing packets off the wire. They use them to create …
  • Packet objects, which are processed and broken up into readily available properties such as "src_ip" and "src_port". These are used to feed …
  • Session objects, which track actual TCP conversations. Session objects are in charge of things like injection, and ACK-ing packets when we go into injection mode.

    Now when the main script wants to inject, we can simply tell the session to do so:


    $sessions->{$context}->inject($input);

    Cooter would be proud indeed.

    So now that the college profs are off our back, it's time to play with TDS, the official fuzzy protocol mascot of Microsoft SQL. There's a really great document here that explains the whole thing:


    http://msdn.microsoft.com/en-us/library/cc448435.aspx


    Other than some header info, it's almost like taking what you type on the command line and shoving it into a packet. Here's an example of a simple query (taken from Microsoft SQL Server 2008):


    Packet2

    Starting at 0x0020, the play-by-play details:


    01 – Type = SQL Batch. Generic construct for sending SQL messages.

    01 – Status = End of Message. This is the last packet in this request.

    0056 – Length. Size of the packet including 8 byte packet header.

    0000 – SPID / Server Process ID. Can be specified by client, but not required or common to do so.

    01 – Packet ID. Just set this to 01, it's for future functionality to track a sequence of TDS packets but is currently ignored according to the spec.

    00 – Window. Also reserved for future use, set to 00.


    And now we get into the data headers:


    16 00 00 00 – Total Length of Data Headers. Bytes from here to SQL command.

    12 00 00 00 – Header Length. Bytes from here to SQL command. We only have one header so it's a little redundant with the "Total Length".

    02 00 – Header Type. 01 is Query Notifications, 02 is a Transaction Descriptor, which we're using to do the query.

    00 00 00 00 00 00 00 01 – MARS Transaction Descriptor. Multiple Active Result Sets has been around since Microsoft SQL 2005, and is a way to feed multiple requests into a session without waiting for the results. We can set this to 01 for now (but should probably make a non-MARS version for older versions of the database)

    00 00 00 – Outstanding request count. How many outstanding MARS requests we have, in this case none. (Oddly, in the spec this is 4 bytes, but I've been seeing 3 in actual MSSQL traffic)


    Then we have the actual payload, which is encoded using UCS-2 Little-Endian. That's a fancy way of saying multi-byte encoding with the little number in the front, which is also a fancy way of saying that you put a 00 after your ASCII characters. It's a bit more complex than that if you want to use international symbols, or Funny Letters as they like to say where I grew up. (In my neighborhood you could get punched in the face for saying things like "umlaut")

    The great thing about this whole setup is that we don't have any session-specific blobs of data. Unlike Oracle Net8, where we had a bunch of stuff going on that we didn't understand, we can pretty much build these payloads from scratch and sit them on top of our L3-L4 sled. It's also nice to have the actual spec in front of us, but even if we didn't, we could just fake it using a sled like we did in Net8 land.

    So now, using the Session class as a base, we can create our own injection logic that creates the payload and adds our text accordingly. Here's the code from thicknet/session/MSSQL.pm:


    my $str = encode("UCS-2LE", $cmd);
    my $len = length($str) + 30;
    my $data = pack('C*',0x01,0x01,0x00,$len,0x00,0x00,0x01,0x00,0x16,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00);
    $data = $data . $str;

    Don't freak out about the hex, you have the cheat sheet a few paragraphs up. Both the TDS header and the data header can be completely static except for the data length, which we populate with $len. Once created, we call pcap_sendpacket, and it's off to the races. We also have to own the TCP session at this point, but that's old hat since last time.

    Here's the new MSSQL module in action. Notice that we don't see any visible disruption in SQL Management Studio. Database "studio" apps like this one frequently maintain a connection pool to speed the process of performing queries. Sometimes the database times out these connections, so it's a common occurrence for the application to open another connection without thinking twice. This design aspect is great for us, since it allows us to grab a connection without freaking out the user.

    The overarching moral of the story remains the same as always: encrypt your database communications. thicknet will fall flat on its smug face if it tries to inject into an encrypted stream. Microsoft offers good options here, so it's definitely worth a look:


    http://technet.microsoft.com/en-us/library/ms189067.aspx


    But enough of this cautionary stuff, there's a whole world outside of Hazzard County just waiting for you. You, with your object-oriented thicknet and your crazy sleds with the doors welded shut. The cipher might get you, but the creds never will.

Trustwave reserves the right to review all comments in the discussion below. Please note that for security and other reasons, we may not approve comments containing links.