Nmap Security Scanner
*Intro
*Ref Guide
*Install Guide
*Download
*Changelog
*Book
*Docs
Security Lists
*Nmap Hackers
*Nmap Dev
*Bugtraq
*Full Disclosure
*Pen Test
*Basics
*More
Security Tools
*Pass crackers
*Sniffers
*Vuln Scanners
*Web scanners
*Wireless
*Exploitation
*Packet crafters
*More
Site News
Site Search:
Exploit World
Advertising
About/Contact
Credits
Sponsors:
edgeos



Nmap Development: Re: Suspect that --host-timeout is not working in 4.50?

Re: Suspect that --host-timeout is not working in 4.50?

From: jah <jah_at_zadkiel.plus.com>
Date: Fri, 21 Dec 2007 03:00:00 +0000

On 16/12/2007 04:08, Randolph Reitz wrote:
>
> On Dec 15, 2007, at 3:03 PM, jah wrote:
>
>> On 15/12/2007 00:34, Randolph Reitz wrote:
>>>
>>>
>>> [scanner_at_clouseau ~]$ nmap -d -sS -F --host_timeout 1m -A
>>> 131.225.136.140
>> I've got a feeling that one of the scripts running during the above
>> scan you gave as an example is causing a problem for you (either
>> bruteTelnet or anonFTP), would you mind running the following similar
>> scan:
>>
>> nmap -sS -p21,23 --script=bruteTelnet,anonFTP --script-trace -d3
>> --log-errors 131.225.136.140
>>
>> I'm not sure whether nse scripts are meant to obey host-timeout rules
>> or not, but neither of these scripts should take very long, certainly
>> not the times you're seeing and using script-trace should help
>> identify what is causing the massive delay.
>>
>> Regards,
>>
>> jah
>
> Hi,
>
> I ran the nmap command for ~11 minutes and the output file is ...
>
> [scanner_at_clouseau ~]$ ls -lt | head
> total 2231076
> -rw-rw-r-- 1 scanner scanner 2207219285 Dec 15 21:52 namp_test.out
>
> Wow, that's big. Looks like a loop that never ends. Here is the
> first minute ...
>
>
>
> --
> This email has been verified as Virus free
> Virus Protection and more available at http://www.plus.net
>
>
>
> There is no firewall at the Fermilab border, so you can run the
> command if you wish.
>
So there are two issues here. One, the script doesn't obey
--host-timeout and Two, there's a bug, I think, in bruteTelnet.
Can anyone shed any light on the first? Are NSE scripts supposed to
obey --host-timeout? Is there a way for the script to get that
information from nmap api?

On the second, some observations from playing with the script.
The failure occurs when the 10th user and/or pass has been tried at
which point the output from NSOCK goes crazy outputting nearly 160000
lines every second:

    NSOCK (212.6570s) nsock_loop() started (timeout=50ms). 0 events pending

It doesn't take many seconds to have a several gigabyte file when piping
nmaps output!

Using Wireshark, I was able to determine that the script opens a new
socket for each login attempt (rather than using the available number of
login attempts (in this case 2) and reconnecting once the telnet server
breaks the connection). The script never closes any (well, maybe just
the one) connections that it creates (until script conclusion or a
forced abort whereupon RST, ACKs are sent) and I assume that it runs out
of the 10 allowed sockets available to the script.

I've attached a patch (and the modified script too for Randys benefit)
that hopefully closes each socket after an unsuccessful login attempt,
but try as I might, I cannot sort out the business of utilising
remaining login attempts in a given connection... perhaps someone with
more nouse will have a go.

Just for extra info, in a given connection, Randys server seems to ask
for a username and then appears to validate it and doesn't prompt for a
password:

    login:

then responds with the following after an unsuccessful login:

    Login incorrect\r\n
    login:

and then:

    Login incorrect\r\n

This script could be much faster if it would utilise that remaining
login attempt because it would (in this case) halve the number of times
needed to 'negotiate options' - about 14 secs each time. Finally, if
these are indeed usernames that are requested, the script ought to be
able to move to the next username in the userpass table rather than
repeating the username for each user:pass pair. In this case, it tries
'guest' 3 times, 'root' 4 times, 'admin' ...

jah

id='bruteforce'
author = 'Eddie Bell <ejlbell_at_gmail.com>'
description='brute force telnet login credientials'
license = 'See nmaps COPYING for licence'
categories = {'intrusive'}

require('shortport')
require('stdnse')
require('strbuf')

local soc
local catch = function() soc:close() end
local try = nmap.new_try(catch)

portrule = shortport.port_or_service(23, 'telnet')

local escape_cred = function(cred)
        if cred == '' then
                return '<blank>'
        else
                return cred
        end
end

--[[
Returns a function which returns the next user/pass pair each time
it is called. When no more pairs are available nil is returned.

There are plenty more possible pairs but we need to find
a compromise between speed and coverage
--]]

