Chapter 8. Test and Code Writing

Table of Contents

Scan Database Field Values
User-Defined Tests
Scan Database Syntax
Plugins
Initialization Phase
Start hook
Reconnaisance Phase
Scan Phase
Prefetch hook
Postfetch hook
Reporting Phase
Data Structures
Standard Methods
Global Variables
Test Identifiers
Code Copyrights

Scan Database Field Values

Though some checks can be found in other plugins, the db_tests contains the bulk of the web test information. Here is a description of the field values:

Table 8.1. Scan Database Fields

Test IDNikto test ID
OSVDB-IDCorresponding vulnerability entry number for osvdb.org
Server TypeGeneric server matching type
URIURI to retrieve
HTTP MethodHTTP method to use for URI
Match 1String or code to match for successful test
Match 1 (Or)String or code to alternatively match for successful test
Match1 (And)String or code to also match for successful test
Fail 1String or code to match for test failure
Fail 2String or code to match for test failure (alternative)
SummarySummary message to report for successful test
HTTP DataHTTP data to be sent during POST tests
HeadersAdditional headers to send during test

User-Defined Tests

Users can create their own, private tests for any of the databases. By placing a syntactically correct database file in the databases directory, with a file name prefaced with a "u", the data will be loaded along with the built-in checks.

For example, create the file databases/udb_tests and it will be loaded at the same time databases/db_tests is loaded. These files will also be checked for syntax when -dbcheck is used.

For tests which require a "private" OSVDB ID, use the OSVDB ID 0 (zero). This should be used for all vulnerabilities that do not (or should not) exist in OSVDB, as ID 0 is for testing only. You are encouraged to send missing information to OSVDB at moderators@osvdb.org.

For the "Test ID", it is recommended you use unique numbers between 400000 and 499999 to allow for growth of the Nikto database without interfering with your own tests (note: numbers above 500000 are reserved for other tests).

Please help Nikto's continued success by sending test updates to .

Scan Database Syntax

The scan database is a CSV delimited file which contains most of the tests. Fields are enclosed by quotes and separated by commas. The field order is:

Test-ID, OSVDB-ID, Tuning Type, URI, HTTP Method, Match 1, Match 1 Or, Match1 And, Fail 1, Fail 2, Summary, HTTP Data, Headers

Here is an example test:

"120","3092","2","/manual/","GET","200","","","","","Web server manual","",""

Plugins

Plugins allow other bits of code to hook into Nikto's processing and perform extra checks that cannot be achieved through the standard plugins.

Plugins can use several hooks, these are:

Initialization (mandatory)

Plugin initialization is performed before targets are assigned. During this phase, the plugin should tell Nikto about its existence and capabilities. It may optionally set up any later required variables.

This is always called for every plugin, even if the plugin will not be used in the scan. Any database loading or variable setup should be deferred to the start hook.

Start (optional)

The start hook is called after target enumeration, but before target scanning occcurs, and will only be called on plugins that are scheduled to be run. It will only execute once.

This hook is normally used to load databases or to initialize memory for the plugin.

Reconnaisance (optional)

During the reconnaisance phase, the plugin should look for interesting information that may be of use during the scan phase. It may report vulnerablities, though this is discouraged.

Scan (optional)

The scan phase should perform the meat of the plugin - this is where it should look at the web server and return any potential vulnerabilities.

Reporting (optional)

The reporting phase is used to export any found vulnerabilities into a format that they can be used later, for example written as a file report, or imported into a database. No testing of the web server, or reporting of new vulnerbilies should be performed in this phase.

This phase is slightly more complex than the others and may be called at several points during Nikto's execution, as detailed later

Prefetch (optional)

The prefetch phase is called before every request to the server. It is passed a copy of the request hash and can amend it before it is sent to the server.

This is normally used where standard data is needed for every request, for example, if authentication is needed.

As this hook will be executed for every request made, for efficiency, the hook sub should be kept optimal. It is strongly advised that conditional execution (see below) is used to ensure that it is only run when needed.

Postfetch (optional)

The postfetch phase is called after every request to the server. It is passed a copy of the response hash, so that it can check for content or header specific risks.

As this hook will be executed for every request made, for efficiency, the hook sub should be kept optimal. It is strongly advised that conditional execution (see below) is used to ensure that it is only run when needed.

Plugins are written in standard perl in the current context. They should be placed within the PLUGINDIR defined in the Nikto configuration file and must have a filename ending in .plugin.

An important concept to grasp about plugins and the order that are executed in is plugin weight: each phase will execute all defined plugins in the order defined by the weight. A plugin's weight is defined as a number between 1 and 100, where 1 is high priority and 100 is low priority. Plugins of equal weight will be executed in an undefined order.

Initialization Phase

As described above, all plugins must be able to execute in the initialisation phase or they will be ignored.

A perl sub must exist called filename_init. The sub is passed no parameters and should return a hash reference to a hash that should contain the following entries:

name (mandatory)

The short name of the plugin. This is used to identify the plugin during verbose logging and will, in future versions, be used to select plugin execution. The name should be one word and, ideally, lower case.

full_name (mandatory)

The full name of the plugin. This is used to identify the plugin during verbose logging and may be used in reporting modules to identify tests run against the web server.

author (mandatory)

The name or handle of the author of the plugin. This may be used during reporting to identify ownerships of copyright of tests run against the web server.

description (mandatory)

A short sentence to describe the purpose of the plugin. This may be used during reporting, or by a front end to describe the purpose of the plugin.

copyright (mandatory)

The copyright string (or lack of it) of the plugin. This may be used during reporting to ensure that appropriate copyright is assigned to reports.

hooks (optional)

This should be a hash of hashes that contains information about the hooks that the plugin can respond to.

Each key of the hooks element should be the name of the required hook with a hash value that details the information. The components of the hash should be:

method (mandatory)

This should be a reference to a function called for the hook.

cond (optional)

This is an expression to be evaluated before the plugin is executed; if true, the plugins is executed, if false, the plugin is skipped. This can be used to minimise plugin execution.

weight (optional)

This is the weight used to schedule the running of the plugin during the reconnaisance phase. If this is left undefined it will default to 50.

options (optional)

This is a hash that contains help information about any parameters that can be passed to the plugin. The information will be shown if the -list-plugins parameter is used.

Each key of the options element should be the name of the parameter, with a value of a string providing a brief description of the parameter.

report_head (optional)

This should be a reference to a function executed before any testing commences. If this is left undefined then the plugin will not be called to produce a report header.

report_host_start (optional)

This should be a reference to a function executed before the reconnaisance phase of each host. If this is left undefined then the plugin will not be called to produce a host header.

report_host_end (optional)

This should be a reference to a function executed after the scan phase of each host. If this is left undefined then the plugin will not be called to produce a host footer.

report_item (optional)

This should be a reference to a function executed after each found vulnerability. If this is left undefined then the plugin will not be called to produce an item record.

report_close (optional)

This should be a reference to a function executed after testing of all hosts has been finished. If this is left undefined then the plugin will not be called to close the report.

report_format (optional)

This should describe the file format that the plugin handles. This is internally matched with the contents of the -output switch to reduce excessive calls to plugins.

report_weight (optional)

This is the weight used to schedule the running of the plugin during the reporting phase. If this is left undefined it will default to 50.

Example 8.1. Example initialisation function

sub nikto_auth_init {
    my $id = { name             => 'auth',
               full_name        => 'Guess authentication',
               author           => 'Sullo/Deity',
               description      => 'Attempt to guess authentication realms',
               hooks            => {
                                    start => {
                                       method => \&nikto_auth_load,
                                       weight => 1,
                                    },
                                    postfetch => {
                                       method => \&nikto_auth,
                                       weight => 19,
                                       cond   => '$result->{whisker}->{code} eq 401',
                                    },
                                    prefetch => {
                                       method => \&nikto_auth_pre,
                                       weight => 19,
                                    },
                                   },                   
               copyright        => "2010 CIRT Inc"
               };

    return $id;
}

Start hook

