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