Wednesday, October 15, 2014

How to install OSSEC and configuring (host based intrusion detection)

OSSEC is a full platform to monitor and control your systems. It mixes together all the aspects of HIDS (host-based intrusion detection), log monitoring and SIM/SIEM together in a simple, powerful and open source solution.

File Integrity checking
There is one thing in common to any attack to your networks and computers: they change your systems in some way. The goal of file integrity checking (or FIM – file integrity monitoring) is to detect these changes and alert you when they happen. It can be an attack, or a misuse by an employee or even a typo by an admin, any file, directory or registry change will be alerted to you.

Log Monitoring
Your operating system wants to speak to you, but do you know how to listen? Every operating system, application, and device on your network generate logs (events) to let you know what is happening. OSSEC collects, analyzes and correlates these logs to let you know if something wrong is going on (attack, misuse, errors, etc). Do you want to know when an application is installed on your client box? Or when someone changes a rule in your firewall? By monitoring your logs, OSSEC will let you know of that.

Rootkit detection
Criminals (also known as hackers) want to hide their actions, but using rootkit detection you can be notified when they (or trojans, viruses, etc) change your system in this way.

Active response
Take immediate and automatic responses when something happens. Why wait for hours when you can alert your admin and block an attack right way?

Working:
OSSEC is composed of multiple pieces. It has a central manager monitoring everything and receiving information from agents, syslog, databases and from agentless devices.

Manager
The manager is the central piece of the OSSEC deployment. It stores the file integrity checking databases, the logs, events and system auditing entries. All the rules, decoders and major configuration options are stored centrally in the manager, making easy to administer even a large number of agents.

Agents
The agent is a small program installed on the systems you desire to monitor. It will collect information on real time and forward to the manager for analysis and correlation. It has a very small memory and CPU footprint by default, not affecting with the system’s usage.
Agent security: It runs with a low privilege user (created during the installation) and inside a chroot jail isolated from the system. Most of the agent configuration is pushed from the manager, with just some of them are stored locally on each agent. In case these local options are changed, the manager will receive the information and will generate an alert.

Agentless
For systems that you can’t install an agent, OSSEC allows you to perform file integrity monitoring on them without the agent installed. It can be very useful to monitor firewalls, routers and even Unix systems where you are not allowed to install the agent.

Untitled



INSTALLATION:
Download ossec-hids-2.x.tar.gz from https://github.com/ossec/ossec-hids/archive/2.8.2.tar.gz
#tar xzvf ossec-hids-2.x.tar.gz
#cd ossec-hids-2.x.tar.gz
#./install.sh



1

For English type : en
What kind of installation do you want (server,agent,local.hybrid): server
Choose wghere to install the OSSEC-HIDS [/var/ossec]: /var/ossec
Do you want email notification: y
what’s your email address: sam@sam.com
what’s your SMTP server ip/add: mail.sam.com
Do you want to run the integrity check daemon: y
Do you want to run the rootkit detection engine: y
Do you want to enable active response: y
Do you want to enable remote syslog (port 514 udp): y
2


#/var/ossec/bin/ossec-control start
#cd /var/www/html
#wget http://www.ossec.net/files/ui/ossec-wui-0.3.tar.gz
#tar zxvf ossec-wui-*.tar.gz
#rm -f ossec-wui-*.tar.gz
#mv ossec-wui-* ossec-wui
#mkdir /var/www/html/ossec-wui/tmp
#chown -R apache:apache /var/www/html/ossec-wui
#add apache user to ossec group
#usermod -G ossec apache
#/var/ossec/bin/ossec-control start

5

Configure apache to run at startup and start it
#chkconfig httpd on
#service httpd start
That’s it. Ossec server installation completed.now I can can browse to http://localhost/ossec-wui. The default user and password are: ossec/ossec.


7

Installing ossec agent (client in linux system)
Download ossec-hids-2.7.tar.gz from http://www.ossec.net/main/downloads
#tar xzvf ossec-hids-2.7.tar.gz
#cd ossec-hids-2.7.tar.gz
#./install.sh

For English type : en
What kind of installation do you want (server,agent,local.hybrid): agent
Choose where to install the OSSEC-agent [/var/ossec]: /var/ossec
What’s the ip address of OSSEC-HIDS server: 192.168.31.1
Do you want to run the integrity check daemon: y
Do you want to run the rootkit detection engine: y
Do you want to enable active response: y
Do you want to enable remote syslog (port 514 udp): y

8

On the OSSEC server run manage_agents tool to add a new client
#/var/ossec/bin/manage_agents
Choose A to add an agent: A
provide a name for your new agent: arthar2
provide the IP of your new agent: 192.168.31.150
Provide an OSSEC ID for your new agent: 001
Confirm adding it?: y
Choose E to extract key for an agent: E
Provide the ID of the new agent: 001
Copy the agent key information
press ENTER to return to the main manu
Choose Q to quit

10
11

On the OSSEC client run manage_agent tool to configure the new client
 
#/var/ossec/bin/manage_client
Choose I to Import key from the server: I
Paster the the already copied agent ket: paste
confirm adding it: y
Press Enter to return to the main manu
Choose Q to Quit

12

Start OSSEC
#/var/ossec/bin/ossec-control start
That’s it. OSSEC client installation completed. Now i can browse to http://localhost/ossec and see messages from my new OSSEC client.

14


Installing ossec agent (client in windows system)
Download OSSEC windows agent from http://www.ossec.net/main/downloads

Run the downloaded exe file
A. Welcome to OSSEC HIDS Windows Agent v2.6 Setup Wizard – Click Next
B. License Agreement – Read the license agreement and if you agree click on I agree
C. Choose Components – If you are not running IIS, click to remove the mark on “Scan and monitor IIS 

logs” and click Next
D. Choose Install Location – Click Install
E. Completing the OSSEC HIDS Windows Agent v2.6 Setup Wizard – check that “Run OSSEC Agent 

Manager” is marked and click Finish
On the OSSEC server run manage_agents tool to add a new client
#/var/ossec/bin/manage_agents
Choose A to add an agent: A
provide a name for your new agent: arthar
provide the IP of your new agent: 192.168.31.2
Provide an OSSEC ID for your new agent: 002
Confirm adding it?: y
Choose E to extract key for an agent: E
Provide the ID of the new agent: 002
Copy the agent key information
press ENTER to return to the main manu
Choose Q -> Enter to quit

z zz

On the OSSEC windows client run manage_agent tool if it’s not already running and configure your new client installation
A. Start -> All Programs -> OSSEC -> Manage Agents
B. OSSEC Agent Manager – Fill my 192.168.31.1, and the “Authentication Key” that was copied from the server. Click save
C. Confirm Importing Key – Click OK
D. OSSEC Agent Manager – Click on Manage -> Start OSSEC
E. Close OSSEC Agent Manager
That’s it. OSSEC client installation completed. Now i can browse to http://localhost/ossec and see messages from my new OSSEC client.


zzz 


  zzzz

Wednesday, October 1, 2014

How to writing snort rules - Part III

Continuing with the posts about Snort Snort installation (part II), now we have a complete installation and web interface to monitor our network alerts. One of the most important things when you maintain an IDS like Snort in a network, is the include of new rules to alert of possible attacks, behaviors of Malware or simply the needed of control a part of our traffic for some reasons. The rules of Snort are very flexible and has a lot of possibilities of configuration, logically in this post I’ll do a short introduction to write basic rules explaining the components of a rule and some options that can be useful.

Anatomy of a rule
A Snort rule, basically is composed by the header (information about the traffic) and the options (contains some action to do on the packet).
  • Headers is composed by:
Action Protocol Source IP Source Port Direction Operator Destination IP Destination Port (Options)
Action: Refers what snort will do when a packet match with the rule. Possible actions:
– alert: Generates an alert and register the packet.
– log: register the packet.
– pass: ignore the packet.
– activate: make an alert and next run other dynamic rule.
– dynamic: Remain inactive until an activate rule active them. Then will run as log action.
Protocol: We can use TCP, UDP, ICMP and IP protocols.
We can use the next syntax to refer an IP address or Port number:
– “!” : exclude the IP address or Port number for the rule.
– “172.16.0.0/16″: refers all the device from this network.
– “any”: refers any IP address or Port number.
– “:” : Refers a range of ports number.
– variables: We can use the variables declared in snort.conf like HOME_NET and EXTERNAL_NET.
Direction Operator: indicates the orientation of the packet for the apply of the rule. The possible operators:
– “->”: Indicates the source IP and port before the operator and after the destination.
– “<>”: Used to refer bidirectional traffic, Snort consider the pair IP and port numbers as source or destination.

Some examples of header rules
alert tcp any any -> 172.16.0.0/16 1:1023 : This rule will match with the attempts for tcp connection from any source to all the hosts in the network 172.16.0.0 to a port range from 1 to 1023.
log udp 10.0.0.0/8 :1024 -> 192.168.1.0/24 100: : Log all the UDP traffic from the network 10.0.0.0 with source port less than 1024 and destination for the network 192.168.1.0 with destination port greater than 100.
log tcp 192.168.1.0/24 any <> 192.168.1.1 22 : Log all the SSH requests from the network 192.168.1.0 to the host 192.168.1.1 and the SSH request from this host.
  • Options: Contains the messages and information necessary for the decision of the alert. The different options are separated with the caracter “;”. Exists 4 main categories of options:
Meta-data: Provides some information about the rule. Posible metadata options can be:
– msg: Indicates a string message to print when the rule is matched.
– sid: identification for a single snort rule.
– rev: Used with the option id for reference a revision number for a rule.
– classtype: Defines the classification of a rule and is defined in the file configuration classification.config and is classified by a priority.
Payload: Refers to the “useful” data from the interior of a packet, usually is known the body of the data excluding the data overhead like the packet headers. Some possible options:
– content: This option allows a rule to search specific content in useful data of a packet. The pattern to search can be in text format or in binary mode written between the character pipe “|”, representing binary numbers like hexadecimal format.
– rawbytes: Allows a rule search in the packet data without any decodification from the preprocessor.
– depth: indicates the number of bytes that Snort will search on the payload.
Non-payload: Looks data in other parts of the packet different of the body data. Some options:
– dsize: Indicates the payload size in bytes.
– id: Is used to revise the ID field on the packet header. Some exploits or scanners specify this field.
– flags: Used to check if specific TCP flag bit is enabled, some useful flags are S (SYN), A (ACK), F (Finish).
Post-detection: This option is a trigger for a rule when this is activate.
Some Examples of Snort Rules
  • Detecting when root user is trying to send an email:

alert tcp any any ->; 192.168.1.0/24 25 (sid:1002345;rev:2;msg: "root users attempts to send an email"; content: "mail from: root";classtype:suspicious-login;)

  • Identifying the source of icmp traffic of a windows host:
– First of all I had to capture a icmp packet with tcpdump to see what’s the signature for a ping packet of a Windows host, and this is the capture that I received of data in Hexadecimal format:





alert icmp any any ->; 192.168.1.0/24 any (sid:1002356;msg:"Hey!! A windows Host is pinging me!";itype:8;content:"|6566 6768 696a 6b6c 6d6e 6f70 7172 7374 7576 7761 6263 6465 6667 6869|";nocase;depth:32;classtype:icmp-event;)

  • Registering all access to the url /admin on the web server 192.168.1.250:

alert tcp any any ->; 192.168.1.250 80 (sid:1002354;rev:2;msg:"Warning!!, A host is trying to access /admin"; uricontent:"/admin";classtype:web-application-activity;)

  • Alert all SMTP traffic that contains a file virus.exe attached:

alert tcp any any ->; 192.168.1.0/24 25 (sid:1002311; rev:3; msg:"Warning!! The virus.exe is included in one mail!!"; content:"filename="virus.exe"";classtype:suspicious-filename-detect;)
 


 

To add our rules in needed to include the new file in snort.conf file and restart snortd:


# vi /etc/snort/snort.conf
include $RULE_PATH/test.rule

# /etc/init.d/snortd restart

How to install Snort with BASE & barnyard2 - Part II

Continuing with the last post Snort installation part I now I’ll explain how to install BASE and barnyard2. BASE (Basic Analysis and Security Engine) provides a web front-end to query and analyze the alerts coming from Snort. The alerts will send to a MySQL database, this feature is provided by barnyard2. Barnyard2 is an output system for Snort, it reads the binary logs from snort using the unified2 format and then it will resend the information of this logs to a database backend, for this We’ll configure Snort to output alerts to this format.

Install BASE dependencies





# yum install -y mysql-server mysql-devel php-mysql php-adodb php-pear php-gd httpd
# pear channel-update pear.php.net
# pear install Numbers_Roman

Preparing MySQL environment
– Initializing mysql and configuring to start the daemon at boot time:


# service mysql start
# chkconfig --levels 235 mysql on
– Preparing the new database for snort:

# mysql -u root -p



<pre>mysql> create database snort;
mysql> grant select,insert,update,delete,create on snort.* to snort@localhost;
mysql> set password for snort@localhost=PASSWORD('snortpassword');

Setup snort to log out in unified2 format

# vi /etc/snort/snort.conf

output unified2: filename snort.u2, limit 128
Installing barnyard2










# tar -xzvf barnyard2-1.9.tar.gz
# cd barnyard2-1.9
# ./configure --with-mysql
# make && make install
# cp etc/barnyard2.conf /etc/snort/
# mysql -u snort -psnortpassword snort < schemas/create_mysql
# touch /etc/snort/barnyard2.waldo
# chmod 777 /etc/snort/barnyard2.waldo
# chown snort:snort /etc/snort/barnyard2.waldo
– Edit barnyard2 configuration:

# vi /etc/snort/barnyard2.conf







config reference_file: /etc/snort/reference.config
config classification_file: /etc/snort/classification.config
config gen_file: /etc/snort/rules/gen-msg.map
config sid_file: /etc/snort/rules/sid-msg.map
input unified2
config hostname: localhost
config interface: eth0
config alert_with_interface_name
output database: log, mysql, user=snort password=snortpassword dbname=snort host=localhost
Adapting our init script to work with barnyard2

# vi /etc/init.d/snortd

