9.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
10 High
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
NONE
Confidentiality Impact
COMPLETE
Integrity Impact
COMPLETE
Availability Impact
COMPLETE
AV:N/AC:L/Au:N/C:C/I:C/A:C
0.973 High
EPSS
Percentile
99.8%
Checks for backups and swap files of common content management system and web server configuration files.
When web server files are edited in place, the text editor can leave backup or swap files in a place where the web server can serve them. The script checks for these files:
wp-config.php
: WordPressconfig.php
: phpBB, ExpressionEngineconfiguration.php
: JoomlaLocalSettings.php
: MediaWiki/mediawiki/LocalSettings.php
: MediaWikimt-config.cgi
: Movable Typemt-static/mt-config.cgi
: Movable Typesettings.php
: Drupal.htaccess
: ApacheAnd for each of these file applies the following transformations (using config.php
as an example):
config.bak
: Generic backup.config.php.bak
: Generic backup.config.php~
: Vim, Gedit.#config.php#
: Emacs.config copy.php
: Mac OS copy.Copy of config.php
: Windows copy.config.php.save
: GNU Nano..config.php.swp
: Vim swap.config.php.swp
: Vim swap.config.php.old
: Generic backup.This script is inspired by the CMSploit program by Feross Aboukhadijeh: <http://www.feross.org/cmsploit/>.
directory to save all the valid config files found
the path where the CMS is installed
See the documentation for the slaxml library.
See the documentation for the smbauth library.
See the documentation for the http library.
nmap --script=http-config-backup <target>
PORT STATE SERVICE REASON
80/tcp open http syn-ack
| http-config-backup:
| /%23wp-config.php%23 HTTP/1.1 200 OK
|_ /config.php~ HTTP/1.1 200 OK
local coroutine = require "coroutine"
local http = require "http"
local io = require "io"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
local url = require "url"
description = [[
Checks for backups and swap files of common content management system
and web server configuration files.
When web server files are edited in place, the text editor can leave
backup or swap files in a place where the web server can serve them. The
script checks for these files:
* <code>wp-config.php</code>: WordPress
* <code>config.php</code>: phpBB, ExpressionEngine
* <code>configuration.php</code>: Joomla
* <code>LocalSettings.php</code>: MediaWiki
* <code>/mediawiki/LocalSettings.php</code>: MediaWiki
* <code>mt-config.cgi</code>: Movable Type
* <code>mt-static/mt-config.cgi</code>: Movable Type
* <code>settings.php</code>: Drupal
* <code>.htaccess</code>: Apache
And for each of these file applies the following transformations (using
<code>config.php</code> as an example):
* <code>config.bak</code>: Generic backup.
* <code>config.php.bak</code>: Generic backup.
* <code>config.php~</code>: Vim, Gedit.
* <code>#config.php#</code>: Emacs.
* <code>config copy.php</code>: Mac OS copy.
* <code>Copy of config.php</code>: Windows copy.
* <code>config.php.save</code>: GNU Nano.
* <code>.config.php.swp</code>: Vim swap.
* <code>config.php.swp</code>: Vim swap.
* <code>config.php.old</code>: Generic backup.
This script is inspired by the CMSploit program by Feross Aboukhadijeh:
http://www.feross.org/cmsploit/.
]];
---
-- @usage
-- nmap --script=http-config-backup <target>
--
-- @output
-- PORT STATE SERVICE REASON
-- 80/tcp open http syn-ack
-- | http-config-backup:
-- | /%23wp-config.php%23 HTTP/1.1 200 OK
-- |_ /config.php~ HTTP/1.1 200 OK
--
-- @args http-config-backup.path the path where the CMS is installed
-- @args http-config-backup.save directory to save all the valid config files found
--
author = "Riccardo Cecolin";
license = "Same as Nmap--See https://nmap.org/book/man-legal.html";
categories = { "auth", "intrusive" };
portrule = shortport.http;
local function make_grep(pattern)
return function(s)
return string.match(s, pattern)
end
end
local grep_php = make_grep("<%?php");
local grep_cgipath = make_grep("CGIPath");
local function check_htaccess(s)
return string.match("<Files") or string.match(s, "RewriteRule")
end
local CONFIGS = {
{ filename = "wp-config.php", check = grep_php }, -- WordPress
{ filename = "config.php", check = grep_php }, -- phpBB, ExpressionEngine
{ filename = "configuration.php", check = grep_php }, -- Joomla
{ filename = "LocalSettings.php", check = grep_php }, -- MediaWiki
{ filename = "/mediawiki/LocalSettings.php", check = grep_php }, -- MediaWiki
{ filename = "mt-config.cgi", check = grep_cgipath }, -- Movable Type
{ filename = "mt-static/mt-config.cgi", check = grep_cgipath }, -- Movable Type
{ filename = "settings.php", check = grep_php }, -- Drupal
{ filename = ".htaccess", check = check_htaccess }, -- Apache
};
-- Return directory, filename pair. directory may be empty.
local function splitdir(path)
local dir, filename
dir, filename = string.match(path, "^(.*/)(.*)$")
if not dir then
dir = ""
filename = path
end
return dir, filename
end
-- Return basename, extension pair. extension may be empty.
local function splitext(filename)
local base, ext;
base, ext = string.match(filename, "^(.+)(%..+)")
if not base then
base = filename
ext = ""
end
return base, ext
end
-- Functions mangling filenames.
local TRANSFORMS = {
function(fn)
local base, ext = splitext(fn);
if ext ~= "" then
return base .. ".bak" -- generic bak file
end
end,
function(fn) return fn .. ".bak" end,
function(fn) return fn .. "~" end, -- vim, gedit
function(fn) return "#" .. fn .. "#" end, -- Emacs
function(fn)
local base, ext = splitext(fn);
return base .. " copy" .. ext -- mac copy
end,
function(fn) return "Copy of " .. fn end, -- windows copy
function(fn) return fn .. ".save" end, -- nano
function(fn) if string.sub(fn, 1, 1) ~= "." then return "." .. fn .. ".swp" end end, -- vim swap
function(fn) return fn .. ".swp" end, -- vim swap
function(fn) return fn .. ".old" end, -- generic backup
};
---
--Creates combinations of backup names for a given filename
--Taken from: http-backup-finder.nse
local function backupNames (filename)
local dir, basename;
dir, basename = splitdir(filename);
return coroutine.wrap(function()
for _, transform in ipairs(TRANSFORMS) do
local result = transform(basename);
if result == nil then
elseif type(result) == "string" then
coroutine.yield(dir .. result);
result = {result}
elseif type(result) == "table" then
for _, r in ipairs(result) do
coroutine.yield(dir .. r);
end
end
end
end)
end
---
--Writes string to file
--Taken from: hostmap.nse
-- @param filename Filename to write
-- @param contents Content of file
-- @return True if file was written successfully
local function write_file (filename, contents)
local f, err = io.open(filename, "w");
if not f then
return f, err;
end
f:write(contents);
f:close();
return true;
end
action = function (host, port)
local path = stdnse.get_script_args("http-config-backup.path") or "/";
local save = stdnse.get_script_args("http-config-backup.save");
local backups = {};
if not path:match("/$") then
path = path .. "/";
end
if not path:match("^/") then
path = "/" .. path;
end
if (save and not(save:match("/$") ) ) then
save = save .. "/";
end
local status_404, result_404, known_404 = http.identify_404(host, port)
if not status_404 then
stdnse.debug1("Can't distinguish 404 response. Quitting.")
return stdnse.format_output(false, "Can't determine file existence")
end
-- for each config file
for _, cfg in ipairs(CONFIGS) do
-- for each alteration of the filename
for entry in backupNames(cfg.filename) do
local url_path
url_path = url.build({path = path .. entry});
-- http request
local response = http.get(host, port, url_path);
-- if it's not 200, don't bother. If it is, check that it's not a false 404
if response.status == 200 and http.page_exists(response, result_404, known_404, url_path) then
-- check it if is valid before inserting
if cfg.check(response.body) then
local filename = stdnse.escape_filename((host.targetname or host.ip) .. url_path)
-- save the content
if save then
local status, err = write_file(save .. filename, response.body);
if status then
stdnse.debug1("%s saved", filename);
else
stdnse.debug1("error saving %s", err);
end
end
table.insert(backups, url_path .. " " .. response["status-line"]);
else
stdnse.debug1("%s: found but not matching: %s",
host.targetname or host.ip, url_path);
end
end
end
end
return stdnse.format_output(true, backups);
end;
9.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
10 High
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
NONE
Confidentiality Impact
COMPLETE
Integrity Impact
COMPLETE
Availability Impact
COMPLETE
AV:N/AC:L/Au:N/C:C/I:C/A:C
0.973 High
EPSS
Percentile
99.8%