hivexcavator 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|