Sunday, October 16, 2011

Some thoughts about MySQL proxy as a DB Firewall

Here are some notes about how to do a self learning DB Firewall.

MySQL proxy as a DB Firewall

A. Syntax statistics
1. Get the token chains (trees)
2. Store the chain producing stats
3. Be flexible on multiple criteria (dis/con)junctions (ie: advanced search forms will append multiple conditions with and/or/IN)

i.e:
- query:
SELECT user, email FROM users WHERE user = "Something"
- becomes:
TK_SQL_SELECT TK_LITERAL TK_COMMA TK_LITERAL TK_SQL_FROM TK_LITERAL TK_SQL_WHERE TK_LITERAL TK_EQ TK_STRING

- query:
SELECT user, comment, email FROM comments WHERE user = "someone" AND comment LIKE "%too bad"
- becomes:
TK_SQL_SELECT TK_LITERAL TK_COMMA TK_LITERAL TK_COMMA TK_LITERAL TK_SQL_FROM TK_LITERAL TK_SQL_WHERE TK_LITERAL TK_EQ TK_STRING TK_SQL_AND TK_LITERAL TK_SQL_LIKE TK_STRING

- query:
UPDATE comments SET comment = "too good" WHERE user = "someone" AND comment LIKE "%too bad"

- becomes:
TK_SQL_UPDATE TK_LITERAL TK_SQL_SET TK_LITERAL TK_EQ TK_STRING TK_SQL_WHERE TK_LITERAL TK_EQ TK_STRING TK_SQL_AND TK_LITERAL TK_SQL_LIKE TK_STRING

With those token chains we can build a tree of statistics based on SQL syntactic statements.


B. Value Stats of user-defined words over syntactic stats
1. For SQL statemens where columns and tables should be constants
2. Produce stats based on userdatas/values

i.e:
- query:
SELECT user, email FROM users WHERE user = "Something"
- becomes:
TK_SQL_SELECT TK_LITERAL TK_COMMA TK_LITERAL TK_SQL_FROM TK_LITERAL TK_SQL_WHERE TK_LITERAL TK_EQ TK_STRING
- values to get stats: TK_LITERALs
user email users


C. Statistical Regex of known chars for specific fields (paranoid mode… approach very restrictive):
1. By checking TK_STRINGS, we can build automatic regex to allow certain types
i.e:
- query:
SELECT user, email FROM users WHERE user = "Something"
- Char class seen for field "user":
\w
- query:
SELECT user, email FROM users WHERE user = "Some@thing.com"
- Char class seen for field "user":
\w@\.


D. Administrative data:
1. Forbid unusual information_scheme queries
2. Custom triggers


E. Other:
1. Forbid unusual "load into IN/OUT file"
2. Other unusual / malicious techniques used in sql injections (i.e: tautological analysis, like 1=1, 1=0, or i.e: listing of fields using NULL or zero in order to build a union select statement, or i.e: ORDER BY 1,2,3..)


F. Time to learn Lua! Happy Hacking!

Thursday, May 26, 2011

Holograms!

Lastly I've been playing with Kinect, Processing, shiffman's openkinect, and a homemade screen :).

Thanks to Jaime Blasco for his Kinect, and Antonio Rincón for his proyector.
Here is the result: Holograms!

Monday, May 9, 2011

Lost in translation: WTF is happening inside my Android phone

Jaime Blasco and Pablo Rincon

Description
=======

Lost in translation: WTF is happening inside my Android phone, by Jaime Blasco and I (Pablo Rincón Crespo).

-- English --
In this talk we'll try to cover the state of the art of Android Security, focussing on the most used techniques to analyze malware apps (tools, environments, static/dynamic analysis, reversing, antiemulation..).

We will also use a real example of one of the latest known malware for this platform, detected on february 2011, unmasking the logic of the CnC servers that controlled this malware.

-- Spanish description --
En esta charla trataremos de describir el estado actual de la seguridad en Android, haciendo hincapié en las técnicas más usadas a la hora de analizar aplicaciones maliciosas (herramientas, entorno, análisis estático, dinámico, reversing, anti-emulación, anti-análisis...).

En la conferencia se utilizará un ejemplo real de uno de los últimos troyanos aparecidos para esta plataforma, llegando a "desenmascarar" el funcionamiento de los servidores utilizados para controlar el malware.
--


Video and Slides
==========

At last, we have the video and slides:
The talk I did with Jaime Blasco at Rootedcon 2011. It's in Spanish.
http://vimeo.com/23058076

But we have the slides in english
http://www.slideshare.net/rootedcon/jaime-blasco-pablo-rincn

Friday, January 14, 2011

Python + divert sockets + scapy

I have prepared a small class for playing with divert sockets and python. This example shows how to register a packet handler for packets fetched from the divert, then it loads a Scapy packet from the IP layer, displaying the contents, and at last it forward the packet to be delivered. It's just a Proof of Concept, that doesn't block packets, but if you implement your logic at the packet handler, setting the veredict to false it should block it.

And you might think now, why not just use a crafted RST packet from scapy? And I would answer, because it is more reliable, and because you might prefer not to send anything to the source IP that you want to block. An rst packet is enough to know that something (some app) is alive at the other side. On the other hand, a combination of both would be the best fit here, because that will avoid long timeouts while waitting for an answer, and duplicated requests (some browsers make a lot of retries).


To test it (on MacOSX), you need to have scapy installed. Then create a divert socket with a rule similar to this (be careful, this will enqueue all the packets at ipfw and it might break your connections if you don't attach a program to veredict them on time):

one@macuto2$ sudo ipfw add divert 3282 tcp from any to any
Password:
00100 divert 3282 ip from any to any proto tcp

Now run the attached script as follows:
sudo python2.6 DivertSocket.py 3282
And it should start fetching packets reaching the function of the packet handler, where you should place your logic. You might want to try to block petitions by for example ip addresses (just for testing), or content payload, but remember that this doesn't reassemble tcp streams, there would be much more to do for content inspection. Anyway I hope someone will have some fun testing it.


DivertSocket.py
-- cut here --
import socket
import sys
import re

from scapy.all import *

if not socket.__dict__.has_key("IPPROTO_DIVERT"):
# Define if
socket.IPPROTO_DIVERT = 254

class DivertSocket:
def __init__(self, port, delegateFunc=None):

self.sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_DIVERT)
# Here the addr can be any. The important one is the port
self.sock.bind(("0.0.0.0", port))
# By default the max
self.bufsize = 65535
# Set blocking
self.sock.setblocking(True)
# Register callback
self.delegateFunc = delegateFunc
self.__loop = 1

def start(self, default=0):
self.fetchPackets(default)

def fetchPackets(self, default=0):
while self.__loop:
buf, addr = self.sock.recvfrom(self.bufsize)
# If we registered a delegate funcion, call it
if self.delegateFunc != None:
self.delegateFunc(buf, addr)
# Else send it if the default behavior matches
else:
print "Warning, no functions registered for inspection!"
if default:
self.sendPacket(buf, addr)
else:
print "You need to implement a callback function for inspection"
sys.exit(-1)

def setVeredict(self, buf, addr, veredict=False):
if veredict:
if self.__sendPacket(buf, addr) == False:
print "Pkt not sent. Weird.. Need to see which packet causes this error"

def __sendPacket(self, buf, addr = None):
try:
if addr:
return self.sock.sendto(buf, addr)
#else try send it raw anyway..
return self.sock.send(buf)
except KeyboardInterrupt, e:
print "Stopping Engine..."
sys.exit(0)
except:
print "Could not send packet..."
return False

def stop(self):
self.__loop = 0
self.sock.close()


def pktHandler(buf, addr):
p = IP(buf)
print p.display()
ds.setVeredict(buf,addr, True)

ds = DivertSocket(int(sys.argv[1]), pktHandler)

ds.start()
-- stop here --

BTW, I would be very pleased if someone can test it under linux using iptables. Sooner or later I'll try it anyway.

Happy hacking!
;-)

Thursday, January 13, 2011

Video Streamming - Flash/Flex/Actionscript3 - NetConnection+NetStream+RTMP FMS (Flash Media Server)

I have been playing with flash/flex and video streamming + chat, just for fun. I think there might be some people searching for some working examples, and I didn't find too much on the net working straight forward with the latest versions. So, that's why I want to share my working code:

Features:
- Publish a Stream (sound included)
- Receive a Stream (playing sound)
- Minimal chat components

What's needed: Flash Builder/Flex Actionscript 3 (You can addapt the example for normal Flash). FMS aka Flash Media Server with the application "live", that's usually installed by default. FMS will install Apache, publishing an admin console to view connections, app's status and stream status (*Hint: Check the stream status of the app "live").