The Start hook is called between target and enumeration and the start of scanning. It will only ever be called once, no matter how many targets Nikto is being run against. It will only be run against plugins that will be executed later.

This makes it the ideal place to set up any required variables, assign variables or load databases for the plugin to be used in response to later hooks.

The start method is passed no parameters and should return nothing.

void start_method(void); 
void;
 

Reconnaisance Phase

The reconnaisance phase is executed for each target at the start of each scan.

Each reconnaisance method such expect to take a mark hash ref. It should return nothing.

void recon_method(mark,  
 parameters); 
hashref mark;
hashref parameters;
 

The reconnaisance phase is intended to be used to pull information about the web server for later use by the plugin, or by other plugins. Reporting vulnerabilities in this phase is discouraged.

Example uses of the reconnaisance phase are to spider a site, check for known applications etc.

Scan Phase

The scan phase is the meat of the plugin's life, this is run, for each target, immediately after the reconnaisance phase.

Each scan should check for vulnerabilities it knows about and report on them as it finds one.

void scan_method(mark,  
 parameters); 
hashref mark;
hashref parameters;
 

Prefetch hook

The prefetch hook is executed before any request is made to the target. It is designed to allow the plugin to alter the libwhisker hash before the request.

In normal execution, the prefetch hook should not report vulnerabilities.

hashref request, hashref response prefetch_method(mark,  
 parameters,  
 request,  
 result); 
hashref mark;
hashref parameters;
hashref request;
hashref result;
 

Postfetch hook

The postfetch hook is the executed after any request is made to the target. It is designed to allow the plugin to check for issues with the response, such as vulnerable headers or content.

In normal execution, the postfetch hook should report any vulnerabilities that it finds.

hashref request, hashref response postfetch_method(mark,  
 parameters,  
 request,  
 result); 
hashref mark;
hashref parameters;
hashref request;
hashref result;
 

Reporting Phase

This is potentially the most convoluted phase as it has several hooks that may be used for each section in the scan's lifetime.

The hooks are:

Report Head

This hook is called immediately after target acquisition and before the reconnaisance phase. It is designed to allow the reporting plugin to open the report and ensure that any headers are appropiately written.

handle report_head(filename); 
string filename;
 

The filename parameter is a bit of a misnomer; it will be a copy of the string passed to the -output switch and may indicate, for example, a database name.

The handle is a handle that will be passed to other reporting functions for this plugin so should be internally consistent.

Report Host Start

This hook is called immediately before the reconnaisance phase for each target. It is designed to allow the reporting plugin to write any host specfic information.

void report_host_start(rhandle,  
 mark); 
handle rhandle;
hashref mark;
 

The rhandle parameter is the output of the plugin's Report Head function.

The mark parameter is a hashref for the target information (described below).

Report Host End

This hook is called immediately after the scan phase for each target. It is designed to allow the reporting plugin to close any host specfic information.

void report_host_end(rhandle,  
 mark); 
handle rhandle;
hashref mark;
 

The rhandle parameter is the output of the plugin's Report Head function.

The mark parameter is a hashref for the target information (described below).

Report Item

This hook is called once for each vulnerability found on the target This should report details about the vulnerability.

void report_item(rhandle,  
 mark,  
 vulnerbility); 
handle rhandle;
hashref mark;
hashref vulnerbility;
 

The rhandle parameter is the output of the plugin's Report Head function.

The mark parameter is a hashref for the target information (described below).

The vulnerability parameter is a hashref for the vulnerability information (described below).

Report Close

This hook is called immediately after all targets have been scanned. It is designed to allow the reporting plugin to elegantly close the report.

void report_close(rhandle); 
handle rhandle;
 

The rhandle parameter is the output of the plugin's Report Head function.

Data Structures

The below data structures are used to communicate between the various plugin methods. Unless otherwise mentioned, they are all standard perl hash references with the detailed members.

Mark

The mark hash contains all information about a target. It contains the below members. It should be read-only.

Table 8.2. Members of the Mark structure

identHost identifier, usually equivalent to what was passed on the command line.
hostnameHost name of the target.
ipIP address of the target.
portTCP port of the target.
display_nameEither the hostname, or the IP address of the target, dependant on whether a hostname has been discovered.
sslFlag to indicate whether the target runs over SSL. If it is set to 0, then the plugin should not use SSL. Any other value indicates SSL should be used.
vhostVirtual hostname to use for the target.
rootRoot URI to use for the target.
bannerBanner of the target's web server.

Parameters

The parameters hash contains all parameters that are passed directly to a plugin through one of the hooks.

The hash has a key of the parameter name and a value of the passed parameter. Implementation and sanity checking of the values is left up to the plugin.

If a parameter has not been set, it will not be in the hash. If it has been set to an undefined value, it will be set to the number 1 in the hash.

Some parameters, such as verbose and debug will be automatically handled by the Nikto, though the parameters will still be included in the hash.

Vulnerability

The vulnerability hash contains all information about a vulnerability. It contains the below members. It should be read-only and should only be written using the add_vulnerability method.

Table 8.3. Members of the Vulnerability structure

markHash ref to a mark data structure.
messageMessage for the vulnerability.
nikto_idTest ID (tid) of the vulnerability, this should be a unique number which'll identify the vulnerability.
osvdbOSVDB reference to the vulnerability in the Open Source Vulnerability Database. This may be 0 if an OSVDB reference is not relevant or doesn't exist.
methodHTTP method used to find the vulnerability.
uriURI for the result.
resultAny HTTP data, excluding headers.

Standard Methods

Several standard methods are defined in nikto_core.plugin that can be used for all plugins. It is strongly advised that these should be used where possible instead of writing new methods.

For some methods, such as add_vulnerability which write to global variables, these must be the only interface to those global variables.

array change_variables(line); 
string line;
 

Expands any variables in the line parameter. The expansions are variables defined in the global array @VARIABLES, which may be read from db_variables, or added by reconnaisance plugin methods.

int is_404(uri,  
 content,  
 HTTPcode); 
string uri;
string content;
string HTTPcode;
 

Makes a guess whether the result is a real web page or an error page. As several web servers are badly configured and don't return HTTP 404 codes when a page isn't found, Nikto attempts to look for common error pages. Returns 1 if the page looks like an error.

string get_ext(uri); 
string uri;
 

Attempts to work out the extension of the uri. Will return the extension or the special cases: DIRECTORY, DOTFILE, NONE.

string date_disp(); 
void;
 

Returns the current time in a human readable format (YYYY-mm-dd hh:mm:ss)

string rm_active(content); 
string content;
 

Attempts to remove active content (e.g. dates, adverts etc.) from a page. Returns a filtered version of the content.

string get_banner(mark); 
hashref mark;
 

Pulls the web servers banner. This is automatically performed for all targets before a mark is passed to the plugin.

boolean content_present(HTTPcode); 
string HTTPcode;
 

Checks the HTTPresponse against known "found" responses. TRUE indicates that the request was probably successful.

string HTTPCode, string content nfetch(mark,  
 uri,  
 method,  
 content,  
 headers,  
 flags,  
 testid); 
hashref mark;
string uri;
string method;
string content;
hashref headers;
hashref flags;
string testid;
 

Makes a simple request through libwhisker with the passed parameters. nfetch is hook aware and will cause all requests to be passed through the prefetch and postfetch hooks.

The flags hash is a selection of flags that may modify the behaviour of the request. The current flags are defined:

Table 8.4. Members of the flags structure

nocleanTells nfetch not to perform sanity checks on the structure. Normally requests will be checked to ensure that a valid Host header is included and that the Content-Length header matches the size of any content, setting this flag prevents the checks
noprefetchTells nfetch not to run the prefetch hook.
nopostfetchTells nfetch not to run the postfetch hook.
noerrorTells nfetch not to report error responses from the request.

hashref setup_hash(requesthash,  
 mark); 
hashref requesthash;
hashref mark;
 

Sets up up a libwhisker hash with the normal Nikto variables. This should be used if any custom calls to libwhisker are used.

string char_escape(line); 
string line;
 

Escapes any characters within line.

array parse_csv(text); 
string text;
 

Breaks a line of CSV text into an array of items.

arrayref init_db(dbname); 
string dbname;
 

Initializes a database that is in PLUGINDIR and returns an arrayref. The arrayref is to an array of hashrefs, each hash member is configured by the first line in the database file, for example:

"nikto_id","md5hash","description"

This will result in an array of hashrefs with parameters:

array[0]->{nikto_id}
array[0]->{md5hash}
array[0]->{description}
void add_vulnerability(mark,  
 message,  
 nikto_id,  
 osvdb,  
 method,  
 uri,  
 data); 
hashref mark;
string message;
string nikto_id;
string osvdb;
string method;
string uri;
string data;
 

Adds a vulnerability for the mark, displays it to standard out and sends it to any reporting plugins.

void nprint(message,  
 display); 
string message;
string display;
 

Prints message to standard out. Display specifies a filter for the message, currently this can be "v" for verbose and "d" for debug output.

Global Variables

The following global variables exist within Nikto, most of them are defined for internal use and their use by plugins is not advised. Several have been deprecated, these should not be used by plugins.

%TEMPLATES (read/write)

Hash to store the HTML and XML report templates.

%CONTENTSEARCH (read)

Hash to contain all the entries in db_content_search - a list of strings and related info to alert on from any request (regardless of test result).

%CLI (read)

Hash of passed CLI parameters

%VARIABLES (read) (write)

Hash of contents of the entries in db_variables. Plugins should only write to this hash in the reconnaisance phase.

%TESTS (read) (write)

Hash of the db_tests database. This is only intended to be used by the tests plugin, though it could be used by a reconnaisance plugin to add tests on the fly.

%NIKTO (read)

Hash which contains internal Nikto data, such as help for the command line parameters.

%CONFIGFILE (read)

Hash containing the data read from the configuration files.

%request (read) (write) (deprecated), %result (read) (write) (deprecated)

Global libwhisker hash. This should not be used; nfetch or a local hash should be used.

%COUNTERS (read) (write)

Hash containing various global counters (e.g. number of requests)

%db_extensions (read) (deprecated)

Hash containing a list of common extensions

%FoF (read) (write)

Hash containing data for each extension and what the server produces if a request for a non-existent file is requested.

%UPDATES (read) (write)

Hash containing any updates that need to be sent back to cirt.net

$DIV (read)

Divider mark for the items sent to standard out.

@DBFILE (read)

Placeholder used to hold the contents of db_tests.

$PROXYCHECKED (read) (deprecated)

Flag to see whether connection through the proxy has been checked.

@RESULTS (read)

Array of reported vulnerabilities, should only be written to through add_vulnerability.

@PLUGINS (read)

Array of hashrefs for each plugin. Used internally to run plugins.

@MARKS (read)

Array of marks to indicate each target.

@REPORTS (read)

Ordered array that reporting plugins should be run in. Used for efficency on calling reporting plugins.

Test Identifiers

Each test, whether it comes from one of the databases or in code, must have a unique identifier. The numbering scheme for writing tests is as follows:

Table 8.5. TID Scheme

000000db_tests
400000user defined tests (udb* files)
500000db_favicon
600000db_outdated
700000db_realms
750000db_content_search
800000db_server_msgs
900000tests defined in code

As much data as possible in the %TESTS hash should be populated for each new test that is defined in code (plugins). These fields include URI for the test, message to print on success, HTTP method and OSVDB ID. Without a 'message' value in %TESTS output will not be saved in HTML or XML reports. Not all tests are expected to have a uri, method or OSVDB ID. Here is an example of setting those fields:

$TESTS{999999}{uri}="/~root";
$TESTS{999999}{message}="Enumeration of users is possible by requesting ~username";
$TESTS{999999}{method}="GET";
$TESTS{999999}{osvdb}=637;

Code Copyrights

Any new or updated code, tests or information sent to the author is assumed to free of copyrights. By sending new or updated code, tests or information to the author you relinquish all claims of copyright on the material, and agree that this code can be claimed under the same copyright as Nikto.