mirror of
https://github.com/nmap/nmap.git
synced 2025-12-17 05:09:00 +00:00
FTP EPSV support to fix IPv6 scans
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
local comm = require "comm"
|
local comm = require "comm"
|
||||||
local stdnse = require "stdnse"
|
local stdnse = require "stdnse"
|
||||||
local string = require "string"
|
local string = require "string"
|
||||||
|
local ipOps = require "ipOps"
|
||||||
|
local nmap = require "nmap"
|
||||||
_ENV = stdnse.module("ftp", stdnse.seeall)
|
_ENV = stdnse.module("ftp", stdnse.seeall)
|
||||||
|
|
||||||
local ERROR_MESSAGES = {
|
local ERROR_MESSAGES = {
|
||||||
@@ -109,4 +111,82 @@ function read_reply(buffer)
|
|||||||
return nil, string.format("Unparseable response: %q", line)
|
return nil, string.format("Unparseable response: %q", line)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Close an FTP command connection
|
||||||
|
--
|
||||||
|
-- @param socket The socket with the live connection
|
||||||
|
function close(socket)
|
||||||
|
socket:send("QUIT\r\n")
|
||||||
|
socket:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Start PASV mode
|
||||||
|
--
|
||||||
|
-- For IPv6 connections, attempts to use EPSV (RFC 2428). If the server sends an address that is not the target address, then this is an error.
|
||||||
|
-- @param socket The connected command socket
|
||||||
|
-- @param buffer The receive buffer
|
||||||
|
-- @return The connected data socket, or nil on error
|
||||||
|
-- @return Error message if data socket is nil
|
||||||
|
function pasv(socket, buffer)
|
||||||
|
local epsv = false
|
||||||
|
local status, lhost, lport, rhost, rport = socket:get_info()
|
||||||
|
if not status then
|
||||||
|
return nil, ("Can't determine remote host IP: %s"):format(lhost)
|
||||||
|
end
|
||||||
|
epsv = #ipOps.ip_to_str(rhost) > 4
|
||||||
|
|
||||||
|
::TRY_AGAIN::
|
||||||
|
local cmd = epsv and "EPSV" or "PASV"
|
||||||
|
-- ask the server for a Passive Mode: it should give us a port to
|
||||||
|
-- listen to, where it will dump the directory listing
|
||||||
|
local status, err = socket:send(cmd .. "\r\n")
|
||||||
|
if not status then
|
||||||
|
return status, err
|
||||||
|
end
|
||||||
|
local code, message = read_reply(buffer)
|
||||||
|
|
||||||
|
local pasv_port
|
||||||
|
if epsv then
|
||||||
|
if not code then
|
||||||
|
return nil, ("EPSV failed: socket %s"):format(message)
|
||||||
|
elseif code ~= 229 then
|
||||||
|
stdnse.debug2("Falling back to PASV. EPSV: %d %s", code, message)
|
||||||
|
epsv = false
|
||||||
|
goto TRY_AGAIN
|
||||||
|
end
|
||||||
|
status, pasv_port = string.match(message, "%((.)%1%1(%d+)%1%)")
|
||||||
|
if not status then
|
||||||
|
stdnse.debug2("Can't parse EPSV response: %s", message)
|
||||||
|
epsv = false
|
||||||
|
goto TRY_AGAIN
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if not code or code >= 300 then
|
||||||
|
return nil, ("PASV failed: %d %s"):format(code or "socket", message)
|
||||||
|
end
|
||||||
|
-- Compute the PASV port as given by the server
|
||||||
|
-- The server should answer with something like
|
||||||
|
-- 2xx Entering Passive Mode (a,b,c,d,hp,lp)
|
||||||
|
-- (-- IP--,PORT)
|
||||||
|
-- PORT is (hp x 256) + lp
|
||||||
|
local ip, high, low = string.match(message, "%((%d+,%d+,%d+,%d+),(%d+),(%d+)%)")
|
||||||
|
if not ip then
|
||||||
|
return nil, string.format("Can't parse PASV response: %q", message)
|
||||||
|
end
|
||||||
|
ip = ip:gsub(",", ".")
|
||||||
|
if not (ipOps.compare_ip(ip, "eq", rhost) or ipOps.compare_ip(ip, "eq", "0.0.0.0")) then
|
||||||
|
return nil, ("PASV IP %s is not the same as %s"):format(ip, rhost)
|
||||||
|
end
|
||||||
|
|
||||||
|
pasv_port = high * 256 + low
|
||||||
|
end
|
||||||
|
|
||||||
|
local list_socket = nmap.new_socket()
|
||||||
|
status, err = list_socket:connect(rhost, pasv_port, "tcp")
|
||||||
|
if not status then
|
||||||
|
return status, err
|
||||||
|
end
|
||||||
|
|
||||||
|
return list_socket
|
||||||
|
end
|
||||||
|
|
||||||
return _ENV;
|
return _ENV;
|
||||||
|
|||||||
@@ -48,37 +48,15 @@ portrule = shortport.port_or_service({21,990}, {"ftp","ftps"})
|
|||||||
-- list sent.
|
-- list sent.
|
||||||
-- ---------------------
|
-- ---------------------
|
||||||
local function list(socket, buffer, target, max_lines)
|
local function list(socket, buffer, target, max_lines)
|
||||||
local status, err
|
|
||||||
|
|
||||||
-- ask the server for a Passive Mode: it should give us a port to
|
local list_socket, err = ftp.pasv(socket, buffer)
|
||||||
-- listen to, where it will dump the directory listing
|
if not list_socket then
|
||||||
status, err = socket:send("PASV\r\n")
|
return nil, err
|
||||||
if not status then
|
|
||||||
return status, err
|
|
||||||
end
|
end
|
||||||
local code, message = ftp.read_reply(buffer)
|
|
||||||
|
|
||||||
-- Compute the PASV port as given by the server
|
|
||||||
-- The server should answer with something like
|
|
||||||
-- 2xx Entering Passive Mode (a,b,c,d,hp,lp)
|
|
||||||
-- (-- IP--,PORT)
|
|
||||||
-- PORT is (hp x 256) + lp
|
|
||||||
local high, low = string.match(message, "%(%d+,%d+,%d+,%d+,(%d+),(%d+)%)")
|
|
||||||
if not high then
|
|
||||||
return nil, string.format("Can't parse PASV response: %q", message)
|
|
||||||
end
|
|
||||||
|
|
||||||
local pasv_port = high * 256 + low
|
|
||||||
|
|
||||||
-- Send the LIST command on the commands socket. "Fire and forget"; we
|
-- Send the LIST command on the commands socket. "Fire and forget"; we
|
||||||
-- don't need to take care of the answer on this socket.
|
-- don't need to take care of the answer on this socket.
|
||||||
status, err = socket:send("LIST\r\n")
|
local status, err = socket:send("LIST\r\n")
|
||||||
if not status then
|
|
||||||
return status, err
|
|
||||||
end
|
|
||||||
|
|
||||||
local list_socket = nmap.new_socket()
|
|
||||||
status, err = list_socket:connect(target, pasv_port, "tcp")
|
|
||||||
if not status then
|
if not status then
|
||||||
return status, err
|
return status, err
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user