@@ -150,72 +150,116 @@ def smb_defaults(packet)
150
150
packet . v [ 'ProcessID' ] = self . process_id . to_i
151
151
end
152
152
153
-
154
- # The main dispatcher for all incoming SMB packets
155
- def smb_recv_parse ( expected_type , ignore_errors = false )
153
+ # Receive a full SMB reply and cache the parsed packet
154
+ def smb_recv_and_cache
155
+ @smb_recv_cache ||= [ ]
156
156
157
157
# This will throw an exception if it fails to read the whole packet
158
158
data = self . smb_recv
159
159
160
160
pkt = CONST ::SMB_BASE_PKT . make_struct
161
161
pkt . from_s ( data )
162
- res = pkt
162
+
163
+ # Store the received packet into the cache
164
+ @smb_recv_cache << [ pkt , data , Time . now ]
165
+ end
166
+
167
+ # Scan the packet receive cache for a matching response
168
+ def smb_recv_cache_find_match ( expected_type )
169
+
170
+ clean = [ ]
171
+ found = nil
172
+
173
+ @smb_recv_cache . each do |cent |
174
+ pkt , data , tstamp = cent
175
+
176
+ # Return matching packets and mark for removal
177
+ if pkt [ 'Payload' ] [ 'SMB' ] . v [ 'Command' ] == expected_type
178
+ found = [ pkt , data ]
179
+ clean << cent
180
+ end
181
+
182
+ # Purge any packets older than 5 minutes
183
+ if Time . now . to_i - tstamp . to_i > 300
184
+ clean << cent
185
+ end
186
+
187
+ break if found
188
+ end
189
+
190
+ clean . each do |cent |
191
+ @smb_recv_cache . delete ( cent )
192
+ end
193
+
194
+ found
195
+ end
196
+
197
+ # The main dispatcher for all incoming SMB packets
198
+ def smb_recv_parse ( expected_type , ignore_errors = false )
199
+
200
+ pkt = nil
201
+ data = nil
202
+
203
+ # This allows for some leeway when a previous response has not
204
+ # been processed but a new request was sent. The old response
205
+ # will eventually be timed out of the cache.
206
+ 1 . upto ( 3 ) do |attempt |
207
+ smb_recv_and_cache
208
+ pkt , data = smb_recv_cache_find_match ( expected_type )
209
+ break if pkt
210
+ end
163
211
164
212
begin
165
213
case pkt [ 'Payload' ] [ 'SMB' ] . v [ 'Command' ]
166
214
167
215
when CONST ::SMB_COM_NEGOTIATE
168
- res = smb_parse_negotiate ( pkt , data )
216
+ res = smb_parse_negotiate ( pkt , data )
169
217
170
218
when CONST ::SMB_COM_SESSION_SETUP_ANDX
171
- res = smb_parse_session_setup ( pkt , data )
219
+ res = smb_parse_session_setup ( pkt , data )
172
220
173
221
when CONST ::SMB_COM_TREE_CONNECT_ANDX
174
- res = smb_parse_tree_connect ( pkt , data )
222
+ res = smb_parse_tree_connect ( pkt , data )
175
223
176
224
when CONST ::SMB_COM_TREE_DISCONNECT
177
- res = smb_parse_tree_disconnect ( pkt , data )
225
+ res = smb_parse_tree_disconnect ( pkt , data )
178
226
179
227
when CONST ::SMB_COM_NT_CREATE_ANDX
180
- res = smb_parse_create ( pkt , data )
228
+ res = smb_parse_create ( pkt , data )
181
229
182
230
when CONST ::SMB_COM_TRANSACTION , CONST ::SMB_COM_TRANSACTION2
183
- res = smb_parse_trans ( pkt , data )
231
+ res = smb_parse_trans ( pkt , data )
184
232
185
233
when CONST ::SMB_COM_NT_TRANSACT
186
- res = smb_parse_nttrans ( pkt , data )
234
+ res = smb_parse_nttrans ( pkt , data )
187
235
188
236
when CONST ::SMB_COM_NT_TRANSACT_SECONDARY
189
- res = smb_parse_nttrans ( pkt , data )
237
+ res = smb_parse_nttrans ( pkt , data )
190
238
191
239
when CONST ::SMB_COM_OPEN_ANDX
192
- res = smb_parse_open ( pkt , data )
240
+ res = smb_parse_open ( pkt , data )
193
241
194
242
when CONST ::SMB_COM_WRITE_ANDX
195
- res = smb_parse_write ( pkt , data )
243
+ res = smb_parse_write ( pkt , data )
196
244
197
245
when CONST ::SMB_COM_READ_ANDX
198
- res = smb_parse_read ( pkt , data )
246
+ res = smb_parse_read ( pkt , data )
199
247
200
248
when CONST ::SMB_COM_CLOSE
201
- res = smb_parse_close ( pkt , data )
249
+ res = smb_parse_close ( pkt , data )
202
250
203
251
when CONST ::SMB_COM_DELETE
204
- res = smb_parse_delete ( pkt , data )
252
+ res = smb_parse_delete ( pkt , data )
205
253
206
254
else
207
255
raise XCEPT ::InvalidCommand
208
256
end
209
257
210
- if ( pkt [ 'Payload' ] [ 'SMB' ] . v [ 'Command' ] != expected_type )
211
- raise XCEPT ::InvalidType
212
- end
213
-
214
258
if ( ignore_errors == false and pkt [ 'Payload' ] [ 'SMB' ] . v [ 'ErrorClass' ] != 0 )
215
259
raise XCEPT ::ErrorCode
216
260
end
217
261
218
- rescue XCEPT ::InvalidWordCount , XCEPT ::InvalidCommand , XCEPT ::InvalidType , XCEPT :: ErrorCode
262
+ rescue XCEPT ::InvalidWordCount , XCEPT ::InvalidCommand , XCEPT ::ErrorCode
219
263
$!. word_count = pkt [ 'Payload' ] [ 'SMB' ] . v [ 'WordCount' ]
220
264
$!. command = pkt [ 'Payload' ] [ 'SMB' ] . v [ 'Command' ]
221
265
$!. error_code = pkt [ 'Payload' ] [ 'SMB' ] . v [ 'ErrorClass' ]
@@ -1837,88 +1881,150 @@ def find_first(path)
1837
1881
0 , # Storage type is zero
1838
1882
] . pack ( 'vvvvV' ) + path + "\x00 "
1839
1883
1840
- begin
1841
- resp = trans2 ( CONST ::TRANS2_FIND_FIRST2 , parm , '' )
1842
- search_next = 0
1843
- begin
1844
- pcnt = resp [ 'Payload' ] . v [ 'ParamCount' ]
1845
- dcnt = resp [ 'Payload' ] . v [ 'DataCount' ]
1846
- poff = resp [ 'Payload' ] . v [ 'ParamOffset' ]
1847
- doff = resp [ 'Payload' ] . v [ 'DataOffset' ]
1884
+ resp = trans2 ( CONST ::TRANS2_FIND_FIRST2 , parm , '' )
1885
+ search_next = 0
1886
+
1887
+ # Loop until we run out of results
1888
+ loop do
1889
+ pcnt = resp [ 'Payload' ] . v [ 'ParamCount' ]
1890
+ dcnt = resp [ 'Payload' ] . v [ 'DataCount' ]
1891
+ poff = resp [ 'Payload' ] . v [ 'ParamOffset' ]
1892
+ doff = resp [ 'Payload' ] . v [ 'DataOffset' ]
1848
1893
1849
- # Get the raw packet bytes
1850
- resp_rpkt = resp . to_s
1894
+ # Get the raw packet bytes
1895
+ resp_rpkt = resp . to_s
1851
1896
1852
- # Remove the NetBIOS header
1853
- resp_rpkt . slice! ( 0 , 4 )
1897
+ # Remove the NetBIOS header
1898
+ resp_rpkt . slice! ( 0 , 4 )
1854
1899
1855
- resp_parm = resp_rpkt [ poff , pcnt ]
1856
- resp_data = resp_rpkt [ doff , dcnt ]
1900
+ resp_parm = resp_rpkt [ poff , pcnt ]
1901
+ resp_data = resp_rpkt [ doff , dcnt ]
1857
1902
1858
- if search_next == 0
1859
- # search id, search count, end of search, error offset, last name offset
1860
- sid , scnt , eos , eoff , loff = resp_parm . unpack ( 'v5' )
1861
- else
1862
- # FINX_NEXT doesn't return a SID
1863
- scnt , eos , eoff , loff = resp_parm . unpack ( 'v4' )
1864
- end
1865
- didx = 0
1866
- while ( didx < resp_data . length )
1867
- info_buff = resp_data [ didx , 70 ]
1868
- break if info_buff . length != 70
1869
- info = info_buff . unpack (
1870
- 'V' + # Next Entry Offset
1871
- 'V' + # File Index
1872
- 'VV' + # Time Create
1873
- 'VV' + # Time Last Access
1874
- 'VV' + # Time Last Write
1875
- 'VV' + # Time Change
1876
- 'VV' + # End of File
1877
- 'VV' + # Allocation Size
1878
- 'V' + # File Attributes
1879
- 'V' + # File Name Length
1880
- 'V' + # Extended Attr List Length
1881
- 'C' + # Short File Name Length
1882
- 'C' # Reserved
1883
- )
1884
- name = resp_data [ didx + 70 + 24 , info [ 15 ] ] . sub ( /\x00 +$/n , '' )
1885
- files [ name ] =
1886
- {
1887
- 'type' => ( ( info [ 14 ] & 0x10 ) ==0x10 ) ? 'D' : 'F' ,
1888
- 'attr' => info [ 14 ] ,
1889
- 'info' => info
1890
- }
1891
-
1892
- break if info [ 0 ] == 0
1893
- didx += info [ 0 ]
1894
- end
1895
- last_search_id = sid
1896
- last_offset = loff
1897
- last_filename = name
1898
- if eos == 0 and last_offset != 0 #If we aren't at the end of the search, run find_next
1899
- resp = find_next ( last_search_id , last_offset , last_filename )
1900
- search_next = 1 # Flip bit so response params will parse correctly
1901
- end
1902
- end until eos != 0 or last_offset == 0
1903
- rescue ::Exception
1904
- raise $!
1903
+ if search_next == 0
1904
+ # search id, search count, end of search, error offset, last name offset
1905
+ sid , scnt , eos , eoff , loff = resp_parm . unpack ( 'v5' )
1906
+ else
1907
+ # FIND_NEXT doesn't return a SID
1908
+ scnt , eos , eoff , loff = resp_parm . unpack ( 'v4' )
1909
+ end
1910
+
1911
+ didx = 0
1912
+ while ( didx < resp_data . length )
1913
+ info_buff = resp_data [ didx , 70 ]
1914
+ break if info_buff . length != 70
1915
+
1916
+ info = info_buff . unpack (
1917
+ 'V' + # Next Entry Offset
1918
+ 'V' + # File Index
1919
+ 'VV' + # Time Create
1920
+ 'VV' + # Time Last Access
1921
+ 'VV' + # Time Last Write
1922
+ 'VV' + # Time Change
1923
+ 'VV' + # End of File
1924
+ 'VV' + # Allocation Size
1925
+ 'V' + # File Attributes
1926
+ 'V' + # File Name Length
1927
+ 'V' + # Extended Attr List Length
1928
+ 'C' + # Short File Name Length
1929
+ 'C' # Reserved
1930
+ )
1931
+
1932
+ name = resp_data [ didx + 70 + 24 , info [ 15 ] ]
1933
+
1934
+ # Verify that the filename was actually present
1935
+ break unless name
1936
+
1937
+ # Key the file list minus any trailing nulls
1938
+ files [ name . sub ( /\x00 +$/n , '' ) ] =
1939
+ {
1940
+ 'type' => ( info [ 14 ] & CONST ::SMB_EXT_FILE_ATTR_DIRECTORY == 0 ) ? 'F' : 'D' ,
1941
+ 'attr' => info [ 14 ] ,
1942
+ 'info' => info
1943
+ }
1944
+
1945
+ break if info [ 0 ] == 0
1946
+ didx += info [ 0 ]
1947
+ end
1948
+
1949
+ last_search_id = sid
1950
+ last_offset = loff
1951
+ last_filename = name
1952
+
1953
+ # Exit the search if we reached the end of our results
1954
+ break if ( eos != 0 or last_search_id . nil? or last_offset . to_i == 0 )
1955
+
1956
+ # If we aren't at the end of the search, run find_next
1957
+ resp = find_next ( last_search_id , last_offset , last_filename )
1958
+
1959
+ # Flip bit so response params will parse correctly
1960
+ search_next = 1
1905
1961
end
1906
1962
1907
- return files
1963
+ files
1908
1964
end
1909
1965
1910
1966
# Supplements find_first if file/dir count exceeds max search count
1911
1967
def find_next ( sid , resume_key , last_filename )
1912
1968
1913
1969
parm = [
1914
- sid , # Search ID
1915
- 20 , # Maximum search count (Size of 20 keeps response to 1 packet)
1916
- 260 , # Level of interest
1917
- resume_key , # Resume key from previous (Last name offset)
1918
- 6 , # Close search if end of search
1919
- ] . pack ( 'vvvVv' ) + last_filename . to_s + "\x00 " # Last filename returned from find_first or find_next
1920
- resp = trans2 ( CONST ::TRANS2_FIND_NEXT2 , parm , '' )
1921
- return resp # Returns the FIND_NEXT2 response packet for parsing by the find_first function
1970
+ sid , # Search ID
1971
+ 20 , # Maximum search count (Size of 20 keeps response to 1 packet)
1972
+ 260 , # Level of interest
1973
+ resume_key , # Resume key from previous (Last name offset)
1974
+ 6 , # Close search if end of search
1975
+ ] . pack ( 'vvvVv' ) +
1976
+ last_filename . to_s + # Last filename returned from find_first or find_next
1977
+ "\x00 " # Terminate the file name
1978
+
1979
+ # Returns the FIND_NEXT2 response packet for parsing by the find_first function
1980
+ trans2 ( CONST ::TRANS2_FIND_NEXT2 , parm , '' )
1981
+ end
1982
+
1983
+ # Recursively search for files matching a regular expression
1984
+ def file_search ( current_path , regex , depth )
1985
+ depth -= 1
1986
+ return [ ] if depth < 0
1987
+
1988
+ results = find_first ( current_path + "*" )
1989
+ files = [ ]
1990
+
1991
+ results . each_pair do |fname , finfo |
1992
+
1993
+ # Skip current and parent directory results
1994
+ next if %W{ . .. } . include? ( fname )
1995
+
1996
+ # Verify the results contain an attribute
1997
+ next unless finfo and finfo [ 'attr' ]
1998
+
1999
+ if finfo [ 'attr' ] & CONST ::SMB_EXT_FILE_ATTR_DIRECTORY == 0
2000
+ # Add any matching files to our result set
2001
+ files << "#{ current_path } #{ fname } " if fname =~ regex
2002
+ else
2003
+ # Recurse into the discovery subdirectory for more files
2004
+ begin
2005
+ search_path = "#{ current_path } #{ fname } \\ "
2006
+ file_search ( search_path , regex , depth ) . each { |fn | files << fn }
2007
+ rescue Rex ::Proto ::SMB ::Exceptions ::ErrorCode => e
2008
+
2009
+ # Ignore common errors related to permissions and non-files
2010
+ if %W{
2011
+ STATUS_ACCESS_DENIED
2012
+ STATUS_NO_SUCH_FILE
2013
+ STATUS_OBJECT_NAME_NOT_FOUND
2014
+ STATUS_OBJECT_PATH_NOT_FOUND
2015
+ } . include? e . get_error ( e . error_code )
2016
+ next
2017
+ end
2018
+
2019
+ $stderr. puts [ e , e . get_error ( e . error_code ) , search_path ]
2020
+
2021
+ raise e
2022
+ end
2023
+ end
2024
+
2025
+ end
2026
+
2027
+ files . uniq
1922
2028
end
1923
2029
1924
2030
# Creates a new directory on the mounted tree
@@ -1931,31 +2037,27 @@ def create_directory(name)
1931
2037
# public read/write methods
1932
2038
attr_accessor :native_os , :native_lm , :encrypt_passwords , :extended_security , :read_timeout , :evasion_opts
1933
2039
attr_accessor :verify_signature , :use_ntlmv2 , :usentlm2_session , :send_lm , :use_lanman_key , :send_ntlm
1934
- attr_accessor :system_time , :system_zone
1935
- #misc
1936
- attr_accessor :spnopt # used for SPN
2040
+ attr_accessor :system_time , :system_zone
2041
+ attr_accessor :spnopt
1937
2042
1938
2043
# public read methods
1939
2044
attr_reader :dialect , :session_id , :challenge_key , :peer_native_lm , :peer_native_os
1940
2045
attr_reader :default_domain , :default_name , :auth_user , :auth_user_id
1941
2046
attr_reader :multiplex_id , :last_tree_id , :last_file_id , :process_id , :last_search_id
1942
2047
attr_reader :dns_host_name , :dns_domain_name
1943
2048
attr_reader :security_mode , :server_guid
1944
- #signing related
1945
2049
attr_reader :sequence_counter , :signing_key , :require_signing
1946
2050
1947
- # private methods
2051
+ # private write methods
1948
2052
attr_writer :dialect , :session_id , :challenge_key , :peer_native_lm , :peer_native_os
1949
2053
attr_writer :default_domain , :default_name , :auth_user , :auth_user_id
1950
2054
attr_writer :dns_host_name , :dns_domain_name
1951
2055
attr_writer :multiplex_id , :last_tree_id , :last_file_id , :process_id , :last_search_id
1952
2056
attr_writer :security_mode , :server_guid
1953
- #signing related
1954
2057
attr_writer :sequence_counter , :signing_key , :require_signing
1955
2058
1956
2059
attr_accessor :socket
1957
2060
1958
-
1959
2061
end
1960
2062
end
1961
2063
end
0 commit comments