ruby_smb 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.travis.yml +4 -1
  4. data/README.md +35 -47
  5. data/examples/enum_registry_key.rb +28 -0
  6. data/examples/enum_registry_values.rb +30 -0
  7. data/examples/pipes.rb +2 -1
  8. data/examples/read_registry_key_value.rb +32 -0
  9. data/lib/ruby_smb/client/authentication.rb +43 -25
  10. data/lib/ruby_smb/client/echo.rb +20 -2
  11. data/lib/ruby_smb/client/negotiation.rb +27 -12
  12. data/lib/ruby_smb/client/tree_connect.rb +20 -14
  13. data/lib/ruby_smb/client/utils.rb +2 -2
  14. data/lib/ruby_smb/client/winreg.rb +46 -0
  15. data/lib/ruby_smb/client.rb +28 -4
  16. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  17. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  18. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  19. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  20. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  21. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  22. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  23. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  24. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  25. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  26. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  27. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  28. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  29. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  30. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  31. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  32. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  33. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  34. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  35. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  36. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  37. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  38. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  39. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  40. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  41. data/lib/ruby_smb/dcerpc.rb +38 -0
  42. data/lib/ruby_smb/dispatcher/socket.rb +3 -1
  43. data/lib/ruby_smb/error.rb +40 -1
  44. data/lib/ruby_smb/generic_packet.rb +33 -4
  45. data/lib/ruby_smb/smb1/file.rb +67 -11
  46. data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
  47. data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
  48. data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
  49. data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
  50. data/lib/ruby_smb/smb1/packet/empty_packet.rb +10 -1
  51. data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
  52. data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
  53. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
  54. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
  55. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
  56. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
  57. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
  58. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
  59. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
  60. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
  61. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
  62. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
  63. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
  64. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
  65. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -1
  66. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
  67. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -1
  68. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
  69. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
  70. data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
  71. data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
  72. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
  73. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
  74. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
  75. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
  76. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
  77. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
  78. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
  79. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
  80. data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
  81. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
  82. data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
  83. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
  84. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
  85. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
  86. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
  87. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  88. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
  89. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +2 -5
  90. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
  91. data/lib/ruby_smb/smb1/pipe.rb +86 -5
  92. data/lib/ruby_smb/smb1/tree.rb +40 -2
  93. data/lib/ruby_smb/smb2/file.rb +97 -1
  94. data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
  95. data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
  96. data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
  97. data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
  98. data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
  99. data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
  100. data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -3
  101. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
  102. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
  103. data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
  104. data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
  105. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +2 -5
  106. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +2 -1
  107. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
  108. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
  109. data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
  110. data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
  111. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  112. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
  113. data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
  114. data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
  115. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +2 -5
  116. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -2
  117. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
  118. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
  119. data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
  120. data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
  121. data/lib/ruby_smb/smb2/pipe.rb +98 -11
  122. data/lib/ruby_smb/smb2/tree.rb +44 -6
  123. data/lib/ruby_smb/version.rb +1 -1
  124. data/lib/ruby_smb.rb +0 -1
  125. data/ruby_smb.gemspec +3 -3
  126. data/spec/lib/ruby_smb/client_spec.rb +271 -11
  127. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  128. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  129. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  130. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  131. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  132. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  133. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  134. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  135. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  136. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  137. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  138. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  139. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  140. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  141. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  142. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  143. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  144. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  145. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  146. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  147. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  148. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  149. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  150. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  151. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +3 -1
  152. data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
  153. data/spec/lib/ruby_smb/smb1/file_spec.rb +191 -2
  154. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +68 -0
  155. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
  156. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
  157. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
  158. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +272 -149
  159. data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
  160. data/spec/lib/ruby_smb/smb2/file_spec.rb +295 -2
  161. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +78 -0
  162. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
  163. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +8 -0
  164. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +322 -145
  165. data/spec/lib/ruby_smb/smb2/tree_spec.rb +214 -0
  166. data.tar.gz.sig +0 -0
  167. metadata +70 -11
  168. metadata.gz.sig +0 -0
  169. data/lib/ruby_smb/smb1/dcerpc.rb +0 -67
  170. data/lib/ruby_smb/smb2/dcerpc.rb +0 -70
  171. data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +0 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d89696524ee8ae603c50be9ffd9fe6bbcb1f217f
4
- data.tar.gz: 6fcbab915f26646e9981caf65f5d75de64cea4d1
3
+ metadata.gz: 3fe1b1b7bdd2516bd388f86c020a9fa99b70faa0
4
+ data.tar.gz: aa11ed030f543c0d83b61929d10c35eea20a8455
5
5
  SHA512:
6
- metadata.gz: 7cd15fadb1d5b251fd73a20914830a26eae38363d3e622bf3219cf46dfa2f0fa72716ff7ff2bd49fa503eaac922676c193b5002bd107f6a9502310c4cadcdda5
7
- data.tar.gz: 5bdef20abe76ea38602ff99092c15ae19d3bda1d799ae5bda38fd0d2e7110b2115275a962b3df113e31b2a7b29d35ef0e5577bd9c47301fa059205f5f4bd3a11
6
+ metadata.gz: 92cc9708588a610104f8bacc8430fedd537f2ba283bb323b791ff45005a43417cf9570ce63f658b7a38c566570c2b651adb17b6750b3b117279552efaf138cab
7
+ data.tar.gz: 071db1651283d080b2adc728cd1f2225e051d52f86608f9f54dd6d79d39330127c3b5179d143ec9e5e155d132af1e128c4c9ca42744264caec8b249e0b0c86ec
checksums.yaml.gz.sig CHANGED
Binary file
data/.travis.yml CHANGED
@@ -1,5 +1,8 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  rvm:
4
- - '2.2'
4
+ - '2.3.8'
5
+ - '2.4.5'
6
+ - '2.5.3'
7
+ - '2.6.1'
5
8
  - 'jruby-9000'
data/README.md CHANGED
@@ -4,11 +4,12 @@
4
4
  [![Code Climate](https://codeclimate.com/github/rapid7/ruby_smb.png)](https://codeclimate.com/github/rapid7/ruby_smb)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/rapid7/ruby_smb/badge.svg?branch=master)](https://coveralls.io/github/rapid7/ruby_smb?branch=master)
6
6
 
7
- A native Ruby implementation of the SMB Protocol Family. It currently supports
7
+ This is a native Ruby implementation of the SMB Protocol Family. It currently supports:
8
+
8
9
  1. [[MS-SMB]](https://msdn.microsoft.com/en-us/library/cc246231.aspx)
9
10
  1. [[MS-SMB2]](http://msdn.microsoft.com/en-us/library/cc246482.aspx)
10
11
 
11
- This library currently include both a client level, and packet level support. A user can parse and manipulate raw SMB packets, or use the simple client to perform SMB operations.
12
+ The RubySMB library provides client-level and packet-level support for the protocol. A user can parse and manipulate raw SMB packets, or use the client to perform higher-level SMB operations.
12
13
 
13
14
  See the Wiki for more information on this project's long-term goals, style guide, and developer tips.
14
15
 
@@ -32,22 +33,17 @@ Or install it yourself as:
32
33
 
33
34
  ### Defining a packet
34
35
 
35
- All packets are implemented in a declarative style with BinData. Nested data
36
- structures are used where appropriate to give users an easy method of adjusting
37
- data.
36
+ All packets are implemented in a declarative style with BinData. Nested data structures are used where appropriate to give users an easy method of manipulating individual fields inside of a packet.
38
37
 
39
38
  #### SMB1
39
+
40
40
  SMB1 Packets are made up of three basic components:
41
+
41
42
  1. **The SMB Header** - This is a standard SMB Header. All SMB1 packets use the same SMB header.
42
- 1. **The Parameter Block** - This is where function parameters are passed across the wire in the packet. Parameter Blocks will always
43
- have a 'Word Count' field that gives the size of the Parameter Block in words(2-bytes)
44
- 1. **The Data Block** - This is the data section of the packet. the Data Block will always have a 'byte count' field that gives the size of
45
- the Data block in bytes.
43
+ 1. **The Parameter Block** - This is where function parameters are passed across the wire in the packet. Parameter blocks will always have a 'Word Count' field that gives the size of the parameter block in words (2-bytes)
44
+ 1. **The Data Block** - This is the data section of the packet. The data block will always have a 'byte count' field that gives the size of the Data block in bytes.
46
45
 
47
- The SMB Header can always just be declared as a field in the BinData DSL for the packet class, because its structure never changes.
48
- For the ParameterBlock and DataBlocks, we always define subclasses for this particular packet. They inherit the 'Word Count' and
49
- 'Byte Count' fields, along with the auto-calculation routines for those fields, from their ancestors. Any other fields are then
50
- defined in our subclass before we start the DSL declarations for the packet.
46
+ The SMB Header can always just be declared as a field in the BinData DSL for the packet class, because its structure never changes. For the Parameter block and data blocks, we always define subclasses for this particular packet. They inherit the 'Word Count' and 'Byte Count' fields, along with the auto-calculation routines for those fields, from their ancestors. Any other fields are then defined in our subclass before we start the DSL declarations for the packet.
51
47
 
52
48
  Example:
53
49
 
@@ -90,8 +86,7 @@ end
90
86
 
91
87
  #### SMB2
92
88
 
93
- SMB2 Packets are far simpler than their older SMB1 counterparts. We still abstract out the SMB2 header since it is the same
94
- structure used for every packet. Beyond that, the SMB2 packet is relatively flat in comparison to SMB1.
89
+ SMB2 Packets are far simpler than their older SMB1 counterparts. We still abstract out the SMB2 header since it is the same structure used for every packet. Beyond that, the SMB2 packet is relatively flat in comparison to SMB1.
95
90
 
96
91
  Example:
97
92
  ```ruby
@@ -127,8 +122,7 @@ end
127
122
  ### Using a Packet class
128
123
 
129
124
  #### Manually
130
- You can instantiate an instance of a particular packet class, and then reach into the data structure to set or read explicit
131
- values in a fairly straightforward manner.
125
+ You can create an instance of any particular packet class, and then reach into the data structure to set or read explicit values in a fairly straightforward manner.
132
126
 
133
127
  Example:
134
128
  ```ruby
@@ -152,8 +146,7 @@ Example:
152
146
  2.3.3 :009 >
153
147
  ```
154
148
 
155
- You can also pass field/value pairs into the packet constructor as arguments, prefilling out certain fields
156
- if you wish.
149
+ You can also pass field/value pairs into the packet constructor as arguments, defaulting individual fields as desired.
157
150
 
158
151
  Example:
159
152
  ```ruby
@@ -165,9 +158,7 @@ Example:
165
158
 
166
159
  #### Reading from a Binary Blob
167
160
 
168
- Sometimes you need to read a binary blob and apply one of the packet structures to it.
169
- For example, when you are reading a response packet off of the wire, you will need to read the raw response
170
- string into an actual packet class. This is done using the #read class method.
161
+ Sometimes you need to read a binary data and apply one of the packet structures to it. For example, when you are reading a response packet, you will need to read the raw response string into an actual packet class. This is done using the #read class method.
171
162
 
172
163
  ```ruby
173
164
  2.3.3 :014 > blob = "\xFFSMB+\x00\x00\x00\x00\x98\x01`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
@@ -178,8 +169,7 @@ string into an actual packet class. This is done using the #read class method.
178
169
  ```
179
170
 
180
171
  #### Outputting to a Binary Blob
181
- Any structure or packet in rubySMB can also be output back into a binary blob using
182
- BinData's #to_binary_s method.
172
+ Any structure or packet in RubySMB can also be converted back into a binary blob using BinData's #to_binary_s method.
183
173
 
184
174
  Example:
185
175
  ```ruby
@@ -190,38 +180,35 @@ Example:
190
180
  ```
191
181
  ### Using the Client
192
182
 
193
- Sitting on top of the packet layer in RubySMB is the RubySMB::Client. This is the level msot users will interact with.
194
- It provides fairly simple conveience methods for performing SMB actions. It handles the creation, sending and receiving of packets
195
- for the user, relying on reasonable defaults in many cases.
183
+ Sitting on top of the packet layer in RubySMB is the RubySMB::Client class. This is the abstraction that most users of RubySMB will interact with. It provides simple conveience methods for performing SMB actions. It handles the creation, sending and receiving of packets for the user, providing reasonable defaults in many cases.
196
184
 
197
185
  #### Negotiation
198
186
 
199
- The RubySMB Client is capabale of multi-protocol negotiation. The user simply specifies whether SMB1 and/or SMB2 should be supported, and
200
- the client will negotiate the propper protocol and dialect behind the scenes.
187
+ The RubySMB client is capable of multi-protocol negotiation. The user simply specifies whether SMB1 and/or SMB2 should be supported, and the client negotiates the protocol and dialect behind the scenes.
201
188
 
202
- In the below example, we tell the client that both SMb1 and SMB2 should be supported. The Client will then Negotiate with the
203
- server on which version should be used. The user does not have to ever worry about which version was negotiated.
204
- Example:
189
+ In the following example, we tell the client that both SMB1 and SMB2 should be supported. The client will then negotiate with the server which version should be used.
190
+
191
+ Negotiation Example:
205
192
  ```ruby
206
193
  sock = TCPSocket.new address, 445
207
194
  dispatcher = RubySMB::Dispatcher::Socket.new(sock)
208
195
 
209
- client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: 'msfadmin', password: 'msfadmin')
196
+ client = RubySMB::Client.new(dispatcher, username: 'msfadmin', password: 'msfadmin')
210
197
  client.negotiate
211
198
  ```
212
199
 
213
200
  #### Authentication
214
201
 
215
- Authentication is achieved via the [ruby ntlm gem](https://rubygems.org/gems/rubyntlm). While the client
216
- will not currently attempt older basic authentication on its own, it will attempt an anonymous login, if no
217
- user credentials are supplied:
202
+ RubySMB uses the [Ruby NTLM gem](https://rubygems.org/gems/rubyntlm) for authentication. While the client
203
+ will not currently attempt older basic authentication on its own, it will attempt an anonymous login if no
204
+ user credentials are supplied.
218
205
 
219
206
  Authenticated Example:
220
207
  ```ruby
221
208
  sock = TCPSocket.new address, 445
222
209
  dispatcher = RubySMB::Dispatcher::Socket.new(sock)
223
210
 
224
- client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: 'msfadmin', password: 'msfadmin')
211
+ client = RubySMB::Client.new(dispatcher, username: 'msfadmin', password: 'msfadmin')
225
212
  client.negotiate
226
213
  client.authenticate
227
214
  ```
@@ -231,24 +218,23 @@ Anonymous Example:
231
218
  sock = TCPSocket.new address, 445
232
219
  dispatcher = RubySMB::Dispatcher::Socket.new(sock)
233
220
 
234
- client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: '', password: '')
221
+ client = RubySMB::Client.new(dispatcher, username: '', password: '')
235
222
  client.negotiate
236
223
  client.authenticate
237
224
  ```
238
225
 
239
226
  #### Connecting to a Tree
240
227
 
241
- While there is one Client, that has branching code-paths for SMB1 and SMB2, once you connect to a
242
- Tree you will be given a protocol specific Tree object. This Tree object will be responsible for all file operations
243
- that are to be conducted on that Tree.
228
+ While there is one RubySMB::Client object that supports both SMB1 and SMB2, once the library connects to an SMB tree, it returns a protocol-specific RubySMB::Tree object. This Tree object executes all subsequent file operations on the tree.
229
+
230
+ In the below example we see a simple script to connect to a remote tree, and list all files in a given sub-directory.
244
231
 
245
- In the below example we see a simple script to connect to a remote Tree, and list all files in a given sub-directory.
246
232
  Example:
247
233
  ```ruby
248
234
  sock = TCPSocket.new address, 445
249
235
  dispatcher = RubySMB::Dispatcher::Socket.new(sock)
250
236
 
251
- client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: 'msfadmin', password: 'msfadmin')
237
+ client = RubySMB::Client.new(dispatcher, username: 'msfadmin', password: 'msfadmin')
252
238
  client.negotiate
253
239
  client.authenticate
254
240
 
@@ -273,28 +259,30 @@ Example:
273
259
 
274
260
 
275
261
  ## Developer tips
276
- You'll want to have Wireshark and perhaps a tool like Impacket (which provides a small SMB client in one of its examples) installed to help with your work:
262
+
263
+ It is useful to have Wireshark and a reference SMB client, such as Impacket's installed to help debug and compare output:
277
264
 
278
265
  ### Wireshark
266
+
267
+ Configure Wireshark in Debian-based systems to be able to capture traffic without root user privileges:
268
+
279
269
  - `sudo apt-get install wireshark`
280
270
  - `sudo dpkg-reconfigure wireshark-common`
281
271
  - `sudo addgroup wireshark`
282
272
  - `sudo usermod -a -G wireshark <USERNAME>`
283
273
 
284
274
  ### Impacket
275
+
285
276
  - `sudo apt-get install python-setuptools`
286
277
  - `sudo easy_install pyasn1 pycrypto`
287
278
  - Download from GitHub (https://github.com/coresecurity/impacket)
288
279
  - `sudo python setup.py install`
289
280
  - `cd examples && python smbclient.py <USER>:<PASS>@<WINDOWS HOST IP>`
290
281
 
291
-
292
-
293
282
  ## License
294
283
 
295
284
  `ruby_smb` is released under a 3-clause BSD license. See [LICENSE.txt](LICENSE.txt) for full text.
296
285
 
297
-
298
286
  ## Contributing
299
287
 
300
288
  1. Fork it ( https://github.com/rapid7/ruby_smb/fork )
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This example script is used for testing Winreg registry key enumeration functionality
4
+ # It will attempt to connect to a host and enumerate registry subkeys of a specified registry key.
5
+ # Example usage: ruby enum_registry_key.rb 192.168.172.138 msfadmin msfadmin HKLM\\My\\Key
6
+ # This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin credentialas and enumerate HKLM\\My\\Key subkeys.
7
+
8
+ require 'bundler/setup'
9
+ require 'ruby_smb'
10
+
11
+ address = ARGV[0]
12
+ username = ARGV[1]
13
+ password = ARGV[2]
14
+ registry_key = ARGV[3]
15
+
16
+ sock = TCPSocket.new address, 445
17
+ dispatcher = RubySMB::Dispatcher::Socket.new(sock, read_timeout: 60)
18
+
19
+ client = RubySMB::Client.new(dispatcher, smb1: true, smb2: true, username: username, password: password)
20
+ protocol = client.negotiate
21
+ status = client.authenticate
22
+
23
+ puts "#{protocol} : #{status}"
24
+
25
+ enum_result = client.enum_registry_key(address, registry_key)
26
+ puts enum_result
27
+
28
+ client.disconnect!
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This example script is used for testing values enumeration of a specific Winreg registry.
4
+ # It will attempt to connect to a host and enumerate values of a specified registry key.
5
+ # Example usage: ruby enum_registry_values.rb 192.168.172.138 msfadmin msfadmin HKLM\\My\\Key
6
+ # This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin credentialas and enumerate HKLM\\My\\Key values.
7
+
8
+ require 'bundler/setup'
9
+ require 'ruby_smb'
10
+
11
+ address = ARGV[0]
12
+ username = ARGV[1]
13
+ password = ARGV[2]
14
+ registry_key = ARGV[3]
15
+
16
+ sock = TCPSocket.new address, 445
17
+ dispatcher = RubySMB::Dispatcher::Socket.new(sock, read_timeout: 60)
18
+
19
+ client = RubySMB::Client.new(dispatcher, smb1: true, smb2: true, username: username, password: password)
20
+ protocol = client.negotiate
21
+ status = client.authenticate
22
+
23
+ puts "#{protocol} : #{status}"
24
+
25
+ enum_result = client.enum_registry_values(address, registry_key)
26
+ puts enum_result
27
+
28
+ client.disconnect!
29
+
30
+
data/examples/pipes.rb CHANGED
@@ -33,7 +33,8 @@ end
33
33
 
34
34
  client.authenticate
35
35
  client.tree_connect("\\\\#{address}\\IPC$")
36
- pipe = client.create_pipe(pipename, nil)
36
+ client.create_pipe(pipename)
37
+ pipe = client.last_file
37
38
 
38
39
  puts "Available: #{pipe.peek_available}"
39
40
  puts "PipeState: #{pipe.peek_state}" # 3 == OK
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This example script is used for testing the Winreg registry key value read functionality.
4
+ # It will attempt to connect to a host and reads the value of a specified registry key.
5
+ # Example usage: ruby enum_registry_key.rb 192.168.172.138 msfadmin msfadmin HKLM\\My\\Key ValueName
6
+ # This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin credentialas and reads the ValueName data corresponding to the HKLM\\My\\Key registry key.
7
+
8
+ require 'bundler/setup'
9
+ require 'ruby_smb'
10
+
11
+ address = ARGV[0]
12
+ username = ARGV[1]
13
+ password = ARGV[2]
14
+ registry_key = ARGV[3]
15
+ value_name = ARGV[4]
16
+
17
+ sock = TCPSocket.new address, 445
18
+ dispatcher = RubySMB::Dispatcher::Socket.new(sock, read_timeout: 60)
19
+
20
+ client = RubySMB::Client.new(dispatcher, smb1: true, smb2: true, username: username, password: password)
21
+ protocol = client.negotiate
22
+ status = client.authenticate
23
+
24
+ puts "#{protocol}: #{status}"
25
+ puts "Key: #{registry_key}"
26
+ puts "Value: #{value_name}"
27
+
28
+ key_value = client.read_registry_key_value(address, registry_key, value_name)
29
+ puts key_value
30
+
31
+ client.disconnect!
32
+
@@ -54,14 +54,15 @@ module RubySMB
54
54
  end
55
55
 
56
56
  def smb1_anonymous_auth_response(raw_response)
57
- begin
58
- packet = RubySMB::SMB1::Packet::SessionSetupLegacyResponse.read(raw_response)
59
- rescue
60
- packet = RubySMB::SMB1::Packet::EmptyPacket.read(raw_response)
61
- end
62
-
63
- unless packet.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
64
- raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb_header.command} and not #{RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP}"
57
+ packet = RubySMB::SMB1::Packet::SessionSetupLegacyResponse.read(raw_response)
58
+
59
+ unless packet.valid?
60
+ raise RubySMB::Error::InvalidPacket.new(
61
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
62
+ expected_cmd: RubySMB::SMB1::Packet::SessionSetupLegacyResponse::COMMAND,
63
+ received_proto: packet.smb_header.protocol,
64
+ received_cmd: packet.smb_header.command
65
+ )
65
66
  end
66
67
  packet
67
68
  end
@@ -147,14 +148,15 @@ module RubySMB
147
148
 
148
149
  # Takes the raw binary string and returns a {RubySMB::SMB1::Packet::SessionSetupResponse}
149
150
  def smb1_ntlmssp_final_packet(raw_response)
150
- begin
151
- packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
152
- rescue
153
- packet = RubySMB::SMB1::Packet::EmptyPacket.read(raw_response)
154
- end
151
+ packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
155
152
 
156
- unless packet.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
157
- raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb_header.command} and not #{RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP}"
153
+ unless packet.valid?
154
+ raise RubySMB::Error::InvalidPacket.new(
155
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
156
+ expected_cmd: RubySMB::SMB1::Packet::SessionSetupResponse::COMMAND,
157
+ received_proto: packet.smb_header.protocol,
158
+ received_cmd: packet.smb_header.command
159
+ )
158
160
  end
159
161
  packet
160
162
  end
@@ -162,15 +164,20 @@ module RubySMB
162
164
  # Takes the raw binary string and returns a {RubySMB::SMB1::Packet::SessionSetupResponse}
163
165
  def smb1_ntlmssp_challenge_packet(raw_response)
164
166
  packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
165
- status_code = packet.status_code
167
+ unless packet.valid?
168
+ raise RubySMB::Error::InvalidPacket.new(
169
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
170
+ expected_cmd: RubySMB::SMB1::Packet::SessionSetupResponse::COMMAND,
171
+ received_proto: packet.smb_header.protocol,
172
+ received_cmd: packet.smb_header.command
173
+ )
174
+ end
166
175
 
176
+ status_code = packet.status_code
167
177
  unless status_code.name == 'STATUS_MORE_PROCESSING_REQUIRED'
168
178
  raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
169
179
  end
170
180
 
171
- unless packet.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
172
- raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb_header.command} and not #{RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP}"
173
- end
174
181
  packet
175
182
  end
176
183
 
@@ -212,23 +219,34 @@ module RubySMB
212
219
  # Takes the raw binary string and returns a {RubySMB::SMB2::Packet::SessionSetupResponse}
213
220
  def smb2_ntlmssp_final_packet(raw_response)
214
221
  packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
215
- unless packet.smb2_header.command == RubySMB::SMB2::Commands::SESSION_SETUP
216
- raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb2_header.command} and not #{RubySMB::SMB2::Commands::SESSION_SETUP}"
222
+ unless packet.valid?
223
+ raise RubySMB::Error::InvalidPacket.new(
224
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
225
+ expected_cmd: RubySMB::SMB2::Packet::SessionSetupResponse::COMMAND,
226
+ received_proto: packet.smb2_header.protocol,
227
+ received_cmd: packet.smb2_header.command
228
+ )
217
229
  end
230
+
218
231
  packet
219
232
  end
220
233
 
221
234
  # Takes the raw binary string and returns a {RubySMB::SMB2::Packet::SessionSetupResponse}
222
235
  def smb2_ntlmssp_challenge_packet(raw_response)
223
236
  packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
237
+ unless packet.valid?
238
+ raise RubySMB::Error::InvalidPacket.new(
239
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
240
+ expected_cmd: RubySMB::SMB2::Packet::SessionSetupResponse::COMMAND,
241
+ received_proto: packet.smb2_header.protocol,
242
+ received_cmd: packet.smb2_header.command
243
+ )
244
+ end
245
+
224
246
  status_code = packet.status_code
225
247
  unless status_code.name == 'STATUS_MORE_PROCESSING_REQUIRED'
226
248
  raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
227
249
  end
228
-
229
- unless packet.smb2_header.command == RubySMB::SMB2::Commands::SESSION_SETUP
230
- raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb2_header.command} and not #{RubySMB::SMB2::Commands::SESSION_SETUP}"
231
- end
232
250
  packet
233
251
  end
234
252
 
@@ -16,7 +16,16 @@ module RubySMB
16
16
  (count - 1).times do
17
17
  raw_response = dispatcher.recv_packet
18
18
  end
19
- RubySMB::SMB1::Packet::EchoResponse.read(raw_response)
19
+ response = RubySMB::SMB1::Packet::EchoResponse.read(raw_response)
20
+ unless response.valid?
21
+ raise RubySMB::Error::InvalidPacket.new(
22
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
23
+ expected_cmd: RubySMB::SMB1::Packet::EchoResponse::COMMAND,
24
+ received_proto: response.smb_header.protocol,
25
+ received_cmd: response.smb_header.command
26
+ )
27
+ end
28
+ response
20
29
  end
21
30
 
22
31
  # Sends an ECHO request packet and returns the
@@ -26,7 +35,16 @@ module RubySMB
26
35
  def smb2_echo
27
36
  request = RubySMB::SMB2::Packet::EchoRequest.new
28
37
  raw_response = send_recv(request)
29
- RubySMB::SMB2::Packet::EchoResponse.read(raw_response)
38
+ response = RubySMB::SMB2::Packet::EchoResponse.read(raw_response)
39
+ unless response.valid?
40
+ raise RubySMB::Error::InvalidPacket.new(
41
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
42
+ expected_cmd: RubySMB::SMB2::Packet::EchoResponse::COMMAND,
43
+ received_proto: response.smb2_header.protocol,
44
+ received_cmd: response.smb2_header.command
45
+ )
46
+ end
47
+ response
30
48
  end
31
49
  end
32
50
  end
@@ -47,23 +47,38 @@ module RubySMB
47
47
  def negotiate_response(raw_data)
48
48
  response = nil
49
49
  if smb1
50
- begin
51
- packet = RubySMB::SMB1::Packet::NegotiateResponseExtended.read raw_data
52
- rescue StandardError => e
53
- raise RubySMB::Error::InvalidPacket, "Not a Valid SMB1 Negoitate Response #{e.message}"
54
- end
50
+ packet = RubySMB::SMB1::Packet::NegotiateResponseExtended.read raw_data
55
51
  response = packet if packet.valid?
56
52
  end
57
53
  if smb2 && response.nil?
58
- begin
59
- packet = RubySMB::SMB2::Packet::NegotiateResponse.read raw_data
60
- rescue StandardError => e
61
- raise RubySMB::Error::InvalidPacket, "Not a Valid SMB2 Negoitate Response #{e.message}"
62
- end
63
- response = packet
54
+ packet = RubySMB::SMB2::Packet::NegotiateResponse.read raw_data
55
+ response = packet if packet.valid?
64
56
  end
65
57
  if response.nil?
66
- raise RubySMB::Error::InvalidPacket, 'No Valid Negotiate Response found'
58
+ if packet.packet_smb_version == 'SMB1'
59
+ extended_security = if packet.is_a? RubySMB::SMB1::Packet::NegotiateResponseExtended
60
+ packet.parameter_block.capabilities.extended_security
61
+ else
62
+ "n/a"
63
+ end
64
+ raise RubySMB::Error::InvalidPacket.new(
65
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
66
+ expected_cmd: RubySMB::SMB1::Packet::NegotiateResponseExtended::COMMAND,
67
+ expected_custom: "extended_security=1",
68
+ received_proto: packet.smb_header.protocol,
69
+ received_cmd: packet.smb_header.command,
70
+ received_custom: "extended_security=#{extended_security}"
71
+ )
72
+ elsif packet.packet_smb_version == 'SMB2'
73
+ raise RubySMB::Error::InvalidPacket.new(
74
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
75
+ expected_cmd: RubySMB::SMB2::Packet::NegotiateResponse::COMMAND,
76
+ received_proto: packet.smb2_header.protocol,
77
+ received_cmd: packet.smb2_header.command
78
+ )
79
+ else
80
+ raise RubySMB::Error::InvalidPacket, 'Unknown SMB protocol version'
81
+ end
67
82
  end
68
83
  response
69
84
  end
@@ -17,11 +17,7 @@ module RubySMB
17
17
  request.smb_header.tid = 65_535
18
18
  request.data_block.path = share
19
19
  raw_response = send_recv(request)
20
- begin
21
- response = RubySMB::SMB1::Packet::TreeConnectResponse.read(raw_response)
22
- rescue EOFError
23
- response = RubySMB::SMB1::Packet::EmptyPacket.read(raw_response)
24
- end
20
+ response = RubySMB::SMB1::Packet::TreeConnectResponse.read(raw_response)
25
21
  smb1_tree_from_response(share, response)
26
22
  end
27
23
 
@@ -30,9 +26,16 @@ module RubySMB
30
26
  # @param share [String] the share path to connect to
31
27
  # @param response [RubySMB::SMB1::Packet::TreeConnectResponse] the response packet to parse into our Tree
32
28
  # @return [RubySMB::SMB1::Tree]
29
+ # @raise [RubySMB::Error::InvalidPacket] if the response command is not a TreeConnectResponse packet
30
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response status code is not STATUS_SUCCESS
33
31
  def smb1_tree_from_response(share, response)
34
- unless response.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_TREE_CONNECT
35
- raise RubySMB::Error::InvalidPacket, 'Not a TreeConnectResponse'
32
+ unless response.valid?
33
+ raise RubySMB::Error::InvalidPacket.new(
34
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
35
+ expected_cmd: RubySMB::SMB1::Packet::TreeConnectResponse::COMMAND,
36
+ received_proto: response.smb_header.protocol,
37
+ received_cmd: response.smb_header.command
38
+ )
36
39
  end
37
40
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
38
41
  raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
@@ -54,11 +57,7 @@ module RubySMB
54
57
  request.smb2_header.tree_id = 65_535
55
58
  request.encode_path(share)
56
59
  raw_response = send_recv(request)
57
- begin
58
- response = RubySMB::SMB2::Packet::TreeConnectResponse.read(raw_response)
59
- rescue EOFError
60
- response = RubySMB::SMB2::Packet::ErrorPacket.read(raw_response)
61
- end
60
+ response = RubySMB::SMB2::Packet::TreeConnectResponse.read(raw_response)
62
61
  smb2_tree_from_response(share, response)
63
62
  end
64
63
 
@@ -67,9 +66,16 @@ module RubySMB
67
66
  # @param share [String] the share path to connect to
68
67
  # @param response [RubySMB::SMB2::Packet::TreeConnectResponse] the response packet to parse into our Tree
69
68
  # @return [RubySMB::SMB2::Tree]
69
+ # @raise [RubySMB::Error::InvalidPacket] if the response command is not a TreeConnectResponse packet
70
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response status code is not STATUS_SUCCESS
70
71
  def smb2_tree_from_response(share, response)
71
- unless response.smb2_header.command == RubySMB::SMB2::Commands::TREE_CONNECT
72
- raise RubySMB::Error::InvalidPacket, 'Not a TreeConnectResponse'
72
+ unless response.valid?
73
+ raise RubySMB::Error::InvalidPacket.new(
74
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
75
+ expected_cmd: RubySMB::SMB2::Packet::TreeConnectResponse::COMMAND,
76
+ received_proto: response.smb2_header.protocol,
77
+ received_cmd: response.smb2_header.command
78
+ )
73
79
  end
74
80
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
75
81
  raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
@@ -25,7 +25,7 @@ module RubySMB
25
25
  end
26
26
 
27
27
  def open(path, disposition=RubySMB::Dispositions::FILE_OPEN, write: false, read: true)
28
- file = last_tree.open_file(filename: path.gsub(/\\/, ''), write: write, read: read, disposition: disposition)
28
+ file = last_tree.open_file(filename: path.sub(/^\\/, ''), write: write, read: read, disposition: disposition)
29
29
  @last_file_id = if file.respond_to?(:guid)
30
30
  file.guid.to_binary_s
31
31
  elsif file.respond_to?(:fid)
@@ -50,7 +50,7 @@ module RubySMB
50
50
  end
51
51
 
52
52
  def delete(path)
53
- file = last_tree.open_file(filename: path.gsub(/\\/, ''), delete: true)
53
+ file = last_tree.open_file(filename: path.sub(/^\\/, ''), delete: true)
54
54
  file.delete
55
55
  file.close
56
56
  end