3 mxml files:
- Publisher
- Viewer
- Main app

You would also like to create your own app on FMS, but I'm not covering this at the moment.

So, here we go:

The publisher will send the stream to the server through the NetConnection, using the webcam attached to a NetStream (with only 1 direction). It will also attach the mic if any.

Publisher.mxml
-- cut here --
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="410" height="490" creationComplete="LiveStreams()">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>

<fx:Script>
<![CDATA[

import flash.display.MovieClip;
import flash.events.ActivityEvent;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.media.Camera;
import flash.media.Microphone;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;

import mx.utils.ObjectUtil;

var nc:NetConnection;
var ns:NetStream;
var video:Video;
var camera:Camera;
var mic:Microphone;
private var meta:Object;


public function sendChat() {
var obj:Object = new Object();
obj = new Object();
obj.chat = this.txtSend.text;
this.txtChat.text = this.txtChat.text + "\n" + this.txtSend.text;
this.txtChat.verticalScrollPosition = this.txtChat.maxVerticalScrollPosition;
this.txtSend.text = "";
ns.send("receiveChat", obj);
}

public function LiveStreams()
{
startBtn.addEventListener(MouseEvent.CLICK, startHandler);
clearBtn.addEventListener(MouseEvent.CLICK, clearHandler);
stopBtn.addEventListener(MouseEvent.CLICK, stopHandler);
}

private function ns_onMetaData(item:Object):void {
trace("meta");
trace(ObjectUtil.toString(item));

/*
meta = item;
// Resize Video object to same size as meta data.
video.width = item.width;
video.height = item.height;
// Resize UIComponent to same size as Video object.
uic.width = video.width;
uic.height = video.height;
panel.title = "framerate: " + item.framerate;
panel.visible = true;
*/
}
 
private function ns_onCuePoint(item:Object):void {
trace("cue");
}
/*
* Connect and start publishing the live stream
*/
private function startHandler(event:MouseEvent):void {
trace("Okay, let's connect now");

nc = new NetConnection();
nc.client=this;
nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
nc.connect(this.txtServer.text);
}


/*
* Disconnect from the server
*/
private function stopHandler(event:MouseEvent):void {
trace("Now we're disconnecting");
nc.close();
}

/*
* Clear the MetaData associated with the stream
*/
private function clearHandler(event:MouseEvent):void {
if (ns){
trace("Clearing MetaData");
ns.send("@clearDataFrame", "onMetaData");
}
}


private function netStatusHandler(event:NetStatusEvent):void
{
trace("connected is: " + nc.connected );
trace("event.info.level: " + event.info.level);
trace("event.info.code: " + event.info.code);

switch (event.info.code)
{
case "NetConnection.Connect.Success":
trace("Congratulations! you're connected");
publishLiveStream();
break;
case "NetConnection.Connect.Rejected":
trace ("Oops! the connection was rejected");
break;
case "NetStream.Play.Stop":
trace("The stream has finished playing");
break;
case "NetStream.Play.StreamNotFound":
trace("The server could not find the stream you specified");
break;
case "NetStream.Publish.Start":

trace("Adding metadata to the stream");
// when publishing starts, add the metadata to the stream
var metaData:Object = new Object();
metaData.title = "UnStreammmm";
metaData.width = 200;
metaData.height = 150;
ns.send("@setDataFrame", "onMetaData", metaData);
break;

case "NetStream.Publish.BadName":
trace("The stream name is already used");
break;
}
}

public function onBWDone():void{
}

private function activityHandler(event:ActivityEvent):void {
trace("activityHandler: " + event);
trace("activating: " + event.activating);
}

/*
* Create a live stream, attach the camera and microphone, and
* publish it to the local server
*/
private function publishLiveStream():void {
ns = new NetStream(nc);
ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
var nsClient:Object = {};
nsClient.onMetaData = ns_onMetaData;
nsClient.onCuePoint = ns_onCuePoint;
ns.client = nsClient;

camera = Camera.getCamera();
mic = Microphone.getMicrophone();

if (camera != null){

camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler);

video = new Video();
video.smoothing=true;
video.width=200;
video.height=150;
video.attachCamera(camera);

ns.attachCamera(camera);
uic.addChild(video);
//this.addChild(video);
//myvid.source=camera;
}

if (mic != null) {
mic.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
mic.setUseEchoSuppression(true);
mic.rate = 44;
ns.attachAudio(mic);
}

if (camera != null || mic != null){
// start publishing
// triggers NetStream.Publish.Start
ns.publish(this.txtStream.text, "live");
} else {
trace("Please check your camera and microphone");
}
}



]]>
</fx:Script>

