The Nmap Scripting Engine offers a library to handle requests and other common functions of an HTTP client. With this library, NSE developers can accomplish many tasks, from information gathering to vulnerability exploitation.
This recipe will show you how to use the HTTP library to send an HTTP request to identify vulnerable Trendnet TV-IP110W webcams.
Trendnet TV-IP110W webcams allow unauthenticated access to their video feed by simply requesting the URI /anony/mjpg.cgi
. Let's write an NSE script to detect these devices. For now, let's ignore the documentation tags:
http-trendnet-tvip110w.nse
and start by filling up the NSE script basic information fields:description = [[ Attempts to detect webcams Trendnet TV-IP110W vulnerable to unauthenticated access to the video stream by querying the URI "/anony/mjpg.cgi". Original advisory: http://console-cowboys.blogspot.com/2012/01/trendnet-cameras-i-always-feel-like.html ]] categories = {"exploit","vuln"}
local http = require "http" local shortport = require "shortport" local stdnse = require "stdnse"
shortport.http
to tell Nmap to execute the script when a web server is found:portrule = shortport.http
/anony/mjpg.cgi
and checking for status code 200:action = function(host, port) local uri = "/anony/mjpg.cgi" local _, status_404, resp_404 = http.identify_404(host, port) if status_404 == 200 then stdnse.print_debug(1, "%s: Web server returns ambiguous response. Trendnet webcams return standard 404 status responses. Exiting.", SCRIPT_NAME) return end stdnse.print_debug(1, "%s: HTTP HEAD %s", SCRIPT_NAME, uri) local resp = http.head(host, port, uri) if resp.status and resp.status == 200 then return string.format("Trendnet TV-IP110W video feed is unprotected:http://%s/anony/mjpg.cgi", host.ip) end end
$ nmap -p80 -n -Pn --script http-trendnet-tvip110w.nse <target>
PORT STATE SERVICE REASON 80/tcp open http syn-ack |_http-trendnet-tvip110w: Trendnet TV-IP110W video feed is unprotected:http://192.168.4.20/anony/mjpg.cgi
The complete script with documentation tags can be downloaded from https://github.com/cldrn/nmap-nse-scripts/blob/master/scripts/6.x/http-trendnet-tvip110w.nse.
In the script http-trendnet-tvip110w.nse
, we defined the execution rule with the alias http
from the shortport
library:
portrule = shortport.http
The alias shortport.http
is defined in the file /nselib/shortport.lua
as follows:
LIKELY_HTTP_PORTS = { 80, 443, 631, 7080, 8080, 8088, 5800, 3872, 8180, 8000 } LIKELY_HTTP_SERVICES = { "http", "https", "ipp", "http-alt", "vnc-http", "oem-agent", "soap", "http-proxy", } http = port_or_service(LIKELY_HTTP_PORTS, LIKELY_HTTP_SERVICES)
The http
library has methods such as http.head()
, http.get()
, and http.post()
corresponding to the common HTTP methods HEAD
, GET
, and POST
respectively, but it also has a generic method named http.generic_request()
to allow more flexibility to developers who may want to try more obscure HTTP verbs.
In the script http-trendnet-tvip110w
, we used the function http.head()
to retrieve the URI /anony/mjpg.cgi
:
local resp = http.head(host, port, uri)
The function http.head()
returns a table containing the following response information:
status-line
: Contains the returned status line. For example, HTTP/1.1 404 Not Found
.status
: Contains the status code returned by the web server.body
: Contains the response body.cookies
: Table of cookies set by the web server.header
: Associative table where the returned headers are stored. The name of the header is used as an index. For example, header["server"]
contains the Server field returned by the web server.rawheader
: Numbered array of headers in the same order as they were sent by the web server.The library stdnse
is also used in the script http-trendnet-tvip110w.nse
. This library is a collection of miscellaneous functions that come in handy when writing NSE scripts. The script used the function stdnse.print_debug()
, a function to print debugging messages:
stdnse.print_debug(<debug level required>, <format string>, arg1, arg2...)
The complete documentation for these libraries can be found at http://nmap.org/nsedoc/lib/http.html and http://nmap.org/nsedoc/lib/stdnse.html.
Some web servers do not return regular status 404 code responses when a page does not exist, and instead return status code 200 all the time. This is an aspect that is often overlooked and even I have made the mistake before of assuming that a status of 200 meant that the URI exists. We need to be careful with this to avoid false positives in our scripts. The functions http.identify_404()
and http.page_exists()
were created to identify if a server returns regular 404 responses and if a given page exists.
local status_404, req_404, page_404 = http.identify_404(host, port)
If the function http.identify_404(host, port)
was successful, we can use http.page_exists()
:
if http.page_exists(data, req_404, page_404, uri, true) then stdnse.print_debug(1, "Page exists! → %s", uri) end
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>
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)
Some web server's configuration supports 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 are done 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
3.128.79.88