ruby_smb 1.0.4 → 2.0.2

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 (130) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/enum_registry_key.rb +28 -0
  8. data/examples/enum_registry_values.rb +30 -0
  9. data/examples/negotiate.rb +51 -8
  10. data/examples/pipes.rb +2 -1
  11. data/examples/read_file_encryption.rb +56 -0
  12. data/examples/read_registry_key_value.rb +32 -0
  13. data/lib/ruby_smb.rb +4 -1
  14. data/lib/ruby_smb/client.rb +207 -18
  15. data/lib/ruby_smb/client/authentication.rb +27 -8
  16. data/lib/ruby_smb/client/encryption.rb +62 -0
  17. data/lib/ruby_smb/client/negotiation.rb +153 -12
  18. data/lib/ruby_smb/client/signing.rb +19 -0
  19. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  20. data/lib/ruby_smb/client/utils.rb +8 -7
  21. data/lib/ruby_smb/client/winreg.rb +46 -0
  22. data/lib/ruby_smb/crypto.rb +30 -0
  23. data/lib/ruby_smb/dcerpc.rb +38 -0
  24. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  25. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  26. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  27. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  28. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  29. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  30. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  31. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  33. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  34. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  35. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  36. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  37. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  38. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  39. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  40. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  41. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  42. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  43. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  44. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  45. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  46. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  47. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  48. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  49. data/lib/ruby_smb/dispatcher/socket.rb +4 -3
  50. data/lib/ruby_smb/error.rb +28 -1
  51. data/lib/ruby_smb/smb1/commands.rb +1 -1
  52. data/lib/ruby_smb/smb1/file.rb +6 -4
  53. data/lib/ruby_smb/smb1/packet/empty_packet.rb +4 -2
  54. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  55. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  56. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  57. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  58. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  59. data/lib/ruby_smb/smb1/pipe.rb +79 -3
  60. data/lib/ruby_smb/smb1/tree.rb +12 -3
  61. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  62. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  63. data/lib/ruby_smb/smb2/file.rb +25 -43
  64. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  65. data/lib/ruby_smb/smb2/packet.rb +2 -0
  66. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  67. data/lib/ruby_smb/smb2/packet/error_packet.rb +9 -4
  68. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  69. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  70. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  71. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  72. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  73. data/lib/ruby_smb/smb2/pipe.rb +77 -3
  74. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  75. data/lib/ruby_smb/smb2/tree.rb +23 -17
  76. data/lib/ruby_smb/version.rb +1 -1
  77. data/ruby_smb.gemspec +5 -3
  78. data/spec/lib/ruby_smb/client_spec.rb +1441 -61
  79. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  80. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  81. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  82. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  83. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  84. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  85. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  86. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  87. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  88. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  89. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  90. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  91. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  92. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  93. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  94. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  95. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  96. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  97. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  98. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  99. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  100. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  101. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  102. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  103. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  104. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
  105. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  106. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  107. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +10 -0
  108. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  109. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  110. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  111. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  112. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +210 -148
  113. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  114. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  115. data/spec/lib/ruby_smb/smb2/file_spec.rb +86 -62
  116. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  117. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  118. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +29 -2
  119. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  120. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  121. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  122. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  123. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  124. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +220 -149
  125. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  126. data/spec/lib/ruby_smb/smb2/tree_spec.rb +53 -8
  127. metadata +187 -81
  128. metadata.gz.sig +0 -0
  129. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  130. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -1,15 +1,53 @@
1
1
  module RubySMB
2
2
  module Dcerpc
3
+ MAX_XMIT_FRAG = 4280
4
+ MAX_RECV_FRAG = 4280
5
+
6
+ require 'windows_error/win32'
3
7
  require 'ruby_smb/dcerpc/error'
4
8
  require 'ruby_smb/dcerpc/uuid'
5
9
  require 'ruby_smb/dcerpc/ndr'
6
10
  require 'ruby_smb/dcerpc/ptypes'
7
11
  require 'ruby_smb/dcerpc/p_syntax_id_t'
12
+ require 'ruby_smb/dcerpc/rrp_unicode_string'
8
13
  require 'ruby_smb/dcerpc/pdu_header'
9
14
  require 'ruby_smb/dcerpc/srvsvc'
15
+ require 'ruby_smb/dcerpc/winreg'
10
16
  require 'ruby_smb/dcerpc/request'
11
17
  require 'ruby_smb/dcerpc/response'
12
18
  require 'ruby_smb/dcerpc/bind'
13
19
  require 'ruby_smb/dcerpc/bind_ack'
20
+
21
+
22
+ # Bind to the remote server interface endpoint.
23
+ #
24
+ # @param options [Hash] the options to pass to the Bind request packet. At least, :endpoint must but provided with an existing Dcerpc class
25
+ # @return [RubySMB::Dcerpc::BindAck] the BindAck response packet
26
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if an invalid packet is received
27
+ # @raise [RubySMB::Dcerpc::Error::BindError] if the response is not a BindAck packet or if the Bind result code is not ACCEPTANCE
28
+ def bind(options={})
29
+ bind_req = RubySMB::Dcerpc::Bind.new(options)
30
+ write(data: bind_req.to_binary_s)
31
+ @size = 1024
32
+ dcerpc_raw_response = read()
33
+ begin
34
+ dcerpc_response = RubySMB::Dcerpc::BindAck.read(dcerpc_raw_response)
35
+ rescue IOError
36
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
37
+ end
38
+ unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::BIND_ACK
39
+ raise RubySMB::Dcerpc::Error::BindError, "Not a BindAck packet"
40
+ end
41
+
42
+ res_list = dcerpc_response.p_result_list
43
+ if res_list.n_results == 0 ||
44
+ res_list.p_results[0].result != RubySMB::Dcerpc::BindAck::ACCEPTANCE
45
+ raise RubySMB::Dcerpc::Error::BindError,
46
+ "Bind Failed (Result: #{res_list.p_results[0].result}, Reason: #{res_list.p_results[0].reason})"
47
+ end
48
+ @tree.client.max_buffer_size = dcerpc_response.max_xmit_frag
49
+ dcerpc_response
50
+ end
51
+
14
52
  end
15
53
  end
@@ -35,8 +35,8 @@ module RubySMB
35
35
 
36
36
  pdu_header :pdu_header, label: 'PDU header'
37
37
 
38
- uint16 :max_xmit_frag, label: 'max transmit frag size', initial_value: 0xFFFF
39
- uint16 :max_recv_frag, label: 'max receive frag size', initial_value: 0xFFFF
38
+ uint16 :max_xmit_frag, label: 'max transmit frag size', initial_value: RubySMB::Dcerpc::MAX_XMIT_FRAG
39
+ uint16 :max_recv_frag, label: 'max receive frag size', initial_value: RubySMB::Dcerpc::MAX_RECV_FRAG
40
40
  uint32 :assoc_group_id, label: 'ncarnation of client-server assoc group'
41
41
 
42
42
  p_cont_list_t :p_context_list, label: 'Presentation context list', endpoint: -> { endpoint }
@@ -46,8 +46,8 @@ module RubySMB
46
46
 
47
47
  pdu_header :pdu_header, label: 'PDU header'
48
48
 
49
- uint16 :max_xmit_frag, label: 'Max transmit frag size', initial_value: 0xFFFF
50
- uint16 :max_recv_frag, label: 'Max receive frag size', initial_value: 0xFFFF
49
+ uint16 :max_xmit_frag, label: 'Max transmit frag size', initial_value: RubySMB::Dcerpc::MAX_XMIT_FRAG
50
+ uint16 :max_recv_frag, label: 'Max receive frag size', initial_value: RubySMB::Dcerpc::MAX_RECV_FRAG
51
51
  uint32 :assoc_group_id, label: 'Association group ID'
52
52
  port_any_t :sec_addr, label: 'Secondary address'
53
53
  string :pad, length: -> { pad_length }
@@ -10,6 +10,9 @@ module RubySMB
10
10
 
11
11
  # Raised when an invalid packet is received
12
12
  class InvalidPacket < DcerpcError; end
13
+
14
+ # Raised when an error is returned during a Winreg operation
15
+ class WinregError < DcerpcError; end
13
16
  end
14
17
  end
15
18
  end
@@ -7,36 +7,115 @@ module RubySMB
7
7
  VER_MAJOR = 2
8
8
  VER_MINOR = 0
9
9
 
10
- class NdrString < BinData::Record
10
+ # An NDR Top-level Full Pointers representation as defined in
11
+ # [Transfer Syntax NDR - Top-level Full Pointers](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_11_01)
12
+ # This class must be inherited and the subclass must have a #referent protperty
13
+ class NdrTopLevelFullPointer < BinData::Primitive
11
14
  endian :little
12
15
 
13
- uint32 :max_count, initial_value: -> { str.length }
14
- uint32 :offset, initial_value: 0
15
- uint32 :actual_count, initial_value: -> { str.length }
16
- stringz16 :str, read_length: -> { actual_count }
16
+ uint32 :referent_identifier, initial_value: 0x00020000
17
17
 
18
- def assign(v)
19
- self.max_count = v.size
20
- self.actual_count = v.size
21
- self.str = v
18
+ def get
19
+ is_a_null_pointer? ? 0 : self.referent
20
+ end
21
+
22
+ def set(v)
23
+ if v.is_a?(Integer) && v == 0
24
+ self.referent_identifier = 0
25
+ else
26
+ self.referent = v
27
+ end
28
+ end
29
+
30
+ def is_a_null_pointer?
31
+ self.referent_identifier == 0
22
32
  end
23
33
  end
24
34
 
25
- class NdrLpStr < BinData::Record
35
+ # An NDR Conformant and Varying String representation as defined in
36
+ # [Transfer Syntax NDR - Conformant and Varying Strings](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02)
37
+ # The string elements are Stringz16 (unicode)
38
+ class NdrString < BinData::Primitive
26
39
  endian :little
27
40
 
28
- uint32 :referent_identifier
29
- ndr_string :ndr_str
41
+ uint32 :max_count
42
+ uint32 :offset, initial_value: 0
43
+ uint32 :actual_count
44
+ stringz16 :str, read_length: -> { actual_count }, onlyif: -> { actual_count > 0 }
30
45
 
31
- def assign(v)
32
- self.ndr_str = v
46
+ def get
47
+ self.actual_count == 0 ? 0 : self.str
33
48
  end
34
49
 
50
+ def set(v)
51
+ if v.is_a?(Integer) && v == 0
52
+ self.actual_count = 0
53
+ else
54
+ self.str = v
55
+ self.max_count = self.actual_count = str.to_binary_s.size / 2
56
+ end
57
+ end
58
+ end
59
+
60
+ # A pointer to a NdrString structure
61
+ class NdrLpStr < NdrTopLevelFullPointer
62
+ endian :little
63
+
64
+ ndr_string :referent, onlyif: -> { !is_a_null_pointer? }
65
+
35
66
  def to_s
36
- self.ndr_str.str
67
+ is_a_null_pointer? ? "\0" : self.referent
68
+ end
69
+ end
70
+
71
+ # An NDR Context Handle representation as defined in
72
+ # [IDL Data Type Declarations - Basic Type Declarations](http://pubs.opengroup.org/onlinepubs/9629399/apdxn.htm#tagcjh_34_01)
73
+ class NdrContextHandle < BinData::Primitive
74
+ endian :little
75
+ uint32 :context_handle_attributes
76
+ uuid :context_handle_uuid
77
+
78
+ def get
79
+ {:context_handle_attributes => context_handle_attributes, :context_handle_uuid => context_handle_uuid}
37
80
  end
81
+
82
+ def set(handle)
83
+ if handle.is_a?(Hash)
84
+ self.context_handle_attributes = handle[:context_handle_attributes]
85
+ self.context_handle_uuid = handle[:context_handle_uuid]
86
+ elsif handle.is_a?(NdrContextHandle)
87
+ read(handle.to_binary_s)
88
+ else
89
+ read(handle.to_s)
90
+ end
91
+ end
92
+ end
93
+
94
+ # A pointer to a DWORD
95
+ class NdrLpDword < NdrTopLevelFullPointer
96
+ endian :little
97
+
98
+ uint32 :referent, onlyif: -> { !is_a_null_pointer? }
99
+ end
100
+
101
+ # An NDR Uni-dimensional Conformant-varying Arrays representation as defined in:
102
+ # [Transfer Syntax NDR - NDR Constructed Types](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_04)
103
+ class NdrLpByte < BinData::Record
104
+ endian :little
105
+
106
+ uint32 :referent_identifier, initial_value: 0x00020000
107
+ uint32 :max_count, initial_value: -> { actual_count }, onlyif: -> { referent_identifier != 0 }
108
+ uint32 :offset, initial_value: 0, onlyif: -> { referent_identifier != 0 }
109
+ uint32 :actual_count, initial_value: -> { bytes.size }, onlyif: -> { referent_identifier != 0 }
110
+ array :bytes, :type => :uint8, initial_length: -> { actual_count }, onlyif: -> { referent_identifier != 0 }
111
+ end
112
+
113
+ # A pointer to a Windows FILETIME structure
114
+ class NdrLpFileTime < NdrTopLevelFullPointer
115
+ endian :little
116
+
117
+ file_time :referent, onlyif: -> { !is_a_null_pointer? }
38
118
  end
39
119
  end
40
120
  end
41
-
42
121
  end
@@ -21,7 +21,7 @@ module RubySMB
21
21
  end
22
22
 
23
23
  uint32 :packed_drep, label: 'NDR data representation format label', initial_value: 0x10
24
- uint16 :frag_length, label: 'Total length of fragment', initial_value: -> { parent.do_num_bytes }
24
+ uint16 :frag_length, label: 'Total length of fragment', initial_value: -> { parent.num_bytes }
25
25
  uint16 :auth_length, label: 'Length of auth_value'
26
26
  uint32 :call_id, label: 'Call identifier', initial_value: 1
27
27
  end
@@ -6,19 +6,38 @@ module RubySMB
6
6
  endian :little
7
7
 
8
8
  pdu_header :pdu_header, label: 'PDU header'
9
+ uint32 :alloc_hint, label: 'Allocation hint', initial_value: -> { stub.num_bytes }
10
+ uint16 :p_cont_id, label: 'Presentation context identification'
11
+ uint16 :opnum, label: 'Operation Number'
12
+ uuid :object, label: 'Object UID', onlyif: -> { pdu_header.pfc_flags.object_uuid == 1 }
9
13
 
10
- uint32 :alloc_hint, label: 'Allocation hint', initial_value: -> { stub.do_num_bytes }
11
- uint16 :p_cont_id, label: 'Presentation context identification'
12
- uint16 :opnum, label: 'Operation Number'
13
-
14
- uuid :object, label: 'Object UID', onlyif: -> { pdu_header.pfc_flags.object_uuid == 1 }
15
-
16
- choice :stub, label: 'Stub', selection: -> { opnum } do
17
- net_share_enum_all RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL, host: -> { host }
14
+ choice :stub, label: 'Stub', selection: -> { @obj.parent.get_parameter(:endpoint) || '' } do
15
+ choice 'Winreg', selection: -> { opnum } do
16
+ open_root_key_request RubySMB::Dcerpc::Winreg::OPEN_HKCR, opnum: RubySMB::Dcerpc::Winreg::OPEN_HKCR
17
+ open_root_key_request RubySMB::Dcerpc::Winreg::OPEN_HKCU, opnum: RubySMB::Dcerpc::Winreg::OPEN_HKCU
18
+ open_root_key_request RubySMB::Dcerpc::Winreg::OPEN_HKLM, opnum: RubySMB::Dcerpc::Winreg::OPEN_HKLM
19
+ open_root_key_request RubySMB::Dcerpc::Winreg::OPEN_HKPD, opnum: RubySMB::Dcerpc::Winreg::OPEN_HKPD
20
+ open_root_key_request RubySMB::Dcerpc::Winreg::OPEN_HKU, opnum: RubySMB::Dcerpc::Winreg::OPEN_HKU
21
+ open_root_key_request RubySMB::Dcerpc::Winreg::OPEN_HKCC, opnum: RubySMB::Dcerpc::Winreg::OPEN_HKCC
22
+ open_root_key_request RubySMB::Dcerpc::Winreg::OPEN_HKPT, opnum: RubySMB::Dcerpc::Winreg::OPEN_HKPT
23
+ open_root_key_request RubySMB::Dcerpc::Winreg::OPEN_HKPN, opnum: RubySMB::Dcerpc::Winreg::OPEN_HKPN
24
+ close_key_request RubySMB::Dcerpc::Winreg::REG_CLOSE_KEY
25
+ enum_key_request RubySMB::Dcerpc::Winreg::REG_ENUM_KEY
26
+ enum_value_request RubySMB::Dcerpc::Winreg::REG_ENUM_VALUE
27
+ open_key_request RubySMB::Dcerpc::Winreg::REG_OPEN_KEY
28
+ query_info_key_request RubySMB::Dcerpc::Winreg::REG_QUERY_INFO_KEY
29
+ query_value_request RubySMB::Dcerpc::Winreg::REG_QUERY_VALUE
30
+ string :default
31
+ end
32
+ choice 'Srvsvc', selection: -> { opnum } do
33
+ net_share_enum_all RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL, host: -> { host rescue '' }
34
+ string :default
35
+ end
36
+ string :default
18
37
  end
19
38
 
20
39
  string :auth_verifier, label: 'Authentication verifier',
21
- onlyif: -> { pdu_header.auth_length > 0 },
40
+ onlyif: -> { pdu_header.auth_length > 0 },
22
41
  read_length: -> { pdu_header.auth_length }
23
42
 
24
43
  def initialize_instance
@@ -0,0 +1,35 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+
6
+ # A RRP_UNICODE_STRING structure as defined in
7
+ # [2.2.4 RRP_UNICODE_STRING](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/c0c90f11-a4c4-496a-ac09-8a8a3697ceef)
8
+ class RrpUnicodeString < BinData::Primitive
9
+ endian :little
10
+
11
+ uint16 :buffer_length, initial_value: -> { buffer.to_s == "\0" ? 0 : buffer.actual_count * 2 }
12
+ uint16 :maximum_length, initial_value: -> { buffer.to_s == "\0" ? 0 : buffer.max_count * 2 }
13
+ ndr_lp_str :buffer
14
+
15
+ def get
16
+ self.buffer
17
+ end
18
+
19
+ def set(buf)
20
+ self.buffer = buf
21
+ self.buffer_length = self.buffer.to_s == "\0" ? 0 : self.buffer.actual_count * 2
22
+ self.maximum_length = self.buffer.to_s == "\0" ? 0 : self.buffer.max_count * 2
23
+ end
24
+ end
25
+
26
+ # A pointer to a RRP_UNICODE_STRING structure
27
+ class PrrpUnicodeString < Ndr::NdrTopLevelFullPointer
28
+ endian :little
29
+
30
+ rrp_unicode_string :referent, onlyif: -> { !is_a_null_pointer? }
31
+ end
32
+
33
+ end
34
+ end
35
+
@@ -10,6 +10,16 @@ module RubySMB
10
10
  NET_SHARE_ENUM_ALL = 0xF
11
11
 
12
12
  require 'ruby_smb/dcerpc/srvsvc/net_share_enum_all'
13
+
14
+ def net_share_enum_all(host)
15
+ bind(endpoint: RubySMB::Dcerpc::Srvsvc)
16
+
17
+ net_share_enum_all_request_packet = RubySMB::Dcerpc::Srvsvc::NetShareEnumAll.new(host: host)
18
+ response = dcerpc_request(net_share_enum_all_request_packet)
19
+
20
+ shares = RubySMB::Dcerpc::Srvsvc::NetShareEnumAll.parse_response(response)
21
+ shares.map{|s|{name: s[0], type: s[1], comment: s[2]}}
22
+ end
13
23
  end
14
24
  end
15
25
  end
@@ -5,6 +5,10 @@ module RubySMB
5
5
  #https://msdn.microsoft.com/en-us/library/cc247293.aspx
6
6
 
7
7
  class NetShareEnumAll < BinData::Record
8
+ attr_reader :opnum
9
+
10
+ mandatory_parameter :host
11
+
8
12
  endian :little
9
13
 
10
14
  uint32 :referent_id, initial_value: 0x00000001
@@ -27,6 +31,11 @@ module RubySMB
27
31
  uint32 :resume_referent_id, initial_value: 0x00000001
28
32
  uint32 :resume_handle, initial_value: 0
29
33
 
34
+ def initialize_instance
35
+ super
36
+ @opnum = NET_SHARE_ENUM_ALL
37
+ end
38
+
30
39
  def pad_length
31
40
  offset = (server_unc.abs_offset + server_unc.to_binary_s.length) % 4
32
41
  (4 - offset) % 4
@@ -0,0 +1,340 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Winreg
4
+
5
+ UUID = '338CD001-2244-31F1-AAAA-900038001003'
6
+ VER_MAJOR = 1
7
+ VER_MINOR = 0
8
+
9
+ # Operation numbers
10
+ OPEN_HKCR = 0x00
11
+ OPEN_HKCU = 0x01
12
+ OPEN_HKLM = 0x02
13
+ OPEN_HKPD = 0x03
14
+ OPEN_HKU = 0x04
15
+ REG_CLOSE_KEY = 0x05
16
+ REG_ENUM_KEY = 0x09
17
+ REG_ENUM_VALUE = 0x0a
18
+ REG_OPEN_KEY = 0x0f
19
+ REG_QUERY_INFO_KEY = 0x10
20
+ REG_QUERY_VALUE = 0x11
21
+ OPEN_HKCC = 0x1b
22
+ OPEN_HKPT = 0x20
23
+ OPEN_HKPN = 0x21
24
+
25
+ require 'ruby_smb/dcerpc/winreg/regsam'
26
+ require 'ruby_smb/dcerpc/winreg/open_root_key_request'
27
+ require 'ruby_smb/dcerpc/winreg/open_root_key_response'
28
+ require 'ruby_smb/dcerpc/winreg/close_key_request'
29
+ require 'ruby_smb/dcerpc/winreg/close_key_response'
30
+ require 'ruby_smb/dcerpc/winreg/enum_key_request'
31
+ require 'ruby_smb/dcerpc/winreg/enum_key_response'
32
+ require 'ruby_smb/dcerpc/winreg/enum_value_request'
33
+ require 'ruby_smb/dcerpc/winreg/enum_value_response'
34
+ require 'ruby_smb/dcerpc/winreg/open_key_request'
35
+ require 'ruby_smb/dcerpc/winreg/open_key_response'
36
+ require 'ruby_smb/dcerpc/winreg/query_info_key_request'
37
+ require 'ruby_smb/dcerpc/winreg/query_info_key_response'
38
+ require 'ruby_smb/dcerpc/winreg/query_value_request'
39
+ require 'ruby_smb/dcerpc/winreg/query_value_response'
40
+
41
+ ROOT_KEY_MAP = {
42
+ "HKEY_CLASSES_ROOT" => OPEN_HKCR,
43
+ "HKCR" => OPEN_HKCR,
44
+ "HKEY_CURRENT_USER" => OPEN_HKCU,
45
+ "HKCU" => OPEN_HKCU,
46
+ "HKEY_LOCAL_MACHINE" => OPEN_HKLM,
47
+ "HKLM" => OPEN_HKLM,
48
+ "HKEY_PERFORMANCE_DATA" => OPEN_HKPD,
49
+ "HKPD" => OPEN_HKPD,
50
+ "HKEY_USERS" => OPEN_HKU,
51
+ "HKU" => OPEN_HKU,
52
+ "HKEY_CURRENT_CONFIG" => OPEN_HKCC,
53
+ "HKCC" => OPEN_HKCC,
54
+ "HKEY_PERFORMANCE_TEXT" => OPEN_HKPT,
55
+ "HKPT" => OPEN_HKPT,
56
+ "HKEY_PERFORMANCE_NLS_TEXT" => OPEN_HKPN,
57
+ "HKPN" => OPEN_HKPN
58
+ }
59
+
60
+ # Open the registry root key and return a handle for it. The key can be
61
+ # either a long format (e.g. HKEY_LOCAL_MACHINE) or a short format
62
+ # (e.g. HKLM)
63
+ #
64
+ # @param root_key [String] the root key to open
65
+ # @return [Ndr::NdrContextHandle] the RPC context handle for the root key
66
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a OpenRootKeyResponse packet
67
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
68
+ def open_root_key(root_key)
69
+ root_key_opnum = RubySMB::Dcerpc::Winreg::ROOT_KEY_MAP[root_key]
70
+ raise ArgumentError, "Unknown Root Key: #{root_key}" unless root_key_opnum
71
+
72
+ root_key_request_packet = OpenRootKeyRequest.new(opnum: root_key_opnum)
73
+ response = dcerpc_request(root_key_request_packet)
74
+
75
+ begin
76
+ root_key_response_packet = OpenRootKeyResponse.read(response)
77
+ rescue IOError
78
+ raise RubySMB::Dcerpc::Error::InvalidPacket,
79
+ "Error reading OpenRootKeyResponse (command = #{root_key_opnum})"
80
+ end
81
+ unless root_key_response_packet.error_status == WindowsError::Win32::ERROR_SUCCESS
82
+ raise RubySMB::Dcerpc::Error::WinregError,
83
+ "Error returned when opening root key #{root_key}: "\
84
+ "#{WindowsError::Win32.find_by_retval(root_key_response_packet.error_status.value).join(',')}"
85
+ end
86
+
87
+ root_key_response_packet.ph_key
88
+ end
89
+
90
+ # Open the registry key specified by a root key handle (previously open
91
+ # with #open_root_key) and a subkey. It returns a handle for the key.
92
+ #
93
+ # @param handle [Ndr::NdrContextHandle] the handle for the root key
94
+ # @param sub_key [String] the subkey to open
95
+ # @return [Ndr::NdrContextHandle] the RPC context handle for the key
96
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a OpenKeyResponse packet
97
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
98
+ def open_key(handle, sub_key)
99
+ openkey_request_packet = RubySMB::Dcerpc::Winreg::OpenKeyRequest.new(hkey: handle, lp_sub_key: sub_key)
100
+ openkey_request_packet.sam_desired.read_control = 1
101
+ openkey_request_packet.sam_desired.key_query_value = 1
102
+ openkey_request_packet.sam_desired.key_enumerate_sub_keys = 1
103
+ openkey_request_packet.sam_desired.key_notify = 1
104
+ response = dcerpc_request(openkey_request_packet)
105
+ begin
106
+ open_key_response = RubySMB::Dcerpc::Winreg::OpenKeyResponse.read(response)
107
+ rescue IOError
108
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the OpenKey response"
109
+ end
110
+ unless open_key_response.error_status == WindowsError::Win32::ERROR_SUCCESS
111
+ raise RubySMB::Dcerpc::Error::WinregError, "Error returned when opening subkey #{sub_key}: "\
112
+ "#{WindowsError::Win32.find_by_retval(open_key_response.error_status.value).join(',')}"
113
+ end
114
+
115
+ open_key_response.phk_result
116
+ end
117
+
118
+ # Retrieve the data associated with the named value of a specified
119
+ # registry open key.
120
+ #
121
+ # @param handle [Ndr::NdrContextHandle] the handle for the key
122
+ # @param value_name [String] the name of the value
123
+ # @return [String] the data of the value entry
124
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a QueryValueResponse packet
125
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
126
+ def query_value(handle, value_name)
127
+ query_value_request_packet = RubySMB::Dcerpc::Winreg::QueryValueRequest.new(hkey: handle, lp_value_name: value_name)
128
+ query_value_request_packet.lp_data.referent_identifier = 0
129
+ response = dcerpc_request(query_value_request_packet)
130
+ begin
131
+ query_value_response = RubySMB::Dcerpc::Winreg::QueryValueResponse.read(response)
132
+ rescue IOError
133
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the QueryValue response"
134
+ end
135
+ unless query_value_response.error_status == WindowsError::Win32::ERROR_SUCCESS
136
+ raise RubySMB::Dcerpc::Error::WinregError, "Error returned when reading value #{value_name}: "\
137
+ "#{WindowsError::Win32.find_by_retval(query_value_response.error_status.value).join(',')}"
138
+ end
139
+
140
+ query_value_request_packet = RubySMB::Dcerpc::Winreg::QueryValueRequest.new(hkey: handle, lp_value_name: value_name)
141
+ query_value_request_packet.lpcb_data = query_value_response.lpcb_data
142
+ query_value_request_packet.lp_data.max_count = query_value_response.lpcb_data.referent
143
+ response = dcerpc_request(query_value_request_packet)
144
+ begin
145
+ query_value_response = RubySMB::Dcerpc::Winreg::QueryValueResponse.read(response)
146
+ rescue IOError
147
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the QueryValue response"
148
+ end
149
+ unless query_value_response.error_status == WindowsError::Win32::ERROR_SUCCESS
150
+ raise RubySMB::Dcerpc::Error::WinregError, "Error returned when reading value #{value_name}: "\
151
+ "#{WindowsError::Win32.find_by_retval(query_value_response.error_status.value).join(',')}"
152
+ end
153
+
154
+ query_value_response.data
155
+ end
156
+
157
+ # Close the handle to the registry key.
158
+ #
159
+ # @param handle [Ndr::NdrContextHandle] the handle for the key
160
+ # @return [WindowsError::Win32] the response error status
161
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a CloseKeyResponse packet
162
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
163
+ def close_key(handle)
164
+ close_key_request_packet = RubySMB::Dcerpc::Winreg::CloseKeyRequest.new(hkey: handle)
165
+ response = dcerpc_request(close_key_request_packet)
166
+ begin
167
+ close_key_response = RubySMB::Dcerpc::Winreg::CloseKeyResponse.read(response)
168
+ rescue IOError
169
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the CloseKey response"
170
+ end
171
+ unless close_key_response.error_status == WindowsError::Win32::ERROR_SUCCESS
172
+ raise RubySMB::Dcerpc::Error::WinregError, "Error returned when closing the key: "\
173
+ "#{WindowsError::Win32.find_by_retval(close_key_response.error_status.value).join(',')}"
174
+ end
175
+
176
+ close_key_response.error_status
177
+ end
178
+
179
+ # Retrive relevant information on the key that corresponds to the
180
+ # specified key handle.
181
+ #
182
+ # @param handle [Ndr::NdrContextHandle] the handle for the key
183
+ # @return [RubySMB::Dcerpc::Winreg::QueryInfoKeyResponse] the QueryInfoKeyResponse packet
184
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a QueryInfoKeyResponse packet
185
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
186
+ def query_info_key(handle)
187
+ query_info_key_request_packet = RubySMB::Dcerpc::Winreg::QueryInfoKeyRequest.new(hkey: handle)
188
+ response = dcerpc_request(query_info_key_request_packet)
189
+ begin
190
+ query_info_key_response = RubySMB::Dcerpc::Winreg::QueryInfoKeyResponse.read(response)
191
+ rescue IOError
192
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the query_infoKey response"
193
+ end
194
+ unless query_info_key_response.error_status == WindowsError::Win32::ERROR_SUCCESS
195
+ raise RubySMB::Dcerpc::Error::WinregError, "Error returned when querying information: "\
196
+ "#{WindowsError::Win32.find_by_retval(query_info_key_response.error_status.value).join(',')}"
197
+ end
198
+
199
+ query_info_key_response
200
+ end
201
+
202
+ # Enumerate the subkey at the specified index.
203
+ #
204
+ # @param handle [Ndr::NdrContextHandle] the handle for the key
205
+ # @param index [Numeric] the index of the subkey
206
+ # @return [String] the subkey name
207
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a EnumKeyResponse packet
208
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
209
+ def enum_key(handle, index)
210
+ enum_key_request_packet = RubySMB::Dcerpc::Winreg::EnumKeyRequest.new(hkey: handle, dw_index: index)
211
+ enum_key_request_packet.lpft_last_write_time = 0
212
+ enum_key_request_packet.lp_class.referent.buffer = 0
213
+ enum_key_request_packet.lp_name.buffer.referent.max_count = 256
214
+ response = dcerpc_request(enum_key_request_packet)
215
+ begin
216
+ enum_key_response = RubySMB::Dcerpc::Winreg::EnumKeyResponse.read(response)
217
+ rescue IOError
218
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the EnumKey response"
219
+ end
220
+ unless enum_key_response.error_status == WindowsError::Win32::ERROR_SUCCESS
221
+ raise RubySMB::Dcerpc::Error::WinregError, "Error returned when enumerating the key: "\
222
+ "#{WindowsError::Win32.find_by_retval(enum_key_response.error_status.value).join(',')}"
223
+ end
224
+
225
+ enum_key_response.lp_name.to_s
226
+ end
227
+
228
+ # Enumerate the value at the specified index for the specified registry key.
229
+ #
230
+ # @param handle [Ndr::NdrContextHandle] the handle for the key
231
+ # @param index [Numeric] the index of the subkey
232
+ # @return [String] the data of the value entry
233
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a EnumValueResponse packet
234
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
235
+ def enum_value(handle, index)
236
+ enum_value_request_packet = RubySMB::Dcerpc::Winreg::EnumValueRequest.new(hkey: handle, dw_index: index)
237
+ enum_value_request_packet.lp_data.referent_identifier = 0
238
+ enum_value_request_packet.lp_value_name.buffer.referent.max_count = 256
239
+ response = dcerpc_request(enum_value_request_packet)
240
+ begin
241
+ enum_value_response = RubySMB::Dcerpc::Winreg::EnumValueResponse.read(response)
242
+ rescue IOError
243
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the Enumvalue response"
244
+ end
245
+ unless enum_value_response.error_status == WindowsError::Win32::ERROR_SUCCESS
246
+ raise RubySMB::Dcerpc::Error::WinregError, "Error returned when enumerating values: "\
247
+ "#{WindowsError::Win32.find_by_retval(enum_value_response.error_status.value).join(',')}"
248
+ end
249
+
250
+ enum_value_response.lp_value_name.to_s
251
+ end
252
+
253
+ # Checks if the specified registry key exists. It returns true if it
254
+ # exists, false otherwise.
255
+ #
256
+ # @param key [String] the registry key to check
257
+ # @return [Boolean]
258
+ def has_registry_key?(key)
259
+ bind(endpoint: RubySMB::Dcerpc::Winreg)
260
+
261
+ root_key, sub_key = key.gsub(/\//, '\\').split('\\', 2)
262
+ begin
263
+ root_key_handle = open_root_key(root_key)
264
+ subkey_handle = open_key(root_key_handle, sub_key)
265
+ rescue RubySMB::Dcerpc::Error::WinregError
266
+ return false
267
+ end
268
+ close_key(subkey_handle)
269
+ return true
270
+ end
271
+
272
+ # Retrieve the data associated with the named value of a specified
273
+ # registry key.
274
+ #
275
+ # @param key [String] the registry key
276
+ # @param value_name [String] the name of the value to read
277
+ # @return [String] the data of the value entry
278
+ def read_registry_key_value(key, value_name)
279
+ bind(endpoint: RubySMB::Dcerpc::Winreg)
280
+
281
+ root_key, sub_key = key.gsub(/\//, '\\').split('\\', 2)
282
+ root_key_handle = open_root_key(root_key)
283
+ subkey_handle = open_key(root_key_handle, sub_key)
284
+ value = query_value(subkey_handle, value_name)
285
+ close_key(subkey_handle)
286
+ value
287
+ end
288
+
289
+ # Enumerate the subkeys of a specified registry key. If only a root key
290
+ # is provided, it enumerates its subkeys.
291
+ #
292
+ # @param key [String] the registry key
293
+ # @return [Array<String>] the subkeys
294
+ def enum_registry_key(key)
295
+ bind(endpoint: RubySMB::Dcerpc::Winreg)
296
+
297
+ root_key, sub_key = key.gsub(/\//, '\\').split('\\', 2)
298
+ root_key_handle = open_root_key(root_key)
299
+ subkey_handle = if sub_key.nil? || sub_key.empty?
300
+ root_key_handle
301
+ else
302
+ open_key(root_key_handle, sub_key)
303
+ end
304
+ query_info_key_response = query_info_key(subkey_handle)
305
+ key_count = query_info_key_response.lpc_sub_keys.to_i
306
+ enum_result = []
307
+ key_count.times do |i|
308
+ enum_result << enum_key(subkey_handle, i)
309
+ end
310
+ close_key(subkey_handle)
311
+ enum_result
312
+ end
313
+
314
+ # Enumerate the values for the specified registry key.
315
+ #
316
+ # @param key [String] the registry key
317
+ # @return [Array<String>] the values
318
+ def enum_registry_values(key)
319
+ bind(endpoint: RubySMB::Dcerpc::Winreg)
320
+
321
+ root_key, sub_key = key.gsub(/\//, '\\').split('\\', 2)
322
+ root_key_handle = open_root_key(root_key)
323
+ subkey_handle = if sub_key.nil? || sub_key.empty?
324
+ root_key_handle
325
+ else
326
+ open_key(root_key_handle, sub_key)
327
+ end
328
+ query_info_key_response = query_info_key(subkey_handle)
329
+ value_count = query_info_key_response.lpc_values.to_i
330
+ enum_result = []
331
+ value_count.times do |i|
332
+ enum_result << enum_value(subkey_handle, i)
333
+ end
334
+ close_key(subkey_handle)
335
+ enum_result
336
+ end
337
+
338
+ end
339
+ end
340
+ end