| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
|
|---|
| 31 |
=pod |
|---|
| 32 |
|
|---|
| 33 |
=head1 NAME |
|---|
| 34 |
|
|---|
| 35 |
HoneyClient::Agent::Integrity::Registry - Perl extension to perform |
|---|
| 36 |
static checks of the Windows OS registry. |
|---|
| 37 |
|
|---|
| 38 |
=head1 VERSION |
|---|
| 39 |
|
|---|
| 40 |
This documentation refers to HoneyClient::Agent::Integrity::Registry version 1.0. |
|---|
| 41 |
|
|---|
| 42 |
=head1 SYNOPSIS |
|---|
| 43 |
|
|---|
| 44 |
use HoneyClient::Agent::Integrity::Registry; |
|---|
| 45 |
use Data::Dumper; |
|---|
| 46 |
|
|---|
| 47 |
# Create the registry object. Upon creation, the object |
|---|
| 48 |
# will be initialized, by collecting a baseline of the registry. |
|---|
| 49 |
my $registry = HoneyClient::Agent::Integrity::Registry->new(); |
|---|
| 50 |
|
|---|
| 51 |
# ... Some time elapses ... |
|---|
| 52 |
|
|---|
| 53 |
# Check the registry, for any changes. |
|---|
| 54 |
my $changes = $registry->check(); |
|---|
| 55 |
|
|---|
| 56 |
if (!defined($changes)) { |
|---|
| 57 |
print "No registry changes have occurred.\n"; |
|---|
| 58 |
} else { |
|---|
| 59 |
print "Registry has changed:\n"; |
|---|
| 60 |
print Dumper($changes); |
|---|
| 61 |
} |
|---|
| 62 |
|
|---|
| 63 |
# $changes refers to an array of hashtable references, where |
|---|
| 64 |
# each hashtable has the following format: |
|---|
| 65 |
# |
|---|
| 66 |
# $changes = [ { |
|---|
| 67 |
# # The registry directory name. |
|---|
| 68 |
# 'key' => 'HKEY_LOCAL_MACHINE\Software...', |
|---|
| 69 |
# |
|---|
| 70 |
# # Indicates if the registry directory was deleted, |
|---|
| 71 |
# # added, or changed. |
|---|
| 72 |
# 'status' => 'deleted' | 'added' | 'changed', |
|---|
| 73 |
# |
|---|
| 74 |
# # An array containing the list of entries within the |
|---|
| 75 |
# # registry directory that have been deleted, added, or |
|---|
| 76 |
# # changed. If this array is empty, then the corresponding |
|---|
| 77 |
# # registry directory in the original and new hives contained |
|---|
| 78 |
# # no entries. |
|---|
| 79 |
# 'entries' => [ { |
|---|
| 80 |
# 'name' => "\"string\"", # A (potentially) quoted string; |
|---|
| 81 |
# # "@" for default |
|---|
| 82 |
# 'new_value' => "string", # New string; maybe undef, if deleted |
|---|
| 83 |
# 'old_value' => "string", # Old string; maybe undef, if added |
|---|
| 84 |
# }, ], |
|---|
| 85 |
# }, ] |
|---|
| 86 |
|
|---|
| 87 |
=head1 DESCRIPTION |
|---|
| 88 |
|
|---|
| 89 |
This library allows the Agent module to easily baseline and check |
|---|
| 90 |
the Windows OS registry hives for any changes that may occur, while |
|---|
| 91 |
instrumenting a target application. |
|---|
| 92 |
|
|---|
| 93 |
This library uses modified code from the 'regutils' library by |
|---|
| 94 |
John Rochester and Michael Rendell. |
|---|
| 95 |
See L<http://www.cs.mun.ca/~michael/regutils/> for more information. |
|---|
| 96 |
|
|---|
| 97 |
=cut |
|---|
| 98 |
|
|---|
| 99 |
package HoneyClient::Agent::Integrity::Registry; |
|---|
| 100 |
|
|---|
| 101 |
use strict; |
|---|
| 102 |
use warnings; |
|---|
| 103 |
use Carp (); |
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 |
|
|---|
| 109 |
use HoneyClient::Util::Config qw(getVar); |
|---|
| 110 |
|
|---|
| 111 |
|
|---|
| 112 |
use HoneyClient::Agent::Integrity::Registry::Parser; |
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
use Data::Dumper; |
|---|
| 116 |
|
|---|
| 117 |
|
|---|
| 118 |
use Storable qw(dclone); |
|---|
| 119 |
|
|---|
| 120 |
|
|---|
| 121 |
use Log::Log4perl qw(:easy); |
|---|
| 122 |
|
|---|
| 123 |
|
|---|
| 124 |
use IO::Handle; |
|---|
| 125 |
use IO::File; |
|---|
| 126 |
use Fcntl qw(:seek); |
|---|
| 127 |
|
|---|
| 128 |
|
|---|
| 129 |
use File::Temp qw(tmpnam unlink0); |
|---|
| 130 |
|
|---|
| 131 |
|
|---|
| 132 |
use Filesys::CygwinPaths qw(:all); |
|---|
| 133 |
|
|---|
| 134 |
|
|---|
| 135 |
use Search::Binary; |
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 |
|
|---|
| 139 |
|
|---|
| 140 |
|
|---|
| 141 |
BEGIN { |
|---|
| 142 |
|
|---|
| 143 |
require Exporter; |
|---|
| 144 |
our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION); |
|---|
| 145 |
|
|---|
| 146 |
|
|---|
| 147 |
$VERSION = 0.9; |
|---|
| 148 |
|
|---|
| 149 |
@ISA = qw(Exporter); |
|---|
| 150 |
|
|---|
| 151 |
|
|---|
| 152 |
@EXPORT = qw( ); |
|---|
| 153 |
|
|---|
| 154 |
|
|---|
| 155 |
|
|---|
| 156 |
|
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 |
|
|---|
| 160 |
|
|---|
| 161 |
|
|---|
| 162 |
%EXPORT_TAGS = ( |
|---|
| 163 |
'all' => [ qw( ) ], |
|---|
| 164 |
); |
|---|
| 165 |
|
|---|
| 166 |
|
|---|
| 167 |
@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
|---|
| 168 |
|
|---|
| 169 |
$SIG{PIPE} = 'IGNORE'; |
|---|
| 170 |
} |
|---|
| 171 |
our (@EXPORT_OK, $VERSION); |
|---|
| 172 |
|
|---|
| 173 |
=pod |
|---|
| 174 |
|
|---|
| 175 |
=begin testing |
|---|
| 176 |
|
|---|
| 177 |
# Make sure Log::Log4perl loads |
|---|
| 178 |
BEGIN { use_ok('Log::Log4perl', qw(:nowarn)) |
|---|
| 179 |
or diag("Can't load Log::Log4perl package. Check to make sure the package library is correctly listed within the path."); |
|---|
| 180 |
|
|---|
| 181 |
# Suppress all logging messages, since we need clean output for unit testing. |
|---|
| 182 |
Log::Log4perl->init({ |
|---|
| 183 |
"log4perl.rootLogger" => "DEBUG, Buffer", |
|---|
| 184 |
"log4perl.appender.Buffer" => "Log::Log4perl::Appender::TestBuffer", |
|---|
| 185 |
"log4perl.appender.Buffer.min_level" => "fatal", |
|---|
| 186 |
"log4perl.appender.Buffer.layout" => "Log::Log4perl::Layout::PatternLayout", |
|---|
| 187 |
"log4perl.appender.Buffer.layout.ConversionPattern" => "%d{yyyy-MM-dd HH:mm:ss} %5p [%M] (%F:%L) - %m%n", |
|---|
| 188 |
}); |
|---|
| 189 |
} |
|---|
| 190 |
require_ok('Log::Log4perl'); |
|---|
| 191 |
use Log::Log4perl qw(:easy); |
|---|
| 192 |
|
|---|
| 193 |
# Make sure the module loads properly, with the exportable |
|---|
| 194 |
# functions shared. |
|---|
| 195 |
BEGIN { use_ok('HoneyClient::Util::Config', qw(getVar setVar)) |
|---|
| 196 |
or diag("Can't load HoneyClient::Util::Config package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 197 |
require_ok('HoneyClient::Util::Config'); |
|---|
| 198 |
can_ok('HoneyClient::Util::Config', 'getVar'); |
|---|
| 199 |
can_ok('HoneyClient::Util::Config', 'setVar'); |
|---|
| 200 |
use HoneyClient::Util::Config qw(getVar setVar); |
|---|
| 201 |
|
|---|
| 202 |
# Suppress all logging messages, since we need clean output for unit testing. |
|---|
| 203 |
Log::Log4perl->init({ |
|---|
| 204 |
"log4perl.rootLogger" => "DEBUG, Buffer", |
|---|
| 205 |
"log4perl.appender.Buffer" => "Log::Log4perl::Appender::TestBuffer", |
|---|
| 206 |
"log4perl.appender.Buffer.min_level" => "fatal", |
|---|
| 207 |
"log4perl.appender.Buffer.layout" => "Log::Log4perl::Layout::PatternLayout", |
|---|
| 208 |
"log4perl.appender.Buffer.layout.ConversionPattern" => "%d{yyyy-MM-dd HH:mm:ss} %5p [%M] (%F:%L) - %m%n", |
|---|
| 209 |
}); |
|---|
| 210 |
|
|---|
| 211 |
# Make sure Data::Dumper loads |
|---|
| 212 |
BEGIN { use_ok('Data::Dumper') |
|---|
| 213 |
or diag("Can't load Data::Dumper package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 214 |
require_ok('Data::Dumper'); |
|---|
| 215 |
use Data::Dumper; |
|---|
| 216 |
|
|---|
| 217 |
# Make sure Storable loads |
|---|
| 218 |
BEGIN { use_ok('Storable', qw(dclone)) |
|---|
| 219 |
or diag("Can't load Storable package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 220 |
require_ok('Storable'); |
|---|
| 221 |
can_ok('Storable', 'dclone'); |
|---|
| 222 |
use Storable qw(dclone); |
|---|
| 223 |
|
|---|
| 224 |
# Make sure IO::Handle loads |
|---|
| 225 |
BEGIN { use_ok('IO::Handle') |
|---|
| 226 |
or diag("Can't load IO::Handle package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 227 |
require_ok('IO::Handle'); |
|---|
| 228 |
use IO::Handle; |
|---|
| 229 |
|
|---|
| 230 |
# Make sure IO::File loads |
|---|
| 231 |
BEGIN { use_ok('IO::File') |
|---|
| 232 |
or diag("Can't load IO::File package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 233 |
require_ok('IO::File'); |
|---|
| 234 |
use IO::File; |
|---|
| 235 |
|
|---|
| 236 |
# Make sure Fcntl loads |
|---|
| 237 |
BEGIN { use_ok('Fcntl') |
|---|
| 238 |
or diag("Can't load Fcntl package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 239 |
require_ok('Fcntl'); |
|---|
| 240 |
use Fcntl qw(:seek); |
|---|
| 241 |
|
|---|
| 242 |
# Make sure File::Temp loads |
|---|
| 243 |
BEGIN { use_ok('File::Temp') |
|---|
| 244 |
or diag("Can't load File::Temp package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 245 |
require_ok('File::Temp'); |
|---|
| 246 |
can_ok('File::Temp', 'tmpnam'); |
|---|
| 247 |
can_ok('File::Temp', 'unlink0'); |
|---|
| 248 |
use File::Temp qw(tmpnam unlink0); |
|---|
| 249 |
|
|---|
| 250 |
# Make sure Filesys::CygwinPaths loads |
|---|
| 251 |
BEGIN { use_ok('Filesys::CygwinPaths') |
|---|
| 252 |
or diag("Can't load Filesys::CygwinPaths package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 253 |
require_ok('Filesys::CygwinPaths'); |
|---|
| 254 |
use Filesys::CygwinPaths qw(:all); |
|---|
| 255 |
|
|---|
| 256 |
# Make sure Search::Binary loads |
|---|
| 257 |
BEGIN { use_ok('Search::Binary') |
|---|
| 258 |
or diag("Can't load Search::Binary package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 259 |
require_ok('Search::Binary'); |
|---|
| 260 |
can_ok('Search::Binary', 'binary_search'); |
|---|
| 261 |
use Search::Binary; |
|---|
| 262 |
|
|---|
| 263 |
# Make sure HoneyClient::Agent::Integrity::Registry::Parser loads |
|---|
| 264 |
BEGIN { use_ok('HoneyClient::Agent::Integrity::Registry::Parser') |
|---|
| 265 |
or diag("Can't load HoneyClient::Agent::Integrity::Registry::Parser package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 266 |
require_ok('HoneyClient::Agent::Integrity::Registry::Parser'); |
|---|
| 267 |
use HoneyClient::Agent::Integrity::Registry::Parser; |
|---|
| 268 |
|
|---|
| 269 |
# Make sure HoneyClient::Agent::Integrity::Registry loads |
|---|
| 270 |
BEGIN { use_ok('HoneyClient::Agent::Integrity::Registry') |
|---|
| 271 |
or diag("Can't load HoneyClient::Agent::Integrity::Registry package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 272 |
require_ok('HoneyClient::Agent::Integrity::Registry'); |
|---|
| 273 |
use HoneyClient::Agent::Integrity::Registry; |
|---|
| 274 |
|
|---|
| 275 |
# Make sure File::Basename loads. |
|---|
| 276 |
BEGIN { use_ok('File::Basename', qw(dirname basename fileparse)) or diag("Can't load File::Basename package. Check to make sure the package library is correctly listed within the path."); } |
|---|
| 277 |
require_ok('File::Basename'); |
|---|
| 278 |
can_ok('File::Basename', 'dirname'); |
|---|
| 279 |
can_ok('File::Basename', 'basename'); |
|---|
| 280 |
can_ok('File::Basename', 'fileparse'); |
|---|
| 281 |
use File::Basename qw(dirname basename fileparse); |
|---|
| 282 |
|
|---|
| 283 |
=end testing |
|---|
| 284 |
|
|---|
| 285 |
=cut |
|---|
| 286 |
|
|---|
| 287 |
|
|---|
| 288 |
|
|---|
| 289 |
|
|---|
| 290 |
|
|---|
| 291 |
|
|---|
| 292 |
our $LOG = get_logger(); |
|---|
| 293 |
|
|---|
| 294 |
|
|---|
| 295 |
$Data::Dumper::Terse = 1; |
|---|
| 296 |
$Data::Dumper::Indent = 0; |
|---|
| 297 |
|
|---|
| 298 |
=pod |
|---|
| 299 |
|
|---|
| 300 |
=head1 DEFAULT PARAMETER LIST |
|---|
| 301 |
|
|---|
| 302 |
When a Registry B<$object> is instantiated using the B<new()> function, |
|---|
| 303 |
the following parameters are supplied default values. Each value |
|---|
| 304 |
can be overridden by specifying the new (key => value) pair into the |
|---|
| 305 |
B<new()> function, as arguments. |
|---|
| 306 |
|
|---|
| 307 |
=head2 hives_to_check |
|---|
| 308 |
|
|---|
| 309 |
=over 4 |
|---|
| 310 |
|
|---|
| 311 |
This parameter indicates the default array of registry hive names |
|---|
| 312 |
to monitor for changes. |
|---|
| 313 |
|
|---|
| 314 |
=back |
|---|
| 315 |
|
|---|
| 316 |
=head2 key_dirnames_to_ignore |
|---|
| 317 |
|
|---|
| 318 |
=over 4 |
|---|
| 319 |
|
|---|
| 320 |
This parameter indicates the default array of regular expressions |
|---|
| 321 |
that each registry directory will be checked against. Any matching |
|---|
| 322 |
key directory names will be ignored and any subsequent additions, |
|---|
| 323 |
deletions, or changes to all content in these matches will also |
|---|
| 324 |
be ignored. |
|---|
| 325 |
|
|---|
| 326 |
Each $entry will be used via the syntax /$entry/. Thus, |
|---|
| 327 |
it is recommended to specify the ^ prefix and $ suffix, when |
|---|
| 328 |
possible. |
|---|
| 329 |
|
|---|
| 330 |
A single backslash (\) must be represented using triple |
|---|
| 331 |
backslashes (\\\) and each $entry must not end with any |
|---|
| 332 |
backslash character. |
|---|
| 333 |
|
|---|
| 334 |
=back |
|---|
| 335 |
|
|---|
| 336 |
=head2 bypass_baseline |
|---|
| 337 |
|
|---|
| 338 |
=over 4 |
|---|
| 339 |
|
|---|
| 340 |
When set to 1, the object will forgo any type of initial baselining |
|---|
| 341 |
process, upon initialization. Otherwise, baselining will occur |
|---|
| 342 |
as normal, upon initialization. |
|---|
| 343 |
|
|---|
| 344 |
=back |
|---|
| 345 |
|
|---|
| 346 |
=cut |
|---|
| 347 |
|
|---|
| 348 |
my %PARAMS = ( |
|---|
| 349 |
|
|---|
| 350 |
|
|---|
| 351 |
|
|---|
| 352 |
hives_to_check => [ |
|---|
| 353 |
'HKEY_LOCAL_MACHINE', |
|---|
| 354 |
'HKEY_CLASSES_ROOT', |
|---|
| 355 |
'HKEY_CURRENT_USER', |
|---|
| 356 |
'HKEY_USERS', |
|---|
| 357 |
'HKEY_CURRENT_CONFIG', |
|---|
| 358 |
], |
|---|
| 359 |
|
|---|
| 360 |
|
|---|
| 361 |
|
|---|
| 362 |
|
|---|
| 363 |
|
|---|
| 364 |
|
|---|
| 365 |
|
|---|
| 366 |
|
|---|
| 367 |
|
|---|
| 368 |
|
|---|
| 369 |
|
|---|
| 370 |
|
|---|
| 371 |
|
|---|
| 372 |
key_dirnames_to_ignore => [ |
|---|
| 373 |
'^HKEY_CURRENT_USER\\\SessionInformation.*$', |
|---|
| 374 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Internet Explorer\\\Main$', |
|---|
| 375 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Internet Explorer\\\Security\\\AntiPhishing.*$', |
|---|
| 376 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Internet Explorer\\\TypedURLs$', |
|---|
| 377 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Explorer\\\MenuOrder\\\Favorites\\\Links.*$', |
|---|
| 378 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Explorer\\\MenuOrder\\\Start Menu2\\\Programs.*$', |
|---|
| 379 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Explorer\\\MountPoints2\\\CPC\\\Volume.*$', |
|---|
| 380 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Explorer\\\UserAssist\\\.+\\\Count.*$', |
|---|
| 381 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Ext\\\Stats\\\.+\\\iexplore.*$', |
|---|
| 382 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Internet Settings\\\Connections.*$', |
|---|
| 383 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Internet Settings\\\5.0\\\Cache.*$', |
|---|
| 384 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\ShellNoRoam\\\DUIBags\\\ShellFolders\\\.*$', |
|---|
| 385 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\ShellNoRoam\\\BagMRU.*$', |
|---|
| 386 |
'^HKEY_CURRENT_USER\\\Software\\\Microsoft\\\Windows\\\ShellNoRoam\\\MUICache.*$', |
|---|
| 387 |
'^HKEY_CURRENT_USER\\\Volatile Environment$', |
|---|
| 388 |
'^HKEY_LOCAL_MACHINE\\\SOFTWARE\\\Microsoft\\\Cryptography\\\RNG$', |
|---|
| 389 |
'^HKEY_LOCAL_MACHINE\\\SOFTWARE\\\Microsoft\\\Windows\\\CurrentVersion\\\BITS$', |
|---|
| 390 |
'^HKEY_LOCAL_MACHINE\\\SOFTWARE\\\Microsoft\\\Windows\\\CurrentVersion\\\Group Policy\\\State\\\Machine\\\Extension-List\\\.*$', |
|---|
| 391 |
'^HKEY_LOCAL_MACHINE\\\SOFTWARE\\\Microsoft\\\Windows\\\CurrentVersion\\\WindowsUpdate\\\.*$', |
|---|
| 392 |
'^HKEY_LOCAL_MACHINE\\\SOFTWARE\\\Microsoft\\\Windows\\\CurrentVersion\\\WindowsUpdate\\\Auto Update.*$', |
|---|
| 393 |
'^HKEY_LOCAL_MACHINE\\\SOFTWARE\\\Microsoft\\\Windows NT\\\CurrentVersion\\\Winlogon\\\Notify\\\WgaLogon\\\Settings$', |
|---|
| 394 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\ControlSet.+\\\Services\\\.+\\\Parameters\\\Tcpip.*$', |
|---|
| 395 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\ControlSet.+\\\Services\\\Dhcp\\\Parameters.*$', |
|---|
| 396 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\ControlSet.+\\\Services\\\Eventlog\\\Application\\\ESENT.*$', |
|---|
| 397 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\ControlSet.+\\\Services\\\SharedAccess\\\Epoch.*$', |
|---|
| 398 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\ControlSet.+\\\Services\\\Tcpip\\\Parameters\\\Interfaces\\\.*$', |
|---|
| 399 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\CurrentControlSet\\\Services\\\Dhcp\\\Parameters.*$', |
|---|
| 400 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\CurrentControlSet\\\Services\\\Eventlog\\\Application\\\ESENT.*$', |
|---|
| 401 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\CurrentControlSet\\\Services\\\SharedAccess\\\Epoch$', |
|---|
| 402 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\CurrentControlSet\\\Services\\\Tcpip\\\Parameters\\\Interfaces\\\.*$', |
|---|
| 403 |
'^HKEY_LOCAL_MACHINE\\\SYSTEM\\\CurrentControlSet\\\Services\\\.+\\\Parameters\\\Tcpip.*$', |
|---|
| 404 |
'^HKEY_USERS\\\.+\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Explorer\\\UserAssist\\\.+\\\Count.*$', |
|---|
| 405 |
'^HKEY_USERS\\\.+\\\Software\\\Microsoft\\\Windows\\\ShellNoRoam\\\BagMRU.*$', |
|---|
| 406 |
'^HKEY_USERS\\\.+\\\UNICODE Program Groups.*$', |
|---|
| 407 |
'^HKEY_USERS\\\S.+\\\SessionInformation$', |
|---|
| 408 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Internet Explorer\\\Main$', |
|---|
| 409 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Internet Explorer\\\Security\\\AntiPhishing.*$', |
|---|
| 410 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Internet Explorer\\\TypedURLs$', |
|---|
| 411 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Explorer\\\MenuOrder\\\Favorites\\\Links.*$', |
|---|
| 412 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Explorer\\\MenuOrder\\\Start Menu2\\\Programs.*$', |
|---|
| 413 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Explorer\\\MountPoints2\\\CPC\\\Volume.*$', |
|---|
| 414 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Ext\\\Stats\\\.+\\\iexplore.*$', |
|---|
| 415 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Internet Settings\\\Connections.*$', |
|---|
| 416 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Windows\\\CurrentVersion\\\Internet Settings\\\5.0\\\Cache.*$', |
|---|
| 417 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Windows\\\ShellNoRoam\\\DUIBags\\\ShellFolders\\\.*$', |
|---|
| 418 |
'^HKEY_USERS\\\S.+\\\Software\\\Microsoft\\\Windows\\\ShellNoRoam\\\MUICache.*$', |
|---|
| 419 |
], |
|---|
| 420 |
|
|---|
| 421 |
|
|---|
| 422 |
|
|---|
| 423 |
|
|---|
| 424 |
bypass_baseline => 0, |
|---|
| 425 |
|
|---|
| 426 |
|
|---|
| 427 |
|
|---|
| 428 |
|
|---|
| 429 |
_baseline_parsers => { }, |
|---|
| 430 |
|
|---|
| 431 |
|
|---|
| 432 |
|
|---|
| 433 |
|
|---|
| 434 |
_checkpoint_parsers => { }, |
|---|
| 435 |
|
|---|
| 436 |
|
|---|
| 437 |
|
|---|
| 438 |
|
|---|
| 439 |
_filenames => { }, |
|---|
| 440 |
|
|---|
| 441 |
|
|---|
| 442 |
|
|---|
| 443 |
|
|---|
| 444 |
_currentKeys => { }, |
|---|
| 445 |
|
|---|
| 446 |
|
|---|
| 447 |
|
|---|
| 448 |
|
|---|
| 449 |
_currentEntryIndex => { }, |
|---|
| 450 |
|
|---|
| 451 |
|
|---|
| 452 |
|
|---|
| 453 |
_last_search_index => undef, |
|---|
| 454 |
|
|---|
| 455 |
|
|---|
| 456 |
|
|---|
| 457 |
|
|---|
| 458 |
_group_index_linenums => [ ], |
|---|
| 459 |
); |
|---|
| 460 |
|
|---|
| 461 |
|
|---|
| 462 |
|
|---|
| 463 |
|
|---|
| 464 |
|
|---|
| 465 |
|
|---|
| 466 |
|
|---|
| 467 |
|
|---|
| 468 |
|
|---|
| 469 |
sub DESTROY { |
|---|
| 470 |
my $self = shift; |
|---|
| 471 |
|
|---|
| 472 |
|
|---|
| 473 |
my $parser = undef; |
|---|
| 474 |
my $fname = undef; |
|---|
| 475 |
foreach my $hive (@{$self->{hives_to_check}}) { |
|---|
| 476 |
$parser = $self->{_baseline_parsers}->{$hive}; |
|---|
| 477 |
if (defined($parser)) { |
|---|
| 478 |
$fname = $self->{_filenames}->{$parser}; |
|---|
| 479 |
$LOG->debug("Deleting baseline of hive '" . $hive . "' in '" . |
|---|
| 480 |
$fname . "'."); |
|---|
| 481 |
if (!unlink($fname)) { |
|---|
| 482 |
$LOG->fatal("Error: Unable to unlink '" . $hive . "' hive data in '" . $fname ."'."); |
|---|
| 483 |
Carp::croak("Error: Unable to unlink '" . $hive . "' hive data in '" . $fname ."'."); |
|---|
| 484 |
} |
|---|
| 485 |
delete($self->{_filenames}->{$parser}); |
|---|
| 486 |
delete($self->{_baseline_parsers}->{$hive}); |
|---|
| 487 |
} |
|---|
| 488 |
$parser = $self->{_checkpoint_parsers}->{$hive}; |
|---|
| 489 |
if (defined($parser)) { |
|---|
| 490 |
$fname = $self->{_filenames}->{$parser}; |
|---|
| 491 |
$LOG->debug("Deleting checkpoint of hive '" . $hive . "' in '" . |
|---|
| 492 |
$fname . "'."); |
|---|
| 493 |
if (!unlink($fname)) { |
|---|
| 494 |
$LOG->fatal("Error: Unable to unlink '" . $hive . "' hive data in '" . $fname ."'."); |
|---|
| 495 |
Carp::croak("Error: Unable to unlink '" . $hive . "' hive data in '" . $fname ."'."); |
|---|
| 496 |
} |
|---|
| 497 |
delete($self->{_filenames}->{$parser}); |
|---|
| 498 |
delete($self->{_checkpoint_parsers}->{$hive}); |
|---|
| 499 |
} |
|---|
| 500 |
} |
|---|
| 501 |
} |
|---|
| 502 |
|
|---|
| 503 |
|
|---|
| 504 |
|
|---|
| 505 |
|
|---|
| 506 |
|
|---|
| 507 |
|
|---|
| 508 |
|
|---|
| 509 |
|
|---|
| 510 |
|
|---|
| 511 |
sub _snapshot { |
|---|
| 512 |
|
|---|
| 513 |
my ($self, $parser_collection) = @_; |
|---|
| 514 |
my $parser = undef; |
|---|
| 515 |
my $fname = undef; |
|---|
| 516 |
my $fname_tmp = undef; |
|---|
| 517 |
foreach my $hive (@{$self->{hives_to_check}}) { |
|---|
| 518 |
$fname = tmpnam(); |
|---|
| 519 |
$fname_tmp = tmpnam(); |
|---|
| 520 |
$LOG->debug("Storing snapshot of hive '" . $hive . "' into '" . $fname . "'."); |
|---|
| 521 |
$LOG->debug("Creating temporary file '" . $fname_tmp . "' to perform data conversion."); |
|---|
| 522 |
|
|---|
| 523 |
|
|---|
| 524 |
if (system("regedit.exe /a \"" . fullwin32path($fname_tmp) . "\" \"$hive\" && |
|---|
| 525 |
cat " . $fname_tmp . " | sed -e 's/\r//g' > " . $fname) != 0) { |
|---|
| 526 |
$LOG->fatal("Error: Unable to write '" . $hive . "' hive data to '" . $fname ."'."); |
|---|
| 527 |
Carp::croak("Error: Unable to write '" . $hive . "' hive data to '" . $fname ."'."); |
|---|
| 528 |
} |
|---|
| 529 |
|
|---|
| 530 |
|
|---|
| 531 |
_cleanup($fname_tmp); |
|---|
| 532 |
|
|---|
| 533 |
$parser = HoneyClient::Agent::Integrity::Registry::Parser->init(input_file => $fname, |
|---|
| 534 |
index_groups => 1, |
|---|
| 535 |
show_progress => 0); |
|---|
| 536 |
|
|---|
| 537 |
$parser_collection->{$hive} = $parser; |
|---|
| 538 |
$self->{_filenames}->{$parser} = $fname; |
|---|
| 539 |
} |
|---|
| 540 |
} |
|---|
| 541 |
|
|---|
| 542 |
|
|---|
| 543 |
|
|---|
| 544 |
|
|---|
| 545 |
|
|---|
| 546 |
|
|---|
| 547 |
|
|---|
| 548 |
|
|---|
| 549 |
|
|---|
| 550 |
|
|---|
| 551 |
|
|---|
| 552 |
sub _cmpGroup { |
|---|
| 553 |
my ($self, $x, $y) = @_; |
|---|
| 554 |
$x =~ tr/A-Z/a-z/; |
|---|
| 555 |
$x =~ s/\\/\001/g; |
|---|
| 556 |
$y =~ tr/A-Z/a-z/; |
|---|
| 557 |
$y =~ s/\\/\001/g; |
|---|
| 558 |
return $x cmp $y; |
|---|
| 559 |
} |
|---|
| 560 |
|
|---|
| 561 |
|
|---|
| 562 |
|
|---|
| 563 |
|
|---|
| 564 |
|
|---|
| 565 |
|
|---|
| 566 |
|
|---|
| 567 |
|
|---|
| 568 |
|
|---|
| 569 |
|
|---|
| 570 |
|
|---|
| 571 |
sub _cmpEntryName { |
|---|
| 572 |
my ($self, $x, $y) = @_; |
|---|
| 573 |
|
|---|
| 574 |
if ($x eq '@') { |
|---|
| 575 |
$x = ''; |
|---|
| 576 |
} else { |
|---|
| 577 |
$x =~ s/^"//; |
|---|
| 578 |
$x =~ s/"$//; |
|---|
| 579 |
$x =~ tr/A-Z/a-z/; |
|---|
| 580 |
} |
|---|
| 581 |
|
|---|
| 582 |
if ($y eq '@') { |
|---|
| 583 |
$y = ''; |
|---|
| 584 |
} else { |
|---|
| 585 |
$y =~ s/^"//; |
|---|
| 586 |
$y =~ s/"$//; |
|---|
| 587 |
$y =~ tr/A-Z/a-z/; |
|---|
| 588 |
} |
|---|
| 589 |
|
|---|
| 590 |
return $x cmp $y; |
|---|
| 591 |
} |
|---|
| 592 |
|
|---|
| 593 |
|
|---|
| 594 |
|
|---|
| 595 |
|
|---|
| 596 |
|
|---|
| 597 |
|
|---|
| 598 |
|
|---|
| 599 |
|
|---|
| 600 |
sub _nextGroup { |
|---|
| 601 |
my ($self, $parser) = @_; |
|---|
| 602 |
|
|---|
| 603 |
|
|---|
| 604 |
$self->{_currentKeys}->{$parser} = $parser->nextGroup(); |
|---|
| 605 |
|
|---|
| 606 |
|
|---|
| 607 |
if (!defined($self->{_currentKeys}->{$parser})) { |
|---|
| 608 |
$LOG->fatal("Error: Unable to read registry keys from '" . |
|---|
| 609 |
$self->{_filenames}->{$parser} . "'."); |
|---|
| 610 |
Carp::croak("Error: Unable to read registry keys from '" . |
|---|
| 611 |
$self->{_filenames}->{$parser} . "'."); |
|---|
| 612 |
} |
|---|
| 613 |
|
|---|
| 614 |
|
|---|
| 615 |
|
|---|
| 616 |
if (!%{$self->{_currentKeys}->{$parser}}) { |
|---|
| 617 |
return undef; |
|---|
| 618 |
} |
|---|
| 619 |
|
|---|
| 620 |
|
|---|
| 621 |
|
|---|
| 622 |
$self->{_currentEntryIndex}->{$parser} = 0; |
|---|
| 623 |
|
|---|
| 624 |
|
|---|
| 625 |
return $self->{_currentKeys}->{$parser}->{'key'}; |
|---|
| 626 |
} |
|---|
| 627 |
|
|---|
| 628 |
|
|---|
| 629 |
|
|---|
| 630 |
|
|---|
| 631 |
|
|---|
| 632 |
|
|---|
| 633 |
|
|---|
| 634 |
|
|---|
| 635 |
sub _nextVal { |
|---|
| 636 |
my ($self, $parser) = @_; |
|---|
| 637 |
|
|---|
| 638 |
|
|---|
| 639 |
my ($k) = $self->{_currentKeys}->{$parser}; |
|---|
| 640 |
|
|---|
| 641 |
|
|---|
| 642 |
my ($i) = $self->{_currentEntryIndex}->{$parser}; |
|---|
| 643 |
|
|---|
| 644 |
|
|---|
| 645 |
if ($i >= @{$k->{'entries'}}) { |
|---|
| 646 |
return undef; |
|---|
| 647 |
} |
|---|
| 648 |
|
|---|
| 649 |
|
|---|
| 650 |
$self->{_currentEntryIndex}->{$parser} = $i + 1; |
|---|
| 651 |
|
|---|
| 652 |
|
|---|
| 653 |
return (${$k->{'entries'}}[$i]->{'name'}, |
|---|
| 654 |
${$k->{'entries'}}[$i]->{'value'}); |
|---|
| 655 |
} |
|---|
| 656 |
|
|---|
| 657 |
|
|---|
| 658 |
|
|---|
| 659 |
|
|---|
| 660 |
|
|---|
| 661 |
|
|---|
| 662 |
|
|---|
| 663 |
|
|---|
| 664 |
|
|---|
| 665 |
|
|---|
| 666 |
|
|---|
| 667 |
|
|---|
| 668 |
sub _diff { |
|---|
| 669 |
|
|---|
| 670 |
my ($self, $src_parser, $tgt_parser) = @_; |
|---|
| 671 |
|
|---|
| 672 |
my $src_linenums = []; |
|---|
| 673 |
my $tgt_linenums = []; |
|---|
| 674 |
my $diff_types = []; |
|---|
| 675 |
|
|---|
| 676 |
|
|---|
| 677 |
my $src_filename = $self->{_filenames}->{$src_parser}; |
|---|
| 678 |
my $tgt_filename = $self->{_filenames}->{$tgt_parser}; |
|---|
| 679 |
|
|---|
| 680 |
my $fname_tmp = tmpnam(); |
|---|
| 681 |
$LOG->debug("Creating temporary file '" . $fname_tmp . "' to perform differential analysis."); |
|---|
| 682 |
|
|---|
| 683 |
|
|---|
| 684 |
|
|---|
| 685 |
|
|---|
| 686 |
system("((diff --speed-large-files \"" . $src_filename . "\" \"" . $tgt_filename . "\" && " . |
|---|
| 687 |
"echo \"0NOCHANGES\") | " . |
|---|
| 688 |
"grep -e '^[0-9].*' || echo \"FAILURE\") > " . $fname_tmp . " 2>/dev/null"); |
|---|
| 689 |
|
|---|
| 690 |
my $fh = new IO::File($fname_tmp, "r"); |
|---|
| 691 |
if (!defined($fh)) { |
|---|
| 692 |
$LOG->fatal("Error: Unable to read file '" . $fname_tmp . "'!"); |
|---|
| 693 |
Carp::croak("Error: Unable to read file '" . $fname_tmp . "'!"); |
|---|
| 694 |
} |
|---|
| 695 |
|
|---|
| 696 |
|
|---|
| 697 |
$/ = "\n"; |
|---|
| 698 |
$_ = <$fh>; |
|---|
| 699 |
|
|---|
| 700 |
if (defined($_)) { |
|---|
| 701 |
if ($_ eq "0NOCHANGES\n") { |
|---|
| 702 |
$LOG->info("No changes detected in specified data."); |
|---|
| 703 |
_cleanup($fname_tmp); |
|---|
| 704 |
return ($src_linenums, $tgt_linenums); |
|---|
| 705 |
} |
|---|
| 706 |
if ($_ eq "FAILURE\n") { |
|---|
| 707 |
|
|---|
| 708 |
_cleanup($fname_tmp); |
|---|
| 709 |
$LOG->fatal("Error: Unable to diff '" . $src_filename . "' against '" . $tgt_filename ."'."); |
|---|
| 710 |
Carp::croak("Error: Unable to diff '" . $src_filename . "' against '" . $tgt_filename ."'."); |
|---|
| 711 |
} |
|---|
| 712 |
} |
|---|
| 713 |
|
|---|
| 714 |
do { |
|---|
| 715 |
if (/([0-9]+)(?:|,[0-9]+)([a-z])([0-9]+)/) { |
|---|
| 716 |
push (@{$src_linenums}, $1); |
|---|
| 717 |
push (@{$diff_types}, $2); |
|---|
| 718 |
push (@{$tgt_linenums}, $3); |
|---|
| 719 |
} |
|---|
| 720 |
} while (<$fh>); |
|---|
| 721 |
|
|---|
| 722 |
_cleanup($fname_tmp); |
|---|
| 723 |
return ($src_linenums, $tgt_linenums, $diff_types); |
|---|
| 724 |
} |
|---|
| 725 |
|
|---|
| 726 |
|
|---|
| 727 |
|
|---|
| 728 |
|
|---|
| 729 |
|
|---|
| 730 |
sub _cleanup { |
|---|
| 731 |
my $tmpfile = shift; |
|---|
| 732 |
undef $/; |
|---|
| 733 |
if (!unlink($tmpfile)) { |
|---|
| 734 |
$LOG->fatal("Error: Unable to delete temporary file '" . $tmpfile ."'."); |
|---|
| 735 |
Carp::croak("Error: Unable to delete temporary file '" . $tmpfile ."'."); |
|---|
| 736 |
} |
|---|
| 737 |
} |
|---|
| 738 |
|
|---|
| 739 |
|
|---|
| 740 |
|
|---|
| 741 |
|
|---|
| 742 |
|
|---|
| 743 |
|
|---|
| 744 |
|
|---|
| 745 |
sub _filter { |
|---|
| 746 |
|
|---|
| 747 |
my ($self, $changes) = @_; |
|---|
| 748 |
|
|---|
| 749 |
|
|---|
| 750 |
my $filteredChanges = [ ]; |
|---|
| 751 |
|
|---|
| 752 |
|
|---|
| 753 |
my $changeFiltered = 0; |
|---|
| 754 |
|
|---|
| 755 |
foreach my $change (@{$changes}) { |
|---|
| 756 |
$changeFiltered = 0; |
|---|
| 757 |
foreach my $criteria (@{$self->{'key_dirnames_to_ignore'}}) { |
|---|
| 758 |
if ($change->{'key'} =~ /$criteria/) { |
|---|
| 759 |
$changeFiltered = 1; |
|---|
| 760 |
last; |
|---|
| 761 |
} |
|---|
| 762 |
} |
|---|
| 763 |
if (!$changeFiltered) { |
|---|
| 764 |
push (@{$filteredChanges}, $change); |
|---|
| 765 |
} |
|---|
| 766 |
} |
|---|
| 767 |
|
|---|
| 768 |
return $filteredChanges; |
|---|
| 769 |
} |
|---|
| 770 |
|
|---|
| 771 |
|
|---|
| 772 |
|
|---|
| 773 |
|
|---|
| 774 |
|
|---|
| 775 |
|
|---|
| 776 |
|
|---|
| 777 |
|
|---|
| 778 |
|
|---|
| 779 |
|
|---|
| 780 |
|
|---|
| 781 |
sub _search { |
|---|
| 782 |
|
|---|
| 783 |
my ($self, $value_to_compare, $current_array_index) = @_; |
|---|
| 784 |
|
|---|
| 785 |
|
|---|
| 786 |
if (defined($current_array_index)) { |
|---|
| 787 |
$self->{'_last_search_index'} = $current_array_index; |
|---|
| 788 |
} else { |
|---|
| 789 |
$self->{'_last_search_index'}++; |
|---|
| 790 |
} |
|---|
| 791 |
|
|---|
| 792 |
|
|---|
| 793 |
if (defined(@{$self->{'_group_index_linenums'}}[$self->{'_last_search_index'}])) { |
|---|
| 794 |
return($value_to_compare <=> @{$self->{'_group_index_linenums'}}[$self->{'_last_search_index'}], |
|---|
| 795 |
$self->{'_last_search_index'}); |
|---|
| 796 |
} |
|---|
| 797 |
|
|---|
| 798 |
|
|---|
| 799 |
return (undef, $self->{'_last_search_index'}); |
|---|
| 800 |
} |
|---|
| 801 |
|
|---|
| 802 |
|
|---|
| 803 |
|
|---|
| 804 |
|
|---|
| 805 |
|
|---|
| 806 |
|
|---|
| 807 |
sub _compare { |
|---|
| 808 |
|
|---|
| 809 |
|
|---|
| 810 |
my ($self, %args) = @_; |
|---|
| 811 |
|
|---|
| 812 |
my $before_parser = $args{'before_parser'}; |
|---|
| 813 |
my $after_parser = $args{'after_parser'}; |
|---|
| 814 |
|
|---|
| 815 |
|
|---|
| 816 |
my $currentChange = { }; |
|---|
| 817 |
|
|---|
| 818 |
|
|---|
| 819 |
my $currentChangeExists = 0; |
|---|
| 820 |
|
|---|
| 821 |
|
|---|
| 822 |
|
|---|
| 823 |
|
|---|
| 824 |
my $changes = []; |
|---|
| 825 |
|
|---|
| 826 |
|
|---|
| 827 |
|
|---|
| 828 |
|
|---|