<mx:UIComponent id="uic" x="5" y="72" width="200" height="150" />

<mx:Button id="startBtn" x="15" y="40" label="Start"/>
<mx:Button id="clearBtn" x="93" y="40" label="Clear"/>
<mx:Button id="stopBtn" x="171" y="40" label="Stop"/>
<s:TextInput x="59" y="11" id="txtServer" text="rtmp://192.168.1.130/live"/>
<s:TextInput x="266" y="11" id="txtStream"/>
<s:Label x="15" y="15" text="Server"/>
<s:Label x="206" y="16" text="Stream"/>
<mx:TextArea x="10" y="380" width="390" height="66" id="txtChat" verticalScrollPolicy="auto" editable="false"/>
<s:TextInput x="10" y="458" width="308" id="txtSend"/>
<s:Button x="326" y="459" label="Send" click="sendChat();"/>
</s:Group>

-- stop here --


The Viewer component can connect to a stream, play sound and receive chat msgs from the stream being published:

Viewer.mxml
-- start here --
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="410" height="490" creationComplete="LiveStreams()">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>

<fx:Script>
<![CDATA[

import flash.display.MovieClip;
import flash.events.ActivityEvent;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.media.Camera;
import flash.media.Microphone;
import flash.media.Sound;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.media.SoundTransform;
import mx.utils.ObjectUtil;

var nc:NetConnection;
var ns:NetStream;
var video:Video;
var sound:Sound;
private var meta:Object;


public function LiveStreams()
{
startBtn.addEventListener(MouseEvent.CLICK, startHandler);
clearBtn.addEventListener(MouseEvent.CLICK, clearHandler);
stopBtn.addEventListener(MouseEvent.CLICK, stopHandler);
}

public function receiveChat(obj:Object) {
trace(ObjectUtil.toString(obj));
this.txtChat.text = this.txtChat.text + "\n" + obj.chat;
this.txtChat.verticalScrollPosition = this.txtChat.maxVerticalScrollPosition;
}

private function ns_onMetaData(item:Object):void {
trace("meta"+item.toString()+" "+item.title );
trace(ObjectUtil.toString(item));

/*
meta = item;
// Resize Video object to same size as meta data.
video.width = item.width;
video.height = item.height;
// Resize UIComponent to same size as Video object.
uic.width = video.width;
uic.height = video.height;
panel.title = "framerate: " + item.framerate;
panel.visible = true;
trace(ObjectUtil.toString(item));
*/
}
 
private function ns_onCuePoint(item:Object):void {
trace("cue");
}
/*
* Connect and start publishing the live stream
*/
private function startHandler(event:MouseEvent):void {
trace("Okay, let's connect now");

nc = new NetConnection();
nc.client=this;
nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
nc.connect(this.txtServer.text);
}


/*
* Disconnect from the server
*/
private function stopHandler(event:MouseEvent):void {
trace("Now we're disconnecting");
nc.close();
}

/*
* Clear the MetaData associated with the stream
*/
private function clearHandler(event:MouseEvent):void {
if (ns){
trace("Clearing MetaData");
ns.send("@clearDataFrame", "onMetaData");
}
}


private function netStatusHandler(event:NetStatusEvent):void
{
trace("connected is: " + nc.connected );
trace("event.info.level: " + event.info.level);
trace("event.info.code: " + event.info.code);

switch (event.info.code)
{
case "NetConnection.Connect.Success":
trace("Congratulations! you're connected");
publishLiveStream();
break;
case "NetConnection.Connect.Rejected":
trace ("Oops! the connection was rejected");
break;
case "NetStream.Play.Stop":
trace("The stream has finished playing");
break;
case "NetStream.Play.StreamNotFound":
trace("The server could not find the stream you specified");
break;
case "NetStream.Publish.Start":

trace("Adding metadata to the stream");
// when publishing starts, add the metadata to the stream
var metaData:Object = new Object();
metaData.title = "UnStreammmm";
metaData.width = 200;
metaData.height = 150;
ns.send("@setDataFrame", "onMetaData", metaData);
break;

case "NetStream.Publish.BadName":
trace("The stream name is already used");
break;
}
}

public function onBWDone():void{
}

private function activityHandler(event:ActivityEvent):void {
trace("activityHandler: " + event);
trace("activating: " + event.activating);
}

/*
* Create a live stream, attach the camera and microphone, and
* publish it to the local server
*/
private function publishLiveStream():void {
ns = new NetStream(nc);
ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
var nsClient:Object = {};
nsClient.receiveChat = receiveChat;
nsClient.onMetaData = ns_onMetaData;
nsClient.onCuePoint = ns_onCuePoint;
ns.client = nsClient;


video = new Video();
video.smoothing=true;
video.attachNetStream(ns);
video.width=200;
video.height=150;
sound = new Sound();
ns.soundTransform = new SoundTransform(1);
ns.play(this.txtStream.text);
uic.addChild(video);
//this.addChild(video);
//myvid.source=camera;

}



]]>
</fx:Script>
<mx:UIComponent id="uic" x="5" y="70" width="200" height="150" />
<mx:Button id="startBtn" x="15" y="40" label="Start"/>
<mx:Button id="clearBtn" x="98" y="40" label="Clear"/>
<mx:Button id="stopBtn" x="176" y="40" label="Stop"/>
<s:TextInput x="54" y="9" id="txtServer" text="rtmp://192.168.1.130/live"/>
<s:TextInput x="261" y="9" id="txtStream"/>
<s:Label x="10" y="13" text="Server"/>
<s:Label x="201" y="14" text="Stream"/>
<mx:TextArea x="10" y="380" width="390" height="100" id="txtChat" verticalScrollPolicy="auto" editable="false"/>
</s:Group>

-- stop here --


Now we merge both components, Publisher and Viewer into a single app:

MainApp.mxml
-- start here --
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:ns1="*">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<ns1:Publisher x="10" y="10" width="410" height="350">
</ns1:Publisher>
<ns1:Viewer x="432" y="10" width="410" height="350">
</ns1:Viewer>
</s:Application>

-- stop here --

To run the examples you must set the IP of the FMS server (IP_OF_THE_SERVER). Then open the webpage in 2 different browsers/tabs.
On browser/tab 1 set the stream name of the publisher component to for example "Jaime" (Thanks for testing), and the stream name of the viewer to "Pablo".
On browser/tab 2 set the stream name of the publisher component to for example "Pablo" (Thanks for testing), and the stream name of the viewer to "Jaime" (just the opposite to the other browser/tab).
Now click start on the publisher components and then start on the viewer components. You can also try to send some text with the chat window. That is thanks to metadata functions, where you can define a function handler at the netstream and send metadata with that handler name.

I found some issues because of older versions (ex:onBWDone()). That's why you will find some empty definitions at the code. Anyway they are not needed as soon as you create a custom client attached to the NetConnection.

As a final note, I'm not capturing all the exceptions that the application can throw, so please be careful using it! It is just a proof of concept..

;-)

Wednesday, September 8, 2010

Suricata 1.0.2 Released

We have a new release! Today, Victor Julien announced a new release of Suricata. See the details below:


The OISF development team is proud to announce Suricata 1.0.2, the
second maintenance release for Suricata 1.0, the Open Source Intrusion
Detection and Prevention engine.

Get the new release here:
http://www.openinfosecfoundation.org/download/suricata-1.0.2.tar.gz

New features

