66class MetasploitModule < Msf ::Auxiliary
77
88 # Exploit mixins should be called first
9+ include Msf ::Exploit ::Remote ::MsWkst
910 include Msf ::Exploit ::Remote ::SMB ::Client
1011 include Msf ::Exploit ::Remote ::SMB ::Client ::Authenticated
1112 include Msf ::Exploit ::Remote ::DCERPC
@@ -24,191 +25,109 @@ def initialize
2425 [
2526 'natron' , # original module
2627 'Joshua D. Abraham <jabra[at]praetorian.com>' , # database storage
28+ 'NtAlexio2 <[email protected] >' , # refactor 2729 ] ,
2830 'References' =>
2931 [
3032 [ 'URL' , 'https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstauserenum' ]
3133 ] ,
3234 'License' => MSF_LICENSE ,
3335 )
34-
35- deregister_options ( 'RPORT' )
36-
3736 end
3837
39- def parse_value ( resp , idx )
40- #val_length = resp[idx,4].unpack("V")[0]
41- idx += 4
42- #val_offset = resp[idx,4].unpack("V")[0]
43- idx += 4
44- val_actual = resp [ idx , 4 ] . unpack ( "V" ) [ 0 ]
45- idx += 4
46- value = resp [ idx , val_actual *2 ]
47- idx += val_actual * 2
48-
49- idx += val_actual % 2 * 2 # alignment
50-
51- return value , idx
38+ def rport
39+ @rport
5240 end
5341
54- def parse_net_wksta_enum_users_info ( resp )
55- accounts = [ Hash . new ( ) ]
56-
57- idx = 20
58- count = resp [ idx , 4 ] . unpack ( "V" ) [ 0 ] # wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> Max Count
59- idx += 4
60-
61- 1 . upto ( count ) do
62- # wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> Ref ID
63- idx += 4 # ref id name
64- idx += 4 # ref id logon domain
65- idx += 4 # ref id other domains
66- idx += 4 # ref id logon server
67- end
42+ def smb_direct
43+ @smb_direct
44+ end
6845
69- 1 . upto ( count ) do
70- # wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> ID1 max count
46+ def connect ( *args , **kwargs )
47+ super ( *args , **kwargs , direct : @smb_direct )
48+ end
7149
72- account_name , idx = parse_value ( resp , idx )
73- logon_domain , idx = parse_value ( resp , idx )
74- other_domains , idx = parse_value ( resp , idx )
75- logon_server , idx = parse_value ( resp , idx )
50+ def run_session
51+ smb_services = [ { port : self . simple . peerport , direct : self . simple . direct } ]
52+ smb_services . map { | smb_service | run_service ( smb_service [ :port ] , smb_service [ :direct ] ) }
53+ end
7654
77- accounts << {
78- :account_name => account_name ,
79- :logon_domain => logon_domain ,
80- :other_domains => other_domains ,
81- :logon_server => logon_server
82- }
55+ def run_rhost
56+ if datastore [ 'RPORT' ] . blank? || datastore [ 'RPORT' ] == 0
57+ smb_services = [
58+ { port : 445 , direct : true } ,
59+ { port : 139 , direct : false }
60+ ]
61+ else
62+ smb_services = [
63+ { port : datastore [ 'RPORT' ] , direct : datastore [ 'SMBDirect' ] }
64+ ]
8365 end
8466
85- accounts
67+ smb_services . map { | smb_service | run_service ( smb_service [ :port ] , smb_service [ :direct ] ) }
8668 end
8769
88- def rport
89- @rport || datastore [ 'RPORT' ]
70+ def run_service ( port , direct )
71+ @rport = port
72+ @smb_direct = direct
73+
74+ ipc_tree = connect_ipc
75+ wkssvc_pipe = connect_wkssvc ( ipc_tree )
76+ endpoint = RubySMB ::Dcerpc ::Wkssvc . freeze
77+
78+ user_info = user_enum ( endpoint ::WKSTA_USER_INFO_1 )
79+ user_info . wkui1_buffer
80+
81+ rescue Msf ::Exploit ::Remote ::SMB ::Client ::Ipc ::SmbIpcAuthenticationError => e
82+ print_warning ( e . message )
83+ nil
84+ rescue RubySMB ::Error ::RubySMBError => e
85+ print_error ( "Error: #{ e . message } " )
86+ nil
87+ rescue ::Timeout ::Error
88+ rescue ::Exception => e
89+ print_error ( "Error: #{ e . class } #{ e } " )
90+ ensure
91+ disconnect_wkssvc
9092 end
9193
92- def smb_direct
93- @smbdirect || datastore [ 'SMBDirect' ]
94- end
94+ def format_results ( results )
95+ users_table = Rex ::Text ::Table . new (
96+ 'Indent' => 4 ,
97+ 'Header' => "Logged-on Users" ,
98+ 'Columns' =>
99+ [
100+ 'Name' ,
101+ 'Domain' ,
102+ 'Other Domains' ,
103+ 'Logon Server'
104+ ] ,
105+ 'SortIndex' => 0 ,
106+ )
95107
96- def store_username ( username , res , ip , rport )
97- service_data = {
98- address : ip ,
99- port : rport ,
100- service_name : 'smb' ,
101- protocol : 'tcp' ,
102- workspace_id : myworkspace_id ,
103- proof : res
104- }
105-
106- credential_data = {
107- origin_type : :service ,
108- module_fullname : fullname ,
109- username : username
110- }
111-
112- credential_data . merge! ( service_data )
113-
114- credential_core = create_credential ( credential_data )
115-
116- login_data = {
117- core : credential_core ,
118- status : Metasploit ::Model ::Login ::Status ::UNTRIED
119- }
120-
121- login_data . merge! ( service_data )
122- create_credential_login ( login_data )
123- end
108+ results . compact . each do |result_set |
109+ result_set . each { |result | users_table << [ result . wkui1_username , result . wkui1_logon_domain , result . wkui1_oth_domains , result . wkui1_logon_server ] }
110+ end
124111
125- def run_host ( ip )
112+ users_table
126113
127- ports = [ 139 , 445 ]
114+ end
128115
116+ def run_host ( _ip )
129117 if session
130- print_status ( "Using existing session #{ session . sid } " )
131- client = session . client
132- self . simple = ::Rex ::Proto ::SMB ::SimpleClient . new ( client . dispatcher . tcp_socket , client : client )
133- ports = [ simple . port ]
134- self . simple . connect ( "\\ \\ #{ simple . address } \\ IPC$" ) # smb_login connects to this share for some reason and it doesn't work unless we do too
118+ self . simple = session . simple_client
119+ results = run_session
120+ else
121+ results = run_rhost
135122 end
136123
137- ports . each do |port |
138-
139- @rport = port
140- begin
141- unless session
142- connect ( )
143- smb_login ( )
144- end
145-
146- uuid = [ '6bffd098-a112-3610-9833-46c3f87e345a' , '1.0' ]
147-
148- handle = dcerpc_handle_target (
149- uuid [ 0 ] , uuid [ 1 ] , 'ncacn_np' , [ "\\ wkssvc" ] , simple . address
150- )
151- begin
152- dcerpc_bind ( handle )
153- stub =
154- NDR . uwstring ( "\\ \\ " + simple . address ) + # Server Name
155- NDR . long ( 1 ) + # Level
156- NDR . long ( 1 ) + # Ctr
157- NDR . long ( rand ( 0xffffffff ) ) + # ref id
158- NDR . long ( 0 ) + # entries read
159- NDR . long ( 0 ) + # null ptr to user0
160-
161- NDR . long ( 0xffffffff ) + # Prefmaxlen
162- NDR . long ( rand ( 0xffffffff ) ) + # ref id
163- NDR . long ( 0 ) # null ptr to resume handle
164-
165- dcerpc . call ( 2 , stub )
166-
167- resp = dcerpc . last_response ? dcerpc . last_response . stub_data : nil
168-
169- accounts = parse_net_wksta_enum_users_info ( resp )
170- accounts . shift
171-
172- if datastore [ 'VERBOSE' ]
173- accounts . each do |x |
174- print_status x [ :logon_domain ] + "\\ " + x [ :account_name ] +
175- "\t (logon_server: #{ x [ :logon_server ] } , other_domains: #{ x [ :other_domains ] } )"
176- end
177- else
178- print_status "#{ accounts . collect { |x | x [ :logon_domain ] + "\\ " + x [ :account_name ] } . join ( ", " ) } "
179- end
180-
181- found_accounts = [ ]
182- accounts . each do |x |
183- comp_user = x [ :logon_domain ] + "\\ " + x [ :account_name ]
184- found_accounts . push ( comp_user . scan ( /[[:print:]]/ ) . join ) unless found_accounts . include? ( comp_user . scan ( /[[:print:]]/ ) . join )
185- end
186-
187- found_accounts . each do |comp_user |
188- if comp_user . to_s =~ /\$ $/
189- next
190- end
191-
192- print_good ( "Found user: #{ comp_user } " )
193- store_username ( comp_user , resp , simple . address , rport )
194- end
195-
196- rescue ::Rex ::Proto ::SMB ::Exceptions ::ErrorCode => e
197- print_error ( "UUID #{ uuid [ 0 ] } #{ uuid [ 1 ] } ERROR 0x%.8x" % e . error_code )
198- #puts e
199- #return
200- rescue ::Exception => e
201- print_error ( "UUID #{ uuid [ 0 ] } #{ uuid [ 1 ] } ERROR #{ $!} " )
202- #puts e
203- #return
204- end
205-
206- disconnect ( )
207- return
208- rescue ::Exception
209- print_line ( $!. to_s )
210- end
124+ unless results . to_s . empty?
125+ results_table = format_results ( results )
126+ results_table . rows = results_table . rows . uniq # Remove potentially duplicate entries from port 139 & 445
127+
128+ print_line
129+ print_line results_table . to_s
211130 end
212- end
213131
132+ end
214133end
0 commit comments