pax_global_header00006660000000000000000000000064115545525440014524gustar00rootroot0000000000000052 comment=c0442ad7d74465533c11736fe03bc46b951a37b6 nikto-2.1.4/000077500000000000000000000000001155455254400126545ustar00rootroot00000000000000nikto-2.1.4/docs/000077500000000000000000000000001155455254400136045ustar00rootroot00000000000000nikto-2.1.4/docs/CHANGES.txt000066400000000000000000001112261155455254400154200ustar00rootroot000000000000002011-02-20 Nikto 2.1.4 release - Tickets 148, 160, 188: XML CHANGES: - Removed 'cyphers' from DTD (was never populated via the code) - The 'niktoscan' element is now included (was in schema, but unused) - 'niktoscan' new variables: scanstart, scanend, scanelapsed - removed from templates (duplicate of hoststest) - removed from DTD - Removed duplicate element from xml_summary.tpl - Properly close - Incremented nxmlversion to 1.1 - Tickets 202, 203: Rewrote set_targets to not accidentally collapse targets, which fixed terminate signal issues - Ticket 201: Rewritten & fixed authorization code work better and make fewer requests - Ticket 195: Update interactive status counts if mutate options are used - Ticket 194: Look for internal IPs in cookies - Ticket 192: Relabel IDS evasion as 'encoding techniques' - Ticket 186: Enable sleep for fractions of seconds - Ticket 185: Make multiple index file output links in html reports - Ticket 184: Fix -root option - Ticket 181: Fix COOKIE set via nikto.conf. Also allow multiple cookies. - Ticket 179: Update docs for -useproxy - Ticket 178: Add -Interactive-off to disable interactivity - Ticket 177: Enable http keep-alive - Ticket 173: Skip current host with 'N' in interactive mode - Ticket 169: Allow regular expressions in db_tests - Ticket 155: -findonly is deprecated in favor of -Plugins "@@NONE" (-f will replicate this functionality) - Ticket 82: Auth is now checked per realm, not per resource - Fix parsing of nmap greppable output so that any port descr matching http is checked. Thanks Moses Hernandez & @mubix for reporting & testing. - Fix a potential div by zero error - Fix a potential for false positives or negatives with version matches - Various cleanups in nikto_report_xml.plugin and nikto_report_html.plugin - Not all udb* files were loaded properly - Server name not properly printed in update/submission output - Created $mark->{'components'} to store server build items instead of @BUILDITEMS - Variable consolidation & memory usage cleanup - Move message on -root from notices to target host info (suggestion from YGN) - Automatically escape invalid regexes in databases at run-time, so no dying - Validate regex field syntax on -dbcheck - Move -root option to %mark so it works on a per-host level if passed via URI - Added nikto_ssl.plugin to check cert's CN vs hostname - Add basic retry on error in nfetch() - Change how db_404_strings are used by moving where they ae checked--should reduce FP - Fix missing url sent to rm_active_content during error mapping--shoudl prevent many FPs - Actually check for code-based nocache flag in cache_add and cache_fetch - Make nikto_multiple_index.plugin only look at 200 responses 2010-09-06 Nikto 2.1.3 release - Ticket 164: Error when proxy starts to give 502 - Ticket 165: Don't show incorrect # of items checked in -findonly - Ticket 166: Allow interactive pause - Ticket 167: Update manual - Ticket 168: Fix scan not working behind proxy when domain can't resolve - Ticket 170: Implemented MSF output - Ticket 171: Allow proxy to be specified on command line - Fix incorrect running of some plugins - Interactive status report gives guess of time remaining - Don't print duplicate findings (such as indexing) - Minor standardization stuff - Documentation updates - Fixed broken cache - Cleaned up status report code - Version output now shows status of SSL and XMLRPC availability 2010-07-07 Nikto 2.1.2 release - Ticket 8: Interactive scan status. - Ticket 122: Cleanup db_404_strings to prevent over-matching. - Ticket 122: Use db_404_strings as a higher priority. - Ticket 125: fetch is dead, long live nfetch! - Ticket 126: subdomain plugin tries to guess domain on unqualified hostname. - Ticket 127: dav methods are treated specially and reported all at once. - Ticket 129: Change references for config.txt to nikto.conf. - Ticket 130: Added -D E to show HTTP errors, otherwise suppress. - Ticket 132: Properly check for HTTP and HTTPS ports in cache. - Ticket 133: Regular expression matching causes errors. Removed char_escape and some other regexs in favor of the faster quotemeta(). Also set many regexs to non-capturing for speed. - Ticket 134: Added documentation of -config to usage_short. - Ticket 136: Moved set_scan_items to only run once, should speed things up with multiple targets. - Ticket 137: Added -ask to override nikto.conf's UPDATES value (same options). - Ticket 139: Partial fix: Moved URI error handling and reporting result to nfetch, rather than being in nikto_tests. - Ticket 141: pre-compile RE in content_search to give some speed-up. - Ticket 142: Enhancement to allow easier addition of hooks. - Ticket 144: Cleaned up map_codes to use general rules, still needs some for redirection. - Ticket 145: Added OSVDB 0 to orphan items in db_tests. - Ticket 146: Paritial fix: with new "start" hook which is run at the start after target enumeration. - Ticket 147: Grab HTTP information on the fly, deprecate get_banner. - Ticket 150: Special characters in XML output. - Ticket 152: HTTP Version set in nikto.conf over-ridden. - Ticket 153: Properly check for HTTP and HTTPS ports in cache. - Ticket 156: Update system couldn't update nikto_core.plugin. - Ticket 163: Scan details not appearing in XML reports. - Allow changing certain config settings during scans. - Optimized rm_active_content() a little by shuffling code and reducing some mem copies/regexs. Needs more work. - Update nikto.conf to switch tests to always have the (report:500) parameter. - Updates to read known headers on the fly, rather than make requests for them. - Fixed a bug with the order of parameters in hooks (broke parameters being passed to some plugins). - Added the parameter "report" to tests plugin to report when completed x number of tests. - Stop LibWhisker producing an error when talking HTTP to HTTPS during port_check. - Merged apacheusers and apache_enum_users. - Add facillity for a plugin to inform which options it can take. - Added nbe output plugin which written by Frank Breedijk of the Seccubus project. - Moved do_auth to a postfetch plugin. - Removed dead code from fetch(). - Optimizations in nfetch(), nikto.pl, & elsewhere. - Added support for prefetch and postfetch hooks. - Moved content_search to a plugin. - Some tuning around plugin execution. - Updated user_enum_apache to use Plugins instead of mutate. - Rewrote the macro expanding bit to make it more efficient. - Mutate 1 now wrapped into nikto_tests and doesn't take up anywhere near the amount of memory! - Starting to deprecate mutate by replacing with plugin options. -mutate 2 (passfiles) is now implemented within tests and uses less memory. - Updated -check_updates to use nfetch instead of fetch. - Updated -Plugins support. - Add filename support to rm_active_content. - Added basic support for -D s (scrub, removes some information from the log). - Match plugin names case-insensitive. - Warn if RFIURL is undefined. 2010-01-20 Nikto 2.1.1 - Ticket 117: Fixed SKIPPORTS - Ticket 116: Moved User-Agent string to nikto.conf - Ticket 116: Added dynamic variables to User-Agent (Testid, Evasion methods) - Ticket 95: Added support for OSVDB, now the fun bit of filling it in - Ticket 111: Basic syntax checks for all databases - Ticket 109: Added an extra optional element to xml output to contain the SSL date. Need to do similar for html, txt and csv - Ticket 106: Shorts authentication being successful if an error is returned - Ticket 107: Support for short reads in LW2.5 - Ticket 98: If -Format is missed guess the format based on file extension in -output. Default is none if -output is omitted. - Ticket 96: Multiple index file enhancements for groups and better unique file identification - Ticket 103: content in xml report is now wrapped in CDATA - Ticket 110: Mutate now respects db variables - Ticket 97: Fix for response caching - Ticket 99: Spelling disagreements between Brits and Americans - Added @RFIURL to nikto.conf for a remote file include location, and supporting code. - Added ~2300 RFI tests from the combined RSnake/OSVDB list - Removed NMAP and NMAPOPTS from nikto.conf as it is no longer used/supported - Reporting: simplify xml/html code, fix a bug when a space is in the uri, and load ony needed templates - Enable 2 new LW evasion tacticts (carriage return or binary value as request spacer) - Added support to select plugins via -Plugins and -list-plugins option to list current plugins - Major bug fix for proxy usage - Don't report p3p header as unusual - Various changes to aid future binary db usage for mutates - Various changes to aid future multi-threading - Fix for multiple index files 2009-12-21 nikto.pl - Ticket 100: Fix for reading home directory on Windows - Some new additions to db_realms and db_embedded 2009-08-29 templates/xml* docs/nikto.dtd - Added tag for scan/host statistics 2009-08-25 plugins/db_httpoptions - Ticket 89: - remove TRACE and TRACK from the db 2009-08-19 plugins/nikto_headers - Added test for asp source code disclosure through the Translate header 2009-08-13 plugins/* plugins/nikto_embedded plugins/db_embedded - Various fixes to use nfetch and fix proxy use - New plugin added to identify embedded devices 2009-08-12 plugins/nikto_core - New fetch (nfetch) sub added which uses a local request/result hash. All requests should use this instead of fetch. - Patch to add a URI cache within fetch, can be disabled with -nocache 2009-08-04 plugins/nikto_core - Patch to actually report the URI when it works out a password - Added test for DEBUG HTTP verb 2009-08-03 plugins/nikto.pl - Put in a quick catch for port ranges (e.g. 80-90) if people use the old style of port entries - Put in a simple signal handler to close reporting if a sigint is caught 2009-08-02 plugins/nikto_multiple_index db_multiple_index - Added check for multiple index files for request #16 - Turned standard headers into a database file to close off #22 2009-08-01 plugins/* nikto.pl - Fixes for xml reporter to allow multiple hosts - Fixes for html, txt and csv exporters - Tickets 80 and 85 2009-07-31 plugins/* nikto.pl - Fix for ePO agent/HP iLO to not report for each known type of webserver - Big changes to the way nikto assigns targets to remove globals, have deliberately broken nmap scanning and allowing port ranges. 2009-07-20 plugins/nikto_core plugins/nikto_outdated db_tests db_outdated - Fix to ensure that -Tuning works as expected, fixes ticket #84 - Fix to add a warning if a web server has been configured to restrict information on its server banner, fixes ticket #66 - Minor amendment to lightttpd version to fix ticket #67 - Fix to nikto_core to make dbcheck work! - New item for ticket #75 2009-06-30 plugins/nikto_dictionary_attack - Add plugin to use dirbuster lists with mutate 6 and mutate-options - couple of minor fixes to prevent errors on Windows and exporting as text 2009-06-27 plugins/nikto_user_enum_cgiwrap.plugin - Managled cgiwrap and apache plugins together and allow use of a dictionary (to speed things up). Also made it work with new reporting style - Added -mutate-options switch 2009-06-15 plugins/nikto_reports.plugin templates/xml_end - Fixed bug with xml not terminating correctly 2009-05-11 plugins/nikto_core.plugin plugins/nikto_subdomain.plugin - Added subdomain buteforcer as mutate option 5, thanks to Ryan DewHurst - Added extra tests to pull information if scanning ePO agent or HP WBEM - Added test to recognise a Dell Remote Access Console - Added -no404 switch to disable 404 checking. Warning, this produces a lot of false positives at the moment 2009-01-10 plugins/nikto_core.plugin - Added fix for #73 to apply multiple variables for tests, supplied by Laurent Licour - Removed test_target function as now done in the nikto_test plugin - Added support for Allow directive in robots.txt - Added exit if cannot connect to a defined proxy 2008-11-11 plugins/nikto_core.plugin nikto.pl plugins/nikto_reports.plugin - Added report phase to plugin runner 2008-10-09 plugins/nikto_core.plugin nikto.pl - Further tunings to authentication code to simplify it. 2008-10-02 plugins/nikto_core.plugin nikto.pl - Altered authentication code to make it simpler. - Now supports NTLM authentication. 2008-09-24 plugins/nikto_core.plugin db_tets - Fixed problem with nikto using authentication provided from the command line. It now adds it to the list of realms. - Added extra test to highlight localstart.asp if it is the default page. - Added tests to identify Ampache. 2008-09-23 plugins/*.plugin - Added support for conditional recon and scan plugins. 2008-09-21 plugins/*.plugin - Changes for new plugin running structure; it needs to be finished (conditional plugins and report methods) but it is good enough to release now. 2008-09-20 plugins/nikto_core.plugin plugins/nikto_cgi.plugin nikto.pl plugins/nikto_reports.plugin templates/xml_host_head.tmpl docs/nikto.dtd - Fixes to ensure nikto produces less perl warnings - Fix for ticket #59: add vhost to xml output 2008-09-16 plugins/nikto_core.plugin plugins/nikto_httpoptions.plugin - Fix for ticket #37 - Allow fetch to optionally call LW2::http_fixup_request - Better handling of extra headers within fetch 2008-09-14 plugins/db_server_msgs plugins/nikto_*.plugin - Update server messages to dynamic database format - Altered all plugins to use a separate RESULTS array for storing results; meaning that it is easier to abstract - Added add_vulnerability method to nikto_core to report vulnerabilities and reduce code size. - Added ability to add an extra hash to fetch() to allow extra headers to be added. 2008-09-12 plugins/nikto_core.plugin plugins/nikto_httpoptions.plugin plugins/db_httpoptions plugins/nikto_favicon plugins/db_favicon - Fix for ticket #38: httpoptions are drawn from a database - Now setup to allow dynamic databases, rather than all being imported by nikto_core at start time - Altered favicon database to use dynamic database 2008-09-06 plugins/nikto_core.plugin tmpl/htm_close.tmpl - Fix for ticket #53: all plugins now show last mod date - Fix for ticket #51: updated copyright date in HTML 2008-09-04 plugins/nikto_core.plugin - Ticket 55: introduced by the solution for ticket #44 - Ticket 53 2008-08-12 plugins/db_outdated -- Nikto 2.03 - Fix for Jetty to latest version, fixes ticket #49 2008-08-07 docs/nikto_manual.html - New export of the manual from the docbook - Updated versions in nikto.pl 2008-08-06 plugins/db_outdated - Added various new versions 2008-08-05 plugins/db_favicon - Fix for ticket #45 - Added favicons for Roku Soundbridge and Ampache 2008-07-14 plugins/nikto_headers.plugin - Changes to look at non-standard headers - Changes to examine Apache's ETag header 2008-07-07 nikto.pl plugins/nikto_core.plugin plugins/nikto_reports.plugin - Fix for ticket #41 - a rather nasty bug that's been in nikto 2 since its inception; where variables weren't fully expanded. 2008-07-02 plugins/nikto_core.plugin - Fix for ticket #11 - change CGIDIRS test so that they're not hardcoded. The reponse codes are now kept in a variable in db_variables - Applied same to enumerating apache users plugin - Fix for ticket #39 - we now check whether getoptions failed, show usage and exit with a code of one. This also means that it will exit gracefully if a parameter is missed out when one is required. 2008-06-24 plugins/nikto_core.plugin - Fix for ticket #35 - allow multiple HTTP methods to identify an HTTP server, these are set with the variable CHECKMETHODS in config.txt - Fix for a bug in the nmap reader where it would ignore the IP address if it nmap didn't return a hostname. 2008-06-22 plugins/db_tests - Fix for ticket #26 - stop domino tests producing false positives 2008-06-20 plugins/nikto_httpoptions.plugin - Fix for ticket #30 - ensure that propfind has the right OSVDB tag 2008-04-22 plugins/nikto_outdated.plugin - Change to allow stop duplication of items when scanning more than one host. Fix for bug 28 2008-04-16 plugins/nikto_core.plugin - Change to allow reading of a host list from stdin - Fix for enhancement 10: read from nmap output (only -oG) 2008-04-15 plugins/nikto_core.plugin - Fixes for bug 25: Unopen ports are now reported 2008-04-14 templates/htm* - Fixes for bug 24: HTML output is now valid HTML 4.01 Strict 2008-04-11 nikto.pl - Started using international dates instead of the weird US format - Added a fix for bug id 23: allow a range of ports instead of a comma separated list 2008-04-11 db_outdated - Updated current version of Apache to 2.2.8 01.06.2008 2.02 - Added XML output thanks to the work of Jabra. XML format comes from templates (same as HTML). See the 'templates' dir for more info. - HTML reports changed by Jabra to remove some oddities and remove HTML from items - Fixed non-reporting of non-HTTP ports (or closed ports) when at least one port was HTTP. - Removed experimental knowledge base (KB) code, as XML output is more flexible for long-term scan tracking - Added unique identifiers to all tests from databases, and all tests created in code - Updated documentation 01.02.2008 nikto_core - Fixed improper parsing of long options (-update, etc.). Thanks to Frank Breedijk for figuring this out. 12.30.2007 db_servers - Removed as it is not used 12.19.2007 nikto_msgs.plugin - Add a boundary for regex on versions to cut down false positives 12.19.2007 niko_favicon.plugin - Added OSVDB ID 12.18.2007 niko_favicon.plugin - Fix false positive when favicon.ico doesn't exist 11.22.2007 Nikto 2.01 release - Fix anti ids encoding use. thanks to Francisco Amato - Fix virtual host usage if set via CLI. thanks Jon Hart - Fix Host header restoration when testing for IIS IP leak - Fix for plugindir & templatedir if EXECDIR is set in config.txt, thanks Shiraishi.M and Will Andrews for pointing it out. - Fix count of items--count now accurately reflects the number of items, not just number of vulns. thanks Frank Breedijk - Kick a few more things to KB that should be saved - Added SKIPIDS to config.txt to completely ignore some tests loaded from db_tests. Suggested by Christian Folini. - Enhanced rm_active_content to try to exclude the file/QUERYSTRING requested - Unset the auth header after guessing at it. Thanks Paul Woroshow for reporting the bug. 11.12.2007 nikto_headers.plugin - Fix internal IP address snarfing for IIS, thanks Frank Breedijk for pointing it out 11.10.2007 Nikto 2.00 release - Rewrite of nikto_httpoptions.plugin to read the Public header - Fixups to prevent namespace violations in nikto.pl and nikto_core.plugin - Add some normalizations to the -root option variable, suggested by Erik Cabetas - Added -Display with options for suppressing redirects & cookies from being included in output - Added -Tuning options to let users specify what they would like to test, or exclude certain categories - Added config.txt's NMAPOPTS, thanks Sean Lewis for the suggestion - All new HTML report - Bugfix: a found cookie would report for every port/server after it was found - Bugfix: all hosts scanned with all ports if hosts file used - Bugfix: all hosts scanned with port 80 despite what the user wanted - Bugfix: Reverse DNS inet_aton error fix, pointed out by Jason Peel @ Foundstone - Changed auth checking so it will test any directory found, not just /, and removed nikto_realms.plugin as a consequence - Changed scan_database.db format significantly (and name), (and all the code to deal with tests) - Completely new 404 engine which causes less false-positives (see docs) - Created dump_lw_hash instead of dump_request_hash & dump_result_hash - Implemented a knowledge base which (should) store all the gory details of scans... probably use this later ;) - Moved pre-defined variables from config.txt to variables.db so they can be automagically updated. Entries in config.txt are still read. - Removed %CFG, storing vars in %NIKTO instead - Removed -generic - Removed extraneous global vars - Removed load_realms, combined with load_variables - Replaced %CONFIG with %NIKTOCONFIG - Set MAX_WARN to trigger on any response code, skipping 404|403|401|400 to avoid common ones - Added -Single single request mode - Updates to use the RFP's LibWhisker 2.0 - Added -Help to show extended help ouput, changed default help screen to be shorter. Suggested by Jericho. - Additional error checking on invalid reverse-dns (Paul Woroshow) - Cleaned up comment/line parsing routines in multiple places, from Erik Cabetas - Tightened some for loops with real values instead of guessing, from Erik Cabetas - Addded error message if no host is specified, from Erik Cabetas - Added more robust output file type checking (txt/htm/cvs), from Erik Cabetas - Added more debug statements regarding which CGI directories will be scanned, from Erik Cabatas - Bugfix: more 'half dead host' scanning issues resolved with Jericho. LW is much pickier now about calling http_close - Added error if -F specified without -o, from Erik Cabetas - Bugfix: server category match no longer matches partial strings, from Erik Cabetas - Bugfix: mis-pasted line, pointed to by Erik Cabetas - Send all errors to STDERR - Added -config option to specify a config file, thanks to Pavel Kankovsky - fixed regex issue on banner. thanks Alexander Ehlert for pointing it out - All other plugins updated for v2 changes - Added favicon.ico hash checking - ... gobs more 02.06.2004 nikto_core.plugin 1.21 - Cleaned up comment/line parsing routines in multiple places, from Erik Cabetas - Tightened some for loops with real values instead of guessing, from from Erik Cabetas - Removed duplicate bit of code, from Erik Cabetas - Addded error message if no host is specified, from Erik Cabetas - Added more robust output file type checking (txt/htm/cvs), from Erik Cabetas - Added more debug statements regarding which CGI directories will be scanned, from Erik Cabatas 12.17.2003 nikto_core.plugin 1.20 - Fixed BID links, thanks Richard Tortorella for the report. 10.27.2003 Nikto 1.32 release nikto_core.plugin 1.19 - Removed unecessary 'use IO::Socket' call from resolve() - Removed unecessary counters - Replaced some slow foreach counters - Moved proxy_check earlier, before port_scan, so it will be set first - Removed -allcgi option in favor of -CGIdir, which can specify to test 'all', 'none' or a specific directory. - Bugfix: testing through proxy by making sure host name is set instead of ip, thanks to Fabrice Annic for the catch - Bugfix: a regex/logic/if error in test_target, thanks Pavel Kankovsky for the bug report. 401/302 messages will now report regardless of test/pass fail. - Bugfix: -dbcheck now identifies duplicates without relying on message text, thanks Jericho / Attrition.org for pointing this out nikto.pl 1.12 - Rearranged order of get_banner & setup so that it would be called right nikto_headers.plugin 1.08 - Added DAAP header check 10.02.2003 nikto_core.plugin 1.18 - Fixed get_banner to properly handle multi host/port scans 10.01.2003 nikto_outdated.plugin 1.12 - Fixed improper matching in version evals, reported by Paul Bakker 09.30.2003 nikto_core.plugin 1.17 - Reordered loop code to make -f scans faster. - Added a skip for "(Win32)" in the version updates back to cirt.net nikto_outdated.plugin 1.11 - Stripping () from version strings 09.24.2003 Nikto 1.31 release nikto_core.plugin 1.16 - Fixed a bug in resolve() that may prevent name lookups when host files used - Fixed a bug in resolve() where scan would exit if 1 name resolution from host file failed - Changed set_targets so that if the -h value exists as a file it reads that instead of resolving it as a name. This eliminates need for .csv or .txt file name endings. - Added auto or semi-auto update of version strings to CIRT.net. This is done through a simple GET request. Controlled via config.txt's UPDATES variable. *ABSOLUTELY NO* server info is sent... only versions from HTTP headers, i.e. "Apache/4.0". Thanks to Jericho for feedback/ideas. - Added a host counter output at end & for every 10 hosts - Set CHANGES.txt download only on *code* updates, not DBs - Added MAX_WARN to config.txt for warning level on OK/Moved messages, thanks Jericho for the suggestion. - Added PROMPTS to config.txt to allow user control of prompting--good for unattended scans - Added a regex test to dbcheck() better catch errors in server_msgs.db - Thanks again to Jericho for many updated tests/information. - Cleaned up port scan code - Fixed/improved scanning through proxies nikto_outdated.plugin 1.09 - Added support for sending updates of version strings to CIRT.net. See nikto_core.plugin version 1.15 notes. LW.pm - 1.8 - Updated to LW.pm v1.8, see the change log included with it (www.wiretrip.net/rfp/). nikto.pl - 1.10 - Implemented versioning on nikto.pl (!), many changes to support core 1.15 - Put 'require LW.pm' down *after* we know where it is.. duh. Thanks J Barber (ussysadmin.com) for the suggestion. Also changed it 'require' vs 'use' so in the future I can update it, if necessary. - Hosts are now tested in the same order as the appear in an input file 08.18.2003 nikto_outdated.plugin 1.08 - Fixed nasty regex bug in the version eval, and made more efficient. Pointed out by fr0stman, thx Zeno for assistance 07.22.2003 nikto_headers.plugin 1.07 - Added Host header back after delete in IIS Content-Location check. Thanks to Abdi Ponce for the bug report & debug. nikto_httpoptions.plugin 1.04 - Changed PROPPATCH, TRACK, TRACE messages. Changed PROPFIND message, thanks to Jericho for tracking down some good info on it. Added SEARCH message. nikto_core.plugin 1.14 - Added tags to the HTML output for browser-neatness - Removed a stray debug print 07.03.2003 - Thanks to Jeremy Bae for many Jeus Webserver tests. 06.29.2003 nikto_core.plugin 1.13 - changed some &function calls to function() to keep $_ from being passed down another level.. thanks to zeno for the heads-up. nikto_headers.plugin 1.05 - fixed the IIS4 content-location check as it had a tendency to fail miserably... 06.29.2003 nikto_core.plugin 1.12 - changed output of dump_request to be more like normal request text 06.29.2003 nikto_core.plugin 1.11 - bug fix for scanning through proxies 06.19.2003 nikto_core.plugin 1.10 - added 'csv' to file formats in -help output (doh!) - minor speedups 06.17.2003 nikto_user_enum_apache.plugin 1.02 - Bugfix: some user names not tested (zz, zzz, etc.) - Major rewrite for speed improvements nikto_user_enum_cgiwrap.plugin 1.01 - Bugfix: some user names not tested (zz, zzz, etc.) - Major rewrite for speed improvements 06.16.2003 nikto_core.plugin 1.09 - dbcheck option enhanced: check that all plugins are in the order file - dbcheck option enhanced: check that all plugins have properly named sub calls - update option enhanced: retrieves updated CHANGES.txt file with code updates - Bugfix: resolve() did not properly catch invalid IP addresses. Reported by Rick Tortorella. 06.12.2003 nikto_core.plugin 1.08 - Removed iprint() entirely (finally) - Made "Needs Auth" links active in HTML output 05.30.2003 nikto_core.plugin 1.07 - Bugfix: 05.30.2003 nikto_core.plugin 1.06 - Added number of elapsed seconds to final host/port output - Bugfix: Changed CAN/CVE link to point to cve.mitre.org instead of ICAT - Bugfix: Duplicate port 80 in nmap options if -p not specified but 80 specified in hosts file 05.28.2003 nikto_core.plugin 1.05 - Bugfix: -update code prevented automatic updates. Found & fixed by Keith Young. Also reported by Paul Worshaw. 05.27.2003 Nikto 1.30 release General changes - removed nikto_google.plugin entirely (may add better plugin later) - major "under the hood" changes to make things easier to maintain, read & modify - killed as many global vars as I could stand in favor of a few global hashes (CLI input, etc.) - added $CURRENT_HOST_ID and $CURRENT_PORT as globals--these are the pointers to "where you are" (mostly as in $TARGETS) - added the ability to have basic conditional items for tests, i.e. "200!index" to designate a response of "200" but the content does not contain "index" (suggested by Paul Woroshow). - added -V option, which displays versions of all code files & databases (suggested by Jericho) - specifying -ssl now forces *all ports* on *all servers* to use ssl. best that can be done for now. - added multi-host support via a text file with port specification in the file or via CLI - all new save file routines - unbuffered file output to keep partial/cancelled run data - removed the -w option in favor of -F with multiple formats - added support for NTLM authentication - added cgiwrap plugin nikto_core.plugin 1.05 - Many updates to support multiple host scans - Added UA for update agents - Changed all %SERVER hash refs to either %CLI or %TARGETS - Removed %BANNERS (now in %TARGETS) - Added set_targets() to handle various target input methods - Bugfix: non-SSL ports not found after first SSL port found on a host - Bugfix: authentication realms were not checked with the proper root if -r was specified on the CLI - Bugfix: can't call 'fprint' if core plugin is not found (duh!). Found by Erwin Paternotte. nikto_user_enum_cgiwrap.plugin 1.00 - added nikto_mutate.plugin 1.05 - change for using %CLI nikto_passfiles.plugin 1.01 - change for using %CLI nikto_user_enum_apache.plugin 1.01 - change for using %CLI - renamed from 'nikto_userenum.plugin' nikto_msgs.plugin 1.03 - minor changes for multi-host support plugins_order.txt 1.03 - removed nikto_google.plugin 02.23.2003 nikto_core.plugin 1.04 - Added a work around for servers that answer with blank www-authenticate headers with invalid id/pass combos nikto_realms.plugin 1.00 - Added to distro realms.db 1.00 - Added to distro plugins_order.txt 1.02 - Added nikto_realms.plugin 01.22.2003 nikto_httpoptions.plugin 1.03 - standardized wording, added TRACE option, added more description to WebDAV msgs (thanks Jericho at attrition.org). 01.22.2003 nikto_core.plugin 1.03 - fixed a bug with matching proper server categories, thanks to Paul Woroshow. 01.17.2003 nikto_core.plugin 1.02 - fixed the GetOptions only looking for "-gener" instead of "-generic", thanks to Michel Arboi 01.02.2003 nikto_core.plugin 1.01 - fixed proxy authentication not prompting for -update option 01.01.2003 Nikto 1.23 - added nikto_plugin_order.txt to force plugin order to something we want rather than alpha - added nikto_core.plugin & removed most functions from nikto.pl - added -cookies option - enhanced db syntax error checking (spurred by syntax problems Thomas Reinke found) - started using the LW 1.6 libraries - fixed infinite loop output problem (no longer wrapping long lines) - removed usage from saved output (too long) - remove nikto_frontpage.plugin and put checks in scan_database.db - moved server categories from scan_database.db to servers.db - got rid of the leading "c," requirement from scan_database.db - added STATIC-COOKIE config item as suggested by Eyal Udassin - made CLI options case sensitive (to support more options, hosts files, etc) - added Javier Fernandez-Sanguino Pen~a's Apache user enumeration plugin - added -r (-root) file prepend as suggested by Eyal Udassin - many DB typo fixes from Jay Swofford - fixed a regex bug in nikto_robots.plugin and nikto_apacheusers.plugin - new update location (path) to better support upgrades that don't effect db syntax 08.21.2002 Nikto 1.21 - Fixed all the proxy code--none of it was working due to where it was set in the initialization. - Added -update to the help output. Not sure why it wasn't there. 08.12.2002 Nikto 1.20 - Re-packaged to take out a testing line from LW.pm. Thanks to D Rhoades for the catch 08.11.2002 Nikto 1.20 - Moved all mutate options to plugins - Added password file mutate plugin - Added better error messages if problems arise - Test for false-positives on all CGI directories - Added -useproxy CLI - Printing SSL certs the server accepts - Fixed port sorting if -f is used - Forked 1.20DCX edition for DefCon 10 CD: difference is only output - Fixed a bug where "findonly" was referenced as "findports" (thanks J DePriest) - Added properly wrapped text output in saved files 05.25.2002 Nikto 1.100 - stopped nikto from dying if no config.txt file found - added Apache user enumeration plugin - added robots.txt plugin - set false-positive message to display at end of run as well as during 04.23.2002 Nikto 1.10BETA_3 - fixed CAN/CVE links, added BID/CA/MS links (suggested by Jericho). - prints total number of 'issues' found (suggested by Jericho). - fixed proxy usage in the cirt.net update function. - updated to use LW 1.4, which fixes an SSL infinite loop problem. - fixed 401 auth suppression (broken in beta 2). - added robots plugin to examine robots.txt & add items found to the mutate check 03.31.2002 Nikto 1.10BETA_2 - fixed the config.txt DEFAULTHTTPVER variable setting so it really works - made proxy_check run only once per session - removed all reference to "nikto" in the scan_database.db 03.23.2002 Nikto 1.10BETA_1 - renamed plugins from .pl to .plugin, just for clarity. but they're still perl files - allowed nikto.pl to update plugins the same as .db files - usage of LW 1.2 - countless "under the hood" type things - lowercase-incoming-headers to more easily handle case sensitive nonsense - compartmentalized a LOT more code to make things easier to read - created config.txt file configuration w/o midifying nikto.pl itself - added user_scan_database.db so that it won't get ovwr-written if the user adds checks - enabled RFP's LibWhisker anti-ids options - change "check," to "c," in scan_database, just to save a little bandwidth on cirt.net :) - added plugin to check HTTP methods - created a 'mutate' mode for really brute force finding stuff on servers - added the ability to set default CLI options via config file - added PLUGINDIR config variable - added plugin to check other HTTP headers (just x-powered-by for now) - added ability for nikto to auto-determine ssl v non-ssl on a port - added port scanning ability (with or without nmap) - added ability to send message via the update script's versions.txt file. I don't know why, but it may be handy to let folks know if a new beta is out, or something. - implemented the virtual host headers as patched by Pasi Eronen 01.17.2002 Nikto 1.018 - Added /mpcgi/ to the @CGIDIRS array based on some suggestions. - Fixed a bug in the auth_check function (thanks RFP), and cleaned up error reporting on failed auths 01.12.2002 Nikto 1.017 - Fixed a bug where the data portion of a request did not reset to null after some checks (thanks to Phil Brass for pointing me at it & letting me test against his server). 01.10.2002 Nikto 1.016 - Add dump_*hash functions - Added pause (-x) in scan loop - Fixed a bug which caused a major slowdown - Added load_conf for setup for configuration files (future) - Fixed http vs. https links in output files 01.08.2002 Nikto 1.015 - Fixed a bug (?) in Libwhisker PR4 (will check v1 code...) - Corrected an error which caused a few false-positives (404 really IS not found :) 01.07.2002 Nikto 1.014 - Removed comment filtering from lines in scan_database.db to accommodate SSI includes - Fixed quoting removal for data portions in checks (so " is valid). 01.06.2002 Nikto 1.013 - Made major globabl variable changes, moved tons of them to hashes - Wrote some basic plugin writing documentation & added 'docs' directory 01.03.2002 Nikto 1.012 - Added extended output for scan archival reasons (suggested by Steve Saady) - Changed host auth failure to a warning, not stoppage - Added "data" portion to scan_database.db - Added @IP and @HOSTNAME substitutions for scan_database.db checks (will be replaced by actual IP/hostname) - in case they are needed in the future. - Added JUNK() to scan_database.db checks to facilitate future buffer-overflows (non-DoS), and future DoS plugins - Added Proxy-agent as valid the same as Server result strings - Changed -l to -n ("nolookup") to be more accurate 01.02.2002 Nikto 1.011 - Added proxy auth for db update requests (oops). - Started .xxx version numbering scheme to make life easier - Fixed href tags in HTM output (< and > encoding and target host/ip) - Added "caseless" WWW-Authenticate finding (for iPlanet Proxy) 12.31.2001 Nikto 1.01 - Added regex to remove comments from scan_database.db in case they ever exist - Fixed extra 'Host:' line being sent to server (duh). - Fixed non 'GET' request data posting (duh). - Added -timeout option 12.27.2001 Nikto 1.00 - Finalized beta version for release ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nikto-2.1.4/docs/LICENSE.txt������������������������������������������������������������������������0000664�0000000�0000000�00000027574�11554552544�0015446�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ������������������������������������������������������������������������������������������������������������������������������������nikto-2.1.4/docs/nikto.1����������������������������������������������������������������������������0000664�0000000�0000000�00000023144�11554552544�0015016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" Title: nikto .\" Author: .\" Generator: DocBook XSL Stylesheets v1.73.2 <http://docbook.sf.net/> .\" Date: 01/19/2010 .\" Manual: .\" Source: .\" .TH "NIKTO" "1" "01/19/2010" "" "" .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .SH "NAME" nikto \- Scan web server for known vulnerabilities .SH "SYNOPSIS" .HP 21 \fB/usr/local/bin/nikto\fR [options...] .SH "DESCRIPTION" .PP Examine a web server to find potential problems and security vulnerabilities, including: .sp .RS 4 \h'-04'\(bu\h'+03'Server and software misconfigurations .RE .sp .RS 4 \h'-04'\(bu\h'+03'Default files and programs .RE .sp .RS 4 \h'-04'\(bu\h'+03'Insecure files and programs .RE .sp .RS 4 \h'-04'\(bu\h'+03'Outdated servers and programs .RE .PP Nikto is built on LibWhisker (by RFP) and can run on any platform which has a Perl environment\&. It supports SSL, proxies, host authentication, IDS evasion and more\&. It can be updated automatically from the command\-line, and supports the optional submission of updated version data back to the maintainers\&. .SH "OPTIONS" .PP Below are all of the Nikto command line options and explanations\&. A brief version of this text is available by running Nikto with the \-h (\-help) option\&. .PP \fB\-Cgidirs\fR .RS 4 Scan these CGI directories\&. Special words "none" or "all" may be used to scan all CGI directories or none, (respectively)\&. A literal value for a CGI directory such as "/cgi\-test/" may be specified (must include trailing slash)\&. If this is option is not specified, all CGI directories listed in config\&.txt will be tested\&. .RE .PP \fB\-config\fR .RS 4 Specify an alternative config file to use instead of the config\&.txt located in the install directory\&. .RE .PP \fB\-dbcheck\fR .RS 4 Check the scan databases for syntax errors\&. .RE .PP \fB\-Display\fR .RS 4 Control the output that Nikto shows\&. See Chapter 5 for detailed information on these options\&. Use the reference number or letter to specify the type, multiple may be used: .sp 1 \- Show redirects .sp 2 \- Show cookies received .sp 3 \- Show all 200/OK responses .sp 4 \- Show URLs which require authentication .sp D \- Debug Output .sp V \- Verbose Output .RE .PP \fB\-evasion\fR .RS 4 Specify the LibWhisker IDS evasion technique to use (see the LibWhisker docs for detailed information on these)\&. Use the reference number to specify the type, multiple may be used: .sp 1 \- Random URI encoding (non\-UTF8) .sp 2 \- Directory self\-reference (/\&./) .sp 3 \- Premature URL ending .sp 4 \- Prepend long random string .sp 5 \- Fake parameter .sp 6 \- TAB as request spacer .sp 7 \- Change the case of the URL .sp 8 \- Use Windows directory separator (\e) .RE .PP \fB\-findonly\fR .RS 4 Only discover the HTTP(S) ports, do not perform a security scan\&. This will attempt to connect with HTTP or HTTPS, and report the Server header\&. .RE .PP \fB\-Format\fR .RS 4 Save the output file specified with \-o (\-output) option in this format\&. If not specified, the default will be taken from the file extension specified in the \-output option\&. Valid formats are: .sp csv \- a comma\-seperated list .sp htm \- an HTML report .sp txt \- a text report .sp xml \- an XML report .RE .PP \fB\-host\fR .RS 4 Host(s) to target\&. Can be an IP address, hostname or text file of hosts\&. A single dash (\-) maybe used for stdout\&. Can also parse nmap \-oG style output .RE .PP \fB\-Help\fR .RS 4 Display extended help information\&. .RE .PP \fB\-id\fR .RS 4 ID and password to use for host Basic host authentication\&. Format is "id:password"\&. .RE .PP \fB\-list\-plugins\fR .RS 4 Will list all plugins that Nikto can run against targets and then will exit without performing a scan\&. These can be tuned for a session using the \-plugins option\&. .sp The output format is: .sp Plugin \fIname\fR .sp \ \&\fIfull name\fR \- \fIdescription\fR .sp \ \&Written by \fIauthor\fR, Copyright (C) \fIcopyright\fR .RE .PP \fB\-mutate\fR .RS 4 Specify mutation technique\&. A mutation will cause Nikto to combine tests or attempt to guess values\&. These techniques may cause a tremendous amount of tests to be launched against the target\&. Use the reference number to specify the type, multiple may be used: .sp 1 \- Test all files with all root directories .sp 2 \- Guess for password file names .sp 3 \- Enumerate user names via Apache (/~user type requests) .sp 4 \- Enumerate user names via cgiwrap (/cgi\-bin/cgiwrap/~user type requests) .sp 5 \- Attempt to brute force sub\-domain names, assume that the host name is the parent domain .sp 6 \- Attempt to guess directory names from the supplied dictionary file .RE .PP \fB\-mutate\-options\fR .RS 4 Provide extra information for mutates, e\&.g\&. a dictionary file .RE .PP \fB\-nolookup\fR .RS 4 Do not perform name lookups on IP addresses\&. .RE .PP \fB\-nossl\fR .RS 4 Do not use SSL to connect to the server\&. .RE .PP \fB\-no404\fR .RS 4 Disable 404 (file not found) checking\&. This will reduce the total number of requests made to the webserver and may be preferable when checking a server over a slow link, or an embedded device\&. This will generally lead to more false positives being discovered\&. .RE .PP \fB\-output\fR .RS 4 Write output to the file specified\&. The format used will be taken from the file extension\&. This can be over\-riden by using the \-Format option (e\&.g\&. to write text files with a different extenstion\&. Existing files will have new information appended\&. .RE .PP \fB\-plugins\fR .RS 4 Select which plugins will be run on the specified targets\&. A comma separated list should be provided which lists the names of the plugins\&. The names can be found by using \-list\-plugins\&. .sp There are two special entries: ALL, which specifies all plugins shall be run and NONE, which specifies no plugins shall be run\&. The default is ALL .RE .PP \fB\-port\fR .RS 4 TCP port(s) to target\&. To test more than one port on the same host, specify the list of ports in the \-p (\-port) option\&. Ports can be specified as a range (i\&.e\&., 80\-90), or as a comma\-delimited list, (i\&.e\&., 80,88,90)\&. If not specified, port 80 is used\&. .RE .PP \fB\-Pause\fR .RS 4 Seconds to delay between each test\&. .RE .PP \fB\-root\fR .RS 4 Prepend the value specified to the beginning of every request\&. This is useful to test applications or web servers which have all of their files under a certain directory\&. .RE .PP \fB\-ssl\fR .RS 4 Only test SSL on the ports specified\&. Using this option will dramatically speed up requests to HTTPS ports, since otherwise the HTTP request will have to timeout first\&. .RE .PP \fB\-Single\fR .RS 4 Perform a single request to a target server\&. Nikto will prompt for all options which can be specified, and then report the detailed output\&. See Chapter 5 for detailed information\&. .RE .PP \fB\-timeout\fR .RS 4 Seconds to wait before timing out a request\&. Default timeout is 10 seconds\&. .RE .PP \fB\-Tuning\fR .RS 4 Tuning options will control the test that Nikto will use against a target\&. By default, if any options are specified, only those tests will be performed\&. If the "x" option is used, it will reverse the logic and exclude only those tests\&. Use the reference number or letter to specify the type, multiple may be used: .sp 0 \- File Upload .sp 1 \- Interesting File / Seen in logs .sp 2 \- Misconfiguration / Default File .sp 3 \- Information Disclosure .sp 4 \- Injection (XSS/Script/HTML) .sp 5 \- Remote File Retrieval \- Inside Web Root .sp 6 \- Denial of Service .sp 7 \- Remote File Retrieval \- Server Wide .sp 8 \- Command Execution / Remote Shell .sp 9 \- SQL Injection .sp a \- Authentication Bypass .sp b \- Software Identification .sp c \- Remote Source Inclusion .sp x \- Reverse Tuning Options (i\&.e\&., include all except specified) .sp The given string will be parsed from left to right, any x characters will apply to all characters to the right of the character\&. .RE .PP \fB\-useproxy\fR .RS 4 Use the HTTP proxy defined in the configuration file\&. .RE .PP \fB\-update\fR .RS 4 Update the plugins and databases directly from cirt\&.net\&. .RE .PP \fB\-Version\fR .RS 4 Display the Nikto software, plugin and database versions\&. .RE .PP \fB\-vhost\fR .RS 4 Specify the Host header to be sent to the target\&. .RE .SH "FILES" .PP \fInikto\&.conf\fR .RS 4 The Nikto configuration file\&. This sets Nikto\'s global options\&. Several nikto\&.conf files may exist and are parsed in the below order\&. As each configuration file is loaded is supersedes any previously set configuration: .sp .RS 4 \h'-04'\(bu\h'+03'System wide (e\&.g\&. /etc/nikto\&.conf) .RE .sp .RS 4 \h'-04'\(bu\h'+03'Home directory (e\&.g\&. $HOME/nikto\&.conf) .RE .sp .RS 4 \h'-04'\(bu\h'+03'Current directory (e\&.g\&. \&./nikto\&.conf) .RE .RE .PP \fI${NIKTO_DIR}/plugins/db*\fR .RS 4 db files are the databases that nikto uses to check for vulnerabilities and issues within the web server\&. .RE .PP \fI${NIKTO_DIR}/plugins/*\&.plugin\fR .RS 4 All nikto\'s plugins exist here\&. Nikto itself is just a wrapper script to manage CLI and pass through to the plugins\&. .RE .PP \fI${NIKTO_DIR}/templates\fR .RS 4 Contains the templates for nikto\'s output formats\&. .RE .SH "BUGS" .PP The current features are not supported: .sp .RS 4 \h'-04'\(bu\h'+03'SOCKS Proxies .RE .SH "AUTHORS" .PP Nikto was originally written and maintained by Sullo, CIRT, Inc\&. It is currently maintained by David Lodge\&. See the main documentation for other contributors\&. .PP All code is (C) CIRT, Inc\&., except LibWhisker which is (C) rfp\&.labs (wiretrip\&.net)\&. Other portions of code may be (C) as specified\&. .SH "SEE ALSO" .PP \fINikto Homepage\fR\&[1] .SH "NOTES" .IP " 1." 4 Nikto Homepage .RS 4 \%http://www.cirt.net/ .RE ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nikto-2.1.4/docs/nikto.dtd��������������������������������������������������������������������������0000664�0000000�0000000�00000002677�11554552544�0015441�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!ELEMENT niktoscan (scandetails*,statistics?)> <!ATTLIST niktoscan hoststest CDATA #IMPLIED> <!ATTLIST niktoscan options CDATA #REQUIRED> <!ATTLIST niktoscan version CDATA #REQUIRED> <!ATTLIST niktoscan scanstart CDATA #REQUIRED> <!ATTLIST niktoscan scanend CDATA #REQUIRED> <!ATTLIST niktoscan scanelapsed CDATA #REQUIRED> <!ATTLIST niktoscan nxmlversion CDATA #REQUIRED> <!ELEMENT scandetails (item*,statistics?,ssl?)> <!ATTLIST scandetails targetip CDATA #REQUIRED> <!ATTLIST scandetails targethostname CDATA #REQUIRED> <!ATTLIST scandetails targetport CDATA #REQUIRED> <!ATTLIST scandetails targetbanner CDATA #REQUIRED> <!ATTLIST scandetails starttime CDATA #REQUIRED> <!ATTLIST scandetails sitename CDATA #REQUIRED> <!ATTLIST scandetails siteip CDATA #REQUIRED> <!ATTLIST scandetails hostheader CDATA #IMPLIED> <!ELEMENT ssl EMPTY> <!ATTLIST ssl ciphers CDATA #IMPLIED> <!ATTLIST ssl issuers CDATA #REQUIRED> <!ATTLIST ssl info CDATA #REQUIRED> <!ELEMENT item (description,uri?,namelink?,iplink?)> <!ATTLIST item id CDATA #REQUIRED> <!ATTLIST item osvdbid CDATA #IMPLIED> <!ATTLIST item osvdblink CDATA #IMPLIED> <!ATTLIST item method CDATA #IMPLIED> <!ELEMENT description ANY> <!ELEMENT uri ANY> <!ELEMENT namelink ANY> <!ELEMENT iplink ANY> <!ELEMENT statistics EMPTY> <!ATTLIST statistics itemstested CDATA #IMPLIED> <!ATTLIST statistics itemsfound CDATA #IMPLIED> <!ATTLIST statistics elapsed CDATA #IMPLIED> <!ATTLIST statistics endtime CDATA #IMPLIED> �����������������������������������������������������������������nikto-2.1.4/docs/nikto_manual.html������������������������������������������������������������������0000664�0000000�0000000�00000326336�11554552544�0017170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Nikto v2.1.0 - The Manual

