hivexcavator 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d4d06b0b8f7b2af64065f2a63ff500e8c16ea41506dbc2c86dbde98f021be81d
4
+ data.tar.gz: b5136b478c400a20b4faf68efc018dbfdff2f409e862ca2b32bee9b80b7a203c
5
+ SHA512:
6
+ metadata.gz: abaefd8f2fb7e29972a32321f11bc9e764fdf983f7249950b954fee80882efffc1af6c657f08ab4386fa887a3888ae2a26397b7187a468d1c8869148e3aa7858
7
+ data.tar.gz: 23d7f34c90a3d8465de1bbea763ed9b029e95ce9abb66f9068f58ef272509bb498d16c3747fbd0d947910e5d08b226104bbbb693b4af86101d3ac5c5de202f04
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Alexandre ZANNI at ACCEIS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/bin/hivexcavator ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Internal
5
+ require 'hivexcavator'
6
+ require 'hivexcavator/cli'
7
+
8
+ HivExcavator::CLI
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Third party
4
+ require 'docopt'
5
+ require 'paint'
6
+
7
+ class HivExcavator
8
+ # module used for the CLI binary only, not required by the library
9
+ module CLI
10
+ pal = HivExcavator::PALETTE
11
+
12
+ doc = <<~DOCOPT
13
+ #{Paint['HivExcavator', :bold, pal[:MAIN]]} version #{Paint[HivExcavator::VERSION, :bold, pal[:THIRD]]}
14
+
15
+ #{Paint['Usage:', pal[:SECOND]]}
16
+ #{Paint['hivexcavator', pal[:THIRD]]} [options] <bcd>
17
+
18
+ #{Paint['Parameters:', pal[:SECOND]]}
19
+ #{Paint['<bcd>', pal[:FOURTH]]} BCD file
20
+
21
+ #{Paint['Options:', pal[:SECOND]]}
22
+ #{Paint['--no-color', pal[:FOURTH]]} Disable colorized output (NO_COLOR environment variable is respected too)
23
+ #{Paint['--debug', pal[:FOURTH]]} Display arguments
24
+ #{Paint['-h', pal[:FOURTH]]}, #{Paint['--help', pal[:FOURTH]]} Show this screen
25
+ #{Paint['--version', pal[:FOURTH]]} Show version
26
+
27
+ #{Paint['Examples:', pal[:SECOND]]}
28
+ hivexcavator ~/test/pxe/conf.bcd
29
+
30
+ #{Paint['Project:', pal[:SECOND]]}
31
+ #{Paint['author', :underline]} (https://pwn.by/noraj / https://twitter.com/noraj_rawsec)
32
+ #{Paint['source', :underline]} (https://github.com/acceis/hivexcavator)
33
+ #{Paint['documentation', :underline]} (https://acceis.github.io/hivexcavator)
34
+ DOCOPT
35
+
36
+ begin
37
+ args = Docopt.docopt(doc, version: HivExcavator::VERSION)
38
+ Paint.mode = 0 if args['--no-color']
39
+ puts args if args['--debug']
40
+ if args['<bcd>']
41
+ hiex = HivExcavator.new(args['<bcd>'])
42
+ hiex.display
43
+ end
44
+ rescue Docopt::Exit => e
45
+ puts e.message
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HivExcavator
4
+ # Version of HivExcavator library and app
5
+ VERSION = '0.0.1'
6
+ end
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Internal
4
+ require 'hivexcavator/version'
5
+ # Third party
6
+ require 'hivex'
7
+ require 'paint'
8
+
9
+ # Extracting the contents of Microsoft Windows Registry (hive) and display it as a colorful tree but mainly focused on
10
+ # parsing BCD files to extract WIM files path for PXE attacks.
11
+ class HivExcavator
12
+ # Windows Registry Value Type
13
+ # @see https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.registryvaluekind?view=net-7.0
14
+ TYPE = {
15
+ -1 => 'none',
16
+ 0 => 'unknown',
17
+ 1 => 'string',
18
+ 2 => 'expandstring',
19
+ 3 => 'binary',
20
+ 4 => 'dword', # hex
21
+ 7 => 'multistring',
22
+ 11 => 'qword' # hex
23
+ }.freeze
24
+
25
+ # Color palette of HivExcavator for display
26
+ PALETTE = {
27
+ MAIN: '#fe218b', # 70d6ff
28
+ SECOND: '#fed700', # ff70a6
29
+ THIRD: '#21b0fe', # ff9770
30
+ FOURTH: '#06d6a0' # ffd670
31
+ }.freeze
32
+
33
+ # Instantiate HivExcavator
34
+ # @param hive [String|Hivex::Hivex] Can be either a file path to a BCD file +String+ or a Hivex (hive)
35
+ # instance +Hivex::Hivex+.
36
+ def initialize(hive)
37
+ case hive
38
+ when Hivex::Hivex
39
+ @hive = hive
40
+ when String
41
+ @hive = Hivex.open(hive, {})
42
+ end
43
+ end
44
+
45
+ # Does a node has children?
46
+ # @param hive [Hivex::Hivex] hive instance
47
+ # @param node [Integer] node index
48
+ # @return [Boolean]
49
+ def self.node_children?(hive, node)
50
+ !hive.node_children(node).empty?
51
+ end
52
+
53
+ # Does a node has children?
54
+ # @param node [Integer] node index
55
+ # @return [Boolean]
56
+ def node_children?(node)
57
+ HivExcavator.node_children?(@hive, node)
58
+ end
59
+
60
+ # Does a node has a parent?
61
+ # @param hive [Hivex::Hivex] hive instance
62
+ # @param node [Integer] node index
63
+ # @return [Boolean]
64
+ def self.node_parent?(hive, node)
65
+ hive.node_parent(node).integer?
66
+ rescue Hivex::Error # Bad address
67
+ false
68
+ end
69
+
70
+ # Does a node has a parent?
71
+ # @param node [Integer] node index
72
+ # @return [Boolean]
73
+ def node_parent?(node)
74
+ HivExcavator.node_parent?(@hive, node)
75
+ end
76
+
77
+ # Does a node has values?
78
+ # @param hive [Hivex::Hivex] hive instance
79
+ # @param node [Integer] node index
80
+ # @return [Boolean]
81
+ def self.node_values?(hive, node)
82
+ !hive.node_values(node).empty?
83
+ end
84
+
85
+ # Does a node has values?
86
+ # @param node [Integer] node index
87
+ # @return [Boolean]
88
+ def node_values?(node)
89
+ HivExcavator.node_values?(@hive, node)
90
+ end
91
+
92
+ # Calculate the depth (nesting level) of a node (from the root node)
93
+ # @param hive [Hivex::Hivex] hive instance
94
+ # @param node [Integer] node index
95
+ # @return [Integer] depth level
96
+ def self.node_depth(hive, node)
97
+ count = 0
98
+ parent = node
99
+ while node_parent?(hive, parent)
100
+ parent = hive.node_parent(parent)
101
+ count += 1
102
+ end
103
+ count
104
+ end
105
+
106
+ # Calculate the depth (nesting level) of a node (from the root node)
107
+ # @param node [Integer] node index
108
+ # @return [Integer] depth level
109
+ def node_depth(node)
110
+ HivExcavator.node_depth(@hive, node)
111
+ end
112
+
113
+ # Output a number of whitespace depending on the depth
114
+ # @param hive [Hivex::Hivex] hive instance
115
+ # @param node [Integer] node index
116
+ # @param spaces [Integer] number of whitespaces per level
117
+ # @return [String] whitespaces
118
+ def self.space_depth(hive, node, spaces = 2)
119
+ ' ' * spaces * node_depth(hive, node)
120
+ end
121
+
122
+ # Output a number of whitespace depending on the depth
123
+ # @param node [Integer] node index
124
+ # @param spaces [Integer] number of whitespaces per level
125
+ # @return [String] whitespaces
126
+ def space_depth(node, spaces = 2)
127
+ HivExcavator.space_depth(@hive, node, spaces)
128
+ end
129
+
130
+ # Try to resolve known types to extract the value else just fix encoding of the provided value
131
+ # @param hive [Hivex::Hivex] hive instance
132
+ # @param value [Integer] value index
133
+ # @return [String] The decoded value
134
+ def self.extract_value(hive, value)
135
+ value_type = TYPE[hive.value_type(value)[:type]]
136
+ case value_type
137
+ when 'string'
138
+ hive.value_string(value)
139
+ when 'dword'
140
+ hive.value_dword(value)
141
+ when 'qword'
142
+ hive.value_qword(value)
143
+ else
144
+ hive.value_value(value)[:value].encode('UTF-8', 'Windows-1252').gsub("\n", '')
145
+ end
146
+ end
147
+
148
+ # Try to resolve known types to extract the value else just fix encoding of the provided value
149
+ # @param value [Integer] value index
150
+ # @return [String] The decoded value
151
+ def extract_value(value)
152
+ HivExcavator.extract_value(@hive, value)
153
+ end
154
+
155
+ # Display the BCD file as a tree
156
+ # @param hive [Hivex::Hivex] hive instance
157
+ # @param current_node [Integer] node index
158
+ # @return [nil]
159
+ def self.node_list(hive, current_node)
160
+ nodes = hive.node_children(current_node)
161
+ nodes.each do |node|
162
+ node_name = hive.node_name(node)
163
+ puts "#{space_depth(hive, node)}#{Paint[node_name, PALETTE[:MAIN]]} (#{Paint[node, PALETTE[:SECOND]]})"
164
+ if node_values?(hive, node)
165
+ node_values = hive.node_values(node)
166
+ node_values.each do |value|
167
+ value_value = extract_value(hive, value)
168
+ print " #{space_depth(hive, node)}#{Paint[hive.value_key(value), PALETTE[:FOURTH]]} :"
169
+ puts "#{Paint[value_value, PALETTE[:THIRD]]} (#{Paint[value, PALETTE[:SECOND]]})"
170
+ end
171
+ end
172
+ node_list(hive, node) if node_children?(hive, node)
173
+ end
174
+ nil
175
+ end
176
+
177
+ # Display the BCD file as a tree
178
+ # @param current_node [Integer] node index
179
+ # @return [nil]
180
+ def node_list(current_node)
181
+ HivExcavator.node_list(@hive, current_node)
182
+ end
183
+
184
+ # Display a line with the name of the store / hive
185
+ # @param hive [Hivex::Hivex] hive instance
186
+ # @return [nil]
187
+ def self.diplay_store(hive)
188
+ root = hive.root
189
+ puts "#{Paint[hive.node_name(root), PALETTE[:MAIN]]} (#{Paint[root, PALETTE[:SECOND]]})"
190
+ end
191
+
192
+ # Display a line with the name of the store / hive
193
+ # @return [nil]
194
+ def diplay_store
195
+ HivExcavator.diplay_store(@hive)
196
+ end
197
+
198
+ # Display the tree of all nodes, key and values
199
+ # @param hive [Hivex::Hivex] hive instance
200
+ # @return [nil]
201
+ def self.display_tree(hive)
202
+ node_list(hive, hive.root)
203
+ end
204
+
205
+ # Display the tree of all nodes, key and values
206
+ # @return [nil]
207
+ def display_tree
208
+ HivExcavator.display_tree(@hive)
209
+ end
210
+
211
+ # Display the store name ({diplay_store}) and the tree ({display_tree})
212
+ # @param hive [Hivex::Hivex] hive instance
213
+ # @return [nil]
214
+ # @example
215
+ # require 'hivexcavator'
216
+ # require 'hivex'
217
+ #
218
+ # store = Hivex.open('/home/noraj/test/pxe/conf.bcd', {})
219
+ # HivExcavator.display(store)
220
+ # store.close
221
+ def self.display(hive)
222
+ diplay_store(hive)
223
+ display_tree(hive)
224
+ end
225
+
226
+ # Display the store name ({diplay_store}) and the tree ({display_tree})
227
+ # @return [nil]
228
+ # @example
229
+ # require 'hivexcavator'
230
+ # require 'hivex'
231
+ #
232
+ # store = '/home/noraj/test/pxe/conf.bcd'
233
+ # hiex = HivExcavator.new(store)
234
+ # hiex.display
235
+ def display
236
+ HivExcavator.display(@hive)
237
+ end
238
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hivexcavator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexandre ZANNI
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-10-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: docopt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: paint
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.3'
41
+ description: Extracting the contents of Microsoft Windows Registry (hive) and display
42
+ it as a colorful tree but mainly focused on parsing BCD files to extract WIM files
43
+ path for PXE attacks.
44
+ email: alexandre.zanni@europe.com
45
+ executables:
46
+ - hivexcavator
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - LICENSE.txt
51
+ - bin/hivexcavator
52
+ - lib/hivexcavator.rb
53
+ - lib/hivexcavator/cli.rb
54
+ - lib/hivexcavator/version.rb
55
+ homepage: https://acceis.github.io/hivexcavator/
56
+ licenses:
57
+ - MIT
58
+ metadata:
59
+ yard.run: yard
60
+ bug_tracker_uri: https://github.com/acceis/hivexcavator/issues
61
+ changelog_uri: https://github.com/acceis/hivexcavator/blob/master/docs/CHANGELOG.md
62
+ documentation_uri: https://acceis.github.io/hivexcavator/
63
+ homepage_uri: https://acceis.github.io/hivexcavator/
64
+ source_code_uri: https://github.com/acceis/hivexcavator/
65
+ rubygems_mfa_required: 'true'
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 3.0.0
75
+ - - "<"
76
+ - !ruby/object:Gem::Version
77
+ version: '4.0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubygems_version: 3.3.25
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Parse BCD files to extract WIM files path (among other stuff)
88
+ test_files: []