############################################################################ ## I N T R O D U C T I O N ## All comments relate to the privateye.config file and Privateye's ParserNormal parser. If you write some other parser for some reason, I'd suggest you write your own documentation for it. All commands are of the form <command> <args> The command itself is not case sensitive. Anything between [] marks is optional to the command The names of all objects are now only alphanumeric characters and '_', as defined by PCRE's \w+. ############################################################################ ## A L E R T P A R S E R ## ALERTPARSER <name> <type> [<args>] Creates an AlertParser, an object that takes input from Input objects and performs various actions on it. The "ADDPATH" command is used with AlertParsers to determine what is done with the alert after it has been parsed. Types and arguments follow: ALERT <trigger> <regexp> (<field1>[, <field2>, ...]) Creates an alert parser that will run its regular expression on the alert string. If it does not match, the parser ignores that alert string. Otherwise, it populates the fields given with the groups from the regexp. Immediately after the alert is created, <trigger> is run on it. If <trigger> fails, the alert passes no further. CONFIG <output> A special type of AlertParser, this treats each input line as a console configuration line and deals with it accordingly. This allows for on-the-fly configuration of the program. Example: ALERTPARSER alertparser1 ALERT trigger1 \ /^#(\w*)#(\w*)#(\w*)#\s*$/ (a, b, c) This parser takes in a string and creates an alert with fields 'a', 'b', and 'c', the three groups of alphanumeric chars between the first four '#' marks. It ignores other syntaxes. After creating the alert with these three fields, 'alertparser1' passes the alert through 'trigger1'. If it returns false, the alert stops there. ############################################################################ ## U S E R H A S H ## USERHASH <name> <trigger> "<string>" Creates a user hash (the value used to reference the current user state) by taking the string <string> and substituting all strings '${field}' with the corresponding field value in the alert. <trigger> is checked before the hash string values are substituted, and if it returns false, the UserHash drops the current alert. Example: USERHASH userhash1 trigger1 "UNAME=${uname}" This UserHash checks 'trigger1', and if it returns true, substitutes the value of alert field 'uname' into the string and returns it. ############################################################################ ## T R I G G E R ## TRIGGER <name> <type> [<args>] Creates a trigger of the type given. Types of triggers and args needed are as follows: ## BOOLEAN TRIGGERS ## TRUE Always returns true FALSE Always returns false AND <trigger1> <trigger2> .. <triggerN> Performs a boolean AND on the return values of triggers 1 through N, returning that as its value OUTOF <num> <trigger1> <trigger2> .. <triggerN> Returns true if at least <num> number of triggers 1 to N return true. OR <trigger1> <trigger2> .. <triggerN> Performs a boolean OR on the return values of triggers 1 through N, returning that as its value NOT <trigger> Returns the opposite of <trigger>'s return value. ## MISC TRIGGERS ## OUTPUT <output> "<message>" Tries to output <message> to the output vector <output>. On success, return true. On failure, return false. The message can contain any number of ${<field>} strings, where <field> is an alert field. The actual values of these fields will be replaced into the message before it is sent. The escape sequences \n, \t, \", and \\ work so far in <message> ACTION <field> <action> Performs <action>, and sets alert field <field> to <action>'s output. Always returns true. USERFLAG <userflag> Returns true if the user flag <userflag> is set and has not expired. See the USERFLAG action for more details. ## DATABASE TRIGGERS ## (see DICTIONARY SYNTAX below) DB_SQL <db_driver> <db_host> <db_name> <db_user> <db_passwd> \ <db_sql> <db_input> <db_output> This trigger executes the user-defined SQL statement <db_sql> on the database specified by the first five arguments. Each '?' in the SQL is substituted with the value of the alert field in the corresponding position in <db_input> (an array like the array of fields used for AlertParser). The values of the first record returned by the SQL query are stored into the corresponding fields in <db_output>, another field name array. See the examples below for one use of this trigger. It's recommended, by the way, that you postpend "LIMIT 1" to all SELECT SQL statements that you use in DB_SQL, as all information after the first record is thrown out anyway. A quick note: a returned value of NULL will result in the alert field being set to the empty string, not NULL. For the moment, this just seems to work better. ## LDAP TRIGGERS ## LDAP_SEARCH <ldap_host> <ldap_user> <ldap_passwd> "<base_dn>" "<filter>" <dict:ldap->alert> [dict:ldap_opt_name->opt_val] This trigger runs a search on an LDAP directory, on server <ldap_host> and authenticated with <ldap_user> and <ldap_passwd>. <base_dn> and <filter>, both of which can contain alert fields, are used to search the directory. <dict:ldap->alert> is used to specify the attributes to request (all of the 'ldap' entries) and their mappings to alert fields (the 'alert' entries). See the Dictionary Syntax below. If required, the last dictionary allows for the setting of LDAP options. See example below. ## FIELD TRIGGERS ## REGEXP <field> <regexp> Runs a regular expression on the given field of an alert, returns true on a match. SUBSTR <field> "<string>" [pos] Returns true if <string> is a substring of <field>. If <pos> is given, this will only return true if <string> starts at position <pos> (zero indexed). COPY <from> <to> Copy field <from> of the alert to field <to> of the alert. SET <field> "<string>" Set alert's field <field> to the constant string <string> (no substitutions made). FIELD <field> Returns true if the alert contains a field named <field> ## MATH TRIGGERS ## (see MATH SYNTAX below) MATH_SET <field> '<math>' Sets alert field <field> to the value returned by math expression <math>, returning true on success or false on error (divide by zero, etc.) MATH_EQ '<math_1>' '<math_2>' Returns true if <math_1>'s return value is equal to <math_2>'s return value. As always, be careful with floating point integers. MATH_GT '<math_1>' '<math_2>' Returns true if <math_1> is greater than <math_2>, false if not or on error. MATH_LT '<math_1>' '<math_2>' Returns true if <math_1> is less than <math_2>, false if not or on error. Examples: TRIGGER trigger1 REGEXP a /badstuff/i Returns true if field 'a' of an alert contains the string 'badstuff', case insensitive because of the trailing 'i' TRIGGER trigger2 TRUE Always returns true TRIGGER trigger3 AND trigger2 trigger1 Tries trigger2, if true then tries trigger1, if true then returns true TRIGGER trigger4 DB_SQL mysql dbhost dbname dbuser dbpass \ "SELECT `val1`, `val2` FROM `tbl` WHERE `val3` > ? LIMIT 1" \ ( af3 ) ( af1, af2 ) When this trigger is hit, it will connect through 'mysql' to the host 'dbhost' with username 'dbname' and password 'dbpass'. It will then execute the specified SQL statement, testing if `val3` is greater than alert field value 'af3'. The alert fields 'af1' and 'af2' will be set to the first `val1` and `val2` values returned. TRIGGER trigger5 OUTPUT stdout "Field ABC has data \"${ABC}\"" Creates a trigger that outputs the string 'Field ABC has data "<ABC>"', where <ABC> is the data in field 'abc' of the alert. TRIGGER trigger6 LDAP_SEARCH ldap.server ldap_uname ldap_passwd \ "DC=mydomain,DC=com" "samaccountname=${username}" \ { [displayname]:[fullname] } \ { [LDAP_OPT_REFERRALS]:[0] [LDAP_OPT_PROTOCOL_VERSION]:[3] } This trigger runs an LDAP search in an Active Directory environment. Connects to LDAP server 'ldap.server', then binds with user name 'ldap_uname' and password 'ldap_passwd'. Using the given base DN, 'DC=mydomain,DC=com' and the filter 'samaccountname=${username}', it asks for the LDAP attribute 'displayname' of the first returned entry. It then takes that attribute's value and places it into the alert field 'fullname'. Since Microsoft is stupid and Active Direcory is not just LDAP, but BROKEN LDAP, the options seen above must be set when using AD. LDAP_OPT_REFERRALS must be 0, and LDAP_OPT_PROTOCOL_VERSION must be 3. *sigh* ############################################################################ ## D I C T I O N A R Y S Y N T A X ## Dictionary syntax works like this: The whole dictionary is surrounded by curly braces '{' and '}' Elements are seperated by whitespace. Each element's name/value pairs are seperated by a colon ':' Each element is surrounded by square braces '[' and ']' There can be no space between the ']' of the name, the ':' separator, and the '[' of the value. Spaces within element names are signifigant Example: { [a]:[1] [b]:[2] [c]:[froggy] } This is a dictionary that maps a->1, b->2, and c->froggy. When dictionaries are used as arguments, they will be signified as <dict:foo->bar>, where 'foo' describes the element names and 'bar' describes the element values. ############################################################################ ## M A T H S Y N T A X ## Math Triggers use mathematical functions to create math objects, which allow for simple computations to be done between alert fields and constant values. Math equations are written in prefix notation (reverse Polish), with the operator followed by its two operands. Parentheses are never needed and are not recognized. Operators currently supported include: +: Addition -: Subtraction *: Multiplication /: Division &: Bitwise AND |: Bitwise OR ^: Bitwise XOR ~: Bitwise NOT (unary operator) Numbers are of the form /-?\d*\.?\d+/. The following are legal: 2 -6 58 .03 -2.7 -0.89 .123 -.77 The following are NOT legal: 232. Any set of alphanumeric characters that is not a number will be considered the name of an alert field. abc _qwerty_ etc An example equation: * 2 + abc 5.5 : Returns 2 * (alert['abc'] + 5.5) ############################################################################ ## A C T I O N ## ACTION <name> <type> [<args>] Creates an action of the type given. Types of actions and args needed are as follows: NULL Do absolutely nothing. SH <shellcommand> Runs the shell command given. Within the shell command may be any number of strings ${<field>}, where <field> is the field name of an alert. These strings will be substituted before the command is run. AND <action1> <action2> ... <actionN> Perform <action1>, then <action2>, etc. and return the concatination of their outputs. BRANCH <trigger> <actionT> <actionF> Checks to see if <trigger> returns true or false. If true, execute the action <actionT>. Otherwise, execute action <actionF>. USERFLAG <userflag> <lifetime> Sets a flag named <userflag> on the current user. This flag will disappear after <lifetime> seconds. In the meantime, it can be seen by the USERFLAG trigger. Note that each new user flag with the same name will delete the previous and reset the lifetime timer to the current time plus its own lifetime. CLEARFLAGS <userflag1> <userflag2> .. <userflagN> Clear all specified user flags. TRIGGER <trigger> Perform a trigger check, then return either "<trigger-name> SUCCEEDED" or "<trigger-name> FAILED" RULELIST <rulelist> Run the user and alert through the rule list <rulelist> Example: ACTION action1 SH echo "Field 'a' says ${a}" Echos the value of field 'a' to standard output ############################################################################ ## R U L E ## RULE <name> <trigger> <action> <threshold_num> <threshold_duration> [<time>] Creates a new rule. Whenever the trigger returns true, the user (as returned by the User Hash above) will store that event, and if the frequency ever reaches <threshold_num> events per <threshold_duration> seconds, it will perform <action>. If <threshold_num> is 1, <threshold_duration>'s value doesn't matter. It should still be put in, however. The <time> argument is optional. If set, the rule will only trigger at most once every <time> seconds. <time> defaults to 0. Example: RULE rule1 trigger1 action1 2 60 The action 'action1' will be performed by any user that triggers 'trigger1' two or more times in 60 seconds. ############################################################################ ## R U L E L I S T ## RULELIST <name> Rules will be ignored unless they are part of a rule list. A rule list is a set of rules that can be run to examine an alert for a user. Rules are added to a rule list with the ADDRULE command, below. Example: RULELIST rulelist1 Creates an empty rule list named 'rulelist1' ############################################################################ ## A D D R U L E ## ADDRULE <rulelist> <rule> Unlike other commands, this does not actually create any objects. Instead, it adds the rule <rule> to the rule list <rulelist>. Rules cannot be examined unless they are part of a rule list. Example: ADDRULE rulelist1 rule1 Adds the rule 'rule1' to the rule list 'rulelist1' ############################################################################ ## I N P U T ## INPUT <name> <type> [<args>] Input objects allow for the creation of multiple input vectors. While running, the first Input vector to have data will always be used. If more than one input vector has data available at once, data will be taken from Inputs in the order they are specified in this file, I hope. Input types and arguments are as follows. All INPUT objects have an argument called 'separator', which cannot be set when the object is created, but which can be changed once it is (see the ARGS command below). 'separator' is a regular expression which matches the last line of input for a single alert. By default, it is "/\n$/", which will match every line. For Snort logs, though, we want to capture all data up to an empty line, so we change it to "/^\n$/", which doesn't match until an empty line is seen. FILE <filename> Reads through filename from start to finish. When the file no longer has data, it will no longer be a viable input, even if data is appended later on. TAIL <filename> Tails a file. This runs the command "tail -f -n 0 <filename>", so any data already in the file will be ignored. STDIN Reads data from the program's standard input. Example: INPUT input1 TAIL /var/log/messages Tails the /var/log/messages file. I have no idea why anyone would want to do this. ############################################################################ ## O U T P U T ## OUTPUT <name> <type> [<args>] Output objects allow for data to be written to an external (to the program) source. These can be used for logging, etc. Output types and arguments needed are as follows: FILEA <filename> Opens file <filename> for appending. Data currently in the file is not lost, and if <filename> does not exist, it will be created. FILEW <filename> Opens file <filename> for writing. If <filename> exists, it will be lost, since this overwrites <filename> with a new, empty file when created. STDOUT Writes output to the program's standard output handle. STDERR Writes output to the program's standard error handle. NULL Throws away output. Use like '>/dev/null' AND <output1> <output2> Send output to both outputs, return true only if both succeed. This does not preempt, so even if <output1> output fails, the OutputAnd object will attempt to send data to <output2>. Examples: OUTPUT local1 FILEA /var/log/privateye.log Append output to the file /var/log/privateye.log ############################################################################ ## I N P U T - O U T P U T O B J E C T S ## INPUTOUTPUT <name> <type> [<args>] Certain objects can act as both input and output. When created, these objects take on three different names, "Input_${name}", "Output_${name}", and "InputOutput_${name}". If an object exists beforehand with any of these names, the creation of the object will fail. Types and required arguments are as follows: TCP_SERVER <port> [timeout] Creates a TCP server listening on port <port>. When connected from the outside, two-way communication will commence. Until a connection is made, input and output communication will fail. The server will timeout each connection after <timeout> seconds. THE DEFAULT IS 10. To disable the timeout, use '0'. TCP_CLIENT <host> <port> Attempt to connect, via TCP, to <host> on port <port>. Once connected, use this single stream for input and output until the connection is closed TCP_SERVER_PASSWD <port> <timeout> <passwd> Create a TCP server as above, but one that requires a password before it will accept any input. This performs a fun bit of token-based authenti- cation that works like this: Immediately when the client connects, the server sends a one-line query string. The client appends the password to that string, then returns the MD5sum of that new string. The server checks that MD5sum to the correct sum, and if they're not the same, it disconnects. TCP_SERVER_CRYPT <port> <timeout> <passwd> Creates an encrypted TCP server. The initial authorization works just as with the TCP_SERVER_PASSWD (this part is not encrypted). Then, it encrypts all remaining traffic in rijndael-256 encryption (base64 encoded) and will only accept input in this form as well. ############################################################################ ## P A T H ## PATH <name> <type> [<args>] Creates an input path, that takes data from an input vector and deals with it in a specific manner. Types and required arguments are as follows: ALERT The normal path type. AlertParsers, UserHashes, and RuleLists must be added to any path for it to work. Each AlertParser will parse every input passed to the path. For each alert generated, each UserHash will find the username of this user. For each user found, The alert will be sent through each rule list of the path. CONFIG <output> The input is parsed as a configuration option and changes the internal environment of the program. Each input string is sent back to the <output> vector, along with a string "SUCCEEDED" or "FAILED" to determine whether the specified change took place. It is recommended that you only attach each CONFIG path to a single input, as interlaced configuration input could be detrimental. Examples: PATH path1 ALERT Create an alert path. This will be used by the next three commands. PATH path2 CONFIG stdout All input attached to this path is parsed by the configuration parser, and output is sent to the output object named 'stdout'. ############################################################################ ## A D D P A R S E R ## ADDPARSER <input> <alertparser> Adds an alert parser <alertparser> to the input <input>. All data coming through <input> from here on out is passed to <alertparser> for parsing. Note that this may be done many times, and each input from <input> will be passed to all <alertparser>s added to that input. ############################################################################ ## A D D P A T H ## ADDPATH <alertparser> <path> Adds path <path> to the AlertParser <alertparser>. From now on, whenever an alert is generated by <alertparser>, it will be passed to <path> for processing. This can be done any number of times to an AlertParser, and alerts from that AlertParser will traverse all paths. ############################################################################ ## D U M P A L L ## DUMPALL A debugging feature, this command does not actually affect the environment. Instead, it dumps all information it can about all named objects. Named objects include any object that can be created in the config file. Example: DUMPALL Dumps named object information to standard output. ############################################################################ ## D U M P ## DUMP <name> Another debugging feature, this command takes the name of any named object, along with its object prefix, and dumps its data to standard output. Object prefixes include 'Trigger_', 'Rule_', 'RuleList_', 'UserHash_', 'Path_', etc. You should probably be able to guess the rest. ############################################################################ ## I N C L U D E ## INCLUDE <filename> This parses all of the file <filename> inline with the contents of this file. The difference between this method and defining a config path to the file is that the config path does not start until the main engine starts, after the initial parsing of the initial config file. ############################################################################ ## A R G ## ARG <name> <command> [<args>] The "ARG" command allows for dynamic switching of object arguments of the object <name>. The various commands and their arguments are as follows: GETARGS Returns a list of all changable arguments of the current object. Output will be of the form: [<argtype>] <arg> = <currval> where <argtype> is the type of the argument, <arg> is the argument name, and <currval> is the current value of the argument (IE: the name of the object that <arg> is currently set to). SETARG <arg> "<new>" Sets the argument <arg> of the current object to the new object named <new>. Note that <new> must be a full name, including prefix. ############################################################################ ## U S E R S ## USERS <BACKUP|RESTORE> <dbType> <dbHost> <dbName> \ <dbUser> <dbPasswd> <dbTable> <userhash> <object> The USERS command is used to store the data about users normally kept in memory by Privateye into a database. This data is either stored or read from the mysql fields <userhash> (probably should be VARCHAR(255)) and <object> (probably should be TEXT) which will contain the serialized objects. ############################################################################ Now you can write your own stuff! Woot. ############################################################################