File: brute.lua

package info (click to toggle)
nmap 6.00-0.3%2Bdeb7u1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 55,576 kB
  • sloc: ansic: 108,190; cpp: 60,191; sh: 19,314; python: 16,531; xml: 9,428; makefile: 2,473; perl: 1,980; yacc: 608; lex: 469; asm: 372
file content (913 lines) | stat: -rw-r--r-- 32,321 bytes parent folder | download
1
2
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
---
-- The brute library is an attempt to create a common framework for performing
-- password guessing against remote services. 
--
-- The library currently attempts to parallellize the guessing by starting
-- a number of working threads. The number of threads can be defined using
-- the brute.threads argument, it defaults to 10.
--
-- The library contains the following classes:
-- * <code>Account</code>
-- ** Implements a simple account class, that converts account "states" to common text representation.
-- ** The state can be either of the following: OPEN, LOCKED or DISABLED
-- * <code>Engine</code>
-- ** The actual engine doing the brute-forcing .
-- * <code>Error</code>
-- ** Class used to return errors back to the engine.
-- * <code>Options</code>
-- ** Stores any options that should be used during brute-forcing.
--
-- In order to make use of the framework a script needs to implement a Driver 
-- class. The Driver class is then to be passed as a parameter to the Engine 
-- constructor, which creates a new instance for each guess. The Driver class 
-- SHOULD implement the following four methods:
--
-- <code>
-- Driver:login = function( self, username, password )
-- Driver:check = function( self )
-- Driver:connect = function( self )
-- Driver:disconnect = function( self )
-- </code>
--
-- The <code>login</code> method does not need a lot of explanation. The login
-- function should return two parameters. If the login was successful it should
-- return true and an <code>Account</code>. If the login was a failure it
-- should return false and an <code>Error</code>. The driver can signal the
-- Engine to retry a set of credentials by calling the Error objects
-- <code>setRetry</code> method. It may also signal the Engine to abort all
-- password guessing by calling the Error objects <code>setAbort</code> method.
--
-- The following example code demonstrates how the Error object can be used.
-- 
-- <code>
-- 		-- After a number of incorrect attempts VNC blocks us, so we abort
-- 		if ( not(status) and x:match("Too many authentication failures") ) then
-- 			local err = brute.Error:new( data )
--			-- signal the engine to abort
-- 			err:setAbort( true )
-- 			return false, err			
-- 		elseif ( not(status) ) then
-- 			local err = brute.Error:new( "VNC handshake failed" )
-- 			-- This might be temporary, signal the engine to retry
-- 			err:setRetry( true )
-- 			return false, err
-- 		end
-- 		.
-- 		.
-- 		.
-- 		-- Return a simple error, no retry needed
-- 		return false, brute.Error:new( "Incorrect password" ) 
-- </code>
--
-- The purpose of the <code>check</code> method is to be able to determine
-- whether the script has all the information it needs, before starting the
-- brute force. It's the method where you should check, e.g., if the correct
-- database or repository URL was specified or not. On success, the
-- <code>check</code> method returns true, on failure it returns false and the
-- brute force engine aborts. 
--
-- NOTE: The <code>check</code> method is deprecated and will be removed from
-- all scripts in the future. Scripts should do this check in the action
-- function instead. 
--
-- The <code>connect</code> method provides the framework with the ability to
-- ensure that the thread can run once it has been dispatched a set of
-- credentials. As the sockets in NSE are limited we want to limit the risk of
-- a thread blocking, due to insufficient free sockets, after it has aquired a
-- username and password pair.
--
-- The following sample code illustrates how to implement a sample
-- <code>Driver</code> that sends each username and password over a socket.
--
-- <code>
--   Driver = {
--		new = function(self, host, port, options)
--			local o = {}
--       	setmetatable(o, self)
--	        self.__index = self
--			o.host = host
--			o.port = port
--			o.options = options
--			return o
--		end,
--		connect = function( self )
--			self.socket = nmap.new_socket()
--			return self.socket:connect( self.host, self.port )
--		end,
--		disconnect = function( self )
--			return self.socket:close()
--		end, 
--		check = function( self ) 
--			return true
--		end,
--		login = function( self, username, password )
--			local status, err, data
--			status, err = self.socket:send( username .. ":" .. password)
--			status, data = self.socket:receive_bytes(1)
--
--			if ( data:match("SUCCESS") ) then
--				return true, brute.Account:new(username, password, "OPEN")
--			end
--			return false, brute.Error:new( "login failed" )
--		end,
--   }
-- </code>
--
-- The following sample code illustrates how to pass the <code>Driver</code>
-- off to the brute engine.
--
-- <code>
--   action = function(host, port)
--      local options = { key1 = val1, key2 = val2 }
--      local status, accounts = brute.Engine:new(Driver, host, port, options):start()
--	    if( not(status) ) then
--	       return accounts
--      end
--      return stdnse.format_output( true, accounts )
--   end
-- </code>
--
-- For a complete example of a brute implementation consult the 
-- <code>svn-brute.nse</code> or <code>vnc-brute.nse</code> scripts
--
-- @args brute.useraspass guess the username as password for each user
--       (default: true)
-- @args brute.emptypass guess an empty password for each user
--       (default: false)
-- @args brute.unique make sure that each password is only guessed once
--       (default: true)
-- @args brute.firstonly stop guessing after first password is found
--       (default: false)
-- @args brute.passonly iterate over passwords only for services that provide
--       only a password for authentication. (default: false)
-- @args brute.retries the number of times to retry if recoverable failures
--       occure. (default: 3)
-- @args brute.delay the number of seconds to wait between guesses (default: 0)
-- @args brute.threads the number of initial worker threads, the number of
--       active threads will be automatically adjusted.
-- @args brute.mode can be user, pass or creds and determines what mode to run
--       the engine in.
--       * user - the unpwdb library is used to guess passwords, every password
--                password is tried for each user. (The user iterator is in the
--                outer loop)
--       * pass - the unpwdb library is used to guess passwords, each password
--                is tried for every user. (The password iterator is in the
--                outer loop)
--       * creds- a set of credentials (username and password pairs) are
--                guessed against the service. This allows for lists of known
--                or common username and password combinations to be tested.
--       If no mode is specified and the script has not added any custom
--       iterator the pass mode will be enabled.
-- @args brute.credfile a file containing username and password pairs delimited
--       by '/'
-- @args brute.guesses the number of guesses to perform against each account.
--       (default: 0 (unlimited)). The argument can be used to prevent account
--       lockouts.
--
-- @author "Patrik Karlsson <patrik@cqure.net>"
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html

--
-- Version 0.73
-- Created 06/12/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
-- Revised 07/13/2010 - v0.2 - added connect, disconnect methods to Driver
--							   <patrik@cqure.net>
-- Revised 07/21/2010 - v0.3 - documented missing argument brute.mode
-- Revised 07/23/2010 - v0.4 - fixed incorrect statistics and changed output to 
--							   include statistics, and to display "no accounts
--							   found" message.
-- Revised 08/14/2010 - v0.5 - added some documentation and smaller changes per
--                             David's request.
-- Revised 08/30/2010 - v0.6 - added support for custom iterators and did some
--                             needed cleanup.
-- Revised 06/19/2011 - v0.7 - added support for creds library [Patrik]
-- Revised 07/07/2011 - v0.71- fixed some minor bugs, and changed credential
--                             iterator to use a file handle instead of table
-- Revised 07/21/2011 - v0.72- added code to allow script reporting invalid
--								(non existing) accounts using setInvalidAccount
-- Revised 11/12/2011 - v0.73- added support for max guesses per account to 
--                             prevent account lockouts.
--                             bugfix: added support for guessing the username
--                             as password per default, as suggested by the
--                             documentation.

module(... or "brute", package.seeall)
require 'unpwdb'
require 'datafiles'
require 'creds'

-- Engine options that can be set by scripts
-- Supported options are:
--   * firstonly   - stop after finding the first correct password
--                   (can be set using script-arg brute.firstonly)
--   * passonly    - guess passwords only, don't supply a username
--                   (can be set using script-arg brute.passonly)
--   * max_retries - the amount of retries to do before aborting
--                   (can be set using script-arg brute.retries)
--   * delay       - sets the delay between attempts
--                   (can be set using script-arg brute.delay)
--   * mode        - can be set to either cred, user or pass and controls
--                   whether the engine should iterate over users, passwords
--                   or fetch a list of credentials from a single file.
--                   (can be set using script-arg brut.mode)
--   * title       - changes the title of the result table where the
--                   passwords are returned.
--   * nostore     - don't store the results in the credential library
--   * max_guesses - the maximum amount of guesses to perform for each
--                   account.
--   * useraspass  - guesses the username as password (default: true)
--   * emptypass   - guesses an empty string as password (default: false)
--
Options = {
	
	new = function(self)
		local o = {}
       	setmetatable(o, self)
        self.__index = self

		o.emptypass = self.checkBoolArg("brute.emptypass", false)
		o.useraspass = self.checkBoolArg("brute.useraspass", true)
		o.firstonly = self.checkBoolArg("brute.firstonly", false)
		o.passonly = self.checkBoolArg("brute.passonly", false)
		o.max_retries = tonumber( nmap.registry.args["brute.retries"] ) or 3
		o.delay = tonumber( nmap.registry.args["brute.delay"] ) or 0
		o.max_guesses = tonumber( nmap.registry.args["brute.guesses"] ) or 0

		return o
	end,
	
	--- Checks if a script argument is boolean true or false
	--
	-- @param arg string containing the name of the argument to check
	-- @param default boolean containing the default value
	-- @return boolean, true if argument evaluates to 1 or true, else false
	checkBoolArg = function( arg, default )
		local val = stdnse.get_script_args(arg) or default
		return (val == "true" or val==true or tonumber(val)==1)
	end,
	
	--- Sets the brute mode to either iterate over users or passwords
	-- @see description for more information.
	--
	-- @param mode string containing either "user" or "password"
	-- @return status true on success else false
	-- @return err string containing the error message on failure
	setMode = function( self, mode )
		local modes = { "password", "user", "creds" }
		local supported = false
		
		for _, m in ipairs(modes) do
			if ( mode == m ) then supported = true end
		end
		
		if ( not(supported) ) then
			stdnse.print_debug("ERROR: brute.options.setMode: mode %s not supported", mode)
			return false, "Unsupported mode"
		else
			self.mode = mode
		end
		return true
	end,

	--- Sets an option parameter
	--
	-- @param param string containing the parameter name
	-- @param value string containing the parameter value
	setOption = function( self, param, value ) self[param] = value end,
	
	--- Set an alternate title for the result output (default: Accounts)
	--
	-- @param title string containing the title value
	setTitle = function(self, title) self.title = title end,

}

-- The account object which is to be reported back from each driver
Account =
{
	--- Creates a new instance of the Account class
	--
	-- @param username containing the user's name
	-- @param password containing the user's password
	-- @param state containing the account state and should be one of the
	--        following <code>OPEN</code>, <code>LOCKED</code>,
	--        <code>DISABLED</code>.
	new = function(self, username, password, state)
		local o = { username = username, password = password, state = state }
       	setmetatable(o, self)
        self.__index = self
		return o
	end,
	
	--- Converts an account object to a printable script
	--
	-- @return string representation of object
	toString = function( self )
		local c
		if ( #self.username > 0 ) then
			c = ("%s:%s"):format( self.username, #self.password > 0 and self.password or "<empty>" )
		else
			c = ("%s"):format( ( self.password and #self.password > 0 ) and self.password or "<empty>" )
		end
		if ( creds.StateMsg[self.state] ) then
			return ( "%s - %s"):format(c, creds.StateMsg[self.state] )
		else
			return ("%s"):format(c)
		end
	end,
			
}

-- The Error class, is currently only used to flag for retries
-- It also contains the error message, if one was returned from the driver.
Error =
{
	retry = false,
	
	new = function(self, msg)
		local o = { msg = msg, done = false }
       	setmetatable(o, self)
        self.__index = self
		return o
	end,
	
	--- Is the error recoverable?
	--
	-- @return status true if the error is recoverable, false if not
	isRetry = function( self ) return self.retry end,
	
	--- Set the error as recoverable
	--
	-- @param r boolean true if the engine should attempt to retry the
	--        credentials, unset or false if not
	setRetry = function( self, r ) self.retry = r end,
	
	--- Set the error as abort all threads
	--
	-- @param b boolean true if the engine should abort guessing on all threads
	setAbort = function( self, b ) self.abort = b end,
	
	--- Was the error abortable
	--
	-- @return status true if the driver flagged the engine to abort
	isAbort = function( self ) return self.abort end,
	
	--- Get the error message reported
	--
	-- @return msg string containing the error message
	getMessage = function( self ) return self.msg end,
	
	--- Is the thread done?
	--
	-- @return status true if done, false if not
	isDone = function( self ) return self.done end,
	
	--- Signals the engine that the thread is done and should be terminated
	--
	-- @param b boolean true if done, unset or false if not
	setDone = function( self, b ) self.done = b end,
	
	-- Marks the username as invalid, aborting further guessing.
	-- @param username
	setInvalidAccount = function(self, username)
		self.invalid_account = username
	end,

	-- Checks if the error reported the account as invalid.
	-- @return username string containing the invalid account
	isInvalidAccount = function(self)
		return self.invalid_account 
	end,
	
}

-- The brute engine, doing all the nasty work
Engine =
{
	STAT_INTERVAL = 20,
		
	--- Creates a new Engine instance
	--
	-- @param driver, the driver class that should be instantiated
	-- @param host table as passed to the action method of the script
	-- @param port table as passed to the action method of the script
	-- @param options table containing any script specific options
	-- @return o new Engine instance	
	new = function(self, driver, host, port, options)
		local o = { 
			driver = driver,
			host = host,
			port = port,
			driver_options = options,
			terminate_all = false,
			error = nil,
			counter = 0,
			threads = {},
			tps = {},
			iterators = {},
			found_accounts = {},
			account_guesses = {},
			options = Options:new(),
		}
       	setmetatable(o, self)
        self.__index = self
		o.max_threads = stdnse.get_script_args("brute.threads") or 10
		return o
	end,

	--- Adds an iterator to the list
	--
	-- @param iterator function to add to the list
	addIterator = function( self, iterator )
		table.insert( self.iterators, iterator )
	end,
	
	--- Limit the number of worker threads
	--
	-- @param max number containing the maximum number of allowed threads
	setMaxThreads = function( self, max ) self.max_threads = max end,
			
	--- Returns the number of non-dead threads
	--
	-- @return count number of non-dead threads
	threadCount = function( self )
		local count = 0

		for thread in pairs(self.threads) do
			if ( coroutine.status(thread) == "dead" ) then
				self.threads[thread] = nil
			else
				count = count + 1
			end
		end
		return count
	end,
	
	--- Calculates the number of threads that are actually doing any work
	--
	-- @return count number of threads performing activity
	activeThreads = function( self )
		local count = 0
		for thread, v in pairs(self.threads) do
			if ( v.guesses ~= nil ) then count = count + 1 end
		end
		return count		
	end,
	
	--- Iterator wrapper used to iterate over all registered iterators
	--
	-- @return iterator function
	get_next_credential = function( self )
		local function next_credential ()
			-- iterate over all credential iterators
			for _, iter in ipairs( self.iterators ) do
				for user, pass in iter do
					-- makes sure the credentials have not been tested before
					self.used_creds = self.used_creds or {}
					if ( not(self.used_creds[user..pass]) ) then
						self.used_creds[user..pass] = true
						coroutine.yield( user, pass )
					end
				end
			end
			while true do coroutine.yield(nil, nil) end
		end
		return coroutine.wrap( next_credential )
	end,
	
	--- Does the actual authentication request
	--
	-- @return true on success, false on failure
	-- @return response Account on success, Error on failure
	doAuthenticate = function( self )

		local status, response
		local next_credential = self:get_next_credential()
		local retries = self.options.max_retries
		local username, password
		
		repeat
			local driver = self.driver:new( self.host, self.port, self.driver_options )
			status = driver:connect()

			-- Did we succesfully connect?
			if ( status ) then
				if ( not(username) and not(password) ) then
					repeat
						username, password = next_credential()
						if ( not(username) and not(password) ) then
							driver:disconnect()
							self.threads[coroutine.running()].terminate = true
							return false 
						end
					until ( ( not(self.found_accounts) or not(self.found_accounts[username]) ) and
						  ( self.options.max_guesses == 0 or not(self.account_guesses[username]) or 
						    self.options.max_guesses > self.account_guesses[username] ) )
					
					-- increases the number of guesses for an account
					self.account_guesses[username] = self.account_guesses[username] and self.account_guesses[username] + 1 or 1
				end

				-- make sure that all threads locked in connect stat terminate quickly
				if ( Engine.terminate_all ) then 
					driver:disconnect()
					return false
				end
			
				local c
				-- Do we have a username or not?
				if ( username and #username > 0 ) then
					c = ("%s/%s"):format(username, #password > 0 and password or "<empty>")
				else
					c = ("%s"):format(#password > 0 and password or "<empty>")
				end
			
				local msg = ( retries ~= self.options.max_retries ) and "Re-trying" or "Trying"
				stdnse.print_debug(2, "%s %s against %s:%d", msg, c, self.host.ip, self.port.number )
				status, response = driver:login( username, password )

				driver:disconnect()
				driver = nil		
			end

			retries = retries - 1

		-- End if:
		-- * The guess was successfull
		-- * The response was not set to retry
		-- * We've reached the maximum retry attempts
		until( status or ( response and not( response:isRetry() ) ) or retries == 0)
	
		-- Increase the amount of total guesses
		self.counter = self.counter + 1
					
		-- did we exhaust all retries, terminate and report?
		if ( retries == 0 ) then
			Engine.terminate_all = true
			self.error = "Too many retries, aborted ..."
			response = Error:new("Too many retries, aborted ...")
			response.abort = true
		end
		return status, response
	end,
	
	login = function(self, cvar )
		local condvar = nmap.condvar( cvar )		
		local thread_data = self.threads[coroutine.running()]
		local interval_start = os.time()
		
		while( true ) do
			-- Should we terminate all threads?
			if ( self.terminate_all or thread_data.terminate ) then break	end
			
			local status, response = self:doAuthenticate()
				
			if ( status ) then
				-- Prevent locked accounts from appearing several times
				if ( not(self.found_accounts) or self.found_accounts[response.username] == nil ) then
					if ( not(self.options.nostore) ) then
						creds.Credentials:new( self.options.script_name, self.host, self.port ):add(response.username, response.password, response.state )
					else
						self.credstore = self.credstore or {}
						table.insert(self.credstore, response:toString() )
					end
					
					stdnse.print_debug("Discovered account: %s", response:toString())

					-- if we're running in passonly mode, and want to continue guessing
					-- we will have a problem as the username is always the same.
					-- in this case we don't log the account as found.
					if ( not(self.options.passonly) ) then
						self.found_accounts[response.username] = true
					end
					
					-- Check if firstonly option was set, if so abort all threads
					if ( self.options.firstonly ) then self.terminate_all = true end
				end
			else
				if ( response and response:isAbort() ) then
					self.terminate_all = true
					self.error = response:getMessage()
					break
				elseif( response and response:isDone() ) then
					break
				elseif ( response and response:isInvalidAccount() ) then
					self.found_accounts[response:isInvalidAccount()] = true
				end
			end
				
			local timediff = (os.time() - interval_start)
	
			-- This thread made another guess
			thread_data.guesses = ( thread_data.guesses and thread_data.guesses + 1 or 1 )

			-- Dump statistics at regular intervals
			if ( timediff > Engine.STAT_INTERVAL ) then
				interval_start = os.time()
				local tps = self.counter / ( os.time() - self.starttime )
				table.insert(self.tps, tps )
				stdnse.print_debug(2, "threads=%d,tps=%d", self:activeThreads(), tps )
			end

			-- if delay was speciefied, do sleep
			if ( self.options.delay > 0 ) then stdnse.sleep( self.options.delay ) end
		end
		condvar "signal"
	end,
			
	--- Starts the brute-force
	--
	-- @return status true on success, false on failure
	-- @return err string containing error message on failure
	start = function(self)
	
		local cvar = {}
		local condvar = nmap.condvar( cvar )
		
		assert(self.options.script_name, "SCRIPT_NAME was not set in options.script_name")
		assert(self.port.number and self.port.protocol, "Invalid port table detected")
		self.port.service = self.port.service or "unknown"
		
		-- Only run the check method if it exist. We should phase this out
		-- in favor of a check in the action function of the script
		if ( self.driver:new( self.host, self.port, self.driver_options ).check ) then
			-- check if the driver is ready!
			local status, response = self.driver:new( self.host, self.port, self.driver_options ):check()
			if( not(status) ) then return false, response end
		end
		
		local status, usernames = unpwdb.usernames()
		if ( not(status) ) then	return false, "Failed to load usernames" end

		-- make sure we have a valid pw file
		local status, passwords = unpwdb.passwords()
		if ( not(status) ) then	return false, "Failed to load passwords" end
	
		local mode = self.options.mode or stdnse.get_script_args("brute.mode")
	
		-- if no mode was given, but a credfile is present, assume creds mode
		if ( not(mode) and stdnse.get_script_args("brute.credfile") ) then
			if ( stdnse.get_script_args("userdb") or
				 stdnse.get_script_args("passdb") ) then
				return false, "\n  ERROR: brute.credfile can't be used in combination with userdb/passdb"
			end
			mode = 'creds'
		end
	
		-- Are we guessing against a service that has no username (eg. VNC)
		if ( self.options.passonly ) then
			local function single_user_iter(next)
				local function next_user() coroutine.yield( "" ) end
				return coroutine.wrap(next_user)
			end
			-- only add this iterator if no other iterator was specified
			if ( #self.iterators == 0 ) then
				table.insert( self.iterators, Iterators.user_pw_iterator( single_user_iter(), passwords ) )
			end
		elseif ( mode == 'creds' ) then
			local credfile = stdnse.get_script_args("brute.credfile")
			if ( not(credfile) ) then
				return false, "No credential file specified (see brute.credfile)"
			end
		
			local f = io.open( credfile, "r" )
			if ( not(f) ) then
				return false, ("Failed to open credfile (%s)"):format(credfile)
			end
		
			table.insert( self.iterators, Iterators.credential_iterator( f ) )
		elseif ( mode and mode == 'user' ) then
			table.insert( self.iterators, Iterators.user_pw_iterator( usernames, passwords ) )
		elseif( mode and mode == 'pass' ) then
			table.insert( self.iterators, Iterators.pw_user_iterator( usernames, passwords ) )
		elseif ( mode ) then
			return false, ("Unsupported mode: %s"):format(mode)
		-- Default to the pw_user_iterator in case no iterator was specified
		elseif ( 0 == #self.iterators ) then
			table.insert( self.iterators, Iterators.pw_user_iterator( usernames, passwords ) )
		end

		if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options.useraspass ) then
			-- if we're only guessing passwords, this doesn't make sense
			if ( not(self.options.passonly) ) then
				table.insert( self.iterators, 1, Iterators.pw_same_as_user_iterator(usernames, "lower"))
			end
		end
		
		if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options.emptypass ) then
			local function empty_pass_iter()
				local function next_pass()
					coroutine.yield( "" )
				end
				return coroutine.wrap(next_pass)
			end
			table.insert( self.iterators, 1, Iterators.account_iterator(usernames, empty_pass_iter(), mode or "pass"))
		end
			 

		self.starttime = os.time()

		-- Startup all worker threads
		for i=1, self.max_threads do
			local co = stdnse.new_thread( self.login, self, cvar )
			self.threads[co] = {}
			self.threads[co].running = true
		end

		-- wait for all threads to finnish running
		while self:threadCount()>0 do condvar "wait" end
		
		local valid_accounts
		
		if ( not(self.options.nostore) ) then
			valid_accounts = creds.Credentials:new(self.options.script_name, self.host, self.port):getTable()
		else
			valid_accounts = self.credstore
		end
		
		local result = {}
		-- Did we find any accounts, if so, do formatting
		if ( valid_accounts and #valid_accounts > 0 ) then
			valid_accounts.name = self.options.title or "Accounts"
			table.insert( result, valid_accounts )
		else
			table.insert( result, {"No valid accounts found", name="Accounts"} )
		end
		
		-- calculate the average tps
		local sum = 0
		for _, v in ipairs( self.tps ) do sum = sum + v	end
		local time_diff = ( os.time() - self.starttime )
		time_diff = ( time_diff == 0 ) and 1 or time_diff
		local tps = ( sum == 0 ) and ( self.counter / time_diff ) or ( sum / #self.tps )

		-- Add the statistics to the result
		local stats = {}
		table.insert(stats, ("Performed %d guesses in %d seconds, average tps: %d"):format( self.counter, time_diff, tps ) )
		stats.name = "Statistics"
		table.insert( result, stats )
		
		if ( self.options.max_guesses > 0 ) then
			-- we only display a warning if the guesses are equal to max_guesses
			for user, guesses in pairs(self.account_guesses) do
				if ( guesses == self.options.max_guesses ) then
					table.insert( result, { name = "Information", 
						  ("Guesses restricted to %d tries per account to avoid lockout"):format(self.options.max_guesses) } )
					break
				end
			end
		end

		result = ( #result ) and stdnse.format_output( true, result ) or ""
		
		-- Did any error occure? If so add this to the result.
		if ( self.error ) then
			result = result .. ("  \n ERROR: %s"):format( self.error )
			return false, result
		end
		return true, result
	end,
	
}

Iterators = {

	--- Iterates over each user and password
	--
	-- @param users table/function containing list of users
	-- @param pass table/function containing list of passwords
	-- @param mode string, should be either 'user' or 'pass' and controls
	--        whether the users or passwords are in the 'outer' loop
	-- @return function iterator
	account_iterator = function(users, pass, mode)		
		local function next_credential ()
			local outer, inner

			if ( mode == 'pass' ) then
				outer, inner = pass, users
			elseif ( mode == 'user' ) then
				outer, inner = users, pass
			else
				return
			end

			if ( 'table' == type(users) and 'table' == type(pass) ) then
				for _, o in ipairs(outer) do
					for _, i in ipairs(inner) do
						if ( mode == 'pass' ) then
							coroutine.yield( i, o )
						else
							coroutine.yield( o, i )
						end
					end
				end
			elseif ( 'function' == type(users) and 'function' == type(pass) ) then
				for o in outer do
					for i in inner do
						if ( mode == 'pass' ) then
							coroutine.yield( i, o )
						else
							coroutine.yield( o, i )
						end
					end
					inner("reset")
				end
			end
			while true do coroutine.yield(nil, nil) end
		end
		return coroutine.wrap( next_credential )	
	end,


	--- Try each password for each user (user in outer loop)
	--
	-- @param users table/function containing list of users
	-- @param pass table/function containing list of passwords
	-- @return function iterator
	user_pw_iterator = function( users, pass )
		return Iterators.account_iterator( users, pass, "user" )
	end,

	--- Try each user for each password (password in outer loop)
	--
	-- @param users table/function containing list of users
	-- @param pass table/function containing list of passwords
	-- @return function iterator
	pw_user_iterator = function( users, pass )
		return Iterators.account_iterator( users, pass, "pass" )
	end,

	--- An iterator that returns the username as password
	--
	-- @param users function returning the next user
	-- @param case string [optional] 'upper' or 'lower', specifies if user
	--        and password pairs should be case converted.
	-- @return function iterator
	pw_same_as_user_iterator = function( users, case )	
		local function next_credential ()
			for user in users do
				if ( case == 'upper' ) then
					coroutine.yield(user, user:upper())
				elseif( case == 'lower' ) then
					coroutine.yield(user, user:lower())
				else
					coroutine.yield(user, user)
				end
			end
			users("reset")
			while true do coroutine.yield(nil, nil) end
		end
		return coroutine.wrap( next_credential )
	end,

	--- An iterator that returns the username and uppercase password
	--
	-- @param users table containing list of users
	-- @param pass table containing list of passwords
	-- @param mode string, should be either 'user' or 'pass' and controls
	--        whether the users or passwords are in the 'outer' loop
	-- @return function iterator
	pw_ucase_iterator = function( users, passwords, mode )
		local function next_credential ()
			for user, pass in Iterators.account_iterator(users, passwords, mode) do
				coroutine.yield( user, pass:upper() )
			end
			while true do coroutine.yield(nil, nil) end
		end
		return coroutine.wrap( next_credential )
	end,
	
	--- Credential iterator (for default or known user/pass combinations)
	--
	-- @param f file handle to file containing credentials separated by '/'
	-- @return function iterator
	credential_iterator = function( f )
		local function next_credential ()
			local c = {}
			for line in f:lines() do
				if ( not(line:match("^#!comment:")) ) then
					local trim = function(s) return s:match('^()%s*$') and '' or s:match('^%s*(.*%S)') end 
					line = trim(line)
					local user, pass = line:match("^([^%/]*)%/(.*)$")
					coroutine.yield( user, pass )
				end
			end
			f:close()
			while true do coroutine.yield( nil, nil ) end
		end
		return coroutine.wrap( next_credential )
	end,
		
	unpwdb_iterator = function( mode )
		local status, users, passwords

		status, users = unpwdb.usernames()
		if ( not(status) ) then return end
		
		status, passwords = unpwdb.passwords()
		if ( not(status) ) then return end
		
		return Iterators.account_iterator( users, passwords, mode )
	end,
	
}