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 +7 -0
- data/LICENSE.txt +21 -0
- data/bin/hivexcavator +8 -0
- data/lib/hivexcavator/cli.rb +48 -0
- data/lib/hivexcavator/version.rb +6 -0
- data/lib/hivexcavator.rb +238 -0
- metadata +88 -0
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,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
|
data/lib/hivexcavator.rb
ADDED
@@ -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: []
|