local new_auth_iter = function()
        local userpass = {
                -- guest
                {'guest', ''}, {'guest', 'guest'}, {'guest', 'visitor'},

                -- root
                {'root', ''}, {'root', 'root'},
                {'root', 'pass'}, {'root', 'password'},

                -- admin
                {'admin', ''}, {'admin', 'admin'},
                {'admin', 'pass'}, {'admin', 'password'},

                -- adminstrator
                {'adminstrator', ''}, {'adminstrator', 'adminstrator'},
                {'adminstrator', 'password'}, {'adminstrator', 'pass'},
                
                -- others
                {'visitor', ''}, {'netman', 'netman'}, {'Admin', 'Admin'},
                {'manager', 'manager'}, {'security', 'security'},
                {'username', 'password'}, {'user', 'pass'},

                -- sentinel
                {nil, nil}
        }

        local i = 1
        return function(direction)
                if not userpass[i][1] then
                        return
                 end

                i = i + 1
                stdnse.print_debug(3, id .. " " ..
                                  userpass[i-1][1] .. ":" .. escape_cred(userpass[i-1][2]))
                return userpass[i-1][1], userpass[i-1][2]
        end
end

--[[
Go through telnet's option palaver so we can get to the login prompt.
We just deny every options the server asks us about.
--]]

local negotiate_options = function(result)
        local index, x, opttype, opt, retbuf

        index = 0
        retbuf = strbuf.new()

        while true do

                -- 255 is IAC (Interpret As Command)
                index, x = string.find(result, '\255', index)

                if not index then
                        break
                end

                opttype = string.byte(result, index+1)
                opt = string.byte(result, index+2)

                -- don't want it! won't do it!
                if opttype == 251 or opttype == 252 then
                        opttype = 254
                elseif opttype == 253 or opttype == 254 then
                        opttype = 252
                end

                retbuf = retbuf .. string.char(255)
                retbuf = retbuf .. string.char(opttype)
                retbuf = retbuf .. string.char(opt)
                index = index + 1
        end
        soc:send(strbuf.dump(retbuf))
end

--[[
A semi-state-machine that takes action based on output from the
server. Through pattern matching, it tries to deem if a user/pass
pair is valid. Telnet does not have a way of telling the client
if it was authenticated....so we have to make an educated guess
--]]

local brute_line = function(line, user, pass, usent)

        if (line:find 'incorrect' or line:find 'failed' or line:find 'denied' or
            line:find 'invalid' or line:find 'bad') and usent then
                usent = false
                return 2, nil, usent

        elseif (line:find '[/>%%%$#]+' or line:find "last login%s*:" or
                line:find '%u:\\') and not
               (line:find 'username%s*:' and line:find 'login%s*:') and
               usent then
                return 1, escape_cred(user) .. ' - ' .. escape_cred(pass) .. '\n', usent
                
        elseif line:find 'username%s*:' or line:find 'login%s*:' then
                try(soc:send(user .. '\r\0'))
                usent = true

        elseif line:find 'password%s*:' or line:find 'passcode%s*:' then
                -- fix, add 'password only' support
                if not usent then return 1, nil, usent end
                try(soc:send(pass .. '\r\0'))
        end

        return 0, nil, usent
end

--[[
Splits the input into lines and passes it to brute_line()
so it can try to login with <user> and <pass>

return value:
        (1, user:pass) - valid pair
        (2, nil) - invalid pair
        (3, nil) - disconnected and invalid pair
        (4, nil) - disconnected and didn't send pair
--]]

local brute_cred = function(user, pass)
        local status, ret, value, usent, results

        usent = false ; ret = 0

        while true do
                status, results = soc:receive_lines(1)

                -- remote host disconnected
                if not status then
                        if usent then return 3
                        else return 4
                        end
                end

                if (string.byte(results, 1) == 255) then
                        negotiate_options(results)
                end

                results = string.lower(results)

                for line in results:gmatch '[^\r\n]+' do
                        ret, value, usent = brute_line(line, user, pass, usent)
                        if (ret > 0) then
                                return ret, value
                        end
                end
        end
        return 1, "error -> this should never happen"
end

action = function(host, port)
        local pair, status, auth_iter
        local user, pass, count, rbuf
        
        pair = nil ; status = 3 ; count = 0
        auth_iter = new_auth_iter();

        soc = nmap.new_socket()
        soc:set_timeout(4000)

        -- continually try user/pass pairs (reconnecting, if we have to)
    -- until we find a valid one or we run out of pairs
        while not (status == 1) do

                if status == 2 or status == 3 then
                        user, pass = auth_iter()
                end

                -- make sure we don't get stuck in a loop
                if status == 4 then
                        count = count + 1
                        if count > 3 then return nil end
                else count = 0 end

                -- no more users left
                if not user then break end

                if status == 3 or status == 4 then
                        soc:close()
                        try(soc:connect(host.ip, port.number, port.protocol))
                end

                status, pair = brute_cred(user, pass)
        end
        soc:close()
        return pair
end

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

Received on Dec 20 2007
[ Nmap | Sec Tools | Mailing Lists | Site News | About/Contact | Advertising | Privacy ]
edgeos