Nmap Development mailing list archives

[NSE] NSE HTTP library


From: Sven Klemm <sven () c3d2 de>
Date: Wed, 16 Jan 2008 22:57:16 +0100

I've written a HTTP library for NSE in lua. I implemented the
following 3 functions so far: http.get( host, port, path ),
http.request( host, port, request ) and http.get_url( url ).

The host parameter may either be a string or table. The port parameter
may either be a number or a table. This means you can directly pass
the host or port tables you get from nmap into these functions.

The return value of the functions is a table with the following
structure: {status = 200, header = {}, body ="<html>...</html>"}
The header table has an entry for each received header with the header
name being the key.
Status is the HTTP Status code of the request. In case of an error
status is nil. body is a string containing the body of the request.

The library supports http as well as https.

I've also attached a sample script that checks for publicly accessible
svn-Metadata that demonstrates the usage.

It would be really cool if this could be integrated into nmap.

Cheers,
Sven


-- 
Sven Klemm
http://cthulhu.c3d2.de/~sven/

-- See nmaps COPYING for licence
module(...,package.seeall)

require 'stdnse'
require 'url'

--
-- http.get( host, port, path )
-- http.request( host, port, request )
-- http.get_url( url )
--
-- host may either be a string or table
-- port may either be a number or a table
--
-- the format of the return value is a table with the following structure:
-- {status = 200, header = {}, body ="<html>...</html>"}
-- the header table has an entry for each received header with the header name being the key
-- table also has an entry named "status" which contains the http status code of the request
-- in case of an error status is nil


-- fetch relative URL with get request
get = function( host, port, path )
  local hostname = host
  if type(host) == 'table' then
    hostname = ( host.name ~= '' and host.name ) or host.ip
  end

  local data
  data = "GET "..path.." HTTP/1.1\r\n"
  data = data .. "Host: "..hostname.."\r\n"
  data = data .. "User-Agent: Nmap NSE\r\n"
  data = data .. "Connection: close\r\n\r\n"

  return http.request( host, port, data )
end

-- URL with get request
get_url = function( u )
  local parsed = url.parse( u )
  local port = {}

  port.service = parsed.scheme
  port.number = parsed.port

  if not port.number then
    if parsed.scheme == 'https' then
      port.number = 443
    else
      port.number = 80
    end
  end

  local path = parsed.path or "/"
  if parsed.query then
    path = path .. "?" .. parsed.query
  end

  return get( parsed.host, port, path )
end

-- send http request and return the result as table
-- host may be a table or the hostname
-- port may be a table or the portnumber
request = function( host, port, data )

  if type(host) == 'table' then
    host = ( host.name ~= '' and host.name ) or host.ip
  end

  local protocol = 'tcp'
  if type(port) == 'table' then
    if nmap.have_ssl() and ( port.service == 'https' or ( port.version and port.version.service_tunnel == 'ssl' ) ) then
      protocol = 'ssl'
    end
    port = port.number
  end

  socket = nmap.new_socket()
  socket:connect( host, port, protocol )
  socket:send( data )

  local buffer = stdnse.make_buffer( socket, "\r?\n" )

  local status, line, result
  local header, body = {}, {}
  -- header loop
  while true do
    status, line = buffer()
    if (not status or line == "") then break end
    table.insert(header,line)
  end

  result = {status=nil,header={},body=""}

  -- build nicer table for header
  for key, value in pairs( header ) do
    if key == 1 then
      local code
      _, _, code = string.find( value, "HTTP/%d\.%d (%d+)")
      result.status = tonumber(code)
    else
      _, _, key, value = string.find( value, "(.+): (.*)" )
      if key and value then
        result.header[key] = value:gsub( '[\r\n]+', '' )
      end
    end
  end

  -- body loop
  while true do
    status, line = buffer()
    if (not status) then break end
    table.insert(body,line)
  end

  socket:close()
  result.body = table.concat( body )

  return result

end

id = ".svn/entries"

description = "checks for accessable svn metadata"
author = "Sven Klemm <sven () c3d2 de>"
license = "See nmaps COPYING for licence"
categories = {"safe","discovery"}

require "shortport"
require "http"

portrule = shortport.port_or_service({80,443},{'http','https','http-proxy'})

action = function(host, port)
        local answer = http.get( host, port, "/.svn/entries" )

  if answer.status == 200 and not answer.body:match('<html') then
    return "Readable subversion metadata"
  end
end

Attachment: signature.asc
Description: OpenPGP digital signature


_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org

Current thread: