Nmap Development mailing list archives

Re: Sounds like ftp-anon needs work?


From: David Fifield <david () bamsoftware com>
Date: Thu, 27 May 2010 20:45:00 -0600

On Sun, May 23, 2010 at 09:01:21PM +0100, Rob Nicholls wrote:
Here's a new version to keep everyone on their toes.

It should be quicker (as it gives up after a 530, rather than typically
waiting for a timeout), hopefully much easier to read the code if anyone
wants to improve it in the future (e.g. adding checks to confirm R/W), and
should support returning an ACCT if it sees a 332.

Rob, I like the functionality of this script but I don't like its
looping structure. I find it hard to understand. I can see why you made
it into a loop. Reading line by line in Nsock isn't elegant, and you've
consolidated all the network reads in one place.

It's better to factor out a function that reads a single reply. use
stdnse.make_buffer to overcome the problem that nmap.receive_lines
returns more lines than you ask for. Here's a function to read a single
reply, even a multi-line reply.

-- Read an FTP reply and return the numeric code and the message. See RFC 959,
-- section 4.2. The buffer argument should have been created with
-- stdnse.make_buffer(socket, "\r?\n"). On error, returns nil and an error
-- message.
local function read_reply(buffer)
        local readline
        local line, err
        local code, message
        local _, p, tmp

        line, err = buffer()
        if not line then
                return line, err
        end

        -- Single-line response?
        code, message = string.match(line, "^(%d%d%d) (.*)$")
        if code then
                return tonumber(code), message
        end

        -- Multi-line response?
        _, p, code, message = string.find(line, "^(%d%d%d)-(.*)$")
        if p then
                while true do
                        line, err = buffer()
                        if not line then
                                return line, err
                        end
                        tmp = string.match(line, "^%d%d%d (.*)$")
                        if tmp then
                                message = message .. "\n" .. tmp
                                break
                        end
                        message = message .. "\n" .. line
                end
                return tonumber(code), message
        end

        return nil, string.format("Unparseable response: %q", line)
end

With this I think you can give the script a much more linear structure:

buffer = stdnse.make_buffer(socket, "\r?\n")
code, banner = read_reply(buffer)
socket:send("USER anonymous\r\n")
code, message = read_reply(buffer)
if code == 331 then
        socket:send("PASS IEUser@\r\n")
        code, message = read_reply(buffer)
        etc.
elseif code >= 200 && code < 300 then
        etc.
else
        etc.
end

Plus, doing it with a subsidiary function lets you handle multi-line
responses anywhere, not just in the banner. Can you make the script look
more like this?

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


Current thread: