Writing Custom OSSEC Rules

Step 1: Add the log files you want to monitor to ossec.conf

Do this step on ossec server and agent both. Open up /var/ossec/etc/ossec.conf and, near the end of the file (before </ossec_config>), add the following:

<localfile>       
  <log_format>syslog</log_format>
  <location>/var/log/my_app_log.log</location>
</localfile>

I used syslog here as it’s recommended for log files that have one entry per line. Available values for log_format are syslog, snort-full, snort-fast, squid, iis, eventlog (for Windows event logs), mysql_log, postgresql_log, nmapg or apache.

If you’re monitoring log files that contain changeable dates, OSSEC understands strftime variables, so, for example, if your log file is /var/log/apache2/access.log.2010-09-25, you can set location to <location>/var/log/apache2/access.log.%Y-%m-%d.

Tip: You can render a strftime variable at the command line to verify it quickly. Just type date +%X at the command line, where X is the stftime variable. date +%Y-%m-%d gives us the string we need for our access logs, date +%s gives us Epoch time UTC.

Step 2: Create a custom decoder

OSSEC uses decoders to parse log files. After it finds the proper decoder for a log, it will parse out fields defined in /etc/decoders.xml, then compare these values to values in rule files – and will trigger an alert when values in the deciphered log file match values specified in rule files. These values can also be passed to active response commands, if you’ve got them enabled.

The log line I want to trigger an alert for looks something like this:

2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!

Do this step on ossec server. Open up /var/ossec/etc/local_decoder.xml (you can also use decoder.xml, which already exists, but using local_decoder.xml will assure that you don’t overwrite it on upgrade). First, we want to create a decoder that will match the first part of the log entry. We’ll use the date and first few characters to grab it using a regular expression. Note that OSSEC has its own sort of interpretation of regex, so don’t try to get fancy. I spent a lot of time pulling my hair out after using \d{4} type regex syntax – think simpler and you’ll have more success: you have to use \d\d\d\d instead.

In the following decoder, we start at the beginning of the line (\^), then match the digits in YYYY-MM-DD HH:MM:SS. After the date and time, I may have a few different log levels listed, INFO, WARN, DEBUG, etc., so I’ll just match any number of characters greater than 0 (\w+). We also want to end on something relatively unique since the log level regex I used is so loosy-goosy, and I know this is a ForceField alert and all ForceField alerts will contain ForceField, so I’ll use the following.

<decoder name="forcefield">  
  <prematch>^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \w+ ForceField</prematch>
</decoder>

Let’s take a break here, and see if this triggers our alert. Save and exit local_decoder.xml, then run /var/ossec/bin/ossec-logtest.

When it comes up, paste your log line:

2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield

**Phase 1: Completed pre-decoding.       
full event: '2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!'       
hostname: 'my_system'       
program_name: '(null)'       
log: '2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!'
**Phase 2: Completed decoding.       
decoder: 'forcefield'

You should see forcefield show up as the decoder. Great! Now, let’s parse out the values we care about.
In case you get an issue like “No decoders match” in ossec-logtest, please check your decoder file. It might be most probably due to some syntax error in your decoder xml.

Re-open local_decoder.xml and, beneath your forcefield decoder, create a new decoder:

<decoder name="forcefield-alert">  
  <parent>forcefield</parent>  
  <regex offset="after_parent">IP:(\d+.\d+.\d+.\d+)@(\w+): (forcefield \w+); (\.*)</regex>  
  <order>srcip,url,action,extra_data</order>
</decoder>

So, what’d we do here?

The obvious stuff first: We gave it a name, and designated forcefield-alert as a child of forcefield. Whenever a log matches the forcefield decoder, it’ll then be decoded using forcefield-alert to extract the data fields to match on.

Now for the fun stuff…First, we set the offset to “after_parent” – this means that OSSEC starts looking for matches after the ‘prematch’ stuff (date, time, & ForceField) we specified inside the parent forcefield.

So our log line actually looks like this:

2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!`

But after extracting the pre-match data, our log line, in OSSEC’s brain, looks like this:

IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!

So what do we care about? What fields do we want to test again? A good rule is to decode any data that you want to match inside a rule as well as any data you might need to initiate an active response. I set these items to bold below:

IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!

OSSEC only allows specific field definitions. These can be found at the top of the local_decoder.xml file. For the purposes of our log file, we’ll want the IP, the script, the action taken by the system, and the additional data.

When creating the regex for OSSEC, we extract all data inside parenthesis, so we build our regex like this:

IP:(\d+.\d+.\d+.\d+)@(\w+): (forcefield \w+); (\.*)

Then, to specify which parenthetical regex is which field, you add the <order> line, using available fields in decoders.xml:

<order>srcip,url,action,extra_data</order>

Save your local_decoder.xml and let’s run the log file through ossec-logtest again.

ossec-testrule: Type one log per line.
2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!
**Phase 1: Completed pre-decoding.       
full event: '2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!'       
hostname: 'my_system'       
program_name: '(null)'       
log: '2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!'
**Phase 2: Completed decoding.       
decoder: 'forcefield'       
srcip: '127.0.0.1'       
url: 'script_x'       
action: 'forcefield on'       
extra_data: 'enabled forcefield arbitrarily!'

Looks good! It found our decoder and extracted the fields the way we want ‘em. Now, we’re ready to write local rules.

Step 3: Write custom rules

Do this on ossec server. Open /var/ossec/local_rules.xml and add rules. First, we create a group, and a “catch-all” rule to run against any log that is decoded by our forcefield decoder. We set this as level 0 because we don’t want it to trigger an alert:

<group name="forcefield">
  <rule id="700005" level="0">
    <decoded_as>forcefield</decoded_as>   
    <description>Custom Forcefield Alert</description>
  </rule>
</group>

Next, we add dependent rules that trigger if the action matches what’s specified in the rule. <if_sid> specifies the dependency:

<group name="forcefield">
  <rule id="700005" level="0">    
    <decoded_as>forcefield</decoded_as>
    <description>Custom Forcefield Alert</description>
  </rule>  
  <!-- Alert if forcefield enabled -->  
  <rule id="700006" level="12">    
    <if_sid>700005</if_sid>    
      <action>forcefield on</action>    
      <description>Forcefield enabled!</description>  
  </rule>  
  <!-- Alert if forcefield disabled -->    
  <rule id="700007" level="7">    
    <if_sid>700005</if_sid>    
      <action>forcefield off</action>    
      <description>Forcefield off!</description>  
  </rule>  
  <rule id="700008" level="14">    
    <if_sid>700005</if_sid>    
      <action>forcefield hyperdrive</action>    
      <description>Forcefield in hyperdrive, watch out!</description>  
  </rule></group>

Save your local_rules.xml file, and let’s test it again:

ossec-testrule: Type one log per line.
2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!
**Phase 1: Completed pre-decoding.       
full event: '2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!'       
hostname: 'my_system'       
program_name: '(null)'       
log: '2010-09-25 15:28:42 WARN ForceField IP:127.0.0.1@script_x: forcefield on; enabled forcefield arbitrarily!'
**Phase 2: Completed decoding.       
decoder: 'forcefield'       
srcip: '127.0.0.1'       
url: 'script_x'       
action: 'forcefield on'       
extra_data: 'enabled forcefield arbitrarily!'
**Phase 3: Completed filtering (rules).       
Rule id: '700006'       
Level: '12'       
Description: 'Forcefield enabled!'
**Alert to be generated.`

Cool — now we’re ready to restart OSSEC and check alerts. When restarting OSSEC, you may find that the new log file that you’re using should exist before you restart OSSEC — if it doesn’t find it, it ignores it. Also, when writing your own rules, set levels specific to your OSSEC deployment — for example, if you’ve enabled active response and want to trigger it, make sure you extract the srcip using your decoder and set the level in the rule to match the level specific to your active response command in ossec.conf.

You’ll probably find that you need to do some tuning, and that some of the alerts you receive will trigger unwanted alerts if they fall through the decoder sieve. I haven’t figured out a way to exclude the file from inspection if it fails to match any decoder (if you know of one, let me know!), but the solution I’ve used is to create a new local rule that matches based on the syslog sid and match, like so:

    <rule id="100009" level="0">
      <if_sid>1002</if_sid>
      <match>Some string in the log I don't want to see</match>
      <description>Don't syslog alert on this one</description>
    </rule>

Repeat for each false positive. It’d be really useful to only allow a single decoder to work on a log file – if anyone knows how to do that, let me know!

OSSEC custom rules: https://apassionatechie.wordpress.com/2019/12/01/ossec-custom-rules-examples/

OSSEC: Writing your own rules


OSSEC cutom decoders, rules and alerts: https://apassionatechie.wordpress.com/2019/12/01/writing-ossec-custom-rules-decoders-and-alerts/
OSSEC issues: https://apassionatechie.wordpress.com/2019/12/01/warn-waiting-for-server-reply-not-started-ossec-agent/

OSSEC start problem due to keys

OSSEC – Custom rules examples

Silencing certain rules

<rule id="100030" level="0">
  <if_sid>503,502</if_sid>
  <description>List of rules to be ignored.</description>
</rule>

OSSEC will not produce any alert when rule 502 and 503 is triggered, level=”0″ ignores alerts.


Ignore alert if rules triggered by certain IP

<rule id="100225" level="0">
  <if_sid>40101</if_sid>
  <srcip>127.0.0.1</srcip>
  <description>Ignore this</description>
</rule>

If rule 40101 triggered by 127.0.0.1, dont produce any alert


Ignore alert if contains certain strings

<rule id="100223" level="0">
  <if_sid>1002</if_sid>
  <match>terrorist|terror|femmefatale|heart-attack</match>
  <description>Ignore 1002 false positive</description>
</rule>

OSSEC is using OS_match/sregex syntax in <match>


Ignore alert if contains certain strings (using regex)

<rule id="100207" level="4">
  <if_sid>1002,1003</if_sid>
  <regex>^WordPress database error You have an error in your SQL syntax(\.*)functionName$</regex>
  <description>Unescaped SQL query, known issue</description>
</rule>

OSSEC is using OS_regex/regex syntax in <regex>


Trigger custom rule when certain field match certain value in cdb list

<rule id="100215" level="5">
  <if_sid>31101</if_sid>
  <list lookup="match_key" field="url">rules/badurl</list>
  <description>URL is in badurl</description>
</rule>

Trigger custom rule when certain rules is fired x time within n second from same srcip

<rule id="100216" level="10" frequency="4" timeframe="90">
  <if_matched_sid>100215</if_matched_sid>
  <same_source_ip />
  <description>Multiple badurl access </description>
  <description>from same source ip.</description>
  <group>web_scan,recon,</group>
</rule>

Overriding rules

<rule id="1003" level="13" overwrite="yes" maxsize="2000">
  <description>Non standard syslog message (size too large).</description>
</rule>

Original rule 1003 have 10245 as its maxsize. Using overwrite=”yes” will make OSSEC overwrite certain field in original rule


Custom rule group

<group name="app_error">
  <rule id="100207" level="4">
    <if_sid>1002,1003</if_sid>
    <regex>^WordPress database error You have an error in your SQL syntax(\.*)functionName$</regex>
    <description>Unescaped SQL query, known issue</description>
  </rule>

  <rule id="100218" level="0">
    <if_sid>1003</if_sid>
    <match>WUID | WTB</match>
    <description>ignorance is bliss</description>
  </rule>
</group>

Writing OSSEC Custom Rules, Decoders and Alerts

OSSEC (http://www.ossec.net) is an open-source host-based intrusion detection system (HIDS). OSSEC can be used to monitor your local files and logs to check for intrusions, alert you of rootkit installation and do file integrity checking. OSSEC is a wonderful tool because it is highly customizable. By default, OSSEC monitors many of the programs commonly installed on a machine, but its real power comes from the ability of system administrators to customize OSSEC. By writing custom rules and decoders, you can allow OSSEC to parse through non-standard log files and generate alerts based on custom criteria. This allows OSSEC to monitor custom applications and provide intrusion detection services that might otherwise not be available, or would have to be developed on a per-application basis.

OSSEC rules are based on log file parsing. The log files that OSSEC monitors are specified in the /var/log/ossec/etc/ossec.conf file in the following format:

  <!-- Files to monitor (localfiles) -->

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/messages</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/secure</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/maillog</location>
  </localfile>

  <localfile>
    <log_format>apache</log_format>
    <location>/var/log/httpd/error_log</location>
  </localfile>

  <localfile>
    <log_format>apache</log_format>
    <location>/var/log/httpd/access_log</location>
  </localfile>

Each file that is monitored depends on a “decoder” which is a regular expression used to parse up the pieces of the log file to extract fields such as the source IP, the time, and the actual log message. The decoders are stored in /var/ossec/etc/decoder.xml. The following is an extract of the SSH decoder portion of the decoder.xml logfile:

<decoder name="sshd">
  <program_name>^sshd</program_name>
</decoder>

<decoder name="sshd-success">
  <parent>sshd</parent>
  <prematch>^Accepted</prematch>
  <regex offset="after_prematch">^ \S+ for (\S+) from (\S+) port </regex>
  <order>user, srcip</order>
  <fts>name, user, location</fts>
</decoder>

<decoder name="ssh-denied">
  <parent>sshd</parent>
  <prematch>^User \S+ from </prematch>
  <regex offset="after_parent">^User (\S+) from (\S+) </regex>
  <order>user, srcip</order>
</decoder>

You can see that the decoder.xml file is used to parse through the log file using regular expression pattern matching. This means that you can add additional files to the list of those which OSSEC is checking if you would like. You’ll also note that the XML rules in decoder.xml are nested so that you can use the <parent> tag to nest rules. A rule with a “parent” will only attempt matching if the parent rule matched successfully. Using the order and its statements you can populate OSSEC’s predefined variables with portions of the log file. The following variables are supported:

  • location
  • hostname
  • log_tag
  • srcip
  • dstip
  • srcport
  • dstport
  • protocol
  • action
  • user
  • dstuser
  • id
  • command
  • url
  • data

Supposing you have a log file produced by an application that isn’t covered by the default decoders you could write your own decoder and parsing rules. Unfortunately OSSEC only supports logs in the formats syslog, snort-full, snort-fast, squid, iis, eventlog, mysql_log, postgresql_log, nmapg or apache. Therefore any custom logging you write must conform to one of these formats. Syslog is probably the easiest to use as it is designed to handle any one line log entry.

Let us suppose we have a custom PHP based application that resides in /var/www/html/custom. Our application will write Apache format logs to a file called ‘alert.log’ in the ‘logs/’ application subdirectory. This program has the following lines in example.php:

<?php

$id = $_GET['id'];
$logfile = 'logs/alert.log';
if (! is_numeric($_GET['id'])) {
	$timestamp = date("Y-m-d H:m:s ");
	$ip = $_SERVER['REMOTE_ADDR'];
	$log = fopen($logfile, 'a');
	$message = $timestamp . $ip . ' PHP app Attempt at non-numeric input (possible attack) detected!' . "\n";
	fwrite($log, $message);
}

?>

This would write a log file to /var/www/html/custom/logs/alert.log in the format:

2009-10-13 11:10:36 192.168.97.1 PHP app Attempt at non-numeric input (possible attack) detected!

Once we have this application log set up we need to adjust our OSSEC configuration so that it reads the new log file. The following change needs to be done in both agent and server’s ossec.conf file. We can add the following lines to our /var/ossec/etc/ossec.conf file to enable OSSEC to read this new log file:

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/www/html/custom/logs/alert.log</location>
  </localfile>

Once OSSEC is monitoring this file (this will require us to restart OSSEC) we’ll need an appropriate decoder. Make this change on ossec server. Add the following in /var/ossec/etc/local_decoder.xml. By default ossec reads only 2 decoder files: decoder.xml and local_decoder.xml. decoder.xml can be overwritten during upgrades, so add all the custom decoders in local_decoder.xml. Writing a decoder for this format would be quite simple. It would appear as:

<!-- Custom decoder for example -->
<decoder name="php-app">
  <prematch>^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d</prematch>
</decoder>

<decoder name="php-app-alert">
  <parent>php-app</parent>
  <regex offset="after_parent">^ (\d+.\d+.\d+.\d+) PHP app</regex>
  <order>srcip</order>
</decoder>

What we’re doing here is telling OSSEC how to extract IP information from the log. All the strings in the regex portion of the new decoder can be assigned, in order, to options listed in the order tag. You can define each of OSSEC’s possible variables and tell OSSEC how to identify them in the logs using the decoder.

Once we have our decoder we can write custom rules based on the log file. This is to be done on ossec server. There are two ways to create custom rules for OSSEC. The first is to alter the ossec.conf configuration file and add a new rule file to the list. The second is to simply append your rules to the local-rules.xml rules file. Either one works, but the second makes upgrading to newer versions of OSSEC a little cleaner.

We’ll add the following group to our local-rules.xml file, found in the rules directory under the OSSEC installation root:

<group name="syslog,php-app,">
  <rule id="110000" level="0">
    <decoded_as>php-app</decoded_as>
    <description>PHP custom app group.</description>
  </rule>

  <rule id="110001" level="10">
    <if_sid>110000</if_sid>
    <srcip>127.0.0.1</srcip>
    <match>attack</match>
    <description>Possible attack from localhost?!?</description>
  </rule>
</group>

You’ll notice that we have two rules. Because rules can be nested it is usually helpful to subdivide them into small, hierarchical pieces. In this case, we have one rule that serves as a catch-all for our custom application alerts. After that, we can write rules for any number of circumstances and have these rules only checked if the parent rule is matched. This rule will fire if an entry is written into the custom alert.log that contains the source IP of 127.0.0.1. The rule id is extremely important in this definition. OSSEC reserves rule id’s above 100,000 for custom rules. It is useful to develop a schema for your new rules, for instance allocating each 1000 above 100,000 for a generic, catch-all rule and writing child rules in that space. This helps to avoid the hassle of having intermingled rule numbers and aids in long term maintenance.

To clarify the case above, there are two rules. The first rule will only fire if a log entry is “decoded_as” or matches the decoder for “php-app.” If this decoder is used then rule 110,000 will be triggered. The second rule is only checked if rule 110,000 is triggered as specified in the if_sid tag. This rule will only be triggered if the source ip, specified in the srcip tag, is equal to ‘127.0.0.1.’ If this is the case then the rule will do a string match for the word “attack” in the log entry. If this match is successful the rule will trigger at level 10 as specified in the rule tag. This will cause an OSSEC alert to be logged with the associated description. OSSEC by default also attempts to e-mail alerts with level 7 or higher to recipients specified in the ossec.conf file. As you can see, with the addition of the decoder and these rules we’ve allowed OSSEC to read our custom format logfile.

While this example may seem straightforward writing your own decoders and rules can be maddening. Because OSSEC will not dynamically load the XML files defining your decoders, rules, or files to watch, you must restart the program to propagate changes. This can be a real hassle when you’re debugging new XML rules or decoders. To alleviate the problem of constantly restarting the server you can use the program ossec-logtest found in the bin directory of the OSSEC installation root. This is present on the ossec server. This program allows you to paste, or type, one line of a log file into the input then traces the decoders and rules that the line matches like so:

# bin/ossec-logtest 
2009/10/13 13:30:25 ossec-testrule: INFO: Started (pid: 14330).
ossec-testrule: Type one log per line.

2009-10-13 12:10:09 127.0.0.1 PHP app Attempt to attack the host!


**Phase 1: Completed pre-decoding.
       full event: '2009-10-13 12:10:09 127.0.0.1 PHP app Attempt to attack the host!'
       hostname: 'webdev'
       program_name: '(null)'
       log: '2009-10-13 12:10:09 127.0.0.1 PHP app Attempt to attack the host!'

**Phase 2: Completed decoding.
       decoder: 'php-app'
       srcip: '127.0.0.1'

**Phase 3: Completed filtering (rules).
       Rule id: '110001'
       Level: '10'
       Description: 'Possible attack from localhost?!?'
**Alert to be generated.

Note that this program will not reload changes, but you can quit ossec-logtest, make changes to any of the XML files then restart it to test your changes. Using ossec-logtest is invaluable when trying to create new rules as it saves you the hassle of restarting the server and the hassle of actually triggering events for which you want to generate alerts.

In case you get an issue like “No decoders match” in ossec-logtest, please check your decoder file. It might be most probably due to some syntax error in your decoder xml.

After your testing in logtest is successful, restart your ossec agent and master with

/var/ossec/bin/ossec-control restart

After this your alerting system will be in place for your custom logs just like other alerts.

OSSEC: Writing your own rules

Before we start writing rules, we should be aware of some rules to be followed:

  • The first rule of writing custom rules is to never modify the existing rule files in the /var/ossec/rules directory except local_rules.xml. Changes to those rules may modify the behavior of entire chains of rules and complicate troubleshooting.
  • The second rule of writing custom rules is to use IDs above 100000 as IDs below it are reserved. Interfering with those IDs is the same as tampering with the distributed rules files themselves. You risk an update of OSSEC clobbering all your hard work.
  • The third rule is to maintain order in your rules. As the rules parser is loading the rules at startup, it validates the existence of referenced rules and groups. If you reference a rule that hasn’t been loaded, the parser will fail.

Minding these three rules helps to ensure that our installations won’t break with upgrades and that we can always get back to a “stock” OSSEC by removing the local_rules.xml file from our configuration.

Every rule must have an ID, a level, a description, and a match condition. The IDs must be unique, and our rules must have an ID over 100000. It’s important to note that re-using or reordering rule IDs can cause confusion or inaccuracy in historic data.

Rules in OSSEC have a level from 0 to 15. The higher the level, more certain the analyzer is of an attack. Level 0 is a special level to tell OSSEC to ignore the alerts where no log will be generated and OSSEC will discard the alert and data silently. By default, OSSEC considers anything at or exceeding level 7 to be e-mail worthy, but it is also configurable.

Rules also require a description field to explain what the rule does. This description will be used as the event identifier in the e-mails and log messages that OSSEC generates. As it will be a part of the reporting, it’s best to explain the rule professionally and format it consistently. Descriptions like “alerting for this thing” won’t be helpful to your colleagues, whereas “Ignore failed login attempts from vulnerability scanners between 4:00 and 7:00” will be clear and informative.

Now that we’re well versed with the protocols of the rules, let’s process some data from our custom application logging via syslog as follows:

May 4 19:12:03 server custom-app: Startup initiated.
May 4 19:12:07 server custom-app: No error detected during startup!
May 4 19:12:08 server custom-app: Startup completed, processing data.
May 4 19:12:08 server custom-app: Failed login from '4.5.6.7' as testuser

We’re receiving an alert about unknown errors and authentication failures from our custom application. We would prefer to silence these unknown error messages and ensure that we don’t provide alerts for failed logins from 4.5.6.7, our vulnerability scanner.

How to do it…

In order to figure out the first step, we need to understand what’s happening to generate the alerts:

  1. Use the ossec-logtest tool provided by OSSEC. It works by accepting log messages on STDIN (your terminal input) and explaining the path through the rules. Here’s how we can run it:
    $ sudo /var/ossec/bin/ossec-logtest
    
  2. Then we can paste in log lines to see which ones are generating alerts:
    ossec-testrule: Type one log per line.
    
    May  4 19:12:03 server custom-app: Startup initiated.
    
    **Phase 1: Completed pre-decoding.
         full event: 'May  4 19:12:03 server custom-app: Startup initiated.'
         hostname: 'server'
         program_name: 'custom-app'
         log: 'Startup initiated.'
    
    **Phase 2: Completed decoding.
         No decoder matched.
    
  3. The first log message completed the parsing of the line and no alert was generated. So we try the next log message:
    **Phase 1: Completed pre-decoding.
         full event: 'May  4 19:12:07 server custom-app: No error detected during startup!'
         hostname: 'server'
         program_name: 'custom-app'
         log: 'No error detected during startup!'
    
    **Phase 2: Completed decoding.
         No decoder matched.
    
    **Phase 3: Completed filtering (rules).
         Rule id: '1002'
         Level: '2'
         Description: 'Unknown problem somewhere in the system.'
    **Alert to be generated.
    
  4. We can see from this output that our unknown problem is being generated by this log line. We get the rule ID and the level being generated and using this information, we can write a rule to ignore it using OSSEC’s level="0".
    <!-- Local Rules for Example.com -->
    <group name="local,syslog,">
       <rule id="100000" level="0">
         <if_sid>1002</if_sid>
         <program_name>custom-app</program_name>
         <description>Ignore errors for custom-app</description>
       </rule>
    </group>
  5. Once we’ve saved the local_rules.xml file, we can restart ossec-logtest and try the event again:
    **Phase 3: Completed filtering (rules).
         Rule id: '100000'
         Level: '0'
         Description: 'Ignore unknown errors for custom-app'
    
  6. Now that we’ve moved this event to level 0, we can look at the failed login events:
    May  4 19:12:08 server custom-app: Failed login from '4.5.6.7' as testuser'
    **Phase 3: Completed filtering (rules).
         Rule id: '2501'
         Level: '5'
         Description: 'User authentication failure.'
    **Alert to be generated.
    
  7. We’ll use a simple match with this data to silence this alert from 4.5.6.7:
    <rule id="100001" level="0">
      <if_sid>2501</if_sid>
      <program_name>custom-app</program_name>
      <match>4.5.6.7</match>
      <description>Ignore failed logins from scanner</description>
    </rule>
  8. And now re-running ossec-logtest, we see:
    May  4 19:12:08 server custom-app: Failed login from '4.5.6.7' as testuser'
    
    **Phase 3: Completed filtering (rules).
         Rule id: '100001'
         Level: '1'
         Description: 'Ignore failed logins from our security scanner'
    **Alert to be generated.
    

Using these two rules, we’ve been able to silence the noisiest log entries in our sample environment.

How it works…

OSSEC rules are processed sequentially. Each rule has a number of conditions and a logical AND is applied to the conditions. The more specific we make the rule, the more accurate it will be. In our example, we filtered events using the program_name attribute for the string, custom-app.

Each rule also specifies an if_sid element, which requires the log message to be flagged with the rule ID we specify. The if_sid parameter can take a comma-separated list of rule IDs, where the rule will match if any of those IDs are matched. Consider that multiple instances of the same element appear in a rule; refer to the following example:

<match>illegal user</match>
<match>unknown user</match>
<match>invalid user</match>

That grouping is surrounded in logical OR. It’s important to note that after our rules match, the ID is changed and other rules looking for those lines with the same if_sid parameter, which was originally set, will fail to match. When a new rule matches, it replaces the attributes of the alert with its own values, replacing the ID and level.

While our first rule stopped at eliminating the match based on the rule ID and program name only, the second rule used the match attribute to find a string in the log message itself. In addition to match, there is also a regex attribute to allow more flexible matching of strings.

WARN: Waiting for server reply (not started) OSSEC agent

The first thing to understand is how to check the status of your agents and easiest way to do that is running the following on the server install (my mothership):

# /var/ossec/bin/agent_control -lc

This will list out all your agents and if they are active it’ll read Active. If they are inactive, they don’t read inactive unfortunately, they just don’t show up.

The next thing is to check your logs and in the default installations this is where it’ll be:

# tail -F /var/ossec/logs/ossec.log

If you have a connection issue you’re likely to see something like the following in the client log:

2012/10/09 03:39:33 ossec-agentd(4101): WARN: Waiting for server reply (not started). Tried: '[mothership IP]'.
2012/10/09 03:39:35 ossec-agentd: INFO: Trying to connect to server ([mothership IP]:1514).
2012/10/09 03:39:35 ossec-agentd: INFO: Using IPv4 for: [mothership IP] .
2012/10/09 03:39:56 ossec-agentd(4101): WARN: Waiting for server reply (not started). Tried: '[mothership IP]'.
2012/10/09 03:40:16 ossec-agentd: INFO: Trying to connect to server ([mothership IP]:1514).
2012/10/09 03:40:16 ossec-agentd: INFO: Using IPv4 for: [mothership IP].

As you are probably thinking this isn’t exactly the most helpful of warnings, it’s not telling you anything about the issue. But you do know you can’t connect. A couple of things I can say that will help troubleshoot on the client box is to do the following:

First check your IPTABLES rules:

# iptables -nL

If you have a number of rules and policies you might want to try disabling everything to see if you can establish a connection. To verify that its reaching the mothership server though you’ll want to run tcpdump on the mothership and see if any packets are reaching the box. Easiest way is to do the following:

# tcpdump -i eth0 port 1514
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes

Note that eth0 is your network interface card. If on a NIX box you can run ifconfig and you’re looking for the card that has your internet protocol address next to the inet addr:. So it’d look like this:

# ifconfig
eth0      Link encap:Ethernet  HWaddr G3:4P:91:CD:5A:6B  
          inet addr:100.1.5.68  Bcast:100.1.5.255  Mask:255.255.255.0

Once you identify the interface that is what you define in the syntax. And port is the UDP port that is used to communicate, if you didn’t change it on setup then it’ll be 1514. If it’s running you’ll start seeing traffic coming into the box as the servers kick it into gear. The easiest way to get it talking is to restart the agent boxes and you can do so here:

# /var/ossec/bin/ossec-control restart

If you have cleared your firewall and you don’t see traffic take a look at the ossec.log file on the mothership to see what might be going on. If you see the following you’re in luck:

# tail -F /var/ossec/logs/ossec.log
2012/10/09 03:47:17 ossec-remoted: WARN: Duplicate error:  global: 0, local: 51, saved global: 5, saved local:7563
2012/10/09 03:47:17 ossec-remoted(1407): ERROR: Duplicated counter for 'Agent001'.
2012/10/09 03:47:23 ossec-remoted: WARN: Duplicate error:  global: 0, local: 52, saved global: 5, saved local:7563
2012/10/09 03:47:23 ossec-remoted(1407): ERROR: Duplicated counter for 'Agent001'.
2012/10/09 03:47:27 ossec-remoted: WARN: Duplicate error:  global: 0, local: 53, saved global: 5, saved local:7563
2012/10/09 03:47:27 ossec-remoted(1407): ERROR: Duplicated counter for 'Agent001'.
2012/10/09 03:47:32 ossec-remoted: WARN: Duplicate error:  global: 0, local: 54, saved global: 5, saved local:7563
2012/10/09 03:47:32 ossec-remoted(1407): ERROR: Duplicated counter for 'Agent001'.
2012/10/09 03:47:38 ossec-remoted: WARN: Duplicate error:  global: 0, local: 55, saved global: 5, saved local:7563
2012/10/09 03:47:38 ossec-remoted(1407): ERROR: Duplicated counter for 'Agent001'.

If it still doesn’t work try removing the agent from master and re-adding it:

OSSEC start problem due to keys

OSSEC start problem due to keys

The following is an error I got after starting ossec as an agent that is supposed to connect back to a central server:

#/var/ossec/bin/ossec-control start
Starting OSSEC HIDS v2.5.1 (by Trend Micro Inc.)…
Started ossec-execd…
ossec-agentd(1402): ERROR: Authentication key file ‘/var/ossec/etc/client.keys’ not found.
ossec-agentd(1750): ERROR: No remote connection configured. Exiting.
ossec-agentd(4109): ERROR: Unable to start without auth keys. Exiting.

If the /var/ossec/etc/client.keys file is already present on agent, comment out the line containing the key in it.

The server (the one that ossec connects to) needs to have a key created for this agent, you can do so via:
# /var/ossec/bin/manage_agents
Press ‘A’ for Adding agent, enter agent name, agent ip.
Return to main menu, press ‘E’ to export keys, enter the agent id from the list given. It will provide with a key.

On the client (the one that got the error message) you need to add the key via the same command.
/var/ossec/bin/manage_agents
Press ‘I’ to import key,
Paste the key we exported from the master.

Finally you can start ossec on the client:
# /var/ossec/bin/ossec-control start