HoneyClient::Agent::Integrity - Perl extension to perform configurable


HoneyClient-Agent documentation Contained in the HoneyClient-Agent distribution.

Index


Code Index:

NAME

Top

HoneyClient::Agent::Integrity - Perl extension to perform configurable integrity checks of the Agent VM OS.

VERSION

Top

This documentation refers to HoneyClient::Agent::Integrity version 0.98.

SYNOPSIS

Top

  use HoneyClient::Agent::Integrity;
  use Data::Dumper;

  # Create the Integrity object.  Upon creation, the object will
  # be initialized, by performing a baseline of the Agent VM OS.
  my $integrity = HoneyClient::Agent::Integrity->new();

  # ... Some time elapses ...

  # Check the Agent VM, for any violations.
  my $changes = $integrity->check();

  if (!defined($changes)) {
      print "No integrity violations have occurred.\n";
  } else {
      print "System integrity has been compromised:\n";
      print Dumper($changes);
  }

  # $changes refers to an array of hashtable references, where
  # each hashtable has the following format:
  #
  # $changes = {
  #     registry => [ {
  #         # The registry directory name.
  #         'key' => 'HKEY_LOCAL_MACHINE\Software...',
  #
  #         # Indicates if the registry directory was deleted,
  #         # added, or changed.
  #         'status' => 'deleted' | 'added' | 'changed',
  # 
  #         # An array containing the list of entries within the
  #         # registry directory that have been deleted, added, or
  #         # changed.  If this array is empty, then the corresponding
  #         # registry directory in the original and new hives contained
  #         # no entries.
  #         'entries'  => [ {
  #             'name' => "\"string\"",  # A (potentially) quoted string; 
  #                                      # "@" for default
  #             'new_value' => "string", # New string; maybe undef, if deleted
  #             'old_value' => "string", # Old string; maybe undef, if added
  #         }, ],
  #    }, ],
  #
  #    filesystem => [ {
  #         # Indicates if the filesystem entry was deleted,
  #         # added, or changed.
  #         'status' => 'deleted' | 'added' | 'changed',
  #
  #         # If the entry has been added/changed, then this 
  #         # hashtable contains the file/directory's new information.
  #         'new' => {
  #             'name'  => 'C:\WINDOWS\SYSTEM32...',
  #             'size'  => 1263, # in bytes
  #             'mtime' => 1178135092, # modification time, seconds since epoch
  #         },
  #
  #         # If the entry has been deleted/changed, then this
  #         # hashtable contains the file/directory's old information.
  #         'old' => {
  #             'name'  => 'C:\WINDOWS\SYSTEM32...',
  #             'size'  => 802, # in bytes
  #             'mtime' => 1178135028, # modification time, seconds since epoch
  #         },
  #   }, ],
  # }

DESCRIPTION

Top

# TODO: This text needs to change.

INITIALIZATION

# TODO: This text needs to change.

In order to properly check the system, a snapshot must be taken of a known-good state.

For the filesystem this means a listing is created which contains cryptographic hashes of files in their start state. The only files what are checked are those which are explicitly specified in the checklist file (or are found in a specified directory) and are not in the exclusion list will be checked. Initialization of the filesystem is done with the initFileSystem() function, described later.

For the registry a similar logic applies in that the only the specified keys are checked and only if they are not in the exclusion list. The desired registry keys are exported to a text file via the command line functionality of regedit. This is done via initRegistry().

CHECKING

# TODO: This text needs to change.

Checking the filesystem entails running mostly the same code as the initialization piece in order to obtain a snapshot of the current state of the filesystem. At that point additional checks are performed to look for additions, deletions, and modifications to the filesystem. These checks are done with checkFileSystem().

A speed-optimized check of the registry is performed by first dumping the current state, again with the command line version of regedit. Then the unix "diff" utility is used to compare the clean registry dump to the current one. The output from a diff is in a format which shows the minimum possible changes which can be done to the first file in order to yield the same content as the second file. Therefore this format must be parsed in order to determine what specific additions, deletions, and modifications were made to the clean registry. Further, because the output of diff need not exactly reflect changes (for instance when the same content would be the first line of the previous value and the last line of the new value) this requires some cases to re-consult the original and current state in order to disambiguate the changes which were made. These tests are done in checkRegistry().

NOTE: Because these are simple, static, user-space checks, they can fail in the presense of even user-space rootkits. Therefore these checks should not be taken as definitive proof of the absense of malicious software until they are integrated more tightly with the system.

DEFAULT PARAMETER LIST

Top

When an Integrity $object is instantiated using the new() function, the following parameters are supplied default values. Each value can be overridden by specifying the new (key => value) pair into the new() function, as arguments.

bypass_baseline

When set to 1, the object will forgo any type of initial baselining process, upon initialization. Otherwise, baselining will occur as normal, upon initialization.

METHODS IMPLEMENTED

Top

The following functions have been implemented by any Integrity object.