Nikto v2.1.0 - The Manual


Chapter 1. Introduction

Overview

Nikto is a web server assessment tool. It is designed to find various default and insecure files, configurations and programs on any type of web server.

Description

Examine a web server to find potential problems and security vulnerabilities, including:

  • Server and software misconfigurations

  • Default files and programs

  • Insecure files and programs

  • Outdated servers and programs

Nikto is built on LibWhisker (by RFP) and can run on any platform which has a PERL environment. It supports SSL, proxies, host authentication, IDS evasion and more. It can be updated automatically from the command-line, and supports the optional submission of updated version data back to the maintainers.

The name "Nikto" is taken from the movie "The Day the Earth Stood Still", and of course subsequent abuse by Bruce Campbell in "Army of Darkness". More information on the pop-culture popularity of Nikto can be found at http://www.blather.net/blather/2005/10/klaatu_barada_nikto_the_day_th.html

Advanced Error Detection Logic

Most web security tools, (including Nikto 1.32 and below), rely heavily on the HTTP response to determine if a page or script exists on the target. Because many servers do not properly adhere to RFC standards and return a 200 "OK" response for requests which are not found or forbidden, this can lead to many false-positives. In addition, error responses for various file extensions can differ--the "not found" response for a .html file is often different than a .cgi.

Some testing tools, such as Nessus, also look at the content of the response to help eliminate these false positives. While often effective, this method relies on pre-defined strings to help eliminate false positives.

As of version 2.0 Nikto no longer assumes the error pages for different file types will be the same. A list of unique file extensions is generated at run-time (from the test database), and each of those extensions is tested against the target. For every file type, the "best method" of determining errors is found: standard RFC response, content match or MD4 hash (in decreasing order of preference). This allows Nikto to use the fastest and most accurate method for each individual file type, and therefore help eliminate the false positives seen for some servers in version 1.32 and below.

For example, if a server responds with a 404 "not found" error for a non-existent .txt file, Nikto will match the HTTP response of "404" on tests. If the server responds with a 200 "OK" response, it will try to match on the content, and assuming it finds a match (for example, the words "could not be found"), it will use this method for determining missing .txt files. If the other methods fail, Nikto will attempt to remove date and time strings (which can constantly change) from the returned page's content, generate an MD5 hash of the content, and then match that hash value against future .txt tests. The latter is by far the slowest type of match, but in many cases will provide valid results for a particular file type.

History

The Nikto 1.00 Beta was released on December 27, 2001, (followed almost immediately by the 1.01 release). Over the course of two years Nikto's code evolved into the most popular freely available web vulnerability scanner. The 2.0 release, in November, 2007 represents several years of improvements.

In 2008, due to other commitments, Sullo, the original author couldn't continue to support Nikto and the code was released under the GPL and passed to the community for support.

Chapter 2. Installation

Table of Contents

Requirements
Install

Requirements

Any system which supports a basic PERL installation should allow Nikto to run. It has been extensively tested on:

  • Windows (using ActiveState Perl)

  • Mac OSX

  • Various Linux and Unix installations (including RedHat, Solaris, Debian, Knoppix, etc.)

The only required PERL module that does not come standard is LibWhisker. Nikto comes with and is configured to use a local LW.pm file (in the plugins directory), but users may wish to change Nikto to use a version installed on the system. See Section 2 for further information.

For SSL support the Net::SSLeay PERL module must be installed (which in turn requires OpenSSL on the Unix platform). Windows support for SSL is dependent on the installation package, but is rumored to exist for ActiveState's Perl.

The nmap scanner can also be used, if desired. In some cases using nmap will slow down Nikto execution, as it must call an external program. For scanning many ports across one or more servers, using nmap will be faster than using Nikto's internal PERL scanning.

Install

These instructions do not include information on installing PERL, PERL Modules, OpenSSL, LibWhisker or any of the utilities that may be needed during installation (such as gzip, tar, etc.). Please see the distributor's documentation for information on how to install and configure those software packages.

Unpack the download file:

tar -xvfz nikto-current.tar.gz

Assuming a standard OS/PERL installation, Nikto should now be usable. See Chapter 4 (Options) or Chapter 8 (Troubleshooting) for further configuration information.

Chapter 3. Usage

Basic Testing

The most basic Nikto scan requires simply a host to target, since port 80 is assumed if none is specified. The host can either be an IP or a hostname of a machine, and is specified using the -h (-host) option. This will scan the IP 192.168.0.1 on TCP port 80:

perl nikto.pl -h 192.168.0.1

To check on a different port, specify the port number with the -p (-port) option. This will scan the IP 192.168.0.1 on TCP port 443:

perl nikto.pl -h 192.168.0.1 -p 443

Hosts, ports and protocols may also be specified by using a full URL syntax, and it will be scanned:

perl nikto.pl -h https://192.168.0.1:443/

There is no need to specify that port 443 may be SSL, as Nikto will first test regular HTTP and if that fails, HTTPS. If you are sure it is an SSL server, specifying -s (-ssl) will speed up the test.

perl nikto.pl -h 192.168.0.1 -p 443 -ssl
[Note]Note

-mutate 1 increases the number of tests so that all filenames are tested against all databases inc db_tests. This will produce over 2,000,000 extra tests, which will use up a massive amount of resource.

More complex tests can be performed using the -mutate parameter, as detailed later. This can produce extra tests, some of which may be provided with extra parameters through the -mutate-options parameter. For example, using -mutate 3, with or without a file attempts to brute force usernames if the web server allows ~user URIs:

perl nikto.pl -h 192.168.0.1 -mutate 3 -mutate-options user-list.txt

Multiple Port Testing

Nikto can scan multiple ports in the same scanning session. To test more than one port on the same host, specify the list of ports in the -p (-port) option. Ports can be specified as a range (i.e., 80-90), or as a comma-delimited list, (i.e., 80,88,90). This will scan the host on ports 80, 88 and 443.

perl nikto.pl -h 192.168.0.1 -p 80,88,443

Multiple Host Testing

Nikto support scanning multiple hosts in the same session via a text file of host names or IPs. Instead of giving a host name or IP for the -h (-host) option, a file name can be given. A file of hosts must be formatted as one host per line, with the port number(s) at the end of each line. Ports can be separated from the host and other ports via a colon or a comma. If no port is specified, port 80 is assumed.

This is an example of a valid hosts file:

Example 3.1. Valid Hosts File

192.168.0.1:80
http://192.168.0.1:8080/
192.168.0.3

[Note]Note

For win32 users: due to peculiaries in the way that cmd.exe works with pipes, the above example may not work for you. In this case a temporary file will have to be used to store the output from nmap

A host file may also be an nmap output in "greppable" format (i.e. from the output from -oG).

A file may be passed to Nikto through stdout/stdin using a "-" as the filename. For example:

nmap -p80 192.168.0.0/24 -oG - | nikto.pl -h -

Using a Proxy

If the machine running Nikto only has access to the target host (or update server) via an HTTP proxy, the test can still be performed. Set the PROXY* variables (as described in section 4), then execute Nikto with the -u (-useproxy) command. All connections will be relayed through the HTTP proxy specified in the configuration file.

perl nikto.pl -h 192.168.0.1 -p 80 -u

Updating

Nikto can be automatically updated, assuming you have Internet connectivity from the host Nikto is installed on. To update to the latest plugins and databases, simply run Nikto with the -update command.

[Note]Note

The -update option cannot be abbreviated.

perl nikto.pl -update

If updates are required, you will see a list of the files downloaded:

 perl nikto.pl -update
 + Retrieving 'nikto_core.plugin'
 + Retrieving 'CHANGES.txt'
      

Updates may also be manually downloaded from http://www.cirt.net/

Integration with Nessus

Nessus (http://www.nessus.org/nessus/) can be configured to automatically launch Nikto when it finds a web server. Ensure Nikto works properly, then place the directory containing nikto.pl in root's PATH environment variable. When nessusd starts, it should see the nikto.pl program and enable usage through the GUI.

Chapter 4. Command Line Options

All Options

Below are all of the Nikto command line options and explanations. A brief version of this text is available by running Nikto with the -h (-help) option.

-Cgidirs

Scan these CGI directories. Special words "none" or "all" may be used to scan all CGI directories or none, (respectively). A literal value for a CGI directory such as "/cgi-test/" may be specified (must include trailing slash). If this is option is not specified, all CGI directories listed in config.txt will be tested.

-config

Specify an alternative config file to use instead of the config.txt located in the install directory.

-dbcheck

Check the scan databases for syntax errors.

-Display

Control the output that Nikto shows. See Chapter 5 for detailed information on these options. Use the reference number or letter to specify the type, multiple may be used:

1 - Show redirects

2 - Show cookies received

3 - Show all 200/OK responses

4 - Show URLs which require authentication

D - Debug Output

V - Verbose Output

-evasion

Specify the LibWhisker IDS evasion technique to use (see the LibWhisker docs for detailed information on these). Use the reference number to specify the type, multiple may be used:

1 - Random URI encoding (non-UTF8)

2 - Directory self-reference (/./)

3 - Premature URL ending

4 - Prepend long random string

5 - Fake parameter

6 - TAB as request spacer

7 - Change the case of the URL

8 - Use Windows directory separator (\)

-findonly

Only discover the HTTP(S) ports, do not perform a security scan. This will attempt to connect with HTTP or HTTPS, and report the Server header.

-Format

Save the output file specified with -o (-output) option in this format. If not specified, the default will be taken from the file extension specified in the -output option. Valid formats are:

csv - a comma-seperated list

htm - an HTML report

txt - a text report

xml - an XML report

-host

Host(s) to target. Can be an IP address, hostname or text file of hosts. A single dash (-) maybe used for stdout. Can also parse nmap -oG style output

-Help

Display extended help information.

-id

ID and password to use for host Basic host authentication. Format is "id:password".

-list-plugins

Will list all plugins that Nikto can run against targets and then will exit without performing a scan. These can be tuned for a session using the -plugins option.

The output format is:

Plugin name

 full name - description

 Written by author, Copyright (C) copyright

-mutate

Specify mutation technique. A mutation will cause Nikto to combine tests or attempt to guess values. These techniques may cause a tremendous amount of tests to be launched against the target. Use the reference number to specify the type, multiple may be used:

1 - Test all files with all root directories

2 - Guess for password file names

3 - Enumerate user names via Apache (/~user type requests)

4 - Enumerate user names via cgiwrap (/cgi-bin/cgiwrap/~user type requests)

5 - Attempt to brute force sub-domain names, assume that the host name is the parent domain

6 - Attempt to guess directory names from the supplied dictionary file

-mutate-options

Provide extra information for mutates, e.g. a dictionary file

-nolookup

Do not perform name lookups on IP addresses.

-nossl

Do not use SSL to connect to the server.

-no404

Disable 404 (file not found) checking. This will reduce the total number of requests made to the webserver and may be preferable when checking a server over a slow link, or an embedded device. This will generally lead to more false positives being discovered.

-output

Write output to the file specified. The format used will be taken from the file extension. This can be over-riden by using the -Format option (e.g. to write text files with a different extenstion. Existing files will have new information appended.

-plugins

Select which plugins will be run on the specified targets. A comma separated list should be provided which lists the names of the plugins. The names can be found by using -list-plugins.

There are two special entries: ALL, which specifies all plugins shall be run and NONE, which specifies no plugins shall be run. The default is ALL

-port

TCP port(s) to target. To test more than one port on the same host, specify the list of ports in the -p (-port) option. Ports can be specified as a range (i.e., 80-90), or as a comma-delimited list, (i.e., 80,88,90). If not specified, port 80 is used.

-Pause

Seconds to delay between each test.

-root

Prepend the value specified to the beginning of every request. This is useful to test applications or web servers which have all of their files under a certain directory.

-ssl

Only test SSL on the ports specified. Using this option will dramatically speed up requests to HTTPS ports, since otherwise the HTTP request will have to timeout first.

-Single

Perform a single request to a target server. Nikto will prompt for all options which can be specified, and then report the detailed output. See Chapter 5 for detailed information.

-timeout

Seconds to wait before timing out a request. Default timeout is 10 seconds.

-Tuning

Tuning options will control the test that Nikto will use against a target. By default, if any options are specified, only those tests will be performed. If the "x" option is used, it will reverse the logic and exclude only those tests. Use the reference number or letter to specify the type, multiple may be used:

0 - File Upload

1 - Interesting File / Seen in logs

2 - Misconfiguration / Default File

3 - Information Disclosure

4 - Injection (XSS/Script/HTML)

5 - Remote File Retrieval - Inside Web Root

6 - Denial of Service

7 - Remote File Retrieval - Server Wide

8 - Command Execution / Remote Shell

9 - SQL Injection

a - Authentication Bypass

b - Software Identification

c - Remote Source Inclusion

x - Reverse Tuning Options (i.e., include all except specified)

The given string will be parsed from left to right, any x characters will apply to all characters to the right of the character.

-useproxy

Use the HTTP proxy defined in the configuration file.

-update

Update the plugins and databases directly from cirt.net.

-Version

Display the Nikto software, plugin and database versions.

-vhost

Specify the Host header to be sent to the target.

Mutation Techniques

A mutation will cause Nikto to combine tests or attempt to guess values. These techniques may cause a tremendous amount of tests to be launched against the target. Use the reference number to specify the type, multiple may be combined.

  1. Test all files with all root directories. This takes each test and splits it into a list of files and directories. A scan list is then created by combining each file with each directory.

  2. Guess for password file names. Takes a list of common password file names (such as "passwd", "pass", "password") and file extensions ("txt", "pwd", "bak", etc.) and builds a list of files to check for.

  3. Enumerate user names via Apache (/~user type requests). Exploit a misconfiguration with Apache UserDir setups which allows valid user names to be discovered. This will attempt to brute-force guess user names. A file of known users can also be supplied by supplying the file name in the -mutate-options parameter.

  4. Enumerate user names via cgiwrap (/cgi-bin/cgiwrap/~user type requests). Exploit a flaw in cgiwrap which allows valid user names to be discovered. This will attempt to brute-force guess user names. A file of known users can also be supplied by supplying the file name in the -mutate-options parameter.

  5. Attempt to brute force sub-domain names. This will attempt to brute force know domain names, it will assume the given host (without a www) is the parent domain.

  6. Attempt to brute directory names. This is the only mutate option that requires a file to be passed in the -mutate-options parameter. It will use the given file to attempt to guess directory names. Lists of common directories may be found in the OWASP DirBuster project.

Display

By default only some basic information about the target and vulnerabilities is shown. Using the -Display parameter can produce more information for debugging issues.

  • 1 - Show redirects. This will display all requests which elicit a "redirect" response from the server.

  • 2 - Show cookies received. This will display all cookies that were sent by the remote host.

  • 3 - Show all 200/OK responses. This will show all responses which elicit an "okay" (200) response from the server. This could be useful for debugging.

  • 4 - Show URLs which require authentication. This will show all responses which elicit an "authorization required" header.

  • D - Debug Output. Show debug output, which shows the verbose output and extra information such as variable content.

  • V - Verbose Output. Show verbose output, which typically shows where Nikto is during program execution.

Scan Tuning

Scan tuning can be used to decrease the number of tests performed against a target. By specifying the type of test to include or exclude, faster, focused testing can be completed. This is useful in situations where the presence of certain file types are undesired -- such as XSS or simply "interesting" files.

Test types can be controlled at an individual level by specifying their identifier to the -T (-Tuning) option. In the default mode, if -T is invoked only the test type(s) specified will be executed. For example, only the tests for "Remote file retrieval" and "Command execution" can performed against the target:

perl nikto.pl -h 192.168.0.1 -T 58

If an "x" is passed to -T then this will negate all tests of types following the x. This is useful where a test may check several different types of exploit. For example:

perl nikto.pl -h 192.168.0.1 -T 58xb

The valid tuning options are:

  • 0 - File Upload. Exploits which allow a file to be uploaded to the target server.

  • 1 - Interesting File / Seen in logs. An unknown but suspicious file or attack that has been seen in web server logs (note: if you have information regarding any of these attacks, please contact CIRT, Inc.).

  • 2 - Misconfiguration / Default File. Default files or files which have been misconfigured in some manner. This could be documentation, or a resource which should be password protected.

  • 3 - Information Disclosure. A resource which reveals information about the target. This could be a file system path or account name.

  • 4 - Injection (XSS/Script/HTML). Any manner of injection, including cross site scripting (XSS) or content (HTML). This does not include command injection.

  • 5 - Remote File Retrieval - Inside Web Root. Resource allows remote users to retrieve unauthorized files from within the web server's root directory.

  • 6 - Denial of Service. Resource allows a denial of service against the target application, web server or host (note: no intentional DoS attacks are attempted).

  • 7 - Remote File Retrieval - Server Wide. Resource allows remote users to retrieve unauthorized files from anywhere on the target.

  • 8 - Command Execution / Remote Shell. Resource allows the user to execute a system command or spawn a remote shell.

  • 9 - SQL Injection. Any type of attack which allows SQL to be executed against a database.

  • a - Authentication Bypass. Allows client to access a resource it should not be allowed to access.

  • b - Software Identification. Installed software or program could be positively identified.

  • c - Remote source inclusion. Software allows remote inclusion of source code.

  • x - Reverse Tuning Options. Perform exclusion of the specified tuning type instead of inclusion of the specified tuning type.

Single Request Mode

Single request mode is designed to preform a solitary request against the target. This is useful to confirm a test result using the same resources Nikto used during a scan. The single option allows manual setting of most variables used by Nikto and LibWhisker, and upon completion will display both the request and the result of the operation.

Most options have a default value or can be left blank. The most common and required values are at the beginning of the "questions" section for slightly easier use. True and false are specified by numeric equivalents, 1 and 0 respectively. Please note that Single mode is not very user-friendly. Here is an example Nikto run with the -Single option.


[dave@yggdrasil nikto-2.03]$ ./nikto.pl -Single
--------------------------------------------  Nikto 2.1.0
--------------------------------------------  Single Request Mode
                              Hostname or IP: localhost
                                   Port (80):
                                     URI (/): /test.html
                                     SSL (0):
                                  Proxy host:
                                  Proxy port:
                      Show HTML Response (1):
                          HTTP Version (1.1):
                           HTTP Method (GET):
      User-Agent (Mozilla/4.75 (Nikto/2.1.0):
                     Connection (Keep-Alive):
                                        Data:
                        force_bodysnatch (0):
                             force_close (1):
                             http_space1 ( ):
                             http_space2 ( ):
                     include_host_in_uri (0):
           invalid_protocol_return_value (1):
                                max_size (0):
                             protocol (HTTP):
           require_newline_after_headers (0):
                                   retry (0):
                           ssl_save_info (0):
                                timeout (10):
                             uri_password ():
                              uri_postfix ():
                               uri_prefix ():
                                 uri_user ():
                         Enable Anti-IDS (0):
--------------------------------------------  Done with questions
        Host Name: localhost
        Host IP: 127.0.0.1
        HTTP Response Code: 404
--------------------------------------------  Connection Details
        Connection: Keep-Alive
        Host: localhost
        User-Agent: Mozilla/4.75 (Nikto/2.1.0
        data:
        force_bodysnatch: 0
        force_close: 1
        force_open: 0
        host: localhost
        http_space1:
        http_space2:
        ignore_duplicate_headers: 1
        include_host_in_uri: 0
        invalid_protocol_return_value: 1
        max_size: 0
        method: GET
        port: 80
        protocol: HTTP
        require_newline_after_headers: 0
        retry: 0
        ssl: 0
        ssl_save_info: 0
        timeout: 10
        trailing_slurp: 0
        uri: /test.html
        uri_param_sep: ?
        uri_postfix:
        uri_prefix:
        version: 1.1
--------------------------------------------  Response Headers
        Connection: close
        Content-Length: 268
        Content-Type: text/html; charset=iso-8859-1
        Date: Tue, 18 Aug 2009 10:13:57 GMT
        Server: Apache/2
        code: 404
        http_data_sent: 1
        http_eol:

        http_space1:
        http_space2:
        message: Not Found
        protocol: HTTP
        uri: /test.html
        version: 1.1
--------------------------------------------  Response Content
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /test.html was not found on this server.</p>
<hr>
<address>Apache/2 Server at localhost Port 80</address>
</body></html>

Chapter 5. Configuration Files

Table of Contents

Location
Format
Variables

Location

Nikto, like any non-trivial program needs to know a few things about how to work with the current environment. For most situations the default configuration file will work. Sometimes, tuning may be required, or some things may need to be changes.

Nikto will look for a configuration file in three places and if it finds one, will apply it in the strict order, listed below. A later found configuration file will overwrite any variables set in an earlier configuration file. The locations are:

  1. /etc/nikto.conf (this may be altered depending on platform)

  2. $HOME/nikto.conf

  3. nikto.conf

Format

The configuration files are formated like a standard Unix configuration file: blank lines are ignored, any line starting with a # is ignored, variables are set with VariableName=Value line.

Variables

The following variables may be set within the configuration file:

CLIOPTS

Default options that should always be passed to the command line. For example:

CLIOPTS=-output results.txt -Format text

Default Setting

CLIOPTS=
NIKTODTD

Path to the location of the DTD used for XML output. If the path is not absolute then it will be relative to the directory where Nikto is executed.

Default Setting

NIKTODTD=docs/nikto.dtd
NMAP, NMAPOPTS

Deprecated

Location of nmap and the default nmap options. Nikto used to use nmap to aid in checking for valid HTTP ports on any targets. From Nikto 2.10, nmap is no longer used from within Nikto and this variable will do nothing. This variable may be removed in a later version.

Default Setting

NMAP=/usr/local/bin/nmap
NMPOPTS=-P0
SKIPPORTS

Deprecated

This configuration item originally defined ports that would never be scanned by Nikto. This is currently unused and deprecated.

Default Setting

SKIPPORTS=21 111
SKIPIDS
[Note]Note

Note, this filter only applies to tests in the db_tests database

Contains a space separated list of Test IDs (tids) that Nikto will not run on the system, for example:

SKIPIDS=000045 000345

Default Setting

SKIPIDS=
DEFAULTHTTPVER

Defines the default version of HTTP that Nikto will use, unless superceded by a specific test. Usually keeping this to the default will suffice, though some web servers may only work with later versions of the HTTP protocol.

Default Setting

DEFAULTHTTPVER=1.0
UPDATES

If the outdated Nikto plugin sees a web server it doesn't know of, or a version that is later than that defined in db_outdated, then it will send this information back to cirt.net for inclusion in future versions of Nikto. Server specific information (e.g. IP addresses or hostnames) are not sent.

This item can be set to one of the below values:

UPDATES=yes

Display each submission and ask for permission before it is sent

UPDATES=no

Do not send any data back to cirt.net

UPDATES=auto

Send data back to cirt.net with no prompting

Default Setting

UPDATES=yes
MAX_WARN

Unused

Produces a warning of a number of MOVED responses are retrieved. This is currently unused.

Default Setting

MAX_WARN=20
PROMPTS

Deprecated

Disables Nikto prompts if set to "no". This is currently unused and has been deprecated by the UPDATES item.

Default Setting

PROMPTS=
CIRT

The IP address that Nikto will use to update the databases and plugins, or will send version information back to (as described in the UPDATES item).

Default Setting

CIRT=209.172.49.178
PROXYHOST, PROXYPORT, PROXYUSER, PROXYPASS

Address, port and username password of a proxy to relay all requests through. Note, to use a proxy, you must set the configuration items in the configuration file and supply the -useproxy switch to the command line.

Default Setting

PROXYHOST=
PROXYPORT=
PROXYUSER=
PROXYPASS=
STATIC-COOKIE

Adds the supplied cookie to all requests made via Nikto, this is generally useful is an authentication cookie is required for a website. For example:

STATIC-COOKIE=userid=0

Default Setting

STATIC-COOKIE=
CHECKMETHODS

Nikto will attempt to identify targets as webservers by sending a request to fetch the / URI via certain HTTP methods. Some web servers do not implement all HTTP methods and may cause Nikto to fail to identify the web server correctly if it doesn't support the method being used.

If this setting is missing from the configuration file, then Nikto will default back to the Nikto 2.02 default of HEAD.

Default Setting

CHECKMETHODS=HEAD GET
EXECDIR, PLUGINDIR, TEMPLATEDIR, DOCDIR

Defines where to find the location of Nikto, its plugins, XML/HTML templates and documents. This should only normally be changed if repackaging Nikto to work with different file system standards. Nikto will use the EXECDIR item to guess the other directories.

Default Setting

EXECDIR=.
PLUGINDIR=EXECDIR/plugins
TEMPLATEDIR=EXECDIR/templates
DOCDIR=EXECDIR/docs

Chapter 6. Output and Reports

Export Formats

Nikto saved output comes in four flavours: text, CSV, XML or HTML. When using -output, an output format may be specified with -Format. Text format is assumed if nothing is specified with -Format. The DTD for the Nikto XML format can be found in the 'docs' directory (nikto.dtd).

HTML and XML Customisation

HTML reports are generated from template files located in the templates directory. Variables are defined as #variable-name, and are replaced when the report is generated. The files htm_start.tmpl and htm_end.tmpl are included at the beginning and end of the report (respectively). The htm_summary.tmpl also appears at the beginning of the report. The htm_host_head appears once for every host, and the htm_host_item.tmpl and htm_host_im.tmpl appear once for each item found on a host and each "informational message" per host (respectively).

All valid variables are used in these templates. Future versions of this documentation will include a list of variables and their meaning.

The copyright statements must not be removed from the htm_end.tmpl without placing them in another of the templates. It is a violation of the Nikto licence to remove these notices.

Chapter 7. Test and Code Writing

Scan Database Field Values

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

Table 7.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 plugins 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 plugins/udb_tests and it will be loaded at the same time plugins/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

To allow a bit more flexibility, Nikto allows plugins so that there is easy expansion of existing capabilities and some future proofing.

Plugins are run in four different phases, these are:

Initialisation (mandatory)

Plugin initialisation 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.

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

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.

Initialisation 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.

recon_method (optional)

This should be a reference to a function used during the reconnaisance phase of the plugin's execution. If this is left undefined then the plugin will not execute during the reconnaisance phase.

recon_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.

recon_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.

scan_method (optional)

This should be a reference to a function used during the scan phase of the plugin's execution. If this is left undefined then the plugin will not execute during the scan phase.

scan_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.

scan_weight (optional)

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

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 7.1. Example initialisation function

 sub nikto_dictionary_attack_init
{
   my $id =
   {
      name         => "dictionary",
      full_name    => "Dictionary attack",
      author       => "Deity",
      description  => "Attempts to dictionary attack commonly known directories/files",
      recon_method => \&nikto_dictionary_attack,
      recon_cond   => '$CLI{mutate} =~ /6/',
      recon_weight => 20,
      copyright    => "2009 CIRT Inc"
   };

   return $id;
}  

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); 
hashref  mark;

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); 
hashref  mark;

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 7.2. Members of the Mark structure

ident Host identifier, usually equivalent to what was passed on the command line.
hostname Host name of the target.
ip IP address of the target.
port TCP port of the target.
display_name Either the hostname, or the IP address of the target, dependant on whether a hostname has been discovered.
ssl Flag 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.
vhost Virtual hostname to use for the target.
root Root URI to use for the target.
banner Banner of the target's web server.

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 7.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(); 
 ;

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 fetch(uri,  
 method,  
 content,  
 headers,  
 noclean); 
string  uri;
string  method;
string  content;
hashref  headers;
boolean  noclean;

Deprecated

Performs a simple HTTP request to URI using the HTTP method, method. content supplies any data to pass in the HTTP body. headers allows any custom headers to be placed in the request. noclean is a flag specifying that the request shouldn't be cleaned up before being sent (e.g. if the Host: header is blank).

string HTTPCode, string content nfetch(uri,  
 method,  
 content,  
 headers,  
 noclean); 
string  uri;
string  method;
string  content;
hashref  headers;
boolean  noclean;

An updated version of fetch that uses a local, rather than a global request/result structure. This should be used in preference to fetch.

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;

Initialises 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.

%ERRSTRINGS (read)

Hash to contain all the entries in db_404 - a list of strings that may indicate a 404.

%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.

$CONTENT (read) (write) (deprecated)

Global variable to store data from a fetch or nfetch. A local variable should be used instead

%NIKTO (read)

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

%REALMS (read)

Hash of data from db_realms.

%NIKTOCONFIG (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.

@BUILDITEMS (read) (write) (deprecated)

Array to hold information for tests to act on later. Use should be avoided, a local variable should be used instead.

$PROXYCHECKED (read)

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

$http_eol (read) (deprecated)

Contains the http end of line pattern.

@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.

%CACHE (read) (write)

Containing the URI cache, should only be read/written through nfetch. Members:

Table 7.4. Members of the cache structure

{uri}URI for the cache
{uri}{method}HTTP method used
{uri}{res}HTTP result for URI
{uri}{content}data for URI
{uri}{mark}mark hashref for URI

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 7.5. TID Scheme

000000db_tests
400000user defined tests (udb* files)
500000db_favicon
600000db_outdated
700000db_realms
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.

Chapter 8. Troubleshooting

Table of Contents

SOCKS Proxies
Debugging

SOCKS Proxies

Nikto does not currently support SOCKS proxies.

Debugging

The major route to debugging Nikto requests is to use the -Display with v (verbose) or d (debug). This will output a vast amount of extra information to the screen, so it is advised to redirect output to a file when using them.

Chapter 9. Licences

Table of Contents

Nikto
LibWhisker
Tests

Nikto

Nikto is licensed under the GNU General Public License (GPL), and copyrighted by CIRT, Inc.

LibWhisker

LibWhisker is licensed under the GNU General Public License (GPL), and copyrighted by Rain Forrest Puppy.

Tests

The web tests are licensed for use with Nikto only, and may not be reused without written consent from CIRT, Inc.

Chapter 10. Credits

Table of Contents

Nikto
Thanks

Nikto

Nikto was originally written and maintained by Sullo, CIRT, Inc. It is currently maintained by David Lodge. LibWhisker was written by Rain Forrest Puppy

Thanks

Many people have provided feedback, fixes, and suggestions. This list attempts to make note of those people, though not all contributors are listed. In no particular order:

  • Nikto 2 Testing: Paul Woroshow, Mark G. Spencer, Michel Arboi, Jericho, rfp

  • Jericho (attrition.org/OSVDB/OSF). Support/ideas/tests/corrections/spam and help matching OSVDB IDs to tests.

  • rfp (wiretrip.net). LibWhisker and continuing support.

  • Erik Cabetas for many updates and fixes.

  • Jake Kouns (OSVDB/OSF).

  • Jabra (spl0it.org) for XML DTD, XML templates and supporting code.

  • Stephen Valdez. Extensive testing. We all miss you.

  • S Saady. Extensive testing.

  • Zeno (cgisecurity.com). Nikto mirroring.

  • P Eronen (nixu.com). Provided many code fixes.

  • M Arboi. Great support by writing the code to make Nikto work within Nessus, as well as bug reports.

  • T Seyrat. Maintains Nikto for the Debian releases.

  • J DePriest. Ideas/fixes.

  • P Woroshow. Ideas/fixes.

  • fr0stman. Tests.

  • H Heimann. Tests.

  • Xiola (xiola.net). Web design and more.

  • Ryan Dewhurst. Domain guessing code.

This document is © 2009 CIRT, Inc. and may not be reused without permission.

nikto-2.1.4/nikto.conf000066400000000000000000000051601155455254400146510ustar00rootroot00000000000000######################################################################################################### # CONFIG STUFF # $Id: nikto.conf 632 2011-02-19 02:49:31Z sullo $ ######################################################################################################### # default command line options, can't be an option that requires a value. used for ALL runs. # CLIOPTS=-g -a # ports never to scan SKIPPORTS=21 111 # User-Agent variables: # @VERSION - Nikto version # @TESTID - Test identifier # @EVASIONS - List of active evasions USERAGENT=Mozilla/4.75 (Nikto/@VERSION) (Evasions:@EVASIONS) (Test:@TESTID) # RFI URL. This remote file should return a phpinfo call, for example: # You may use the one below, if you like. RFIURL=http://cirt.net/rfiinc.txt? # IDs never to alert on (Note: this only works for IDs loaded from db_tests) #SKIPIDS= # if Nikto is having difficulty finding the 'plugins', set the full install path here # EXECDIR=/usr/local/nikto # The DTD NIKTODTD=docs/nikto.dtd # the default HTTP version to try... can/will be changed as necessary DEFAULTHTTPVER=1.0 # Nikto can submit updated version strings to CIRT.net. It won't do this w/o permission. You should # send updates because it makes the data better for everyone ;) *NO* server specific information # such as IP or name is sent, just the relevant version information. # UPDATES=yes - ask before each submission if it should send # UPDATES=no - don't ask, don't send # UPDATES=auto - automatically attempt submission *without prompting* UPDATES=yes # Warning if MAX_WARN OK or MOVED responses are retrieved MAX_WARN=20 # Prompt... if set to 'no' you'll never be asked for anything. Good for automation. #PROMPTS=no # cirt.net : set the IP so that updates can work without name resolution -- just in case CIRT=174.142.17.165 # Proxy settings -- still must be enabled by -useproxy #PROXYHOST=127.0.0.1 #PROXYPORT=8080 #PROXYUSER=proxyuserid #PROXYPASS=proxypassword # Cookies: send cookies with all requests # Multiple can be set by separating with a semi-colon, e.g.: # "cookie1"="cookie value";"cookie2"="cookie val" #STATIC-COOKIE= # The below allows you to vary which HTTP methods are used to check whether an HTTP(s) server # is running. Some web servers, such as the autopsy web server do not implement the HEAD method CHECKMETHODS=HEAD GET # If you want to specify the location of any of the files, specify them here # EXECDIR=/opt/nikto # PLUGINDIR=/opt/nikto/plugins # TEMPLATEDIR=/opt/nikto/templates # DOCDIR=/opt/nikto/docs # Default plugin macros @@MUTATE=dictionary;subdomain @@DEFAULT=@@ALL;-@@MUTATE;tests(report:500) nikto-2.1.4/nikto.pl000077500000000000000000000260271155455254400143470ustar00rootroot00000000000000#!/usr/bin/perl use strict; #VERSION,2.1.4 # $Id: nikto.pl 632 2011-02-19 02:49:31Z sullo $ use Getopt::Long; Getopt::Long::Configure('no_ignore_case'); # use LW2; ### Change this line to use a different installed version ############################################################################### # Nikto # ############################################################################### # Copyright (C) 2001 CIRT, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 # of the License only. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Contact Information: # Sullo (sullo@cirt.net) # http://cirt.net/ ####################################################################### # See the LICENSE.txt file for more information on the License Nikto is distributed under. # # This program is intended for use in an authorized manner only, and the author # can not be held liable for anything done with this program, code, or items discovered # with this program's use. ####################################################################### # global var/definitions use vars qw/$TEMPLATES %CLI %VARIABLES %TESTS/; use vars qw/%NIKTO %CONFIGFILE %request %result %COUNTERS %db_extensions/; use vars qw/@RESULTS @PLUGINS @DBFILE @REPORTS %CACHE %CONTENTSEARCH/; # setup $COUNTERS{'scan_start'} = time(); $VARIABLES{'DIV'} = "-" x 75; $VARIABLES{'name'} = "Nikto"; $VARIABLES{'version'} = "2.1.4"; $VARIABLES{'configfile'} = "/etc/nikto.conf"; ### Change if it's having trouble finding it # signal trap so we can close down reports properly $SIG{'INT'} = \&safe_quit; # read just the --config option { my %optcfg; Getopt::Long::Configure('pass_through', 'noauto_abbrev'); GetOptions(\%optcfg, "config=s"); Getopt::Long::Configure('nopass_through', 'auto_abbrev'); if (defined $optcfg{'config'}) { $VARIABLES{'configfile'} = $optcfg{'config'}; } } # Read the config files in order my ($error, $home); my $config_exists = 0; $error = load_config("$VARIABLES{'configfile'}"); $config_exists = 1 if ($error eq ""); # Guess home directory -- to support Windows foreach my $var (split(/ /, "HOME USERPROFILE")) { $home = $ENV{$var} if ($ENV{$var}); } $error = load_config("$home/nikto.conf"); $config_exists = 1 if ($error eq ""); $error = load_config("nikto.conf"); $config_exists = 1 if ($error eq ""); if ($config_exists == 0) { die "- Could not find a valid nikto config file\n"; } setup_dirs(); require "$CONFIGFILE{'PLUGINDIR'}/nikto_core.plugin"; nprint("T:" . localtime($COUNTERS{'scan_start'}) . ": Starting", "d"); require "$CONFIGFILE{'PLUGINDIR'}/nikto_single.plugin"; require "$CONFIGFILE{'PLUGINDIR'}/LW2.pm"; my ($a, $b) = split(/\./, $LW2::VERSION); die("- You must use LW2 2.4 or later\n") if ($a != 2 || $b < 4); general_config(); load_databases(); load_databases('u'); nprint("- $VARIABLES{'name'} v$VARIABLES{'version'}"); LW2::http_init_request(\%request); $request{'whisker'}->{'ssl_save_info'} = 1; $request{'whisker'}->{'lowercase_incoming_headers'} = 1; $request{'whisker'}->{'timeout'} = $CLI{'timeout'} || 10; if (defined $CLI{'evasion'}) { $request{'whisker'}->{'encode_anti_ids'} = $CLI{'evasion'}; } $request{'User-Agent'} = $VARIABLES{'useragent'}; $request{'whisker'}->{'retry'} = 0; if ($CLI{'useproxy'} && ($CONFIGFILE{PROXYPORT} ne '') && ($CONFIGFILE{PROXYHOST} ne '')) { $request{'whisker'}->{'proxy_host'} = $CONFIGFILE{PROXYHOST}; $request{'whisker'}->{'proxy_port'} = $CONFIGFILE{PROXYPORT}; } nprint($VARIABLES{'DIV'}); # No targets - quit while we're ahead if ($CLI{'host'} eq "") { nprint("+ ERROR: No host specified"); usage(); } $COUNTERS{'total_targets'} = $COUNTERS{'hosts_completed'} = 0; load_plugins(); # Parse the supplied list of targets my @MARKS = set_targets($CLI{'host'}, $CLI{'ports'}, $CLI{'ssl'}, $CLI{'root'}); # Now check each target is real and remove duplicates/fill in extra information foreach my $mark (@MARKS) { $mark->{'test'} = 1; # Try to resolve the host ($mark->{'hostname'}, $mark->{'ip'}, $mark->{'display_name'}) = resolve($mark->{'ident'}); # Skip if we can't resolve the host - we'll error later if (!defined $mark->{'ip'}) { $mark->{'test'} = 0; next; } # Check that the port is open my $open = port_check($mark->{'hostname'}, $mark->{'ip'}, $mark->{'port'}); if (defined $CLI{'vhost'}) { $mark->{'vhost'} = $CLI{'vhost'} } if ($open == 0) { $mark->{'test'} = 0; next; } else { $COUNTERS{'total_targets'}++; } $mark->{'ssl'} = $open - 1; } # Open reporting report_head($CLI{'format'}, $CLI{'file'}); # Load db_tests set_scan_items(); # Start hook to allow plugins to load databases etc run_hooks("", "start"); # Now we've done the precursor, do the scan foreach my $mark (@MARKS) { next unless ($mark->{'test'}); $mark->{'start_time'} = time(); $VARIABLES{'TEMPL_HCTR'}++; # These should just be passed in the hash - but it's a lot of work to move to a local $request $request{'whisker'}->{'host'} = $mark->{'hostname'} || $mark->{'ip'}; if (defined $CLI{'vhost'}) { $request{'Host'} = $CLI{'vhost'}; $mark->{'vhost'} = $CLI{'vhost'}; } $request{'whisker'}->{'port'} = $mark->{'port'}; $request{'whisker'}->{'ssl'} = $mark->{'ssl'}; $request{'whisker'}->{'version'} = $CONFIGFILE{'DEFAULTHTTPVER'}; # Cookies if (defined $CONFIGFILE{'STATIC-COOKIE'}) { $mark->{'cookiejar'} = LW2::cookie_new_jar(); # parse conf line into name/value pairs foreach my $p (split(/;/, $CONFIGFILE{'STATIC-COOKIE'})) { $p =~ s/(?:^\s+|\s+$)//; $p =~ s/"(?:[ ]+)?=(?:[ ]+)?"/","/g; my @cv = parse_csv($p); # Set into the jar LW2::cookie_set(\%{ $mark->{'cookiejar'} }, $cv[0], $cv[1]); } } $mark->{'total_vulns'} = 0; $mark->{'total_errors'} = 0; my %FoF = (); nfetch($mark, "/", "GET", "", "", { nocache => 1, noprefetch => 1, nopostfetch => 1 }, "getinfo"); report_host_start($mark); if ($CLI{'plugins'} eq '@@NONE') { my $protocol = "http"; if ($mark->{'ssl'}) { $protocol .= "s"; } if ($mark->{'banner'} eq "") { $mark->{'banner'} = "(no identification possible)"; } add_vulnerability($mark, "Server: $protocol://$mark->{'display_name'}:$mark->{'port'}\t$mark->{'banner'}", 0); } else { dump_target_info($mark); unless ((defined $CLI{'nofof'}) || ($CLI{'plugins'} eq '@@NONE')) { map_codes($mark) } run_hooks($mark, "recon"); run_hooks($mark, "scan"); } $mark->{'end_time'} = time(); my $time = date_disp($mark->{'end_time'}); my $elapsed = $mark->{'end_time'} - $mark->{'start_time'}; if ($CLI{'plugins'} ne '@@NONE') { if (!$mark->{'terminate'}) { nprint( "+ $COUNTERS{'total_checks'} items checked: $mark->{'total_errors'} error(s) and $mark->{'total_vulns'} item(s) reported on remote host" ); } else { nprint( "+ Scan terminated: $mark->{'total_errors'} error(s) and $mark->{'total_vulns'} item(s) reported on remote host" ); } nprint("+ End Time: $time ($elapsed seconds)"); } nprint($VARIABLES{'DIV'}); $COUNTERS{'hosts_completed'}++; report_host_end($mark); } $COUNTERS{'scan_end'} = time(); $COUNTERS{'scan_elapsed'} = ($COUNTERS{'scan_end'} - $COUNTERS{'scan_start'}); report_summary(); report_close(); if ($CLI{'plugins'} ne '@@NONE') { nprint("+ $COUNTERS{'hosts_completed'} host(s) tested"); nprint("+ $COUNTERS{'totalrequests'} requests made in $COUNTERS{'scan_elapsed'} seconds", "v"); send_updates(@MARKS); } nprint("T:" . localtime() . ": Ending", "d"); exit; ################################################################################# # load config file # error=load_config(FILENAME) sub load_config { my $configfile = $_[0]; open(CONF, "<$configfile") || return "+ ERROR: Unable to open config file '$configfile'"; my @CONFILE = ; close(CONF); foreach my $line (@CONFILE) { $line =~ s/\#.*$//; chomp($line); $line =~ s/\s+$//; $line =~ s/^\s+//; next if ($line eq ""); my @temp = split(/=/, $line, 2); if ($temp[0] ne "") { $CONFIGFILE{ $temp[0] } = $temp[1]; } } # add CONFIG{'CLIOPTS'} to ARGV if defined... if (defined $CONFIGFILE{'CLIOPTS'}) { my @t = split(/ /, $CONFIGFILE{'CLIOPTS'}); foreach my $c (@t) { push(@ARGV, $c); } } # Check for necessary config items check_config_defined("CHECKMETHODS", "HEAD"); check_config_defined('@@MUTATE', 'dictionary;subdomain'); check_config_defined('@@DEFAULT', '@@ALL,-@@MUTATE'); return ""; } ################################################################################# # find plugins directory sub setup_dirs { my $CURRENTDIR = $0; chomp($CURRENTDIR); $CURRENTDIR =~ s#[\\/]nikto.pl$##; # First assume we get it from CONFIGFILE unless (defined $CONFIGFILE{'EXECDIR'}) { if (-d "$ENV{'PWD'}/plugins") { $CONFIGFILE{'EXECDIR'} = $ENV{'PWD'}; } elsif (-d "$CURRENTDIR/plugins") { $CONFIGFILE{'EXECDIR'} = $CURRENTDIR; } elsif (-d "./plugins") { $CONFIGFILE{'EXECDIR'} = $CURRENTDIR; } else { print STDERR "Could not work out the nikto EXECDIR, try setting it in nikto.conf\n"; exit; } } unless (defined $CONFIGFILE{'PLUGINDIR'}) { $CONFIGFILE{'PLUGINDIR'} = "$CONFIGFILE{'EXECDIR'}/plugins"; } unless (defined $CONFIGFILE{'TEMPLATEDIR'}) { $CONFIGFILE{'TEMPLATEDIR'} = "$CONFIGFILE{'EXECDIR'}/templates"; } unless (defined $CONFIGFILE{'DOCUMENTDIR'}) { $CONFIGFILE{'DOCUMENTDIR'} = "$CONFIGFILE{'EXECDIR'}/docs"; } return; } ###################################################################### ## check_config_defined(item, default) ## Checks whether config has been set, warns and sets to a default sub check_config_defined { my $item = $_[0]; my $default = $_[1]; if (!defined $CONFIGFILE{$item}) { print STDERR "- Warning: $item is not defined in Nikto configuration, setting to \"$default\"\n"; $CONFIGFILE{$item} = $default; } } nikto-2.1.4/plugins/000077500000000000000000000000001155455254400143355ustar00rootroot00000000000000nikto-2.1.4/plugins/LW2.pm000066400000000000000000005767561155455254400153310ustar00rootroot00000000000000#!perl # LW2 version 2.5 # LW2 Copyright (c) 2009, Jeff Forristal (wiretrip.net) # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # - Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. =head1 NAME LW2 - Perl HTTP library version 2.5 =head1 SYNOPSIS use LW2; require 'LW2.pm'; =head1 DESCRIPTION Libwhisker is a Perl library useful for HTTP testing scripts. It contains a pure-Perl reimplementation of functionality found in the C, C, C, C, C, C, C, C, C, C, and C modules. Libwhisker is designed to be portable (a single perl file), fast (general benchmarks show libwhisker is faster than LWP), and flexible (great care was taken to ensure the library does exactly what you want to do, even if it means breaking the protocol). =head1 FUNCTIONS The following are the functions contained in Libwhisker: =over 4 =cut package LW2; $LW2::VERSION="2.5"; $PACKAGE='LW2'; BEGIN { package LW2; $PACKAGE='LW2'; ## LW module manager stuff ## $LW_SSL_LIB = 0; $LW_SSL_KEEPALIVE = 0; $LW_NONBLOCK_CONNECT = 1; $_SSL_LIBRARY = undef; eval "use Socket"; if ( !$@ ) { eval "use Net::SSLeay"; # do we have SSL support? if ( !$@ ) { $LW_SSL_LIB = 1; $_SSL_LIBRARY = 'Net::SSLeay'; Net::SSLeay::load_error_strings(); Net::SSLeay::SSLeay_add_ssl_algorithms(); Net::SSLeay::randomize(); } else { eval "use Net::SSL"; if ( !$@ ) { $LW_SSL_LIB = 2; $_SSL_LIBRARY = 'Net::SSL'; } } if ( $^O !~ /Win32/ ) { eval "use POSIX qw(:errno_h :fcntl_h)"; if ($@) { $LW_NONBLOCK_CONNECT = 0; } } else { # taken from Winsock2.h *EINPROGRESS = sub { 10036 }; *EWOULDBLOCK = sub { 10035 }; } } } # BEGIN ######################################################################## =item B Params: $auth_method, \%req, $user, \@passwords [, $domain, $fail_code ] Return: $first_valid_password, undef if error/none found Perform a HTTP authentication brute force against a server (host and URI defined in %req). It will try every password in the password array for the given user. The first password (in conjunction with the given user) that doesn't return HTTP 401 is returned (and the brute force is stopped at that point). You should retry the request with the given password and double-check that you got a useful HTTP return code that indicates successful authentication (200, 302), and not something a bit more abnormal (407, 500, etc). $domain is optional, and is only used for NTLM auth. Note: set up any proxy settings and proxy auth in %req before calling this function. You can brute-force proxy authentication by setting up the target proxy as proxy_host and proxy_port in %req, using an arbitrary host and uri (preferably one that is reachable upon successful proxy authorization), and setting the $fail_code to 407. The $auth_method passed to this function should be a proxy-based one ('proxy-basic', 'proxy-ntlm', etc). if your server returns something other than 401 upon auth failure, then set $fail_code to whatever is returned (and it needs to be something *different* than what is received on auth success, or this function won't be able to tell the difference). =cut sub auth_brute_force { my ( $auth_method, $hrin, $user, $pwordref, $dom, $fail_code ) = @_; my ( $P, %hout ); $fail_code ||= 401; return undef if ( !defined $auth_method || length($auth_method) == 0 ); return undef if ( !defined $user || length($user) == 0 ); return undef if ( !( defined $hrin && ref($hrin) ) ); return undef if ( !( defined $pwordref && ref($pwordref) ) ); map { ( $P = $_ ) =~ tr/\r\n//d; auth_set( $auth_method, $hrin, $user, $P, $dom ); return undef if ( http_do_request( $hrin, \%hout ) ); return $P if ( $hout{whisker}->{code} != $fail_code ); } @$pwordref; return undef; } ######################################################################## =item B Params: \%req Return: nothing (modifies %req) Modifes %req to disable all authentication (regular and proxy). Note: it only removes the values set by auth_set(). Manually-defined [Proxy-]Authorization headers will also be deleted (but you shouldn't be using the auth_* functions if you're manually handling your own auth...) =cut sub auth_unset { my $href = shift; return if ( !defined $href || !ref($href) ); delete $$href{Authorization}; delete $$href{'Proxy-Authorization'}; delete $$href{whisker}->{auth_callback}; delete $$href{whisker}->{auth_proxy_callback}; delete $$href{whisker}->{auth_data}; delete $$href{whisker}->{auth_proxy_data}; } ######################################################################## =item B Params: $auth_method, \%req, $user, $password [, $domain] Return: nothing (modifies %req) Modifes %req to use the indicated authentication info. Auth_method can be: 'basic', 'proxy-basic', 'ntlm', 'proxy-ntlm'. Note: this function may not necessarily set any headers after being called. Also, proxy-ntlm with SSL is not currently supported. =cut sub auth_set { my ( $method, $href, $user, $pass, $domain ) = ( lc(shift), @_ ); return if ( !( defined $href && ref($href) ) ); return if ( !defined $user || !defined $pass ); if ( $method eq 'basic' ) { $$href{'Authorization'} = 'Basic ' . encode_base64( $user . ':' . $pass, '' ); } if ( $method eq 'proxy-basic' ) { $$href{'Proxy-Authorization'} = 'Basic ' . encode_base64( $user . ':' . $pass, '' ); } if ( $method eq 'ntlm' ) { http_close($href); $$href{whisker}->{auth_data} = ntlm_new( $user, $pass, $domain ); $$href{whisker}->{auth_callback} = \&_ntlm_auth_callback; } if ( $method eq 'proxy-ntlm' ) { utils_croak('',"auth_set: proxy-ntlm auth w/ SSL not currently supported") if ( $href->{whisker}->{ssl} > 0 ); http_close($href); $$href{whisker}->{auth_proxy_data} = ntlm_new( $user, $pass, $domain ); $$href{whisker}->{auth_proxy_callback} = \&_ntlm_auth_proxy_callback; } } ######################################################################## =item B Params: none Return: $jar Create a new cookie jar, for use with the other functions. Even though the jar is technically just a hash, you should still use this function in order to be future-compatible (should the jar format change). =cut sub cookie_new_jar { return {}; } ######################################################################## =item B Params: $jar, \%response [, \%request, $reject ] Return: $num_of_cookies_read Read in cookies from an %response hash, and put them in $jar. Notice: cookie_read uses internal magic done by http_do_request in order to read cookies regardless of 'Set-Cookie[2]' header appearance. If the optional %request hash is supplied, then it will be used to calculate default host and path values, in case the cookie doesn't specify them explicitly. If $reject is set to 1, then the %request hash values are used to calculate and reject cookies which are not appropriate for the path and domains of the given request. =cut sub cookie_read { my ( $count, $jarref, $hrs, $hrq, $rej ) = ( 0, @_ ); return 0 if ( !( defined $jarref && ref($jarref) ) ); return 0 if ( !( defined $hrs && ref($hrs) ) ); return 0 if ( !( defined $$hrs{whisker}->{cookies} && ref( $$hrs{whisker}->{cookies} ) ) ); my @opt; if(defined $hrq && ref($hrq)){ push @opt, $hrq->{whisker}->{host}; my $u = $hrq->{whisker}->{uri}; $u=~s#/.*?$##; $u='/' if($u eq ''); push @opt, $u, $rej; } foreach ( @{ $hrs->{whisker}->{cookies} } ) { cookie_parse( $jarref, $_ , @opt); $count++; } return $count; } ######################################################################## =item B Params: $jar, $cookie [, $default_domain, $default_path, $reject ] Return: nothing Parses the cookie into the various parts and then sets the appropriate values in the cookie $jar. If the cookie value is blank, it will delete it from the $jar. See the 'docs/cookies.txt' document for a full explanation of how Libwhisker parses cookies and what RFC aspects are supported. The optional $default_domain value is taken literally. Values with no leading dot (e.g. 'www.host.com') are considered to be strict hostnames and will only match the identical hostname. Values with leading dots (e.g. '.host.com') are treated as sub-domain matches for a single domain level. If the cookie does not indicate a domain, and a $default_domain is not provided, then the cookie is considered to match all domains/hosts. The optional $default_path is used when the cookie does not specify a path. $default_path must be absolute (start with '/'), or it will be ignored. If the cookie does not specify a path, and $default_path is not provided, then the default value '/' will be used. Set $reject to 1 if you wish to reject cookies based upon the provided $default_domain and $default_path. Note that $default_domain and $default_path must be specified for $reject to actually do something meaningful. =cut sub cookie_parse { my ( $jarref, $header ) = (shift, shift); my ( $Dd, $Dp, $R ) = (shift, shift, shift||0); return if ( !( defined $jarref && ref($jarref) ) ); return if ( !( defined $header && length($header) > 0 ) ); my @C = ( undef, undef, undef, undef, 0 ); $header =~ tr/\r\n//d; my ($f,%seen,$n,$t) = (1); while( length($header) ){ $header =~ s/^[ \t]+//; last if(!($header =~ s/^([^ \t=;]+)//)); # LW2.5 change: cookie name is no longer lower-cased # my $an = lc($1); my $an = $1; my $av = undef; $header =~ s/^[ \t]+//; if(substr($header,0,1) eq '='){ $header=~s/^=[ \t]*//; if(substr($header,0,1) eq '"'){ my $p = index($header,'"',1); last if($p == -1); $av = substr($header,1,$p-1); substr($header,0,$p+1)=''; } else { $av = $1 if($header =~ s/^([^ \t;,]*)//); } } else { my $p = index($header,';'); substr($header,0,$p)=''; } $header =~ s/^.*?;//; if($f){ return if(!defined $av); ($f,$n,$C[0])=(0,$an,$av); } else { $seen{$an}=$av if(!exists $seen{$an}); } } return if(!defined $n || $n eq ''); my $del = 0; $del++ if($C[0] eq ''); $del++ if(defined $seen{'max-age'} && $seen{'max-age'} eq '0'); if($del){ delete $$jarref{$n} if exists $$jarref{$n}; return; } if(defined $seen{domain} && $seen{domain} ne ''){ $t = $seen{domain}; $t='.'.$t if(substr($t,0,1) ne '.' && !_is_ip_address($t)); } else { $t=$Dd; } $t=~s/\.+$// if(defined $t); $C[1]=$t; if(defined $seen{path}){ $t = $seen{path}; } else { $t=$Dp || '/'; } $t=~s#/+$##; $t='/' if(substr($t,0,1) ne '/'); $C[2]=$t; $C[4]=1 if(exists $seen{secure}); return if($R && !_is_valid_cookie_match($C[1], $C[2], $Dd, $Dp)); $$jarref{$n} = \@C; } ######################################################################## sub _is_ip_address { my $n = shift; return 1 if($n=~/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/); return 0; } sub _is_valid_cookie_match { my ($cd, $cp, $td, $tp) = @_; return 0 if(index($tp,$cp)!=0); if(substr($cd,0,1) eq '.'){ if( $td =~ /(.+)$cd$/ ){ return 1 if(index($1,'.') == -1); } return 0; } else { return 0 if($cd ne $td); } return 1; } ######################################################################## =item B Params: $jar, \%request, $override Return: nothing Goes through the given $jar and sets the Cookie header in %req pending the correct domain and path. If $override is true, then the secure, domain and path restrictions of the cookies are ignored and all cookies are essentially included. Notice: cookie expiration is currently not implemented. URL restriction comparision is also case-insensitive. =cut sub cookie_write { my ( $jarref, $hin, $override ) = @_; my ( $name, $out ) = ( '', '' ); return if ( !( defined $jarref && ref($jarref) ) ); return if ( !( defined $hin && ref($hin) ) ); $override = $override || 0; $$hin{'whisker'}->{'ssl'} = $$hin{'whisker'}->{'ssl'} || 0; foreach $name ( keys %$jarref ) { next if ( $name eq '' ); if($override){ $out .= "$name=$$jarref{$name}->[0];"; next; } next if ( $$hin{'whisker'}->{'ssl'} == 0 && $$jarref{$name}->[4] > 0 ); if ( $$hin{'whisker'}->{'host'} =~ /$$jarref{$name}->[1]$/i && $$hin{'whisker'}->{'uri'} =~ /^$$jarref{$name}->[2])/ ) { $out .= "$name=$$jarref{$name}->[0];"; } } if ( $out ne '' ) { $$hin{'Cookie'} = $out; } } ######################################################################## =item B Params: $jar, $name Return: @elements Fetch the named cookie from the $jar, and return the components. The returned items will be an array in the following order: value, domain, path, expire, secure value = cookie value, should always be non-empty string domain = domain root for cookie, can be undefined path = URL path for cookie, should always be a non-empty string expire = undefined (depreciated, but exists for backwards-compatibility) secure = whether or not the cookie is limited to HTTPs; value is 0 or 1 =cut sub cookie_get { my ( $jarref, $name ) = @_; return undef if ( !( defined $jarref && ref($jarref) ) ); if ( defined $$jarref{$name} ) { return @{ $$jarref{$name} }; } return undef; } ######################################################################## =item B Params: $jar Return: @names Fetch all the cookie names from the jar, which then let you cooke_get() them individually. =cut sub cookie_get_names { my ( $jarref, $name ) = @_; return undef if ( !( defined $jarref && ref($jarref) ) ); return keys %$jarref; } ######################################################################## =item B Params: $jar, $domain, $url, $ssl Return: @names Fetch all the cookie names from the jar which are valid for the given $domain, $url, and $ssl values. $domain should be string scalar of the target host domain ('www.example.com', etc.). $url should be the absolute URL for the page ('/index.html', '/cgi-bin/foo.cgi', etc.). $ssl should be 0 for non-secure cookies, or 1 for all (secure and normal) cookies. The return value is an array of names compatible with cookie_get(). =cut sub cookie_get_valid_names { my ( $jarref, $domain, $url, $ssl ) = @_; return () if ( !( defined $jarref && ref($jarref) ) ); return () if ( !defined $domain || $domain eq '' ); return () if ( !defined $url || $url eq '' ); $ssl ||= 0; my (@r, $name); foreach $name ( keys %$jarref ) { next if ( $name eq '' ); next if ( $$jarref{$name}->[4] > 0 && $ssl == 0 ); if ( $domain =~ /$$jarref{$name}->[1]$/i && $url =~ /^$$jarref{$name}->[2])/i ) { push @r, $name; } } return @r; } ######################################################################## =item B Params: $jar, $name, $value, $domain, $path, $expire, $secure Return: nothing Set the named cookie with the provided values into the %jar. $name is required to be a non-empty string. $value is required, and will delete the named cookie from the $jar if it is an empty string. $domain and $path can be strings or undefined. $expire is ignored (but exists for backwards-compatibility). $secure should be the numeric value of 0 or 1. =cut sub cookie_set { my ( $jarref, $name, $value, $domain, $path, $expire, $secure ) = @_; my @construct; return if ( !( defined $jarref && ref($jarref) ) ); return if ( $name eq '' ); if ( !defined $value || $value eq '' ) { delete $$jarref{$name}; return; } $path = $path || '/'; $secure = $secure || 0; @construct = ( $value, $domain, $path, undef, $secure ); $$jarref{$name} = \@construct; } ######################################################################## ##################################################### # cluster global variables %_crawl_config = ( 'save_cookies' => 0, 'reuse_cookies' => 1, 'save_offsites' => 0, 'save_non_http' => 0, 'follow_moves' => 1, 'url_limit' => 1000, 'use_params' => 0, 'params_double_record' => 0, 'skip_ext' => { gif => 1, jpg => 1, png => 1, gz => 1, swf => 1, pdf => 1, zip => 1, wav => 1, mp3 => 1, asf => 1, tgz => 1 }, 'save_skipped' => 0, 'save_referrers' => 0, 'use_referrers' => 1, 'do_head' => 0, 'callback' => 0, 'netloc_bug' => 1, 'normalize_uri' => 1, 'source_callback' => 0 ); %_crawl_linktags = ( 'a' => 'href', 'applet' => [qw(codebase archive code)], 'area' => 'href', 'base' => 'href', 'bgsound' => 'src', 'blockquote' => 'cite', 'body' => 'background', 'del' => 'cite', 'embed' => [qw(src pluginspage)], 'form' => 'action', 'frame' => [qw(src longdesc)], 'iframe' => [qw(src longdesc)], 'ilayer' => 'background', 'img' => [qw(src lowsrc longdesc usemap)], 'input' => [qw(src usemap)], 'ins' => 'cite', 'isindex' => 'action', 'head' => 'profile', 'layer' => [qw(background src)], 'link' => 'href', # 'meta' => 'http-equiv', 'object' => [qw(codebase data archive usemap)], 'q' => 'cite', 'script' => 'src', 'table' => 'background', 'td' => 'background', 'th' => 'background', 'xmp' => 'href', ); ##################################################### =item B Params: $START, $MAX_DEPTH, \%request_hash [, \%tracking_hash ] Return: $crawl_object The crawl_new() functions initializes a crawl object (hash) to the default values, and then returns it for later use by crawl(). $START is the starting URL (in the form of 'http://www.host.com/url'), and MAX_DEPTH is the maximum number of levels to crawl (the START URL counts as 1, so a value of 2 will crawl the START URL and all URLs found on that page). The request_hash is a standard initialized request hash to be used for requests; you should set any authentication information or headers in this hash in order for the crawler to use them. The optional tracking_hash lets you supply a hash for use in tracking URL results (otherwise crawl_new() will allocate a new anon hash). =cut sub crawl_new { my ( $start, $depth, $reqref, $trackref ) = @_; my %X; return undef if ( !defined $start || !defined $depth ); return undef if ( !defined $reqref || !ref($reqref) ); $trackref = {} if ( !defined $trackref || !ref($trackref) ); $X{track} = $trackref; $X{request} = $reqref; $X{depth} = $depth || 2; $X{start} = $start; $X{magic} = 7340; $X{reset} = sub { $X{errors} = []; # all errors encountered $X{urls} = []; # temp; used to hold all URLs on page $X{server_tags} = {}; # all server tags found $X{referrers} = {}; # who refers to what URLs $X{offsites} = {}; # all URLs that point offsite $X{response} = {}; # temp; the response hash $X{non_http} = {}; # all non_http URLs found $X{cookies} = {}; # all cookies found $X{forms} = {}; # all forms found $X{jar} = {}; # temp; cookie jar $X{url_queue} = []; # temp; URLs to still fetch $X{config} = {}; %{ $X{config} } = %_crawl_config; %{ $X{track} } = (); $X{parsed_page_count} = 0; }; $X{crawl} = sub { crawl( \%X, @_ ) }; $X{reset}->(); return \%X; } ##################################################### =item B Params: $crawl_object [, $START, $MAX_DEPTH ] Return: $count [ undef on error ] The heart of the crawl package. Will perform an HTTP crawl on the specified HOST, starting at START URI, proceeding up to MAX_DEPTH. Crawl_object needs to be the variable returned by crawl_new(). You can also indirectly call crawl() via the crawl_object itself: $crawl_object->{crawl}->($START,$MAX_DEPTH) Returns the number of URLs actually crawled (not including those skipped). =cut { # START OF CRAWL CONTAINER sub crawl { my ( $C, $START, $MAX_DEPTH ) = @_; return undef if ( !defined $C || !ref($C) || $C->{magic} != 7340 ); # shortcuts, to reduce dereferences and typing my $CONFIG = $C->{config}; my $TRACK = $C->{track}; my $URLS = $C->{urls}; my $RESP = $C->{response}; my $REQ = $C->{request}; my $Q = $C->{url_queue}; $START ||= $C->{start}; $C->{depth} = $MAX_DEPTH || $C->{depth}; my ( $COUNT, $T, @ST ) = ( 0, '' ); # ST[] = [ 0.HOST, 1.PORT, 2.URL, 3.DEPTH, 4.CWD, 5.REF ] my @v = uri_split($START); my $error = undef; $error = 'Start protocol not http or https' if ( $v[1] ne 'http' && $v[1] ne 'https' ); $error = 'Bad start host' if ( !defined $v[2] || $v[2] eq '' ); push( @{ $C->{errors} }, $error ) && return undef if ( defined $error ); @ST = ( $v[2], $v[3], $v[0], 1, '', '' ); $REQ->{whisker}->{ssl} = 1 if ( $v[1] eq 'https' ); $REQ->{whisker}->{host} = $ST[0]; $REQ->{whisker}->{port} = $ST[1]; $REQ->{whisker}->{lowercase_incoming_headers} = 1; $REQ->{whisker}->{ignore_duplicate_headers} = 0; delete $REQ->{whisker}->{parameters}; http_fixup_request($REQ); push @$Q, \@ST; while (@$Q) { @ST = @{ shift @$Q }; next if ( defined $TRACK->{ $ST[2] } && $TRACK->{ $ST[2] } ne '?' ); if ( $ST[3] > $C->{depth} ) { $TRACK->{ $ST[2] } = '?' if ( $CONFIG->{save_skipped} > 0 ); next; } $ST[4] = uri_get_dir( $ST[2] ); $REQ->{whisker}->{uri} = $ST[2]; if ( $ST[5] ne '' && $CONFIG->{use_referrers} > 0 ) { $REQ->{Referrer} = $ST[5]; } my $result = _crawl_do_request( $REQ, $RESP, $C ); if ( $result == 1 || $result == 2 ) { push @{ $C->{errors} }, "$ST[2]: $RESP->{whisker}->{error}"; next; } $COUNT++; $TRACK->{ $ST[2] } = $RESP->{whisker}->{code} if ( $result == 0 || $result == 4 ); $TRACK->{ $ST[2] } = '?' if ( ( $result == 3 || $result == 5 ) && $CONFIG->{save_skipped} > 0 ); if ( defined $RESP->{server} && !ref( $RESP->{server} ) ) { $C->{server_tags}->{ $RESP->{server} }++; } if ( defined $RESP->{'set-cookie'} ) { if ( $CONFIG->{save_cookies} > 0 ) { if ( ref( $RESP->{'set-cookie'} ) ) { $C->{cookies}->{$_}++ foreach ( @{ $RESP->{'set-cookie'} } ); } else { $C->{cookies}->{ $RESP->{'set-cookie'} }++; } } cookie_read( $C->{jar}, $RESP ) if ( $CONFIG->{reuse_cookies} > 0 ); } next if ( $result == 4 || $result == 5 ); next if ( scalar @$Q > $CONFIG->{url_limit} ); if ( $result == 0 ) { # page should be parsed if ( $CONFIG->{source_callback} != 0 && ref( $CONFIG->{source_callback} ) eq 'CODE' ) { &{ $CONFIG->{source_callback} }($C); } html_find_tags( \$RESP->{whisker}->{data}, \&_crawl_extract_links_test, 0, $C, \%_crawl_linktags ); $C->{parsed_page_count}++; } push @$URLS, $RESP->{location} if ( $result == 3 ); foreach $T (@$URLS) { $T =~ tr/\0\r\n//d; next if ( length($T) == 0 ); next if ( $T =~ /^#/i ); # fragment push @{ $C->{referrers}->{$T} }, $ST[2] if ( $CONFIG->{save_referrers} > 0 ); if ( $T =~ /^([a-zA-Z0-9]*):/ && lc($1) ne 'http' && lc($1) ne 'https' ) { push @{ $C->{non_http}->{$T} }, $ST[2] if ( $CONFIG->{save_non_http} > 0 ); next; } if ( substr( $T, 0, 2 ) eq '//' && $CONFIG->{netloc_bug} > 0 ) { if ( $REQ->{whisker}->{ssl} > 0 ) { $T = 'https:' . $T; } else { $T = 'http:' . $T; } } if ( $CONFIG->{callback} != 0 ) { next if &{ $CONFIG->{callback} }( $T, $C ); } $T = uri_absolute( $T, $ST[4], $CONFIG->{normalize_uri} ); # (uri,protocol,host,port,params,frag,user,pass) @v = uri_split($T); # make sure URL is on same host and port if ( ( defined $v[2] && $v[2] ne $ST[0] ) || ( $v[3] > 0 && $v[3] != $ST[1] ) ) { $C->{offsites}->{ uri_join(@v) }++ if ( $CONFIG->{save_offsites} > 0 ); next; } if ( $v[0] =~ /\.([a-z0-9]+)$/i ) { if ( defined $CONFIG->{skip_ext}->{ lc($1) } ) { $TRACK->{ $v[0] } = '?' if ( $CONFIG->{save_skipped} > 0 ); next; } } if ( defined $v[4] && $CONFIG->{use_params} > 0 ) { $TRACK->{ $v[0] } = '?' if ( $CONFIG->{params_double_record} > 0 && !defined $TRACK->{ $v[0] } ); $v[0] = $v[0] . '?' . $v[4]; } next if ( defined $TRACK->{ $v[0] } ) ; # we've processed this already # ST[] = [ 0.HOST, 1.PORT, 2.URL, 3.DEPTH, 4.CWD, 5.REF ] push @$Q, [ $ST[0], $ST[1], $v[0], $ST[3] + 1, '', $ST[2] ]; } # foreach @$URLS = (); # reset for next round } # while return $COUNT; } # end sub crawl ##################################################### sub _crawl_extract_links_test { my ( $TAG, $hr, $dr, $start, $len, $OBJ ) = ( lc(shift), @_ ); return undef if ( !scalar %$hr ); # fastpath quickie # we know this is defined, due to our tagmap my $t = $_crawl_linktags{$TAG}; while ( my ( $key, $val ) = each %$hr ) { # normalize element values $$hr{ lc($key) } = $val; } # all of this just to catch meta refresh URLs if ( $TAG eq 'meta' && defined $$hr{'http-equiv'} && $$hr{'http-equiv'} eq 'refresh' && defined $$hr{'content'} && $$hr{'content'} =~ m/url=(.+)/i ) { push( @{ $OBJ->{urls} }, $1 ); } elsif ( ref($t) ) { foreach (@$t) { push( @{ $OBJ->{urls} }, $$hr{$_} ) if ( defined $$hr{$_} ); } } else { push( @{ $OBJ->{urls} }, $$hr{$t} ) if ( defined $$hr{$t} ); } if ( $TAG eq 'form' && defined $$hr{action} ) { my $u = $OBJ->{response}->{whisker}->{uri}; $OBJ->{forms}->{ uri_absolute( $$hr{action}, $u, 1 ) }++; } return undef; } ################################################################ sub _crawl_do_request_ex { my ( $hrin, $hrout, $OBJ ) = @_; my $ret; $ret = http_do_request( $hrin, $hrout ); return ( 2, $ret ) if ( $ret == 2 ); # if there was connection error, do not continue if ( $ret == 0 ) { # successful request # WARNING: what if *all* HEAD respones are 302'd on purpose, but # all GETs are normal? if ( $$hrout{whisker}->{code} < 308 && $$hrout{whisker}->{code} > 300 ) { if ( $OBJ->{config}->{follow_moves} > 0 ) { return ( 3, $ret ) if ( defined $$hrout{location} && !ref( $$hrout{location} ) ); } return ( 5, $ret ); # not avail } if ( $$hrout{whisker}->{code} == 200 ) { # no content-type is treated as text/htm if ( defined $$hrout{'content-type'} && $$hrout{'content-type'} !~ /^text\/htm/i ) { return ( 4, $ret ); } } } return ( -1, $ret ); # fallthrough } ################################################################ sub _crawl_do_request { my ( $hrin, $hrout, $OBJ ) = @_; my ( $cret, $lwret ); if ( $OBJ->{config}->{do_head} && $$hrin{whisker}->{method} ne 'HEAD' ) { my $save = $$hrin{whisker}->{method}; $$hrin{whisker}->{method} = 'HEAD'; ( $cret, $lwret ) = _crawl_do_request_ex( $hrin, $hrout, $OBJ ); $$hrin{whisker}->{method} = $save; return $cret if ( $cret > 0 ); if ( $lwret == 0 ) { # successful request if ( $$hrout{whisker}->{code} == 501 ) { # HEAD not allowed $OBJ->{config}->{do_head} = 0; # no more HEAD requests } } # request errors are essentially redone via GET, below } ( $cret, $lwret ) = _crawl_do_request_ex( $hrin, $hrout, $OBJ ); return $lwret if ( $cret < 0 ); return $cret; } } # CRAWL_CONTAINER ################################################################ ######################################################################## =item B Params: $name, \@array [, $name, \%hash, $name, \$scalar ] Return: $code [ undef on error ] The dump function will take the given $name and data reference, and will create an ASCII perl code representation suitable for eval'ing later to recreate the same structure. $name is the name of the variable that it will be saved as. Example: $output = LW2::dump('request',\%request); NOTE: dump() creates anonymous structures under the name given. For example, if you dump the hash %hin under the name 'hin', then when you eval the dumped code you will need to use %$hin, since $hin is now a *reference* to a hash. =cut sub dump { my %what = @_; my ( $final, $k, $v ) = (''); while ( ( $k, $v ) = each %what ) { return undef if ( ref($k) || !ref($v) ); $final .= "\$$k = " . _dump( 1, $v, 1 ); $final =~ s#,\n$##; $final .= ";\n"; } return $final; } ######################################################################## =item B Params: $file, $name, \@array [, $name, \%hash, $name, \@scalar ] Return: 0 if success; 1 if error This calls dump() and saves the output to the specified $file. Note: LW does not checking on the validity of the file name, it's creation, or anything of the sort. Files are opened in overwrite mode. =cut sub dump_writefile { my $file = shift; my $output = &dump(@_); return 1 if ( !open( OUT, ">$file" ) || !defined $output ); binmode(OUT); print OUT $output; close(OUT); } ######################################################################## sub _dump { # dereference and dump an element my ( $t, $ref, $depth ) = @_; my ( $out, $k, $v ) = (''); $depth ||= 1; # to protect against circular loops return 'undef' if ( $depth > 128 ); if ( !defined $ref ) { return 'undef'; } elsif ( ref($ref) eq 'HASH' ) { $out .= "{\n"; while ( ( $k, $v ) = each %$ref ) { # next if ( $k eq '' ); $out .= "\t" x $t; $out .= _dumpd($k) . ' => '; if ( ref($v) ) { $out .= _dump( $t + 1, $v, $depth + 1 ); } else { $out .= _dumpd($v); } $out .= ",\n" unless ( substr( $out, -2, 2 ) eq ",\n" ); } $out =~ s#,\n$#\n#; $out .= "\t" x ( $t - 1 ); $out .= "},\n"; } elsif ( ref($ref) eq 'ARRAY' ) { $out .= "["; if ( ~~@$ref ) { $out .= "\n"; foreach $v (@$ref) { $out .= "\t" x $t; if ( ref($v) ) { $out .= _dump( $t + 1, $v, $depth + 1 ); } else { $out .= _dumpd($v); } $out .= ",\n" unless ( substr( $out, -2, 2 ) eq ",\n" ); } $out =~ s#,\n$#\n#; $out .= "\t" x ( $t - 1 ); } $out .= "],\n"; } elsif ( ref($ref) eq 'SCALAR' ) { $out .= _dumpd($$ref); } elsif ( ref($ref) eq 'REF' ) { $out .= _dump( $t, $$ref, $depth + 1 ); } elsif ( ref($ref) ) { # unknown/unsupported ref $out .= "undef"; } else { # normal scalar $out .= _dumpd($ref); } return $out; } ######################################################################## sub _dumpd { # escape a scalar string my $v = shift; return 'undef' if ( !defined $v ); return "''" if ( $v eq '' ); return "$v" if ( $v eq '0' || $v !~ tr/0-9//c && $v !~ m#^0+# ); if ( $v !~ tr/ !-~//c ) { $v =~ s/(['\\])/\\$1/g; return "'$v'"; } $v =~ s#\\#\\\\#g; $v =~ s#"#\\"#g; $v =~ s#\r#\\r#g; $v =~ s#\n#\\n#g; $v =~ s#\t#\\t#g; $v =~ s#\$#\\\$#g; $v =~ s#([^!-~ ])#sprintf('\\x%02x',ord($1))#eg; return "\"$v\""; } ######################################################################## ######################################################################## { # package variables my $MIMEBASE64_TRYLOADING = 1; ######################################################################## =item B Params: $data [, $eol] Return: $b64_encoded_data This function does Base64 encoding. If the binary MIME::Base64 module is available, it will use that; otherwise, it falls back to an internal perl version. The perl version carries the following copyright: Copyright 1995-1999 Gisle Aas NOTE: the $eol parameter will be inserted every 76 characters. This is used to format the data for output on a 80 character wide terminal. =cut sub encode_base64 { if ($MIMEBASE64_TRYLOADING) { eval "require MIME::Base64"; $MIMEBASE64_TRYLOADING = 0; } goto &MIME::Base64::encode_base64 if ($MIME::Base64::VERSION); my $res = ""; my $eol = $_[1]; $eol = "\n" unless defined $eol; pos( $_[0] ) = 0; while ( $_[0] =~ /(.{1,45})/gs ) { $res .= substr( pack( 'u', $1 ), 1 ); chop($res); } $res =~ tr|` -_|AA-Za-z0-9+/|; my $padding = ( 3 - length( $_[0] ) % 3 ) % 3; $res =~ s/.{$padding}$/'=' x $padding/e if $padding; if ( length $eol ) { $res =~ s/(.{1,76})/$1$eol/g; } $res; } ######################################################################## =item B Params: $data Return: $b64_decoded_data A perl implementation of base64 decoding. The perl code for this function was actually taken from an older MIME::Base64 perl module, and bears the following copyright: Copyright 1995-1999 Gisle Aas =cut sub decode_base64 { if ($MIMEBASE64_TRYLOADING) { eval "require MIME::Base64"; $MIMEBASE64_TRYLOADING = 0; } goto &MIME::Base64::decode_base64 if ($MIME::Base64::VERSION); my $str = shift; my $res = ""; $str =~ tr|A-Za-z0-9+=/||cd; $str =~ s/=+$//; # remove padding $str =~ tr|A-Za-z0-9+/| -_|; # convert to uuencoded format while ( $str =~ /(.{1,60})/gs ) { my $len = chr( 32 + length($1) * 3 / 4 ); # compute length byte $res .= unpack( "u", $len . $1 ); # uudecode } $res; } ######################################################################## } # end package variables ######################################################################## =item B Params: $data Return: $result This function encodes every character (except the / character) with normal URL hex encoding. =cut sub encode_uri_hex { # normal hex encoding my $str = shift; $str =~ s/([^\/])/sprintf("%%%02x",ord($1))/ge; return $str; } ######################################################################### =item B Params: $data Return: $result This function randomly encodes characters (except the / character) with normal URL hex encoding. =cut sub encode_uri_randomhex { # random normal hex encoding my @T = split( //, shift ); my $s; foreach (@T) { if (m#[;=:&@\?]#) { $s .= $_; next; } if ( ( rand() * 2 ) % 2 == 1 ) { $s .= sprintf( "%%%02x", ord($_) ); } else { $s .= $_; } } return $s; } ######################################################################### =item B Params: $data Return: $result This function randomly changes the case of characters in the string. =cut sub encode_uri_randomcase { my ( $x, $uri ) = ( '', shift ); return $uri if ( $uri !~ tr/a-zA-Z// ); # fast-path my @T = split( //, $uri ); for ( $x = 0 ; $x < ( scalar @T ) ; $x++ ) { if ( ( rand() * 2 ) % 2 == 1 ) { $T[$x] =~ tr/A-Za-z/a-zA-Z/; } } return join( '', @T ); } ######################################################################### =item B Params: $data Return: $result This function converts a normal string into Windows unicode format (non-overlong or anything fancy). =cut sub encode_unicode { my ( $c, $r ) = ( '', '' ); foreach $c ( split( //, shift ) ) { $r .= pack( "v", ord($c) ); } return $r; } ######################################################################### =item B Params: $unicode_string Return: $decoded_string This function attempts to decode a unicode (UTF-8) string by converting it into a single-byte-character string. Overlong characters are converted to their standard characters in place; non-overlong (aka multi-byte) characters are substituted with the 0xff; invalid encoding characters are left as-is. Note: this function is useful for dealing with the various unicode exploits/vulnerabilities found in web servers; it is *not* good for doing actual UTF-8 parsing, since characters over a single byte are basically dropped/replaced with a placeholder. =cut sub decode_unicode { my $str = $_[0]; return $str if ( $str !~ tr/!-~//c ); # fastpath my ( $lead, $count, $idx ); my $out = ''; my $len = length($str); my ( $ptr, $no, $nu ) = ( 0, 0, 0 ); while ( $ptr < $len ) { my $c = substr( $str, $ptr, 1 ); if ( ord($c) >= 0xc0 && ord($c) <= 0xfd ) { $count = 0; $c = ord($c) << 1; while ( ( $c & 0x80 ) == 0x80 ) { $c <<= 1; last if ( $count++ == 4 ); } $c = ( $c & 0xff ); for ( $idx = 1 ; $idx < $count ; $idx++ ) { my $o = ord( substr( $str, $ptr + $idx, 1 ) ); $no = 1 if ( $o != 0x80 ); $nu = 1 if ( $o < 0x80 || $o > 0xbf ); } my $o = ord( substr( $str, $ptr + $idx, 1 ) ); $nu = 1 if ( $o < 0x80 || $o > 0xbf ); if ($nu) { $out .= substr( $str, $ptr++, 1 ); } else { if ($no) { $out .= "\xff"; # generic replacement char } else { my $prior = ord( substr( $str, $ptr + $count - 1, 1 ) ) << 6; $out .= pack( "C", (( ord( substr( $str, $ptr + $count, 1 ) ) & 0x7f ) + $prior ) & 255 ); } $ptr += $count + 1; } $no = $nu = 0; } else { $out .= $c; $ptr++; } } return $out; } ######################################################################## =item B Params: \%request, $modes Return: nothing encode_anti_ids computes the proper anti-ids encoding/tricks specified by $modes, and sets up %hin in order to use those tricks. Valid modes are (the mode numbers are the same as those found in whisker 1.4): =over 4 =item 1 Encode some of the characters via normal URL encoding =item 2 Insert directory self-references (/./) =item 3 Premature URL ending (make it appear the request line is done) =item 4 Prepend a long random string in the form of "/string/../URL" =item 5 Add a fake URL parameter =item 6 Use a tab instead of a space as a request spacer =item 7 Change the case of the URL (works against Windows and Novell) =item 8 Change normal seperators ('/') to Windows version ('\') =item 9 Session splicing [NOTE: not currently available] =item A Use a carriage return (0x0d) as a request spacer =item B Use binary value 0x0b as a request spacer =back You can set multiple modes by setting the string to contain all the modes desired; i.e. $modes="146" will use modes 1, 4, and 6. =cut sub encode_anti_ids { my ( $rhin, $modes ) = ( shift, shift ); my ( @T, $x, $c, $s, $y ); my $ENCODED = 0; my $W = $$rhin{'whisker'}; return if ( !( defined $rhin && ref($rhin) ) ); # in case they didn't do it already $$rhin{'whisker'}->{'uri_orig'} = $$rhin{'whisker'}->{'uri'}; # note: order is important! # mode 9 - session splicing #if($modes=~/9/){ # $$rhin{'whisker'}->{'ids_session_splice'}=1; #} # mode 4 - prepend long random string if ( $modes =~ /4/ ) { $s = ''; if ( $$W{'uri'} =~ m#^/# ) { $y = &utils_randstr; $s .= $y while ( length($s) < 512 ); $$W{'uri'} = "/$s/.." . $$W{'uri'}; } } # mode 7 - (windows) random case sensitivity if ( $modes =~ /7/ ) { $$W{'uri'} = encode_uri_randomcase( $$W{'uri'} ); } # mode 2 - directory self-reference (/./) if ( $modes =~ /2/ ) { $$W{'uri'} =~ s#/#/./#g; } # mode 8 - windows directory separator (\) if ( $modes =~ /8/ ) { $$W{'uri'} =~ s#/#\\#g; $$W{'uri'} =~ s#^\\#/#; $$W{'uri'} =~ s#^([a-zA-Z0-9_]+):\\#$1://#; $$W{'uri'} =~ s#\\$#/#; } # mode 1 - random URI (non-UTF8) encoding if ( $modes =~ /1/ ) { if ( $ENCODED == 0 ) { $$W{'uri'} = encode_uri_randomhex( $$W{'uri'} ); $ENCODED = 1; } } # mode 5 - fake parameter if ( $modes =~ /5/ ) { ( $s, $y ) = ( &utils_randstr, &utils_randstr ); $$W{'uri'} = "/$s.html%3F$y=/../$$W{'uri'}"; } # mode 3 - premature URL ending if ( $modes =~ /3/ ) { $s = &utils_randstr; $$W{'uri'} = "/%20HTTP/1.1%0d%0aAccept%3a%20$s/../..$$W{'uri'}"; } # mode 6 - TAB as request spacer if ( $modes =~ /6/ ) { $$W{'http_space1'} = "\t"; } # mode A - CR as request spacer if ( $modes =~ /A/i ) { $$W{'http_space1'} = $$W{'http_space2'} = "\x0d"; } # mode B - 0x0b as request spacer if ( $modes =~ /B/i ) { $$W{'http_space1'} = $$W{'http_space2'} = "\x0b"; } } =item B The goal is to parse the variable, human-readable HTML into concrete structures useable by your program. The forms functions does do a good job at making these structures, but I will admit: they are not exactly simple, and thus not a cinch to work with. But then again, representing something as complex as a HTML form is not a simple thing either. I think the results are acceptable for what's trying to be done. Anyways... Forms are stored in perl hashes, with elements in the following format: $form{'element_name'}=@([ 'type', 'value', @params ]) Thus every element in the hash is an array of anonymous arrays. The first array value contains the element type (which is 'select', 'textarea', 'button', or an 'input' value of the form 'input-text', 'input-hidden', 'input-radio', etc). The second value is the value, if applicable (it could be undef if no value was specified). Note that select elements will always have an undef value--the actual values are in the subsequent options elements. The third value, if defined, is an anonymous array of additional tag parameters found in the element (like 'onchange="blah"', 'size="20"', 'maxlength="40"', 'selected', etc). The array does contain one special element, which is stored in the hash under a NULL character ("\0") key. This element is of the format: $form{"\0"}=['name', 'method', 'action', @parameters]; The element is an anonymous array that contains strings of the form's name, method, and action (values can be undef), and a @parameters array similar to that found in normal elements (above). Accessing individual values stored in the form hash becomes a test of your perl referencing skills. Hint: to access the 'value' of the third element named 'choices', you would need to do: $form{'choices'}->[2]->[1]; The '[2]' is the third element (normal array starts with 0), and the actual value is '[1]' (the type is '[0]', and the parameter array is '[2]'). =cut ################################################################ # Cluster global variables %_forms_ELEMENTS = ( 'form' => 1, 'input' => 1, 'textarea' => 1, 'button' => 1, 'select' => 1, 'option' => 1, '/select' => 1 ); ################################################################ =item B Params: \$html_data Return: \@found_forms This function parses the given $html_data into libwhisker form hashes. It returns a reference to an array of hash references to the found forms. =cut sub forms_read { my $dr = shift; return undef if ( !ref($dr) || length($$dr) == 0 ); my $A = [ {}, [] ]; html_find_tags( $dr, \&_forms_parse_callback, 0, $A, \%_forms_ELEMENTS ); if ( scalar %{ $A->[0] } ) { push( @{ $A->[1] }, $A->[0] ); } return $A->[1]; } ################################################################ =item B Params: \%form_hash Return: $html_of_form [undef on error] This function will take the given %form hash and compose a generic HTML representation of it, formatted with tabs and newlines in order to make it neat and tidy for printing. Note: this function does *not* escape any special characters that were embedded in the element values. =cut sub forms_write { my $hr = shift; return undef if ( !ref($hr) || !( scalar %$hr ) ); return undef if ( !defined $$hr{"\0"} ); my $t = '
[0] . '" method="'; $t .= $$hr{"\0"}->[1] . '" action="' . $$hr{"\0"}->[2] . '"'; if ( defined $$hr{"\0"}->[3] ) { $t .= ' ' . join( ' ', @{ $$hr{"\0"}->[3] } ); } $t .= ">\n"; my ( $name, $ar ); while ( ( $name, $ar ) = each(%$hr) ) { next if ( $name eq "\0" ); next if ( $name eq '' && $ar->[0]->[0] eq '' ); foreach $a (@$ar) { my $P = ''; $P = ' ' . join( ' ', @{ $$a[2] } ) if ( defined $$a[2] ); $t .= "\t"; if ( $$a[0] eq 'textarea' ) { $t .= "\n"; } elsif ( $$a[0] =~ m/^input-(.+)$/ ) { $t .= "\n"; } elsif ( $$a[0] eq 'option' ) { $t .= "\t