- Added an SSH application layer module, improving performance and accuracy
- Added two new SSH rule keywords: "ssh.protoversion" and
"ssh.softwareversion"
- Added support for missing HTTP related PCRE modifiers /H, /M and /C
(bug #220)

Improvements

- Fixed several TCP stream engine evasion issues found by Judy Novak
from G2, Inc.
- Improved accuracy of the http_client_body keyword
- Improved dropping of packets in IPS mode when a signature matches in
the reassembled stream or the application layer
- Improved error reporting if the engine runs out of memory in the
initialization stage
- Fixed a reported segv in the HTTP method detection keyword (bug #231)
- Several smaller issues were fixed

Because of the TCP evasions that are fixed upgrading is highly recommended.


Known issues & missing features

As always, we are doing our best to make you aware of continuing
development and items within the engine that are not yet complete or
optimal. With this in mind, please notice the list we have included of
known items we are working on.

Wednesday, July 21, 2010

HowTo setup suricata 1.0.0 on Mac OS X on IDS and IPS mode with IPFW

I'm really proud to announce that Suricata 1.0 has been released. This is the first stable version, with just one year old, as a result of a great effort of the development team, covering compatibility with nearly all the emerging-threats rule feed. Thanks to all of you guys! Keep up the good work. The engine has some known issues, that you can check on the OISF redmine . After the SF meeting I'm sure that we will work out a lot of new features for incoming releases that will start accomplishing the phase two of the project.

In order to setup Suricata running on a Ubuntu Linux/GNU box , you might want to follow the howto's of Victor Julien, posted at his blog (Inliniac).

But if you are a Mac OS X user, you might want to follow this steps. First I will try to cover a basic setup on IDS mode, and later will add the steps needed for IPS mode with IPFW.


1. Get the compiler and libraries:

The fastest way is to install XCode. XCode is a toolkit for Mac OS X developers that includes the most common compilers for GUI and terminal development. It includes GNU gcc, and is able to compile C, C++, Objective-C, Objective-C++, Java and AppleScript.

Next you need the libraries. You can install them one by one, but I guess it's easier, and probably faster to install a port manager tool like MacPorts (that was my choice).

After setting up MacPorts, run the following command:
port install autoconf automake make libnet11 libpcap pcre \
libyaml libtool pkgconfig
(*probably the auto* tools and "make" are yet installed by xcode).

Ok, now we should have the libraries installed (by default the paths differs a bit from linux.. they are usually installed at /opt/local/include).


2. Get and build the source.

Now let's get the source. Go to the download section of OISF to fetch the latest stable release. Anyway, you can also do this (but you should get the latest up2date version):
wget \
"http://openinfosecfoundation.org/download/suricata-1.0.0.tar.gz"
tar xvzf suricata-1.0.0.tar.gz
cd suricata-1.0.0
# If you want to play with suricata code
# you might want to enable debug with --enable-debug.
# if not, unittests should be more than enough
./configure --enable-unittests
make
sudo make install

3. Prepare the environment

Now that we have the source code compiled, let's prepare the environment.

Let's follow some common steps (should be nearly the same as Linux).
Aadd a user account and group for running suricata. If you have Mac OS X 10.4 or less:
dscl / -create /Users/suricata UserShell /bin/false \
RealName "Suricata idps engine" UniqueID 500 PrimaryGroupID 500
If you have Mac OS X 10.5 or higher:
dscl . -create /Users/suricata UserShell /bin/false \
RealName "Suricata idps engine" UniqueID 500 PrimaryGroupID 500
As the shell is /bin/false, you should not be able to log in with it.
# Create a directory for logs:
sudo mkdir /var/log/suricata/
# A directory for the config files:
sudo mkdir /etc/suricata/

# Copy the config file and classification config to /etc/suricata/
sudo cp /path/to/suricata-1.0.0/suricata.yaml /etc/suricata/
sudo cp /path/to/suricata-1.0.0/classification.config\
/etc/suricata/

# Ensure we will have enough perms to write the logs
sudo chown suricata:suricata /var/log/suricata/
Now you can get rule feeds for the engine from two different providers: Emerging Threats, and Sourcefire VRT. Let's go with emerging threats:
wget http://www.emergingthreats.net/rules/emerging.rules.tar.gz
cd /etc/suricata/
sudo tar xzvf /path/to/emerging.rules.tar.gz

4. Start the engine

Now we have a basic environment prepared for running the engine, on IDS mode and emerging threats rule feed.

You can start the engine by executing:
suricata -c /etc/suricata/suricata.yaml -i en1 --user suricata\
--group suricata
(the interface is en1 on my box, but it might differ to yours).

Cool, now we have the engine working. You can now check the stats log files located at /var/log/suricata. By default all the output types are enabled. Of course this is not the most optimal configuration. You can disable the outputs that doesn't work for you by editing /etc/suricata/suricata.yaml. maybe you want to go further and install sguil, acidbase, snorby, or any other viewer compatible with unified output (but that's another article I should write). By now, lets just check /var/log/suricata/fast.log to view the generated alerts.

5. Setup suricata as IPS with IPFW.

If you want to use suricata as IPS you will need to recompile the source, adding an extra option to the configure script. Go to the path of the sources and run the following commands:

cd /path/to/suricata-1.0.0
./configure --enable-unittests --enable-ipfw
make
sudo make install
Now that we have the binary capable of talking with ipfw, let's say to ipfw what traffic we want to allow/reject with suricata. By default, ipfw has a "catch all" rule, allowing all ip traffic. This one:

ipfw list
65535 allow ip from any to any


We need a ipfw rule to forward the traffic to the engine. We also need the engine to be running and getting the packets from the divert port of ipfw.
***Otherwise, no program will say to ipfw to allow the traffic, and you'll break all your connections! :)
No worries. Just keep in mind that you can execute the following command to stop the ipfw rule:
ipfw flush
Are you sure? [yn] y

Flushed all rules.
And you will have connection again. So, what we are going to do is to add the following rule:
ipfw add 100 divert 8000 ip from any to any

# it should print something like this:
# 00100 divert 8000 ip from any to any

You can check the rules by running ipfw list. And then, launch suricata reading from the divert 8000 we have just loaded into ipfw:

suricata -c /etc/suricata/suricata.yaml -d 8000 --user suricata\
--group suricata


Please, notice that we do not specify an interface here. We are getting the traffic from ipfw (not the interface). Now suricata can tell ipfw which packets to allow and which ones to deny.

The engine will also need special rules. By default the rules start with the action "alert", but to use IPS, that word should be "drop".

So now, you can test someting like this:

drop tcp any any -> any 80 (msg:"testing drop"; content:"google"; http_header; sid:123321;)

Save this rule to a file named test.rules and start the engine with

suricata -c /etc/suricata/suricata.yaml -d 8000 --user suricata\
--group suricata -s test.rules

And then try to load any webpage different to google. It should work. And if you try to navigate to google, the engine should directly stop that packets. Getting no response on your web browser (probably a timeout).

Please, notice that to "drop" packets is different to "reject". A reject packet is not dropped, but a special packet is sent to the endpoints to force a connection close. You might want to combine rules with different actions on the same file. Something like:

alert tcp any any -> any 80 (msg:"testing drop"; content:"google"; http_header; sid:1;)

reject tcp any any -> any 80 (msg:"testing reject"; content:"yahoo"; http_header; sid:2;)

drop tcp any any -> any 80 (msg:"testing drop"; content:"bing"; http_header; sid:3;)


And the engine should log all of them, but should only drop requests to bing, reject requests to yahoo, and alert requests to google.

But there's another important action that we need to know. That is "pass". A "pass" rule allows as to ignore anyothers actions triggered from other rules. This means that you can use it to fix possible false positives and add certain exceptions, depending on your network, your ruleset, etc preventing that connections to be dropped or rejected. So we can add the following rule as an exception:

pass tcp any any -> any 80 (msg:"testing drop"; content:"mail.yahoo.com"; http_header; sid:4;)

6. Final notes

To build a good rule set for IPS mode is definitely a must, and a critical task. You will need to fine tune your rule set since you must prevent the engine to fall under false positives that might be the result of a bad rule design, that could drop packets where it should allow them. So you will need to keep an eye on this, and maybe write some scripts to make your own score of reliability for rules (for a huge number of rules), or check them.. one by one. Of course, for a production environment you should use a more adaptative approach, like setting up suricata as ids mode first for a certain testing period, check all the generated alerts for false positives, and avoid using them with the action drop/reject. After that period of time (that should depend on the number of hosts you're monitoring and the throughgput and type of traffic), you should have a more trustable list of rules to modify with the action of drop (or reject).

Reject doesn't depend on ipfw. Keep this in mind since you don't need to pass all the traffic through ipfw to block connections. You can do a midterm approach. For example, you can pass only certain traffic to ipfw by creating more custom rules like "ipfw add 100 divert 8000 ip from 192.168.10.0/24 to my.server.com", then write reject rules like "reject !192.168.10.0/24 any <> !my.server.com any (...)". The "drop" rules will only affect to the communication between 192.168.10.0/24 and my.server.com, but reject will affect to the rest of connections. IPFW has a lot of features you should check, in order to set up your firewall.

With all of this said, you can go further into more complex configurations.
And that's all for now.

Please, feel free to ask me any questions/problems/suggestions you might have following this guideline. I'll be happy to help.