Featured image

tl;dr skip to Using Nikto yourself

What is Nikto? Link to heading

Nikto is a useful tool for securing your website, differing greatly from tools like Nessus in both results and methods. While Nessus is scanning for CVEs in operating systems and network devices, Nikto scans your website for common misconfigurations, exposed sensitive files, evidence of known exploits etc. Nikto isn’t checking your networking hardware for firmware exploits, but it will tell you that you’re serving https://hugolee.xyz/.git/config to the public. Like all good tools, Nikto is free and Open-source.

What does Nikto look like? Link to heading

Before beginning, I should preface that scanning anything but your own website is at the very least rude and at the most illegal, it’s also very obvious, so don’t go running it against Google and Microsoft unless you’re prepared to have your IP Address been blacklisted from 90% of the internet and the police at your door.

Here’s a quick example of what Nikto yields for hugolee.xyz:

nikto.pl -host hugolee.xyz
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          65.109.141.163
+ Target Hostname:    hugolee.xyz
+ Target Port:        443
---------------------------------------------------------------------------
+ SSL Info:        Subject:  /CN=hugolee.xyz
                   Altnames: commento.hugolee.xyz, gitea.hugolee.xyz, hugolee.xyz, matomo.hugolee.xyz, www.hugolee.xyz
                   Ciphers:  TLS_AES_256_GCM_SHA384
                   Issuer:   /C=US/O=Let's Encrypt/CN=R3
+ Start Time:         2023-06-22 20:08:06 (GMT1)
---------------------------------------------------------------------------
+ Server: nginx
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ The Content-Encoding header is set to "deflate" this may mean that the server is vulnerable to the BREACH attack.
+ Multiple index files found: /index.xml, /index.html
+ OSVDB-3092: /sitemap.xml: This gives a nice listing of the site content.
+ 7970 requests: 0 error(s)a and 3 item(s) reported on remote host
+ End Time:           2023-06-22 20:41:47 (GMT1) (2021 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

We can see here a few different results separated into sections, different sites will have wildly different sections here depending on which plugins turn out to be relevant. A small static site like mine hosted on a raw NGINX instance doesn’t have much going on besides web server misconfigurations, but a big dynamically generated PHP/MySQL site or fancy responsive JavaScript application has many more moving parts, microservices, configuration files etc. ready for Nikto to scope out.

What do I do with the results? Link to heading

Not everything Nikto tells you is bad, a vulnerability, or in need of intervention. What you’ve gained is the same information you can expect an attacker to hold. Looking at our example, we can see the first two sections are just information about the current job and Nikto:

- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          65.109.141.163
+ Target Hostname:    hugolee.xyz
+ Target Port:        443
---------------------------------------------------------------------------

Next, we have our first plugin results, information about the SSL certificate:

---------------------------------------------------------------------------
+ SSL Info:        Subject:  /CN=hugolee.xyz
                   Altnames: commento.hugolee.xyz, gitea.hugolee.xyz, hugolee.xyz, matomo.hugolee.xyz, www.hugolee.xyz
                   Ciphers:  TLS_AES_256_GCM_SHA384
                   Issuer:   /C=US/O=Let's Encrypt/CN=R3
+ Start Time:         2023-06-22 20:08:06 (GMT1)
---------------------------------------------------------------------------

The SSL certificate is used to verify ownership for encryption purposes, I use the same certificate for all sites hosted on this server, and you can see these listed in the Altnames section, including my Gitea, Commento, and Matomo instances.
I’m not trying to hide these sites, but these results are a useful indication of how and where prying eyes can find your endpoints which will become targets for further scanning. If exposing endpoints within your SSL certificate is a concern to you, then you can mitigate it by using a wildcard cert instead, which instead of specifying specific subdomains simply has a * entry; the altnames section would then look something like this:
Altnames: *.hugolee.xyz, hugolee.xyz

However, you must not rely on secret URLs to secure your site. A skilled hacker will use wordlists and other information they’ve scraped to find any and all exposed sites on your server. If you’re hosting a non-hardened web-service publicly then expect it to be viewed by everyone, scrutinised by experts, and breached by criminals.

The next section is an analysis of Nginx, the web server software:

---------------------------------------------------------------------------
+ Server: nginx
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ The Content-Encoding header is set to "deflate" this may mean that the server is vulnerable to the BREACH attack.
+ Multiple index files found: /index.xml, /index.html
+ OSVDB-3092: /sitemap.xml: This gives a nice listing of the site content.
+ 7970 requests: 0 error(s) and 3 item(s) reported on remote host
+ End Time:           2023-06-22 20:41:47 (GMT1) (2021 seconds)
---------------------------------------------------------------------------

Here we can see notices for:

  • No CGI Directories found.
  • The “deflate” Content-Encoder header, suggesting a vulnerability to the BREACH attack.
  • Multiple index files found.
  • A Sitemap at /sitemap.xml
  • Statistics and timings.

Below are a descriptions and mitigations for the results Nikto found for my website. Your website will likely report different results not covered here. You should research what each result means and the best mitigations for your situation. Once changes are made you can always run Nikto again to confirm if the issues are still present. Remember, not all results are vulnerabilities; consider whether you’re comfortable for an attacker to have the knowledge Nikto has given to yourself, if not, then act upon it.

CGI Directories Link to heading

Common Gateway Interface (CGI) is a system that allows clients of a web-server to execute server-side code. The PHP language is a popular example of CGI being used. The CGI directories mentioned by Nikto refers to its attempt to locate exposed directories containing CGI scripts. In the early days of CGI use, Apache and other web servers provided a number of example scripts that could be found and used to execute server-side code. To make matters worse these scripts were vulnerable to code-injection and returned the command output to the client, this made it possible to run any server-side code the attacker wanted and receive the output. The directories Nikto is searching for are not necessarily vulnerable to code-injection, but exposing all the available server-side scripts to an attacker would be a poor idea. You can’t pick the lock if you can’t find the door.

CGI Directories Mitigations Link to heading

You should always disable directory indexing wherever it’s not necessary. This means when a client accesses a directory on a web-server they are met with a 403 error instead of a list of files within:

BREACH Vulnerability Link to heading

BREACH is an exploit for decrypting SSL web data through a Man-In-The-Middle (MITM) attack by exploiting the way compression works over HTTP. As described in the Nikto output, web servers that have enabled HTTP compression through deflate or gzip are vulnerable. By comparing the size of data returned when modifying payloads it’s possible to slowly but surely calculate the encryption key used and perform session hijacking.

BREACH Vulnerability Mitigations Link to heading

BREACH can be mitigated entirely by simply disabling HTTP compression. For small static sites this is not an issue, however if you’re running a large web application, then losing compression will have a significant impact on your web server’s performance; if you’re paying by CPU and bandwidth usage then it will come at a cost too.

The alternative mitigation is called Heal-The-Breach (HTB) which adds random-sized padding to compressed data. While this does create an additional CPU and bandwidth overhead, the resulting payload is still significantly smaller, and the CPU usage is minimal, keeping the compression as a major net positive. Unfortunately… it looks like no one’s interested in implementing HTB. Microsoft say they are not implementing it into dotnet, claiming it doesn’t resolve the problem only delays it. Microsoft recommend disabling compression for pages with sensitive data, well… they probably know what they’re talking about… right? Luckily for me, my little site, small and static, isn’t really affected whether I turn compression on or off, for everyone else though… good luck ๐Ÿ‘.

HTTP compression can be disabled by changing gzip on; to gzip off; in the nginx.conf, your configuration may differ see the resources below.

Multiple Index Files Link to heading

Typically, a web page has a single index file which is served as the default page. In my case, the index.html is the default web-page and index.xml is for RSS support, auto-generated by the HUGO engine.

Multiple Index files Mitigations Link to heading

Nikto discovering multiple index files is not necessarily a bad result or a sign of misconfiguration. You should check each file flagged and confirm for yourself whether these files should be available or not. Remove any files that should not be accessed, or deny them using your web server config.

Sitemap Link to heading

A sitemap is a file that lists every page on your website, in the early days of the internet they would be sitemaps designed for people to read, to see a listing of every web-page, so they can easily jump to the content they want. The internet doesn’t work quite the same any more, but sitemaps still have their uses for Search Engine Optimization (SEO). Having a valid sitemap listing all the public pages on your website allows search engines like Google to index every page on your site and present them in relevant search results. It also allows an attacker to get an accurate and easily-parsable listing of every page you have, this might be used to scrape your site for information, or run scripts like Nikto to scrutinise the security of each page.

Sitemap Mitigations Link to heading

If your website isn’t meant to be publicly listed, and you’re not interested in SEO, then you can simply remove or deny access to the sitemap. However, if you do need the sitemap you should check its contents and confirm each link is meant to be publicly-facing. Remove any pages that do not need to be indexed, such as pages behind logins.

Using Nikto yourself Link to heading

  1. First let’s download Nikto:
    git clone https://github.com/sullo/nikto ~/nikto

  2. Now, run Nikto:
    ~/nikto/program/nikto.pl With no parameters, Nikto will print out the full help page so you can see all the options available.

  3. You can easily start a scan on your own website using the following command:
    ~/nikto/program/nikto.pl -host <website> 2>&1 | tee ~/nikto-results.txt
    Swap out <website> with the URL of your own site. The above command outputs results into the text file ~/nikto-results.txt
    2>&1 | tee is just some magic Bash syntax to redirect the terminal output into a text file, while also showing it in the terminal. It’s best not to think about why or how it works.
    You can also use the -output <filename> switch, although by default this doesn’t show as much information as in the terminal.

  4. You can scan multiple hosts in a row by creating a text file with each host on one line, you can also specify ports and protocols if needed:

    hugolee.xyz
    https://web1.hugolee.xyz
    http://db.hugolee.xyz:9090
    

    ~/nikto/program/nikto.pl -host hosts.txt -output . -Format txt
    Using -output . makes Nikto auto-name the output file, when doing this you also need to specify the output format using -Format, you can see the list of available formats here.

Results in Nikto are separated by lines depending on which plugin it came from, plugins specify which tests are performed. By default, all plugins are run, this takes about 40 - 60 minutes; if you’re short on time you can try selecting only the relevant plugins for your server; for example, if you know you’re running an Nginx web server, you don’t need to include the Apache plugins etc.

  • See the full list of plugins available:
    ~/nikto/program/nikto.pl -list-plugins

  • Run your test with just a single plugin:
    ~/nikto/program/nikto.pl -host <website> -Plugins "apache_expect_xss(verbose,debug)"

Nikto has many more parameters available, for the full usage see the official wiki.

๐ŸŽŠ ๐ŸŽ‰ Hurray! We’ve completed security! ๐ŸŽ‰ ๐ŸŽŠ
Sadly not… the output from Nikto are results, not solutions. Check up on What do I do with the results? to find out what you should do next.

Detecting Nikto scans Link to heading

I mentioned earlier the importance of using Nikto for only sites you have explicit permission to run against. Nikto is not by any means a passive scanning tool, any web-sever will generate a tonne of logs and any decent reporting system will immediately flag your IP as suspicious.

Here’s an example of what my NGINX logs say while I run Nikto:

hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /hugolee_xyz.tar.lzma HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /hugolee_xyz.tar.lzma HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /dump.tar.lzma HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /dump.tar.lzma HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /dump.egg HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /dump.egg HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /archive.tgz HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /archive.tgz HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /site.jks HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /site.jks HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /xyz.gz HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /xyz.gz HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /hugolee.xyz.war HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /hugolee.xyz.war HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:48 +0000] "GET /hugolee.cer HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /hugolee.cer HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /hugoleexyz.sql HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /hugoleexyz.sql HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /dump.jks HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /dump.jks HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /site.egg HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /site.egg HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /hugolee.xyz.jks HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
65.109.141.163 <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /hugolee.xyz.jks HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
hugolee.xyz <My IP Address> - - [05/Jul/2023:22:18:49 +0000] "GET /backup.tgz HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"

That’s it! I hope this guide has been useful and informative for you. This time maybe you really have achieved security*, tick that box on your project plan and proceed with confidence! ๐Ÿš€ ๐Ÿš€ ๐Ÿš€

  • Security Project โœ…

Remember only scan websites you own, or have the explicit permission from the owner to scan.

*This article is not security advice, your data is at risk, always consult a licensed security practitioner before storing data online. This article and its contents are provided with absolutely no warranty, use at your own risk.