BARNYARD2=/usr/local/bin/barnyard2
start()
{
[ -x $SNORTD ] || exit 5
echo -n $"Starting $prog: "
daemon --pidfile=$PID_FILE $SNORTD $LINK_LAYER $NO_PACKET_LOG $DUMP_APP -D $PRINT_INTERFACE $INTERFACE -u $USER -g $GROUP $CONF -l $LOGDIR $PASS_FIRST $BPFFILE $BPF && success || failure
RETVAL=$?
$BARNYARD2 -c /etc/snort/barnyard2.conf -d /var/log/snort -f snort.u2 -w /etc/snort/barnyard2.waldo -u snort -g snort -D
[ $RETVAL -eq 0 ] && touch $lockfile
echo
return $RETVAL
}
stop()
{
echo -n $"Stopping $prog: "
killproc $SNORTD
killproc $BARNYARD2
if [ -e $PID_FILE ]; then
chown -R $USER:$GROUP /var/run/snort_eth0.* && rm -f /var/run/snort_eth0.pi*
fi
RETVAL=$?
if [ "x$runlevel" = x0 -o "x$runlevel" = x6 ] ; then
trap TERM
killall $prog 2>/dev/null
trap TERM
fi
[ $RETVAL -eq 0 ] && rm -f $lockfile
echo
return $RETVAL
}
– Restart Snortd:

# /etc/init.d/snortd restart

Installing BASE

# tar -xzvf base-1.4.5.tar.gz
# cp -r base-1.4.5/ /var/www/base
# cd /var/www/base/
# cp base_conf.php.dist base_conf.php
– Edit BASE scripts configuration:

# vi base_conf.php

$BASE_urlpath = '/base';
$DBlib_path = '/usr/share/php/adodb';
$alert_dbname = 'snort';
$alert_host = 'localhost';
$alert_port = '3306';
$alert_user = 'snort';
$alert_password = 'snortpassword';

Configuring Apache

# vi /etc/httpd/conf.d/base.conf

Alias /base /var/www/base/
<directory "/var/www/base/">
AllowOverride None
Order allow,deny
Allow from all
AuthName "Snort IDS"
AuthType Basic
AuthUserFile /etc/snort/base.passwd
Require valid-user
</directory>
– Generating password file for web access for BASE:

# htpasswd -c /etc/snort/base.passwd snortadmin
– Restart apache:

# service httpd restart

Accessing to the BASE web environment
http://IP-WEB-SERVER/base/base_db_setup.php
and click create BASE AV


How to install Snort - Part I


An IDS is a security tool, that allow us to monitor our network events searching attempts to compromise the security of our systems. It’s possible matching predefinied rules emulating the behaviour of an attack and it’s possible to deny the package or simply alert us to an email or sending messages to log. Basically we can find two types of IDS:
  • HIDS: Host based IDS, monitors the activity of a single machine, searching anomaling behaviors.
  • NID: Network IDS, capture and analyze network packages to search attack patterns.
Generally an IDS can be located in each network segment, for example front of the firewall or back of the firewall or also can be implemented in the same firewall if we have a small network traffic, with this way we can analyze all input and output traffic.

SNORT
Snort is a NIDS, implements real time scanning of attack detection and port scanning detecting. The basic architecture of snort:
  • Packet capture module: Used to capture network traffic using libpcap library.
  • Decoder: It ensures to form the data structures of the packages captured and identify the network protocol.
  • Preprocessor: prepocessors are plugins developed generally in C and process the packets provided by the decoder and ensambles the packets received. This preprocessors are configured in snort.conf file configuration. Some preprocessor examples may be:
    – sfPortscan
    – Frag3
    – HTTP
    – SSH
    – To see a complete list visit: http://manual.snort.org/node17.html
  •  Detection engine: Analyze the packets based in our rules configued.
  •  Detection plugins: Used to modify the behaviour of the detection engine.
  • Output plugins: Defines how and where saves the alters and the packages generated.


For this post I’ll explain how to install and configure snort from the source code in CentOS 6 and download free ruleset for snort and configure for be used.

Installing dependencies and preparing the environment
– Installing rpmforge repository:
– Install prerequisites packages:

# yum -y install libdnet libdnet-devel libpcap libpcap-devel daq gcc make flex bison pcre pcre-devel zlib zlib-devel
– Downloading and installing daq:






# cd /tmp ; wget http://www.snort.org/downloads/1850 -O daq-1.1.1.tar.gz
# tar -xzvf daq-1.1.1.tar.gz
# cd daq-1.1.1/
# ./configure
# make && make install
# ldconfig -v
– Creating snort user and tree directories:








# groupadd snort
# useradd -g snort snort
# mkdir /usr/local/snort
# mkdir /etc/snort
# mkdir /var/log/snort
# mkdir /var/run/snort
# chown snort:snort /var/log/snort
# chown snort:snort /var/run/snort
Installing Snort and configuring the ruleset
– Downloading and installing snort:











# cd /tmp ; wget http://www.snort.org/downloads/1862 -O snort-2.9.3.1.tar.gz
# tar -xzvf snort-2.9.3.1.tar.gz
# cd snort-2.9.3.1/
# ./configure --prefix /usr/local/snort --enable-sourcefire --enable-ipv6
# make && make install
# ln -s /usr/local/snort/bin/snort /usr/bin/snort
# cp /tmp/snort-2.9.3.1/etc/snort.conf /etc/snort/
# cp /tmp/snort-2.9.3.1/etc/unicode.map /etc/snort/
# cp /tmp/snort-2.9.3.1/etc/classification.config /etc/snort/
# cp -r /usr/local/snort/lib/snort_dynamicpreprocessor/ /usr/local/lib/
# cp -r /usr/local/snort/lib/snort_dynamicengine /usr/local/lib/
– Downloading open source ruleset from emerging:




# tar -xzvf emerging.rules.tar.gz
# touch /etc/snort/rules/white_list.rules /etc/snort/rules/black_list.rules
# chown -R snort:snort /etc/snort/
– Edit snort configuration:

# vi /etc/snort/snort.conf







ipvar HOME_NET 192.168.1.0/24
var RULE_PATH /etc/snort/rules
var SO_RULE_PATH /etc/snort/so_rules
var PREPROC_RULE_PATH /etc/snort/preproc_rules
var WHITE_LIST_PATH /etc/snort/rules
var BLACK_LIST_PATH /etc/snort/rules
include $RULE_PATH/emerging.conf
Configuring the init script for Snort
– Create sysconfig snort configuration:

# vi /etc/sysconfig/snort














#### General Configuration
INTERFACE=eth0
CONF=/etc/snort/snort.conf
USER=snort
GROUP=snort
PASS_FIRST=0
#### Logging & Alerting
LOGDIR=/var/log/snort
ALERTMODE=fast
DUMP_APP=1
BINARY_LOG=1
NO_PACKET_LOG=0
PRINT_INTERFACE=0
– Adding the init script:

# vi /etc/init.d/snortd

#!/bin/bash
#
# snort Start up the Snort Intrusion Detection System daemon #
# chkconfig: 2345 55 25
# description: Snort is a Open Source Intrusion Detection System
# This service starts up the snort daemon. #
# processname: snort
# pidfile: /var/run/snort_eth0.pid
### BEGIN INIT INFO
# Provides: snort
# Required-Start: $local_fs $network $syslog
# Required-Stop: $local_fs $syslog
# Should-Start: $syslog
# Should-Stop: $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start up the Snort Intrusion Detection System daemon
# Description: Snort is an application for Open Source Intrusion Detection.
# This service starts up the Snort IDS daemon.
### END INIT INFO
# source function library
. /etc/rc.d/init.d/functions
# pull in sysconfig settings
[ -f /etc/sysconfig/snort ] && . /etc/sysconfig/snort
RETVAL=0
prog="snort"
lockfile=/var/lock/subsys/$prog
# Some functions to make the below more readable
SNORTD=/usr/bin/snort
#OPTIONS="-A fast -b -d -D -i eth0 -u snort -g snort -c /etc/snort/snort.conf -l /var/log/snort"
#PID_FILE=/var/run/snort_eth0.pid
# Convert the /etc/sysconfig/snort settings to something snort can
# use on the startup line.
if [ "$ALERTMODE"X = "X" ]; then
ALERTMODE=""
else
ALERTMODE="-A $ALERTMODE"
fi
if [ "$USER"X = "X" ]; then
USER="snort"
fi
if [ "$GROUP"X = "X" ]; then
GROUP="snort"
fi
if [ "$BINARY_LOG"X = "1X" ]; then
BINARY_LOG="-b"
else
BINARY_LOG=""
fi
if [ "$LINK_LAYER"X = "1X" ]; then
LINK_LAYER="-e"
else
LINK_LAYER=""
fi
if [ "$CONF"X = "X" ]; then
CONF="-c /etc/snort/snort.conf"
else
CONF="-c $CONF"
fi
if [ "$INTERFACE"X = "X" ]; then
INTERFACE="-i eth0"
PID_FILE="/var/run/snort_eth0.pid"
else
PID_FILE="/var/run/snort_$INTERFACE.pid"
INTERFACE="-i $INTERFACE"
fi
if [ "$DUMP_APP"X = "1X" ]; then
DUMP_APP="-d"
else
DUMP_APP=""
fi
if [ "$NO_PACKET_LOG"X = "1X" ]; then
NO_PACKET_LOG="-N"
else
NO_PACKET_LOG=""
fi
if [ "$PRINT_INTERFACE"X = "1X" ]; then
PRINT_INTERFACE="-I"
else
PRINT_INTERFACE=""
fi
if [ "$PASS_FIRST"X = "1X" ]; then
PASS_FIRST="-o"
else
PASS_FIRST=""
fi
if [ "$LOGDIR"X = "X" ]; then
LOGDIR=/var/log/snort
fi
# These are used by the 'stats' option
if [ "$SYSLOG"X = "X" ]; then
SYSLOG=/var/log/messages
fi
if [ "$SECS"X = "X" ]; then
SECS=5
fi
if [ ! "$BPFFILE"X = "X" ]; then
BPFFILE="-F $BPFFILE"
fi
runlevel=$(set -- $(runlevel); eval "echo $$#" )
start()
{
[ -x $SNORTD ] || exit 5
echo -n $"Starting $prog: "
daemon --pidfile=$PID_FILE $SNORTD $ALERTMODE $BINARY_LOG $LINK_LAYER $NO_PACKET_LOG $DUMP_APP -D $PRINT_INTERFACE $INTERFACE -u $USER -g $GROUP $CONF -l $LOGDIR $PASS_FIRST $BPFFILE $BPF && success || failure
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $lockfile
echo
return $RETVAL
}
stop()
{
echo -n $"Stopping $prog: "
killproc $SNORTD
if [ -e $PID_FILE ]; then
chown -R $USER:$GROUP /var/run/snort_eth0.* && rm -f /var/run/snort_eth0.pi*
fi
RETVAL=$?
# if we are in halt or reboot runlevel kill all running sessions
# so the TCP connections are closed cleanly
if [ "x$runlevel" = x0 -o "x$runlevel" = x6 ] ; then
trap TERM
killall $prog 2> /dev/null
trap TERM
fi
[ $RETVAL -eq 0 ] && rm -f $lockfile
echo
return $RETVAL
}
restart() {
stop
start
}
rh_status() {
status -p $PID_FILE $SNORTD
}
rh_status_q() {
rh_status > /dev/null 2>&;1
}
case "$1" in
start)
rh_status_q && exit 0
start
;;
stop)
if ! rh_status_q; then
rm -f $lockfile
exit 0
fi
stop
;;
restart)
restart
;;
status)
rh_status
RETVAL=$?
if [ $RETVAL -eq 3 -a -f $lockfile ] ; then
RETVAL=2
fi
;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
RETVAL=2
esac
exit $RETVAL
– Start snort at system boot time:


# chmod +x /etc/init.d/snortd
# chkconfig --levels 235 snortd on
– Starting snort:

# /etc/init.d/snortd start
Testing the basic functionality of port scanning detection with nmap

# tail -f /var/log/snort/alert

Wednesday, September 24, 2014

Tips for creating better bash scripts

For today's article I wanted to cover a few tips that will make your scripts better, not better for you but better for the next person who has to figure out why it isn't working anymore.

Always start with a shebang

The first rule of shell scripting is that you always start your scripts with a shebang. While the name might sound funny the shebang line is very important, this line tells the system which binary to use as the interpreter for the script. Without the shebang line, the system doesn't know what language to use to process the script script.
A typical bash shebang line would look like the following:
#!/bin/bash
I have seen many scripts were this line is missing, one would think if it wasn't there than the script wouldn't work but that's not necessarily true. If a script does not have an interpreter specified then some systems will default to /bin/sh. While defaulting to /bin/sh would be ok if the script is written for bourne shell, if the script is written for KSH or uses something specific to bash and not bourne than the script may produce unexpected results.
Unlike some of the other tips in this article this one is not just a tip but rather a rule. You must always start your shell scripts with the interpreter line; without it your scripts will eventually fail.

Put a description of the script in the header

Whenever I create a script or any program for that matter I always try to put a description of what the script will do in the beginning of the script. I also include my name and if I am writing these scripts for work, I will include my work email address as well as a date that the script was written.
Here is an example header.
#!/bin/bash
#### Description: Adds users based on provided CSV file 
#### CSV file must use : as separator
#### uid:username:comment:group:addgroups:/home/dir:/usr/shell:passwdage:password
#### Written by: Benjamin Cane - ben@example.com on 03-2012
Why do I put all of this? Well it's simple. The description is there to explain to anyone who is reading through the script what this script does and any information they need to know about it. I include my name and email on the chance that if someone was reading this script and they had a question for me they could reach out and ask. I include the date so that when they are reading the script they at least have some context as to how long ago the script was written. The date also adds a bit of nostalgia when you find a script you've written long ago and ask yourself "What was I thinking when I wrote this?".
A descriptive heading in your scripts can be as custom as you want it to be, there is no hard fast rule of what needs to be in there and what doesn't. In general, just keep it informative and make sure you put it at the top of the script.

Indent your code

Making your code readable is very important, it's something that a lot of people seem to forget as well. Before we get too far into why indentation is important let's look at an example.
NEW_UID=$(echo $x | cut -d: -f1)
NEW_USER=$(echo $x | cut -d: -f2)
NEW_COMMENT=$(echo $x | cut -d: -f3)
NEW_GROUP=$(echo $x | cut -d: -f4)
NEW_ADDGROUP=$(echo $x | cut -d: -f5)
NEW_HOMEDIR=$(echo $x | cut -d: -f6)
NEW_SHELL=$(echo $x | cut -d: -f7)
NEW_CHAGE=$(echo $x | cut -d: -f8)
NEW_PASS=$(echo $x | cut -d: -f9)    
PASSCHK=$(grep -c ":$NEW_UID:" /etc/passwd)
if [ $PASSCHK -ge 1 ]
then
echo "UID: $NEW_UID seems to exist check /etc/passwd"
else
useradd -u $NEW_UID -c "$NEW_COMMENT" -md $NEW_HOMEDIR -s $NEW_SHELL -g $NEW_GROUP -G $NEW_ADDGROUP $NEW_USER
if [ ! -z $NEW_PASS ]
then
echo $NEW_PASS | passwd --stdin $NEW_USER
chage -M $NEW_CHAGE $NEW_USER
chage -d 0 $NEW_USER 
fi
fi
 
Does the above code work, yes but it's not very pretty and if this was a 500 line bash script without any indentation it would be pretty hard to understand what's going on. Now let's look at the same code with indentation.

NEW_UID=$(echo $x | cut -d: -f1)
NEW_USER=$(echo $x | cut -d: -f2)
NEW_COMMENT=$(echo $x | cut -d: -f3)
NEW_GROUP=$(echo $x | cut -d: -f4)
NEW_ADDGROUP=$(echo $x | cut -d: -f5)
NEW_HOMEDIR=$(echo $x | cut -d: -f6)
NEW_SHELL=$(echo $x | cut -d: -f7)
NEW_CHAGE=$(echo $x | cut -d: -f8)
NEW_PASS=$(echo $x | cut -d: -f9)    
PASSCHK=$(grep -c ":$NEW_UID:" /etc/passwd)
if [ $PASSCHK -ge 1 ]
then
  echo "UID: $NEW_UID seems to exist check /etc/passwd"
else
  useradd -u $NEW_UID -c "$NEW_COMMENT" -md $NEW_HOMEDIR -s $NEW_SHELL -g $NEW_GROUP -G $NEW_ADDGROUP $NEW_USER
  if [ ! -z $NEW_PASS ]
  then
      echo $NEW_PASS | passwd --stdin $NEW_USER
      chage -M $NEW_CHAGE $NEW_USER
      chage -d 0 $NEW_USER 
  fi
fi
 
With the indented version it is a lot more apparent that the second if statement is nested within the first, you may not catch that at first glance if you were looking at the un-indented code.
The style of indentation is up to you, whether you want to use 2 spaces, 4 spaces or just a generic tab it doesn't really matter. What matters is that the code is consistently indented the same way every time.

Add Spacing

Where indentation can help make code understandable, spacing helps make code readable. In general I like to space code out based on what the code is doing, again this is a preference and really the point is just make the code more readable and easy to understand.
Below is an example of spacing with the same code as above.

NEW_UID=$(echo $x | cut -d: -f1)
NEW_USER=$(echo $x | cut -d: -f2)
NEW_COMMENT=$(echo $x | cut -d: -f3)
NEW_GROUP=$(echo $x | cut -d: -f4)
NEW_ADDGROUP=$(echo $x | cut -d: -f5)
NEW_HOMEDIR=$(echo $x | cut -d: -f6)
NEW_SHELL=$(echo $x | cut -d: -f7)
NEW_CHAGE=$(echo $x | cut -d: -f8)
NEW_PASS=$(echo $x | cut -d: -f9)

PASSCHK=$(grep -c ":$NEW_UID:" /etc/passwd)
if [ $PASSCHK -ge 1 ]
then
  echo "UID: $NEW_UID seems to exist check /etc/passwd"
else
  useradd -u $NEW_UID -c "$NEW_COMMENT" -md $NEW_HOMEDIR -s $NEW_SHELL -g $NEW_GROUP -G $NEW_ADDGROUP $NEW_USER

  if [ ! -z $NEW_PASS ]
  then
      echo $NEW_PASS | passwd --stdin $NEW_USER
      chage -M $NEW_CHAGE $NEW_USER
      chage -d 0 $NEW_USER 
  fi
fi
 
As you can see the spacing is subtle but every little bit of cleanliness can help make the code easier to troubleshoot later.

Comment your code

Where the header is great for adding a description of the scripts function adding comments within the code is great for explaining whats going on within the code itself. Below I will show the same code snippet from above but this time I will add comments to the code that explains what it does.

## Parse $x (the csv data) and put the individual fields into variables
NEW_UID=$(echo $x | cut -d: -f1)
NEW_USER=$(echo $x | cut -d: -f2)
NEW_COMMENT=$(echo $x | cut -d: -f3)
NEW_GROUP=$(echo $x | cut -d: -f4)
NEW_ADDGROUP=$(echo $x | cut -d: -f5)
NEW_HOMEDIR=$(echo $x | cut -d: -f6)
NEW_SHELL=$(echo $x | cut -d: -f7)
NEW_CHAGE=$(echo $x | cut -d: -f8)
NEW_PASS=$(echo $x | cut -d: -f9)

## Check if the new userid already exists in /etc/passwd
PASSCHK=$(grep -c ":$NEW_UID:" /etc/passwd)
if [ $PASSCHK -ge 1 ]
then
  ## If it does, skip
  echo "UID: $NEW_UID seems to exist check /etc/passwd"
else
  ## If not add the user
  useradd -u $NEW_UID -c "$NEW_COMMENT" -md $NEW_HOMEDIR -s $NEW_SHELL -g $NEW_GROUP -G $NEW_ADDGROUP $NEW_USER

  ## Check if new_pass is empty or not
  if [ ! -z $NEW_PASS ]
  then
      ## If not empty set the password and pass expiry
      echo $NEW_PASS | passwd --stdin $NEW_USER
      chage -M $NEW_CHAGE $NEW_USER
      chage -d 0 $NEW_USER 
  fi
fi
 
If you were to happen upon this snippet of bash code and didn't know what it did you could at least look at the comments and get a pretty good understand of what the goal is. Adding comments to your code will be extremely helpful to the next guy, and it might even help you out. I've found myself looking through a script I wrote maybe a month earlier wondering what I was doing. If you add comments religiously it can save you and others a lot of time later.

Create descriptive variable names

Descriptive variable names might seem like an obvious thing but I find myself using generic variable names all the time. Most of the time these variables are temporary and never used outside of that single code block, but even with temporary variables it is always good to put an explanation of what values they contain.
Below is an example of variable names that are mostly descriptive.

for x in `cat $1`
do
    NEW_UID=$(echo $x | cut -d: -f1)
    NEW_USER=$(echo $x | cut -d: -f2)
 
While it might be pretty obvious what goes into $NEW_UID and $NEW_USER it is not necessarily obvious what the value of $1 is, or what is being set as $x. A more descriptive way of writing this same code can be seen below.

INPUT_FILE=$1
for CSV_LINE in `cat $INPUT_FILE`
do
  NEW_UID=$(echo $CSV_LINE | cut -d: -f1)
  NEW_USER=$(echo $CSV_LINE | cut -d: -f2)
 
With the rewritten block of code it is very apparent that we are reading an input file and that file is a CSV file. It is also more apparent where we are getting the new UID and new USER information to store in the $NEW_UID and $NEW_USER variables.
The exmaple above might seem like a bit of overkill but someone may thank you later for taking a little extra time to be more descriptive with your variables.

Use $(command) for command substitution

If you want to create a variable that's value is derived from another command there are two ways to do it in bash. The first is to wrap the command in back-ticks such as the example below.

DATE=`date +%F`
 
The second method uses a different syntax.

DATE=$(date +%F)
 
While both are technically correct, I personally prefer the second method. This is purely personal prefrence, but in general I think that the $(command) syntax is more obvious than using back-ticks. Let's say for example you are digging through hundreds of lines of bash code; you may find as you read and read that sometimes those back-ticks start looking like single quotes. On top of that, sometimes a single quote tends to look like a back-tick. At the end of the day, it all comes down to preference. So use what works best for you; just make sure you are being consistent with the method you choose to use.

Before you exit on error describe the problem

We have gone though several examples of items that make it easier to read and understand code, but this last one is useful before the troubleshooting process even gets to that point. By adding descriptive errors in your scripts you can save someone a lot troubleshooting time early on. Let's take a look at the following code and see how we can make it more descriptive.

if [ -d $FILE_PATH ]
then
  for FILE in $(ls $FILE_PATH/*)
  do
    echo "This is a file: $FILE"
  done
else
  exit 1
fi
 
The first thing this script does is check if the value of the $FILE_PATH variable is a directory, if it isn't it will exit with a code of 1 which denotes an error. While it's great that we used an exit code that will tell other scripts that this script was not successful, it doesn't explain that to the humans running this script.
Let's make the code a little more human friendly.

if [ -d $FILE_PATH ]
then
  for FILE in $(ls $FILE_PATH/*)
  do
    echo "This is a file: $FILE"
  done
else
  echo "exiting... provided file path does not exist or is not a directory"
  exit 1
fi
 
If you were to run the first snippet, you would expect a huge amount of output. If you didn't get that output you would have to open the script up to see what could have possibly gone wrong. If you were to run the second code snippet however, you would know instantly that the path you gave the script wasn't valid. Adding just one line of code can save a lot of troubleshooting later.
The above examples are just a few things I try to use whenever I write scripts. I'm sure there are other great tips for writing clean and readable bash scripts, if you have any feel free to drop them in the comments box. It's always good to see what tricks others come up with.