HoneyClient::Agent::Integrity->new($param => $value, ...)

Creates a new Integrity object, which contains a hashtable containing any of the supplied "param => value" arguments. Upon creation, the Integrity object initializes all of its sub-modules, by creating a baseline of the operating system.

Inputs:$param is an optional parameter variable.$value is $param's corresponding value.

Note: If any $param(s) are supplied, then an equal number of corresponding $value(s) must also be specified.

Output: The instantiated Integrity $object, fully initialized.

$object->check()

Checks the operating system for various changes, based upon the baseline snapshot of the system, when the new() method was invoked.

Output:$changes, which is an array of hashtable references, where each hashtable has the following format:

  $changes = {
      registry => [ {
          # The registry directory name.
          'key' => 'HKEY_LOCAL_MACHINE\Software...',

          # Indicates if the registry directory was deleted,
          # added, or changed.
          'status' => 'deleted' | 'added' | 'changed',

          # An array containing the list of entries within the
          # registry directory that have been deleted, added, or
          # changed.  If this array is empty, then the corresponding
          # registry directory in the original and new hives contained
          # no entries.
          'entries'  => [ {
              'name' => "\"string\"",  # A (potentially) quoted string; 
                                       # "@" for default
              'new_value' => "string", # New string; maybe undef, if deleted
              'old_value' => "string", # Old string; maybe undef, if added
          }, ],
      }, ],

      filesystem => [ {
          # Indicates if the filesystem entry was deleted,
          # added, or changed.
          'status' => 'deleted' | 'added' | 'changed',

          # If the entry has been added/changed, then this 
          # hashtable contains the file/directory's new information.
          'new' => {
              'name'  => 'C:\WINDOWS\SYSTEM32...',
              'size'  => 1263, # in bytes
              'mtime' => 1178135092, # modification time, seconds since epoch
          },

          # If the entry has been deleted/changed, then this
          # hashtable contains the file/directory's old information.
          'old' => {
              'name'  => 'C:\WINDOWS\SYSTEM32...',
              'size'  => 802, # in bytes
              'mtime' => 1178135028, # modification time, seconds since epoch
          },
      }, ],
  }

Notes:

#=begin testing # # TODO: # #=end testing

BUGS & ASSUMPTIONS

Top

# XXX: Fill this in.

TODO

Top

Need to add sub-modules that support the following capabilities:

SEE ALSO

Top

http://www.honeyclient.org/trac

REPORTING BUGS

Top

http://www.honeyclient.org/trac/newticket

ACKNOWLEDGEMENTS

Top

XXX: Fill this in.

AUTHORS

Top

Kathy Wang, <knwang@mitre.org>

Xeno Kovah, <xkovah@mitre.org>

Thanh Truong, <ttruong@mitre.org>

Darien Kindlund, <kindlund@mitre.org>

Brad Stephenson, <stephenson@mitre.org>

COPYRIGHT & LICENSE

Top


HoneyClient-Agent documentation Contained in the HoneyClient-Agent distribution.
################################################################################
# Created on:  June 01, 2006
# Package:     HoneyClient::Agent::Integrity
# File:        Integrity.pm
# Description: Module for checking the system integrity for possible
#              modifications.
#
# CVS: $Id: Integrity.pm 773 2007-07-26 19:04:55Z kindlund $
#
# @author knwang, xkovah, ttruong, kindlund, stephenson
#
# Copyright (C) 2007 The MITRE Corporation.  All rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, using version 2
# of the License.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
################################################################################

package HoneyClient::Agent::Integrity;

use strict;
use warnings;
use Carp ();


# Include Global Configuration Processing Library
use HoneyClient::Util::Config qw(getVar);

# Include the Registry Checking Library
use HoneyClient::Agent::Integrity::Registry;

# Include the Filesystem Checking Library
use HoneyClient::Agent::Integrity::Filesystem;

# Use Storable Library
use Storable qw(nfreeze thaw dclone);
$Storable::Deparse = 1;
$Storable::Eval = 1;

# Use Dumper Library
use Data::Dumper;

# Include Logging Library
use Log::Log4perl qw(:easy);

#######################################################################
# Module Initialization                                               #
#######################################################################

BEGIN {
    # Defines which functions can be called externally.
    require Exporter;
    our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);

    # Set our package version.
    $VERSION = 0.98;

    @ISA = qw(Exporter);

    # Symbols to export automatically
    @EXPORT = qw( );

    # Items to export into callers namespace by default. Note: do not export
    # names by default without a very good reason. Use EXPORT_OK instead.
    # Do not simply export all your public functions/methods/constants.

    # This allows declaration use HoneyClient::Agent::Integrity ':all';
    # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
    # will save memory.

    %EXPORT_TAGS = (
        'all' => [ qw( ) ],
    );

    # Symbols to autoexport (when qw(:all) tag is used)
    @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

    $SIG{PIPE} = 'IGNORE'; # Do not exit on broken pipes.
}
our (@EXPORT_OK, $VERSION);

#######################################################################
# Global Configuration Variables                                      #
#######################################################################

# The global logging object.
our $LOG = get_logger();

my %PARAMS = (
    # When set to 1, the object will forgo any type of initial baselining
    # process, upon initialization.  Otherwise, baselining will occur
    # as normal, upon initialization.
    bypass_baseline => 0,

    # Contains the Registry object, once initialized.
    # (For internal use only.)
    _registry => undef,

    # Contains the Filesystem object, once initialized.
    # (For internal use only.)
    _filesystem => undef,

    # XXX: comment this
    _changes_found_file => getVar(name => 'changes_found_file'),
);

#######################################################################
# Private Methods Implemented                                         #
#######################################################################

# Helper function, designed to baseline the system.
# 
# Inputs: None.
# Outputs: None.
sub _baseline {
    my $self = shift;
    # XXX: The Registry object MUST be created before the Filesystem object, since
    # the Registry object creates new files that must exist to be added to the
    # Filesystem's baseline list of files that exist on the system.
	$self->{'_registry'} = HoneyClient::Agent::Integrity::Registry->new();
    $self->{'_filesystem'} = HoneyClient::Agent::Integrity::Filesystem->new();
}

#######################################################################
# Public Methods Implemented                                          #
#######################################################################

sub new {
    # - This function takes in an optional hashtable,
    #   that contains various key => 'value' configuration
    #   parameters.
    #
    # - For each parameter given, it overwrites any corresponding
    #   parameters specified within the default hashtable, %PARAMS, 
    #   with custom entries that were given as parameters.
    #
    # - Finally, it returns a blessed instance of the
    #   merged hashtable, as an 'object'.

    # Get the class name.
    my $self = shift;

    # Get the rest of the arguments, as a hashtable.
    # Hash-based arguments are used, since HoneyClient::Util::SOAP is unable to handle
    # hash references directly.  Thus, flat hashtables are used throughout the code
    # for consistency.
    my %args = @_;

    # Check to see if the class name is inherited or defined.
    my $class = ref($self) || $self;

    # Initialize default parameters.
    $self = { };
    my %params = %{dclone(\%PARAMS)};
    @{$self}{keys %params} = values %params;

    # Now, overwrite any default parameters that were redefined
    # in the supplied arguments.
    @{$self}{keys %args} = values %args;

    # Now, assign our object the appropriate namespace.
    bless $self, $class;

    # Perform baselining, if not bypassed.
    if (!$self->{'bypass_baseline'}) {
        $LOG->info("Baselining system.");
        $self->_baseline();
    }

    # Finally, return the blessed object.
    return $self;
}


sub check {

    # Extract arguments.
    my ($self, %args) = @_;

    # Sanity check: Make sure we've been fed an object.
    unless (ref($self)) {
        $LOG->error("Error: Function must be called in reference to a " .
                    __PACKAGE__ . "->new() object!");
        Carp::croak "Error: Function must be called in reference to a " .
                    __PACKAGE__ . "->new() object!\n";
    }

    # Log resolved arguments.
    $LOG->debug(sub {
        # Make Dumper format more terse.
        $Data::Dumper::Terse = 1;
        $Data::Dumper::Indent = 0;
        Dumper(\%args);
    });

	my $changes = {
        'registry' => $self->{'_registry'}->check(),
        'filesystem' => $self->{'_filesystem'}->check(),
    };

    # If any changes were found, write them out to the
    # filesystem.
    if (scalar(@{$changes->{registry}}) ||
        scalar(@{$changes->{filesystem}})) {
        if (!open(CHANGE_FILE, ">>" . $self->{_changes_found_file})) {
            $LOG->error("Unable to write changes to file '" . $self->{_changes_found_file} . "'.");
        } else {
            $Data::Dumper::Terse = 1;
            $Data::Dumper::Indent = 1;
            print CHANGE_FILE Dumper($changes);
            close CHANGE_FILE;
        }
    }

	return $changes;
}

# TODO: Comment this.
sub closeFiles {
    my $self = shift;

    # Sanity check: Make sure we've been fed an object.
    unless (ref($self)) {
        $LOG->error("Error: Function must be called in reference to a " .
                    __PACKAGE__ . "->new() object!");
        Carp::croak "Error: Function must be called in reference to a " .
                    __PACKAGE__ . "->new() object!\n";
    }

    if (defined($self->{'_registry'})) {
        $self->{'_registry'}->closeFiles();
    }
}

# TODO: Comment this.
sub destroy {
    my $self = shift;

    # Sanity check: Make sure we've been fed an object.
    unless (ref($self)) {
        $LOG->error("Error: Function must be called in reference to a " .
                    __PACKAGE__ . "->new() object!");
        Carp::croak "Error: Function must be called in reference to a " .
                    __PACKAGE__ . "->new() object!\n";
    }

    if (defined($self->{'_registry'})) {
        $self->{'_registry'}->destroy();
    }
}

1;