| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 
 | local datafiles = require "datafiles"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local stringaux = require "stringaux"
local table = require "table"
local rand = require "rand"
description = [[
Enumerates TFTP (trivial file transfer protocol) filenames by testing
for a list of common ones.
TFTP doesn't provide directory listings. This script tries to retrieve
filenames from a list. The list is composed of static names from the
file <code>tftplist.txt</code>, plus configuration filenames for Cisco
devices that change based on the target address, of the form
<code>A.B.C.X-confg</code> for an IP address A.B.C.D and for X in 0 to
255.
Use the <code>tftp-enum.filelist</code> script argument to search for
other static filenames.
This script is a reimplementation of tftptheft from
http://code.google.com/p/tftptheft/.
]]
---
-- @usage nmap -sU -p 69 --script tftp-enum.nse --script-args tftp-enum.filelist=customlist.txt <host>
--
-- @args filelist - file name with list of filenames to enumerate at tftp server
--
-- @output
-- PORT   STATE SERVICE REASON
-- 69/udp open  tftp    script-set
-- | tftp-enum:
-- |_  bootrom.ld
author = "Alexander Rudakov"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = { "discovery", "intrusive" }
local REQUEST_ERROR = -1
local FILE_FOUND = 1
local FILE_NOT_FOUND = 2
portrule = shortport.portnumber(69, "udp")
-- return a new array containing the concatenation of all of its
-- parameters. Scaler parameters are included in place, and array
-- parameters have their values shallow-copied to the final array.
-- Note that userdata and function values are treated as scalar.
local function array_concat(...)
  local t = {}
  for n = 1, select("#", ...) do
    local arg = select(n, ...)
    if type(arg) == "table" then
      for _, v in ipairs(arg) do
        t[#t + 1] = v
      end
    else
      t[#t + 1] = arg
    end
  end
  return t
end
local generate_cisco_address_confg = function(base_address)
  local filenames = {}
  local octets = stringaux.strsplit("%.", base_address)
  for i = 0, 255 do
    local address_confg = octets[1] .. "." .. octets[2] .. "." .. octets[3] .. "." .. i .. "-confg"
    table.insert(filenames, address_confg)
  end
  return filenames
end
local generate_filenames = function(host)
  local customlist = stdnse.get_script_args('tftp-enum.filelist')
  local cisco = false
  local status, default_filenames = datafiles.parse_file(customlist or "nselib/data/tftplist.txt" , {})
  if not status then
    stdnse.debug1("Can not open file with tftp file names list")
    return {}
  else
    for i, filename in ipairs(default_filenames) do
      if filename:match('{[Mm][Aa][Cc]}') then
        if not host.mac_addr then
          goto next_filename
        else
          filename = filename:gsub('{M[Aa][Cc]}', string.upper(stdnse.tohex(host.mac_addr)))
          filename = filename:gsub('{m[aA][cC]}', stdnse.tohex(host.mac_addr))
        end
      end
      if filename:match('{cisco}') then
        cisco = true
        table.remove(default_filenames,i)
      end
      ::next_filename::
    end
    if cisco == true then
      local cisco_address_confg_filenames = generate_cisco_address_confg(host.ip)
      return array_concat(default_filenames, cisco_address_confg_filenames)
    end
  end
  return default_filenames
end
local create_tftp_file_request = function(filename)
  return "\0\x01" .. filename .. "\0octet\0"
end
local check_file_present = function(host, port, filename)
  stdnse.debug1("checking file %s", filename)
  local file_request = create_tftp_file_request(filename)
  local socket = nmap.new_socket()
  socket:connect(host, port)
  local status, lhost, lport, rhost, rport = socket:get_info()
  stdnse.debug1("lhost: %s, lport: %s", lhost, lport);
  if (not (status)) then
    stdnse.debug1("error %s", lhost)
    socket:close()
    return REQUEST_ERROR
  end
  local bind_socket = nmap.new_socket("udp")
  stdnse.debug1("local port = %d", lport)
  socket:send(file_request)
  socket:close()
  local bindOK, error = bind_socket:bind(nil, lport)
  stdnse.debug1("starting listener")
  if (not (bindOK)) then
    stdnse.debug1("Error in bind %s", error)
    bind_socket:close()
    return REQUEST_ERROR
  end
  local recvOK, data = bind_socket:receive()
  if (not (recvOK)) then
    stdnse.debug1("Error in receive %s", data)
    bind_socket:close()
    return REQUEST_ERROR
  end
  if (data:byte(1) == 0x00 and data:byte(2) == 0x03) then
    bind_socket:close()
    return FILE_FOUND
  elseif (data:byte(1) == 0x00 and data:byte(2) == 0x05) then
    bind_socket:close()
    return FILE_NOT_FOUND
  else
    bind_socket:close()
    return REQUEST_ERROR
  end
  return FILE_NOT_FOUND
end
local check_open_tftp = function(host, port)
  local random_name = rand.random_string(8, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_")
  local ret_value = check_file_present(host, port, random_name)
  if (ret_value == FILE_FOUND or ret_value == FILE_NOT_FOUND) then
    return true
  else
    return false
  end
end
action = function(host, port)
  if (not (check_open_tftp(host, port))) then
    stdnse.debug1("tftp seems not active")
    return
  end
  stdnse.debug1("tftp detected")
  port.service = "tftp"
  nmap.set_port_state(host, port, "open")
  local results = {}
  local filenames = generate_filenames(host)
  for i, filename in ipairs(filenames) do
    local request_status = check_file_present(host, port, filename)
    if (request_status == FILE_FOUND) then
      table.insert(results, filename)
    end
  end
  return stdnse.format_output(true, results)
end
 |