Intrusion Detection Systems mailing list archives

Re: Detecting exploits/shellcode


From: vision () whitehats com (Max Vision)
Date: Fri, 16 Jun 2000 00:26:36 -0700


Archive: http://msgs.securepoint.com/ids
FAQ: http://www.ticm.com/kb/faq/idsfaq.html
IDS: http://www-rnks.informatik.tu-cottbus.de/~sobirey/ids.html
UNSUBSCRIBE: email "unsubscribe ids" to majordomo () uow edu au
At 09:30 AM 6/15/2000 +0200, Jonas Eriksson wrote:
Is it possible to detect buffer-overflow exploits being sent
over the network, except for having a database of shellcode?

Should it be possible somehow to decode the assembler code
being sent, or am i wrong?
Yes and no.  I'll comment briefly and touch on a few ways that you *can* 
detect buffer-overflow exploits.

First off, forget about "disassembling" binary content in packets - not 
only would you have to try to disassemble to the right target platform 
(x86,sparc,alpha,etc) and operating system but you would also need to know 
where the code starts and stops. Machine language instructions are usually 
several bytes in length.  If you start disassembling anything but the first 
byte of the instruction, you will have very different results.  An example: 
the single byte \x90 does not always represent a NOP instruction in x86 
binary.  Depending on context it may be data, or a part of another 
instruction.  So context is as important as knowing the platform. Also, 
provided that the byte code will be interpreted by the target as machine 
code, then you would never need to perform disassembly because there is a 
direct correlation.. it would be like converting hex to decimal - same 
stuff, just different ways to interpret the data.  Even if you could 
intelligently parse an arbitrary binary stream into meaningful assembly 
instructions, you would still need to compare these to known heuristics - 
and there will be a heavy performance penalty for trying.

I can only think of four ways to detect buffer overflow exploits (there are 
probably more and I would love to hear about them - please feel free to 
email me your ideas:).

* PROTOCOL CONTEXT - LENGTH
Since most buffer overflow exploits are composed of a longer string of data 
than anticipated, it is easy to identify violations of common expected 
lengths.  Right now, I am unable to find fixed command argument lengths in 
the RFC's.  Here is an example: in the POP3 protocol a user authenticates 
with "USER username", where 'username' will be a very short string, 
certainly less than 100 characters in length.  Perhaps 99% of the time the 
username will be 8 characters or less due to very widespread unix account 
name length restrictions.  So if we had our IDS watch for packets to POP3 
that start with "USER " and are longer than a hundred bytes (being 
liberal), then we could detect an attempted overflow of this part of the 
protocol.  Note that this does *not* indicate successful overflow, nor is 
it as general as it should be, since a general pattern should be used 
rather than just "USER username" commands.
I can confirm that the following rule for the free Snort IDS *will* detect 
this case:
alert TCP any any -> any 110 (msg: "pop3-overflow"; content: "USER "; 
nocase; depth: 5; dsize: >100;)

* PROTOCOL CONTEXT - CONTENT
In addition to length, we can usually expect specific types of command 
content - for example only printable characters.  To the best of my 
knowledge, usernames must not contain extended ascii sequences (often 
called control characters or unprintable characters).  Among other tests, 
one could simply check for the presence of these unprintable characters 
where they are not expected.
I don't have an example detection string for the Snort IDS because it does 
not currently support regular expressions (in the public builds) - this 
capability has been developed however, and is present in several (if not 
all) of the commercial IDS systems. [\w\s]*

These next two methods are most effective when combined with the above 
heuristics.

* KNOWN SHELLCODE
This is a little more straightforward.  Your advantage is that the majority 
of the attacks that you will receive will be from script kiddies who are 
running published exploits that they have downloaded - or even more common, 
that they have received in a pre-compiled form.  This means that if you are 
attacked by say, the rpc.ttdbservd remote overflow, the odds are 99+% that 
it will be the beautiful piece of work written by apk.  We can then dumb 
down to the days of earlier antivirus software (or gosh some AV still does 
this) and just watch for known strings.  I spend some of my free time 
creating and maintaining a database of exactly these types of signatures 
called arachNIDS (whitehats.com)  Anyway, here are some URLs to an example 
signature for Snort IDS that will detect a common overflow (sticking to our 
ttdb example):

http://whitehats.com/IDS/242 (rpc.ttdbserv-solaris-overflow)
(Actually that is a bad example, because I just noticed a flaw - note the 
example packet trace is broken into three packets, thus the packet 
containing the shellcode actually just has the ACK flag set, not PSH+ACK as 
the signature requires... so there are a few problems when you aren't doing 
tcp stream reassembly (let alone defragmentation))  but I digress.. don't 
worry about this as it's Snort-specific and the functionality will be added 
soon anyway...

http://dev.whitehats.com/ttdb.html (a quick write-up of the ttdb exploit, a 
messy thing I made a page for awhile back - provided here to illustrate the 
packet traces for a very common remote overflow).

* MULTIPLE NOPS
This is the same thing as matching signatures of known shellcode, only 
instead we are watching for the NOPs.  These NOPs will be sent by the 
exploit as padding to fill the buffer.  Also, we can probably catch other 
overflows where exploit authors "recycle" the same code (and thus use the 
same NOPs).  There are common "NOP" instructions for most machine 
languages, however be aware that in reality an actual "do nothing" 
instruction is *not required* in order to do nothing and serve as 
filler.  In fact, almost every machine instruction that isn't a flow 
control instruction (jmp, call, etc) can be used as a NOP.  As long as the 
instructions that are acting as filler don't disrupt the environment needed 
by the shellcode, an attacker could use almost anything to fill the space 
that leads to the shellcode. Here are some quick examples from my own research:

Sparc "NOPS" used in common remote exploits:
  ttdb (apk):                  char NOP[]="\x80\x1c\x40\x11";
  sadmind (Cheez Whiz):        #define NOP 0x801bc00f   /* xor %o7,%o7,%g0 */
  rpc.nisd (Josh Daymont/ISS): #define SPARC_NOP       (0xa61cc013)
  nlockmgr (senorp):           #define SPARC_NOP 0xa61cc013
  cmsd (horizon):              #define SPARC_NOP 0xac15a16e

Take note that these are all different opcodes, but accomplish the exact 
same result - to act as padding so execution pointer will ride up the stack 
to the shellcode.

So here are some quick Snort signatures that will detect not only the above 
buffer overflow attacks but any permutations that use the same NOPs (four 
nops in a row is probably enough right?):
alert TCP any any -> any any (msg: "nops-sparc-apk"; content: "|801c4011 
801c4011 801c4011 801c4011|";)
alert TCP any any -> any any (msg: "nops-sparc-whiz"; content: "|801bc00f 
801bc00f 801bc00f 801bc00f|";)
alert TCP any any -> any any (msg: "nops-sparc-iss"; content: "|a61cc013 
a61cc013 a61cc013 a61cc013|";)
alert TCP any any -> any any (msg: "nops-sparc-senorp"; content: "|a61cc013 
a61cc013 a61cc013 a61cc013|";)
alert TCP any any -> any any (msg: "nops-sparc-horizon"; content: 
"|ac15a16e ac15a16e ac15a16e ac15a16e|";)

So you see there are *some* things that can be done, just not really at the 
level you hinted at.

Max Vision
http://maxvision.net/
http://whitehats.com/


Current thread: