The Fun Part

After having set up and hardened my Wordpress server, I now moved on to performing some vulnerability assessments with an aim to further secure my server. There were four main tools I used for this task:

  • Nmap
  • Nessus
  • Nikto
  • Wpscan

All of which, rather conveniently, come preinstalled with Kali Linux.

Nmap

My nmap scan was fairly stock standard, I used to following command:

sudo nmap -sV -sC cyruswilkie.ddns.net

And got the following output:

Starting Nmap 7.91 ( https://nmap.org ) at 2021-11-21 11:20 AEDT
Nmap scan report for cyruswilkie.ddns.net (139.180.182.74)
Host is up (0.013s latency).
rDNS record for 139.180.182.74: 139.180.182.74.vultr.com
Not shown: 998 filtered ports
PORT    STATE SERVICE VERSION
22/tcp  open  ssh     OpenSSH 8.4p1 Ubuntu 5ubuntu1.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 08:fe:61:3a:dd:3e:fb:d6:d5:d5:d8:0f:6f:c5:74:62 (RSA)
|   256 a1:68:38:49:c5:f9:b2:46:c4:6a:09:f4:8b:36:75:9a (ECDSA)
|_  256 78:3d:19:34:15:4d:c1:04:b8:74:de:4d:18:e7:d7:2c (ED25519)
443/tcp open  ssl/ssl Apache httpd (SSL-only mode)
|_http-generator: WordPress 5.8.2
| http-robots.txt: 1 disallowed entry 
|_/wp-admin/
|_http-server-header: Apache
|_http-title: Welcome to the world's greatest website – Feel free...
|_http-trane-info: Problem with XML parsing of /evox/about
| ssl-cert: Subject: commonName=cyruswilkie.ddns.net
| Subject Alternative Name: DNS:cyruswilkie.ddns.net
| Not valid before: 2021-10-14T22:23:43
|_Not valid after:  2022-01-12T22:23:42
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 72.82 seconds

Everything here looked basically as expected, only ports 22 and 443 were open and both those ports were happily running my SSH service and Apache server respectively. Nmap’s default scripts didn’t find anything of note here, so I moved on to my next assessment.

Nessus

Performing a Nessus scan in and of itself was a bit of a learning experience for me since I had never even heard of Nessus before I embarked on this project. The basic process for setting up my Nessus scan was as follows:

After having installed Nessus and set up an account, I navigated to https://localhost:8834/ in my browser, once there I hit the ‘New Scan’ button.

Nessus new scan page

I then opted for the basic network scan and filled out the resulting form as follows.

Setting up scan

Once the scan had been set up and configured, I went ahead and launched it.

Launching the scan

After running for a number of minutes it returned the following.

Results of my scan

Most of Nessus’s results were purely informative, however there was one medium risk factor issue identified.

The following certificate was part of the certificate chain sent by the remote host, but it has expired :

|-Subject	: O=Digital Signature Trust Co./CN=DST Root CA X3
|-Not After	: Sep 30 14:01:15 2021 GMT

I was a little perplexed by this issue, since my browser viewed my certificate as valid. I suspect this issue may have been caused by a certificate further up in the chain, which is likely beyond my control, so for that reason I chose to just leave this issue be.

Nikto

Next up was a quick Nikto scan. To perform this scan I simply used the following command.

nikto -h cyruswilkie.ddns.net -ssl

And received the following output.

- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          139.180.182.74
+ Target Hostname:    cyruswilkie.ddns.net
+ Target Port:        443
---------------------------------------------------------------------------
+ SSL Info:        Subject:  /CN=cyruswilkie.ddns.net
                   Ciphers:  TLS_AES_256_GCM_SHA384
                   Issuer:   /C=US/O=Let's Encrypt/CN=R3
+ Start Time:         2021-11-21 11:35:46 (GMT11)
---------------------------------------------------------------------------
+ Server: Apache
+ Uncommon header 'link' found, with multiple values: (<https://cyruswilkie.ddns.net/wp-json/>; rel="https://api.w.org/",<https://cyruswilkie.ddns.net/wp-json/wp/v2/pages/5>; rel="alternate"; type="application/json",<https://cyruswilkie.ddns.net/>; rel=shortlink,)
+ Uncommon header 'x-redirect-by' found, with contents: WordPress
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Entry '/wp-admin/' in robots.txt returned a non-forbidden or redirect HTTP code (302)
+ "robots.txt" contains 2 entries which should be manually viewed.
+ The Content-Encoding header is set to "deflate" this may mean that the server is vulnerable to the BREACH attack.
+ Web Server returns a valid response with junk HTTP methods, this may cause false positives.
+ /wp-links-opml.php: This WordPress script reveals the installed version.
+ OSVDB-3092: /license.txt: License file found may identify site software.
+ /wp-app.log: Wordpress' wp-app.log may leak application/system details.
+ /wordpresswp-app.log: Wordpress' wp-app.log may leak application/system details.
+ /: A Wordpress installation was found.
+ /wordpress: A Wordpress installation was found.
+ /wp-admin/wp-login.php: Wordpress login found
+ /wordpresswp-admin/wp-login.php: Wordpress login found
+ /blog/wp-login.php: Wordpress login found
+ /wp-login.php: Wordpress login found
+ /wordpresswp-login.php: Wordpress login found
+ 7867 requests: 0 error(s) and 17 item(s) reported on remote host
+ End Time:           2021-11-21 11:49:51 (GMT11) (845 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

Nikto picked up a number of interesting things, along with some issues I didn’t entirely understand, chief among those being the reference to BREACH attacks. Nikto also claimed to have found my Wordpress login page, but in actual fact it hadn’t, I’m unsure as to exactly why Nikto claimed this since all the pages mentioned returned 404 responses when accessed through my browser.

The wp-links-opml.php finding interested me, I would have assumed that trying to keep Wordpress version information secret would have been a priority in terms of security, but this script freely divulges it and I couldn’t find a way to prevent it from doing so.

WPScan

Lucky last on my checklist of scans was WPScan, I ran it using the following command.

wpscan --url https://cyruswilkie.ddns.net/ -e u

And received the following output.

_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|

         WordPress Security Scanner by the WPScan Team
                         Version 3.8.19
       Sponsored by Automattic - https://automattic.com/
       @_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________

[i] It seems like you have not updated the database for some time.
[?] Do you want to update now? [Y]es [N]o, default: [N]
[+] URL: https://cyruswilkie.ddns.net/ [139.180.182.74]
[+] Started: Sun Nov 21 11:55:11 2021

Interesting Finding(s):

[+] Headers
 | Interesting Entries:
 |  - Server: Apache
 |  - Content-Security-Policy: script-src 'self' ; img-src 'self' data: https://secure.gravatar.com/; object-src 'self' data: ; frame-src 'self' data: ;
 |  - Referrer-Policy: no-referrer
 |  - Expect-CT: enforce, max-age=43200, report-uri="https://somedomain.com/report"
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[+] robots.txt found: https://cyruswilkie.ddns.net/robots.txt
 | Interesting Entries:
 |  - /wp-admin/
 |  - /wp-admin/admin-ajax.php
 | Found By: Robots Txt (Aggressive Detection)
 | Confidence: 100%

[+] WordPress readme found: https://cyruswilkie.ddns.net/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] The external WP-Cron seems to be enabled: https://cyruswilkie.ddns.net/wp-cron.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 60%
 | References:
 |  - https://www.iplocation.net/defend-wordpress-from-ddos
 |  - https://github.com/wpscanteam/wpscan/issues/1299

Fingerprinting the version - Time: 00:00:00 <==============> (657 / 657) 100.00% Time: 00:00:00
[+] WordPress version 5.8.1 identified (Latest, released on 2021-09-09).
 | Found By: Unique Fingerprinting (Aggressive Detection)
 |  - https://cyruswilkie.ddns.net/wp-admin/js/customize-controls.min.js md5sum is 39495c84838efe87882af857c2affd5e

[+] WordPress theme in use: twentytwenty
 | Location: https://cyruswilkie.ddns.net/wp-content/themes/twentytwenty/
 | Latest Version: 1.8 (up to date)
 | Last Updated: 2021-07-22T00:00:00.000Z
 | Readme: https://cyruswilkie.ddns.net/wp-content/themes/twentytwenty/readme.txt
 | Style URL: https://cyruswilkie.ddns.net/wp-content/themes/twentytwenty/style.css?ver=1.8
 | Style Name: Twenty Twenty
 | Style URI: https://wordpress.org/themes/twentytwenty/
 | Description: Our default theme for 2020 is designed to take full advantage of the flexibility of the block editor...
 | Author: the WordPress team
 | Author URI: https://wordpress.org/
 |
 | Found By: Css Style In Homepage (Passive Detection)
 | Confirmed By: Css Style In 404 Page (Passive Detection)
 |
 | Version: 1.8 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - https://cyruswilkie.ddns.net/wp-content/themes/twentytwenty/style.css?ver=1.8, Match: 'Version: 1.8'

[+] Enumerating Users (via Passive and Aggressive Methods)
 Brute Forcing Author IDs - Time: 00:00:00 <=================> (10 / 10) 100.00% Time: 00:00:00

[i] User(s) Identified:

[+] cyruswilkie
 | Found By: Author Posts - Author Pattern (Passive Detection)
 | Confirmed By:
 |  Wp Json Api (Aggressive Detection)
 |   - https://cyruswilkie.ddns.net/wp-json/wp/v2/users/?per_page=100&page=1
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)

[+] CyrusWilkie
 | Found By: Rss Generator (Passive Detection)
 | Confirmed By: Rss Generator (Aggressive Detection)

[+] an_obscure_individual
 | Found By: Wp Json Api (Aggressive Detection)
 |  - https://cyruswilkie.ddns.net/wp-json/wp/v2/users/?per_page=100&page=1
 | Confirmed By:
 |  Oembed API - Author URL (Aggressive Detection)
 |   - https://cyruswilkie.ddns.net/wp-json/oembed/1.0/embed?url=https://cyruswilkie.ddns.net/&format=json
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)

[!] No WPScan API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 25 daily requests by registering at https://wpscan.com/register

[+] Finished: Sun Nov 21 11:55:18 2021
[+] Requests Done: 70
[+] Cached Requests: 11
[+] Data Sent: 16.436 KB
[+] Data Received: 814.03 KB
[+] Memory used: 178.277 MB
[+] Elapsed time: 00:00:06

The thing that shocked me most about this particular scan was its ability to find my admin user account, an_obscure_individual. Despite having though I’d hidden this account fairly well, WPScan was able to find it in the endpoint https://cyruswilkie.ddns.net/wp-json/wp/v2/. Even more annoyingly, I couldn’t find any easy way to prevent this endpoint from divulging this information, while security through obscurity should never be wholely relied upon, it is nice to have it there as a first line defence.

Luckily, WPScan was unable to find my login page, meaning that any potential attacker would still have a hard time leveraging the discovery of my admin account. Any attacker that did find my login page, would also have to deal with my login attempt limiter plugin, so I still felt that my site as a whole was fairly safe.

Overall this was yet another informative experience that demonstrated how hard it can be to secure a web server. The most notable part of this experience for me was how it showed the limits of web server hardening when using a service such as Wordpress, for all my previous efforts I was still limited in my ability to secure the server by endpoints such as /wp-json/wp/v2/ and wp-links-opml.php which weren’t easily configurable.