diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index a88ad8cad85..7979351fc33 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -180,7 +180,7 @@ class ExtractRegistry extends Extract { final private static String RIP_PL_INCLUDE_FLAG = "-I"; final private static int MS_IN_SEC = 1000; final private static String NEVER_DATE = "Never"; - final private static String SECTION_DIVIDER = "-------------------------"; + final private static String SECTION_DIVIDER = "----------------------------------------"; final private static Logger logger = Logger.getLogger(ExtractRegistry.class.getName()); private final List rrCmd = new ArrayList<>(); private final List rrFullCmd = new ArrayList<>(); @@ -1200,12 +1200,10 @@ private void createRecentlyUsedArtifacts(String regFileName, AbstractFile regFil while (line != null) { line = line.trim(); - if (line.matches("^adoberdr v.*")) { + if (line.matches("^adobe v.*")) { parseAdobeMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Adobe()); } else if (line.matches("^mpmru v.*")) { parseMediaPlayerMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mediaplayer()); - } else if (line.matches("^trustrecords v.*")) { - parseOfficeTrustRecords(regFile, reader, Bundle.Recently_Used_Artifacts_Office_Trustrecords()); } else if (line.matches("^ArcHistory:")) { parse7ZipMRU(regFile, reader, Bundle.Recently_Used_Artifacts_ArcHistory()); } else if (line.matches("^applets v.*")) { @@ -1214,7 +1212,7 @@ private void createRecentlyUsedArtifacts(String regFileName, AbstractFile regFil parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mmc()); } else if (line.matches("^winrar v.*")) { parseWinRARMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Winrar()); - } else if (line.matches("^officedocs2010 v.*")) { + } else if (line.matches("^msoffice v.*")) { parseOfficeDocs2010MRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Officedocs()); } line = reader.readLine(); @@ -1309,7 +1307,7 @@ private void parseAdobeMRUList(AbstractFile regFile, BufferedReader reader, Stri line = reader.readLine(); // Columns are // Key name, file name, sDate, uFileSize, uPageCount - while (!line.contains(SECTION_DIVIDER)) { + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty()) { // Split csv line, handles double quotes around individual file names // since file names can contain commas String tokens[] = line.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); @@ -1591,7 +1589,22 @@ private void parseOfficeDocs2010MRUList(AbstractFile regFile, BufferedReader rea String tokens[] = line.split("\\|"); Long docDate = Long.valueOf(tokens[0]); String fileNameTokens[] = tokens[4].split(" - "); - String fileName = fileNameTokens[1]; + if (fileNameTokens[0].contains("MSOffice LastLoginTime")) { + line = reader.readLine(); + line = line.trim(); + continue; + } + String fileName; + if (fileNameTokens.length > 2) { + fileName = fileNameTokens[2]; + } else { + fileName = fileNameTokens[1]; + } + if (line.contains(" MRU ")) { + comment = Bundle.Recently_Used_Artifacts_Officedocs(); + } else { + comment = Bundle.Recently_Used_Artifacts_Office_Trustrecords(); + } Collection attributes = new ArrayList<>(); attributes.add(new BlackboardAttribute(TSK_PATH, getDisplayName(), fileName)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getDisplayName(), docDate)); @@ -1614,71 +1627,6 @@ private void parseOfficeDocs2010MRUList(AbstractFile regFile, BufferedReader rea } } - /** - * Create recently used artifacts to parse the Office trust records - * (trustrecords) Regipper plugin records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseOfficeTrustRecords(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - String userProfile = regFile.getParentPath(); - userProfile = userProfile.substring(0, userProfile.length() - 1); - List bbartifacts = new ArrayList<>(); - SimpleDateFormat pluginDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", US); - Long usedTime = Long.valueOf(0); - String line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - usedTime = Long.valueOf(0); - if (!line.contains("**") && !line.contains("----------") && !line.contains("LastWrite") - && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("TrustRecords") - && !line.contains("VBAWarnings =")) { - // Columns are - // Date : / - // Split line on " : " which is the record delimiter between position and file - String fileName = null; - String tokens[] = line.split(" : "); - fileName = tokens[1]; - fileName = fileName.replace("%USERPROFILE%", userProfile); - // Time in the format of Wed May 31 14:33:03 2017 Z - try { - String fileUsedTime = tokens[0].replaceAll(" Z", ""); - Date usedDate = pluginDateFormat.parse(fileUsedTime); - usedTime = usedDate.getTime() / 1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for TrustRecords artifact.", tokens[0]), ex); //NON-NLS - } - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getDisplayName(), fileName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getDisplayName(), usedTime)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getDisplayName(), comment)); - try { - BlackboardArtifact bba = createArtifactWithAttributes(BlackboardArtifact.Type.TSK_RECENT_OBJECT, regFile, attributes); - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex); - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) { - postArtifacts(bbartifacts); - } - } - /** * Create associated artifacts using file name and path and the artifact it * associates with diff --git a/thirdparty/rr-full/.gitattributes b/thirdparty/rr-full/.gitattributes new file mode 100644 index 00000000000..dfe0770424b --- /dev/null +++ b/thirdparty/rr-full/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/thirdparty/rr-full/Base.pm b/thirdparty/rr-full/Base.pm deleted file mode 100644 index 81b60eec209..00000000000 --- a/thirdparty/rr-full/Base.pm +++ /dev/null @@ -1,1119 +0,0 @@ -package Parse::Win32Registry::Base; - -use strict; -use warnings; - -use base qw(Exporter); - -use Carp; -use Encode; -use Time::Local qw(timegm); - -our @EXPORT_OK = qw( - warnf - iso8601 - hexdump - format_octets - unpack_windows_time - unpack_string - unpack_unicode_string - unpack_guid - unpack_sid - unpack_ace - unpack_acl - unpack_security_descriptor - unpack_series - make_multiple_subkey_iterator - make_multiple_value_iterator - make_multiple_subtree_iterator - compare_multiple_keys - compare_multiple_values - REG_NONE - REG_SZ - REG_EXPAND_SZ - REG_BINARY - REG_DWORD - REG_DWORD_BIG_ENDIAN - REG_LINK - REG_MULTI_SZ - REG_RESOURCE_LIST - REG_FULL_RESOURCE_DESCRIPTOR - REG_RESOURCE_REQUIREMENTS_LIST - REG_QWORD -); - -our %EXPORT_TAGS = ( - all => [@EXPORT_OK], -); - -use constant REG_NONE => 0; -use constant REG_SZ => 1; -use constant REG_EXPAND_SZ => 2; -use constant REG_BINARY => 3; -use constant REG_DWORD => 4; -use constant REG_DWORD_BIG_ENDIAN => 5; -use constant REG_LINK => 6; -use constant REG_MULTI_SZ => 7; -use constant REG_RESOURCE_LIST => 8; -use constant REG_FULL_RESOURCE_DESCRIPTOR => 9; -use constant REG_RESOURCE_REQUIREMENTS_LIST => 10; -use constant REG_QWORD => 11; - -our $WARNINGS = 0; - -our $CODEPAGE = 'cp1252'; - -sub warnf { - my $message = shift; - warn sprintf "$message\n", @_ if $WARNINGS; -} - -sub hexdump { - my $data = shift; # packed binary data - my $start = shift || 0; # starting value for displayed offset - - return '' if !defined($data); - - my $output = ''; - - my $fake_start = $start & ~0xf; - my $end = length($data); - - my $pos = 0; - if ($fake_start < $start) { - $output .= sprintf '%8x ', $fake_start; - my $indent = $start - $fake_start; - $output .= ' ' x $indent; - my $row = substr($data, $pos, 16 - $indent); - my $len = length($row); - $output .= join(' ', unpack('H2' x $len, $row)); - if ($indent + $len < 16) { - my $padding = 16 - $len - $indent; - $output .= ' ' x $padding; - } - $output .= ' '; - $output .= ' ' x $indent; - $row =~ tr/\x20-\x7e/./c; - $output .= $row; - $output .= "\n"; - $pos += $len; - } - while ($pos < $end) { - $output .= sprintf '%8x ', $start + $pos; - my $row = substr($data, $pos, 16); - my $len = length($row); - $output .= join(' ', unpack('H2' x $len, $row)); - if ($len < 16) { - my $padding = 16 - $len; - $output .= ' ' x $padding; - } - $output .= ' '; - $row =~ tr/\x20-\x7e/./c; - $output .= $row; - $output .= "\n"; - $pos += 16; - } - - return $output; -} - -sub format_octets { - my $data = shift; # packed binary data - my $col = shift || 0; # starting column, e.g. length of initial string - - return "\n" if !defined($data); - - my $output = ''; - - $col = 76 if $col > 76; - my $max_octets = int((76 - $col) / 3) + 1; - - my $end = length($data); - my $pos = 0; - my $num_octets = $end - $pos; - $num_octets = $max_octets if $num_octets > $max_octets; - while ($pos < $end) { - $output .= join(',', unpack("x$pos(H2)$num_octets", $data)); - $pos += $num_octets; - $num_octets = $end - $pos; - $num_octets = 25 if $num_octets > 25; - if ($num_octets > 0) { - $output .= ",\\\n "; - } - } - $output .= "\n"; - return $output; -} - -sub unpack_windows_time { - my $data = shift; - - if (!defined $data) { - return; - } - - if (length($data) < 8) { - return; - } - - # The conversion uses real numbers - # as 32-bit perl does not provide 64-bit integers. - # The equation can be found in several places on the Net. - # My thanks go to Dan Sully for Audio::WMA's _fileTimeToUnixTime - # which shows a perl implementation of it. - my ($lo, $hi) = unpack("VV", $data); -# my $filetime = $high * 2 ** 32 + $low; -# my $epoch_time = int(($filetime - 116444736000000000) / 10000000); - - my $epoch_time; - - if ($lo == 0 && $hi == 0) { - $epoch_time = 0; - } else { - $lo -= 0xd53e8000; - $hi -= 0x019db1de; - $epoch_time = int($hi*429.4967296 + $lo/1e7); - }; - $epoch_time = 0 if ($epoch_time < 0); - - - # adjust the UNIX epoch time to the local OS's epoch time - # (see perlport's Time and Date section) - # my $epoch_offset = timegm(0, 0, 0, 1, 0, 70); - # $epoch_time += $epoch_offset; - - if ($epoch_time < 0 || $epoch_time > 0x7fffffff) { - $epoch_time = undef; - } - - return wantarray ? ($epoch_time, 8) : $epoch_time; -} - -sub iso8601 { - my $time = shift; - my $tz = shift; - - if (!defined $time) { - return '(undefined)'; - } - - if (!defined $tz || $tz ne 'Z') { - $tz = 'Z' - } - - # On Windows, gmtime will return undef if $time < 0 or > 0x7fffffff - if ($time < 0 || $time > 0x7fffffff) { - return '(undefined)'; - } - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime $time; - - # The final 'Z' indicates UTC ("zero meridian") - return sprintf '%04d-%02d-%02dT%02d:%02d:%02d%s', - 1900+$year, 1+$mon, $mday, $hour, $min, $sec, $tz; -} - -sub unpack_string { - my $data = shift; - - if (!defined $data) { - return; - } - - my $str; - my $str_len; - if ((my $end = index($data, "\0")) != -1) { - $str = substr($data, 0, $end); - $str_len = $end + 1; # include the final null in the length - } - else { - $str = $data; - $str_len = length($data); - } - - return wantarray ? ($str, $str_len) : $str; -} - -sub unpack_unicode_string { - my $data = shift; - - if (!defined $data) { - return; - } - - my $str_len = 0; - foreach my $v (unpack('v*', $data)) { - $str_len += 2; - last if $v == 0; # include the final null in the length - } - my $str = decode('UCS-2LE', substr($data, 0, $str_len)); - - # The decode function from Encode may create invalid unicode characters - # which cause subsequent warnings (e.g. during regex matching). - # For example, characters in the 0xd800 to 0xdfff range of the - # basic multilingual plane (0x0000 to 0xffff) are 'surrogate pairs' - # and are expected to appear as a 'high surrogate' (0xd800 to 0xdbff) - # followed by a 'low surrogate' (0xdc00 to 0xdfff). - - # remove any final null - if (length($str) > 0 && substr($str, -1, 1) eq "\0") { - chop $str; - } - - return wantarray ? ($str, $str_len) : $str; -} - -sub unpack_guid { - my $guid = Parse::Win32Registry::GUID->new($_[0]); - return if !defined $guid; - return wantarray ? ($guid, $guid->get_length) : $guid; -} - -sub unpack_sid { - my $sid = Parse::Win32Registry::SID->new($_[0]); - return if !defined $sid; - return wantarray ? ($sid, $sid->get_length) : $sid; -} - -sub unpack_ace { - my $ace = Parse::Win32Registry::ACE->new($_[0]); - return if !defined $ace; - return wantarray ? ($ace, $ace->get_length) : $ace; -} - -sub unpack_acl { - my $acl = Parse::Win32Registry::ACL->new($_[0]); - return if !defined $acl; - return wantarray ? ($acl, $acl->get_length) : $acl; -} - -sub unpack_security_descriptor { - my $sd = Parse::Win32Registry::SecurityDescriptor->new($_[0]); - return if !defined $sd; - return wantarray ? ($sd, $sd->get_length) : $sd; -} - -sub unpack_series { - my $function = shift; - my $data = shift; - - if (!defined $function || !defined $data) { - croak "Usage: unpack_series(\\\&unpack_function, \$data)"; - } - - my $pos = 0; - my @items = (); - while (my ($item, $item_len) = $function->(substr($data, $pos))) { - push @items, $item; - $pos += $item_len; - } - return @items; -} - -sub make_multiple_subkey_iterator { - my @keys = @_; - - # check @keys contains keys - if (@keys == 0 || - grep { defined && !UNIVERSAL::isa($_, 'Parse::Win32Registry::Key') } - @keys) { - croak 'Usage: make_multiple_subkey_iterator($key1, $key2, ...)'; - } - - my %subkeys_seen = (); - my @subkeys_queue; - for (my $i = 0; $i < @keys; $i++) { - my $key = $keys[$i]; - next if !defined $key; - foreach my $subkey ($key->get_list_of_subkeys) { - my $name = $subkey->get_name; - $subkeys_seen{$name}[$i] = $subkey; - } - } - foreach my $name (sort keys %subkeys_seen) { - # make sure number of subkeys matches number of keys - if (@{$subkeys_seen{$name}} != @keys) { - @{$subkeys_seen{$name}}[@keys - 1] = undef; - } - push @subkeys_queue, $subkeys_seen{$name}; - } - - return Parse::Win32Registry::Iterator->new(sub { - my $subkeys = shift @subkeys_queue; - if (defined $subkeys) { - return $subkeys; - } - else { - return; - } - }); -} - -sub make_multiple_value_iterator { - my @keys = @_; - - # check @keys contains keys - if (@keys == 0 || - grep { defined && !UNIVERSAL::isa($_, 'Parse::Win32Registry::Key') } - @keys) { - croak 'Usage: make_multiple_value_iterator($key1, $key2, ...)'; - } - - my %values_seen = (); - my @values_queue; - for (my $i = 0; $i < @keys; $i++) { - my $key = $keys[$i]; - next if !defined $key; - foreach my $value ($key->get_list_of_values) { - my $name = $value->get_name; - $values_seen{$name}[$i] = $value; - } - } - foreach my $name (sort keys %values_seen) { - # make sure number of values matches number of keys - if (@{$values_seen{$name}} != @keys) { - @{$values_seen{$name}}[@keys - 1] = undef; - } - push @values_queue, $values_seen{$name}; - } - - return Parse::Win32Registry::Iterator->new(sub { - my $values = shift @values_queue; - if (defined $values) { - return $values; - } - else { - return; - } - }); -} - -sub make_multiple_subtree_iterator { - my @keys = @_; - - # check @keys contains keys - if (@keys == 0 || - grep { defined && !UNIVERSAL::isa($_, 'Parse::Win32Registry::Key') } - @keys) { - croak 'Usage: make_multiple_subtree_iterator($key1, $key2, ...)'; - } - - my @start_keys = (\@keys); - push my (@subkey_iters), Parse::Win32Registry::Iterator->new(sub { - return shift @start_keys; - }); - my $value_iter; - my $subkeys; # used to remember subkeys while iterating values - - return Parse::Win32Registry::Iterator->new(sub { - if (defined $value_iter && wantarray) { - my $values = $value_iter->(); - if (defined $values) { - return ($subkeys, $values); - } - } - while (@subkey_iters > 0) { - $subkeys = $subkey_iters[-1]->(); # depth-first - if (defined $subkeys) { - push @subkey_iters, make_multiple_subkey_iterator(@$subkeys); - $value_iter = make_multiple_value_iterator(@$subkeys); - return $subkeys; - } - pop @subkey_iters; # iter finished, so remove it - } - return; - }); -} - -sub compare_multiple_keys { - my @keys = @_; - - # check @keys contains keys - if (@keys == 0 || - grep { defined && !UNIVERSAL::isa($_, 'Parse::Win32Registry::Key') } - @keys) { - croak 'Usage: compare_multiple_keys($key1, $key2, ...)'; - } - - my @changes = (); - - my $benchmark_key; - foreach my $key (@keys) { - my $diff = ''; - # Skip comparison for the first value - if (@changes > 0) { - $diff = _compare_keys($benchmark_key, $key); - } - $benchmark_key = $key; - push @changes, $diff; - } - return @changes; -} - -sub compare_multiple_values { - my @values = @_; - - # check @values contains values - if (@values == 0 || - grep { defined && !UNIVERSAL::isa($_, 'Parse::Win32Registry::Value') } - @values) { - croak 'Usage: compare_multiple_values($value1, $value2, ...)'; - } - - my @changes = (); - - my $benchmark_value; - foreach my $value (@values) { - my $diff = ''; - # Skip comparison for the first value - if (@changes > 0) { - $diff = _compare_values($benchmark_value, $value); - } - $benchmark_value = $value; - push @changes, $diff; - } - return @changes; -} - -sub _compare_keys { - my ($key1, $key2) = @_; - - if (!defined $key1 && !defined $key2) { - return ''; # 'MISSING' - } - elsif (defined $key1 && !defined $key2) { - return 'DELETED'; - } - elsif (!defined $key1 && defined $key2) { - return 'ADDED'; - } - - my $timestamp1 = $key1->get_timestamp; - my $timestamp2 = $key2->get_timestamp; - if ($key1->get_name ne $key2->get_name) { - return 'CHANGED'; - } - elsif (defined $timestamp1 && defined $timestamp2) { - if ($timestamp1 < $timestamp2) { - return 'NEWER'; - } - elsif ($timestamp1 > $timestamp2) { - return 'OLDER'; - } - } - else { - return ''; # comment out to check values... - my $value_iter = make_multiple_value_iterator($key1, $key2); - while (my ($val1, $val2) = $value_iter->get_next) { - if (_compare_values($val1, $val2) ne '') { - return 'VALUES'; - } - } - return ''; - } -} - -sub _compare_values { - my ($val1, $val2) = @_; - - if (!defined $val1 && !defined $val2) { - return ''; # 'MISSING' - } - elsif (defined $val1 && !defined $val2) { - return 'DELETED'; - } - elsif (!defined $val1 && defined $val2) { - return 'ADDED'; - } - - my $data1 = $val1->get_data; - my $data2 = $val2->get_data; - if ($val1->get_name ne $val2->get_name || - $val1->get_type != $val2->get_type || - defined $data1 ne defined $data2 || - (defined $data1 && defined $data2 && $data1 ne $data2)) { - return 'CHANGED'; - } - else { - return ''; - } -} - - -package Parse::Win32Registry::Iterator; - -use Carp; - -sub new { - my $class = shift; - my $self = shift; - - my $type = ref $self; - croak 'Missing iterator subroutine' if $type ne 'CODE' - && $type ne __PACKAGE__; - - bless $self, $class; - return $self; -} - -sub get_next { - $_[0]->(); -} - - -package Parse::Win32Registry::GUID; - -sub new { - my $class = shift; - my $data = shift; - - if (!defined $data) { - return; - } - - if (length($data) < 16) { - return; - } - - my $guid = sprintf '{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}', - unpack('VvvC2C6', $data); - - my $self = { - _guid => $guid, - _length => 16, - }; - bless $self, $class; - - return $self; -} - -sub as_string { - my $self = shift; - - return $self->{_guid}; -} - -sub get_length { - my $self = shift; - - return $self->{_length}; -} - - -package Parse::Win32Registry::SID; - -sub new { - my $class = shift; - my $data = shift; - - if (!defined $data) { - return; - } - - # 0x00 byte = revision - # 0x01 byte = number of sub authorities - # 0x07 byte = identifier authority - # 0x08 dword = 1st sub authority - # 0x0c dword = 2nd sub authority - # ... - - if (length($data) < 8) { - return; - } - - my ($rev, $num_sub_auths, $id_auth) = unpack('CCx5C', $data); - - if ($num_sub_auths == 0) { - return; - } - - my $sid_len = 8 + 4 * $num_sub_auths; - - if (length($data) < $sid_len) { - return; - } - - my @sub_auths = unpack("x8V$num_sub_auths", $data); - my $sid = "S-$rev-$id_auth-" . join('-', @sub_auths); - - my $self = { - _sid => $sid, - _length => $sid_len, - }; - bless $self, $class; - - return $self; -} - -# See KB243330 for a list of well known sids -our %WellKnownSids = ( - 'S-1-0-0' => 'Nobody', - 'S-1-1-0' => 'Everyone', - 'S-1-3-0' => 'Creator Owner', - 'S-1-3-1' => 'Creator Group', - 'S-1-3-2' => 'Creator Owner Server', - 'S-1-3-3' => 'Creator Group Server', - 'S-1-5-1' => 'Dialup', - 'S-1-5-2' => 'Network', - 'S-1-5-3' => 'Batch', - 'S-1-5-4' => 'Interactive', - 'S-1-5-5-\\d+-\\d+' => 'Logon Session', - 'S-1-5-6' => 'Service', - 'S-1-5-7' => 'Anonymous', - 'S-1-5-8' => 'Proxy', - 'S-1-5-9' => 'Enterprise Domain Controllers', - 'S-1-5-10' => 'Principal Self', - 'S-1-5-11' => 'Authenticated Users', - 'S-1-5-12' => 'Restricted Code', - 'S-1-5-13' => 'Terminal Server Users', - 'S-1-5-18' => 'Local System', - 'S-1-5-19' => 'Local Service', - 'S-1-5-20' => 'Network Service', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-500' => 'Administrator', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-501' => 'Guest', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-502' => 'KRBTGT', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-512' => 'Domain Admins', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-513' => 'Domain Users', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-514' => 'Domain Guests', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-515' => 'Domain Computers', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-516' => 'Domain Controllers', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-517' => 'Cert Publishers', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-518' => 'Schema Admins', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-519' => 'Enterprise Admins', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-520' => 'Group Policy Creator Owners', - 'S-1-5-\\d+-\\d+-\\d+-\\d+-533' => 'RAS and IAS Servers', - 'S-1-5-32-544' => 'Administrators', - 'S-1-5-32-545' => 'Users', - 'S-1-5-32-546' => 'Guest', - 'S-1-5-32-547' => 'Power Users', - 'S-1-5-32-548' => 'Account Operators', - 'S-1-5-32-549' => 'Server Operators', - 'S-1-5-32-550' => 'Print Operators', - 'S-1-5-32-551' => 'Backup Operators', - 'S-1-5-32-552' => 'Replicators', - 'S-1-16-4096' => 'Low Integrity Level', - 'S-1-16-8192' => 'Medium Integrity Level', - 'S-1-16-12288' => 'High Integrity Level', - 'S-1-16-16384' => 'System Integrity Level', -); - -sub get_name { - my $self = shift; - - my $sid = $self->{_sid}; - - foreach my $regexp (keys %WellKnownSids) { - if ($sid =~ m/^$regexp$/) { - return $WellKnownSids{$regexp}; - } - } - return; -} - -sub as_string { - my $self = shift; - - return $self->{_sid}; -} - -sub get_length { - my $self = shift; - - return $self->{_length}; -} - - -package Parse::Win32Registry::ACE; - -sub new { - my $class = shift; - my $data = shift; - - if (!defined $data) { - return; - } - - # 0x00 byte = type - # 0x01 byte = flags - # 0x02 word = length - - # Types: - # ACCESS_ALLOWED_ACE_TYPE = 0 - # ACCESS_DENIED_ACE_TYPE = 1 - # SYSTEM_AUDIT_ACE_TYPE = 2 - # SYSTEM_MANDATORY_LABEL_ACE_TYPE = x011 - - # Flags: - # OBJECT_INHERIT_ACE = 0x01 - # CONTAINER_INHERIT_ACE = 0x02 - # NO_PROPAGATE_INHERIT_ACE = 0x04 - # INHERIT_ONLY_ACE = 0x08 - # INHERITED_ACE = 0x10 - # SUCCESSFUL_ACCESS_ACE_FLAG = 0x40 (Audit Success) - # FAILED_ACCESS_ACE_FLAG = 0x80 (Audit Failure) - - if (length($data) < 4) { - return; - } - - my ($type, $flags, $ace_len) = unpack('CCv', $data); - - if (length($data) < $ace_len) { - return; - } - - # The data following the header varies depending on the type. - # For ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, SYSTEM_AUDIT_ACE - # the header is followed by an access mask and a sid. - # 0x04 dword = access mask - # 0x08 = SID - - # Only the following types are currently unpacked: - # 0 (ACCESS_ALLOWED_ACE), 1 (ACCESS_DENIED_ACE), 2 (SYSTEM_AUDIT_ACE) - if ($type >= 0 && $type <= 2 || $type == 0x11) { - my $access_mask = unpack('x4V', $data); - my $sid = Parse::Win32Registry::SID->new(substr($data, 8, - $ace_len - 8)); - - # Abandon ace if sid is invalid - if (!defined $sid) { - return; - } - - # Abandon ace if not the expected length - if (($sid->get_length + 8) != $ace_len) { - return; - } - - my $self = { - _type => $type, - _flags => $flags, - _mask => $access_mask, - _trustee => $sid, - _length => $ace_len, - }; - bless $self, $class; - - return $self; - } - else { - return; - } -} - -our @Types = qw( - ACCESS_ALLOWED - ACCESS_DENIED - SYSTEM_AUDIT - SYSTEM_ALARM - ALLOWED_COMPOUND - ACCESS_ALLOWED_OBJECT - ACCESS_DENIED_OBJECT - SYSTEM_AUDIT_OBJECT - SYSTEM_ALARM_OBJECT - ACCESS_ALLOWED_CALLBACK - ACCESS_DENIED_CALLBACK - ACCESS_ALLOWED_CALLBACK_OBJECT - ACCESS_DENIED_CALLBACK_OBJECT - SYSTEM_AUDIT_CALLBACK - SYSTEM_ALARM_CALLBACK - SYSTEM_AUDIT_CALLBACK_OBJECT - SYSTEM_ALARM_CALLBACK_OBJECT - SYSTEM_MANDATORY_LABEL -); - -sub _look_up_ace_type { - my $type = shift; - - if (exists $Types[$type]) { - return $Types[$type]; - } - else { - return ''; - } -} - -sub get_type { - return $_[0]->{_type}; -} - -sub get_type_as_string { - return _look_up_ace_type($_[0]->{_type}); -} - -sub get_flags { - return $_[0]->{_flags}; -} - -sub get_access_mask { - return $_[0]->{_mask}; -} - -sub get_trustee { - return $_[0]->{_trustee}; -} - -sub as_string { - my $self = shift; - - my $sid = $self->{_trustee}; - my $string = sprintf '%s 0x%02x 0x%08x %s', - _look_up_ace_type($self->{_type}), - $self->{_flags}, - $self->{_mask}, - $sid->as_string; - my $name = $sid->get_name; - $string .= " [$name]" if defined $name; - return $string; -} - -sub get_length { - my $self = shift; - - return $self->{_length}; -} - - -package Parse::Win32Registry::ACL; - -use Carp; - -sub new { - my $class = shift; - my $data = shift; - - if (!defined $data) { - return; - } - - # 0x00 byte = revision - # 0x01 - # 0x02 word = length - # 0x04 word = number of aces - # 0x06 - # 0x08 = first ace (variable length) - # ... = second ace (variable length) - # ... - - if (length($data) < 8) { - return; - } - - my ($rev, $acl_len, $num_aces) = unpack('Cxvv', $data); - - if (length($data) < $acl_len) { - return; - } - - my $pos = 8; - my @acl = (); - foreach (my $num = 0; $num < $num_aces; $num++) { - my $ace = Parse::Win32Registry::ACE->new(substr($data, $pos, - $acl_len - $pos)); - # Abandon acl if any single ace is undefined - return if !defined $ace; - push @acl, $ace; - $pos += $ace->get_length; - } - - # Abandon acl if not expected length, but don't use - # $pos != $acl_len as some acls contain unused space. - if ($pos > $acl_len) { - return; - } - - my $self = { - _acl => \@acl, - _length => $acl_len, - }; - bless $self, $class; - - return $self; -} - -sub get_list_of_aces { - my $self = shift; - - return @{$self->{_acl}}; -} - -sub as_string { - croak 'Usage: ACLs do not have an as_string method; use as_stanza instead'; -} - -sub as_stanza { - my $self = shift; - - my $stanza = ''; - foreach my $ace (@{$self->{_acl}}) { - $stanza .= 'ACE: '. $ace->as_string . "\n"; - } - return $stanza; -} - -sub get_length { - my $self = shift; - - return $self->{_length}; -} - - -package Parse::Win32Registry::SecurityDescriptor; - -use Carp; - -sub new { - my $class = shift; - my $data = shift; - - if (!defined $data) { - return; - } - - # Unpacks "self-relative" security descriptors - - # 0x00 word = revision - # 0x02 word = control flags - # 0x04 dword = offset to owner sid - # 0x08 dword = offset to group sid - # 0x0c dword = offset to sacl - # 0x10 dword = offset to dacl - - # Offsets are relative to the start of the security descriptor - - # Control Flags: - # SE_OWNER_DEFAULTED 0x0001 - # SE_GROUP_DEFAULTED 0x0002 - # SE_DACL_PRESENT 0x0004 - # SE_DACL_DEFAULTED 0x0008 - # SE_SACL_PRESENT 0x0010 - # SE_SACL_DEFAULTED 0x0020 - # SE_DACL_AUTO_INHERIT_REQ 0x0100 - # SE_SACL_AUTO_INHERIT_REQ 0x0200 - # SE_DACL_AUTO_INHERITED 0x0400 - # SE_SACL_AUTO_INHERITED 0x0800 - # SE_DACL_PROTECTED 0x1000 - # SE_SACL_PROTECTED 0x2000 - # SE_RM_CONTROL_VALID 0x4000 - # SE_SELF_RELATIVE 0x8000 - - if (length($data) < 20) { - return; - } - - my ($rev, - $flags, - $offset_to_owner, - $offset_to_group, - $offset_to_sacl, - $offset_to_dacl) = unpack('vvVVVV', $data); - - my %sd = (); - my $sd_len = 20; - - my $self = {}; - if ($offset_to_owner > 0 && $offset_to_owner < length($data)) { - my $owner = Parse::Win32Registry::SID->new(substr($data, - $offset_to_owner)); - return if !defined $owner; - $self->{_owner} = $owner; - if ($offset_to_owner + $owner->get_length > $sd_len) { - $sd_len = $offset_to_owner + $owner->get_length; - } - } - if ($offset_to_group > 0 && $offset_to_group < length($data)) { - my $group = Parse::Win32Registry::SID->new(substr($data, - $offset_to_group)); - return if !defined $group; - $self->{_group} = $group; - if ($offset_to_group + $group->get_length > $sd_len) { - $sd_len = $offset_to_group + $group->get_length; - } - } - if ($offset_to_sacl > 0 && $offset_to_sacl < length($data)) { - my $sacl = Parse::Win32Registry::ACL->new(substr($data, - $offset_to_sacl)); - return if !defined $sacl; - $self->{_sacl} = $sacl; - if ($offset_to_sacl + $sacl->get_length > $sd_len) { - $sd_len = $offset_to_sacl + $sacl->get_length; - } - } - if ($offset_to_dacl > 0 && $offset_to_dacl < length($data)) { - my $dacl = Parse::Win32Registry::ACL->new(substr($data, - $offset_to_dacl)); - return if !defined $dacl; - $self->{_dacl} = $dacl; - if ($offset_to_dacl + $dacl->get_length > $sd_len) { - $sd_len = $offset_to_dacl + $dacl->get_length; - } - } - $self->{_length} = $sd_len; - bless $self, $class; - - return $self; -} - -sub get_owner { - my $self = shift; - - return $self->{_owner}; -} - -sub get_group { - my $self = shift; - - return $self->{_group}; -} - -sub get_sacl { - my $self = shift; - - return $self->{_sacl}; -} - -sub get_dacl { - my $self = shift; - - return $self->{_dacl}; -} - -sub as_string { - croak 'Usage: Security Descriptors do not have an as_string method; use as_stanza instead'; -} - -sub as_stanza { - my $self = shift; - - my $stanza = ''; - if (defined(my $owner = $self->{_owner})) { - $stanza .= 'Owner SID: ' . $owner->as_string; - my $name = $owner->get_name; - $stanza .= " [$name]" if defined $name; - $stanza .= "\n"; - } - if (defined(my $group = $self->{_group})) { - $stanza .= 'Group SID: ' . $group->as_string; - my $name = $group->get_name; - $stanza .= " [$name]" if defined $name; - $stanza .= "\n"; - } - if (defined(my $sacl = $self->{_sacl})) { - foreach my $ace ($sacl->get_list_of_aces) { - $stanza .= 'SACL ACE: ' . $ace->as_string . "\n"; - } - } - if (defined(my $dacl = $self->{_dacl})) { - foreach my $ace ($dacl->get_list_of_aces) { - $stanza .= 'DACL ACE: ' . $ace->as_string . "\n"; - } - } - return $stanza; -} - -sub get_length { - my $self = shift; - - return $self->{_length}; -} - -1; diff --git a/thirdparty/rr-full/File.pm b/thirdparty/rr-full/File.pm deleted file mode 100644 index f02424df18d..00000000000 --- a/thirdparty/rr-full/File.pm +++ /dev/null @@ -1,355 +0,0 @@ -package Parse::Win32Registry::WinNT::File; - -use strict; -use warnings; - -use base qw(Parse::Win32Registry::File); - -use Carp; -use Encode; -use File::Basename; -use Parse::Win32Registry::Base qw(:all); -use Parse::Win32Registry::WinNT::Key; - -use constant REGF_HEADER_LENGTH => 0x200; -use constant OFFSET_TO_FIRST_HBIN => 0x1000; - -sub new { - my $class = shift; - my $filename = shift or croak "No filename specified"; - - open my $fh, '<', $filename or croak "Unable to open '$filename': $!"; - - # 0x00 dword = 'regf' signature - # 0x04 dword = seq1 - # 0x08 dword = seq2 - # 0x0c qword = timestamp - # 0x14 dword = major version - # 0x18 dword = minor version - # 0x1c dword = type (0 = registry file, 1 = log file) - # 0x20 dword = (1) - # 0x24 dword = offset to root key - # 0x28 dword = total length of all hbins (excludes header) - # 0x2c dword = (1) - # 0x30 = embedded filename - - # Extracted offsets are always relative to first hbin - - my $bytes_read = sysread($fh, my $regf_header, REGF_HEADER_LENGTH); - if ($bytes_read != REGF_HEADER_LENGTH) { - warnf('Could not read registry file header'); - return; - } - - my ($regf_sig, - $seq1, - $seq2, - $timestamp, - $major_version, - $minor_version, - $type, - $offset_to_root_key, - $total_hbin_length, - $embedded_filename, - $reorg_timestamp, - ) = unpack('a4VVa8VVVx4VVx4a64x56a8', $regf_header); - -# Updated 20200219 -#---------------------------------------------------------------------------- - $bytes_read = sysread($fh, my $re_org, 8, 168); - if ($bytes_read != 8) { - warnf('Could not read re_org timestamp'); - return; - } -#---------------------------------------------------------------------------- - $offset_to_root_key += OFFSET_TO_FIRST_HBIN; - - if ($regf_sig ne 'regf') { - warnf('Invalid registry file signature'); - return; - } - - $embedded_filename = unpack('Z*', decode('UCS-2LE', $embedded_filename)); - - # The header checksum is the xor of the first 127 dwords. - # The checksum is stored in the 128th dword, at offset 0x1fc (508). - my $checksum = 0; - foreach my $x (unpack('V127', $regf_header)) { - $checksum ^= $x; - } - my $embedded_checksum = unpack('x508V', $regf_header); - if ($checksum != $embedded_checksum) { - warnf('Invalid checksum for registry file header'); - } - - my $self = {}; - $self->{_filehandle} = $fh; - $self->{_filename} = $filename; - $self->{_length} = (stat $fh)[7]; - $self->{_offset_to_root_key} = $offset_to_root_key; - $self->{_timestamp} = unpack_windows_time($timestamp); -#---------------------------------------------------------------------------- - $self->{_reorg_timestamp} = unpack_windows_time($reorg_timestamp); -#---------------------------------------------------------------------------- - $self->{_embedded_filename} = $embedded_filename; - $self->{_seq1} = $seq1; - $self->{_seq2} = $seq2; - $self->{_version} = "$major_version.$minor_version"; - $self->{_type} = $type; - $self->{_total_hbin_length} = $total_hbin_length; - $self->{_embedded_checksum} = $embedded_checksum; - $self->{_security_cache} = {}; # comment out to disable cache - bless $self, $class; - - return $self; -} - -sub get_root_key { - my $self = shift; - - my $offset_to_root_key = $self->{_offset_to_root_key}; - - my $root_key = Parse::Win32Registry::WinNT::Key->new($self, - $offset_to_root_key); - return $root_key; -} - -sub get_virtual_root_key { - my $self = shift; - my $fake_root = shift; - - my $root_key = $self->get_root_key; - return if !defined $root_key; - - if (!defined $fake_root) { - # guess virtual root from filename - my $filename = basename $self->{_filename}; - - if ($filename =~ /NTUSER/i) { - $fake_root = 'HKEY_CURRENT_USER'; - } - elsif ($filename =~ /USRCLASS/i) { - $fake_root = 'HKEY_CLASSES_ROOT'; - } - elsif ($filename =~ /SOFTWARE/i) { - $fake_root = 'HKEY_LOCAL_MACHINE\SOFTWARE'; - } - elsif ($filename =~ /SYSTEM/i) { - $fake_root = 'HKEY_LOCAL_MACHINE\SYSTEM'; - } - elsif ($filename =~ /SAM/i) { - $fake_root = 'HKEY_LOCAL_MACHINE\SAM'; - } - elsif ($filename =~ /SECURITY/i) { - $fake_root = 'HKEY_LOCAL_MACHINE\SECURITY'; - } - else { - $fake_root = 'HKEY_UNKNOWN'; - } - } - - $root_key->{_name} = $fake_root; - $root_key->{_key_path} = $fake_root; - - return $root_key; -} - -sub get_timestamp { - my $self = shift; - - return $self->{_timestamp}; -} - -sub get_timestamp_as_string { - my $self = shift; - - return iso8601($self->{_timestamp}); -} - -# Added 20200219 -#--------------------------------------------------------- -sub get_version { - my $self = shift; - return $self->{_version}; -} - -sub get_reorg_timestamp { - my $self = shift; - return $self->{_reorg_timestamp}; -} - -sub get_seq1 { - my $self = shift; - return $self->{_seq1}; -} - -sub get_seq2 { - my $self = shift; - return $self->{_seq2}; -} - -sub is_dirty { - my $self = shift; - if ($self->{_seq1} == $self->{_seq2}) { - return 0; - } - else { - return 1; - } -} - -sub get_type { - my $self = shift; - if ($self->{_type} == 0) { - return "Registry file"; - } - elsif ($self->{_type} == 1) { - return "Log file"; - } - else { - return "Unknown (".$self->{_type}.")"; - } -} -#--------------------------------------------------------- - -sub get_embedded_filename { - my $self = shift; - - return $self->{_embedded_filename}; -} - -sub get_block_iterator { - my $self = shift; - - my $offset_to_next_hbin = OFFSET_TO_FIRST_HBIN; - my $end_of_file = $self->{_length}; - - return Parse::Win32Registry::Iterator->new(sub { - if ($offset_to_next_hbin > $end_of_file) { - return; # no more hbins - } - if (my $hbin = Parse::Win32Registry::WinNT::Hbin->new($self, - $offset_to_next_hbin)) - { - return unless $hbin->get_length > 0; - $offset_to_next_hbin += $hbin->get_length; - return $hbin; - } - else { - return; # no more hbins - } - }); -} - -*get_hbin_iterator = \&get_block_iterator; - -sub _dump_security_cache { - my $self = shift; - - if (defined(my $cache = $self->{_security_cache})) { - foreach my $offset (sort { $a <=> $b } keys %$cache) { - my $security = $cache->{$offset}; - printf '0x%x %s\n', $offset, $security->as_string; - } - } -} - - -package Parse::Win32Registry::WinNT::Hbin; - -use strict; -use warnings; - -use base qw(Parse::Win32Registry::Entry); - -use Carp; -use Parse::Win32Registry::Base qw(:all); -use Parse::Win32Registry::WinNT::Entry; - -use constant HBIN_HEADER_LENGTH => 0x20; - -sub new { - my $class = shift; - my $regfile = shift; - my $offset = shift; - - croak 'Missing registry file' if !defined $regfile; - croak 'Missing offset' if !defined $offset; - - my $fh = $regfile->get_filehandle; - - # 0x00 dword = 'hbin' signature - # 0x04 dword = offset from first hbin to this hbin - # 0x08 dword = length of this hbin / relative offset to next hbin - # 0x14 qword = timestamp (first hbin only) - - # Extracted offsets are always relative to first hbin - - sysseek($fh, $offset, 0); - my $bytes_read = sysread($fh, my $hbin_header, HBIN_HEADER_LENGTH); - if ($bytes_read != HBIN_HEADER_LENGTH) { - return; - } - - my ($sig, - $offset_to_hbin, - $length, - $timestamp) = unpack('a4VVx8a8x4', $hbin_header); - - if ($sig ne 'hbin') { - return; - } - - my $self = {}; - $self->{_regfile} = $regfile; - $self->{_offset} = $offset; - $self->{_length} = $length; - $self->{_header_length} = HBIN_HEADER_LENGTH; - $self->{_allocated} = 1; - $self->{_tag} = $sig; - $self->{_timestamp} = unpack_windows_time($timestamp); - bless $self, $class; - - return $self; -} - -sub get_timestamp { - my $self = shift; - - return $self->{_timestamp}; -} - -sub get_timestamp_as_string { - my $self = shift; - - return iso8601($self->{_timestamp}); -} - -sub get_entry_iterator { - my $self = shift; - - my $regfile = $self->{_regfile}; - my $offset = $self->{_offset}; - my $length = $self->{_length}; - - my $offset_to_next_entry = $offset + HBIN_HEADER_LENGTH; - my $end_of_hbin = $offset + $length; - - return Parse::Win32Registry::Iterator->new(sub { - if ($offset_to_next_entry >= $end_of_hbin) { - return; # no more entries - } - if (my $entry = Parse::Win32Registry::WinNT::Entry->new($regfile, - $offset_to_next_entry)) - { - return unless $entry->get_length > 0; - $offset_to_next_entry += $entry->get_length; - return $entry; - } - else { - return; # no more entries - } - }); -} - -1; diff --git a/thirdparty/rr-full/JSON/PP.pm b/thirdparty/rr-full/JSON/PP.pm deleted file mode 100644 index 0507921463c..00000000000 --- a/thirdparty/rr-full/JSON/PP.pm +++ /dev/null @@ -1,3147 +0,0 @@ -package JSON::PP; - -# JSON-2.0 - -use 5.005; -use strict; - -use Exporter (); -BEGIN { @JSON::PP::ISA = ('Exporter') } - -use overload (); -use JSON::PP::Boolean; - -use Carp (); -#use Devel::Peek; - -$JSON::PP::VERSION = '4.05'; - -@JSON::PP::EXPORT = qw(encode_json decode_json from_json to_json); - -# instead of hash-access, i tried index-access for speed. -# but this method is not faster than what i expected. so it will be changed. - -use constant P_ASCII => 0; -use constant P_LATIN1 => 1; -use constant P_UTF8 => 2; -use constant P_INDENT => 3; -use constant P_CANONICAL => 4; -use constant P_SPACE_BEFORE => 5; -use constant P_SPACE_AFTER => 6; -use constant P_ALLOW_NONREF => 7; -use constant P_SHRINK => 8; -use constant P_ALLOW_BLESSED => 9; -use constant P_CONVERT_BLESSED => 10; -use constant P_RELAXED => 11; - -use constant P_LOOSE => 12; -use constant P_ALLOW_BIGNUM => 13; -use constant P_ALLOW_BAREKEY => 14; -use constant P_ALLOW_SINGLEQUOTE => 15; -use constant P_ESCAPE_SLASH => 16; -use constant P_AS_NONBLESSED => 17; - -use constant P_ALLOW_UNKNOWN => 18; -use constant P_ALLOW_TAGS => 19; - -use constant OLD_PERL => $] < 5.008 ? 1 : 0; -use constant USE_B => $ENV{PERL_JSON_PP_USE_B} || 0; - -BEGIN { - if (USE_B) { - require B; - } -} - -BEGIN { - my @xs_compati_bit_properties = qw( - latin1 ascii utf8 indent canonical space_before space_after allow_nonref shrink - allow_blessed convert_blessed relaxed allow_unknown - allow_tags - ); - my @pp_bit_properties = qw( - allow_singlequote allow_bignum loose - allow_barekey escape_slash as_nonblessed - ); - - # Perl version check, Unicode handling is enabled? - # Helper module sets @JSON::PP::_properties. - if ( OLD_PERL ) { - my $helper = $] >= 5.006 ? 'JSON::PP::Compat5006' : 'JSON::PP::Compat5005'; - eval qq| require $helper |; - if ($@) { Carp::croak $@; } - } - - for my $name (@xs_compati_bit_properties, @pp_bit_properties) { - my $property_id = 'P_' . uc($name); - - eval qq/ - sub $name { - my \$enable = defined \$_[1] ? \$_[1] : 1; - - if (\$enable) { - \$_[0]->{PROPS}->[$property_id] = 1; - } - else { - \$_[0]->{PROPS}->[$property_id] = 0; - } - - \$_[0]; - } - - sub get_$name { - \$_[0]->{PROPS}->[$property_id] ? 1 : ''; - } - /; - } - -} - - - -# Functions - -my $JSON; # cache - -sub encode_json ($) { # encode - ($JSON ||= __PACKAGE__->new->utf8)->encode(@_); -} - - -sub decode_json { # decode - ($JSON ||= __PACKAGE__->new->utf8)->decode(@_); -} - -# Obsoleted - -sub to_json($) { - Carp::croak ("JSON::PP::to_json has been renamed to encode_json."); -} - - -sub from_json($) { - Carp::croak ("JSON::PP::from_json has been renamed to decode_json."); -} - - -# Methods - -sub new { - my $class = shift; - my $self = { - max_depth => 512, - max_size => 0, - indent_length => 3, - }; - - $self->{PROPS}[P_ALLOW_NONREF] = 1; - - bless $self, $class; -} - - -sub encode { - return $_[0]->PP_encode_json($_[1]); -} - - -sub decode { - return $_[0]->PP_decode_json($_[1], 0x00000000); -} - - -sub decode_prefix { - return $_[0]->PP_decode_json($_[1], 0x00000001); -} - - -# accessor - - -# pretty printing - -sub pretty { - my ($self, $v) = @_; - my $enable = defined $v ? $v : 1; - - if ($enable) { # indent_length(3) for JSON::XS compatibility - $self->indent(1)->space_before(1)->space_after(1); - } - else { - $self->indent(0)->space_before(0)->space_after(0); - } - - $self; -} - -# etc - -sub max_depth { - my $max = defined $_[1] ? $_[1] : 0x80000000; - $_[0]->{max_depth} = $max; - $_[0]; -} - - -sub get_max_depth { $_[0]->{max_depth}; } - - -sub max_size { - my $max = defined $_[1] ? $_[1] : 0; - $_[0]->{max_size} = $max; - $_[0]; -} - - -sub get_max_size { $_[0]->{max_size}; } - -sub boolean_values { - my $self = shift; - if (@_) { - my ($false, $true) = @_; - $self->{false} = $false; - $self->{true} = $true; - return ($false, $true); - } else { - delete $self->{false}; - delete $self->{true}; - return; - } -} - -sub get_boolean_values { - my $self = shift; - if (exists $self->{true} and exists $self->{false}) { - return @$self{qw/false true/}; - } - return; -} - -sub filter_json_object { - if (defined $_[1] and ref $_[1] eq 'CODE') { - $_[0]->{cb_object} = $_[1]; - } else { - delete $_[0]->{cb_object}; - } - $_[0]->{F_HOOK} = ($_[0]->{cb_object} or $_[0]->{cb_sk_object}) ? 1 : 0; - $_[0]; -} - -sub filter_json_single_key_object { - if (@_ == 1 or @_ > 3) { - Carp::croak("Usage: JSON::PP::filter_json_single_key_object(self, key, callback = undef)"); - } - if (defined $_[2] and ref $_[2] eq 'CODE') { - $_[0]->{cb_sk_object}->{$_[1]} = $_[2]; - } else { - delete $_[0]->{cb_sk_object}->{$_[1]}; - delete $_[0]->{cb_sk_object} unless %{$_[0]->{cb_sk_object} || {}}; - } - $_[0]->{F_HOOK} = ($_[0]->{cb_object} or $_[0]->{cb_sk_object}) ? 1 : 0; - $_[0]; -} - -sub indent_length { - if (!defined $_[1] or $_[1] > 15 or $_[1] < 0) { - Carp::carp "The acceptable range of indent_length() is 0 to 15."; - } - else { - $_[0]->{indent_length} = $_[1]; - } - $_[0]; -} - -sub get_indent_length { - $_[0]->{indent_length}; -} - -sub sort_by { - $_[0]->{sort_by} = defined $_[1] ? $_[1] : 1; - $_[0]; -} - -sub allow_bigint { - Carp::carp("allow_bigint() is obsoleted. use allow_bignum() instead."); - $_[0]->allow_bignum; -} - -############################### - -### -### Perl => JSON -### - - -{ # Convert - - my $max_depth; - my $indent; - my $ascii; - my $latin1; - my $utf8; - my $space_before; - my $space_after; - my $canonical; - my $allow_blessed; - my $convert_blessed; - - my $indent_length; - my $escape_slash; - my $bignum; - my $as_nonblessed; - my $allow_tags; - - my $depth; - my $indent_count; - my $keysort; - - - sub PP_encode_json { - my $self = shift; - my $obj = shift; - - $indent_count = 0; - $depth = 0; - - my $props = $self->{PROPS}; - - ($ascii, $latin1, $utf8, $indent, $canonical, $space_before, $space_after, $allow_blessed, - $convert_blessed, $escape_slash, $bignum, $as_nonblessed, $allow_tags) - = @{$props}[P_ASCII .. P_SPACE_AFTER, P_ALLOW_BLESSED, P_CONVERT_BLESSED, - P_ESCAPE_SLASH, P_ALLOW_BIGNUM, P_AS_NONBLESSED, P_ALLOW_TAGS]; - - ($max_depth, $indent_length) = @{$self}{qw/max_depth indent_length/}; - - $keysort = $canonical ? sub { $a cmp $b } : undef; - - if ($self->{sort_by}) { - $keysort = ref($self->{sort_by}) eq 'CODE' ? $self->{sort_by} - : $self->{sort_by} =~ /\D+/ ? $self->{sort_by} - : sub { $a cmp $b }; - } - - encode_error("hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)") - if(!ref $obj and !$props->[ P_ALLOW_NONREF ]); - - my $str = $self->object_to_json($obj); - - $str .= "\n" if ( $indent ); # JSON::XS 2.26 compatible - - unless ($ascii or $latin1 or $utf8) { - utf8::upgrade($str); - } - - if ($props->[ P_SHRINK ]) { - utf8::downgrade($str, 1); - } - - return $str; - } - - - sub object_to_json { - my ($self, $obj) = @_; - my $type = ref($obj); - - if($type eq 'HASH'){ - return $self->hash_to_json($obj); - } - elsif($type eq 'ARRAY'){ - return $self->array_to_json($obj); - } - elsif ($type) { # blessed object? - if (blessed($obj)) { - - return $self->value_to_json($obj) if ( $obj->isa('JSON::PP::Boolean') ); - - if ( $allow_tags and $obj->can('FREEZE') ) { - my $obj_class = ref $obj || $obj; - $obj = bless $obj, $obj_class; - my @results = $obj->FREEZE('JSON'); - if ( @results and ref $results[0] ) { - if ( refaddr( $obj ) eq refaddr( $results[0] ) ) { - encode_error( sprintf( - "%s::FREEZE method returned same object as was passed instead of a new one", - ref $obj - ) ); - } - } - return '("'.$obj_class.'")['.join(',', @results).']'; - } - - if ( $convert_blessed and $obj->can('TO_JSON') ) { - my $result = $obj->TO_JSON(); - if ( defined $result and ref( $result ) ) { - if ( refaddr( $obj ) eq refaddr( $result ) ) { - encode_error( sprintf( - "%s::TO_JSON method returned same object as was passed instead of a new one", - ref $obj - ) ); - } - } - - return $self->object_to_json( $result ); - } - - return "$obj" if ( $bignum and _is_bignum($obj) ); - - if ($allow_blessed) { - return $self->blessed_to_json($obj) if ($as_nonblessed); # will be removed. - return 'null'; - } - encode_error( sprintf("encountered object '%s', but neither allow_blessed, convert_blessed nor allow_tags settings are enabled (or TO_JSON/FREEZE method missing)", $obj) - ); - } - else { - return $self->value_to_json($obj); - } - } - else{ - return $self->value_to_json($obj); - } - } - - - sub hash_to_json { - my ($self, $obj) = @_; - my @res; - - encode_error("json text or perl structure exceeds maximum nesting level (max_depth set too low?)") - if (++$depth > $max_depth); - - my ($pre, $post) = $indent ? $self->_up_indent() : ('', ''); - my $del = ($space_before ? ' ' : '') . ':' . ($space_after ? ' ' : ''); - - for my $k ( _sort( $obj ) ) { - if ( OLD_PERL ) { utf8::decode($k) } # key for Perl 5.6 / be optimized - push @res, $self->string_to_json( $k ) - . $del - . ( ref $obj->{$k} ? $self->object_to_json( $obj->{$k} ) : $self->value_to_json( $obj->{$k} ) ); - } - - --$depth; - $self->_down_indent() if ($indent); - - return '{}' unless @res; - return '{' . $pre . join( ",$pre", @res ) . $post . '}'; - } - - - sub array_to_json { - my ($self, $obj) = @_; - my @res; - - encode_error("json text or perl structure exceeds maximum nesting level (max_depth set too low?)") - if (++$depth > $max_depth); - - my ($pre, $post) = $indent ? $self->_up_indent() : ('', ''); - - for my $v (@$obj){ - push @res, ref($v) ? $self->object_to_json($v) : $self->value_to_json($v); - } - - --$depth; - $self->_down_indent() if ($indent); - - return '[]' unless @res; - return '[' . $pre . join( ",$pre", @res ) . $post . ']'; - } - - sub _looks_like_number { - my $value = shift; - if (USE_B) { - my $b_obj = B::svref_2object(\$value); - my $flags = $b_obj->FLAGS; - return 1 if $flags & ( B::SVp_IOK() | B::SVp_NOK() ) and !( $flags & B::SVp_POK() ); - return; - } else { - no warnings 'numeric'; - # if the utf8 flag is on, it almost certainly started as a string - return if utf8::is_utf8($value); - # detect numbers - # string & "" -> "" - # number & "" -> 0 (with warning) - # nan and inf can detect as numbers, so check with * 0 - return unless length((my $dummy = "") & $value); - return unless 0 + $value eq $value; - return 1 if $value * 0 == 0; - return -1; # inf/nan - } - } - - sub value_to_json { - my ($self, $value) = @_; - - return 'null' if(!defined $value); - - my $type = ref($value); - - if (!$type) { - if (_looks_like_number($value)) { - return $value; - } - return $self->string_to_json($value); - } - elsif( blessed($value) and $value->isa('JSON::PP::Boolean') ){ - return $$value == 1 ? 'true' : 'false'; - } - else { - if ((overload::StrVal($value) =~ /=(\w+)/)[0]) { - return $self->value_to_json("$value"); - } - - if ($type eq 'SCALAR' and defined $$value) { - return $$value eq '1' ? 'true' - : $$value eq '0' ? 'false' - : $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ? 'null' - : encode_error("cannot encode reference to scalar"); - } - - if ( $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ) { - return 'null'; - } - else { - if ( $type eq 'SCALAR' or $type eq 'REF' ) { - encode_error("cannot encode reference to scalar"); - } - else { - encode_error("encountered $value, but JSON can only represent references to arrays or hashes"); - } - } - - } - } - - - my %esc = ( - "\n" => '\n', - "\r" => '\r', - "\t" => '\t', - "\f" => '\f', - "\b" => '\b', - "\"" => '\"', - "\\" => '\\\\', - "\'" => '\\\'', - ); - - - sub string_to_json { - my ($self, $arg) = @_; - - $arg =~ s/([\x22\x5c\n\r\t\f\b])/$esc{$1}/g; - $arg =~ s/\//\\\//g if ($escape_slash); - $arg =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg; - - if ($ascii) { - $arg = JSON_PP_encode_ascii($arg); - } - - if ($latin1) { - $arg = JSON_PP_encode_latin1($arg); - } - - if ($utf8) { - utf8::encode($arg); - } - - return '"' . $arg . '"'; - } - - - sub blessed_to_json { - my $reftype = reftype($_[1]) || ''; - if ($reftype eq 'HASH') { - return $_[0]->hash_to_json($_[1]); - } - elsif ($reftype eq 'ARRAY') { - return $_[0]->array_to_json($_[1]); - } - else { - return 'null'; - } - } - - - sub encode_error { - my $error = shift; - Carp::croak "$error"; - } - - - sub _sort { - defined $keysort ? (sort $keysort (keys %{$_[0]})) : keys %{$_[0]}; - } - - - sub _up_indent { - my $self = shift; - my $space = ' ' x $indent_length; - - my ($pre,$post) = ('',''); - - $post = "\n" . $space x $indent_count; - - $indent_count++; - - $pre = "\n" . $space x $indent_count; - - return ($pre,$post); - } - - - sub _down_indent { $indent_count--; } - - - sub PP_encode_box { - { - depth => $depth, - indent_count => $indent_count, - }; - } - -} # Convert - - -sub _encode_ascii { - join('', - map { - $_ <= 127 ? - chr($_) : - $_ <= 65535 ? - sprintf('\u%04x', $_) : sprintf('\u%x\u%x', _encode_surrogates($_)); - } unpack('U*', $_[0]) - ); -} - - -sub _encode_latin1 { - join('', - map { - $_ <= 255 ? - chr($_) : - $_ <= 65535 ? - sprintf('\u%04x', $_) : sprintf('\u%x\u%x', _encode_surrogates($_)); - } unpack('U*', $_[0]) - ); -} - - -sub _encode_surrogates { # from perlunicode - my $uni = $_[0] - 0x10000; - return ($uni / 0x400 + 0xD800, $uni % 0x400 + 0xDC00); -} - - -sub _is_bignum { - $_[0]->isa('Math::BigInt') or $_[0]->isa('Math::BigFloat'); -} - - - -# -# JSON => Perl -# - -my $max_intsize; - -BEGIN { - my $checkint = 1111; - for my $d (5..64) { - $checkint .= 1; - my $int = eval qq| $checkint |; - if ($int =~ /[eE]/) { - $max_intsize = $d - 1; - last; - } - } -} - -{ # PARSE - - my %escapes = ( # by Jeremy Muhlich - b => "\x8", - t => "\x9", - n => "\xA", - f => "\xC", - r => "\xD", - '\\' => '\\', - '"' => '"', - '/' => '/', - ); - - my $text; # json data - my $at; # offset - my $ch; # first character - my $len; # text length (changed according to UTF8 or NON UTF8) - # INTERNAL - my $depth; # nest counter - my $encoding; # json text encoding - my $is_valid_utf8; # temp variable - my $utf8_len; # utf8 byte length - # FLAGS - my $utf8; # must be utf8 - my $max_depth; # max nest number of objects and arrays - my $max_size; - my $relaxed; - my $cb_object; - my $cb_sk_object; - - my $F_HOOK; - - my $allow_bignum; # using Math::BigInt/BigFloat - my $singlequote; # loosely quoting - my $loose; # - my $allow_barekey; # bareKey - my $allow_tags; - - my $alt_true; - my $alt_false; - - sub _detect_utf_encoding { - my $text = shift; - my @octets = unpack('C4', $text); - return 'unknown' unless defined $octets[3]; - return ( $octets[0] and $octets[1]) ? 'UTF-8' - : (!$octets[0] and $octets[1]) ? 'UTF-16BE' - : (!$octets[0] and !$octets[1]) ? 'UTF-32BE' - : ( $octets[2] ) ? 'UTF-16LE' - : (!$octets[2] ) ? 'UTF-32LE' - : 'unknown'; - } - - sub PP_decode_json { - my ($self, $want_offset); - - ($self, $text, $want_offset) = @_; - - ($at, $ch, $depth) = (0, '', 0); - - if ( !defined $text or ref $text ) { - decode_error("malformed JSON string, neither array, object, number, string or atom"); - } - - my $props = $self->{PROPS}; - - ($utf8, $relaxed, $loose, $allow_bignum, $allow_barekey, $singlequote, $allow_tags) - = @{$props}[P_UTF8, P_RELAXED, P_LOOSE .. P_ALLOW_SINGLEQUOTE, P_ALLOW_TAGS]; - - ($alt_true, $alt_false) = @$self{qw/true false/}; - - if ( $utf8 ) { - $encoding = _detect_utf_encoding($text); - if ($encoding ne 'UTF-8' and $encoding ne 'unknown') { - require Encode; - Encode::from_to($text, $encoding, 'utf-8'); - } else { - utf8::downgrade( $text, 1 ) or Carp::croak("Wide character in subroutine entry"); - } - } - else { - utf8::upgrade( $text ); - utf8::encode( $text ); - } - - $len = length $text; - - ($max_depth, $max_size, $cb_object, $cb_sk_object, $F_HOOK) - = @{$self}{qw/max_depth max_size cb_object cb_sk_object F_HOOK/}; - - if ($max_size > 1) { - use bytes; - my $bytes = length $text; - decode_error( - sprintf("attempted decode of JSON text of %s bytes size, but max_size is set to %s" - , $bytes, $max_size), 1 - ) if ($bytes > $max_size); - } - - white(); # remove head white space - - decode_error("malformed JSON string, neither array, object, number, string or atom") unless defined $ch; # Is there a first character for JSON structure? - - my $result = value(); - - if ( !$props->[ P_ALLOW_NONREF ] and !ref $result ) { - decode_error( - 'JSON text must be an object or array (but found number, string, true, false or null,' - . ' use allow_nonref to allow this)', 1); - } - - Carp::croak('something wrong.') if $len < $at; # we won't arrive here. - - my $consumed = defined $ch ? $at - 1 : $at; # consumed JSON text length - - white(); # remove tail white space - - return ( $result, $consumed ) if $want_offset; # all right if decode_prefix - - decode_error("garbage after JSON object") if defined $ch; - - $result; - } - - - sub next_chr { - return $ch = undef if($at >= $len); - $ch = substr($text, $at++, 1); - } - - - sub value { - white(); - return if(!defined $ch); - return object() if($ch eq '{'); - return array() if($ch eq '['); - return tag() if($ch eq '('); - return string() if($ch eq '"' or ($singlequote and $ch eq "'")); - return number() if($ch =~ /[0-9]/ or $ch eq '-'); - return word(); - } - - sub string { - my $utf16; - my $is_utf8; - - ($is_valid_utf8, $utf8_len) = ('', 0); - - my $s = ''; # basically UTF8 flag on - - if($ch eq '"' or ($singlequote and $ch eq "'")){ - my $boundChar = $ch; - - OUTER: while( defined(next_chr()) ){ - - if($ch eq $boundChar){ - next_chr(); - - if ($utf16) { - decode_error("missing low surrogate character in surrogate pair"); - } - - utf8::decode($s) if($is_utf8); - - return $s; - } - elsif($ch eq '\\'){ - next_chr(); - if(exists $escapes{$ch}){ - $s .= $escapes{$ch}; - } - elsif($ch eq 'u'){ # UNICODE handling - my $u = ''; - - for(1..4){ - $ch = next_chr(); - last OUTER if($ch !~ /[0-9a-fA-F]/); - $u .= $ch; - } - - # U+D800 - U+DBFF - if ($u =~ /^[dD][89abAB][0-9a-fA-F]{2}/) { # UTF-16 high surrogate? - $utf16 = $u; - } - # U+DC00 - U+DFFF - elsif ($u =~ /^[dD][c-fC-F][0-9a-fA-F]{2}/) { # UTF-16 low surrogate? - unless (defined $utf16) { - decode_error("missing high surrogate character in surrogate pair"); - } - $is_utf8 = 1; - $s .= JSON_PP_decode_surrogates($utf16, $u) || next; - $utf16 = undef; - } - else { - if (defined $utf16) { - decode_error("surrogate pair expected"); - } - - if ( ( my $hex = hex( $u ) ) > 127 ) { - $is_utf8 = 1; - $s .= JSON_PP_decode_unicode($u) || next; - } - else { - $s .= chr $hex; - } - } - - } - else{ - unless ($loose) { - $at -= 2; - decode_error('illegal backslash escape sequence in string'); - } - $s .= $ch; - } - } - else{ - - if ( ord $ch > 127 ) { - unless( $ch = is_valid_utf8($ch) ) { - $at -= 1; - decode_error("malformed UTF-8 character in JSON string"); - } - else { - $at += $utf8_len - 1; - } - - $is_utf8 = 1; - } - - if (!$loose) { - if ($ch =~ /[\x00-\x1f\x22\x5c]/) { # '/' ok - if (!$relaxed or $ch ne "\t") { - $at--; - decode_error('invalid character encountered while parsing JSON string'); - } - } - } - - $s .= $ch; - } - } - } - - decode_error("unexpected end of string while parsing JSON string"); - } - - - sub white { - while( defined $ch ){ - if($ch eq '' or $ch =~ /\A[ \t\r\n]\z/){ - next_chr(); - } - elsif($relaxed and $ch eq '/'){ - next_chr(); - if(defined $ch and $ch eq '/'){ - 1 while(defined(next_chr()) and $ch ne "\n" and $ch ne "\r"); - } - elsif(defined $ch and $ch eq '*'){ - next_chr(); - while(1){ - if(defined $ch){ - if($ch eq '*'){ - if(defined(next_chr()) and $ch eq '/'){ - next_chr(); - last; - } - } - else{ - next_chr(); - } - } - else{ - decode_error("Unterminated comment"); - } - } - next; - } - else{ - $at--; - decode_error("malformed JSON string, neither array, object, number, string or atom"); - } - } - else{ - if ($relaxed and $ch eq '#') { # correctly? - pos($text) = $at; - $text =~ /\G([^\n]*(?:\r\n|\r|\n|$))/g; - $at = pos($text); - next_chr; - next; - } - - last; - } - } - } - - - sub array { - my $a = $_[0] || []; # you can use this code to use another array ref object. - - decode_error('json text or perl structure exceeds maximum nesting level (max_depth set too low?)') - if (++$depth > $max_depth); - - next_chr(); - white(); - - if(defined $ch and $ch eq ']'){ - --$depth; - next_chr(); - return $a; - } - else { - while(defined($ch)){ - push @$a, value(); - - white(); - - if (!defined $ch) { - last; - } - - if($ch eq ']'){ - --$depth; - next_chr(); - return $a; - } - - if($ch ne ','){ - last; - } - - next_chr(); - white(); - - if ($relaxed and $ch eq ']') { - --$depth; - next_chr(); - return $a; - } - - } - } - - $at-- if defined $ch and $ch ne ''; - decode_error(", or ] expected while parsing array"); - } - - sub tag { - decode_error('malformed JSON string, neither array, object, number, string or atom') unless $allow_tags; - - next_chr(); - white(); - - my $tag = value(); - return unless defined $tag; - decode_error('malformed JSON string, (tag) must be a string') if ref $tag; - - white(); - - if (!defined $ch or $ch ne ')') { - decode_error(') expected after tag'); - } - - next_chr(); - white(); - - my $val = value(); - return unless defined $val; - decode_error('malformed JSON string, tag value must be an array') unless ref $val eq 'ARRAY'; - - if (!eval { $tag->can('THAW') }) { - decode_error('cannot decode perl-object (package does not exist)') if $@; - decode_error('cannot decode perl-object (package does not have a THAW method)'); - } - $tag->THAW('JSON', @$val); - } - - sub object { - my $o = $_[0] || {}; # you can use this code to use another hash ref object. - my $k; - - decode_error('json text or perl structure exceeds maximum nesting level (max_depth set too low?)') - if (++$depth > $max_depth); - next_chr(); - white(); - - if(defined $ch and $ch eq '}'){ - --$depth; - next_chr(); - if ($F_HOOK) { - return _json_object_hook($o); - } - return $o; - } - else { - while (defined $ch) { - $k = ($allow_barekey and $ch ne '"' and $ch ne "'") ? bareKey() : string(); - white(); - - if(!defined $ch or $ch ne ':'){ - $at--; - decode_error("':' expected"); - } - - next_chr(); - $o->{$k} = value(); - white(); - - last if (!defined $ch); - - if($ch eq '}'){ - --$depth; - next_chr(); - if ($F_HOOK) { - return _json_object_hook($o); - } - return $o; - } - - if($ch ne ','){ - last; - } - - next_chr(); - white(); - - if ($relaxed and $ch eq '}') { - --$depth; - next_chr(); - if ($F_HOOK) { - return _json_object_hook($o); - } - return $o; - } - - } - - } - - $at-- if defined $ch and $ch ne ''; - decode_error(", or } expected while parsing object/hash"); - } - - - sub bareKey { # doesn't strictly follow Standard ECMA-262 3rd Edition - my $key; - while($ch =~ /[^\x00-\x23\x25-\x2F\x3A-\x40\x5B-\x5E\x60\x7B-\x7F]/){ - $key .= $ch; - next_chr(); - } - return $key; - } - - - sub word { - my $word = substr($text,$at-1,4); - - if($word eq 'true'){ - $at += 3; - next_chr; - return defined $alt_true ? $alt_true : $JSON::PP::true; - } - elsif($word eq 'null'){ - $at += 3; - next_chr; - return undef; - } - elsif($word eq 'fals'){ - $at += 3; - if(substr($text,$at,1) eq 'e'){ - $at++; - next_chr; - return defined $alt_false ? $alt_false : $JSON::PP::false; - } - } - - $at--; # for decode_error report - - decode_error("'null' expected") if ($word =~ /^n/); - decode_error("'true' expected") if ($word =~ /^t/); - decode_error("'false' expected") if ($word =~ /^f/); - decode_error("malformed JSON string, neither array, object, number, string or atom"); - } - - - sub number { - my $n = ''; - my $v; - my $is_dec; - my $is_exp; - - if($ch eq '-'){ - $n = '-'; - next_chr; - if (!defined $ch or $ch !~ /\d/) { - decode_error("malformed number (no digits after initial minus)"); - } - } - - # According to RFC4627, hex or oct digits are invalid. - if($ch eq '0'){ - my $peek = substr($text,$at,1); - if($peek =~ /^[0-9a-dfA-DF]/){ # e may be valid (exponential) - decode_error("malformed number (leading zero must not be followed by another digit)"); - } - $n .= $ch; - next_chr; - } - - while(defined $ch and $ch =~ /\d/){ - $n .= $ch; - next_chr; - } - - if(defined $ch and $ch eq '.'){ - $n .= '.'; - $is_dec = 1; - - next_chr; - if (!defined $ch or $ch !~ /\d/) { - decode_error("malformed number (no digits after decimal point)"); - } - else { - $n .= $ch; - } - - while(defined(next_chr) and $ch =~ /\d/){ - $n .= $ch; - } - } - - if(defined $ch and ($ch eq 'e' or $ch eq 'E')){ - $n .= $ch; - $is_exp = 1; - next_chr; - - if(defined($ch) and ($ch eq '+' or $ch eq '-')){ - $n .= $ch; - next_chr; - if (!defined $ch or $ch =~ /\D/) { - decode_error("malformed number (no digits after exp sign)"); - } - $n .= $ch; - } - elsif(defined($ch) and $ch =~ /\d/){ - $n .= $ch; - } - else { - decode_error("malformed number (no digits after exp sign)"); - } - - while(defined(next_chr) and $ch =~ /\d/){ - $n .= $ch; - } - - } - - $v .= $n; - - if ($is_dec or $is_exp) { - if ($allow_bignum) { - require Math::BigFloat; - return Math::BigFloat->new($v); - } - } else { - if (length $v > $max_intsize) { - if ($allow_bignum) { # from Adam Sussman - require Math::BigInt; - return Math::BigInt->new($v); - } - else { - return "$v"; - } - } - } - - return $is_dec ? $v/1.0 : 0+$v; - } - - - sub is_valid_utf8 { - - $utf8_len = $_[0] =~ /[\x00-\x7F]/ ? 1 - : $_[0] =~ /[\xC2-\xDF]/ ? 2 - : $_[0] =~ /[\xE0-\xEF]/ ? 3 - : $_[0] =~ /[\xF0-\xF4]/ ? 4 - : 0 - ; - - return unless $utf8_len; - - my $is_valid_utf8 = substr($text, $at - 1, $utf8_len); - - return ( $is_valid_utf8 =~ /^(?: - [\x00-\x7F] - |[\xC2-\xDF][\x80-\xBF] - |[\xE0][\xA0-\xBF][\x80-\xBF] - |[\xE1-\xEC][\x80-\xBF][\x80-\xBF] - |[\xED][\x80-\x9F][\x80-\xBF] - |[\xEE-\xEF][\x80-\xBF][\x80-\xBF] - |[\xF0][\x90-\xBF][\x80-\xBF][\x80-\xBF] - |[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF] - |[\xF4][\x80-\x8F][\x80-\xBF][\x80-\xBF] - )$/x ) ? $is_valid_utf8 : ''; - } - - - sub decode_error { - my $error = shift; - my $no_rep = shift; - my $str = defined $text ? substr($text, $at) : ''; - my $mess = ''; - my $type = 'U*'; - - if ( OLD_PERL ) { - my $type = $] < 5.006 ? 'C*' - : utf8::is_utf8( $str ) ? 'U*' # 5.6 - : 'C*' - ; - } - - for my $c ( unpack( $type, $str ) ) { # emulate pv_uni_display() ? - $mess .= $c == 0x07 ? '\a' - : $c == 0x09 ? '\t' - : $c == 0x0a ? '\n' - : $c == 0x0d ? '\r' - : $c == 0x0c ? '\f' - : $c < 0x20 ? sprintf('\x{%x}', $c) - : $c == 0x5c ? '\\\\' - : $c < 0x80 ? chr($c) - : sprintf('\x{%x}', $c) - ; - if ( length $mess >= 20 ) { - $mess .= '...'; - last; - } - } - - unless ( length $mess ) { - $mess = '(end of string)'; - } - - Carp::croak ( - $no_rep ? "$error" : "$error, at character offset $at (before \"$mess\")" - ); - - } - - - sub _json_object_hook { - my $o = $_[0]; - my @ks = keys %{$o}; - - if ( $cb_sk_object and @ks == 1 and exists $cb_sk_object->{ $ks[0] } and ref $cb_sk_object->{ $ks[0] } ) { - my @val = $cb_sk_object->{ $ks[0] }->( $o->{$ks[0]} ); - if (@val == 0) { - return $o; - } - elsif (@val == 1) { - return $val[0]; - } - else { - Carp::croak("filter_json_single_key_object callbacks must not return more than one scalar"); - } - } - - my @val = $cb_object->($o) if ($cb_object); - if (@val == 0) { - return $o; - } - elsif (@val == 1) { - return $val[0]; - } - else { - Carp::croak("filter_json_object callbacks must not return more than one scalar"); - } - } - - - sub PP_decode_box { - { - text => $text, - at => $at, - ch => $ch, - len => $len, - depth => $depth, - encoding => $encoding, - is_valid_utf8 => $is_valid_utf8, - }; - } - -} # PARSE - - -sub _decode_surrogates { # from perlunicode - my $uni = 0x10000 + (hex($_[0]) - 0xD800) * 0x400 + (hex($_[1]) - 0xDC00); - my $un = pack('U*', $uni); - utf8::encode( $un ); - return $un; -} - - -sub _decode_unicode { - my $un = pack('U', hex shift); - utf8::encode( $un ); - return $un; -} - -# -# Setup for various Perl versions (the code from JSON::PP58) -# - -BEGIN { - - unless ( defined &utf8::is_utf8 ) { - require Encode; - *utf8::is_utf8 = *Encode::is_utf8; - } - - if ( !OLD_PERL ) { - *JSON::PP::JSON_PP_encode_ascii = \&_encode_ascii; - *JSON::PP::JSON_PP_encode_latin1 = \&_encode_latin1; - *JSON::PP::JSON_PP_decode_surrogates = \&_decode_surrogates; - *JSON::PP::JSON_PP_decode_unicode = \&_decode_unicode; - - if ($] < 5.008003) { # join() in 5.8.0 - 5.8.2 is broken. - package JSON::PP; - require subs; - subs->import('join'); - eval q| - sub join { - return '' if (@_ < 2); - my $j = shift; - my $str = shift; - for (@_) { $str .= $j . $_; } - return $str; - } - |; - } - } - - - sub JSON::PP::incr_parse { - local $Carp::CarpLevel = 1; - ( $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new )->incr_parse( @_ ); - } - - - sub JSON::PP::incr_skip { - ( $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new )->incr_skip; - } - - - sub JSON::PP::incr_reset { - ( $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new )->incr_reset; - } - - eval q{ - sub JSON::PP::incr_text : lvalue { - $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new; - - if ( $_[0]->{_incr_parser}->{incr_pos} ) { - Carp::croak("incr_text cannot be called when the incremental parser already started parsing"); - } - $_[0]->{_incr_parser}->{incr_text}; - } - } if ( $] >= 5.006 ); - -} # Setup for various Perl versions (the code from JSON::PP58) - - -############################### -# Utilities -# - -BEGIN { - eval 'require Scalar::Util'; - unless($@){ - *JSON::PP::blessed = \&Scalar::Util::blessed; - *JSON::PP::reftype = \&Scalar::Util::reftype; - *JSON::PP::refaddr = \&Scalar::Util::refaddr; - } - else{ # This code is from Scalar::Util. - # warn $@; - eval 'sub UNIVERSAL::a_sub_not_likely_to_be_here { ref($_[0]) }'; - *JSON::PP::blessed = sub { - local($@, $SIG{__DIE__}, $SIG{__WARN__}); - ref($_[0]) ? eval { $_[0]->a_sub_not_likely_to_be_here } : undef; - }; - require B; - my %tmap = qw( - B::NULL SCALAR - B::HV HASH - B::AV ARRAY - B::CV CODE - B::IO IO - B::GV GLOB - B::REGEXP REGEXP - ); - *JSON::PP::reftype = sub { - my $r = shift; - - return undef unless length(ref($r)); - - my $t = ref(B::svref_2object($r)); - - return - exists $tmap{$t} ? $tmap{$t} - : length(ref($$r)) ? 'REF' - : 'SCALAR'; - }; - *JSON::PP::refaddr = sub { - return undef unless length(ref($_[0])); - - my $addr; - if(defined(my $pkg = blessed($_[0]))) { - $addr .= bless $_[0], 'Scalar::Util::Fake'; - bless $_[0], $pkg; - } - else { - $addr .= $_[0] - } - - $addr =~ /0x(\w+)/; - local $^W; - #no warnings 'portable'; - hex($1); - } - } -} - - -# shamelessly copied and modified from JSON::XS code. - -$JSON::PP::true = do { bless \(my $dummy = 1), "JSON::PP::Boolean" }; -$JSON::PP::false = do { bless \(my $dummy = 0), "JSON::PP::Boolean" }; - -sub is_bool { blessed $_[0] and ( $_[0]->isa("JSON::PP::Boolean") or $_[0]->isa("Types::Serialiser::BooleanBase") or $_[0]->isa("JSON::XS::Boolean") ); } - -sub true { $JSON::PP::true } -sub false { $JSON::PP::false } -sub null { undef; } - -############################### - -package JSON::PP::IncrParser; - -use strict; - -use constant INCR_M_WS => 0; # initial whitespace skipping -use constant INCR_M_STR => 1; # inside string -use constant INCR_M_BS => 2; # inside backslash -use constant INCR_M_JSON => 3; # outside anything, count nesting -use constant INCR_M_C0 => 4; -use constant INCR_M_C1 => 5; -use constant INCR_M_TFN => 6; -use constant INCR_M_NUM => 7; - -$JSON::PP::IncrParser::VERSION = '1.01'; - -sub new { - my ( $class ) = @_; - - bless { - incr_nest => 0, - incr_text => undef, - incr_pos => 0, - incr_mode => 0, - }, $class; -} - - -sub incr_parse { - my ( $self, $coder, $text ) = @_; - - $self->{incr_text} = '' unless ( defined $self->{incr_text} ); - - if ( defined $text ) { - if ( utf8::is_utf8( $text ) and !utf8::is_utf8( $self->{incr_text} ) ) { - utf8::upgrade( $self->{incr_text} ) ; - utf8::decode( $self->{incr_text} ) ; - } - $self->{incr_text} .= $text; - } - - if ( defined wantarray ) { - my $max_size = $coder->get_max_size; - my $p = $self->{incr_pos}; - my @ret; - { - do { - unless ( $self->{incr_nest} <= 0 and $self->{incr_mode} == INCR_M_JSON ) { - $self->_incr_parse( $coder ); - - if ( $max_size and $self->{incr_pos} > $max_size ) { - Carp::croak("attempted decode of JSON text of $self->{incr_pos} bytes size, but max_size is set to $max_size"); - } - unless ( $self->{incr_nest} <= 0 and $self->{incr_mode} == INCR_M_JSON ) { - # as an optimisation, do not accumulate white space in the incr buffer - if ( $self->{incr_mode} == INCR_M_WS and $self->{incr_pos} ) { - $self->{incr_pos} = 0; - $self->{incr_text} = ''; - } - last; - } - } - - my ($obj, $offset) = $coder->PP_decode_json( $self->{incr_text}, 0x00000001 ); - push @ret, $obj; - use bytes; - $self->{incr_text} = substr( $self->{incr_text}, $offset || 0 ); - $self->{incr_pos} = 0; - $self->{incr_nest} = 0; - $self->{incr_mode} = 0; - last unless wantarray; - } while ( wantarray ); - } - - if ( wantarray ) { - return @ret; - } - else { # in scalar context - return defined $ret[0] ? $ret[0] : undef; - } - } -} - - -sub _incr_parse { - my ($self, $coder) = @_; - my $text = $self->{incr_text}; - my $len = length $text; - my $p = $self->{incr_pos}; - -INCR_PARSE: - while ( $len > $p ) { - my $s = substr( $text, $p, 1 ); - last INCR_PARSE unless defined $s; - my $mode = $self->{incr_mode}; - - if ( $mode == INCR_M_WS ) { - while ( $len > $p ) { - $s = substr( $text, $p, 1 ); - last INCR_PARSE unless defined $s; - if ( ord($s) > 0x20 ) { - if ( $s eq '#' ) { - $self->{incr_mode} = INCR_M_C0; - redo INCR_PARSE; - } else { - $self->{incr_mode} = INCR_M_JSON; - redo INCR_PARSE; - } - } - $p++; - } - } elsif ( $mode == INCR_M_BS ) { - $p++; - $self->{incr_mode} = INCR_M_STR; - redo INCR_PARSE; - } elsif ( $mode == INCR_M_C0 or $mode == INCR_M_C1 ) { - while ( $len > $p ) { - $s = substr( $text, $p, 1 ); - last INCR_PARSE unless defined $s; - if ( $s eq "\n" ) { - $self->{incr_mode} = $self->{incr_mode} == INCR_M_C0 ? INCR_M_WS : INCR_M_JSON; - last; - } - $p++; - } - next; - } elsif ( $mode == INCR_M_TFN ) { - while ( $len > $p ) { - $s = substr( $text, $p++, 1 ); - next if defined $s and $s =~ /[rueals]/; - last; - } - $p--; - $self->{incr_mode} = INCR_M_JSON; - - last INCR_PARSE unless $self->{incr_nest}; - redo INCR_PARSE; - } elsif ( $mode == INCR_M_NUM ) { - while ( $len > $p ) { - $s = substr( $text, $p++, 1 ); - next if defined $s and $s =~ /[0-9eE.+\-]/; - last; - } - $p--; - $self->{incr_mode} = INCR_M_JSON; - - last INCR_PARSE unless $self->{incr_nest}; - redo INCR_PARSE; - } elsif ( $mode == INCR_M_STR ) { - while ( $len > $p ) { - $s = substr( $text, $p, 1 ); - last INCR_PARSE unless defined $s; - if ( $s eq '"' ) { - $p++; - $self->{incr_mode} = INCR_M_JSON; - - last INCR_PARSE unless $self->{incr_nest}; - redo INCR_PARSE; - } - elsif ( $s eq '\\' ) { - $p++; - if ( !defined substr($text, $p, 1) ) { - $self->{incr_mode} = INCR_M_BS; - last INCR_PARSE; - } - } - $p++; - } - } elsif ( $mode == INCR_M_JSON ) { - while ( $len > $p ) { - $s = substr( $text, $p++, 1 ); - if ( $s eq "\x00" ) { - $p--; - last INCR_PARSE; - } elsif ( $s eq "\x09" or $s eq "\x0a" or $s eq "\x0d" or $s eq "\x20" ) { - if ( !$self->{incr_nest} ) { - $p--; # do not eat the whitespace, let the next round do it - last INCR_PARSE; - } - next; - } elsif ( $s eq 't' or $s eq 'f' or $s eq 'n' ) { - $self->{incr_mode} = INCR_M_TFN; - redo INCR_PARSE; - } elsif ( $s =~ /^[0-9\-]$/ ) { - $self->{incr_mode} = INCR_M_NUM; - redo INCR_PARSE; - } elsif ( $s eq '"' ) { - $self->{incr_mode} = INCR_M_STR; - redo INCR_PARSE; - } elsif ( $s eq '[' or $s eq '{' ) { - if ( ++$self->{incr_nest} > $coder->get_max_depth ) { - Carp::croak('json text or perl structure exceeds maximum nesting level (max_depth set too low?)'); - } - next; - } elsif ( $s eq ']' or $s eq '}' ) { - if ( --$self->{incr_nest} <= 0 ) { - last INCR_PARSE; - } - } elsif ( $s eq '#' ) { - $self->{incr_mode} = INCR_M_C1; - redo INCR_PARSE; - } - } - } - } - - $self->{incr_pos} = $p; - $self->{incr_parsing} = $p ? 1 : 0; # for backward compatibility -} - - -sub incr_text { - if ( $_[0]->{incr_pos} ) { - Carp::croak("incr_text cannot be called when the incremental parser already started parsing"); - } - $_[0]->{incr_text}; -} - - -sub incr_skip { - my $self = shift; - $self->{incr_text} = substr( $self->{incr_text}, $self->{incr_pos} ); - $self->{incr_pos} = 0; - $self->{incr_mode} = 0; - $self->{incr_nest} = 0; -} - - -sub incr_reset { - my $self = shift; - $self->{incr_text} = undef; - $self->{incr_pos} = 0; - $self->{incr_mode} = 0; - $self->{incr_nest} = 0; -} - -############################### - - -1; -__END__ -=pod - -=head1 NAME - -JSON::PP - JSON::XS compatible pure-Perl module. - -=head1 SYNOPSIS - - use JSON::PP; - - # exported functions, they croak on error - # and expect/generate UTF-8 - - $utf8_encoded_json_text = encode_json $perl_hash_or_arrayref; - $perl_hash_or_arrayref = decode_json $utf8_encoded_json_text; - - # OO-interface - - $json = JSON::PP->new->ascii->pretty->allow_nonref; - - $pretty_printed_json_text = $json->encode( $perl_scalar ); - $perl_scalar = $json->decode( $json_text ); - - # Note that JSON version 2.0 and above will automatically use - # JSON::XS or JSON::PP, so you should be able to just: - - use JSON; - - -=head1 VERSION - - 4.05 - -=head1 DESCRIPTION - -JSON::PP is a pure perl JSON decoder/encoder, and (almost) compatible to much -faster L written by Marc Lehmann in C. JSON::PP works as -a fallback module when you use L module without having -installed JSON::XS. - -Because of this fallback feature of JSON.pm, JSON::PP tries not to -be more JavaScript-friendly than JSON::XS (i.e. not to escape extra -characters such as U+2028 and U+2029, etc), -in order for you not to lose such JavaScript-friendliness silently -when you use JSON.pm and install JSON::XS for speed or by accident. -If you need JavaScript-friendly RFC7159-compliant pure perl module, -try L, which is derived from L web -framework and is also smaller and faster than JSON::PP. - -JSON::PP has been in the Perl core since Perl 5.14, mainly for -CPAN toolchain modules to parse META.json. - -=head1 FUNCTIONAL INTERFACE - -This section is taken from JSON::XS almost verbatim. C -and C are exported by default. - -=head2 encode_json - - $json_text = encode_json $perl_scalar - -Converts the given Perl data structure to a UTF-8 encoded, binary string -(that is, the string contains octets only). Croaks on error. - -This function call is functionally identical to: - - $json_text = JSON::PP->new->utf8->encode($perl_scalar) - -Except being faster. - -=head2 decode_json - - $perl_scalar = decode_json $json_text - -The opposite of C: expects an UTF-8 (binary) string and tries -to parse that as an UTF-8 encoded JSON text, returning the resulting -reference. Croaks on error. - -This function call is functionally identical to: - - $perl_scalar = JSON::PP->new->utf8->decode($json_text) - -Except being faster. - -=head2 JSON::PP::is_bool - - $is_boolean = JSON::PP::is_bool($scalar) - -Returns true if the passed scalar represents either JSON::PP::true or -JSON::PP::false, two constants that act like C<1> and C<0> respectively -and are also used to represent JSON C and C in Perl strings. - -See L, below, for more information on how JSON values are mapped to -Perl. - -=head1 OBJECT-ORIENTED INTERFACE - -This section is also taken from JSON::XS. - -The object oriented interface lets you configure your own encoding or -decoding style, within the limits of supported formats. - -=head2 new - - $json = JSON::PP->new - -Creates a new JSON::PP object that can be used to de/encode JSON -strings. All boolean flags described below are by default I -(with the exception of C, which defaults to I since -version C<4.0>). - -The mutators for flags all return the JSON::PP object again and thus calls can -be chained: - - my $json = JSON::PP->new->utf8->space_after->encode({a => [1,2]}) - => {"a": [1, 2]} - -=head2 ascii - - $json = $json->ascii([$enable]) - - $enabled = $json->get_ascii - -If C<$enable> is true (or missing), then the C method will not -generate characters outside the code range C<0..127> (which is ASCII). Any -Unicode characters outside that range will be escaped using either a -single \uXXXX (BMP characters) or a double \uHHHH\uLLLLL escape sequence, -as per RFC4627. The resulting encoded JSON text can be treated as a native -Unicode string, an ascii-encoded, latin1-encoded or UTF-8 encoded string, -or any other superset of ASCII. - -If C<$enable> is false, then the C method will not escape Unicode -characters unless required by the JSON syntax or other flags. This results -in a faster and more compact format. - -See also the section I later in this document. - -The main use for this flag is to produce JSON texts that can be -transmitted over a 7-bit channel, as the encoded JSON texts will not -contain any 8 bit characters. - - JSON::PP->new->ascii(1)->encode([chr 0x10401]) - => ["\ud801\udc01"] - -=head2 latin1 - - $json = $json->latin1([$enable]) - - $enabled = $json->get_latin1 - -If C<$enable> is true (or missing), then the C method will encode -the resulting JSON text as latin1 (or iso-8859-1), escaping any characters -outside the code range C<0..255>. The resulting string can be treated as a -latin1-encoded JSON text or a native Unicode string. The C method -will not be affected in any way by this flag, as C by default -expects Unicode, which is a strict superset of latin1. - -If C<$enable> is false, then the C method will not escape Unicode -characters unless required by the JSON syntax or other flags. - -See also the section I later in this document. - -The main use for this flag is efficiently encoding binary data as JSON -text, as most octets will not be escaped, resulting in a smaller encoded -size. The disadvantage is that the resulting JSON text is encoded -in latin1 (and must correctly be treated as such when storing and -transferring), a rare encoding for JSON. It is therefore most useful when -you want to store data structures known to contain binary data efficiently -in files or databases, not when talking to other JSON encoders/decoders. - - JSON::PP->new->latin1->encode (["\x{89}\x{abc}"] - => ["\x{89}\\u0abc"] # (perl syntax, U+abc escaped, U+89 not) - -=head2 utf8 - - $json = $json->utf8([$enable]) - - $enabled = $json->get_utf8 - -If C<$enable> is true (or missing), then the C method will encode -the JSON result into UTF-8, as required by many protocols, while the -C method expects to be handled an UTF-8-encoded string. Please -note that UTF-8-encoded strings do not contain any characters outside the -range C<0..255>, they are thus useful for bytewise/binary I/O. In future -versions, enabling this option might enable autodetection of the UTF-16 -and UTF-32 encoding families, as described in RFC4627. - -If C<$enable> is false, then the C method will return the JSON -string as a (non-encoded) Unicode string, while C expects thus a -Unicode string. Any decoding or encoding (e.g. to UTF-8 or UTF-16) needs -to be done yourself, e.g. using the Encode module. - -See also the section I later in this document. - -Example, output UTF-16BE-encoded JSON: - - use Encode; - $jsontext = encode "UTF-16BE", JSON::PP->new->encode ($object); - -Example, decode UTF-32LE-encoded JSON: - - use Encode; - $object = JSON::PP->new->decode (decode "UTF-32LE", $jsontext); - -=head2 pretty - - $json = $json->pretty([$enable]) - -This enables (or disables) all of the C, C and -C (and in the future possibly more) flags in one call to -generate the most readable (or most compact) form possible. - -=head2 indent - - $json = $json->indent([$enable]) - - $enabled = $json->get_indent - -If C<$enable> is true (or missing), then the C method will use a multiline -format as output, putting every array member or object/hash key-value pair -into its own line, indenting them properly. - -If C<$enable> is false, no newlines or indenting will be produced, and the -resulting JSON text is guaranteed not to contain any C. - -This setting has no effect when decoding JSON texts. - -The default indent space length is three. -You can use C to change the length. - -=head2 space_before - - $json = $json->space_before([$enable]) - - $enabled = $json->get_space_before - -If C<$enable> is true (or missing), then the C method will add an extra -optional space before the C<:> separating keys from values in JSON objects. - -If C<$enable> is false, then the C method will not add any extra -space at those places. - -This setting has no effect when decoding JSON texts. You will also -most likely combine this setting with C. - -Example, space_before enabled, space_after and indent disabled: - - {"key" :"value"} - -=head2 space_after - - $json = $json->space_after([$enable]) - - $enabled = $json->get_space_after - -If C<$enable> is true (or missing), then the C method will add an extra -optional space after the C<:> separating keys from values in JSON objects -and extra whitespace after the C<,> separating key-value pairs and array -members. - -If C<$enable> is false, then the C method will not add any extra -space at those places. - -This setting has no effect when decoding JSON texts. - -Example, space_before and indent disabled, space_after enabled: - - {"key": "value"} - -=head2 relaxed - - $json = $json->relaxed([$enable]) - - $enabled = $json->get_relaxed - -If C<$enable> is true (or missing), then C will accept some -extensions to normal JSON syntax (see below). C will not be -affected in anyway. I. I suggest only to use this option to -parse application-specific files written by humans (configuration files, -resource files etc.) - -If C<$enable> is false (the default), then C will only accept -valid JSON texts. - -Currently accepted extensions are: - -=over 4 - -=item * list items can have an end-comma - -JSON I array elements and key-value pairs with commas. This -can be annoying if you write JSON texts manually and want to be able to -quickly append elements, so this extension accepts comma at the end of -such items not just between them: - - [ - 1, - 2, <- this comma not normally allowed - ] - { - "k1": "v1", - "k2": "v2", <- this comma not normally allowed - } - -=item * shell-style '#'-comments - -Whenever JSON allows whitespace, shell-style comments are additionally -allowed. They are terminated by the first carriage-return or line-feed -character, after which more white-space and comments are allowed. - - [ - 1, # this comment not allowed in JSON - # neither this one... - ] - -=item * C-style multiple-line '/* */'-comments (JSON::PP only) - -Whenever JSON allows whitespace, C-style multiple-line comments are additionally -allowed. Everything between C and C<*/> is a comment, after which -more white-space and comments are allowed. - - [ - 1, /* this comment not allowed in JSON */ - /* neither this one... */ - ] - -=item * C++-style one-line '//'-comments (JSON::PP only) - -Whenever JSON allows whitespace, C++-style one-line comments are additionally -allowed. They are terminated by the first carriage-return or line-feed -character, after which more white-space and comments are allowed. - - [ - 1, // this comment not allowed in JSON - // neither this one... - ] - -=item * literal ASCII TAB characters in strings - -Literal ASCII TAB characters are now allowed in strings (and treated as -C<\t>). - - [ - "Hello\tWorld", - "HelloWorld", # literal would not normally be allowed - ] - -=back - -=head2 canonical - - $json = $json->canonical([$enable]) - - $enabled = $json->get_canonical - -If C<$enable> is true (or missing), then the C method will output JSON objects -by sorting their keys. This is adding a comparatively high overhead. - -If C<$enable> is false, then the C method will output key-value -pairs in the order Perl stores them (which will likely change between runs -of the same script, and can change even within the same run from 5.18 -onwards). - -This option is useful if you want the same data structure to be encoded as -the same JSON text (given the same overall settings). If it is disabled, -the same hash might be encoded differently even if contains the same data, -as key-value pairs have no inherent ordering in Perl. - -This setting has no effect when decoding JSON texts. - -This setting has currently no effect on tied hashes. - -=head2 allow_nonref - - $json = $json->allow_nonref([$enable]) - - $enabled = $json->get_allow_nonref - -Unlike other boolean options, this opotion is enabled by default beginning -with version C<4.0>. - -If C<$enable> is true (or missing), then the C method can convert a -non-reference into its corresponding string, number or null JSON value, -which is an extension to RFC4627. Likewise, C will accept those JSON -values instead of croaking. - -If C<$enable> is false, then the C method will croak if it isn't -passed an arrayref or hashref, as JSON texts must either be an object -or array. Likewise, C will croak if given something that is not a -JSON object or array. - -Example, encode a Perl scalar as JSON value without enabled C, -resulting in an error: - - JSON::PP->new->allow_nonref(0)->encode ("Hello, World!") - => hash- or arrayref expected... - -=head2 allow_unknown - - $json = $json->allow_unknown([$enable]) - - $enabled = $json->get_allow_unknown - -If C<$enable> is true (or missing), then C will I throw an -exception when it encounters values it cannot represent in JSON (for -example, filehandles) but instead will encode a JSON C value. Note -that blessed objects are not included here and are handled separately by -c. - -If C<$enable> is false (the default), then C will throw an -exception when it encounters anything it cannot encode as JSON. - -This option does not affect C in any way, and it is recommended to -leave it off unless you know your communications partner. - -=head2 allow_blessed - - $json = $json->allow_blessed([$enable]) - - $enabled = $json->get_allow_blessed - -See L for details. - -If C<$enable> is true (or missing), then the C method will not -barf when it encounters a blessed reference that it cannot convert -otherwise. Instead, a JSON C value is encoded instead of the object. - -If C<$enable> is false (the default), then C will throw an -exception when it encounters a blessed object that it cannot convert -otherwise. - -This setting has no effect on C. - -=head2 convert_blessed - - $json = $json->convert_blessed([$enable]) - - $enabled = $json->get_convert_blessed - -See L for details. - -If C<$enable> is true (or missing), then C, upon encountering a -blessed object, will check for the availability of the C method -on the object's class. If found, it will be called in scalar context and -the resulting scalar will be encoded instead of the object. - -The C method may safely call die if it wants. If C -returns other blessed objects, those will be handled in the same -way. C must take care of not causing an endless recursion cycle -(== crash) in this case. The name of C was chosen because other -methods called by the Perl core (== not by the user of the object) are -usually in upper case letters and to avoid collisions with any C -function or method. - -If C<$enable> is false (the default), then C will not consider -this type of conversion. - -This setting has no effect on C. - -=head2 allow_tags - - $json = $json->allow_tags([$enable]) - - $enabled = $json->get_allow_tags - -See L for details. - -If C<$enable> is true (or missing), then C, upon encountering a -blessed object, will check for the availability of the C method on -the object's class. If found, it will be used to serialise the object into -a nonstandard tagged JSON value (that JSON decoders cannot decode). - -It also causes C to parse such tagged JSON values and deserialise -them via a call to the C method. - -If C<$enable> is false (the default), then C will not consider -this type of conversion, and tagged JSON values will cause a parse error -in C, as if tags were not part of the grammar. - -=head2 boolean_values - - $json->boolean_values([$false, $true]) - - ($false, $true) = $json->get_boolean_values - -By default, JSON booleans will be decoded as overloaded -C<$JSON::PP::false> and C<$JSON::PP::true> objects. - -With this method you can specify your own boolean values for decoding - -on decode, JSON C will be decoded as a copy of C<$false>, and JSON -C will be decoded as C<$true> ("copy" here is the same thing as -assigning a value to another variable, i.e. C<$copy = $false>). - -This is useful when you want to pass a decoded data structure directly -to other serialisers like YAML, Data::MessagePack and so on. - -Note that this works only when you C. You can set incompatible -boolean objects (like L), but when you C a data structure -with such boolean objects, you still need to enable C -(and add a C method if necessary). - -Calling this method without any arguments will reset the booleans -to their default values. - -C will return both C<$false> and C<$true> values, or -the empty list when they are set to the default. - -=head2 filter_json_object - - $json = $json->filter_json_object([$coderef]) - -When C<$coderef> is specified, it will be called from C each -time it decodes a JSON object. The only argument is a reference to -the newly-created hash. If the code references returns a single scalar -(which need not be a reference), this value (or rather a copy of it) is -inserted into the deserialised data structure. If it returns an empty -list (NOTE: I C, which is a valid scalar), the original -deserialised hash will be inserted. This setting can slow down decoding -considerably. - -When C<$coderef> is omitted or undefined, any existing callback will -be removed and C will not change the deserialised hash in any -way. - -Example, convert all JSON objects into the integer 5: - - my $js = JSON::PP->new->filter_json_object(sub { 5 }); - # returns [5] - $js->decode('[{}]'); - # returns 5 - $js->decode('{"a":1, "b":2}'); - -=head2 filter_json_single_key_object - - $json = $json->filter_json_single_key_object($key [=> $coderef]) - -Works remotely similar to C, but is only called for -JSON objects having a single key named C<$key>. - -This C<$coderef> is called before the one specified via -C, if any. It gets passed the single value in the JSON -object. If it returns a single value, it will be inserted into the data -structure. If it returns nothing (not even C but the empty list), -the callback from C will be called next, as if no -single-key callback were specified. - -If C<$coderef> is omitted or undefined, the corresponding callback will be -disabled. There can only ever be one callback for a given key. - -As this callback gets called less often then the C -one, decoding speed will not usually suffer as much. Therefore, single-key -objects make excellent targets to serialise Perl objects into, especially -as single-key JSON objects are as close to the type-tagged value concept -as JSON gets (it's basically an ID/VALUE tuple). Of course, JSON does not -support this in any way, so you need to make sure your data never looks -like a serialised Perl hash. - -Typical names for the single object key are C<__class_whatever__>, or -C<$__dollars_are_rarely_used__$> or C<}ugly_brace_placement>, or even -things like C<__class_md5sum(classname)__>, to reduce the risk of clashing -with real hashes. - -Example, decode JSON objects of the form C<< { "__widget__" => } >> -into the corresponding C<< $WIDGET{} >> object: - - # return whatever is in $WIDGET{5}: - JSON::PP - ->new - ->filter_json_single_key_object (__widget__ => sub { - $WIDGET{ $_[0] } - }) - ->decode ('{"__widget__": 5') - - # this can be used with a TO_JSON method in some "widget" class - # for serialisation to json: - sub WidgetBase::TO_JSON { - my ($self) = @_; - - unless ($self->{id}) { - $self->{id} = ..get..some..id..; - $WIDGET{$self->{id}} = $self; - } - - { __widget__ => $self->{id} } - } - -=head2 shrink - - $json = $json->shrink([$enable]) - - $enabled = $json->get_shrink - -If C<$enable> is true (or missing), the string returned by C will -be shrunk (i.e. downgraded if possible). - -The actual definition of what shrink does might change in future versions, -but it will always try to save space at the expense of time. - -If C<$enable> is false, then JSON::PP does nothing. - -=head2 max_depth - - $json = $json->max_depth([$maximum_nesting_depth]) - - $max_depth = $json->get_max_depth - -Sets the maximum nesting level (default C<512>) accepted while encoding -or decoding. If a higher nesting level is detected in JSON text or a Perl -data structure, then the encoder and decoder will stop and croak at that -point. - -Nesting level is defined by number of hash- or arrayrefs that the encoder -needs to traverse to reach a given point or the number of C<{> or C<[> -characters without their matching closing parenthesis crossed to reach a -given character in a string. - -Setting the maximum depth to one disallows any nesting, so that ensures -that the object is only a single hash/object or array. - -If no argument is given, the highest possible setting will be used, which -is rarely useful. - -See L for more info on why this is useful. - -=head2 max_size - - $json = $json->max_size([$maximum_string_size]) - - $max_size = $json->get_max_size - -Set the maximum length a JSON text may have (in bytes) where decoding is -being attempted. The default is C<0>, meaning no limit. When C -is called on a string that is longer then this many bytes, it will not -attempt to decode the string but throw an exception. This setting has no -effect on C (yet). - -If no argument is given, the limit check will be deactivated (same as when -C<0> is specified). - -See L for more info on why this is useful. - -=head2 encode - - $json_text = $json->encode($perl_scalar) - -Converts the given Perl value or data structure to its JSON -representation. Croaks on error. - -=head2 decode - - $perl_scalar = $json->decode($json_text) - -The opposite of C: expects a JSON text and tries to parse it, -returning the resulting simple scalar or reference. Croaks on error. - -=head2 decode_prefix - - ($perl_scalar, $characters) = $json->decode_prefix($json_text) - -This works like the C method, but instead of raising an exception -when there is trailing garbage after the first JSON object, it will -silently stop parsing there and return the number of characters consumed -so far. - -This is useful if your JSON texts are not delimited by an outer protocol -and you need to know where the JSON text ends. - - JSON::PP->new->decode_prefix ("[1] the tail") - => ([1], 3) - -=head1 FLAGS FOR JSON::PP ONLY - -The following flags and properties are for JSON::PP only. If you use -any of these, you can't make your application run faster by replacing -JSON::PP with JSON::XS. If you need these and also speed boost, -you might want to try L, a fork of JSON::XS by -Reini Urban, which supports some of these (with a different set of -incompatibilities). Most of these historical flags are only kept -for backward compatibility, and should not be used in a new application. - -=head2 allow_singlequote - - $json = $json->allow_singlequote([$enable]) - $enabled = $json->get_allow_singlequote - -If C<$enable> is true (or missing), then C will accept -invalid JSON texts that contain strings that begin and end with -single quotation marks. C will not be affected in any way. -I. I suggest only to use this option to -parse application-specific files written by humans (configuration -files, resource files etc.) - -If C<$enable> is false (the default), then C will only accept -valid JSON texts. - - $json->allow_singlequote->decode(qq|{"foo":'bar'}|); - $json->allow_singlequote->decode(qq|{'foo':"bar"}|); - $json->allow_singlequote->decode(qq|{'foo':'bar'}|); - -=head2 allow_barekey - - $json = $json->allow_barekey([$enable]) - $enabled = $json->get_allow_barekey - -If C<$enable> is true (or missing), then C will accept -invalid JSON texts that contain JSON objects whose names don't -begin and end with quotation marks. C will not be affected -in any way. I. I suggest only to use this option to -parse application-specific files written by humans (configuration -files, resource files etc.) - -If C<$enable> is false (the default), then C will only accept -valid JSON texts. - - $json->allow_barekey->decode(qq|{foo:"bar"}|); - -=head2 allow_bignum - - $json = $json->allow_bignum([$enable]) - $enabled = $json->get_allow_bignum - -If C<$enable> is true (or missing), then C will convert -big integers Perl cannot handle as integer into L -objects and convert floating numbers into L -objects. C will convert C and C -objects into JSON numbers. - - $json->allow_nonref->allow_bignum; - $bigfloat = $json->decode('2.000000000000000000000000001'); - print $json->encode($bigfloat); - # => 2.000000000000000000000000001 - -See also L. - -=head2 loose - - $json = $json->loose([$enable]) - $enabled = $json->get_loose - -If C<$enable> is true (or missing), then C will accept -invalid JSON texts that contain unescaped [\x00-\x1f\x22\x5c] -characters. C will not be affected in any way. -I. I suggest only to use this option to -parse application-specific files written by humans (configuration -files, resource files etc.) - -If C<$enable> is false (the default), then C will only accept -valid JSON texts. - - $json->loose->decode(qq|["abc - def"]|); - -=head2 escape_slash - - $json = $json->escape_slash([$enable]) - $enabled = $json->get_escape_slash - -If C<$enable> is true (or missing), then C will explicitly -escape I (solidus; C) characters to reduce the risk of -XSS (cross site scripting) that may be caused by C<< >> -in a JSON text, with the cost of bloating the size of JSON texts. - -This option may be useful when you embed JSON in HTML, but embedding -arbitrary JSON in HTML (by some HTML template toolkit or by string -interpolation) is risky in general. You must escape necessary -characters in correct order, depending on the context. - -C will not be affected in any way. - -=head2 indent_length - - $json = $json->indent_length($number_of_spaces) - $length = $json->get_indent_length - -This option is only useful when you also enable C or C. - -JSON::XS indents with three spaces when you C (if requested -by C or C), and the number cannot be changed. -JSON::PP allows you to change/get the number of indent spaces with these -mutator/accessor. The default number of spaces is three (the same as -JSON::XS), and the acceptable range is from C<0> (no indentation; -it'd be better to disable indentation by C) to C<15>. - -=head2 sort_by - - $json = $json->sort_by($code_ref) - $json = $json->sort_by($subroutine_name) - -If you just want to sort keys (names) in JSON objects when you -C, enable C option (see above) that allows you to -sort object keys alphabetically. - -If you do need to sort non-alphabetically for whatever reasons, -you can give a code reference (or a subroutine name) to C, -then the argument will be passed to Perl's C built-in function. - -As the sorting is done in the JSON::PP scope, you usually need to -prepend C to the subroutine name, and the special variables -C<$a> and C<$b> used in the subrontine used by C function. - -Example: - - my %ORDER = (id => 1, class => 2, name => 3); - $json->sort_by(sub { - ($ORDER{$JSON::PP::a} // 999) <=> ($ORDER{$JSON::PP::b} // 999) - or $JSON::PP::a cmp $JSON::PP::b - }); - print $json->encode([ - {name => 'CPAN', id => 1, href => 'http://cpan.org'} - ]); - # [{"id":1,"name":"CPAN","href":"http://cpan.org"}] - -Note that C affects all the plain hashes in the data structure. -If you need finer control, C necessary hashes with a module that -implements ordered hash (such as L and L). -C and C don't affect the key order in Cd -hashes. - - use Hash::Ordered; - tie my %hash, 'Hash::Ordered', - (name => 'CPAN', id => 1, href => 'http://cpan.org'); - print $json->encode([\%hash]); - # [{"name":"CPAN","id":1,"href":"http://cpan.org"}] # order is kept - -=head1 INCREMENTAL PARSING - -This section is also taken from JSON::XS. - -In some cases, there is the need for incremental parsing of JSON -texts. While this module always has to keep both JSON text and resulting -Perl data structure in memory at one time, it does allow you to parse a -JSON stream incrementally. It does so by accumulating text until it has -a full JSON object, which it then can decode. This process is similar to -using C to see if a full JSON object is available, but -is much more efficient (and can be implemented with a minimum of method -calls). - -JSON::PP will only attempt to parse the JSON text once it is sure it -has enough text to get a decisive result, using a very simple but -truly incremental parser. This means that it sometimes won't stop as -early as the full parser, for example, it doesn't detect mismatched -parentheses. The only thing it guarantees is that it starts decoding as -soon as a syntactically valid JSON text has been seen. This means you need -to set resource limits (e.g. C) to ensure the parser will stop -parsing in the presence if syntax errors. - -The following methods implement this incremental parser. - -=head2 incr_parse - - $json->incr_parse( [$string] ) # void context - - $obj_or_undef = $json->incr_parse( [$string] ) # scalar context - - @obj_or_empty = $json->incr_parse( [$string] ) # list context - -This is the central parsing function. It can both append new text and -extract objects from the stream accumulated so far (both of these -functions are optional). - -If C<$string> is given, then this string is appended to the already -existing JSON fragment stored in the C<$json> object. - -After that, if the function is called in void context, it will simply -return without doing anything further. This can be used to add more text -in as many chunks as you want. - -If the method is called in scalar context, then it will try to extract -exactly I JSON object. If that is successful, it will return this -object, otherwise it will return C. If there is a parse error, -this method will croak just as C would do (one can then use -C to skip the erroneous part). This is the most common way of -using the method. - -And finally, in list context, it will try to extract as many objects -from the stream as it can find and return them, or the empty list -otherwise. For this to work, there must be no separators (other than -whitespace) between the JSON objects or arrays, instead they must be -concatenated back-to-back. If an error occurs, an exception will be -raised as in the scalar context case. Note that in this case, any -previously-parsed JSON texts will be lost. - -Example: Parse some JSON arrays/objects in a given string and return -them. - - my @objs = JSON::PP->new->incr_parse ("[5][7][1,2]"); - -=head2 incr_text - - $lvalue_string = $json->incr_text - -This method returns the currently stored JSON fragment as an lvalue, that -is, you can manipulate it. This I works when a preceding call to -C in I successfully returned an object. Under -all other circumstances you must not call this function (I mean it. -although in simple tests it might actually work, it I fail under -real world conditions). As a special exception, you can also call this -method before having parsed anything. - -That means you can only use this function to look at or manipulate text -before or after complete JSON objects, not while the parser is in the -middle of parsing a JSON object. - -This function is useful in two cases: a) finding the trailing text after a -JSON object or b) parsing multiple JSON objects separated by non-JSON text -(such as commas). - -=head2 incr_skip - - $json->incr_skip - -This will reset the state of the incremental parser and will remove -the parsed text from the input buffer so far. This is useful after -C died, in which case the input buffer and incremental parser -state is left unchanged, to skip the text parsed so far and to reset the -parse state. - -The difference to C is that only text until the parse error -occurred is removed. - -=head2 incr_reset - - $json->incr_reset - -This completely resets the incremental parser, that is, after this call, -it will be as if the parser had never parsed anything. - -This is useful if you want to repeatedly parse JSON objects and want to -ignore any trailing data, which means you have to reset the parser after -each successful decode. - -=head1 MAPPING - -Most of this section is also taken from JSON::XS. - -This section describes how JSON::PP maps Perl values to JSON values and -vice versa. These mappings are designed to "do the right thing" in most -circumstances automatically, preserving round-tripping characteristics -(what you put in comes out as something equivalent). - -For the more enlightened: note that in the following descriptions, -lowercase I refers to the Perl interpreter, while uppercase I -refers to the abstract Perl language itself. - -=head2 JSON -> PERL - -=over 4 - -=item object - -A JSON object becomes a reference to a hash in Perl. No ordering of object -keys is preserved (JSON does not preserve object key ordering itself). - -=item array - -A JSON array becomes a reference to an array in Perl. - -=item string - -A JSON string becomes a string scalar in Perl - Unicode codepoints in JSON -are represented by the same codepoints in the Perl string, so no manual -decoding is necessary. - -=item number - -A JSON number becomes either an integer, numeric (floating point) or -string scalar in perl, depending on its range and any fractional parts. On -the Perl level, there is no difference between those as Perl handles all -the conversion details, but an integer may take slightly less memory and -might represent more values exactly than floating point numbers. - -If the number consists of digits only, JSON::PP will try to represent -it as an integer value. If that fails, it will try to represent it as -a numeric (floating point) value if that is possible without loss of -precision. Otherwise it will preserve the number as a string value (in -which case you lose roundtripping ability, as the JSON number will be -re-encoded to a JSON string). - -Numbers containing a fractional or exponential part will always be -represented as numeric (floating point) values, possibly at a loss of -precision (in which case you might lose perfect roundtripping ability, but -the JSON number will still be re-encoded as a JSON number). - -Note that precision is not accuracy - binary floating point values cannot -represent most decimal fractions exactly, and when converting from and to -floating point, JSON::PP only guarantees precision up to but not including -the least significant bit. - -When C is enabled, big integer values and any numeric -values will be converted into L and L -objects respectively, without becoming string scalars or losing -precision. - -=item true, false - -These JSON atoms become C and C, -respectively. They are overloaded to act almost exactly like the numbers -C<1> and C<0>. You can check whether a scalar is a JSON boolean by using -the C function. - -=item null - -A JSON null atom becomes C in Perl. - -=item shell-style comments (C<< # I >>) - -As a nonstandard extension to the JSON syntax that is enabled by the -C setting, shell-style comments are allowed. They can start -anywhere outside strings and go till the end of the line. - -=item tagged values (C<< (I)I >>). - -Another nonstandard extension to the JSON syntax, enabled with the -C setting, are tagged values. In this implementation, the -I must be a perl package/class name encoded as a JSON string, and the -I must be a JSON array encoding optional constructor arguments. - -See L, below, for details. - -=back - - -=head2 PERL -> JSON - -The mapping from Perl to JSON is slightly more difficult, as Perl is a -truly typeless language, so we can only guess which JSON type is meant by -a Perl value. - -=over 4 - -=item hash references - -Perl hash references become JSON objects. As there is no inherent -ordering in hash keys (or JSON objects), they will usually be encoded -in a pseudo-random order. JSON::PP can optionally sort the hash keys -(determined by the I flag and/or I property), so -the same data structure will serialise to the same JSON text (given -same settings and version of JSON::PP), but this incurs a runtime -overhead and is only rarely useful, e.g. when you want to compare some -JSON text against another for equality. - -=item array references - -Perl array references become JSON arrays. - -=item other references - -Other unblessed references are generally not allowed and will cause an -exception to be thrown, except for references to the integers C<0> and -C<1>, which get turned into C and C atoms in JSON. You can -also use C and C to improve -readability. - - to_json [\0, JSON::PP::true] # yields [false,true] - -=item JSON::PP::true, JSON::PP::false - -These special values become JSON true and JSON false values, -respectively. You can also use C<\1> and C<\0> directly if you want. - -=item JSON::PP::null - -This special value becomes JSON null. - -=item blessed objects - -Blessed objects are not directly representable in JSON, but C -allows various ways of handling objects. See L, -below, for details. - -=item simple scalars - -Simple Perl scalars (any scalar that is not a reference) are the most -difficult objects to encode: JSON::PP will encode undefined scalars as -JSON C values, scalars that have last been used in a string context -before encoding as JSON strings, and anything else as number value: - - # dump as number - encode_json [2] # yields [2] - encode_json [-3.0e17] # yields [-3e+17] - my $value = 5; encode_json [$value] # yields [5] - - # used as string, so dump as string - print $value; - encode_json [$value] # yields ["5"] - - # undef becomes null - encode_json [undef] # yields [null] - -You can force the type to be a JSON string by stringifying it: - - my $x = 3.1; # some variable containing a number - "$x"; # stringified - $x .= ""; # another, more awkward way to stringify - print $x; # perl does it for you, too, quite often - # (but for older perls) - -You can force the type to be a JSON number by numifying it: - - my $x = "3"; # some variable containing a string - $x += 0; # numify it, ensuring it will be dumped as a number - $x *= 1; # same thing, the choice is yours. - -You can not currently force the type in other, less obscure, ways. - -Since version 2.91_01, JSON::PP uses a different number detection logic -that converts a scalar that is possible to turn into a number safely. -The new logic is slightly faster, and tends to help people who use older -perl or who want to encode complicated data structure. However, this may -results in a different JSON text from the one JSON::XS encodes (and -thus may break tests that compare entire JSON texts). If you do -need the previous behavior for compatibility or for finer control, -set PERL_JSON_PP_USE_B environmental variable to true before you -C JSON::PP (or JSON.pm). - -Note that numerical precision has the same meaning as under Perl (so -binary to decimal conversion follows the same rules as in Perl, which -can differ to other languages). Also, your perl interpreter might expose -extensions to the floating point numbers of your platform, such as -infinities or NaN's - these cannot be represented in JSON, and it is an -error to pass those in. - -JSON::PP (and JSON::XS) trusts what you pass to C method -(or C function) is a clean, validated data structure with -values that can be represented as valid JSON values only, because it's -not from an external data source (as opposed to JSON texts you pass to -C or C, which JSON::PP considers tainted and -doesn't trust). As JSON::PP doesn't know exactly what you and consumers -of your JSON texts want the unexpected values to be (you may want to -convert them into null, or to stringify them with or without -normalisation (string representation of infinities/NaN may vary -depending on platforms), or to croak without conversion), you're advised -to do what you and your consumers need before you encode, and also not -to numify values that may start with values that look like a number -(including infinities/NaN), without validating. - -=back - -=head2 OBJECT SERIALISATION - -As JSON cannot directly represent Perl objects, you have to choose between -a pure JSON representation (without the ability to deserialise the object -automatically again), and a nonstandard extension to the JSON syntax, -tagged values. - -=head3 SERIALISATION - -What happens when C encounters a Perl object depends on the -C, C, C and C -settings, which are used in this order: - -=over 4 - -=item 1. C is enabled and the object has a C method. - -In this case, C creates a tagged JSON value, using a nonstandard -extension to the JSON syntax. - -This works by invoking the C method on the object, with the first -argument being the object to serialise, and the second argument being the -constant string C to distinguish it from other serialisers. - -The C method can return any number of values (i.e. zero or -more). These values and the paclkage/classname of the object will then be -encoded as a tagged JSON value in the following format: - - ("classname")[FREEZE return values...] - -e.g.: - - ("URI")["http://www.google.com/"] - ("MyDate")[2013,10,29] - ("ImageData::JPEG")["Z3...VlCg=="] - -For example, the hypothetical C C method might use the -objects C and C members to encode the object: - - sub My::Object::FREEZE { - my ($self, $serialiser) = @_; - - ($self->{type}, $self->{id}) - } - -=item 2. C is enabled and the object has a C method. - -In this case, the C method of the object is invoked in scalar -context. It must return a single scalar that can be directly encoded into -JSON. This scalar replaces the object in the JSON text. - -For example, the following C method will convert all L -objects to JSON strings when serialised. The fact that these values -originally were L objects is lost. - - sub URI::TO_JSON { - my ($uri) = @_; - $uri->as_string - } - -=item 3. C is enabled and the object is a C or C. - -The object will be serialised as a JSON number value. - -=item 4. C is enabled. - -The object will be serialised as a JSON null value. - -=item 5. none of the above - -If none of the settings are enabled or the respective methods are missing, -C throws an exception. - -=back - -=head3 DESERIALISATION - -For deserialisation there are only two cases to consider: either -nonstandard tagging was used, in which case C decides, -or objects cannot be automatically be deserialised, in which -case you can use postprocessing or the C or -C callbacks to get some real objects our of -your JSON. - -This section only considers the tagged value case: a tagged JSON object -is encountered during decoding and C is disabled, a parse -error will result (as if tagged values were not part of the grammar). - -If C is enabled, C will look up the C method -of the package/classname used during serialisation (it will not attempt -to load the package as a Perl module). If there is no such method, the -decoding will fail with an error. - -Otherwise, the C method is invoked with the classname as first -argument, the constant string C as second argument, and all the -values from the JSON array (the values originally returned by the -C method) as remaining arguments. - -The method must then return the object. While technically you can return -any Perl scalar, you might have to enable the C setting to -make that work in all cases, so better return an actual blessed reference. - -As an example, let's implement a C function that regenerates the -C from the C example earlier: - - sub My::Object::THAW { - my ($class, $serialiser, $type, $id) = @_; - - $class->new (type => $type, id => $id) - } - - -=head1 ENCODING/CODESET FLAG NOTES - -This section is taken from JSON::XS. - -The interested reader might have seen a number of flags that signify -encodings or codesets - C, C and C. There seems to be -some confusion on what these do, so here is a short comparison: - -C controls whether the JSON text created by C (and expected -by C) is UTF-8 encoded or not, while C and C only -control whether C escapes character values outside their respective -codeset range. Neither of these flags conflict with each other, although -some combinations make less sense than others. - -Care has been taken to make all flags symmetrical with respect to -C and C, that is, texts encoded with any combination of -these flag values will be correctly decoded when the same flags are used -- in general, if you use different flag settings while encoding vs. when -decoding you likely have a bug somewhere. - -Below comes a verbose discussion of these flags. Note that a "codeset" is -simply an abstract set of character-codepoint pairs, while an encoding -takes those codepoint numbers and I them, in our case into -octets. Unicode is (among other things) a codeset, UTF-8 is an encoding, -and ISO-8859-1 (= latin 1) and ASCII are both codesets I encodings at -the same time, which can be confusing. - -=over 4 - -=item C flag disabled - -When C is disabled (the default), then C/C generate -and expect Unicode strings, that is, characters with high ordinal Unicode -values (> 255) will be encoded as such characters, and likewise such -characters are decoded as-is, no changes to them will be done, except -"(re-)interpreting" them as Unicode codepoints or Unicode characters, -respectively (to Perl, these are the same thing in strings unless you do -funny/weird/dumb stuff). - -This is useful when you want to do the encoding yourself (e.g. when you -want to have UTF-16 encoded JSON texts) or when some other layer does -the encoding for you (for example, when printing to a terminal using a -filehandle that transparently encodes to UTF-8 you certainly do NOT want -to UTF-8 encode your data first and have Perl encode it another time). - -=item C flag enabled - -If the C-flag is enabled, C/C will encode all -characters using the corresponding UTF-8 multi-byte sequence, and will -expect your input strings to be encoded as UTF-8, that is, no "character" -of the input string must have any value > 255, as UTF-8 does not allow -that. - -The C flag therefore switches between two modes: disabled means you -will get a Unicode string in Perl, enabled means you get an UTF-8 encoded -octet/binary string in Perl. - -=item C or C flags enabled - -With C (or C) enabled, C will escape characters -with ordinal values > 255 (> 127 with C) and encode the remaining -characters as specified by the C flag. - -If C is disabled, then the result is also correctly encoded in those -character sets (as both are proper subsets of Unicode, meaning that a -Unicode string with all character values < 256 is the same thing as a -ISO-8859-1 string, and a Unicode string with all character values < 128 is -the same thing as an ASCII string in Perl). - -If C is enabled, you still get a correct UTF-8-encoded string, -regardless of these flags, just some more characters will be escaped using -C<\uXXXX> then before. - -Note that ISO-8859-1-I strings are not compatible with UTF-8 -encoding, while ASCII-encoded strings are. That is because the ISO-8859-1 -encoding is NOT a subset of UTF-8 (despite the ISO-8859-1 I being -a subset of Unicode), while ASCII is. - -Surprisingly, C will ignore these flags and so treat all input -values as governed by the C flag. If it is disabled, this allows you -to decode ISO-8859-1- and ASCII-encoded strings, as both strict subsets of -Unicode. If it is enabled, you can correctly decode UTF-8 encoded strings. - -So neither C nor C are incompatible with the C flag - -they only govern when the JSON output engine escapes a character or not. - -The main use for C is to relatively efficiently store binary data -as JSON, at the expense of breaking compatibility with most JSON decoders. - -The main use for C is to force the output to not contain characters -with values > 127, which means you can interpret the resulting string -as UTF-8, ISO-8859-1, ASCII, KOI8-R or most about any character set and -8-bit-encoding, and still get the same data structure back. This is useful -when your channel for JSON transfer is not 8-bit clean or the encoding -might be mangled in between (e.g. in mail), and works because ASCII is a -proper subset of most 8-bit and multibyte encodings in use in the world. - -=back - -=head1 BUGS - -Please report bugs on a specific behavior of this module to RT or GitHub -issues (preferred): - -L - -L - -As for new features and requests to change common behaviors, please -ask the author of JSON::XS (Marc Lehmann, Eschmorp[at]schmorp.deE) -first, by email (important!), to keep compatibility among JSON.pm backends. - -Generally speaking, if you need something special for you, you are advised -to create a new module, maybe based on L, which is smaller and -written in a much cleaner way than this module. - -=head1 SEE ALSO - -The F command line utility for quick experiments. - -L, L, and L for faster alternatives. -L and L for easy migration. - -L and L for older perl users. - -RFC4627 (L) - -RFC7159 (L) - -RFC8259 (L) - -=head1 AUTHOR - -Makamaka Hannyaharamitu, Emakamaka[at]cpan.orgE - -=head1 CURRENT MAINTAINER - -Kenichi Ishigaki, Eishigaki[at]cpan.orgE - -=head1 COPYRIGHT AND LICENSE - -Copyright 2007-2016 by Makamaka Hannyaharamitu - -Most of the documentation is taken from JSON::XS by Marc Lehmann - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself. - -=cut diff --git a/thirdparty/rr-full/JSON/PP/Boolean.pm b/thirdparty/rr-full/JSON/PP/Boolean.pm deleted file mode 100644 index 5d5b17c2368..00000000000 --- a/thirdparty/rr-full/JSON/PP/Boolean.pm +++ /dev/null @@ -1,42 +0,0 @@ -package JSON::PP::Boolean; - -use strict; -require overload; -local $^W; -overload::import('overload', - "0+" => sub { ${$_[0]} }, - "++" => sub { $_[0] = ${$_[0]} + 1 }, - "--" => sub { $_[0] = ${$_[0]} - 1 }, - fallback => 1, -); - -$JSON::PP::Boolean::VERSION = '4.05'; - -1; - -__END__ - -=head1 NAME - -JSON::PP::Boolean - dummy module providing JSON::PP::Boolean - -=head1 SYNOPSIS - - # do not "use" yourself - -=head1 DESCRIPTION - -This module exists only to provide overload resolution for Storable and similar modules. See -L for more info about this class. - -=head1 AUTHOR - -This idea is from L written by Marc Lehmann - -=head1 LICENSE - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself. - -=cut - diff --git a/thirdparty/rr-full/Key.pm b/thirdparty/rr-full/Key.pm deleted file mode 100644 index 1263deb41aa..00000000000 --- a/thirdparty/rr-full/Key.pm +++ /dev/null @@ -1,464 +0,0 @@ -package Parse::Win32Registry::WinNT::Key; - -use strict; -use warnings; - -use base qw(Parse::Win32Registry::Key); - -use Carp; -use Encode; -use Parse::Win32Registry::Base qw(:all); -use Parse::Win32Registry::WinNT::Value; -use Parse::Win32Registry::WinNT::Security; - -use constant NK_HEADER_LENGTH => 0x50; -use constant OFFSET_TO_FIRST_HBIN => 0x1000; - -sub new { - my $class = shift; - my $regfile = shift; - my $offset = shift; # offset to nk record relative to start of file - my $parent_key_path = shift; # parent key path (optional) - - croak 'Missing registry file' if !defined $regfile; - croak 'Missing offset' if !defined $offset; - - my $fh = $regfile->get_filehandle; - - # 0x00 dword = key length (negative = allocated) - # 0x04 word = 'nk' signature - # 0x06 word = flags - # 0x08 qword = timestamp - # 0x10 - # 0x14 dword = offset to parent - # 0x18 dword = number of subkeys - # 0x1c - # 0x20 dword = offset to subkey list (lf, lh, ri, li) - # 0x24 - # 0x28 dword = number of values - # 0x2c dword = offset to value list - # 0x30 dword = offset to security - # 0x34 dword = offset to class name - # 0x38 dword = max subkey name length - # 0x3c dword = max class name length - # 0x40 dword = max value name length - # 0x44 dword = max value data length - # 0x48 - # 0x4c word = key name length - # 0x4e word = class name length - # 0x50 = key name [for key name length bytes] - - # Extracted offsets are always relative to first hbin - - sysseek($fh, $offset, 0); - my $bytes_read = sysread($fh, my $nk_header, NK_HEADER_LENGTH); - if ($bytes_read != NK_HEADER_LENGTH) { - warnf('Could not read key at 0x%x', $offset); - return; - } - - my ($length, - $sig, - $flags, - $timestamp, -# added 20190127 - $access_bits, - $offset_to_parent, - $num_subkeys, - $offset_to_subkey_list, - $num_values, - $offset_to_value_list, - $offset_to_security, - $offset_to_class_name, - $largest_subkey_name_length, - $name_length, - $class_name_length, -# added 20190127 - ) = unpack('Va2va8VVVx4Vx4VVVVVx16vv', $nk_header); -# ) = unpack('Va2va8x4VVx4Vx4VVVVx20vv', $nk_header); - - $offset_to_parent += OFFSET_TO_FIRST_HBIN - if $offset_to_parent != 0xffffffff; - $offset_to_subkey_list += OFFSET_TO_FIRST_HBIN - if $offset_to_subkey_list != 0xffffffff; - $offset_to_value_list += OFFSET_TO_FIRST_HBIN - if $offset_to_value_list != 0xffffffff; - $offset_to_security += OFFSET_TO_FIRST_HBIN - if $offset_to_security != 0xffffffff; - $offset_to_class_name += OFFSET_TO_FIRST_HBIN - if $offset_to_class_name != 0xffffffff; - - my $allocated = 0; - if ($length > 0x7fffffff) { - $allocated = 1; - $length = (0xffffffff - $length) + 1; - } - # allocated should be true - - if ($length < NK_HEADER_LENGTH) { - warnf('Invalid value entry length at 0x%x', $offset); - return; - } - - if ($sig ne 'nk') { - warnf('Invalid signature for key at 0x%x', $offset); - return; - } - - $bytes_read = sysread($fh, my $name, $name_length); - if ($bytes_read != $name_length) { - warnf('Could not read name for key at 0x%x', $offset); - return; - } - - if ($flags & 0x20) { - $name = decode($Parse::Win32Registry::Base::CODEPAGE, $name); - } - else { - $name = decode('UCS-2LE', $name); - } - - my $key_path = (defined $parent_key_path) - ? "$parent_key_path\\$name" - : "$name"; - - my $class_name; - if ($offset_to_class_name != 0xffffffff) { - sysseek($fh, $offset_to_class_name + 4, 0); - $bytes_read = sysread($fh, $class_name, $class_name_length); - if ($bytes_read != $class_name_length) { - warnf('Could not read class name at 0x%x', $offset_to_class_name); - $class_name = undef; - } - else { - $class_name = decode('UCS-2LE', $class_name); - } - } - - my $self = {}; - $self->{_regfile} = $regfile; - $self->{_offset} = $offset; - $self->{_length} = $length; - $self->{_allocated} = $allocated; - $self->{_tag} = $sig; - $self->{_name} = $name; - $self->{_name_length} = $name_length; - $self->{_key_path} = $key_path; - $self->{_flags} = $flags; - $self->{_offset_to_parent} = $offset_to_parent; - $self->{_num_subkeys} = $num_subkeys; - $self->{_offset_to_subkey_list} = $offset_to_subkey_list; - $self->{_num_values} = $num_values; - $self->{_offset_to_value_list} = $offset_to_value_list; - $self->{_timestamp} = unpack_windows_time($timestamp); -# added 20190127 - $self->{_access_bits} = $access_bits; - $self->{_largest_subkey_name_length} = $largest_subkey_name_length; - $self->{_offset_to_security} = $offset_to_security; - $self->{_offset_to_class_name} = $offset_to_class_name; - $self->{_class_name_length} = $class_name_length; - $self->{_class_name} = $class_name; - bless $self, $class; - - return $self; -} - -sub get_timestamp { - my $self = shift; - - return $self->{_timestamp}; -} - -sub get_timestamp_as_string { - my $self = shift; - - return iso8601($self->get_timestamp); -} - -# added 20190127 -sub get_access_bits { - my $self = shift; - return $self->{_access_bits}; -} - -sub get_largest_subkey_name_length { - my $self = shift; - return $self->{_largest_subkey_name_length}; -} - - -sub get_class_name { - my $self = shift; - - return $self->{_class_name}; -} - -sub is_root { - my $self = shift; - - my $flags = $self->{_flags}; - return $flags & 4 || $flags & 8; -} - -sub get_parent { - my $self = shift; - - my $regfile = $self->{_regfile}; - my $offset_to_parent = $self->{_offset_to_parent}; - my $key_path = $self->{_key_path}; - - return if $self->is_root; - - my $grandparent_key_path; - my @keys = split /\\/, $key_path, -1; - if (@keys > 2) { - $grandparent_key_path = join('\\', @keys[0..$#keys-2]); - } - - return Parse::Win32Registry::WinNT::Key->new($regfile, - $offset_to_parent, - $grandparent_key_path); -} - -sub get_security { - my $self = shift; - - my $regfile = $self->{_regfile}; - my $offset_to_security = $self->{_offset_to_security}; - my $key_path = $self->{_key_path}; - - if ($offset_to_security == 0xffffffff) { - return; - } - - return Parse::Win32Registry::WinNT::Security->new($regfile, - $offset_to_security, - $key_path); -} - -sub as_string { - my $self = shift; - - my $string = $self->get_path . ' [' . $self->get_timestamp_as_string . ']'; - return $string; -} - -sub parse_info { - my $self = shift; - - my $info = sprintf '0x%x nk len=0x%x alloc=%d "%s" par=0x%x keys=%d,0x%x vals=%d,0x%x sec=0x%x class=0x%x', - $self->{_offset}, - $self->{_length}, - $self->{_allocated}, - $self->{_name}, - $self->{_offset_to_parent}, - $self->{_num_subkeys}, $self->{_offset_to_subkey_list}, - $self->{_num_values}, $self->{_offset_to_value_list}, - $self->{_offset_to_security}, - $self->{_offset_to_class_name}; - if (defined $self->{_class_name}) { - $info .= sprintf ',len=0x%x', $self->{_class_name_length}; - } - return $info; -} - -sub _get_offsets_to_subkeys { - my $self = shift; - - # Offset is passed as a parameter for recursive lists such as 'ri' - my $offset_to_subkey_list = shift || $self->{_offset_to_subkey_list}; - - my $regfile = $self->{_regfile}; - my $fh = $regfile->get_filehandle; - - return if $offset_to_subkey_list == 0xffffffff - || $self->{_num_subkeys} == 0; - - sysseek($fh, $offset_to_subkey_list, 0); - my $bytes_read = sysread($fh, my $subkey_list_header, 8); - if ($bytes_read != 8) { - warnf('Could not read subkey list header at 0x%x', - $offset_to_subkey_list); - return; - } - - # 0x00 dword = subkey list length (negative = allocated) - # 0x04 word = 'lf' signature - # 0x06 word = number of entries - # 0x08 dword = offset to 1st subkey - # 0x0c dword = first four characters of the key name - # 0x10 dword = offset to 2nd subkey - # 0x14 dword = first four characters of the key name - # ... - - # 0x00 dword = subkey list length (negative = allocated) - # 0x04 word = 'lh' signature - # 0x06 word = number of entries - # 0x08 dword = offset to 1st subkey - # 0x0c dword = hash of the key name - # 0x10 dword = offset to 2nd subkey - # 0x14 dword = hash of the key name - # ... - - # 0x00 dword = subkey list length (negative = allocated) - # 0x04 word = 'ri' signature - # 0x06 word = number of entries in ri list - # 0x08 dword = offset to 1st lf/lh/li list - # 0x0c dword = offset to 2nd lf/lh/li list - # 0x10 dword = offset to 3rd lf/lh/li list - # ... - - # 0x00 dword = subkey list length (negative = allocated) - # 0x04 word = 'li' signature - # 0x06 word = number of entries in li list - # 0x08 dword = offset to 1st subkey - # 0x0c dword = offset to 2nd subkey - # ... - - # Extracted offsets are always relative to first hbin - - my @offsets_to_subkeys = (); - - my ($length, - $sig, - $num_entries, - ) = unpack('Va2v', $subkey_list_header); - - my $subkey_list_length; - if ($sig eq 'lf' || $sig eq 'lh') { - $subkey_list_length = 2 * 4 * $num_entries; - } - elsif ($sig eq 'ri' || $sig eq 'li') { - $subkey_list_length = 4 * $num_entries; - } - else { - warnf('Invalid signature for subkey list at 0x%x', - $offset_to_subkey_list); - return; - } - - $bytes_read = sysread($fh, my $subkey_list, $subkey_list_length); - if ($bytes_read != $subkey_list_length) { - warnf('Could not read subkey list at 0x%x', - $offset_to_subkey_list); - return; - } - - if ($sig eq 'lf') { - foreach my $offset (unpack("(Vx4)$num_entries", $subkey_list)) { - push @offsets_to_subkeys, OFFSET_TO_FIRST_HBIN + $offset; - } - } - elsif ($sig eq 'lh') { - foreach my $offset (unpack("(Vx4)$num_entries", $subkey_list)) { - push @offsets_to_subkeys, OFFSET_TO_FIRST_HBIN + $offset; - } - } - elsif ($sig eq 'ri') { - foreach my $offset (unpack("V$num_entries", $subkey_list)) { - my $offsets_ref = - $self->_get_offsets_to_subkeys(OFFSET_TO_FIRST_HBIN + $offset); - if (defined $offsets_ref && ref $offsets_ref eq 'ARRAY') { - push @offsets_to_subkeys, @{ $offsets_ref }; - } - } - } - elsif ($sig eq 'li') { - foreach my $offset (unpack("V$num_entries", $subkey_list)) { - push @offsets_to_subkeys, OFFSET_TO_FIRST_HBIN + $offset; - } - } - - return \@offsets_to_subkeys; -} - -sub get_subkey_iterator { - my $self = shift; - - my $regfile = $self->{_regfile}; - my $key_path = $self->{_key_path}; - - my @offsets_to_subkeys = (); - if ($self->{_num_subkeys} > 0) { - my $offsets_to_subkeys_ref = $self->_get_offsets_to_subkeys; - if (defined $offsets_to_subkeys_ref) { - @offsets_to_subkeys = @{$self->_get_offsets_to_subkeys}; - } - } - - return Parse::Win32Registry::Iterator->new(sub { - while (defined(my $offset_to_subkey = shift @offsets_to_subkeys)) { - my $subkey = Parse::Win32Registry::WinNT::Key->new($regfile, - $offset_to_subkey, $key_path); - if (defined $subkey) { - return $subkey; - } - } - return; # no more offsets to subkeys - }); -} - -sub _get_offsets_to_values { - my $self = shift; - - my $regfile = $self->{_regfile}; - my $fh = $regfile->get_filehandle; - my $offset_to_value_list = $self->{_offset_to_value_list}; - - my $num_values = $self->{_num_values}; - return if $num_values == 0; - # Actually, this could probably just fall through - # as unpack("x4V0", ...) would return an empty array. - - my @offsets_to_values = (); - - # 0x00 dword = value list length (negative = allocated) - # 0x04 dword = 1st offset - # 0x08 dword = 2nd offset - # ... - - # Extracted offsets are always relative to first hbin - - sysseek($fh, $offset_to_value_list, 0); - my $value_list_length = 0x4 + $num_values * 4; - my $bytes_read = sysread($fh, my $value_list, $value_list_length); - if ($bytes_read != $value_list_length) { - warnf("Could not read value list at 0x%x", - $offset_to_value_list); - return; - } - - foreach my $offset (unpack("x4V$num_values", $value_list)) { - push @offsets_to_values, OFFSET_TO_FIRST_HBIN + $offset; - } - - return \@offsets_to_values; -} - -sub get_value_iterator { - my $self = shift; - - my $regfile = $self->{_regfile}; - my $key_path = $self->{_key_path}; - - my @offsets_to_values = (); - if ($self->{_num_values} > 0) { - my $offsets_to_values_ref = $self->_get_offsets_to_values; - if (defined $offsets_to_values_ref) { - @offsets_to_values = @{$self->_get_offsets_to_values}; - } - } - - return Parse::Win32Registry::Iterator->new(sub { - while (defined(my $offset_to_value = shift @offsets_to_values)) { - my $value = Parse::Win32Registry::WinNT::Value->new($regfile, - $offset_to_value); - if (defined $value) { - return $value; - } - } - return; # no more offsets to values - }); -} - -1; diff --git a/thirdparty/rr-full/Parse/Win32Registry.pm b/thirdparty/rr-full/Parse/Win32Registry.pm index 1aa4556ffae..07b8804153f 100644 --- a/thirdparty/rr-full/Parse/Win32Registry.pm +++ b/thirdparty/rr-full/Parse/Win32Registry.pm @@ -4,7 +4,7 @@ use 5.008_001; use strict; use warnings; -our $VERSION = '1.0'; +our $VERSION = '1.1'; use base qw(Exporter); diff --git a/thirdparty/rr-full/Parse/Win32Registry/Base.pm b/thirdparty/rr-full/Parse/Win32Registry/Base.pm index 0b206e7bb59..74c26d64cbe 100644 --- a/thirdparty/rr-full/Parse/Win32Registry/Base.pm +++ b/thirdparty/rr-full/Parse/Win32Registry/Base.pm @@ -161,26 +161,14 @@ sub unpack_windows_time { # The equation can be found in several places on the Net. # My thanks go to Dan Sully for Audio::WMA's _fileTimeToUnixTime # which shows a perl implementation of it. - my ($lo, $hi) = unpack("VV", $data); -# my $filetime = $high * 2 ** 32 + $low; -# my $epoch_time = int(($filetime - 116444736000000000) / 10000000); - - my $epoch_time; - - if ($lo == 0 && $hi == 0) { - $epoch_time = 0; - } else { - $lo -= 0xd53e8000; - $hi -= 0x019db1de; - $epoch_time = int($hi*429.4967296 + $lo/1e7); - }; - $epoch_time = 0 if ($epoch_time < 0); - - + my ($low, $high) = unpack("VV", $data); + my $filetime = $high * 2 ** 32 + $low; + my $epoch_time = int(($filetime - 116444736000000000) / 10000000); + # adjust the UNIX epoch time to the local OS's epoch time # (see perlport's Time and Date section) - # my $epoch_offset = timegm(0, 0, 0, 1, 0, 70); - # $epoch_time += $epoch_offset; + my $epoch_offset = timegm(0, 0, 0, 1, 0, 1970); + $epoch_time += $epoch_offset; if ($epoch_time < 0 || $epoch_time > 0x7fffffff) { $epoch_time = undef; diff --git a/thirdparty/rr-full/Parse/Win32Registry/Win95/Value.pm b/thirdparty/rr-full/Parse/Win32Registry/Win95/Value.pm index baffd13b592..bbb88b83f5b 100644 --- a/thirdparty/rr-full/Parse/Win32Registry/Win95/Value.pm +++ b/thirdparty/rr-full/Parse/Win32Registry/Win95/Value.pm @@ -134,7 +134,9 @@ sub as_regedit_export { # } if ($type == REG_SZ) { - $export .= '"' . $self->get_data . '"'; + my $data = $self->get_data; + $data = '' if !defined($data); + $export .= '"' . $data . '"'; $export .= "\n"; } elsif ($type == REG_BINARY) { diff --git a/thirdparty/rr-full/Parse/Win32Registry/WinNT/Value.pm b/thirdparty/rr-full/Parse/Win32Registry/WinNT/Value.pm index b9e882b29cb..73601334fe9 100644 --- a/thirdparty/rr-full/Parse/Win32Registry/WinNT/Value.pm +++ b/thirdparty/rr-full/Parse/Win32Registry/WinNT/Value.pm @@ -280,7 +280,9 @@ sub as_regedit_export { # } if ($type == REG_SZ) { - $export .= '"' . $self->get_data . '"'; + my $data = $self->get_data; + $data = '' if !defined($data); + $export .= '"' . $data . '"'; $export .= "\n"; } elsif ($type == REG_BINARY) { diff --git a/thirdparty/rr-full/README.md b/thirdparty/rr-full/README.md index 942ee5f5df9..1704f7f645c 100644 --- a/thirdparty/rr-full/README.md +++ b/thirdparty/rr-full/README.md @@ -1,51 +1,31 @@ -RegRipper2.8 -============ - -RegRipper version 2.8 - -This is the GitHub repository for RegRipper version 2.8 - -Note: This tool does NOT automatically process hive transaction logs. If you need -to incorporate data from hive transaction logs into your analysis, consider merging -the data via Maxim Suhanov's yarp + registryFlush.py, or via Eric Zimmerman's rla.exe. - -Updates 20200220 -- Added warning that tool does not automatically process Registry hive transaction logs -- Added check for dirty hives -- Modified C:\Perl\site\lib\Parse\Win32Registry\WinNT\File.pm - - if you're using the Perl version of this tool (Linux, Mac) be sure to copy File.pm - from the repository and replace the appropriate file - -Updates 20200104 -- Fixed issue with processing of key LastWrite times -- Modified C:\Perl\site\lib\Parse\Win32Registry\WinNT\Base.pm - - if you're using the Perl version of this tool (Linux, Mac) be sure to copy Base.pm - from the repository and replace the appropriate file - -Updates 20190128 -- added Time::Local module - - this allows plugins to be written that parse string-based date/time stamps, converting - them to epochs (for timelining, etc.) -- modified C:\Perl\site\lib\Parse\Win32Registry\WinNT\Key.pm - - extract access_bits and largest_subkey_name_length values from Key node structure - - call 'get_access_bits()', 'get_largest_subkey_name_length()' to retrieve the values for parsing/display - - IAW https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md - -Note: The modifications to Key.pm are 'compiled' into the EXE versions of RegRipper. In order to fully take -advantage of them with the .pl versions: -- got to \Perl\site\lib\Parse\Win32Registry\WinNT\ -- rename Key.pm to Key_old.pm -- copy Key.pm from this distro to the folder - -Updates 20200104 -Based on how key LastWrite times were being converted from FILETIME objects to Unix epoch format, the function -appears to have 'broke' as of 1 Jan 2020. As such, I modified/fixed the code, and have updated the compiled -EXEs for the tools. I've also provided an updated Base.pm file, with instructions below as to how to update -your local copy of the file. - -- Navigate to the \site\lib\Parse\Win32Registry\ folder in your Perl installation, and remove any restrictions - or attributes from Base.pm (i.e., 'attrib -r Base.pm') -- Rename Base.pm to Base_old.pm -- Copy the Base.pm from this repository -======= - +# RegRipper4.0 + +What's new in RegRipper4.0 + +## WHAT'S NEW + +RegRipper4.0 includes ISO 8601-ish time stamp formatting, MITRE ATT&CK +mapping (for some, albeit not all, plugins), and Analysis Tips. Also, there +are many new plugins since August, 2020. + +Yara - https://virustotal.github.io/yara/ + +You can run Yara rules against Registry data! Go to the Yara site (above) +and download the latest release. Copy the 'yara64.exe' file to the root of +your RR4.0 folder (the same one with rip.exe). The "run_yara.pl" plugin +provides an example of a RegRipper plugin that implements Yara. Yara rule +files will need to be in the same folder as the Yara executable file. + +## LICENSE + +This version is free for personal and academic (college/university) use ONLY. + +RegRipper4.0 may not be included in vendor products, vendor training, nor in +any distribution. + +### NOTE + +This tool does NOT automatically process hive transaction logs. If you need +to incorporate data from hive transaction logs into your analysis, consider merging +the data via Maxim Suhanov's `yarp` + `registryFlush.py`, or via Eric Zimmerman's `rla.exe` +which is included in [Eric's Registry Explorer/RECmd](https://f001.backblazeb2.com/file/EricZimmermanTools/RegistryExplorer_RECmd.zip). \ No newline at end of file diff --git a/thirdparty/rr-full/license.md b/thirdparty/rr-full/license.md deleted file mode 100644 index 08d0c5adcae..00000000000 --- a/thirdparty/rr-full/license.md +++ /dev/null @@ -1,22 +0,0 @@ -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -This project is licensed under terms of the MIT License - -https://opensource.org/licenses/MIT - -See also: -https://en.wikipedia.org/wiki/MIT_License - -Questions, comments, etc., can be sent to keydet89 at yahoo dot com. \ No newline at end of file diff --git a/thirdparty/rr-full/license.txt b/thirdparty/rr-full/license.txt deleted file mode 100644 index 1660cbd2eae..00000000000 --- a/thirdparty/rr-full/license.txt +++ /dev/null @@ -1,22 +0,0 @@ -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -This project is licensed under terms of the MIT License - -https://opensource.org/licenses/MIT - -See also: -https://en.wikipedia.org/wiki/MIT_License - -Questions, comments, etc., can be sent to keydet89 at yahoo dot com. \ No newline at end of file diff --git a/thirdparty/rr-full/license_p2x.txt b/thirdparty/rr-full/license_p2x.txt deleted file mode 100644 index e05bab8f2ba..00000000000 --- a/thirdparty/rr-full/license_p2x.txt +++ /dev/null @@ -1,34 +0,0 @@ -LICENSE AGREEMENT -You should carefully read the following terms and conditions before using this software. Unless you have a different license agreement signed by IndigoSTAR Software, your use of this software indicates your acceptance of this license agreement and warranty. - -Registered Version - -Each registered copy of Perl2Exe may be used at a single workstation to create an unlimited number of exe files, subject to the following conditions: - -* A separate registered copy of Perl2Exe must be obtained for each workstation on which Perl2Exe will be used even if such use is only temporary. This is not a "concurrent use" license. - -* Exe files created by Perl2Exe are shipped with Run-time portions of Perl2Exe. No registered user, nor anyone else, may alter or modify the generated Exe files. You cannot give anyone else permission to modify the Exe files. - -* Exe files generated by the registered version of Perl2exe may be freely distributed. - -All rights not expressly granted in this license agreement are reserved entirely to IndigoSTAR Software - -Governing Law - -This agreement shall be governed by the laws of the Province of Ontario, Canada. - -Limited Warranty - -IndigoSTAR Software represents and warrants that the software and accompanying files will operate and function as documented, and that IndigoSTAR has full and sufficient right, title and authority to assign or grant the rights and/or licenses granted under this License Agreement. IndigoSTAR further warrants that neither the Software nor accompanying files infringe any intellectual property rights or similar rights of any 3rd party and agrees to indemnify you for any loss or damage related to a claim of infringement. - -Except for these limited warranties, this software and the accompanying files are sold "as is" and without warranties as to performance of merchantability or any other warranties whether expressed or implied. Because of the various hardware and software environments into which Perl2Exe may be put, NO WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE IS OFFERED. Good data processing procedure dictates that any program be thoroughly tested with non-critical data before relying on it. The user must assume the entire risk of using the program. Except for claims based on breach of the limited warranties or the indemnity provided above, the liability of either party for claims arising under this Agreement will be limited exclusively to the amount of fees paid under this agreement. - -Shareware Version - -You are hereby licensed to use the shareware evaluation version of Perl2Exe for evaluation purposes without charge for a period of 30 days. This is not free software. If you use this software after the 30 day evaluation period a registration fee is required. Under no circumstances are you licensed to distribute Exe files created by the shareware evaluation version of Perl2Exe. Unregistered use of Perl2Exe after the 30 day evaluation period is in violation of copyright laws. - -Distribution of Perl2Exe - -You are hereby licensed to make as many copies of the shareware evaluation version of this software and documentation as you wish; give exact copies of the original shareware version to anyone; and distribute the shareware version of the software and documentation in its unmodified form via electronic means. There is no charge for any of the above. - -You are specifically prohibited from charging, or requesting donations, for any such copies, however made; and from distributing the software and/or documentation with other products (commercial or otherwise) without prior written permission, with one exception: Disk Vendors approved by the Association of Shareware Professionals are permitted to redistribute Perl2Exe subject to the conditions in this license, without specific written permission. \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/acmru.pl b/thirdparty/rr-full/plugins/acmru.pl deleted file mode 100644 index c87975de609..00000000000 --- a/thirdparty/rr-full/plugins/acmru.pl +++ /dev/null @@ -1,74 +0,0 @@ -#----------------------------------------------------------- -# acmru.pl -# Plugin for Registry Ripper, NTUSER.DAT edition - gets the -# ACMru values -# -# Change history -# -# -# References -# -# -# copyright 2008 H. Carvey -#----------------------------------------------------------- -package acmru; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080324); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's ACMru key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching acmru v.".$VERSION); - ::rptMsg("acmru v.".$VERSION); # banner - ::rptMsg("- ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Search Assistant\\ACMru'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("ACMru - Search Assistant"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())." (UTC)]"); - my @vals = $s->get_list_of_values(); - my %ac_vals; - foreach my $v (@vals) { - $ac_vals{$v->get_name()} = $v->get_data(); - } - foreach my $a (sort {$a <=> $b} keys %ac_vals) { - ::rptMsg("\t".$a." -> ".$ac_vals{$a}); - } - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/activesetup.pl b/thirdparty/rr-full/plugins/activesetup.pl new file mode 100644 index 00000000000..66b9525bf5b --- /dev/null +++ b/thirdparty/rr-full/plugins/activesetup.pl @@ -0,0 +1,105 @@ +#----------------------------------------------------------- +# activesetup.pl +# Get Active Setup StubPath values +# +# Change history: +# 20201230 - Near-complete overhaul of installedcomp.pl plugin +# +# References: +# https://twitter.com/pabraeken/status/990717080805789697 +# https://helgeklein.com/blog/2010/04/active-setup-explained/ +# http://www.microsoft.com/security/portal/threat/encyclopedia/entry.aspx?Name=Backdoor%3AWin32%2FBifrose.ACI#tab=2 +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, 2013 +#----------------------------------------------------------- +package activesetup; +use strict; + +my %config = (hive => "software, ntuser\.dat", + category => "persistence", + MITRE => "T1547", + osmask => 22, + output => "report", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + version => 20201230); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get Active Setup StubPath values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching activesetup v.".$VERSION); + ::rptMsg("activesetup v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my @paths = ("Microsoft\\Active Setup\\Installed Components", + "Wow6432Node\\Microsoft\\Active Setup\\Installed Components", + "Software\\Microsoft\\Active Setup\\Installed Components", + "Software\\Wow6432Node\\Microsoft\\Active Setup\\Installed Components",); + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg(""); + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_name(); +# If a Default value exists, use it as the name; otherwise, use the key name/GUID + eval { + my $id = $s->get_value("")->get_data(); + $name = $id; + }; + + my $stub = (); + eval { + $stub = $s->get_value("StubPath")->get_data(); + }; + + my $is = (); + eval { + $is = $s->get_value("IsInstalled")->get_data(); +# No IsInstalled value is the same as IsInstalled = 1; what we're interested in here +# is IsInstalled = 0 + }; + + if ($stub) { + ::rptMsg("Name : ".$name); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + ::rptMsg("StubPath : ".$stub); + ::rptMsg("IsInstalled : ".$is); + ::rptMsg(""); + } + } + } + } + else { +# ::rptMsg($key_path." not found."); + } + } + ::rptMsg("Analysis Tip: The Active Setup key defines processes that are run synchronously prior to the Run & RunOnce keys, and"); + ::rptMsg("prior to the Desktop appearing\. For users, logon in blocked while commands are executing."); + ::rptMsg("Ref: https://helgeklein.com/blog/2010/04/active-setup-explained/"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/adoberdr.pl b/thirdparty/rr-full/plugins/adobe.pl similarity index 60% rename from thirdparty/rr-full/plugins/adoberdr.pl rename to thirdparty/rr-full/plugins/adobe.pl index 6138837e0fa..1faa1dede75 100644 --- a/thirdparty/rr-full/plugins/adoberdr.pl +++ b/thirdparty/rr-full/plugins/adobe.pl @@ -1,9 +1,13 @@ #----------------------------------------------------------- -# adoberdr.pl +# adobe.pl # Plugin for Registry Ripper # Parse Adobe Reader MRU keys # # Change history +# 20200903 - updates +# 20200622 - Updated code to check for app version +# 20200620 - renamed "adoberdr.pl" to "adobe.pl", to capture Acrobat data, as well +# 20200520 - minor updates # 20150717 - updated IAW Jason Hale's blog post (see ref), added # .csv output format # 20120716 - added version 10.0 to @versions @@ -16,22 +20,24 @@ # Note: LastWrite times on c subkeys will all be the same, # as each subkey is modified as when a new entry is added # -# copyright 2015 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- -package adoberdr; +package adobe; use strict; my %config = (hive => "NTUSER\.DAT", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20150717); + output => "report", + category => "user activity", + MITRE => "", + version => 20200803); sub getConfig{return %config} sub getShortDescr { - return "Gets user's Adobe Reader cRecentFiles values"; + return "Gets user's Adobe app cRecentFiles values"; } sub getDescr{} sub getRefs {} @@ -43,31 +49,37 @@ sub getShortDescr { sub pluginmain { my $class = shift; my $ntuser = shift; - ::logMsg("Launching adoberdr v.".$VERSION); - ::rptMsg("adoberdr v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::logMsg("Launching adobe v.".$VERSION); + ::rptMsg("adobe v.".$VERSION); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; - ::rptMsg("Adoberdr v.".$VERSION); -# First, let's find out which version of Adobe Acrobat Reader is installed - my $version; - my $tag = 0; - my @versions = ("4\.0","5\.0","6\.0","7\.0","8\.0","9\.0","10\.0","11\.0","12\.0","13\.0","14\.0", "DC"); - foreach my $ver (@versions) { - my $key_path = "Software\\Adobe\\Acrobat Reader\\".$ver."\\AVGeneral\\cRecentFiles"; - if (defined($root_key->get_subkey($key_path))) { - $version = $ver; - $tag = 1; + ::rptMsg("adobe v.".$VERSION); + + my @apps = ("Adobe Acrobat","Acrobat Reader"); + foreach my $app (@apps) { +# First, determine app version + my $version; + my $tag = 0; + my $path = "Software\\Adobe\\".$app; + if (my $key = $root_key->get_subkey($path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_name(); + if (defined($root_key->get_subkey($path."\\".$name."\\AVGeneral\\cRecentFiles"))) { + $version = $name; + } + } + } } - } - if ($tag) { - ::rptMsg("Adobe Acrobat Reader version ".$version." located."); - my $key_path = "Software\\Adobe\\Acrobat Reader\\".$version."\\AVGeneral\\cRecentFiles"; +# ::rptMsg($app." version ".$version." located."); + my $key_path = "Software\\Adobe\\".$app."\\".$version."\\AVGeneral\\cRecentFiles"; my $key = $root_key->get_subkey($key_path); if ($key) { ::rptMsg($key_path); - ::rptMsg(""); +# ::rptMsg(""); # ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); my %arkeys; my @subkeys = $key->get_list_of_subkeys(); @@ -95,13 +107,12 @@ sub pluginmain { eval { $arkeys{$num}{uPageCount} = $s->get_value('uPageCount')->get_data(); }; - - + } - ::rptMsg("Most recent PDF opened: ".gmtime($arkeys{1}{lastwrite})." (UTC)"); +# ::rptMsg("Most recent PDF opened: ".gmtime($arkeys{1}{lastwrite})." (UTC)"); ::rptMsg("Key name,file name,sDate,uFileSize,uPageCount"); foreach my $k (sort {$a <=> $b} keys %arkeys) { - ::rptMsg("c".$k.',"'.$arkeys{$k}{data}.'",'.$arkeys{$k}{sDate}.",".$arkeys{$k}{uFileSize}.",".$arkeys{$k}{uPageCount}); + ::rptMsg("c".$k.",".$arkeys{$k}{data}.",".$arkeys{$k}{sDate}.",".$arkeys{$k}{uFileSize}.",".$arkeys{$k}{uPageCount}); } } else { @@ -111,9 +122,7 @@ sub pluginmain { else { ::rptMsg("Could not access ".$key_path); } - } - else { - ::rptMsg("Adobe Acrobat Reader version not found."); + ::rptMsg(""); } } diff --git a/thirdparty/rr-full/plugins/ahaha.pl b/thirdparty/rr-full/plugins/ahaha.pl deleted file mode 100644 index d8b41791144..00000000000 --- a/thirdparty/rr-full/plugins/ahaha.pl +++ /dev/null @@ -1,84 +0,0 @@ -#----------------------------------------------------------- -# ahaha.pl - plugin to detect possible presence of Ahaha backdoor -# -# Change history -# 20131009 - created -# -# References -# http://www.microsoft.com/security/portal/threat/encyclopedia/Entry.aspx?Name=Adware%3AWin32%2FOpenCandy#tab=2 -# -# Copyright (c) 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package ahaha; -use strict; - -my %config = (hive => "Software,NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 1, - hasRefs => 1, - osmask => 22, - category => "malware", - version => 20131009); -my $VERSION = getVersion(); - -# Functions # -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getDescr {} -sub getShortDescr { - return "Detect possible presence of ahaha malware"; -} -sub getRefs {} - -sub pluginmain { - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching ahaha v.".$VERSION); - ::rptMsg("ahaha v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $count = 0; - - my @paths = ("Microsoft\\Windows\\CurrentVersion\\Run", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", -# Check NTUSER.DAT hive - "Software\\Microsoft\\Windows\\CurrentVersion\\Run", - "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run"); - - foreach my $key_path (@paths) { - if ($key = $root_key->get_subkey($key_path)) { - my @vals = $key->get_list_of_values(); - if (scalar @vals > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); - - if ($name eq "360v") { - ::rptMsg("Possible Backdoor\.Ahaha found\."); - $count = 1; - } - my $lcdata = $data; - $lcdata =~ tr/[A-Z]/[a-z]/; - if (grep(/appdata/,$lcdata) || grep(/application data/,$lcdata)) { - ::rptMsg("Path includes %AppData%: ".$data); - $count = 1; - } - } - } - } - - } - - if ($count == 0) { - ::rptMsg("No indicators found\."); - } - -} - -1; diff --git a/thirdparty/rr-full/plugins/aim.pl b/thirdparty/rr-full/plugins/aim.pl deleted file mode 100644 index 856be9d6460..00000000000 --- a/thirdparty/rr-full/plugins/aim.pl +++ /dev/null @@ -1,97 +0,0 @@ -#----------------------------------------------------------- -# aim -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package aim; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080325); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets info from the AOL Instant Messenger (not AIM) install"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching aim plugin v.".$VERSION); - ::rptMsg("aim v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = 'Software\\America Online\\AOL Instant Messenger (TM)\\CurrentVersion\\Users'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("AIM"); - ::rptMsg($key_path); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - my $user = $s->get_name(); - ::rptMsg("User: $user [".gmtime($s->get_timestamp())."]"); - - my $login = "Login"; - my $recent = "recent IM ScreenNames"; - my $recent2 = "recent ScreenNames"; - - my @userkeys = $s->get_list_of_subkeys(); - foreach my $u (@userkeys) { - my $us = $u->get_name(); -# See if we can get the encrypted password - if ($us =~ m/^$login/) { - my $pwd = ""; - eval { - $pwd = $u->get_value("Password1")->get_data(); - }; - ::rptMsg("Pwd: ".$pwd) if ($pwd ne ""); - } -# See if we can get recent folks they've chatted with... - if ($us eq $recent || $us eq $recent2) { - - my @vals = $u->get_list_of_values(); - if (scalar(@vals) > 0) { - ::rptMsg($user."\\".$us); - my %sns; - foreach my $v (@vals) { - $sns{$v->get_name()} = $v->get_data(); - } - - foreach my $i (sort {$a <=> $b} keys %sns) { - ::rptMsg("\t\t".$i." -> ".$sns{$i}); - } - } - else { -# No values - } - } - } - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/all b/thirdparty/rr-full/plugins/all deleted file mode 100644 index 2e320acc2ec..00000000000 --- a/thirdparty/rr-full/plugins/all +++ /dev/null @@ -1,10 +0,0 @@ -baseline -del -fileless -findexes -malware -null -regtime -rlo -sizes -slack diff --git a/thirdparty/rr-full/plugins/allow_upgrade.pl b/thirdparty/rr-full/plugins/allow_upgrade.pl new file mode 100644 index 00000000000..d4e00605902 --- /dev/null +++ b/thirdparty/rr-full/plugins/allow_upgrade.pl @@ -0,0 +1,75 @@ +#----------------------------------------------------------- +# allow_upgrade.pl +# +# +# Change history: +# 20230725 - created +# +# References: +# https://support.microsoft.com/en-us/windows/ways-to-install-windows-11-e0edbbfb-cfc5-4011-868b-2ce77ac7c70e +# +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package allow_upgrade; +use strict; + +my %config = (hive => "system", + category => "defense evasion", + MITRE => "T1601", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 2023075); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check for AllowUpgradesWithUnsupportedTPMOrCPU value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching allow_upgrade v.".$VERSION); + ::rptMsg("allow_upgrade v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path = "Setup\\MoSetup"; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("Key path: ".$key_path); + ::rptMsg("Key LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $a = $key->get_value("AllowUpgradesWithUnsupportedTPMOrCPU")->get_data(); + ::rptMsg("AllowUpgradesWithUnsupportedTPMOrCPU value: ".$a); + }; + ::rptMsg("AllowUpgradesWithUnsupportedTPMOrCPU value not found.") if ($@); + } + else { + ::rptMsg($key_path." not found"); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: The \"AllowUpgradesWithUnsupportedTPMOrCPU\" value set to 1 is a hack to allow Windows 11"); + ::rptMsg("updates to be installed on systems that did not meet the TPM or CPU checks. This could be interpreted as "); + ::rptMsg("an attempt at defense evasion, by upgrading the system image to provide additional capabilities, such as"); + ::rptMsg("Windows Subsystem for Android."); + ::rptMsg(""); + ::rptMsg("Ref: https://support.microsoft.com/en-us/windows/ways-to-install-windows-11-e0edbbfb-cfc5-4011-868b-2ce77ac7c70e"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/allowedenum.pl b/thirdparty/rr-full/plugins/allowedenum.pl new file mode 100644 index 00000000000..bb784f6b30e --- /dev/null +++ b/thirdparty/rr-full/plugins/allowedenum.pl @@ -0,0 +1,87 @@ +#----------------------------------------------------------- +# allowedenum.pl +# +# To whitelist or show “Documents”, add the GUID {FDD39AD0-238F-46AF-ADB4-6C85480369C7} +# and set its value data to 1. To hide “Documents” remove the GUID value, or set its +# data to 0. +# +# If the “AllowedEnumeration” key exists without any whitelisted entries, none of the +# special folders will show up in File Explorer and Desktop. +# +# Value name, or GUID, represents special folder namespace; data of 1 == show, 0 == hidden +# +# MITRE ATT&CK: https://attack.mitre.org/techniques/T1564/001/ +# +# Change history +# 20200813 - minor updates +# 20200511 - updated date output format +# 20191002 - created +# +# References +# https://www.winhelponline.com/blog/show-hide-shell-folder-namespace-windows-10/ +# +# Copyright 2019-2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package allowedenum; +use strict; + +my %config = (hive => "NTUSER\.DAT, Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1564\.001", + category => "defense evasion", + output => "report", + version => 20200813); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Extracts AllowedEnumeration values to determine hidden special folders"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching allowedenum v.".$VERSION); + ::rptMsg("allowedenum v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + + my @paths = ("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AllowedEnumeration", + "Microsoft\\Windows\\CurrentVersion\\Explorer\\AllowedEnumeration"); + + foreach my $key_path (@paths) { + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + ::rptMsg($v->get_name()." : ".$v->get_data()); + } + } else { + ::rptMsg($key_path." found, has no values."); + } + } + else { + ::rptMsg($key_path." not found."); + } + } + +} + +1; diff --git a/thirdparty/rr-full/plugins/amcache b/thirdparty/rr-full/plugins/amcache index 081bf4a7bc3..38e723b5020 100755 --- a/thirdparty/rr-full/plugins/amcache +++ b/thirdparty/rr-full/plugins/amcache @@ -1 +1,2 @@ amcache +amcache_tln diff --git a/thirdparty/rr-full/plugins/amcache.pl b/thirdparty/rr-full/plugins/amcache.pl index bad27d9a49d..3cc083df479 100644 --- a/thirdparty/rr-full/plugins/amcache.pl +++ b/thirdparty/rr-full/plugins/amcache.pl @@ -2,6 +2,8 @@ # amcache.pl # # Change history +# 20200813 - MITRE update +# 20200515 - updated date output format # 20180311 - updated to support newer version files, albeit without parsing devices # 20170315 - added output for Product Name and File Description values # 20160818 - added check for value 17 @@ -13,7 +15,7 @@ # https://binaryforay.blogspot.com/2017/10/amcache-still-rules-everything-around.html # http://www.swiftforensics.com/2013/12/amcachehve-in-windows-8-goldmine-for.html # -# Copyright (c) 2018 QAR, LLC +# Copyright (c) 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package amcache; @@ -23,9 +25,10 @@ package amcache; hasShortDescr => 1, hasDescr => 1, hasRefs => 1, - osmask => 22, + MITRE => "", + output => "report", category => "program execution", - version => 20180311); + version => 20200813); my $VERSION = getVersion(); # Functions # @@ -111,7 +114,7 @@ sub parseInventoryApplicationFile { $hash = $s->get_value("FileID")->get_data(); $hash =~ s/^0000//; }; - ::rptMsg($path." LastWrite: ".gmtime($lw)); + ::rptMsg($path." LastWrite: ".::format8601Date($lw)."Z"); ::rptMsg("Hash: ".$hash); ::rptMsg(""); } @@ -158,7 +161,7 @@ sub parseFile { if (scalar(@sk) > 0) { foreach my $s (@sk) { ::rptMsg("File Reference: ".$s->get_name()); - ::rptMsg("LastWrite : ".gmtime($s->get_timestamp())." Z"); + ::rptMsg("LastWrite : ".::format8601Date($s->get_timestamp())."Z"); # update 20131213: based on trial and error, it appears that not all file # references will have all of the values, such as Path, or SHA-1 eval { @@ -187,26 +190,26 @@ sub parseFile { eval { @t = unpack("VV",$s->get_value("11")->get_data()); - $gt = gmtime(::getTime($t[0],$t[1])); - ::rptMsg("Last Mod Time : ".$gt." Z"); + $gt = ::format8601Date(::getTime($t[0],$t[1])); + ::rptMsg("Last Mod Time : ".$gt."Z"); }; eval { @t = unpack("VV",$s->get_value("17")->get_data()); - $gt = gmtime(::getTime($t[0],$t[1])); - ::rptMsg("Last Mod Time2: ".$gt." Z"); + $gt = ::format8601Date(::getTime($t[0],$t[1])); + ::rptMsg("Last Mod Time2: ".$gt."Z"); }; eval { @t = unpack("VV",$s->get_value("12")->get_data()); - $gt = gmtime(::getTime($t[0],$t[1])); - ::rptMsg("Create Time : ".$gt." Z"); + $gt = ::format8601Date(::getTime($t[0],$t[1])); + ::rptMsg("Create Time : ".$gt."Z"); }; eval { - $gt = gmtime($s->get_value("f")->get_data()); + $gt = ::format8601Date($s->get_value("f")->get_data()); # $gt = gmtime(unpack("V",$s->get_value("f")->get_data())); - ::rptMsg("Compile Time : ".$gt." Z"); + ::rptMsg("Compile Time : ".$gt."Z"); }; ::rptMsg(""); } diff --git a/thirdparty/rr-full/plugins/amcache_tln.pl b/thirdparty/rr-full/plugins/amcache_tln.pl index de0fbafabb1..6ea2ad74102 100644 --- a/thirdparty/rr-full/plugins/amcache_tln.pl +++ b/thirdparty/rr-full/plugins/amcache_tln.pl @@ -2,6 +2,7 @@ # amcache_tln.pl # # Change history +# 20200813 - MITRE update # 20180311 - updated to support newer version files, albeit without parsing devices # 20170315 - added output for Product Name and File Description values # 20160818 - added check for value 17 @@ -23,9 +24,10 @@ package amcache_tln; hasShortDescr => 1, hasDescr => 1, hasRefs => 1, - osmask => 22, + MITRE => "", + output => "tln", category => "program execution", - version => 20180311); + version => 20200813); my $VERSION = getVersion(); # Functions # diff --git a/thirdparty/rr-full/plugins/amsienable.pl b/thirdparty/rr-full/plugins/amsienable.pl new file mode 100644 index 00000000000..f734fca7405 --- /dev/null +++ b/thirdparty/rr-full/plugins/amsienable.pl @@ -0,0 +1,70 @@ +#----------------------------------------------------------- +# amsienable.pl +# Plugin for Registry Ripper +# +# +# Change history +# 20210217 - created +# +# References +# https://twitter.com/tal_liberman/status/1097145117809541121 +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package amsienable; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1562\.001", + version => 20210217); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets user's AMSIEnable value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching amsienable v.".$VERSION); + ::rptMsg("amsienable v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Software\\Microsoft\\Windows Script\\Settings'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("amsienable"); + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $ae = $key->get_value("AmsiEnable")->get_data(); + ::rptMsg("AmsiEnable value: ".$ae); + }; + ::rptMsg("AmsiEnable value not found.") if ($@); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the AmsiEnable value is 0, AMSI is disabled."); + } + else { + ::rptMsg($key_path." key not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/amsiproviders.pl b/thirdparty/rr-full/plugins/amsiproviders.pl new file mode 100644 index 00000000000..605186ee253 --- /dev/null +++ b/thirdparty/rr-full/plugins/amsiproviders.pl @@ -0,0 +1,116 @@ +#----------------------------------------------------------- +# amsiproviders.pl +# Get AMSI providers +# +# Change history: +# 20210601 - updated to check for removal of Windows Defender GUID +# 20210526 - updated +# 20210521 - created +# +# References: +# https://pentestlab.blog/2021/05/17/persistence-amsi/ +# https://b4rtik.github.io/posts/antimalware-scan-interface-provider-for-persistence/ +# https://docs.microsoft.com/en-us/windows/win32/amsi/antimalware-scan-interface-portal +# https://pentestlaboratories.com/2021/06/01/threat-hunting-amsi-bypasses/ +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, 2013 +#----------------------------------------------------------- +package amsiproviders; +use strict; + +my %config = (hive => "software", + category => "persistence", + MITRE => "T1546", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210601); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get AMSI Providers"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my $key; + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $wd_count = 0; + ::logMsg("Launching amsiproviders v.".$VERSION); + ::rptMsg("amsiproviders v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my @paths = ("Microsoft\\AMSI\\Providers", + "Wow6432Node\\Microsoft\\AMSI\\Providers"); + + foreach my $key_path (@paths) { + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + + eval { + my $f = $key->get_value("FeatureBits")->get_data(); + ::rptMsg("FeatureBits value: ".$f); + + }; + if ($@) { + ::rptMsg("FeatureBits value not found."); + } + ::rptMsg(""); + + my $wd = "{2781761E-28E0-4109-99FE-B9D127C57AFE}"; + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_name(); + $wd_count = 1 unless ($name eq $wd); +# ::rptMsg("Name: ".$name); + my $a; + if ($a = $s->get_value("")) { + my $lw = ::format8601Date($s->get_timestamp())."Z"; + ::rptMsg($name); + ::rptMsg("LastWrite time: ".$lw); + ::rptMsg("Provider : ".$a->get_data()); + + if ($name ne "") { + my $key_path = "Classes\\CLSID\\".$name."\\InProcServer32"; + if (my $inproc = $root_key->get_subkey($key_path)) { + ::rptMsg("Provider DLL : ".$inproc->get_value("")->get_data()); + } + } + ::rptMsg(""); + } + } + } + } + else { +# ::rptMsg($key_path." not found."); + } + } + if ($wd_count == 1) { + ::rptMsg("The AMSI provider for Windows Defender seems to have been removed/could not be found."); + ::rptMsg(""); + } + ::rptMsg("Analysis Tip: AMSI providers can be used for persistence. Ref: https://pentestlab.blog/2021/05/17/persistence-amsi/"); + ::rptMsg(""); + ::rptMsg("The FeatureBit check determines if Authenicode signing is enabled or not."); + ::rptMsg(" 0x01 - signing check is disabled; this is the default behavior (applies if value not found)"); + ::rptMsg(" 0x02 - signing check is enabled"); +} +1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/angelfire.pl b/thirdparty/rr-full/plugins/angelfire.pl deleted file mode 100644 index d6c7d71a007..00000000000 --- a/thirdparty/rr-full/plugins/angelfire.pl +++ /dev/null @@ -1,67 +0,0 @@ -#----------------------------------------------------------- -# angelfire.pl -# -# History: -# 20170831 - created -# -# References: -# https://wikileaks.org/vault7/document/Angelfire-2_0-UserGuide/Angelfire-2_0-UserGuide.pdf -# -# -# copyright 2017 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package angelfire; -use strict; - -my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "malware", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20170831); - -sub getConfig{return %config} -sub getShortDescr { - return "Detects AngelFire"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching angelfire v.".$VERSION); - ::rptMsg("angelfire v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my ($current,$ccs); - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - my $af_path = $ccs."\\Control\\Windows\\SystemLookup"; - my $af; - if ($af = $root_key->get_subkey($af_path)) { - ::rptMsg("AngelFire found."); - ::rptMsg("Path: ".$af_path); - } - else { - ::rptMsg("AngelFire not found."); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/aports.pl b/thirdparty/rr-full/plugins/aports.pl deleted file mode 100644 index 0ec91104e45..00000000000 --- a/thirdparty/rr-full/plugins/aports.pl +++ /dev/null @@ -1,104 +0,0 @@ -#----------------------------------------------------------- -# aports.pl -# Extracts the install path for SmartLine Inc. Active Ports. -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# Copyright (c) 2011-02-04 Brendan Coles -#----------------------------------------------------------- -# Require # -package aports; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110204); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts the install path for SmartLine Inc. Active Ports."; -} -sub getRefs { - my %refs = ("SmartLine Inc. Active Ports Homepage:" => - "http://www.ntutility.com"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - my @interesting_keys = ( - "InstallPath" - ); - - # Initialize # - ::logMsg("Launching aports v.".$VERSION); - ::rptMsg("aports v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\SmartLine Vision\\aports"; - - # If # Active Ports path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Active Ports"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Active Ports registry path # - my %keys; - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Active Ports registry path # - foreach my $v (@vals) { - $keys{$v->get_name()} = $v->get_data(); - } - - # Return # all key names+values for interesting keys # - foreach my $var (@interesting_keys) { - if (exists $keys{$var}) { - ::rptMsg($var." -> ".$keys{$var}); - } - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Active Ports isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/appassoc.pl b/thirdparty/rr-full/plugins/appassoc.pl index e5bf8ab22b1..771fa7e0f12 100644 --- a/thirdparty/rr-full/plugins/appassoc.pl +++ b/thirdparty/rr-full/plugins/appassoc.pl @@ -2,12 +2,15 @@ # appassoc.pl # # Change history +# 20200813 - minor updates +# 20200515 - updated date output format # 20190513 - created # # References -# https://twitter.com/EricRZimmerman/status/916422135987474433 +# https://attack.mitre.org/techniques/T1546/001/ # -# copyright 2017 H. Carvey, keydet89@yahoo.com +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package appassoc; use strict; @@ -16,8 +19,10 @@ package appassoc; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20190513); + output => "report", + category => "persistence", + MITRE => "T1546\.001", + version => 20200813); sub getConfig{return %config} sub getShortDescr { @@ -34,8 +39,10 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching appassoc v.".$VERSION); - ::rptMsg("appassoc v.".$VERSION); # banner - ::rptMsg("- ".getShortDescr()."\n"); # banner + ::rptMsg("appassoc v.".$VERSION); + ::rptMsg("- ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -44,7 +51,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); + ::rptMsg("LastWrite: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); foreach my $v (@vals) { ::rptMsg($v->get_name()); diff --git a/thirdparty/rr-full/plugins/appcertdlls.pl b/thirdparty/rr-full/plugins/appcertdlls.pl index 11d6dcae0ca..55818a18e78 100644 --- a/thirdparty/rr-full/plugins/appcertdlls.pl +++ b/thirdparty/rr-full/plugins/appcertdlls.pl @@ -2,27 +2,29 @@ # appcertdlls.pl # # History: +# 20200813 - minor updates +# 20200427 - updated output date format # 20120912 - created # # References: -# +# https://attack.mitre.org/techniques/T1546/009/ # # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package appcertdlls; use strict; my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "malware", + output => "report", + category => "privilege escalation", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20120817); + output => "report", + MITRE => "T1546\.009", + version => 20200813); sub getConfig{return %config} sub getShortDescr { @@ -41,6 +43,9 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching appcertdlls v.".$VERSION); + ::rptMsg("Launching appcertdlls v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -55,6 +60,8 @@ sub pluginmain { my $appcert_path = $ccs."\\Control\\Session Manager\\AppCertDlls"; my $appcert; if ($appcert = $root_key->get_subkey($appcert_path)) { + ::rptMsg($appcert_path); + ::rptMsg("LastWrite Time: ".::format8601Date($appcert->get_timestamp())."Z"); my @vals = $appcert->get_list_of_values(); if (scalar(@vals) > 0) { foreach my $v (@vals) { diff --git a/thirdparty/rr-full/plugins/appcompatcache.pl b/thirdparty/rr-full/plugins/appcompatcache.pl index 0a4fbaf4631..4bb45757b68 100644 --- a/thirdparty/rr-full/plugins/appcompatcache.pl +++ b/thirdparty/rr-full/plugins/appcompatcache.pl @@ -2,6 +2,9 @@ # appcompatcache.pl # # History: +# 20220920 - updated Win8.1 parsing +# 20200730 - minor updates +# 20200428 - updated output date format # 20190112 - updated parsing for Win8.1 # 20180311 - updated for more recent version of Win10/Win2016 # 20160528 - updated code to not de-dup entries based on filename @@ -30,21 +33,20 @@ # This plugin is based solely on the work and examples provided by Mandiant; # thanks to them for sharing this information, and making the plugin possible. # -# copyright 2016 Quantum Analytics Research, LLC +# copyright 2022 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package appcompatcache; use strict; my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "Program Execution", + category => "file existence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20190112); + MITRE => "", + output => "report", + version => 20220920); sub getConfig{return %config} sub getShortDescr { @@ -64,102 +66,94 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching appcompatcache v.".$VERSION); ::rptMsg("appcompatcache v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is # going to be used over and over again in plugins that access the system # file - my ($current,$ccs); - my $key_path = 'Select'; + my $ccs = ::getCCS($root_key); my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - my $appcompat_path = $ccs."\\Control\\Session Manager"; - my $appcompat; - if ($appcompat = $root_key->get_subkey($appcompat_path)) { + my $appcompat; + my $appcompat_path = $ccs."\\Control\\Session Manager"; + if ($appcompat = $root_key->get_subkey($appcompat_path)) { - my $app_data; + my $app_data; + eval { + $app_data = $appcompat->get_subkey("AppCompatibility")->get_value("AppCompatCache")->get_data(); + ::rptMsg($appcompat_path."\\AppCompatibility"); + ::rptMsg("LastWrite Time: ".::format8601Date($appcompat->get_subkey("AppCompatibility")->get_timestamp())."Z"); + }; + + eval { + $app_data = $appcompat->get_subkey("AppCompatCache")->get_value("AppCompatCache")->get_data(); + ::rptMsg($appcompat_path."\\AppCompatCache"); + ::rptMsg("LastWrite Time: ".::format8601Date($appcompat->get_subkey("AppCompatCache")->get_timestamp())."Z"); + }; + +# ::rptMsg("Length of data: ".length($app_data)); +# ::probe($app_data); + my $sig = unpack("V",substr($app_data,0,4)); + ::rptMsg(sprintf "Signature: 0x%x",$sig); + + if ($sig == 0xdeadbeef) { eval { - $app_data = $appcompat->get_subkey("AppCompatibility")->get_value("AppCompatCache")->get_data(); - ::rptMsg($appcompat_path."\\AppCompatibility"); - ::rptMsg("LastWrite Time: ".gmtime($appcompat->get_subkey("AppCompatibility")->get_timestamp())." Z"); + appXP32Bit($app_data); }; - + } + elsif ($sig == 0xbadc0ffe) { eval { - $app_data = $appcompat->get_subkey("AppCompatCache")->get_value("AppCompatCache")->get_data(); - ::rptMsg($appcompat_path."\\AppCompatCache"); - ::rptMsg("LastWrite Time: ".gmtime($appcompat->get_subkey("AppCompatCache")->get_timestamp())." Z"); + appWin2k3($app_data); + }; + } + elsif ($sig == 0xbadc0fee) { + eval { + appWin7($app_data); }; - -# ::rptMsg("Length of data: ".length($app_data)); -# probe($app_data); - my $sig = unpack("V",substr($app_data,0,4)); - ::rptMsg(sprintf "Signature: 0x%x",$sig); - - if ($sig == 0xdeadbeef) { - eval { - appXP32Bit($app_data); - }; - } - elsif ($sig == 0xbadc0ffe) { - eval { - appWin2k3($app_data); - }; - } - elsif ($sig == 0xbadc0fee) { - eval { - appWin7($app_data); - }; - } - elsif ($sig == 0x80) { + } + elsif ($sig == 0x80) { # ::rptMsg("Possible Win8 system\."); # ::rptMsg(sprintf "Data Length: 0x%08x",length($app_data)); - appWin8($app_data); + appWin8($app_data); # probe($app_data); - } - elsif ($sig == 0x0) { + } + elsif ($sig == 0x0) { # possible win 8.1 system - appWin81($app_data); + appWin81($app_data); # print $app_data; - } - elsif ($sig == 0x30 || $sig == 0x34) { + } + elsif ($sig == 0x30 || $sig == 0x34) { # Windows 10 system - appWin10($app_data); - } - else { - ::rptMsg(sprintf "Unknown signature: 0x%x",$sig); + appWin10($app_data); + } + else { + ::rptMsg(sprintf "Unknown signature: 0x%x",$sig); # probe($app_data); - } + } # this is where we print out the files - foreach my $f (keys %files) { + foreach my $f (keys %files) { # ::rptMsg($f); - my $modtime = $files{$f}{modtime}; - if ($modtime == 0) { - $modtime = ""; - } - else { - $modtime = gmtime($modtime)." Z"; - } - - $str = $files{$f}{filename}." ".$modtime; - $str .= " ".gmtime($files{$f}{updtime})." Z" if (exists $files{$f}{updtime}); - $str .= " ".$files{$f}{size}." bytes" if (exists $files{$f}{size}); - $str .= " Executed" if (exists $files{$f}{executed}); - ::rptMsg($str); + my $modtime = $files{$f}{modtime}; + if ($modtime == 0) { + $modtime = ""; } - } - else { - ::rptMsg($appcompat_path." not found."); + else { + $modtime = ::format8601Date($modtime); + } + + $str = $files{$f}{filename}." ".$modtime; + $str .= " ".::format8601Date($files{$f}{updtime}) if (exists $files{$f}{updtime}); + $str .= " ".$files{$f}{size}." bytes" if (exists $files{$f}{size}); + $str .= " Executed" if (exists $files{$f}{executed}); + ::rptMsg($str); } } else { - ::rptMsg($key_path." not found."); + ::rptMsg($appcompat_path." not found."); } } @@ -304,7 +298,6 @@ sub appWin8 { while($ofs < $len) { my $tag = unpack("V",substr($data,$ofs,4)); - last unless (defined $tag); # 32-bit if ($tag == 0x73746f72) { $jmp = unpack("V",substr($data,$ofs + 8,4)); @@ -350,8 +343,7 @@ sub appWin81 { while ($ofs < $len) { $tag = substr($data,$ofs,4); - last unless (defined $tag); - if ($tag eq "10ts") { + if ($tag eq "10ts" || $tag eq "00ts") { $sz = unpack("V",substr($data,$ofs + 0x08,4)); $name_len = unpack("v",substr($data,$ofs + 0x0c,2)); @@ -424,63 +416,4 @@ sub alertCheckADS { ::alertMsg("ALERT: appcompatcache: Poss. ADS found in path: ".$path) if grep(/:/,$last); } - -#----------------------------------------------------------- -# probe() -# -# Code the uses printData() to insert a 'probe' into a specific -# location and display the data -# -# Input: binary data of arbitrary length -# Output: Nothing, no return value. Displays data to the console -#----------------------------------------------------------- -sub probe { - my $data = shift; - my @d = printData($data); - - foreach (0..(scalar(@d) - 1)) { - print $d[$_]."\n"; - } -} - -#----------------------------------------------------------- -# printData() -# subroutine used primarily for debugging; takes an arbitrary -# length of binary data, prints it out in hex editor-style -# format for easy debugging -#----------------------------------------------------------- -sub printData { - my $data = shift; - my $len = length($data); - - my @display = (); - - my $loop = $len/16; - $loop++ if ($len%16); - - foreach my $cnt (0..($loop - 1)) { -# How much is left? - my $left = $len - ($cnt * 16); - - my $n; - ($left < 16) ? ($n = $left) : ($n = 16); - - my $seg = substr($data,$cnt * 16,$n); - my $lhs = ""; - my $rhs = ""; - foreach my $i ($seg =~ m/./gs) { -# This loop is to process each character at a time. - $lhs .= sprintf(" %02X",ord($i)); - if ($i =~ m/[ -~]/) { - $rhs .= $i; - } - else { - $rhs .= "."; - } - } - $display[$cnt] = sprintf("0x%08X %-50s %s",$cnt,$lhs,$rhs); - - } - return @display; -} 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/appcompatcache_json.pl b/thirdparty/rr-full/plugins/appcompatcache_json.pl new file mode 100644 index 00000000000..668cb605fd2 --- /dev/null +++ b/thirdparty/rr-full/plugins/appcompatcache_json.pl @@ -0,0 +1,433 @@ +#----------------------------------------------------------- +# appcompatcache_json.pl +# +# History: +# 20221129 - modified for JSON output +# 20220920 - updated Win8.1 parsing +# 20200730 - minor updates +# 20200428 - updated output date format +# 20190112 - updated parsing for Win8.1 +# 20180311 - updated for more recent version of Win10/Win2016 +# 20160528 - updated code to not de-dup entries based on filename +# 20160217 - updated to correctly support Win10 +# 20150611 - mod'd for Kevin Pagano +# 20150429 - updated to support Win10 +# 20140724 - update based on data provided by Shafik Punja +# 20130801 - added initial Win8 support; very alpha at the moment +# 20130603 - updated alerts +# 20130509 - added additional alerts/warnings +# 20130425 - added alertMsg() functionality +# 20120817 - updated to address issue with residual data in XP data blocks +# 20120722 - updated the %config hash +# 20120523 - updated to send all files to a single hash, and check for temp paths +# 20120515 - Updated to support 64-bit Win2003 and Vista/Win2008 +# 20120424 - Modified/updated +# 20120418 - created +# +# References: +# https://binaryforay.blogspot.com/2016/05/appcompatcacheparser-v0900-released-and.html +# Blog post: https://blog.mandiant.com/archives/2459 +# Whitepaper: http://fred.mandiant.com/Whitepaper_ShimCacheParser.pdf +# Tool: https://github.com/mandiant/ShimCacheParser +# Win10: http://binaryforay.blogspot.com/2015/04/appcompatcache-changes-in-windows-10.html +# JSON: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON +# +# This plugin is based solely on the work and examples provided by Mandiant; +# thanks to them for sharing this information, and making the plugin possible. +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package appcompatcache_json; +use strict; + +my %config = (hive => "System", + category => "file existence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "json", + version => 20220920); + +sub getConfig{return %config} +sub getShortDescr { + return "Parse files from System hive AppCompatCache"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::logMsg("Launching appcompatcache v.".$VERSION); +# ::rptMsg("appcompatcache v.".$VERSION); # banner +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my $key; + my $appcompat; + my $appcompat_path = $ccs."\\Control\\Session Manager"; + if ($appcompat = $root_key->get_subkey($appcompat_path)) { + + my $app_data; + + eval { + $app_data = $appcompat->get_subkey("AppCompatibility")->get_value("AppCompatCache")->get_data(); + ::rptMsg("{"); + ::rptMsg("\t\"key\": \".$appcompat_path.\""); + ::rptMsg("\t\"value\": \"AppCompatibility\""); + ::rptMsg("\t\"LastWrite Time\": \"".::format8601Date($appcompat->get_subkey("AppCompatibility")->get_timestamp())."Z\""); + }; + + eval { + $app_data = $appcompat->get_subkey("AppCompatCache")->get_value("AppCompatCache")->get_data(); + ::rptMsg("{"); + ::rptMsg(" \"pluginname\": \"appcompatcache_json\""); + ::rptMsg(" \"description\": \"query\\parse the appcompatcache\\shimcache data source\""); + ::rptMsg(" \"key\": \".$appcompat_path.\""); + ::rptMsg(" \"value\": \"AppCompatCache\""); + ::rptMsg(" \"LastWrite Time\": \"".::format8601Date($appcompat->get_subkey("AppCompatCache")->get_timestamp())."Z\""); + }; + +# ::rptMsg("Length of data: ".length($app_data)); +# ::probe($app_data); + my $sig = unpack("V",substr($app_data,0,4)); +# ::rptMsg(sprintf "Signature: 0x%x",$sig); + + if ($sig == 0xdeadbeef) { + eval { + appXP32Bit($app_data); + }; + } + elsif ($sig == 0xbadc0ffe) { + eval { + appWin2k3($app_data); + }; + } + elsif ($sig == 0xbadc0fee) { + eval { + appWin7($app_data); + }; + + } + elsif ($sig == 0x80) { +# ::rptMsg("Possible Win8 system\."); +# ::rptMsg(sprintf "Data Length: 0x%08x",length($app_data)); + appWin8($app_data); +# probe($app_data); + + } + elsif ($sig == 0x0) { +# possible win 8.1 system + appWin81($app_data); +# print $app_data; + } + elsif ($sig == 0x30 || $sig == 0x34) { +# Windows 10 system + appWin10($app_data); + } + else { + ::rptMsg(sprintf "Unknown signature: 0x%x",$sig); +# probe($app_data); + } +# this is where we print out the files + ::rptMsg(" \"members\": ["); + foreach my $f (keys %files) { +# ::rptMsg($f); + + my $modtime = $files{$f}{modtime}; + if ($modtime == 0) { + $modtime = ""; + } + else { + $modtime = ::format8601Date($modtime); + } + ::rptMsg(" {"); + ::rptMsg(" \"value\": \"".$files{$f}{filename}."\""); + ::rptMsg(" \"data\": \"".$modtime."\""); + ::rptMsg(" },"); +# $str = $files{$f}{filename}." ".$modtime; +# $str .= " ".::format8601Date($files{$f}{updtime}) if (exists $files{$f}{updtime}); +# $str .= " ".$files{$f}{size}." bytes" if (exists $files{$f}{size}); +# $str .= " Executed" if (exists $files{$f}{executed}); +# ::rptMsg($str); + } + ::rptMsg(" ]"); + ::rptMsg("}"); + } + else { + ::rptMsg($appcompat_path." not found."); + } +} + +#----------------------------------------------------------- +# appXP32Bit() +# parse 32-bit XP data +#----------------------------------------------------------- +sub appXP32Bit { + my $data = shift; + ::rptMsg("WinXP, 32-bit"); +# header is 400 bytes; each structure is 552 bytes in size + my $num_entries = unpack("V",substr($data,4,4)); + + foreach my $i (0..($num_entries - 1)) { + my $x = substr($data,(400 + ($i * 552)),552); + my $file = (split(/\00\00/,substr($x,0,488)))[0]; + $file =~ s/\00//g; + $file =~ s/^\\\?\?\\//; + my ($mod1,$mod2) = unpack("VV",substr($x,528,8)); + my $modtime = ::getTime($mod1,$mod2); + my ($sz1,$sz2) = unpack("VV",substr($x,536,8)); + my $sz; + ($sz2 == 0)?($sz = $sz1):($sz = "Too big"); + my ($up1,$up2) = unpack("VV",substr($x,544,8)); + my $updtime = ::getTime($up1,$up2); + + $files{$i}{filename} = $file; + $files{$i}{size} = $sz; + $files{$i}{modtime} = $modtime; + $files{$i}{updtime} = $updtime; + } +} +#----------------------------------------------------------- +# appWin2k3() +# parse Win2k3, Vista, Win2k8 data +#----------------------------------------------------------- +sub appWin2k3 { + my $data = shift; + my $num_entries = unpack("V",substr($data,4,4)); +# ::rptMsg("Num_entries: ".$num_entries); + my $struct_sz = 0; + my ($len,$max_len,$padding) = unpack("vvV",substr($data,8,8)); + if (($max_len - $len) == 2) { +# if $padding == 0, 64-bit; otherwise, 32-bit + if ($padding == 0) { + $struct_sz = 32; + ::rptMsg("Win2K3/Vista/Win2K8, 64-bit"); + } + else { + $struct_sz = 24; + ::rptMsg("Win2K3/Vista/Win2K8, 32-bit"); + } + } + + foreach my $i (0..($num_entries - 1)) { + my $struct = substr($data,(8 + ($struct_sz * $i)),$struct_sz); + if ($struct_sz == 24) { + my ($len,$max_len,$ofs,$t0,$t1,$f0,$f1) = unpack("vvVVVVV",$struct); + + my $file = substr($data,$ofs,$len); + $file =~ s/\00//g; + $file =~ s/^\\\?\?\\//; + my $t = ::getTime($t0,$t1); + $files{$i}{filename} = $file; + $files{$i}{modtime} = $t; +# $files{$file}{size} = $f0 if (($f1 == 0) && ($f0 > 3)); + $files{$i}{executed} = 1 if (($f0 < 4) && ($f0 & 0x2)); + } + elsif ($struct_sz == 32) { + my ($len,$max_len,$padding,$ofs0,$ofs1,$t0,$t1,$f0,$f1) = unpack("vvVVVVVVV",$struct); + my $file = substr($data,$ofs0,$len); + $file =~ s/\00//g; + $file =~ s/^\\\?\?\\//; + my $t = ::getTime($t0,$t1); + $files{$i}{filename} = $file; + $files{$i}{modtime} = $t; + $files{$i}{size} = $f0 if (($f1 == 0) && ($f0 > 3)); + $files{$i}{executed} = 1 if (($f0 < 4) && ($f0 & 0x2)); + } + else { +# + } + } +} + +#----------------------------------------------------------- +# appWin7() +# parse Win2k8R2, Win7 data +#----------------------------------------------------------- +sub appWin7 { + my $data = shift; + my $struct_sz = 0; + my $num_entries = unpack("V",substr($data,4,4)); +# ::rptMsg("Num_entries: ".$num_entries); +# 128-byte header + my ($len,$max_len,$padding) = unpack("vvV",substr($data,128,8)); + if (($max_len - $len) == 2) { + if ($padding == 0) { + $struct_sz = 48; + ::rptMsg("Win2K8R2/Win7, 64-bit"); + } + else { + $struct_sz = 32; + ::rptMsg("Win2K8R2/Win7, 32-bit"); + } + } + + foreach my $i (0..($num_entries - 1)) { + my $struct = substr($data,(128 + ($struct_sz * $i)),$struct_sz); + if ($struct_sz == 32) { + my ($len,$max_len,$ofs,$t0,$t1,$f0,$f1) = unpack("vvV5x8",$struct); + my $file = substr($data,$ofs,$len); + $file =~ s/\00//g; + $file =~ s/^\\\?\?\\//; + my $t = ::getTime($t0,$t1); + $files{$i}{filename} = $file; + $files{$i}{modtime} = $t; + $files{$i}{executed} = 1 if ($f0 & 0x2); + } + else { + my ($len,$max_len,$padding,$ofs0,$ofs1,$t0,$t1,$f0,$f1) = unpack("vvV7x16",$struct); + my $file = substr($data,$ofs0,$len); + $file =~ s/\00//g; + $file =~ s/^\\\?\?\\//; + my $t = ::getTime($t0,$t1); + $files{$i}{filename} = $file; + $files{$i}{modtime} = $t; + $files{$i}{executed} = 1 if ($f0 & 0x2); + } + } +} + +#----------------------------------------------------------- +# appWin8() +#----------------------------------------------------------- +sub appWin8 { + my $data = shift; + my $len = length($data); + my ($jmp, $t0, $t1, $sz, $name); + my $ct = 0; + my $ofs = unpack("V",substr($data,0,4)); + + while($ofs < $len) { + my $tag = unpack("V",substr($data,$ofs,4)); +# 32-bit + if ($tag == 0x73746f72) { + $jmp = unpack("V",substr($data,$ofs + 8,4)); + ($t0,$t1) = unpack("VV",substr($data,$ofs + 12,8)); + $sz = unpack("v",substr($data,$ofs + 20,2)); + $name = substr($data,$ofs + 22,$sz); + $name =~ s/\00//g; + $files{$ct}{filename} = $name; + $files{$ct}{modtime} = ::getTime($t0,$t1); + $ct++; + $ofs += ($jmp + 12); + } +# 64-bit + elsif ($tag == 0x73743030 || $tag == 0x73743031) { + $jmp = unpack("V",substr($data,$ofs + 8,4)); + $sz = unpack("v",substr($data,$ofs + 0x0C,2)); + $name = substr($data,$ofs + 0x0E,$sz + 2); + $name =~ s/\00//g; + ($t0,$t1) = unpack("VV",substr($data,($ofs + 0x0E + $sz +2 + 8),8)); + $files{$ct}{filename} = $name; + $files{$ct}{modtime} = ::getTime($t0,$t1); + $ct++; + $ofs += ($jmp + 12); + } + else { +# Unknown tag + } + + } +} + +#----------------------------------------------------------- +# appWin81() +# +#----------------------------------------------------------- +sub appWin81 { + my $data = shift; + my $len = length($data); + my ($tag, $sz, $t0, $t1, $name, $name_len); + my $ct = 0; +# my $ofs = unpack("V",substr($data,0,4)); + my $ofs = 0x80; + + while ($ofs < $len) { + $tag = substr($data,$ofs,4); + if ($tag eq "10ts" || $tag eq "00ts") { + + $sz = unpack("V",substr($data,$ofs + 0x08,4)); + $name_len = unpack("v",substr($data,$ofs + 0x0c,2)); + my $name = substr($data,$ofs + 0x0e,$name_len); + $name =~ s/\00//g; +# ($t0,$t1) = unpack("VV",substr($data,$ofs + 0x03 + $name_len,8)); + ($t0,$t1) = unpack("VV",substr($data,$ofs + 0x0e + $name_len + 0x0a,8)); + $files{$ct}{filename} = $name; + $files{$ct}{modtime} = ::getTime($t0,$t1); + + $ct++; + $ofs += ($sz + 0x0c); + } + } +} + +#----------------------------------------------------------- +# appWin10() +# Ref: http://binaryforay.blogspot.com/2015/04/appcompatcache-changes-in-windows-10.html +#----------------------------------------------------------- +sub appWin10 { + my $data = shift; + my $len = length($data); + my ($tag, $sz, $t0, $t1, $name, $name_len); + my $ct = 0; + my $ofs = unpack("V",substr($data,0,4)); +# my $ofs = 0x30; + + while ($ofs < $len) { + $tag = substr($data,$ofs,4); + if ($tag eq "10ts") { + + $sz = unpack("V",substr($data,$ofs + 0x08,4)); + $name_len = unpack("v",substr($data,$ofs + 0x0c,2)); + my $name = substr($data,$ofs + 0x0e,$name_len); + $name =~ s/\00//g; +# ($t0,$t1) = unpack("VV",substr($data,$ofs + 0x03 + $name_len,8)); + ($t0,$t1) = unpack("VV",substr($data,$ofs + 0x0e + $name_len,8)); + $files{$ct}{filename} = $name; + $files{$ct}{modtime} = ::getTime($t0,$t1); + $ct++; + $ofs += ($sz + 0x0c); + } + } +} + +#----------------------------------------------------------- +# alertCheckPath() +#----------------------------------------------------------- +sub alertCheckPath { + my $path = shift; + $path = lc($path); + my @alerts = ("recycle","globalroot","temp","system volume information","appdata", + "application data"); + + foreach my $a (@alerts) { + if (grep(/$a/,$path)) { + ::alertMsg("ALERT: appcompatcache: ".$a." found in path: ".$path); + } + } +} + +#----------------------------------------------------------- +# alertCheckADS() +#----------------------------------------------------------- +sub alertCheckADS { + my $path = shift; + my @list = split(/\\/,$path); + my $last = $list[scalar(@list) - 1]; + ::alertMsg("ALERT: appcompatcache: Poss. ADS found in path: ".$path) if grep(/:/,$last); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/appcompatcache_tln.pl b/thirdparty/rr-full/plugins/appcompatcache_tln.pl index be9b932bfe1..f5ae795bab9 100644 --- a/thirdparty/rr-full/plugins/appcompatcache_tln.pl +++ b/thirdparty/rr-full/plugins/appcompatcache_tln.pl @@ -2,6 +2,8 @@ # appcompatcache_tln.pl # # History: +# 20220920 - updated Win8.1 parsing +# 20200927 - MITRE update # 20190112 - updated parsing for Win8.1 # 20180311 - updated for more recent version of Win10/Win2016 # 20160528 - updated code to not de-dup entries based on filename @@ -37,14 +39,13 @@ package appcompatcache_tln; use strict; my %config = (hive => "System", - hivemask => 4, - output => "tln", - category => "Program Execution", + category => "file existence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20190112); + MITRE => "", + output => "tln", + version => 20220920); sub getConfig{return %config} sub getShortDescr { @@ -296,7 +297,6 @@ sub appWin8 { while($ofs < $len) { my $tag = unpack("V",substr($data,$ofs,4)); - last unless (defined $tag); # 32-bit if ($tag == 0x73746f72) { $jmp = unpack("V",substr($data,$ofs + 8,4)); @@ -342,8 +342,7 @@ sub appWin81 { while ($ofs < $len) { $tag = substr($data,$ofs,4); - last unless (defined $tag); - if ($tag eq "10ts") { + if ($tag eq "10ts" || $tag eq "00ts") { $sz = unpack("V",substr($data,$ofs + 0x08,4)); $name_len = unpack("v",substr($data,$ofs + 0x0c,2)); diff --git a/thirdparty/rr-full/plugins/appcompatflags.pl b/thirdparty/rr-full/plugins/appcompatflags.pl index c3ea9c49b0e..a1f96fc383d 100644 --- a/thirdparty/rr-full/plugins/appcompatflags.pl +++ b/thirdparty/rr-full/plugins/appcompatflags.pl @@ -7,6 +7,10 @@ # "ELEVATECREATEPROCESS" "RUNASADMIN" "WINXPSP2 RUNASADMIN" # # Change history +# 20220328 - pulled out TelemetryController key content, to make it's own plugin +# 20200730 - updated with MITRE ATT&CK +# 20200609 - updates +# 20200525 - updated date output format # 20130930 - added support for Windows 8 Store key (thanks to # Eric Zimmerman for supplying test data) # 20130905 - added support for both NTUSER.DAT and Software hives; @@ -17,24 +21,24 @@ # References # http://msdn.microsoft.com/en-us/library/bb756937.aspx # -# Copyright (c) 2011-02-04 Brendan Coles -# updated 20130706, H. Carvey, keydet89@yahoo.com +# https://attack.mitre.org/techniques/T1546/011/ +# +# Copyright 2022 Quantum Analytics Research, LLC +# H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- -# Require # package appcompatflags; use strict; -# Declarations # my %config = (hive => "NTUSER\.DAT, Software", hasShortDescr => 1, hasDescr => 1, hasRefs => 1, - osmask => 22, - category => "program execution", - version => 20130930); + MITRE => "T1546\.011", + category => "persistence", + output => "report", + version => 20220328); my $VERSION = getVersion(); -# Functions # sub getConfig {return %config} sub getHive {return $config{hive};} sub getVersion {return $config{version};} @@ -46,7 +50,7 @@ sub getDescr { '"ELEVATECREATEPROCESS" "RUNASADMIN" "WINXPSP2 RUNASADMIN"'; } sub getShortDescr { - return "Extracts AppCompatFlags for Windows."; + return "Extracts AppCompatFlags values."; } sub getRefs { my %refs = ("Application Compatibility: Program Compatibility Assistant" => @@ -54,19 +58,15 @@ sub getRefs { return %refs; } -############################################################ -# pluginmain # -############################################################ sub pluginmain { - - # Declarations # my $class = shift; my $hive = shift; - # Initialize # ::logMsg("Launching appcompatflags v.".$VERSION); - ::rptMsg("appcompatflags v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # 20110830 [fpi] + banner + ::rptMsg("appcompatflags v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + ::rptMsg("MITRE ATT&CK subtechnique ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key; @@ -77,37 +77,24 @@ sub pluginmain { "Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers"); foreach my $key_path (@paths) { - # If AppCompatFlags path exists # if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); - # Extract # all keys from AppCompatFlags registry path # my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # if (scalar(@vals) > 0) { - - # Extract # all key names+values for AppCompatFlags registry path # foreach my $v (@vals) { ::rptMsg($v->get_name()." -> ".$v->get_data()); } - - # Error # key value is null # } else { ::rptMsg($key_path." found, has no values."); } } else { -# We're checking several keys in each hive, so if $key_path isn't found, -# don't generate a report # ::rptMsg($key_path." not found."); } } - # Return # obligatory new-line # ::rptMsg(""); # Get all programs for which PCA "came up", for a user, even if no compatibility modes were @@ -132,13 +119,10 @@ sub pluginmain { } } else { -# As above, don't report on key paths not found # ::rptMsg($key_path." not found\."); } } -# Get Store key contents -# selected # Added 20130930 by H. Carvey @paths = ("Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Store", "Wow6432Node\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Store", @@ -155,15 +139,14 @@ sub pluginmain { my ($t0,$t1) = unpack("VV",substr($v->get_data(),0x2C,8)); my $t = ::getTime($t0,$t1); - ::rptMsg(" ".gmtime($t)." - ".$v->get_name()); + ::rptMsg(" ".::format8601Date($t)."Z - ".$v->get_name()); } } else { ::rptMsg($key_path." found, has no values\."); } } - else { -# As above, don't report on key paths not found + else { # ::rptMsg($key_path." not found\."); } } @@ -176,7 +159,7 @@ sub pluginmain { if (scalar @subkeys > 0) { foreach my $sk (@subkeys) { ::rptMsg("Key name: ".$sk->get_name()); - ::rptMsg("LastWrite time: ".gmtime($sk->get_timestamp())); + ::rptMsg("LastWrite time: ".::format8601Date($sk->get_timestamp())."Z"); my @vals = $sk->get_list_of_values(); if (scalar @vals > 0) { @@ -184,8 +167,8 @@ sub pluginmain { my $name = $v->get_name(); my ($t0,$t1) = unpack("VV",$v->get_data()); my $l = ::getTime($t0,$t1); - my $ts = gmtime($l); - ::rptMsg(" ".$name." ".$ts); + my $ts = ::format8601Date($l); + ::rptMsg(" ".$name." ".$ts."Z"); } } ::rptMsg(""); @@ -193,7 +176,7 @@ sub pluginmain { } } - $key_path = "Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\InstalledSDB"; + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\InstalledSDB"; if ($key = $root_key->get_subkey($key_path)) { my @subkeys = $key->get_list_of_subkeys($key); if (scalar @subkeys > 0) { @@ -212,15 +195,14 @@ sub pluginmain { eval { my ($t0,$t1) = unpack("VV",$sk->get_value("DatabaseInstallTimeStamp")->get_data()); my $l = ::getTime($t0,$t1); - $ts = gmtime($l); - ::rptMsg(" Install TimeStamp: ".$ts); + $ts = ::format8601Date($l); + ::rptMsg(" Install TimeStamp: ".$ts."Z"); }; - ::rptMsg(""); - } } } + } 1; diff --git a/thirdparty/rr-full/plugins/appenvironment.pl b/thirdparty/rr-full/plugins/appenvironment.pl new file mode 100644 index 00000000000..09700d71a9f --- /dev/null +++ b/thirdparty/rr-full/plugins/appenvironment.pl @@ -0,0 +1,120 @@ +#----------------------------------------------------------- +# appenvironment.pl +# +# +# Change history +# 20230726 - updated to include AppExit key +# 20230725 - created +# +# References +# https://nssm.cc/usage +# +# copyright 2023 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package appenvironment; +#use strict; + +my %config = (hive => "System", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + category => "persistence", + MITRE => "T1547", + output => "report", + version => 20230726); + +sub getConfig{return %config} +sub getShortDescr { + return "Check services for AppEnvironment/AppEnvironmentExtra values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching appenvironment v.".$VERSION); + ::rptMsg("appenvironment v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services"; + my $key; + + my $count1 = 0; + my $count2 = 0; + my $count3 = 0; + + if ($key = $root_key->get_subkey($key_path)) { + my @serv = $key->get_list_of_subkeys(); + if (scalar @serv > 0) { + foreach my $s (@serv) { + eval { + my $a = $s->get_subkey("Parameters")->get_value("AppEnvironment")->get_data(); + ::rptMsg("AppEnvironment value: ".$a); + $count1++; + }; +# ::rptMsg("AppEnvironment value not found.") if ($@); + + eval { + my $a = $s->get_subkey("Parameters")->get_value("AppEnvironmentExtra")->get_data(); + ::rptMsg("AppEnvironmentExtra value: ".$a); + $count2++; + }; +# ::rptMsg("AppEnvironmentExtra value not found.") if ($@); + +# check for AppExit key + eval { + if ($s->get_subkey("Parameters\\AppExit")) { + ::rptMsg($key_path."\\".$s->get_name()."\\Parameters\\AppExit key found."); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_subkey("Parameters\\AppExit")->get_timestamp())."Z"); + ::rptMsg(""); + + my $k = $s->get_subkey("Parameters\\AppExit"); + $count3++; + my @vals = $k->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-10s %-10s",$v->get_name(),$v->get_data()); + } + } + else { +# no values found + } + } + else { +# ::rptMsg($key_path."\\".$s->get_name()."\\Parameters\\AppExit key not found."); + } + }; + } + } + else { +# Services key has no subkeys + } + ::rptMsg("No AppEnvironment values found.") if ($count1 == 0); + ::rptMsg("No AppEnvironmentExtra values found.") if ($count2 == 0); + ::rptMsg("No Parameters\\AppExit keys found.") if ($count3 == 0); + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: The AppEnvironment and AppEnvironmentExtra values allow a service to have access to environment"); + ::rptMsg("variables that override those set by the system at service startup. These values are used by svrany\.exe and "); + ::rptMsg("nssm\.exe."); + ::rptMsg(""); + ::rptMsg("Nssm\.exe makes use of the Parameters\\AppExit subkey to determine actions to take upon exit, and can be used"); + ::rptMsg("to specify specific actions based on the app's exit code."); + ::rptMsg(""); + ::rptMsg("Ref: https://nssm.cc/usage"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/appinitdlls.pl b/thirdparty/rr-full/plugins/appinitdlls.pl index 38f4e92929a..4236deed635 100644 --- a/thirdparty/rr-full/plugins/appinitdlls.pl +++ b/thirdparty/rr-full/plugins/appinitdlls.pl @@ -2,6 +2,8 @@ # appinitdlls # # Change history: +# 20200730 - added MITRE ATT&CK +# 20200427 - updated output date format # 20130425 - added alertMsg() functionality # 20130305 - updated to address 64-bit systems # 20080324 - created @@ -10,19 +12,22 @@ # http://msdn.microsoft.com/en-us/library/windows/desktop/dd744762(v=vs.85).aspx # http://support.microsoft.com/kb/q197571 # -# copyright 2013 QAR,LLC +# https://attack.mitre.org/techniques/T1546/010/ +# +# copyright 2020 QAR,LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package appinitdlls; use strict; my %config = (hive => "Software", - category => "autostart", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20130425); + output => "report", + MITRE => "T1546\.010", + version => 20200730); sub getConfig{return %config} sub getShortDescr { @@ -43,8 +48,11 @@ sub pluginmain { my $class = shift; my $hive = shift; ::rptMsg("Launching appinitdlls v.".$VERSION); - ::rptMsg("appinitdlls v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("appinitdlls v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my @paths = ('Microsoft\\Windows NT\\CurrentVersion\\Windows', 'Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Windows'); @@ -56,7 +64,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); eval { my $app = $key->get_value("AppInit_DLLs")->get_data(); diff --git a/thirdparty/rr-full/plugins/appkeys.pl b/thirdparty/rr-full/plugins/appkeys.pl index a58c8b249b6..82b0fb8f7d7 100644 --- a/thirdparty/rr-full/plugins/appkeys.pl +++ b/thirdparty/rr-full/plugins/appkeys.pl @@ -3,6 +3,8 @@ # # # Change history +# 20200813 - MITRE updates +# 20200517 - updated date output format # 20180920 - created # # References @@ -10,7 +12,7 @@ # http://blog.airbuscybersecurity.com/post/2015/06/Latest-improvements-in-PlugX # https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-appcommand # -# Copyright (c) 2018 QAR, LLC +# Copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package appkeys; @@ -20,9 +22,10 @@ package appkeys; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, + MITRE => "", category => "persistence", - version => 20180920); + version => 20200813); + my $VERSION = getVersion(); sub getConfig {return %config} @@ -56,7 +59,7 @@ sub pluginmain { my @sk = $key->get_list_of_subkeys(); if (scalar @sk > 0) { foreach my $s (@sk) { - ::rptMsg("Subkey Name: ".$s->get_name()." LastWrite: ".gmtime($s->get_timestamp())); + ::rptMsg("Subkey Name: ".$s->get_name()." LastWrite: ".::format8601Date($s->get_timestamp())."Z"); eval { my $shell = $s->get_value("ShellExecute")->get_data(); diff --git a/thirdparty/rr-full/plugins/appkeys_tln.pl b/thirdparty/rr-full/plugins/appkeys_tln.pl index f203282e423..61c965461dc 100644 --- a/thirdparty/rr-full/plugins/appkeys_tln.pl +++ b/thirdparty/rr-full/plugins/appkeys_tln.pl @@ -2,6 +2,7 @@ # appkeys_tln.pl # # Change history +# 20200813 - MITRE updates # 20180920 - created # # References @@ -19,9 +20,11 @@ package appkeys_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, + MITRE => "", category => "persistence", - version => 20180920); + output => "tln", + version => 20200813); + my $VERSION = getVersion(); sub getConfig {return %config} diff --git a/thirdparty/rr-full/plugins/applets.pl b/thirdparty/rr-full/plugins/applets.pl index 1356340e660..be05d648feb 100644 --- a/thirdparty/rr-full/plugins/applets.pl +++ b/thirdparty/rr-full/plugins/applets.pl @@ -4,13 +4,17 @@ # Windows\CurrentVersion\Applets Recent File List values # # Change history +# 20201020 - Added check for RegEdit Favorites +# 20200813 - MITRE updates +# 20200525 - updated date output format # 20140723 - updated to address issues of keys/values not in existence # 20080324 - created # # References # # -# copyright 2008 H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package applets; use strict; @@ -20,8 +24,9 @@ package applets; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20140723); + MITRE => "T1204", + output => "report", + version => 20201020); sub getConfig{return %config} sub getShortDescr { @@ -38,8 +43,11 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching applets v.".$VERSION); - ::rptMsg("applets v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("applets v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -48,14 +56,14 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("Applets"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); # Locate files opened in MS Paint my $paint_key = 'Paint\\Recent File List'; my $paint = $key->get_subkey($paint_key); if (defined $paint) { ::rptMsg($key_path."\\".$paint_key); - ::rptMsg("LastWrite Time ".gmtime($paint->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($paint->get_timestamp())."Z"); my @vals = $paint->get_list_of_values(); if (scalar(@vals) > 0) { @@ -86,11 +94,26 @@ sub pluginmain { if (defined $reg) { ::rptMsg(""); ::rptMsg($key_path."\\".$reg_key); - ::rptMsg("LastWrite Time ".gmtime($reg->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($reg->get_timestamp())."Z"); eval { my $lastkey = $reg->get_value("LastKey")->get_data(); ::rptMsg("RegEdit LastKey value -> ".$lastkey); }; + +# added 20 Oct 2020 + eval { + my $fav; + if ($fav = $reg->get_subkey("Favorites")) { + my @vals = $fav->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg(""); + ::rptMsg("RegEdit Favorites"); + foreach my $v (@vals) { + ::rptMsg(sprintf "%-25s %-50s",$v->get_name(),$v->get_data()); + } + } + } + }; } } else { diff --git a/thirdparty/rr-full/plugins/applets_tln.pl b/thirdparty/rr-full/plugins/applets_tln.pl index 740ae86b96b..bcdab0618b9 100644 --- a/thirdparty/rr-full/plugins/applets_tln.pl +++ b/thirdparty/rr-full/plugins/applets_tln.pl @@ -4,12 +4,13 @@ # Windows\CurrentVersion\Applets Recent File List values # # Change history +# 20200813 - MITRE updates # 20120613 - created # # References # # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package applets_tln; @@ -20,8 +21,9 @@ package applets_tln; hasDescr => 0, category => "program execution", hasRefs => 0, - osmask => 22, - version => 20120613); + MITRE => "T1204", + output => "tln", + version => 20200813); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/appmodel.pl b/thirdparty/rr-full/plugins/appmodel.pl new file mode 100644 index 00000000000..364fbc6451a --- /dev/null +++ b/thirdparty/rr-full/plugins/appmodel.pl @@ -0,0 +1,93 @@ +#----------------------------------------------------------- +# appmodel +# +# +# References +# https://docs.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging +# https://twitter.com/malmoeb/status/1560536646696796161 +# https://www.sentinelone.com/labs/inside-malicious-windows-apps-for-malware-deployment/ +# https://twitter.com/wdormann/status/1466039420684021761 +# https://twitter.com/0gtweet/status/1675583251161792512 +# +# History: +# 20230703 - updates to MITRE, references +# 20220819 - created +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package appmodel; +use strict; + +my %config = (hive => "software", + MITRE => "T1548\.002", + category => "privilege escalation", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20230703); + +sub getConfig{return %config} + +sub getShortDescr { + return "Gets AppModelUnlock values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching appmodel v.".$VERSION); + ::rptMsg("appmodel v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key_path = "Microsoft\\Windows\\CurrentVersion\\AppModelUnlock"; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + eval { + my $l = $key->get_value("AllowAllTrustedApps")->get_data(); + ::rptMsg(sprintf "%-35s %-2d","AllowAllTrustedApps",$l); + }; + if ($@) { + ::rptMsg("AllowAllTrustedApps value not found."); + ::rptMsg(""); + } + + eval { + my $l = $key->get_value("AllowDevelopmentWithoutDevLicense")->get_data(); + ::rptMsg(sprintf "%-35s %-2d","AllowDevelopmentWithoutDevLicense",$l); + }; + if ($@) { + ::rptMsg("AllowDevelopmentWithoutDevLicense value not found."); + } + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Misuse of MS Apps can be an infection vector (see ref). "); + ::rptMsg("AllowAllTrustedApps = 1 allows loading of Apps not from the Windows Store (must have valid cert chain)"); + ::rptMsg("(Enables sideloading)"); + ::rptMsg(""); + ::rptMsg("AllowDevelopmentWithoutDevLicense = 1 enables dev mode, allowing install of Apps from IDE, and allows users"); + ::rptMsg("without SeCreateSymbolicLinkPrivilege to create symlinks."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.sentinelone.com/labs/inside-malicious-windows-apps-for-malware-deployment/"); + ::rptMsg("Ref: https://twitter.com/0gtweet/status/1675583251161792512"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/apppaths.pl b/thirdparty/rr-full/plugins/apppaths.pl index 2ff4fea3527..0b3f38b5d3d 100644 --- a/thirdparty/rr-full/plugins/apppaths.pl +++ b/thirdparty/rr-full/plugins/apppaths.pl @@ -5,24 +5,29 @@ # LastWrite time # # References -# +# https://twitter.com/0gtweet/status/1494617231380131841 <-- HKCU processed first # # History: +# 20200813 - minor updates +# 20200511 - updated date output format +# 20190812 - added support for NTUSER.DAT hives # 20120524 - updated to include 64-bit OSs # 20080404 - created # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package apppaths; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "NTUSER\.DAT,Software", + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - version => 20120524); + output => "report", + version => 20200813); sub getConfig{return %config} @@ -54,14 +59,16 @@ sub pluginmain { # used a list of values to address the need for parsing the App Paths key # in the Wow6432Node key, if it exists. my @paths = ("Microsoft\\Windows\\CurrentVersion\\App Paths", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths"); + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths", + "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths", + "Wow6432Node\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths"); foreach my $key_path (@paths) { my $key; if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("App Paths"); - ::rptMsg($key_path); - ::rptMsg(""); +# ::rptMsg("App Paths"); +# ::rptMsg($key_path); +# ::rptMsg(""); my %apps; my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { @@ -77,7 +84,7 @@ sub pluginmain { } foreach my $t (reverse sort {$a <=> $b} keys %apps) { - ::rptMsg(gmtime($t)." (UTC)"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $item (@{$apps{$t}}) { ::rptMsg(" $item"); } @@ -88,7 +95,7 @@ sub pluginmain { } } else { - ::rptMsg($key_path." not found."); +# ::rptMsg($key_path." not found."); } } } diff --git a/thirdparty/rr-full/plugins/apppaths_tln.pl b/thirdparty/rr-full/plugins/apppaths_tln.pl index 634991935c3..4c19b642288 100644 --- a/thirdparty/rr-full/plugins/apppaths_tln.pl +++ b/thirdparty/rr-full/plugins/apppaths_tln.pl @@ -6,6 +6,8 @@ # References # # History: +# 20200813 - minor updates +# 20190812 - added support for NTUSER.DAT hives # 20130429 - created from apppaths.pl # # copyright 2013 Quantum Analytics Research, LLC @@ -14,12 +16,14 @@ package apppaths_tln; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "NTUSER\.DAT, Software", + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - version => 20130429); + output => "tln", + version => 20200813); sub getConfig{return %config} @@ -43,14 +47,13 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching apppaths_tln v.".$VERSION); -# ::rptMsg("apppaths v.".$VERSION); # banner -# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; -# used a list of values to address the need for parsing the App Paths key -# in the Wow6432Node key, if it exists. - my @paths = ("Microsoft\\Windows\\CurrentVersion\\App Paths"); + my @paths = ("Microsoft\\Windows\\CurrentVersion\\App Paths", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths", + "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths", + "Wow6432Node\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths"); foreach my $key_path (@paths) { my $key; diff --git a/thirdparty/rr-full/plugins/appsetup.pl b/thirdparty/rr-full/plugins/appsetup.pl new file mode 100644 index 00000000000..17e3893e634 --- /dev/null +++ b/thirdparty/rr-full/plugins/appsetup.pl @@ -0,0 +1,73 @@ +#----------------------------------------------------------- +# appsetup +# The WindowsUpdate\Test key reportedly provides persistence, as it is checked +# via Windows Update +# +# +# Change history: +# 20200909 - created +# +# Ref: +# https://support.microsoft.com/en-us/help/195461/how-to-set-up-a-logon-script-only-for-terminal-server-users +# +# https://attack.mitre.org/techniques/T1546 +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package appsetup; +use strict; + +my %config = (hive => "Software", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1546", + version => 20200909); + +sub getConfig{return %config} +sub getShortDescr { + return "Get autolaunch entries for when user connects to Terminal Server"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching appsetup v.".$VERSION); + ::rptMsg("appsetup v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $key_path = ('Microsoft\\Windows NT\\CurrentVersion\\WinLogon'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + eval { + my $app = $key->get_value("AppSetup")->get_data(); + ::rptMsg("AppSetup value = ".$app); + ::rptMsg(""); + ::rptMsg("Analysis Tip: The commands listed will be launched when the user connects to a Terminal Server."); + ::rptMsg("The entries will be found in the system32 folder."); + }; + ::rptMsg("AppSetup value not found.") if ($@); + + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/appspecific.pl b/thirdparty/rr-full/plugins/appspecific.pl index a81b283556c..7533cf67146 100644 --- a/thirdparty/rr-full/plugins/appspecific.pl +++ b/thirdparty/rr-full/plugins/appspecific.pl @@ -3,12 +3,14 @@ # # # Change history +# 20200904 - MITRE updates +# 20200515 - updated date output format # 20120820 - created # # References # # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package appspecific; @@ -18,8 +20,10 @@ package appspecific; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120820); + MITRE => "", + output => "report", + category => "config", + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -48,17 +52,15 @@ sub pluginmain { my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())." (UTC)]"); + ::rptMsg($s->get_name()." [".::format8601Date($s->get_timestamp())."Z]"); my $ts; eval { $ts = $s->get_value("Timestamp")->get_data(); my $t = ::getTime(0,$ts); - ::rptMsg("Timestamp: ".gmtime($t)); + ::rptMsg("Timestamp: ".::format8601Date($t)."Z"); }; - - ::rptMsg(""); } } diff --git a/thirdparty/rr-full/plugins/appx.pl b/thirdparty/rr-full/plugins/appx.pl new file mode 100644 index 00000000000..ffc23d73c2d --- /dev/null +++ b/thirdparty/rr-full/plugins/appx.pl @@ -0,0 +1,104 @@ +#----------------------------------------------------------- +# appx.pl +# Checks for persistence via Universal Windows Platform Apps (see ref) +# +# Change history +# 20200904 - MITRE updates +# 20200427 - updated output date format +# 20191014 - created +# +# References +# https://oddvar.moe/2018/09/06/persistence-using-universal-windows-platform-apps-appx/ +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package appx; +use strict; + +my %config = (hive => "NTUSER\.DAT, USRCLASS\.DAT", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + output => "report", + version => 20200904); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks for persistence via Universal Windows Platform Apps"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching appx v.".$VERSION); + ::rptMsg("appx v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + +# NTUSER.DAT Checks + my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\PackagedAppXDebug"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $sk (@subkeys) { + + eval { + my $def = $sk->get_value("")->get_data(); + my $name = $sk->get_name(); + my $lw = $sk->get_timestamp(); + ::rptMsg($key_path."\\".$name." LastWrite Time: ".::format8601Date($lw)."Z"); + ::rptMsg("Default value: ".$def); + }; + } + } + } + else { +# ::rptMsg($key_path." not found."); + } + +# USRCLASS.DAT Checks + my $key_path = "ActivatableClasses\\Package"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @sk1 = $key->get_list_of_subkeys(); + if (scalar @sk1 > 0) { + foreach my $s1 (@sk1) { + my $s1_name = $s1->get_name(); + my $key_path2 = $s1_name."\\DebugInformation"; + if (my $key2 = $key->get_subkey($key_path2)) { + my @sk2 = $key2->get_list_of_subkeys(); + if (scalar @sk2 > 0) { + foreach my $s2 (@sk2) { + eval { + my $debug = $s2->get_value("DebugPath")->get_data(); + my $name = $s2->get_name(); + my $lw = $s2->get_timestamp(); + ::rptMsg($key_path."\\".$key_path2."\\".$name." LastWrite time: ".::format8601Date($lw)."Z"); + ::rptMsg("DebugPath value: ".$debug); + }; + } + } + } + else { +# ::rptMsg($key_path."\\".$key_path2." not found."); + } + } + } + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/appx_tln.pl b/thirdparty/rr-full/plugins/appx_tln.pl new file mode 100644 index 00000000000..eb6c79adb8c --- /dev/null +++ b/thirdparty/rr-full/plugins/appx_tln.pl @@ -0,0 +1,98 @@ +#----------------------------------------------------------- +# appx_tln.pl +# Checks for persistence via Universal Windows Platform Apps (see ref) +# +# Change history +# 20200904 - MITRE updates +# 20191014 - created +# +# References +# https://oddvar.moe/2018/09/06/persistence-using-universal-windows-platform-apps-appx/ +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package appx_tln; +use strict; + +my %config = (hive => "NTUSER\.DAT, USRCLASS\.DAT", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "tln", + version => 20200904); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks for persistence via Universal Windows Platform Apps"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; +# ::logMsg("Launching appx_tln v.".$VERSION); +# ::rptMsg("appx_tln v.".$VERSION); +# ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + +# NTUSER.DAT Checks + my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\PackagedAppXDebug"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $sk (@subkeys) { + eval { + my $def = $sk->get_value("")->get_data(); + my $name = $sk->get_name(); + my $lw = $sk->get_timestamp(); + ::rptMsg($lw."|REG|||NTUSER ".$key_path."\\".$name." Default value: ".$def); + }; + } + } + } + else { +# ::rptMsg($key_path." not found."); + } + +# USRCLASS.DAT Checks + my $key_path = "ActivatableClasses\\Package"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @sk1 = $key->get_list_of_subkeys(); + if (scalar @sk1 > 0) { + foreach my $s1 (@sk1) { + my $s1_name = $s1->get_name(); + my $key_path2 = $s1_name."\\DebugInformation"; + if (my $key2 = $key->get_subkey($key_path2)) { + my @sk2 = $key2->get_list_of_subkeys(); + if (scalar @sk2 > 0) { + foreach my $s2 (@sk2) { + eval { + my $debug = $s2->get_value("DebugPath")->get_data(); + my $name = $s2->get_name(); + my $lw = $s2->get_timestamp(); + ::rptMsg($lw."|REG|||USRCLASS ".$key_path."\\".$key_path2."\\".$name." DebugPath value: ".$debug); + }; + } + } + } + else { +# ::rptMsg($key_path."\\".$key_path2." not found."); + } + } + } + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ares.pl b/thirdparty/rr-full/plugins/ares.pl deleted file mode 100644 index 0b11cc8a2b6..00000000000 --- a/thirdparty/rr-full/plugins/ares.pl +++ /dev/null @@ -1,109 +0,0 @@ -#----------------------------------------------------------- -# ares.pl -# -# -# Change History -# 20140730 - updated search terms detection (G. Neives) -# 20130312 - updated based on data provided by J. Weg -# 20120507 - modified to remove the traversing function, to only get -# a limited amount of data. -# 20110603 - modified F. Kolenbrander -# parsing some values according ares source code, like searches and -# timestamps. -# 20110530 - created -# -# References -# -# -# copyright 2012 Quantum Analytics Research, LLC -# author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package ares; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20140730); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's Software/Ares key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching ares v.".$VERSION); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Ares'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); -# ::rptMsg(""); - my %ares = (); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - $ares{$v->get_name()} = $v->get_data(); - } - ::rptMsg(""); - ::rptMsg("RegisterEmail: ".$ares{"RegisterEmail"}) if (exists $ares{"RegisterEmail"}); - ::rptMsg("Stats\.LstConnect: ".gmtime($ares{"Stats\.LstConnect"})." UTC") if (exists $ares{"Stats\.LstConnect"}); - ::rptMsg("Personal\.Nickname: ".hex2ascii($ares{"Personal\.Nickname"})) if (exists $ares{"Personal\.Nickname"}); - ::rptMsg("General\.Language: ".hex2ascii($ares{"General\.Language"})) if (exists $ares{"General\.Language"}); - ::rptMsg("PrivateMessage\.AwayMessage: ".hex2ascii($ares{"PrivateMessage\.AwayMessage"})) if (exists $ares{"PrivateMessage\.AwayMessage"}); - - } - else { - ::rptMsg($key->get_name()." has no values."); - } - ::rptMsg(""); - getSearchTerms($key); - - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub getSearchTerms { - my $key = shift; - - my $count = 0; - ::rptMsg("Search Terms:"); - my @subkeys = ("audio\.gen","gen\.gen","image\.gen","video\.aut","video\.dat","video\.gen","video\.tit"); - - foreach my $sk (@subkeys) { - my $gen = $key->get_subkey("Search\.History")->get_subkey($sk); - my @vals = $gen->get_list_of_values(); - if (scalar(@vals) > 0) { - $count = 1; - ::rptMsg($gen->get_name()); - ::rptMsg("LastWrite: ".gmtime($gen->get_timestamp())." (UTC)"); - foreach my $v (@vals) { - next if ($v->get_name() eq ""); - ::rptMsg(" ".hex2ascii($v->get_name())); - } - } - } - ::rptMsg("No search terms found\.") if ($count == 0); -} - -sub hex2ascii { - return pack('H*',shift); -} - -1; diff --git a/thirdparty/rr-full/plugins/arpcache.pl b/thirdparty/rr-full/plugins/arpcache.pl index 62ce950da23..515cdc50d53 100644 --- a/thirdparty/rr-full/plugins/arpcache.pl +++ b/thirdparty/rr-full/plugins/arpcache.pl @@ -6,6 +6,8 @@ # starts at 0x1c) # # Change history +# 20200816 - MITRE updates +# 20200515 - updated date output format # 20090413 - Created # # References @@ -16,7 +18,8 @@ # as well as possibly an "Outerinfo" subkey indicating that spyware is # installed. # -# copyright 2009 H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package arpcache; use strict; @@ -25,8 +28,10 @@ package arpcache; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20090413); + MITRE => "", + output => "report", + category => "config", + version => 20200816); sub getConfig{return %config} sub getShortDescr { @@ -54,7 +59,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { @@ -80,24 +85,22 @@ sub pluginmain { foreach my $t (reverse sort {$a <=> $b} keys %arpcache) { - ::rptMsg(gmtime($t)." (UTC)"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $item (@{$arpcache{$t}}) { my ($name,$path,$date) = split(/\|/,$item,3); ::rptMsg(" ".$name); my $str = $path unless ($path eq ""); - $str .= " [".gmtime($date)."]" unless ($date == 0); + $str .= " [".::format8601Date($date)."Z]" unless ($date == 0); ::rptMsg(" -> ".$str) unless ($str eq ""); } } } else { ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } @@ -122,7 +125,6 @@ sub parsePath { while($tag) { $ofs += 2; my $i = substr($data,$ofs,2); - last unless (defined $i); if (unpack("v",$i) == 0) { $tag = 0; } @@ -131,6 +133,6 @@ sub parsePath { } } } - $str =~ s/\x00//g; + $str =~ s/\00//g; return $str; } \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/assoc.pl b/thirdparty/rr-full/plugins/assoc.pl index 0fbb55eee01..058a8f34406 100644 --- a/thirdparty/rr-full/plugins/assoc.pl +++ b/thirdparty/rr-full/plugins/assoc.pl @@ -4,26 +4,33 @@ # Can take considerable time to run; recommend running it via rip.exe # # History +# 20220829 - updated, moved to active plugins folder, added MITRE mapping # 20180117 - updated, based on input from Jean, jean.crush@hotmail.fr # 20080815 - created # +# References +# https://cocomelonc.github.io/malware/2022/08/26/malware-pers-9.html # -# copyright 2008 H. Carvey, keydet89@yahoo.com +# copyright 2022, QAR LLC +# H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package assoc; use strict; -my %config = (hive => "Software,USRCLASS", +my %config = (hive => "software", osmask => 22, hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20180117); + output => "report", + category => "persistence", + MITRE => "T1546\.001", + version => 20220829); sub getConfig{return %config} sub getShortDescr { - return "Get list of file ext associations"; + return "Get shell\\open\\command settings for various file types"; } sub getDescr{} sub getRefs {} @@ -36,56 +43,35 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching assoc v.".$VERSION); - ::rptMsg("assoc v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("assoc v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - my @paths = ("Classes","Classes\\Wow6432Node","Wow6432Node"); - my $key; - foreach my $key_path (@paths) { - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("assoc"); - ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); -# First step will be to get a list of all of the file extensions - my %ext; - my @sk = $key->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - my $name = $s->get_name(); - next unless ($name =~ m/^\.\w+$/); - my $data; - eval { - $data = $s->get_value("")->get_data(); - }; - if ($@) { -# Error generated, as "(Default)" value was not found - } - else { - $ext{$name} = $data if ($data ne ""); - } - } -# Once a list of all file ext subkeys has been compiled, access the file type -# to determine the command line used to launch files with that extension - foreach my $e (keys %ext) { - my $cmd; - eval { - $cmd = $key->get_subkey($ext{$e}."\\shell\\open\\command")->get_value("")->get_data(); - }; - if ($@) { -# error generated attempting to locate .\shell\open\command\(Default) value - } - else { - ::rptMsg($e." : ".$cmd); - } - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } + my $key = (); + my $key_path = "Classes"; + my @types = ("exefile","evtfile","evtxfile","inifile","Excel\.CSV","WSFFile"); + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("assoc"); + foreach my $t (@types) { + + eval { + my $path = $t."\\shell\\open\\command"; + my $cmd = $key->get_subkey($path)->get_value("")->get_data(); + ::rptMsg($path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_subkey($path)->get_timestamp())."Z"); + ::rptMsg("Cmd: ".$cmd); + ::rptMsg(""); + }; } + } + ::rptMsg("Analysis Tip: Malware can persist by taking over the default actions when a user double-clicks a particular file type."); + ::rptMsg(""); +# ::rptMsg(""); + ::rptMsg("Ref: https://cocomelonc.github.io/malware/2022/08/26/malware-pers-9.html"); } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/at.pl b/thirdparty/rr-full/plugins/at.pl deleted file mode 100644 index af1954e1331..00000000000 --- a/thirdparty/rr-full/plugins/at.pl +++ /dev/null @@ -1,65 +0,0 @@ -#----------------------------------------------------------- -# at.pl -# -# -# Change history -# 20140821 - created -# -# -# -# -# Copyright (c) 2014 QAR,LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package at; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - category => "program execution", - version => 20140821); - -my $VERSION = getVersion(); - -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getDescr {} -sub getShortDescr {return "Checks Software hive for AT jobs";} -sub getRefs {} - -sub pluginmain { - my $class = shift; - my $hive = shift; - - ::logMsg("Launching at v.".$VERSION); - ::rptMsg("at v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()); - ::rptMsg(""); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = 'Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tree'; - - if ($key = $root_key->get_subkey($key_path)) { - - my @sk = $key->get_list_of_subkeys(); - if (scalar @sk > 0) { - foreach my $s (@sk) { - my $name = $s->get_name(); - next unless ($name =~ m/^At/); - my $lw = $s->get_timestamp(); - ::rptMsg($name." - LastWrite time: ".gmtime($lw)." UTC"); - } - } - } - else { - - - } -} - -1; diff --git a/thirdparty/rr-full/plugins/at_tln.pl b/thirdparty/rr-full/plugins/at_tln.pl deleted file mode 100644 index cfa456b0089..00000000000 --- a/thirdparty/rr-full/plugins/at_tln.pl +++ /dev/null @@ -1,60 +0,0 @@ -#----------------------------------------------------------- -# at_tln.pl -# -# -# Change history -# 20140821 - created -# -# -# -# -# Copyright (c) 2014 QAR,LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package at_tln; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - category => "program execution", - version => 20140821); - -my $VERSION = getVersion(); - -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getDescr {} -sub getShortDescr {return "Checks Software hive for AT jobs";} -sub getRefs {} - -sub pluginmain { - my $class = shift; - my $hive = shift; - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = 'Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tree'; - - if ($key = $root_key->get_subkey($key_path)) { - - my @sk = $key->get_list_of_subkeys(); - if (scalar @sk > 0) { - foreach my $s (@sk) { - my $name = $s->get_name(); - next unless ($name =~ m/^At/); - my $lw = $s->get_timestamp(); - ::rptMsg($lw."|REG|||[AT Job] ".$name); - } - } - } - else { - - - } -} - -1; diff --git a/thirdparty/rr-full/plugins/attachmgr.pl b/thirdparty/rr-full/plugins/attachmgr.pl index 68749eb681c..8d4b031d8c6 100644 --- a/thirdparty/rr-full/plugins/attachmgr.pl +++ b/thirdparty/rr-full/plugins/attachmgr.pl @@ -7,15 +7,18 @@ # Category: Malware # # Change history +# 20220926 - updated +# 20200814 - MITRE updates +# 20200525 - updated date output format, removed alertMsg() functionality # 20130425 - added alertMsg() functionality # 20130117 - created # # References # http://journeyintoir.blogspot.com/2010/10/anatomy-of-drive-by-part-2.html # http://support.microsoft.com/kb/883260 -# http://blog.handlerdiaries.com/?p=703 +# https://support.microsoft.com/en-us/topic/information-about-the-attachment-manager-in-microsoft-windows-c48a4dcd-8de5-2af5-ee9b-cd795ae42738 # -# copyright 2013 Quantum Analytics Research, LLC +# copyright 2022 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package attachmgr; @@ -25,8 +28,10 @@ package attachmgr; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20130425); + output => "report", + MITRE => "T1553\.005", + category => "defense evasion", + version => 20220926); sub getConfig{return %config} sub getShortDescr { @@ -42,11 +47,12 @@ sub getShortDescr { sub pluginmain { my $class = shift; my $ntuser = shift; - my @temps; ::logMsg("Launching attachmgr v.".$VERSION); - ::rptMsg("attachmgr v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("attachmgr v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -57,19 +63,13 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { foreach my $v (@vals) { my $name = $v->get_name(); my $data = $v->get_data(); -# checks added 20130425 -# settings information derived from MS KB 883260 - ::alertMsg("ALERT: attachmgr: ".$key_path." SaveZoneInformation value found: ".$data) if ($name eq "SaveZoneInformation"); - ::alertMsg("ALERT: attachmgr: ".$key_path." ScanWithAntiVirus value found: ".$data) if ($name eq "ScanWithAntiVirus"); - ::alertMsg("ALERT: attachmgr: ".$key_path." LowRiskFileTypes value includes exe: ".$data) if ($name eq "LowRiskFileTypes" && grep(/exe/,$data)); - - ::rptMsg(sprintf "%-15s %-6s",$name,$data); + ::rptMsg(sprintf "%-30s %-6s",$name,$data); } } else { @@ -81,6 +81,13 @@ sub pluginmain { } ::rptMsg(""); } +# ::rptMsg(""); + ::rptMsg("Analysis Tip: Attachment Manager settings can determine security settings related to attachments."); + ::rptMsg(""); + ::rptMsg("SaveZoneInformation = 1 disables saving of zone information (MOTW)"); + ::rptMsg("HideZoneInfoOnProperties = 1 hides the ability for the users to manually remove zone info from files."); + ::rptMsg(""); + ::rptMsg("Ref: https://support.microsoft.com/en-us/topic/information-about-the-attachment-manager-in-microsoft-windows-c48a4dcd-8de5-2af5-ee9b-cd795ae42738"); } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/attachmgr_tln.pl b/thirdparty/rr-full/plugins/attachmgr_tln.pl index e04d52e60a7..29ad8abecfe 100644 --- a/thirdparty/rr-full/plugins/attachmgr_tln.pl +++ b/thirdparty/rr-full/plugins/attachmgr_tln.pl @@ -7,6 +7,7 @@ # Category: Malware # # Change history +# 20200816 - MITRE updates # 20130425 - created # # References @@ -23,8 +24,10 @@ package attachmgr_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20130425); + MITRE => "", + output => "tln", + category => "config", + version => 20200816); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/audiodev.pl b/thirdparty/rr-full/plugins/audiodev.pl index bb6d6d3d097..68294fa42c0 100644 --- a/thirdparty/rr-full/plugins/audiodev.pl +++ b/thirdparty/rr-full/plugins/audiodev.pl @@ -4,24 +4,27 @@ # for use with mixer.pl/mixer_tln.pl plugins # # Change history: +# 20200814 - MITRE updates +# 20200525 - minor updates # 20141112 - created # # Ref: # http://www.ghettoforensics.com/2014/11/dj-forensics-analysis-of-sound-mixer.html # -# copyright 2014 QAR,LLC +# copyright 2020 QAR,LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package audiodev; use strict; my %config = (hive => "Software", - category => "devices", + category => "devices", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20141112); + MITRE => "", + output => "report", + version => 20200814); sub getConfig{return %config} sub getShortDescr { @@ -84,11 +87,5 @@ sub pluginmain { else { ::rptMsg("Could not get root key\."); } - - - - - - } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/auditfail.pl b/thirdparty/rr-full/plugins/auditfail.pl deleted file mode 100644 index 4ac00d72bd0..00000000000 --- a/thirdparty/rr-full/plugins/auditfail.pl +++ /dev/null @@ -1,68 +0,0 @@ -#----------------------------------------------------------- -# auditfail.pl -# -# Ref: -# http://support.microsoft.com/kb/140058 -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package auditfail; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20081212); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get CrashOnAuditFail value"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my %val = (0 => "Feature is off; the system will not halt", - 1 => "Feature is on; the system will halt when events cannot be written to the ". - "Security Event Log", - 2 => "Feature is on and has been triggered; only Administrators can log in"); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching auditfail v.".$VERSION); - ::rptMsg("auditfail v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# Code for System file, getting CurrentControlSet - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - - my $lsa_path = "ControlSet00".$current."\\Control\\Lsa"; - my $lsa; - if ($lsa = $root_key->get_subkey($lsa_path)) { - - eval { - my $crash = $lsa->get_value("crashonauditfail")->get_data(); - ::rptMsg("CrashOnAuditFail = ".$crash); - ::rptMsg($val{$crash}); - }; - ::rptMsg($@) if ($@); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/auditpol.pl b/thirdparty/rr-full/plugins/auditpol.pl index a1b274d5ca7..5cb3cc0df8a 100644 --- a/thirdparty/rr-full/plugins/auditpol.pl +++ b/thirdparty/rr-full/plugins/auditpol.pl @@ -4,6 +4,8 @@ # *Works for Win7 and Win10 at the moment # # History +# 20200813 - MITRE updates +# 20200515 - updated date output format # 20190510 - updated; Win2016 # 20151202 - created # @@ -14,7 +16,7 @@ # # Equiv: auditpol /get /category:* # -# copyright 2015 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package auditpol; @@ -24,8 +26,10 @@ package auditpol; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20190510); + MITRE => "", + category => "config", + output => "report", + version => 20200813); sub getConfig{return %config} sub getShortDescr { @@ -57,7 +61,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("auditpol"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my $data; diff --git a/thirdparty/rr-full/plugins/auditpol_xp.pl b/thirdparty/rr-full/plugins/auditpol_xp.pl deleted file mode 100644 index a9f4d7b6c26..00000000000 --- a/thirdparty/rr-full/plugins/auditpol_xp.pl +++ /dev/null @@ -1,151 +0,0 @@ -#----------------------------------------------------------- -# auditpol -# Get the audit policy from the Security hive file -# -# -# History -# 20121128 - updated for later versions of Windows -# 20080327 - created -# -# -# copyright 2012 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package auditpol_xp; -use strict; - -my %config = (hive => "Security", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20121128); - -sub getConfig{return %config} -sub getShortDescr { - return "Get audit policy from the Security hive file"; -} -sub getDescr{} -sub getRefs { - my %refs = ("How To Determine Audit Policies from the Registry" => - "http://support.microsoft.com/default.aspx?scid=kb;EN-US;q246120"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my %audit = (0 => "N", - 1 => "S", - 2 => "F", - 3 => "S/F"); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching auditpol_xp v.".$VERSION); - ::rptMsg("auditpol_xp v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Policy\\PolAdtEv"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("auditpol"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $data; - eval { - $data = $key->get_value("")->get_data(); - ::rptMsg("Length of data: ".length($data)." bytes."); - - my @d = printData($data); - foreach (0..(scalar(@d) - 1)) { - ::rptMsg($d[$_]); - } - - }; - if ($@) { - ::rptMsg("Error occurred getting data from ".$key_path); - ::rptMsg(" - ".$@); - } - else { -# Check to see if auditing is enabled - my $enabled = unpack("C",substr($data,0,1)); - if ($enabled) { - ::rptMsg("Auditing is enabled."); -# Get audit configuration settings - my @vals = unpack("V*",$data); - ::rptMsg("\tAudit System Events = ".$audit{$vals[1]}); - ::rptMsg("\tAudit Logon Events = ".$audit{$vals[2]}); - ::rptMsg("\tAudit Object Access = ".$audit{$vals[3]}); - ::rptMsg("\tAudit Privilege Use = ".$audit{$vals[4]}); - ::rptMsg("\tAudit Process Tracking = ".$audit{$vals[5]}); - ::rptMsg("\tAudit Policy Change = ".$audit{$vals[6]}); - ::rptMsg("\tAudit Account Management = ".$audit{$vals[7]}); - ::rptMsg("\tAudit Dir Service Access = ".$audit{$vals[8]}); - ::rptMsg("\tAudit Account Logon Events = ".$audit{$vals[9]}); - } - else { - ::rptMsg("**Auditing is NOT enabled."); - } - } - } - else { - ::rptMsg($key_path." not found."); - } -} - - -#----------------------------------------------------------- -# printData() -# subroutine used primarily for debugging; takes an arbitrary -# length of binary data, prints it out in hex editor-style -# format for easy debugging -#----------------------------------------------------------- -sub printData { - my $data = shift; - my $len = length($data); - my $tag = 1; - my $cnt = 0; - my @display = (); - - my $loop = $len/16; - $loop++ if ($len%16); - - foreach my $cnt (0..($loop - 1)) { -# while ($tag) { - my $left = $len - ($cnt * 16); - - my $n; - ($left < 16) ? ($n = $left) : ($n = 16); - - my $seg = substr($data,$cnt * 16,$n); - my @str1 = split(//,unpack("H*",$seg)); - - my @s3; - my $str = ""; - - foreach my $i (0..($n - 1)) { - $s3[$i] = $str1[$i * 2].$str1[($i * 2) + 1]; - - if (hex($s3[$i]) > 0x1f && hex($s3[$i]) < 0x7f) { - $str .= chr(hex($s3[$i])); - } - else { - $str .= "\."; - } - } - my $h = join(' ',@s3); -# ::rptMsg(sprintf "0x%08x: %-47s ".$str,($cnt * 16),$h); - $display[$cnt] = sprintf "0x%08x: %-47s ".$str,($cnt * 16),$h; - } - return @display; -} - - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/auth.pl b/thirdparty/rr-full/plugins/auth.pl new file mode 100644 index 00000000000..87de1a8234e --- /dev/null +++ b/thirdparty/rr-full/plugins/auth.pl @@ -0,0 +1,88 @@ +#----------------------------------------------------------- +# auth.pl +# Gets information about the most recent login +# +# Change history: +# 20200816 - MITRE update +# 20200724 - created +# +# Ref: +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package auth; +use strict; + +my %config = (hive => "Software", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20200816); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets Authentication info"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching auth v.".$VERSION); + ::rptMsg("auth v.".$VERSION); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + + my @paths = ('Microsoft\\Windows\\CurrentVersion\\Authentication\\LogonUI'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + + my @vals = ("LastLoggedOnSAMUser","LastLoggedOnUser","LastLoggedOnDisplayName","LastLoggedOnUserSID"); + + foreach my $v (@vals) { + eval { + my $i = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-25s %-50s",$v,$i); + }; + } + +# if (my $sess = $key->get_subkey("SessionData")){ +# ::rptMsg(""); +# my @subkeys = $sess->get_list_of_subkeys(); +# if (scalar @subkeys > 0) { +# foreach my $s (@subkeys) { +# ::rptMsg($s->get_name()); +# ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); +# foreach my $v (@vals) { +# eval { +# my $i = $key->get_value($v)->get_data(); +# ::rptMsg(sprintf "%-20s %-50s",$v,$i); +# }; +# } +# ::rptMsg(""); +# } +# } +# } + } + else { + ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/autoadminlogon.pl b/thirdparty/rr-full/plugins/autoadminlogon.pl new file mode 100644 index 00000000000..7605910341d --- /dev/null +++ b/thirdparty/rr-full/plugins/autoadminlogon.pl @@ -0,0 +1,77 @@ +#----------------------------------------------------------- +# autoadminlogon.pl +# Get autoadminlogon settings +# +# History +# 20220829 - created +# +# References +# https://docs.microsoft.com/en-us/troubleshoot/windows-server/user-profiles-and-logon/turn-on-automatic-logon +# +# copyright 2022, QAR LLC +# H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package autoadminlogon; +use strict; + +my %config = (hive => "software", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + category => "persistence", + MITRE => "T1078\.003", + version => 20220829); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get autoadminlogon settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching autoadminlogon v.".$VERSION); + ::rptMsg("Launching autoadminlogon v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key = (); + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\WinLogon"; + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(" "); + + eval { + my $a = $key->get_value("AutoAdminLogon")->get_data(); + ::rptMsg("AutoAdminLogon enabled.") if ($a == 1); + ::rptMsg("AutoAdminLogon disabled.") if ($a == 0); + }; + ::rptMsg("AutoAdminLogon value not found.") if ($@); + + eval { + my $p = $key->get_value("DefaultPassword")->get_data(); + ::rptMsg("DefaultPassword: ".$p); + }; + + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the \"AutoAdminLogon\" value exists and is set to \"1\", the system will automatically log into"); + ::rptMsg("the admin account, and the password can be found in plain text in the \"DefaultPassword\" value."); + ::rptMsg(""); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/troubleshoot/windows-server/user-profiles-and-logon/turn-on-automatic-logon"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/autodialdll.pl b/thirdparty/rr-full/plugins/autodialdll.pl new file mode 100644 index 00000000000..4c06f361a82 --- /dev/null +++ b/thirdparty/rr-full/plugins/autodialdll.pl @@ -0,0 +1,75 @@ +#----------------------------------------------------------- +# autodialdll.pl +# get autodialdll DLL +# +# History +# 20221026 - created +# +# References +# https://www.mdsec.co.uk/2022/10/autodialdlling-your-way/ +# https://www.hexacorn.com/blog/2015/01/13/beyond-good-ol-run-key-part-24/ +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package autodialdll; +use strict; +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1546", + category => "persistence", + version => 20221026); + +sub getConfig{return %config} +sub getShortDescr { + return "Get AutodialDLL DLL"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $key; + + ::logMsg("Launching autodialdll v.".$VERSION); + ::rptMsg("autodialdll v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services\\WinSock2\\Parameters"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $i = $key->get_value("AutodialDLL")->get_data(); + ::rptMsg("AutodialDLL value: ".$i); + }; + ::rptMsg("AutodialDLL value not found.") if ($@); + + + ::rptMsg(""); + ::rptMsg("Analysis Tip: The default setting for the AutodialDLL value is \"C:\\Windows\\system32\\rasadhlp\.dll\"."); + ::rptMsg("Modifying the path to a different DLL has been observed being used for persistence, and it can also be used"); + ::rptMsg("for lateral movement."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.mdsec.co.uk/2022/10/autodialdlling-your-way/"); + } + else { + ::rptMsg($key_path." not found."); + } +} +1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/autoendtasks.pl b/thirdparty/rr-full/plugins/autoendtasks.pl deleted file mode 100644 index 3468a3e5e20..00000000000 --- a/thirdparty/rr-full/plugins/autoendtasks.pl +++ /dev/null @@ -1,68 +0,0 @@ -#----------------------------------------------------------- -# autoendtasks.pl -# -# History -# 20081128 - created -# -# Ref: -# http://support.microsoft.com/kb/555619 -# This Registry setting tells XP (and Vista) to automatically -# end non-responsive tasks; value may not exist on Vista. -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package autoendtasks; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20081128); - -sub getConfig{return %config} - -sub getShortDescr { - return "Automatically end a non-responsive task"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching autoendtasks v.".$VERSION); - ::rptMsg("autoendtasks v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = 'Control Panel\\Desktop'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg("autoendtasks"); - ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $autoend; - eval { - $autoend = $key->get_value("AutoEndTasks")->get_data(); - }; - if ($@) { - ::rptMsg("AutoEndTasks value not found."); - } - else { - ::rptMsg("AutoEndTasks = ".$autoend); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/automount.pl b/thirdparty/rr-full/plugins/automount.pl new file mode 100644 index 00000000000..bd9f0ad1fd8 --- /dev/null +++ b/thirdparty/rr-full/plugins/automount.pl @@ -0,0 +1,74 @@ +#----------------------------------------------------------- +# automount.pl +# get automount settings +# +# History +# 20221010 - created +# +# References +# https://learn.microsoft.com/en-us/windows/win32/api/vds/ne-vds-vds_san_policy +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package automount; +use strict; +my %config = (hive => "System", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1091", + category => "initial access", + version => 20221010); + +sub getConfig{return %config} +sub getShortDescr { + return "Get automount Settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $key; + + ::logMsg("Launching automount v.".$VERSION); + ::rptMsg("automount v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services\\mountmgr"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $i = $key->get_value("NoAutoMount")->get_data(); + ::rptMsg("NoAutoMount value: ".$i); + }; + ::rptMsg("NoAutoMount value not found.") if ($@); + + + ::rptMsg(""); + ::rptMsg("Analysis Tip: Modern Windows OSs will automount file systems, such as from USB devices, assigning a volume name."); + ::rptMsg("NoAutoMount = 0, or does not exist: enabled"); + ::rptMsg("NoAutoMount = 1, disabled"); + ::rptMsg(""); + ::rptMsg("Ref: https://learn.microsoft.com/en-us/windows/win32/api/vds/ne-vds-vds_san_policy"); + } + else { + ::rptMsg($key_path." not found."); + } +} +1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/autorun.pl b/thirdparty/rr-full/plugins/autorun.pl index 814e8b2b63c..09f78ce2716 100644 --- a/thirdparty/rr-full/plugins/autorun.pl +++ b/thirdparty/rr-full/plugins/autorun.pl @@ -1,76 +1,115 @@ #----------------------------------------------------------- -# autorun.pl -# Get autorun settings -# +# autorun +# +# +# # Change history -# +# 20221109 - created # # References -# http://support.microsoft.com/kb/953252 -# http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit -# /regentry/91525.mspx?mfr=true +# https://www.samlogic.net/articles/autorun-enable-disable-nodrivetypeautorun.htm +# https://superuser.com/questions/1378243/nodrivetypeautorun-registry-key-missing-from-windows-10 +# https://learn.microsoft.com/en-us/windows/win32/shell/autoplay-reg # -# copyright 2008-2009 H. Carvey +# Copyright 2022 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package autorun; use strict; -my %config = (hive => "NTUSER\.DAT", +my %config = (hive => "NTUSER\.DAT, Software", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20081212); + MITRE => "T1204", + category => "execution", + output => "report", + version => 20221109); -sub getConfig{return %config} +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} sub getShortDescr { - return "Gets autorun settings"; + return "Checks autorun settings"; } -sub getDescr{} sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); sub pluginmain { my $class = shift; - my $ntuser = shift; + my $hive = shift; + ::logMsg("Launching autorun v.".$VERSION); - ::rptMsg("autorun v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); + ::rptMsg("autorun v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"; my $key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my $key_path = (); + if ($hive_guess eq "software") { + $key_path = "Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"; + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"; + } + else {} + + if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); eval { - my $nodrive = $key->get_value("NoDriveTypeAutoRun")->get_data(); - my $str = sprintf "%-20s 0x%x","NoDriveTypeAutoRun",$nodrive; - ::rptMsg($str); + my $a = $key->get_value("NoDriveTypeAutoRun")->get_data(); + ::rptMsg(sprintf "%-20s 0x%04x","NoDriveTypeAutoRun",$a); }; - ::rptMsg("Error: ".$@) if ($@); - -# http://support.microsoft.com/kb/953252 + ::rptMsg("NoDriveTypeAutoRun value not found.") if ($@); + eval { - my $honor = $key->get_value("HonorAutorunSetting")->get_data(); - my $str = sprintf "%-20s 0x%x","HonorAutorunSetting",$honor; - ::rptMsg($str); + my $a = $key->get_value("NoDriveAutoRun")->get_data(); + ::rptMsg(sprintf "%-20s 0x%04x","NoDriveAutoRun",$a); }; - ::rptMsg("HonorAutorunSetting not found.") if ($@); - ::rptMsg(""); - ::rptMsg("Autorun settings in the HKLM hive take precedence over those in"); - ::rptMsg("the HKCU hive."); + ::rptMsg("NoDriveAutoRun value not found.") if ($@); } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); + ::rptMsg($key_path." key not found."); + } + + if ($hive_guess eq "ntuser") { + ::rptMsg(""); + $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $a = $key->get_value("DisableAutoplay")->get_data(); + ::rptMsg(sprintf "%-20s 0x%04x","DisableAutoplay",$a); + ::rptMsg(""); + ::rptMsg("1 - Autoplay disabled"); + ::rptMsg("0 - Autoplay enabled"); + }; + ::rptMsg("DisableAutoplay value not found.") if ($@); + + } + else { + ::rptMsg($key_path." key not found."); + } } - } -1; \ No newline at end of file +1; diff --git a/thirdparty/rr-full/plugins/backuprestore.pl b/thirdparty/rr-full/plugins/backuprestore.pl index 0b4ab1e604a..1f72aef60b4 100644 --- a/thirdparty/rr-full/plugins/backuprestore.pl +++ b/thirdparty/rr-full/plugins/backuprestore.pl @@ -2,16 +2,25 @@ # backuprestore.pl # Access System hive file to get the contents of the FilesNotToSnapshot, KeysNotToRestore, and FilesNotToBackup keys # +# Threat actors have been observed modifying the contents of the FilesNotToSnapshot OutlookOST value, and then +# stealing a copy of user OST files by creating a snapshot, or via esentutl.exe. +# # Change history -# 20130904: cleaned up code -# 9/14/2012: retired the filesnottosnapshot.pl plugin since BackupRestore checks the same key +# 20201012 - MITRE updates +# 20200517 - updated date output format +# 20130904 - cleaned up code +# 9/14/2012 - retired the filesnottosnapshot.pl plugin since BackupRestore checks the same key # # References # Troy Larson's Windows 7 presentation slide deck http://computer-forensics.sans.org/summit-archives/2010/files/12-larson-windows7-foreniscs.pdf # QCCIS white paper Reliably recovering evidential data from Volume Shadow Copies http://www.qccis.com/downloads/whitepapers/QCC%20VSS # http://msdn.microsoft.com/en-us/library/windows/desktop/bb891959(v=vs.85).aspx # -# copyright 2012 Corey Harrell (Journey Into Incident Response) +# https://attack.mitre.org/techniques/T1562/001/ +# +# original plugin written by Corey Harrell (Journey Into Incident Response) +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package backuprestore; use strict; @@ -20,8 +29,10 @@ package backuprestore; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20130904); + output => "report", + MITRE => "T1562\.001", + category => "defense evasion", + version => 20201012); sub getConfig{return %config} sub getShortDescr { @@ -38,9 +49,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching backuprestore v.".$VERSION); - ::rptMsg("backuprestore v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); - + ::rptMsg("backuprestore v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -58,7 +70,7 @@ sub pluginmain { if ($fns = $root_key->get_subkey($fns_path)) { # ::rptMsg("FilesNotToSnapshot key"); ::rptMsg($fns_path); - ::rptMsg("LastWrite Time ".gmtime($fns->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($fns->get_timestamp())."Z"); ::rptMsg("The listed directories/files are not backed up in Volume Shadow Copies"); ::rptMsg(""); @@ -78,6 +90,9 @@ sub pluginmain { ::rptMsg(" $item"); } } + ::rptMsg("Analysis Tip: A threat actor can add entries in order to gain access to files that are normally"); + ::rptMsg("locked; creating a VSC would then allow them to access that file."); +# ::rptMsg(""); } else { ::rptMsg($fns_path." has no values."); @@ -92,7 +107,7 @@ sub pluginmain { if ($fnb = $root_key->get_subkey($fnb_path)) { ::rptMsg("FilesNotToBackup key"); ::rptMsg($fnb_path); - ::rptMsg("LastWrite Time ".gmtime($fnb->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($fnb->get_timestamp())."Z"); ::rptMsg("Specifies the directories and files that backup applications should not backup or restore"); ::rptMsg(""); @@ -126,7 +141,7 @@ sub pluginmain { if ($knr = $root_key->get_subkey($knr_path)) { ::rptMsg("KeysNotToRestore key"); ::rptMsg($knr_path); - ::rptMsg("LastWrite Time ".gmtime($knr->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($knr->get_timestamp())."Z"); ::rptMsg(""); ::rptMsg("Specifies the names of the registry subkeys and values that backup applications should not restore"); ::rptMsg(""); diff --git a/thirdparty/rr-full/plugins/bam.pl b/thirdparty/rr-full/plugins/bam.pl index af64823cfd3..daebb137568 100644 --- a/thirdparty/rr-full/plugins/bam.pl +++ b/thirdparty/rr-full/plugins/bam.pl @@ -2,6 +2,8 @@ # bam.pl # # History: +# 20200904 - MITRE updates +# 20200427 - updated output date format # 20180225 - created # # References: @@ -10,21 +12,22 @@ # http://batcmd.com/windows/10/services/bam/ # # -# copyright 2018 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package bam; use strict; my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "Program Execution", + hivemask => 4, + output => "report", + category => "execution", + MITRE => "T1059", + output => "report", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20180225); + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -43,8 +46,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching bam v.".$VERSION); - ::rptMsg("bam v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("bam v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -56,7 +61,8 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { $current = $key->get_value("Current")->get_data(); $ccs = "ControlSet00".$current; - my $bam_path = $ccs."\\Services\\bam\\UserSettings"; + my $bam_path = $ccs."\\Services\\bam\\State\\UserSettings"; + my $bam_path2 = $ccs."\\Services\\bam\\UserSettings"; my $bam; if ($bam = $root_key->get_subkey($bam_path)) { my @sk = $bam->get_list_of_subkeys(); @@ -70,6 +76,18 @@ sub pluginmain { else { ::rptMsg($bam_path." not found."); } + if ($bam = $root_key->get_subkey($bam_path2)) { + my @sk = $bam->get_list_of_subkeys(); + if (scalar(@sk) > 0) { + foreach my $s (@sk) { + processKey($s); + } + } + + } + else { + ::rptMsg($bam_path2." not found."); + } } else { ::rptMsg($key_path." not found."); @@ -94,7 +112,7 @@ sub processKey { if ($v->get_type() == 3) { my ($t0,$t1) = unpack("VV",substr($v->get_data(),0,8)); $t = ::getTime($t0,$t1); - ::rptMsg(" ".gmtime($t)." - ".$name); + ::rptMsg(" ".::format8601Date($t)."Z"." - ".$name); } } diff --git a/thirdparty/rr-full/plugins/bam_tln.pl b/thirdparty/rr-full/plugins/bam_tln.pl index b86928fc916..366dbfd2e8c 100644 --- a/thirdparty/rr-full/plugins/bam_tln.pl +++ b/thirdparty/rr-full/plugins/bam_tln.pl @@ -2,6 +2,7 @@ # bam_tln.pl # # History: +# 20200904 - MITRE updates # 20180225 - created # # References: @@ -17,14 +18,14 @@ package bam_tln; use strict; my %config = (hive => "System", - hivemask => 4, - output => "tln", - category => "Program Execution", + hivemask => 4, + output => "tln", + category => "program execution", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20180225); + MITRE => "", + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -53,7 +54,8 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { $current = $key->get_value("Current")->get_data(); $ccs = "ControlSet00".$current; - my $bam_path = $ccs."\\Services\\bam\\UserSettings"; + my $bam_path = $ccs."\\Services\\bam\\State\\UserSettings"; + my $bam_path2 = $ccs."\\Services\\bam\\UserSettings"; my $bam; if ($bam = $root_key->get_subkey($bam_path)) { my @sk = $bam->get_list_of_subkeys(); @@ -67,6 +69,18 @@ sub pluginmain { else { # ::rptMsg($bam_path." not found."); } + if ($bam = $root_key->get_subkey($bam_path2)) { + my @sk = $bam->get_list_of_subkeys(); + if (scalar(@sk) > 0) { + foreach my $s (@sk) { + processKey($s); + } + } + + } + else { +# ::rptMsg($bam_path2." not found."); + } } else { # ::rptMsg($key_path." not found."); diff --git a/thirdparty/rr-full/plugins/banner.pl b/thirdparty/rr-full/plugins/banner.pl deleted file mode 100644 index 7ccab617c4e..00000000000 --- a/thirdparty/rr-full/plugins/banner.pl +++ /dev/null @@ -1,128 +0,0 @@ -#----------------------------------------------------------- -# banner -# Get banner information from the SOFTWARE hive file (if any) -# -# Written By: -# Special Agent Brook William Minnick -# Brook_Minnick@doioig.gov -# U.S. Department of the Interior - Office of Inspector General -# Computer Crimes Unit -# 12030 Sunrise Valley Drive Suite 250 -# Reston, VA 20191 -#----------------------------------------------------------- -package banner; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20081119); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get HKLM\\SOFTWARE.. Logon Banner Values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching banner v.".$VERSION); - ::rptMsg("banner v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Windows\\CurrentVersion\\policies\\system"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Logon Banner Information"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - -# GET LEGALNOTICECAPTION -- - - my $caption; - eval { - $caption = $key->get_value("Legalnoticecaption")->get_data(); - }; - if ($@) { - ::rptMsg("Legalnoticecaption value not found."); - } - else { - ::rptMsg("Legalnoticecaption value = ".$caption); - } - ::rptMsg(""); - -# GET LEGALNOTICETEXT -- - - my $banner; - eval { - $banner = $key->get_value("Legalnoticetext")->get_data(); - }; - if ($@) { - ::rptMsg("Legalnoticetext value not found."); - } - else { - ::rptMsg("Legalnoticetext value = ".$banner); - } - ::rptMsg(""); - - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - -# GET LEGALNOTICECAPTION -- - - my $caption2; - eval { - $caption2 = $key->get_value("Legalnoticecaption")->get_data(); - }; - if ($@) { - ::rptMsg("Legalnoticecaption value not found."); - } - else { - ::rptMsg("Legalnoticecaption value = ".$caption2); - } - ::rptMsg(""); - -# GET LEGALNOTICETEXT -- - - my $banner2; - eval { - $banner2 = $key->get_value("Legalnoticetext")->get_data(); - }; - if ($@) { - ::rptMsg("Legalnoticetext value not found."); - } - else { - ::rptMsg("Legalnoticetext value = ".$banner2); - } - ::rptMsg(""); - - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - -} - -1; diff --git a/thirdparty/rr-full/plugins/base.pl b/thirdparty/rr-full/plugins/base.pl new file mode 100644 index 00000000000..de7fe48fda5 --- /dev/null +++ b/thirdparty/rr-full/plugins/base.pl @@ -0,0 +1,88 @@ +#! c:\perl\bin\perl.exe +#----------------------------------------------------------- +# base.pl +# +# Change history +# 20200904 - MITRE updates +# 20200427 - updated output date format +# 20200219 - created +# +# References: +# https://metacpan.org/pod/Parse::Win32Registry +# https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md +# +# +# copyright 2019-2020 QAR, LLC +# Author: H. Carvey +#----------------------------------------------------------- +package base; +use strict; + +my %config = (hive => "all", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + category => "base", + output => "report", + version => 20200904); + +sub getConfig{return %config} +sub getShortDescr { + return "Parse base info from hive"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $file = shift; + my $reg = Parse::Win32Registry->new($file); + ::logMsg("Launching base v.".$VERSION); + ::rptMsg("base v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + + my $reg = Parse::Win32Registry->new($file); + + my $lastwritten; + my $ts = $reg->get_timestamp(); + if ($ts == 0) { + $lastwritten = 0; + } + else { + $lastwritten = ::format8601Date($ts)."Z"; + } + + my $reorg; + my $ro = $reg->get_reorg_timestamp(); + if ($ro == 0) { + $reorg = 0; + } + else { + $reorg = ::format8601Date($ro); + } + + my $dirty; + if ($reg->is_dirty() == 1) { + $dirty = "True"; + } + elsif ($reg->is_dirty() == 0) { + $dirty = "False"; + } + else { + $dirty = "Unknown"; + } + + ::rptMsg("Last Written Timestamp: ".$lastwritten); + ::rptMsg("ReOrg Timestamp : ".$reorg); + ::rptMsg("Version : ".$reg->get_version()); + ::rptMsg("Type : ".$reg->get_type()); + ::rptMsg("File name : ".$reg->get_embedded_filename()); + ::rptMsg("isDirty : ".$dirty); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/baseline.pl b/thirdparty/rr-full/plugins/baseline.pl index 11ba36c02ae..c8428f0981a 100644 --- a/thirdparty/rr-full/plugins/baseline.pl +++ b/thirdparty/rr-full/plugins/baseline.pl @@ -3,24 +3,27 @@ # baseline.pl # # History +# 20200904 - MITRE updates # 20130211 - Created # -# copyright 2013 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package baseline; use strict; -my %config = (hive => "All", +my %config = (hive => "all", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20130211); + MITRE => "", + output => "report", + category => "config", + version => 20200904); sub getConfig{return %config} sub getShortDescr { - return "Scans a hive file, checking sizes of binary value data"; + return "Baseline scan of hive file, checking sizes of binary value data"; } sub getDescr{} sub getRefs {} diff --git a/thirdparty/rr-full/plugins/bcd.pl b/thirdparty/rr-full/plugins/bcd.pl new file mode 100644 index 00000000000..07c75702d29 --- /dev/null +++ b/thirdparty/rr-full/plugins/bcd.pl @@ -0,0 +1,89 @@ +#----------------------------------------------------------- +# bcd.pl +# +# Change history +# 20220531 - created +# +# References +# https://blog.nviso.eu/2022/05/30/detecting-bcd-changes-to-inhibit-system-recovery/ +# +# Copyright (c) 2022 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package bcd; +use strict; + +my %config = (hive => "bcd", + hasShortDescr => 1, + hasDescr => 1, + hasRefs => 1, + MITRE => "", + category => "", + output => "report", + version => 20220531); +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Parse BCD hive for boot config settings"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching bcd v.".$VERSION); + ::rptMsg("bcd v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = 'Objects'; + + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); + my $k = ""; + if ($k = $s->get_subkey("Elements")) { + + my @subkeys2 = $k->get_list_of_subkeys(); + if (scalar @subkeys2 > 0) { + foreach my $t (@subkeys2) { + ::rptMsg(" ".$t->get_name()); + if ($t->get_name() eq "16000009") { + ::rptMsg("Key 16000009 found."); + + } + elsif ($t->get_name eq "250000e0") { + ::rptMsg("Key 250000e0 found."); + + } + else {} + + } + } + } + else { + ::rptMsg("Elements subkey not found."); + } + } + } + } + else { + ::rptMsg($key_path." not found."); + + } + +} + + + + +1; diff --git a/thirdparty/rr-full/plugins/bho.pl b/thirdparty/rr-full/plugins/bho.pl deleted file mode 100644 index b2742260180..00000000000 --- a/thirdparty/rr-full/plugins/bho.pl +++ /dev/null @@ -1,117 +0,0 @@ -#----------------------------------------------------------- -# bho -# -# -# Change history: -# 20130408 - updated to include Wow6432Node; formating updates -# 20080418 - created -# -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package bho; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20130408); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets Browser Helper Objects from Software hive"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Browser Helper Objects" => - "http://msdn2.microsoft.com/en-us/library/bb250436.aspx"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching bho v.".$VERSION); - ::rptMsg("bho v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my @paths = ("Microsoft\\Windows\\CurrentVersion\\Explorer\\Browser Helper Objects", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Browser Helper Objects"); - - foreach my $key_path (@paths) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @subkeys = $key->get_list_of_subkeys(); - if (scalar (@subkeys) > 0) { - foreach my $s (@subkeys) { - my $name = $s->get_name(); - next if ($name =~ m/^-/); - my $clsid_path = "Classes\\CLSID\\".$name; - my $clsid; - my %bhos; - if ($clsid = $root_key->get_subkey($clsid_path)) { - my $class; - my $mod; - my $lastwrite; - - eval { - $class = $clsid->get_value("")->get_data(); - $bhos{$name}{class} = $class; - }; - if ($@) { - ::logMsg("Error getting Class name for CLSID\\".$name); - ::logMsg("\t".$@); - } - eval { - $mod = $clsid->get_subkey("InProcServer32")->get_value("")->get_data(); - $bhos{$name}{module} = $mod; - }; - if ($@) { - ::logMsg("\tError getting Module name for CLSID\\".$name); - ::logMsg("\t".$@); - } - eval{ - $lastwrite = $clsid->get_subkey("InProcServer32")->get_timestamp(); - $bhos{$name}{lastwrite} = $lastwrite; - }; - if ($@) { - ::logMsg("\tError getting LastWrite time for CLSID\\".$name); - ::logMsg("\t".$@); - } - - foreach my $b (keys %bhos) { - ::rptMsg($b); - ::rptMsg(" Class => ".$bhos{$b}{class}); - ::rptMsg(" Module => ".$bhos{$b}{module}); - ::rptMsg(" LastWrite => ".gmtime($bhos{$b}{lastwrite})); - ::rptMsg(""); - } - } - else { - ::rptMsg($clsid_path." not found."); - ::rptMsg(""); - } - } - } - else { - ::rptMsg($key_path." has no subkeys. No BHOs installed."); - } - } - else { - ::rptMsg($key_path." not found."); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/bitbucket.pl b/thirdparty/rr-full/plugins/bitbucket.pl index f0efa259513..0e57bbe2196 100644 --- a/thirdparty/rr-full/plugins/bitbucket.pl +++ b/thirdparty/rr-full/plugins/bitbucket.pl @@ -1,28 +1,30 @@ #----------------------------------------------------------- -# bitbucket -# Get HKLM\..\BitBucket keys\values (if any) -# +# bitbucket.pl +# # Change history -# 20091020 - Updated; collected additional values +# 20221129 - created # # References -# -# copyright 2009 H. Carvey, keydet89@yahoo.com +# +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package bitbucket; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "NTUSER\.DAT", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20080418); + output => "report", + category => "defense evasion", + MITRE => "T1562\.001", + version => 20221129); sub getConfig{return %config} - sub getShortDescr { - return "Get HKLM\\..\\BitBucket keys\\values"; + return "Gets user's BitBucket settings"; } sub getDescr{} sub getRefs {} @@ -33,50 +35,47 @@ sub getShortDescr { sub pluginmain { my $class = shift; - my $hive = shift; + my $ntuser = shift; ::logMsg("Launching bitbucket v.".$VERSION); - ::rptMsg("bitbucket v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); + ::rptMsg("bitbucket v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket"; + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume'; my $key; if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - eval { - my $global = $key->get_value("UseGlobalSettings")->get_data(); - ::rptMsg("UseGlobalSettings = ".$global); - }; - - eval { - my $nuke = $key->get_value("NukeOnDelete")->get_data(); - ::rptMsg("NukeOnDelete = ".$nuke); - }; - ::rptMsg(""); - my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { - ::rptMsg($key_path."\\".$s->get_name()); - ::rptMsg("LastWrite Time = ".gmtime($s->get_timestamp())." (UTC)"); + ::rptMsg("Volume GUID: ".$s->get_name()); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + + eval { + my $c = $s->get_value("MaxCapacity")->get_data(); + ::rptMsg(sprintf "%-15s %-8s MB","MaxCapacity",$c); + }; + eval { - my $vol = $s->get_value("VolumeSerialNumber")->get_data(); - ::rptMsg("VolumeSerialNumber = 0x".uc(sprintf "%1x",$vol)); + my $n = $s->get_value("NukeOnDelete")->get_data(); + ::rptMsg(sprintf "%-15s 0x%04x","NukeOnDelete",$n); }; ::rptMsg(""); } } else { - ::rptMsg($key_path." has no subkeys."); + ::rptMsg($key_path." has no values."); } + ::rptMsg("Analysis Tip: Volume GUIDs can be mapped to MountedDevices key to determine drive letter(s)."); + ::rptMsg("MaxCapacity is max capacity of the Recycle Bin for the volume, in MB."); + ::rptMsg("NukeOnDelete corresponds to \"Don't move files to the Recycle Bin\. Remove files immediately when deleted.\""); + ::rptMsg(" 0 - disabled"); + ::rptMsg(" 1 - enabled"); } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } diff --git a/thirdparty/rr-full/plugins/bitbucket_user.pl b/thirdparty/rr-full/plugins/bitbucket_user.pl deleted file mode 100644 index 92c799541c2..00000000000 --- a/thirdparty/rr-full/plugins/bitbucket_user.pl +++ /dev/null @@ -1,73 +0,0 @@ -#----------------------------------------------------------- -# bitbucket_user -# Get HKLM\..\BitBucket keys\values (if any) -# -# Change history -# -# References -# -# NOTE: In limited testing, the volume letter subkeys beneath the -# BitBucket key appear to be volatile. -# -# copyright 2009 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package bitbucket_user; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20091020); - -sub getConfig{return %config} - -sub getShortDescr { - return "TEST - Get user BitBucket values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching bitbucket_user v.".$VERSION); - ::rptMsg("bitbucket_user v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - ::rptMsg($key_path."\\".$s->get_name()); - ::rptMsg("LastWrite Time = ".gmtime($s->get_timestamp())." (UTC)"); - eval { - my $purge = $s->get_value("NeedToPurge")->get_data(); - ::rptMsg(" NeedToPurge = ".$purge); - }; - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/blm.pl b/thirdparty/rr-full/plugins/blm.pl new file mode 100644 index 00000000000..c8b62b7326d --- /dev/null +++ b/thirdparty/rr-full/plugins/blm.pl @@ -0,0 +1,90 @@ +#----------------------------------------------------------- +# blm.pl +# +# +# Change history: +# 20210705 - created +# +# References: +# https://twitter.com/R3MRUM/status/1412064892870434818 +# https://twitter.com/Max_Mal_/status/1411261131033923586 +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package blm; +use strict; + +my %config = (hive => "software,ntuser\.dat", + category => "config", + MITRE => "N/A", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210705); + +sub getConfig{return %config} + +sub getShortDescr { + return "Look for BlackLivesMatter key assoc. w/ REvil ransomware"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching blm v.".$VERSION); + ::rptMsg("blm v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my @paths = (); + if ($hive_guess eq "software") { + @paths = ("BlackLivesMatter","Wow6432Node\\BlackLivesMatter"); + } + elsif ($hive_guess eq "ntuser") { + @paths = ("Software\\BlackLivesMatter","Software\\Wow6432Node\\BlackLivesMatter"); + } + else {} + + my $key; + foreach my $key_path (@paths) { + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + my @vals = get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-15s %-25s",$v->get_name(),$v->get_data()); + } + } + } + else { + ::rptMsg($key_path." key not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Per \@REMRUM, REvil v2.04 & v2.07 (Kaseya) stored values beneath this key."); + ::rptMsg("Ref: https://twitter.com/R3MRUM/status/1412064892870434818"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/brisv.pl b/thirdparty/rr-full/plugins/brisv.pl deleted file mode 100644 index 8039b0cd21c..00000000000 --- a/thirdparty/rr-full/plugins/brisv.pl +++ /dev/null @@ -1,70 +0,0 @@ -#----------------------------------------------------------- -# brisv.pl -# Plugin to detect the presence of Trojan.Brisv.A -# Symantec write-up: http://www.symantec.com/security_response/writeup.jsp -# ?docid=2008-071823-1655-99 -# -# Change History: -# 20130429: added alertMsg() functionality -# 20090210: Created -# -# Info on URLAndExitCommandsEnabled value: -# http://support.microsoft.com/kb/828026 -# http://www.hispasec.com/laboratorio/GetCodecAnalysis.pdf -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package brisv; -use strict; - -my %config = (hive => "NTUSER\.DAT", - category => "malware", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20130429); - -sub getConfig{return %config} - -sub getShortDescr { - return "Detect artifacts of a Troj.Brisv.A infection"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching brisv v.".$VERSION); - ::rptMsg("brisv v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\PIMSRV"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $mp_path = "Software\\Microsoft\\MediaPlayer\\Preferences"; - my $url; - eval { - $url = $key->get_subkey($mp_path)->get_value("URLAndExitCommandsEnabled")->get_data(); - ::rptMsg($mp_path."\\URLAndExitCommandsEnabled value set to ".$url); - ::alertMsg($mp_path."\\URLAndExitCommandsEnabled value set: ".$url); - }; -# if an error occurs within the eval{} statement, do nothing - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/btconfig.pl b/thirdparty/rr-full/plugins/btconfig.pl index d9b4737e09c..9f3d4e18e2d 100644 --- a/thirdparty/rr-full/plugins/btconfig.pl +++ b/thirdparty/rr-full/plugins/btconfig.pl @@ -3,9 +3,11 @@ # # # History: +# 20200911 - MITRE updates +# 20200526 - updated date output format # 20130117 - created # -# copyright 2013 Quantum Research Analytics, LLC +# copyright 2020 Quantum Research Analytics, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package btconfig; @@ -15,8 +17,10 @@ package btconfig; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20130117); + output => "report", + category => "devices", + MITRE => "", + version => 20200911); sub getConfig{return %config} sub getShortDescr { @@ -34,7 +38,7 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching btconfig v.".$VERSION); ::rptMsg("Launching btconfig v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -42,7 +46,6 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); ::rptMsg(""); my @sk = $key->get_list_of_subkeys(); foreach my $s (@sk) { @@ -50,7 +53,7 @@ sub pluginmain { my $lw = $s->get_timestamp(); ::rptMsg("Unique ID: ".$name); - ::rptMsg(" LastWrite: ".gmtime($lw)." Z"); + ::rptMsg(" LastWrite: ".::format8601Date($lw)."Z"); my $devname; eval { diff --git a/thirdparty/rr-full/plugins/bthenum.pl b/thirdparty/rr-full/plugins/bthenum.pl new file mode 100644 index 00000000000..40b036da177 --- /dev/null +++ b/thirdparty/rr-full/plugins/bthenum.pl @@ -0,0 +1,137 @@ +#----------------------------------------------------------- +# bthenum +# Gets contents of Enum\WpdBusEnumRoot keys +# +# +# History: +# 20200904 - MITRE updates +# 20200515 - updated date output format +# 20191003 - created +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package bthenum; +use strict; + +my %config = (hive => "System", + MITRE => "", + category => "devices", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20200904); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get BTHENUM subkey info"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $reg; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching bthenum v.".$VERSION); + ::rptMsg("bthenum v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + +# Code for System file, getting CurrentControlSet + my $current; + my $ccs; + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + $current = $key->get_value("Current")->get_data(); + $ccs = "ControlSet00".$current; + } + else { + ::rptMsg($key_path." not found."); + return; + } + + my $key_path = $ccs."\\Enum\\BTHENUM"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $k (@subkeys) { + my $dev_class = $k->get_name(); + next unless ($dev_class =~ m/^Dev/); + ::rptMsg($dev_class); + + my @subkeys2 = $k->get_list_of_subkeys(); + if (scalar(@subkeys2) > 0) { + foreach my $k2 (@subkeys2) { + ::rptMsg($k2->get_name()); + eval { + ::rptMsg(" Properties Key LastWrite: ".::format8601Date($k2->get_subkey("Properties")->get_timestamp())." UTC"); + }; + + eval { + my $t = $k2->get_subkey("Properties\\{a35996ab-11cf-4935-8b61-a6761081ecdf}\\000C")->get_value("")->get_data(); + $t =~ s/\00//g; + ::rptMsg(" Device Address : ".$t); + }; + + eval { + my $t = $k2->get_subkey("Properties\\{2bd67d8b-8beb-48d5-87e0-6cda3428040a}\\0001")->get_value("")->get_data(); + $t =~ s/\00//g; + ::rptMsg(" Device Address : ".$t); + }; +# https://docs.microsoft.com/en-us/windows/win32/properties/props-system-deviceinterface-bluetooth-lastconnectedtime + eval { + my $t = $k2->get_subkey("Properties\\{2bd67d8b-8beb-48d5-87e0-6cda3428040a}\\000B")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$t); + ::rptMsg(" LastConnectedTime : ".::format8601Date(::getTime($t0,$t1))."Z"); + }; + +# + eval { + my $t = $k2->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$t); + ::rptMsg(" First InstallDate : ".::format8601Date(::getTime($t0,$t1))."Z"); + }; + + eval { + my $t = $k2->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$t); + ::rptMsg(" InstallDate : ".::format8601Date(::getTime($t0,$t1))."Z"); + }; + + eval { + my $t = $k2->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$t); + ::rptMsg(" Last Arrival : ".::format8601Date(::getTime($t0,$t1))."Z"); + }; + + eval { + my $t = $k2->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$t); + ::rptMsg(" Last Removal : ".::format8601Date(::getTime($t0,$t1))."Z"); + }; + + ::rptMsg(""); + } + } + } + } + else { + ::rptMsg($key_path." has no subkeys."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/bthport.pl b/thirdparty/rr-full/plugins/bthport.pl index 0aa76bec01d..f1936f92fb1 100644 --- a/thirdparty/rr-full/plugins/bthport.pl +++ b/thirdparty/rr-full/plugins/bthport.pl @@ -5,13 +5,14 @@ # other locations) # # Change history +# 20200904 - MITRE updates +# 20200517 - updated date output format # 20180705 - updated to support Win10, per data provided by Micah Jones # 20170129 - added support for http://www.hexacorn.com/blog/2017/01/29/beyond-good-ol-run-key-part-59/ # 20130115 - created # -# Category: # -# copyright 2018 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package bthport; @@ -21,8 +22,10 @@ package bthport; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180705); + output => "report", + MITRE => "", + category => "devices", + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -39,8 +42,8 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching bthport v.".$VERSION); - ::rptMsg("bthport v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("bthport v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -56,7 +59,7 @@ sub pluginmain { my $cn; if ($cn = $root_key->get_subkey($cn_path)) { ::rptMsg($cn_path); - ::rptMsg("LastWrite: ".gmtime($cn->get_timestamp())." UTC"); + ::rptMsg("LastWrite: ".::format8601Date($cn->get_timestamp())."Z"); my @sk = $cn->get_list_of_subkeys(); if (scalar(@sk) > 0) { @@ -74,12 +77,12 @@ sub pluginmain { eval { my ($t0,$t1) = unpack("VV",$s->get_value("LastSeen")->get_data()); - ::rptMsg("LastSeen : ".gmtime(::getTime($t0,$t1))." Z"); + ::rptMsg("LastSeen : ".::format8601Date(::getTime($t0,$t1))."Z"); }; eval { my ($t0,$t1) = unpack("VV",$s->get_value("LastConnected")->get_data()); - ::rptMsg("LastConnected : ".gmtime(::getTime($t0,$t1))." Z"); + ::rptMsg("LastConnected : ".::format8601Date(::getTime($t0,$t1))."Z"); }; ::rptMsg(""); @@ -97,7 +100,7 @@ sub pluginmain { my $rs; if ($rs = $root_key->get_subkey($rs_path)) { ::rptMsg($rs_path); - ::rptMsg("LastWrite: ".gmtime($rs->get_timestamp())." UTC"); + ::rptMsg("LastWrite: ".::format8601Date($rs->get_timestamp())."Z"); eval { my $spt = $rs->get_value("SupportDLL")->get_data(); diff --git a/thirdparty/rr-full/plugins/bthport_tln.pl b/thirdparty/rr-full/plugins/bthport_tln.pl index e48afd15fca..357b24ce183 100644 --- a/thirdparty/rr-full/plugins/bthport_tln.pl +++ b/thirdparty/rr-full/plugins/bthport_tln.pl @@ -5,13 +5,14 @@ # other locations) # # Change history +# 20200904 - MITRE updates # 20180705 - updated to support Win10, per data provided by Micah Jones # 20170129 - added support for http://www.hexacorn.com/blog/2017/01/29/beyond-good-ol-run-key-part-59/ # 20130115 - created # # Category: # -# copyright 2018 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package bthport_tln; @@ -21,8 +22,10 @@ package bthport_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180705); + output => "tln", + category => "devices", + MITRE => "", + version => 20200904); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/cached.pl b/thirdparty/rr-full/plugins/cached.pl index 1f3a1838b8e..4493ea8303a 100644 --- a/thirdparty/rr-full/plugins/cached.pl +++ b/thirdparty/rr-full/plugins/cached.pl @@ -4,6 +4,8 @@ # NTUSER.DAT hive # # History: +# 20201012 - MITRE updates +# 20200525 - updated date output format # 20150608 - created # # References: @@ -11,22 +13,24 @@ # http://www.nobunkum.ru/analytics/en-com-hijacking # # -# copyright 2015 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package cached; use strict; -my %config = (hive => "NTUSER.DAT", +my %config = (hive => "NTUSER\.DAT", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20150608); + output => "report", + MITRE => "T1218\.002", + category => "persistence", + version => 20201012); sub getConfig{return %config} sub getShortDescr { - return "Gets cached Shell Extensions from NTUSER.DAT hive"; + return "Gets cached Shell Extensions from NTUSER\.DAT hive"; } sub getDescr{} sub getRefs {} @@ -53,15 +57,17 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching cached v.".$VERSION); - ::rptMsg("cached v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("cached v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Cached";; my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); @@ -69,8 +75,8 @@ sub pluginmain { foreach my $v (@vals) { my ($clsid1, $clsid2, $mask) = split(/\s/,$v->get_name(),3); my @t = unpack("VV",substr($v->get_data(),8,8)); - my $tm = gmtime(::getTime($t[0],$t[1])); - my $str = $tm." First Load: ".$clsid1." ("; + my $tm = ::format8601Date(::getTime($t[0],$t[1])); + my $str = $tm."Z First Load: ".$clsid1." ("; if (exists $clsids{$clsid2}) { $str .= $clsids{$clsid2}.")"; } @@ -88,4 +94,4 @@ sub pluginmain { ::rptMsg($key_path." not found."); } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/cached_tln.pl b/thirdparty/rr-full/plugins/cached_tln.pl index 20f6a89f825..9921a920dab 100644 --- a/thirdparty/rr-full/plugins/cached_tln.pl +++ b/thirdparty/rr-full/plugins/cached_tln.pl @@ -4,14 +4,15 @@ # NTUSER.DAT hive # # History: +# 20201012 - MITRE updates # 20150608 - created # # References: # http://herrcore.blogspot.com.tr/2015/06/malware-persistence-with.html # http://www.nobunkum.ru/analytics/en-com-hijacking +# https://attack.mitre.org/techniques/T1218/002/ # -# -# copyright 2015 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package cached_tln; @@ -21,12 +22,14 @@ package cached_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20150608); + output => "tln", + MITRE => "T1218\.002", + category => "persistence", + version => 20201012); sub getConfig{return %config} sub getShortDescr { - return "Gets cached Shell Extensions from NTUSER.DAT hive (TLN)"; + return "Gets cached Shell Extensions from NTUSER\.DAT hive (TLN)"; } sub getDescr{} sub getRefs {} @@ -67,4 +70,4 @@ sub pluginmain { ::rptMsg($key_path." not found."); } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/cain.pl b/thirdparty/rr-full/plugins/cain.pl deleted file mode 100644 index 36bb7a4f073..00000000000 --- a/thirdparty/rr-full/plugins/cain.pl +++ /dev/null @@ -1,93 +0,0 @@ -#----------------------------------------------------------- -# cain.pl -# Extracts details for Cain & Abel by oxid.it -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# Copyright (c) 2011-02-04 Brendan Coles -#----------------------------------------------------------- -# Require # -package cain; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110204); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts details for Cain & Abel by oxid.it"; -} -sub getRefs { - my %refs = ("Cain & Abel Homepage:" => - "http://www.oxid.it/cain.html"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching cain v.".$VERSION); - ::rptMsg("cain v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\Cain\\Settings"; - - # If # Cain path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Cain"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Cain registry path # - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Cain registry path # - foreach my $v (@vals) { - ::rptMsg($v->get_name()." -> ".$v->get_data()); - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Cain isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/calibrator.pl b/thirdparty/rr-full/plugins/calibrator.pl new file mode 100644 index 00000000000..9fcada56f70 --- /dev/null +++ b/thirdparty/rr-full/plugins/calibrator.pl @@ -0,0 +1,64 @@ +#----------------------------------------------------------- +# calibrator.pl +# +# Change history +# 20200904 - MITRE updates +# 20200427 - changed output date format +# 20200416 - created +# +# Refs: +# https://twitter.com/f0wlsec/status/1203118495699013633 +# https://attack.mitre.org/techniques/T1548/002/ +# +# Copyright (c) 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package calibrator; +use strict; + +my %config = (hive => "Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1548\.002", + category => "persistence", + version => 20200904); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr {return "Checks DisplayCalibrator value (possible bypass assoc with LockBit ransomware)";} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching calibrator v.".$VERSION); + ::rptMsg("calibrator v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = 'Microsoft\\Windows NT\\CurrentVersion\\ICM\\Calibration'; + + if ($key = $root_key->get_subkey($key_path)) { + if (my $dc = $key->get_value("DisplayCalibrator")) { + if (my $dc2 = $dc->get_data()) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg("DisplayCalibrator value: ".$dc2); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Most often, the DisplayCalibrator value points to system32\\DCCW\.EXE. If the "); + ::rptMsg("current value points to something else, an investigation may be in order."); + } + } + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ccleaner.pl b/thirdparty/rr-full/plugins/ccleaner.pl deleted file mode 100644 index e77b8797597..00000000000 --- a/thirdparty/rr-full/plugins/ccleaner.pl +++ /dev/null @@ -1,79 +0,0 @@ -#----------------------------------------------------------- -# ccleaner.pl -# Gets CCleaner User Settings -# -# Change history -# 20120128 [ale] % Initial Version based on warcraft3.pl plugin -# -# References -# -# Author: Adrian Leong -#----------------------------------------------------------- -package ccleaner; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20120128); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets User's CCleaner Settings"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; # pops the first element off @_ ie the parameter array passed in to pluginmain - my $hive = shift; # 1st element in @_ is class/package name (ccleaner), 2nd is the hive name passed in from rip.pl - ::logMsg("Launching ccleaner v.".$VERSION); - ::rptMsg("ccleaner v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); # creates a Win32Registry object - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\Piriform\\CCleaner"; - # If CCleaner key_path exists ... ie get_subkey returns a non-empty value - if ($key = $root_key->get_subkey($key_path)) { - # Print registry key name and last modified date - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my %cckeys; # temporary associative array for storing name / value pairs eg ("UpdateCheck", 1) - # Extract ccleaner key values into ccvals array - # Note: ccvals becomes an array of "Parse::Win32Registry::WinNT::Value" - # As this is implemented in an Object oriented manner, we cannot access the values directly - - # we have to use the "get_name" and "get_value" subroutines - my @ccvals = $key->get_list_of_values(); - # If ccvals has any "Values" in it, call "Value::get_name" and "Value::get_data" for each - # and store the results in the %cckeys associative array using data returned by Value::get_name as the id/index - # and Value::get_data for the actual key value - if (scalar(@ccvals) > 0) { - foreach my $val (@ccvals) { - $cckeys{$val->get_name()} = $val->get_data(); - } - # Sorts keynames into a temp list and then prints each key name + value in list order - # the values are retrieved from cckeys assoc. array which was populated in the previous foreach loop - foreach my $keyval (sort keys %cckeys) { - ::rptMsg($keyval." -> ".$cckeys{$keyval}); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." does not exist."); - } - # Return obligatory new-line - ::rptMsg(""); -} - -1; diff --git a/thirdparty/rr-full/plugins/cdstaginginfo.pl b/thirdparty/rr-full/plugins/cdstaginginfo.pl deleted file mode 100644 index 972392a0372..00000000000 --- a/thirdparty/rr-full/plugins/cdstaginginfo.pl +++ /dev/null @@ -1,83 +0,0 @@ -#----------------------------------------------------------- -# cdstaginginfo.pl -# Plugin for Registry Ripper -# -# Change history -# 20131118 - created -# -# References -# http://secureartisan.wordpress.com/2012/06/04/windows-7-cddvd-burning/ -# -# copyright 2013 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package cdstaginginfo; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - category => "useractivity", - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20131118); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's CD Burning\\StagingInfo key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my ($name, $lw, $drvnum, $stage); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching cdstaginginfo v.".$VERSION); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - ::rptMsg("cdstaginginfo v.".$VERSION); - ::rptMsg(""); -# LastVistedMRU - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CD Burning\\StagingInfo"; - my $key; - my @vals; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @subkeys = $key->get_list_of_subkeys(); - - if (scalar @subkeys > 0) { - foreach my $s (@subkeys) { - $name = $s->get_name(); - $lw = $s->get_timestamp(); - ::rptMsg($name); - ::rptMsg("LastWrite: ".gmtime($lw)." Z"); - eval { - $stage = $s->get_value("StagingPath")->get_data(); - ::rptMsg(" StagingPath: ".$stage); - }; - - eval { - $drvnum = $s->get_value("DriveNumber")->get_data(); - ::rptMsg(" DriveNumber: ".$drvnum); - }; - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/certpadding.pl b/thirdparty/rr-full/plugins/certpadding.pl new file mode 100644 index 00000000000..e160687fadf --- /dev/null +++ b/thirdparty/rr-full/plugins/certpadding.pl @@ -0,0 +1,83 @@ +#----------------------------------------------------------- +# certpadding.pl +# Check EnableCertPaddingCheck value +# +# Change history: +# 20220110 - created +# +# References: +# https://research.checkpoint.com/2022/can-you-trust-a-files-digital-signature-new-zloader-campaign-exploits-microsofts-signature-verification-putting-users-at-risk/ +# https://docs.microsoft.com/en-us/security-updates/SecurityAdvisories/2014/2915720?redirectedfrom=MSDN +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, 2022 +#----------------------------------------------------------- +package certpadding; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + MITRE => "T1562", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220110); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check EnableCertPaddingCheck value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching certpadding v.".$VERSION); + ::rptMsg("certpadding v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my @paths = ("Microsoft\\Cryptography\\WintrustConfig", + "Wow6432Node\\Microsoft\\Cryptography\\WintrustConfig"); + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path : ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $cert = $key->get_value("EnableCertPaddingCheck")->get_data(); + ::rptMsg("EnableCertPaddingCheck value: ".$cert); + ::rptMsg("0 - disabled (default)"); + ::rptMsg("1 - enabled"); + }; + ::rptMsg("EnableCertPaddingCheck value not found\. Functionality not enabled\.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: MS13-098 provided checks for certificate padding, but that functionality was shifted to an opt-in"); + ::rptMsg("approach based on the impact to business functionality\. The Checkpoint ZLoader article from 5 Jan 2022 illustrates"); + ::rptMsg("that this functionality can be exploited if the capability is not fully enabled, via the Registry value."); + ::rptMsg(""); + ::rptMsg("Ref: https://research.checkpoint.com/2022/can-you-trust-a-files-digital-signature-new-zloader-campaign-exploits-microsofts-signature-verification-putting-users-at-risk/"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/certs.pl b/thirdparty/rr-full/plugins/certs.pl new file mode 100644 index 00000000000..8a01db5aaf5 --- /dev/null +++ b/thirdparty/rr-full/plugins/certs.pl @@ -0,0 +1,99 @@ +#----------------------------------------------------------- +# certs.pl +# +# +# Change history +# 20220926 - created +# +# References +# https://attack.mitre.org/techniques/T1553/ +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package certs; +use strict; + +my %config = (hive => "software, ntuser\.dat", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1553\.005", + category => "defense evasion", + version => 20220926); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks for MOTW bypasses via certificates"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching certs v.".$VERSION); + ::rptMsg("certs v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my @paths = (); + if ($hive_guess eq "software") { + @paths = ('Microsoft\\SystemCertificates\\Root\\Certificates', + 'Policies\\SystemCertificates\\Root\\Certificates', + 'Microsoft\\EnterpriseCertificates\\Root\\Certificates'); + } + elsif ($hive_guess eq "ntuser") { + @paths = ('Software\\Microsoft\\SystemCertificates\\Root\\Certificates', + 'Software\\Policies\\Microsoft\\SystemCertificates\\Root\\Certificates'); + } + else {} + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + my $name = $v->get_name(); + my $data = $v->get_data(); + ::rptMsg($name." - ".$data); + } + } + else { + ::rptMsg($key_path." has no values."); + } + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + } +# ::rptMsg(""); + ::rptMsg("Analysis Tip: Trust relationships can be subverted by modifying/adding certificates. MS has a subset of root"); + ::rptMsg("certificates that are consistent across systems. Check the reference for a list of those certificates, and "); + ::rptMsg("monitor systems for changes (per the reference)."); + ::rptMsg(""); + ::rptMsg("Ref: https://attack.mitre.org/techniques/T1553/"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/clampi.pl b/thirdparty/rr-full/plugins/clampi.pl deleted file mode 100644 index 464784b2139..00000000000 --- a/thirdparty/rr-full/plugins/clampi.pl +++ /dev/null @@ -1,120 +0,0 @@ -#----------------------------------------------------------- -# clampi.pl -# Checks keys/values set by new version of Trojan.Clampi -# -# Change history -# 20091019 - created -# -# NOTE: This is purely a test plugin, and based solely on the below -# reference. It has not been tested on any systems that were -# known to be infected. -# -# References -# http://www.symantec.com/connect/blogs/inside-trojanclampi-stealing-your-information -# -# copyright 2009 H. Carvey -#----------------------------------------------------------- -package clampi; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20091019); - -sub getConfig{return %config} -sub getShortDescr { - return "TEST - Checks for keys set by Trojan.Clampi PROT module"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching clampi v.".$VERSION); - ::rptMsg("clampi v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $count = 0; - - my $key_path = 'Software\\Microsoft\\Internet Explorer\\Main'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my ($form1, $form2, $form3); - - eval { - $form1 = $key->get_value("Use FormSuggest")->get_data(); - ::rptMsg("\tUse FormSuggest = ".$form1); - $count++ if ($form1 eq "true"); - }; - - eval { - $form2 = $key->get_value("FormSuggest_Passwords")->get_data(); - ::rptMsg("\tFormSuggest_Passwords = ".$form2); - $count++ if ($form2 eq "true"); - }; - - eval { - $form3 = $key->get_value("FormSuggest_PW_Ask")->get_data(); - ::rptMsg("\tUse FormSuggest = ".$form3); - $count++ if ($form3 eq "no"); - }; - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); - $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoComplete"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $auto; - eval { - $auto = $key->get_value("AutoSuggest")->get_data(); - ::rptMsg("\tAutoSuggest = ".$auto); - $count++ if ($auto eq "true"); - }; - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); - $key_path = "Software\\Microsoft\\Internet Account Manager\\Accounts"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $prompt; - eval { - $prompt = $key->get_value("POP3 Prompt for Password")->get_data(); - ::rptMsg("\tPOP3 Prompt for Password = ".$prompt); - $count++ if ($prompt eq "true"); - }; - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); - if ($count == 5) { - ::rptMsg("The system may have been infected with the Trojan.Clampi PROT module."); - } - else { - ::rptMsg("The system does not appear to have been infected with the Trojan.Clampi"); - ::rptMsg("PROT module."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/clampitm.pl b/thirdparty/rr-full/plugins/clampitm.pl deleted file mode 100644 index 5ff75b8070b..00000000000 --- a/thirdparty/rr-full/plugins/clampitm.pl +++ /dev/null @@ -1,80 +0,0 @@ -#----------------------------------------------------------- -# clampitm.pl -# Checks keys/values set by new version of Trojan.Clampi -# -# Change history -# 20100624 - created -# -# NOTE: This is purely a test plugin, and based solely on the below -# reference. It has not been tested on any systems that were -# known to be infected. -# -# References -# http://us.trendmicro.com/imperia/md/content/us/trendwatch/researchandanalysis/ilomo_external.pdf -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package clampitm; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100624); - -sub getConfig{return %config} -sub getShortDescr { - return "Checks for IOCs for Clampi (per Trend Micro)"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching clampitm v.".$VERSION); - ::rptMsg("clampitm v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $count = 0; - - my $key_path = 'Software\\Microsoft\\Internet Explorer\\Settings'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("ClampiTM plugin"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $tag = 1; - my @list = qw/GatesList GID KeyE KeyM PID/; - my @vals = $key->get_list_of_values(); - if (scalar (@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - if (grep(/$name/,@list)) { - ::rptMsg(sprintf "%-10s %-30s",$name,$v->get_data()); - $tag = 0; - } - } - if ($tag) { - ::rptMsg("No Clampi values found."); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/clipbrd.pl b/thirdparty/rr-full/plugins/clipbrd.pl new file mode 100644 index 00000000000..20f4e8a4154 --- /dev/null +++ b/thirdparty/rr-full/plugins/clipbrd.pl @@ -0,0 +1,117 @@ +#----------------------------------------------------------- +# clipbrd.pl +# Threat actors, particularly those interested in cryptocurrency wallets, have been observed +# targeting the clipboard on user's systems. In some instances, they will retrieve data from +# the clipboard; in others, they will replace wallet IDs with their own, hoping that the user +# will paste the wallet address into an app, unknowingly sending the cryptocurrency to the +# attacker's wallet. +# +# Change history: +# 20230419 - added Inversecos' reference +# 20221018 - Updated to check for AllowClipboardHistory value +# 20210801 - created +# +# References: +# https://twitter.com/R3MRUM/status/1412064892870434818 +# https://twitter.com/Max_Mal_/status/1411261131033923586 +# https://www.inversecos.com/2022/05/how-to-perform-clipboard-forensics.html +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package clipbrd; +use strict; + +my %config = (hive => "software,ntuser\.dat", + category => "collection", + MITRE => "T1115", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20230419); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check clipboard settings (possible exfil)"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching clipbrd v.".$VERSION); + ::rptMsg("clipbrd v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + my $key; + my $key_path = (); + + if ($hive_guess eq "software") { + $key_path = "Policies\\Microsoft\\Windows\\System"; + if ($key = $root_key->get_subkey($key_path)) { + eval { + my $c = $key->get_value("AllowCrossDeviceClipboard")->get_data(); + ::rptMsg("AllowCrossDeviceClipboard value: ".$c); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the AllowCrossDeviceClipboard value is set to \"1\", clipboard contents are shared across"); + ::rptMsg("devices, and malware that extracts data from the clipboard could exfil extremely sensitive data."); + }; + ::rptMsg($key_path."\\AllowCrossDeviceClipboard value not found.") if ($@); + + eval { + my $a = $key->get_value("AllowClipboardHistory")->get_data(); + ::rptMsg("AllowClipboardHistory value: ".$a); + + }; + ::rptMsg($key_path."\\AllowClipboardHistory value not found.") if ($@); + + } + else { + ::rptMsg($key_path." not found."); + } + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Software\\Microsoft\\Clipboard"; + if ($key = $root_key->get_subkey($key_path)) { + eval { + my $c = $key->get_value("EnableClipboardHistory")->get_data(); + ::rptMsg("EnableClipboardHistory value: ".$c); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the EnableClipboardHistory value is set to \"1\", malware that extracts data from the"); + ::rptMsg("clipboard could exfil extremely sensitive data."); + ::rptMsg(""); + ::rptMsg("Further, if both values are set, there may be data within the user's ActivitiesCache\.db file that can provide"); + ::rptMsg("valuable insight/evidence."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.inversecos.com/2022/05/how-to-perform-clipboard-forensics.html"); + }; + ::rptMsg($key_path."\\EnableClipboardHistory value not found.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } + } + else {} + +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/clsid.pl b/thirdparty/rr-full/plugins/clsid.pl index 94c1a819262..88ee0966f00 100644 --- a/thirdparty/rr-full/plugins/clsid.pl +++ b/thirdparty/rr-full/plugins/clsid.pl @@ -1,9 +1,11 @@ #----------------------------------------------------------- # clsid.pl -# Plugin to extract file association data from the Software hive file +# Plugin to extract CLSID data from the Software hive file # Can take considerable time to run; recommend running it via rip.exe # # History +# 20200904 - MITRE updates +# 20200526 - updated date output format, added support for USRCLASS.DAT # 20180823 - minor code fix # 20180819 - updated to incorporate check for "TreatAs" value; code rewrite # 20180319 - fixed minor code issue @@ -12,21 +14,25 @@ # 20100227 - created # # References +# https://pentestlab.blog/2020/05/20/persistence-com-hijacking/ # http://msdn.microsoft.com/en-us/library/ms724475%28VS.85%29.aspx # https://docs.microsoft.com/en-us/windows/desktop/com/treatas -# -# #copyright 2010, Quantum Analytics Research, LLC -# copyright 2018, Quantum Analytics Research, LLC +# https://attack.mitre.org/techniques/T1546/015/ +# +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package clsid; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "Software, USRCLASS\.DAT", + MITRE => "T1546\.015", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20180823); + output => "report", + version => 20200904); sub getConfig{return %config} @@ -45,43 +51,58 @@ sub pluginmain { my $hive = shift; my %clsid; ::logMsg("Launching clsid v.".$VERSION); - ::rptMsg("clsid v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("clsid v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); +#--------------------------------------------------------------- +# First, determine the hive + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my @paths = (); + if ($hive_guess eq "software") { + @paths = ("Classes\\CLSID","Classes\\Wow6432Node\\CLSID"); + } + elsif ($hive_guess eq "usrclass") { + @paths = ("CLSID"); + } + else {} + my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; -# my $key_path = "Classes\\CLSID"; - my @paths = ("Classes\\CLSID","Classes\\Wow6432Node\\CLSID"); foreach my $key_path (@paths) { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); # ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); ::rptMsg(""); -# First step will be to get a list of all of the file extensions - my %ext; + my @sk = $key->get_list_of_subkeys(); if (scalar(@sk) > 0) { foreach my $s (@sk) { my $name = $s->get_name(); - my $n; - eval { - $n = $s->get_value("")->get_data(); - $name .= " ".$n unless ($n eq ""); - }; - - ::rptMsg($name); - ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); + ::rptMsg(sprintf "%-20s %-30s",::format8601Date($s->get_timestamp())."Z",$name); eval { my $proc = $s->get_subkey("InprocServer32")->get_value("")->get_data(); - ::rptMsg(" InprocServer32: ".$proc); + ::rptMsg(sprintf "%-20s ".$name."\\InprocServer32: ".$proc, ::format8601Date($s->get_subkey("InprocServer32")->get_timestamp())."Z"); }; - + + eval { + my $prog = $s->get_subkey("ProgID")->get_value("")->get_data(); + ::rptMsg(sprintf "%-20s ".$name."\\ProgID: ".$prog, ::format8601Date($s->get_subkey("ProgID")->get_timestamp())."Z"); + }; + eval { my $treat = $s->get_subkey("TreatAs")->get_value("")->get_data(); - ::rptMsg(" TreatAs: ".$treat); + ::rptMsg(sprintf "%-20s ".$name."\\TreatAs: ".$treat, ::format8601Date($s->get_subkey("TreatAs")->get_timestamp())."Z"); }; ::rptMsg(""); } diff --git a/thirdparty/rr-full/plugins/clsid_tln.pl b/thirdparty/rr-full/plugins/clsid_tln.pl index 3c33be25147..9fe53a952ae 100644 --- a/thirdparty/rr-full/plugins/clsid_tln.pl +++ b/thirdparty/rr-full/plugins/clsid_tln.pl @@ -1,28 +1,38 @@ #----------------------------------------------------------- # clsid_tln.pl -# Plugin to extract file association data from the Software hive file +# Plugin to extract CLSID data from the Software hive file # Can take considerable time to run; recommend running it via rip.exe # # History +# 20200904 - MITRE updates +# 20200526 - updated date output format, added support for USRCLASS.DAT # 20180823 - minor code fix -# 20180820 - created +# 20180819 - updated to incorporate check for "TreatAs" value; code rewrite +# 20180319 - fixed minor code issue +# 20180117 - updated based on input from Jean, jean.crush@hotmail.fr +# 20130603 - added alert functionality +# 20100227 - created # # References +# https://pentestlab.blog/2020/05/20/persistence-com-hijacking/ # http://msdn.microsoft.com/en-us/library/ms724475%28VS.85%29.aspx # https://docs.microsoft.com/en-us/windows/desktop/com/treatas -# -# #copyright 2010, Quantum Analytics Research, LLC -# copyright 2018, Quantum Analytics Research, LLC +# https://attack.mitre.org/techniques/T1546/015/ +# +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package clsid_tln; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "Software, USRCLASS\.DAT", + MITRE => "T1546\.015", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20180823); + output => "tln", + version => 20200904); sub getConfig{return %config} @@ -41,51 +51,65 @@ sub pluginmain { my $hive = shift; my %clsid; # ::logMsg("Launching clsid v.".$VERSION); -# ::rptMsg("clsid v.".$VERSION); # banner -# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner +# ::rptMsg("clsid v.".$VERSION); +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); +#--------------------------------------------------------------- +# First, determine the hive + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my @paths = (); + if ($hive_guess eq "software") { + @paths = ("Classes\\CLSID","Classes\\Wow6432Node\\CLSID"); + } + elsif ($hive_guess eq "usrclass") { + @paths = ("CLSID"); + } + else {} + my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; -# my $key_path = "Classes\\CLSID"; - my @paths = ("Classes\\CLSID","Classes\\Wow6432Node\\CLSID"); foreach my $key_path (@paths) { my $key; if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg($key_path); + ::rptMsg($key_path); # ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); -# ::rptMsg(""); -# First step will be to get a list of all of the file extensions - my %ext; + ::rptMsg(""); + my @sk = $key->get_list_of_subkeys(); if (scalar(@sk) > 0) { foreach my $s (@sk) { - my ($descr,$ts,$proc,$treat); - - $descr = $s->get_name(); - $ts = $s->get_timestamp(); - eval { - my $n = $s->get_value("")->get_data(); - $descr .= " ".$n unless ($n eq ""); - }; + my $name = $s->get_name(); + ::rptMsg($s->get_timestamp()."|REG|||CLSID - ".$name); + eval { my $proc = $s->get_subkey("InprocServer32")->get_value("")->get_data(); - $descr .= " InprocServer32: ".$proc; + ::rptMsg($s->get_subkey("InprocServer32")->get_timestamp()."|REG|||CLSID - ".$name."\\InprocServer32: ".$proc); }; - + + eval { + my $prog = $s->get_subkey("ProgID")->get_value("")->get_data(); + ::rptMsg($s->get_subkey("ProgID")->get_timestamp()."|REG|||CLSID - ".$name."\\ProgID: ".$prog); + }; + eval { my $treat = $s->get_subkey("TreatAs")->get_value("")->get_data(); - $descr .= " TreatAs: ".$treat; + ::rptMsg($s->get_subkey("TreatAs")->get_timestamp()."|REG|||CLID - ".$name."\\TreatAs: ".$treat); }; - ::rptMsg($ts."|CLSID|||".$descr); } } else { -# ::rptMsg($key_path." has no subkeys."); + ::rptMsg($key_path." has no subkeys."); } } else { -# ::rptMsg($key_path." not found."); + ::rptMsg($key_path." not found."); } } } diff --git a/thirdparty/rr-full/plugins/cmd_shell.pl b/thirdparty/rr-full/plugins/cmd_shell.pl index 49220cd40ff..f5776677fe2 100644 --- a/thirdparty/rr-full/plugins/cmd_shell.pl +++ b/thirdparty/rr-full/plugins/cmd_shell.pl @@ -2,6 +2,8 @@ # cmd_shell # # Change History +# 20200904 - MITRE updates +# 20200515 - udpated date output format # 20130405 - added Clients subkey # 20100830 - added "cs" shell command to the path # 20080328 - created @@ -9,19 +11,22 @@ # References # http://www.microsoft.com/security/portal/Threat/Encyclopedia/Entry.aspx? # Name=TrojanClicker%3AWin32%2FVB.GE +# https://attack.mitre.org/techniques/T1546/001/ # -# copyright 2013 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package cmd_shell; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "T1546\.001", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - version => 20130405); + output => "report", + version => 20200904); sub getConfig{return %config} @@ -43,9 +48,11 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching cmd_shell v.".$VERSION); - ::rptMsg("cmd_shell v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my @shells = ("exe","cmd","bat","cs","hta","pif"); + ::rptMsg("cmd_shell v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my @shells = ("exe","cmd","bat","cs","hta","pif","msc"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -55,25 +62,13 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); # ::rptMsg(""); my $val; eval { $val = $key->get_value("")->get_data(); ::rptMsg(" Cmd: ".$val); - - if ($sh eq "hta") { - if ($val eq "C:\\Windows\\SysWOW64\\mshta\.exe \"%1\" %*" || $val eq "C:\\WINDOWS\\system32\\mshta\.exe \"%1\" %*") { - - } - else { - ::alertMsg("ALERT: cmd_shell: ".$key_path." warning: ".$val); - } - } - else { - ::alertMsg("ALERT: cmd_shell: ".$key_path." warning: ".$val) unless ($val eq "\"%1\" %*"); - } - + ::rptMsg(""); }; ::rptMsg("Error: ".$@) if ($@); @@ -90,7 +85,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())." (UTC)"); eval { my $cmd = $key->get_value("")->get_data(); diff --git a/thirdparty/rr-full/plugins/cmd_shell_tln.pl b/thirdparty/rr-full/plugins/cmd_shell_tln.pl deleted file mode 100644 index f46ad84f7b4..00000000000 --- a/thirdparty/rr-full/plugins/cmd_shell_tln.pl +++ /dev/null @@ -1,111 +0,0 @@ -#----------------------------------------------------------- -# cmd_shell_tln -# -# Change History -# 20130425 - created -# -# References -# http://www.microsoft.com/security/portal/Threat/Encyclopedia/Entry.aspx? -# Name=TrojanClicker%3AWin32%2FVB.GE -# -# copyright 2013 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package cmd_shell_tln; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20130425); - -sub getConfig{return %config} - -sub getShortDescr { - return "Gets shell open cmds for various file types"; -} -sub getDescr{} -sub getRefs { - my %refs = ("You Are Unable to Start a Program with an .exe File Extension" => - "http://support.microsoft.com/kb/310585"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching cmd_shell_tln v.".$VERSION); -# ::rptMsg("cmd_shell v.".$VERSION); # banner -# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my @shells = ("exe","cmd","bat","cs","hta","pif"); - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - foreach my $sh (@shells) { - my $key_path = "Classes\\".$sh."file\\shell\\open\\command"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); -# ::rptMsg(""); - my $lw = $key->get_timestamp(); - my $val; - eval { - $val = $key->get_value("")->get_data(); -# ::rptMsg(" Cmd: ".$val); - - if ($sh eq "hta") { - if ($val eq "C:\\Windows\\SysWOW64\\mshta\.exe \"%1\" %*" || $val eq "C:\\WINDOWS\\system32\\mshta\.exe \"%1\" %*") { - - } - else { -# ::alertMsg("ALERT: cmd_shell: ".$key_path." warning: ".$val); - ::alertMsg($lw."|ALERT|||Software\\".$key_path." warning: ".$val); - } - } - else { -# ::alertMsg("ALERT: cmd_shell: ".$key_path." warning: ".$val) unless ($val eq "\"%1\" %*"); - ::alertMsg($lw."|ALERT|||Software\\".$key_path." warning: ".$val) unless ($val eq "\"%1\" %*"); - } - }; - - } - else { -# ::rptMsg($key_path." not found."); -# ::rptMsg(""); - } - } -# ::rptMsg(""); - - my $key_path = "Clients\\StartMenuInternet\\IExplore.exe\\shell\\open\\command"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my $lw = $key->get_timestamp(); - eval { - my $cmd = $key->get_value("")->get_data(); -# ::rptMsg(" Cmd: ".$cmd); - - if ($cmd eq "\"C:\\Program Files\\Internet Explorer\\iexplore\.exe\"" || - $cmd eq "\"C:\\Program Files (x86)\\Internet Explorer\\iexplore\.exe\"") { - - } - else { - ::alertMsg($lw."|ALERT|||Software\\".$key_path." warning: ".$cmd); - } - }; -# ::rptMsg("Error: ".$@) if ($@); - } - else { -# ::rptMsg($key_path." not found\."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/cmd_shell_u.pl b/thirdparty/rr-full/plugins/cmd_shell_u.pl deleted file mode 100644 index 3c98f8afdf3..00000000000 --- a/thirdparty/rr-full/plugins/cmd_shell_u.pl +++ /dev/null @@ -1,67 +0,0 @@ -#----------------------------------------------------------- -# cmd_shell_u -# Get the shell\open\command settings for various file types; gets -# info from USRCLASS.DAT hives, where Classes data is maintained on -# Win7 -# -# Change History -# 20130405 - created -# -# copyright 2013 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package cmd_shell_u; -use strict; - -my %config = (hive => "USRCLASS\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20130405); - -sub getConfig{return %config} - -sub getShortDescr { - return "Gets shell open cmds for various file types from USRCLASS.DAT"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching cmd_shell_u v.".$VERSION); - ::rptMsg("cmd_shell_u v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my @shells = ("\.exe","exefile","ftp","http","https"); - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - foreach my $sh (@shells) { - my $key_path = $sh."\\shell\\open\\command"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); -# ::rptMsg(""); - my $val; - eval { - $val = $key->get_value("")->get_data(); - ::rptMsg(" Cmd: ".$val); - ::rptMsg(""); - }; - ::rptMsg("Error: ".$@) if ($@); - } - else { - ::rptMsg($key_path." not found."); - } - } - ::rptMsg(""); -} -1; diff --git a/thirdparty/rr-full/plugins/cmdproc.pl b/thirdparty/rr-full/plugins/cmdproc.pl index 754350f3232..3c929c83ece 100644 --- a/thirdparty/rr-full/plugins/cmdproc.pl +++ b/thirdparty/rr-full/plugins/cmdproc.pl @@ -3,27 +3,32 @@ # Checks key for files to autostart from cmd.exe # # Change History +# 20200904 - MITRE updates +# 20200515 - updated date output format # 20190223 - added reference # 20130425 - added alertMsg() functionality # 20130115 - created # # References: # https://unit42.paloaltonetworks.com/new-babyshark-malware-targets-u-s-national-security-think-tanks/ +# https://attack.mitre.org/techniques/T1546/ # # Category: autostart,malware,programexecution # -# copyright 2013 Quantum Analytics Research, +# copyright 2020 Quantum Analytics Research, # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package cmdproc; use strict; my %config = (hive => "NTUSER\.DAT", - osmask => 22, + MITRE => "T1546", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20190223); + output => "report", + version => 20200904); sub getConfig{return %config} @@ -41,8 +46,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching cmdproc v.".$VERSION); - ::rptMsg("cmdproc v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("cmdproc v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -50,7 +57,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my $auto; eval { diff --git a/thirdparty/rr-full/plugins/cmdproc_tln.pl b/thirdparty/rr-full/plugins/cmdproc_tln.pl index e6dcf94fe03..0f491b9c735 100644 --- a/thirdparty/rr-full/plugins/cmdproc_tln.pl +++ b/thirdparty/rr-full/plugins/cmdproc_tln.pl @@ -3,10 +3,11 @@ # Checks key for files to autostart from cmd.exe # # Change History +# 20200904 - MITRE updates # 20130425 - created # # References: -# +# https://attack.mitre.org/techniques/T1546/ # Category: autostart,malware,programexecution # # copyright 2013 Quantum Analytics Research, @@ -16,16 +17,18 @@ package cmdproc_tln; use strict; my %config = (hive => "NTUSER\.DAT", - osmask => 22, + MITRE => "T1546", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20130425); + output => "tln", + version => 20200904); sub getConfig{return %config} sub getShortDescr { - return "Autostart - get Command Processor\\AutoRun value from NTUSER.DAT hive (TLN)"; + return "Autostart - get Command Processor\\AutoRun value from NTUSER\.DAT hive (TLN)"; } sub getDescr{} sub getRefs {} @@ -64,4 +67,4 @@ sub pluginmain { # ::rptMsg($key_path." not found."); } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/codeid.pl b/thirdparty/rr-full/plugins/codeid.pl deleted file mode 100644 index f07eac80a7b..00000000000 --- a/thirdparty/rr-full/plugins/codeid.pl +++ /dev/null @@ -1,77 +0,0 @@ -#----------------------------------------------------------- -# codeid -# Get DefaultLevel value from CodeIdentifiers key -# -# -# Change History -# 20100608 - created -# -# References -# SANS ISC blog - http://isc.sans.edu/diary.html?storyid=8917 -# CodeIdentifiers key -# - http://technet.microsoft.com/en-us/library/bb457006.aspx -# SAFER_LEVELID_FULLYTRUSTED value -# - http://msdn.microsoft.com/en-us/library/ms722424%28VS.85%29.aspx -# (262144 == Unrestricted) -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package codeid; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20100608); - -sub getConfig{return %config} - -sub getShortDescr { - return "Gets CodeIdentifier DefaultLevel value"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching codeid v.".$VERSION); - ::rptMsg("codeid v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Policies\\Microsoft\\Windows\\Safer\\CodeIdentifiers"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("CodeID"); - ::rptMsg($key_path); - my $lastwrite = $key->get_timestamp(); - ::rptMsg(" LastWrite time: ".gmtime($lastwrite)." Z"); - ::rptMsg(""); - - my $level; - eval { - $level = $key->get_value("DefaultLevel")->get_data(); - ::rptMsg(sprintf "DefaultLevel = 0x%08x",$level); - }; - - my $exe; - eval { - $exe = $key->get_value("ExecutableTypes")->get_data(); - $exe =~ s/\s/,/g; - ::rptMsg("ExecutableTypes = ".$exe); - - }; - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/codepage.pl b/thirdparty/rr-full/plugins/codepage.pl new file mode 100644 index 00000000000..6f1e3ceb7a6 --- /dev/null +++ b/thirdparty/rr-full/plugins/codepage.pl @@ -0,0 +1,68 @@ +#----------------------------------------------------------- +# codepage.pl +# +# +# +# References: +# +# +# Change history: +# 20200904 - MITRE updates +# 20200519 - created +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package codepage; +use strict; + +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + category => "config", + version => 20200904); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks codepage value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching codepage v.".$VERSION); + ::rptMsg("codepage v.".$VERSION); # banner + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path; + my $key; + + my $ccs; + my $current; + if ($key = $root_key->get_subkey("Select")) { + $current = $key->get_value("Current")->get_data(); + $ccs = "ControlSet00".$current; + } + + $key_path = $ccs."\\Control\\Nls\\CodePage"; + eval { + if ($key = $root_key->get_subkey($key_path)){ + my $acp = $key->get_value("ACP")->get_data(); + ::rptMsg("CodePage key LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(" Code page value = ".$acp); + ::rptMsg(""); + ::rptMsg("Code page description: https://en.wikipedia.org/wiki/Code_page"); + } + }; + ::rptMsg("Control\\Nls\\CodePage\\ACP value not found.") if ($@); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/coinstallers.pl b/thirdparty/rr-full/plugins/coinstallers.pl new file mode 100644 index 00000000000..bbdb270aa27 --- /dev/null +++ b/thirdparty/rr-full/plugins/coinstallers.pl @@ -0,0 +1,100 @@ +#----------------------------------------------------------- +# coinstallers +# Gets contents of Enum\WpdBusEnumRoot keys +# +# +# History: +# 20211212 - created +# +# Ref: +# https://twitter.com/wdormann/status/1432703702079508480 +# https://docs.microsoft.com/en-us/windows-hardware/drivers/install/registering-a-device-specific-co-installer +# 1. Look for CoInstallers32 values for devices; REG_MULTI_SZ value +# 2. Set DisableCoInstallers (not a default value) to "1" +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package coinstallers; +use strict; + +my %config = (hive => "System", + MITRE => "T1546", #Event triggered execution + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + version => 20211212); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get device CoInstallers32 values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $reg; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching coinstallers v.".$VERSION); + ::rptMsg("coinstallers v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + + my $key_path = $ccs."\\Control\\Class"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $k (@subkeys) { + my $dev_class = $k->get_name(); +# ::rptMsg($dev_class); + + my @subkeys2 = $k->get_list_of_subkeys(); + if (scalar(@subkeys2) > 0) { + foreach my $l (@subkeys2) { + my $sk_name = $l->get_name(); + my $device_descr = "Unknown Device Description"; + + eval { + my $d = $l->get_value("DriverDesc")->get_data(); + $device_descr = $d; + }; + + eval { + my $c = $l->get_value("CoInstallers32")->get_data(); + ::rptMsg("Device : ".$device_descr); + ::rptMsg("Key Path : ".$key_path."\\".$dev_class."\\".$sk_name); + ::rptMsg("LastWrite time : ".::format8601Date($l->get_timestamp())."Z"); + ::rptMsg("CoInstaller32 value: ".$c); + + ::rptMsg(""); + }; + } + } +# ::rptMsg(""); + } + } + else { + ::rptMsg($key_path." has no subkeys."); + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg("Analysis Tip: Device-specific co-installers are registered during the process of installing a device, when"); + ::rptMsg("the Coinstallers INF section is processed. SetupAPI then calls the co-installers at each subsequent step of"); + ::rptMsg("the installation process. If more than one co-installer is registered for a device, SetupAPI calls them in the"); + ::rptMsg("order in which they are listed in the registry."); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/comautoapproval.pl b/thirdparty/rr-full/plugins/comautoapproval.pl new file mode 100644 index 00000000000..db1dc407e29 --- /dev/null +++ b/thirdparty/rr-full/plugins/comautoapproval.pl @@ -0,0 +1,107 @@ +#----------------------------------------------------------- +# comautoapproval.pl +# check the COMAutoApprovalList key for potential UAC bypasses; not all listed +# value names will be for COM objects that actually exist on the system, so the +# plugin runs thru the HKLM\Software\Classes\CLSID subkeys to verify those that +# exist on that system. +# +# Change history: +# 20220829 - created +# +# References: +# https://twitter.com/d4rksystem/status/1562507028337131520?s=20&t=3k45RhMaSRvLr6kNc0fdKg +# https://swapcontext.blogspot.com/2020/11/uac-bypasses-from-comautoapprovallist.html +# https://docs.velociraptor.app/exchange/artifacts/pages/windows.registry.comautoapprovallist/ +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package comautoapproval; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + MITRE => "T1548\.002", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + version => 20220829); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check COMAutoApprovalList for potential UAC bypasses"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $root_key = (); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $wd_count = 0; + ::logMsg("Launching comautoapproval v.".$VERSION); + ::rptMsg("comautoapproval v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + my $reg = Parse::Win32Registry->new($hive); + $root_key = $reg->get_root_key; + + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\UAC\\ComAutoApprovalList"; + my $key = (); + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path : ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { +# ::rptMsg($v->get_name()); + processGUID($v->get_name()); + ::rptMsg(""); + } + } + else { + ::rptMsg($key_path." has no values."); + } + } + else { + ::rptMsg($key_path." not found."); + } + +# ::rptMsg(""); + ::rptMsg("Analysis Tip: The COMAutoApprovalList key provides a list of special, elevated COM objects that can"); + ::rptMsg("lead to UAC bypasses\. This plugin runs through that list and enumerates those that exist within the "); + ::rptMsg("Software hive, providing information about each."); + ::rptMsg(""); + ::rptMsg("Ref: https://swapcontext.blogspot.com/2020/11/uac-bypasses-from-comautoapprovallist.html"); +} + +sub processGUID { + my $guid = shift; + my $key_path = "Classes\\CLSID\\".$guid; + my $key = (); + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + ::rptMsg(" Class : ".$key->get_value("")->get_data()); + }; + + eval { + ::rptMsg(" Elevation\\Enabled: ".$key->get_subkey("Elevation")->get_value("Enabled")->get_data()); + }; + + eval { + ::rptMsg(" InProcServer32 : ".$key->get_subkey("InProcServer32")->get_value("")->get_data()); + }; + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/comdlg32.pl b/thirdparty/rr-full/plugins/comdlg32.pl index 23495f85216..080211b1ef3 100644 --- a/thirdparty/rr-full/plugins/comdlg32.pl +++ b/thirdparty/rr-full/plugins/comdlg32.pl @@ -3,6 +3,8 @@ # Plugin for Registry Ripper # # Change history +# 20200904 - MITRE updates +# 20200517 - updated date output format # 20180702 - update to parseGUID function # 20180627 - updated to address Win10, per input from Geoff Rempel # 20121005 - updated to address shell item type 0x3A @@ -16,7 +18,7 @@ # Win2000 - http://support.microsoft.com/kb/319958 # XP - http://support.microsoft.com/kb/322948/EN-US/ # -# copyright 2018 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package comdlg32; @@ -27,8 +29,10 @@ package comdlg32; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180702); + output => "report", + category => "user activity", + MITRE => "", + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -99,7 +103,7 @@ sub pluginmain { my @vals; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @subkeys = $key->get_list_of_subkeys(); @@ -107,42 +111,42 @@ sub pluginmain { foreach my $s (@subkeys) { if ($s->get_name() eq "LastVisitedMRU") { ::rptMsg("LastVisitedMRU"); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())); + ::rptMsg("LastWrite: ".::format8601Date($s->get_timestamp())."Z"); parseLastVisitedMRU($s); ::rptMsg(""); } if ($s->get_name() eq "OpenSaveMRU") { ::rptMsg("OpenSaveMRU"); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())); + ::rptMsg("LastWrite: ".::format8601Date($s->get_timestamp())."Z"); parseOpenSaveMRU($s); ::rptMsg(""); } if ($s->get_name() eq "CIDSizeMRU") { ::rptMsg("CIDSizeMRU"); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())); + ::rptMsg("LastWrite: ".::format8601Date($s->get_timestamp())."Z"); parseCIDSizeMRU($s); ::rptMsg(""); } if ($s->get_name() eq "FirstFolder") { ::rptMsg("FirstFolder"); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); parseFirstFolder($s); ::rptMsg(""); } if ($s->get_name() eq "LastVisitedPidlMRU" || $s->get_name() eq "LastVisitedPidlMRULegacy") { ::rptMsg("LastVisitedPidlMRU"); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); parseLastVisitedPidlMRU($s); ::rptMsg(""); } if ($s->get_name() eq "OpenSavePidlMRU") { ::rptMsg("OpenSavePidlMRU"); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); parseOpenSavePidlMRU($s); ::rptMsg(""); } @@ -213,7 +217,7 @@ sub parseOpenSaveMRU { sub parseOpenSaveValues { my $key = shift; ::rptMsg("OpenSaveMRU\\".$key->get_name()); - ::rptMsg("LastWrite Time: ".gmtime($key->get_timestamp())." Z"); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); my %osmru; my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { @@ -432,7 +436,6 @@ sub parseShellItem { while ($tag) { my %item = (); my $sz = unpack("v",substr($data,$cnt,2)); - return %str unless (defined $sz); $tag = 0 if (($sz == 0) || ($cnt + $sz > $len)); my $dat = substr($data,$cnt,$sz); @@ -606,7 +609,6 @@ sub parseFolderEntry { my $str = ""; while($tag) { my $s = substr($data,$ofs_shortname + $cnt,1); - return %item unless (defined $s); if ($s =~ m/\00/ && ((($cnt + 1) % 2) == 0)) { $tag = 0; } @@ -622,9 +624,7 @@ sub parseFolderEntry { my $tag = 1; my $cnt = 0; while ($tag) { - my $s = substr($data,$ofs + $cnt,2); - return %item unless (defined $s); - if (unpack("v",$s) == 0xbeef) { + if (unpack("v",substr($data,$ofs + $cnt,2)) == 0xbeef) { $tag = 0; } else { diff --git a/thirdparty/rr-full/plugins/comfoo.pl b/thirdparty/rr-full/plugins/comfoo.pl deleted file mode 100644 index 8ea35588f59..00000000000 --- a/thirdparty/rr-full/plugins/comfoo.pl +++ /dev/null @@ -1,90 +0,0 @@ -#----------------------------------------------------------- -#comfoo -# -# -# Change history -# 20131007 - created -# -# -# References -# http://www.secureworks.com/cyber-threat-intelligence/threats/secrets-of-the-comfoo-masters/ -# -# copyright 2013 QAR, LLC -#----------------------------------------------------------- -package comfoo; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - category => "malware", - version => 20131007); - -sub getConfig{return %config} -sub getShortDescr { - return "Checks known Comfoo values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching comfoo v.".$VERSION); - ::rptMsg("comfoo v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my ($current,$ccs); - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - -# Check the services listed in the SecureWorks post/reference - my @svcs = ("NetMan", "SENS", "RasAuto"); - my ($cf_path,$cf); - - foreach my $s (@svcs) { - $cf_path = $ccs."\\Services\\".$s; - if ($cf = $root_key->get_subkey($cf_path)) { - ::rptMsg($cf_path); - ::rptMsg("LastWrite Time ".gmtime($cf->get_subkey("Parameters")->get_timestamp())." (UTC)"); -# ::rptMsg(""); - - eval { - my $start = $cf->get_value("Start")->get_data(); - if ($start != 0x03 && $s ne "SENS") { - ::rptMsg("Start value = ".$start); - ::rptMsg("Comfoo malware is known to change the Start value from 3 to 2"); - ::rptMsg(""); - } - }; - - eval { - my $dllname = $s."\.dll"; - $dllname =~ tr/[A-Z]/[a-z]/; - my $dll = $cf->get_subkey("Parameters")->get_value("ServiceDll")->get_data(); - ::rptMsg("ServiceDll value : ".$dll); - ::rptMsg("Should be/include: ".$dllname); - }; - } - ::rptMsg(""); - } - ::rptMsg("Analysis Tip: Comfoo malware is known to change the ServiceDll value to point"); - ::rptMsg("to something other than the normal value"); - } -} - -1; diff --git a/thirdparty/rr-full/plugins/compdesc.pl b/thirdparty/rr-full/plugins/compdesc.pl index 78263452096..4ec438ac313 100644 --- a/thirdparty/rr-full/plugins/compdesc.pl +++ b/thirdparty/rr-full/plugins/compdesc.pl @@ -4,22 +4,26 @@ # ComputerDescriptions key parser # # Change history -# +# 20200904 - MITRE updates +# 20200511 - updated date output format +# 20080324 - created # # References # # -# copyright 2008 H. Carvey +# copyright 2020 H. Carvey #----------------------------------------------------------- package compdesc; use strict; my %config = (hive => "NTUSER\.DAT", hasShortDescr => 1, + category => "config", hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080324); + MITRE => "", + output => "report", + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -46,7 +50,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("ComputerDescriptions"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { foreach my $v (@vals) { @@ -55,12 +59,10 @@ sub pluginmain { } else { ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } diff --git a/thirdparty/rr-full/plugins/compname.pl b/thirdparty/rr-full/plugins/compname.pl index e7010f8cd6c..7912991a160 100644 --- a/thirdparty/rr-full/plugins/compname.pl +++ b/thirdparty/rr-full/plugins/compname.pl @@ -4,6 +4,8 @@ # computername # # Change history +# 20201021 - added checks for domains +# 20200904 - MITRE updates # 20090727 - added Hostname # # References @@ -18,12 +20,14 @@ package compname; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20090727); + category => "config", + MITRE => "", + output => "report", + version => 20201021); sub getConfig{return %config} sub getShortDescr { - return "Gets ComputerName and Hostname values from System hive"; + return "Gets ComputerName, Hostname, and domain values from System hive"; } sub getDescr{} sub getRefs {} @@ -37,7 +41,7 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching compname v.".$VERSION); ::rptMsg("compname v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -53,25 +57,33 @@ sub pluginmain { my $cn; if ($cn = $root_key->get_subkey($cn_path)) { my $name = $cn->get_value("ComputerName")->get_data(); - ::rptMsg("ComputerName = ".$name); + ::rptMsg(sprintf "%-20s %-50s","ComputerName",$name); } else { ::rptMsg($cn_path." not found."); - ::logMsg($cn_path." not found."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } - my $hostname; - eval { - my $host_path = $ccs."\\Services\\Tcpip\\Parameters"; - $hostname = $root_key->get_subkey($host_path)->get_value("Hostname")->get_data(); - ::rptMsg("TCP/IP Hostname = ".$hostname); - }; + my @hostnames = ("Hostname","NV Hostname"); + my $host_path = $ccs."\\Services\\Tcpip\\Parameters"; + foreach my $hostname (@hostnames) { + eval { + my $host = $root_key->get_subkey($host_path)->get_value($hostname)->get_data(); + ::rptMsg(sprintf "%-20s %-50s",$hostname,$host); + }; + } + my @domains = ("Domain","ICSDomain","DhcpDomain","NV Domain"); + my $domain_path = $ccs."\\Services\\Tcpip\\Parameters"; + foreach my $domain (@domains) { + eval { + my $d = $root_key->get_subkey($domain_path)->get_value($domain)->get_data(); + ::rptMsg(sprintf "%-20s %-50s",$domain,$d); + }; + } } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/consentstore.pl b/thirdparty/rr-full/plugins/consentstore.pl new file mode 100644 index 00000000000..f14ec20b0f7 --- /dev/null +++ b/thirdparty/rr-full/plugins/consentstore.pl @@ -0,0 +1,119 @@ +#----------------------------------------------------------- +# consentstore +# +# Change history: +# 20200904 - MITRE updates +# 20200608 - created +# +# Ref: +# https://medium.com/@7a616368/can-you-track-processes-accessing-the-camera-and-microphone-7e6885b37072 +# https://dfir.pubpub.org/pub/nm5b39ae/release/1 +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package consentstore; +use strict; + +my %config = (hive => "Software, NTUSER\.DAT", + category => "collection", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1123 & T1125", + version => 20200904); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets contents of ConsentStore subkeys"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching consentstore v.".$VERSION); + ::rptMsg("consentstore v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my @paths = ('Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore', + 'Software\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @sk1 = $key->get_list_of_subkeys(); + if (scalar @sk1 > 0) { + foreach my $s1 (@sk1) { + my $top_name = $s1->get_name(); + + my @sk2 = $s1->get_list_of_subkeys(); + if (scalar @sk2 > 0) { + foreach my $s2 (@sk2) { + my $name = $s2->get_name(); + + if ($name eq "NonPackaged") { + my @sk3 = $s2->get_list_of_subkeys(); + if (scalar @sk3 > 0) { + foreach my $s3 (@sk3) { + processKey($s3,$top_name); + } + } + } + else { + processKey($s2,$top_name); + } + + } + } + } + } + } + else { +# ::rptMsg($key_path." not found."); + } + } +} + +sub processKey { + my $key = shift; + my $device = shift; + my $name = $key->get_name(); + + my $start = (); + my $stop = (); + + eval { + my $s = $key->get_value("LastUsedTimeStart")->get_data(); + my ($t0,$t1) = unpack("VV",$s); + $start = ::getTime($t0,$t1); + }; + + eval { + my $s = $key->get_value("LastUsedTimeStop")->get_data(); + my ($t0,$t1) = unpack("VV",$s); + $stop = ::getTime($t0,$t1); + }; + + + if ($start && $stop) { + ::rptMsg($device); + ::rptMsg($name); + ::rptMsg(sprintf "%-20s %-20s","LastWrite time",::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(sprintf "%-20s %-20s","LastUsedTimeStart",::format8601Date($start)."Z"); + ::rptMsg(sprintf "%-20s %-20s","LastUsedTimeStop",::format8601Date($stop)."Z"); + ::rptMsg(""); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/consentstore_tln.pl b/thirdparty/rr-full/plugins/consentstore_tln.pl new file mode 100644 index 00000000000..5e464aaf785 --- /dev/null +++ b/thirdparty/rr-full/plugins/consentstore_tln.pl @@ -0,0 +1,111 @@ +#----------------------------------------------------------- +# consentstore_tln +# +# Change history: +# 20200904 - MITRE updates +# 20200608 - created +# +# Ref: +# https://medium.com/@7a616368/can-you-track-processes-accessing-the-camera-and-microphone-7e6885b37072 +# https://dfir.pubpub.org/pub/nm5b39ae/release/1 +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package consentstore_tln; +use strict; + +my %config = (hive => "Software, NTUSER\.DAT", + category => "collection", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "tln", + MITRE => "T1125 & T1123", + version => 20200904); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets contents of ConsentStore subkeys"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::rptMsg("Launching consentstore v.".$VERSION); +# ::rptMsg("consentstore v.".$VERSION); +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my @paths = ('Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore', + 'Software\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @sk1 = $key->get_list_of_subkeys(); + if (scalar @sk1 > 0) { + foreach my $s1 (@sk1) { + my $top_name = $s1->get_name(); + + my @sk2 = $s1->get_list_of_subkeys(); + if (scalar @sk2 > 0) { + foreach my $s2 (@sk2) { + my $name = $s2->get_name(); + + if ($name eq "NonPackaged") { + my @sk3 = $s2->get_list_of_subkeys(); + if (scalar @sk3 > 0) { + foreach my $s3 (@sk3) { + processKey($s3,$top_name); + } + } + } + else { + processKey($s2,$top_name); + } + + } + } + } + } + } + else { +# ::rptMsg($key_path." not found."); + } + } +} + +sub processKey { + my $key = shift; + my $device = shift; + my $name = $key->get_name(); + $name =~ s/#/\\/g; + + my $start = (); + my $stop = (); + + eval { + my $s = $key->get_value("LastUsedTimeStart")->get_data(); + my ($t0,$t1) = unpack("VV",$s); + $start = ::getTime($t0,$t1); + ::rptMsg($start."|REG|||ConsentStore ".$device." \"".$name."\" LastUsedTimeStart"); + }; + + eval { + my $s = $key->get_value("LastUsedTimeStop")->get_data(); + my ($t0,$t1) = unpack("VV",$s); + $stop = ::getTime($t0,$t1); + ::rptMsg($stop."|REG|||ConsentStore ".$device." \"".$name."\" LastUsedTimeStop"); + }; + +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/controlpanel.pl b/thirdparty/rr-full/plugins/controlpanel.pl deleted file mode 100644 index 17ca50f1d39..00000000000 --- a/thirdparty/rr-full/plugins/controlpanel.pl +++ /dev/null @@ -1,66 +0,0 @@ -#----------------------------------------------------------- -# controlpanel.pl -# Vista ControlPanel key seems to contain some interesting info about the -# user's activities... -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package controlpanel; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 64, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20080428); - -sub getConfig{return %config} - -sub getShortDescr { - return "Look for RecentTask* values in ControlPanel key (Vista)"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching controlpanel v.".$VERSION); - ::rptMsg("controlpanel v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - ::rptMsg("Analysis Tip: The RecentTask* entries appear to only be populated through the"); - ::rptMsg("choices in the Control Panel Home view (in Vista). As each new choice is"); - ::rptMsg("selected, the most recent choice is added as RecentTask1, and each "); - ::rptMsg("RecentTask* entry is incremented and pushed down in the stack."); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $str = sprintf "%-15s %-45s",$v->get_name(),$v->get_data(); - ::rptMsg($str); - } - ::rptMsg(""); - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/cortana.pl b/thirdparty/rr-full/plugins/cortana.pl deleted file mode 100644 index 29d76b946cf..00000000000 --- a/thirdparty/rr-full/plugins/cortana.pl +++ /dev/null @@ -1,166 +0,0 @@ -#------------------------------------------------------------------------------ -# cortana.pl -# Acquires search terms from Cortana in Windows 10 -# -# Change history -# 20150627 - v1 -# -# References -# Internal testing and verification using both manually typed search terms and verbal search terms -# using Cortana and the default search engine. Other browsers were tested and yielded the same results. -# -# author: P. Seagren, patrick.seagren@outlook.com -#------------------------------------------------------------------------------ - -package cortana; -use strict; - -my %config = -( - hive => "ntuser.dat", - hasShortDescr => 1, - hasDescr => 1, - hasRefs => 1, - osmask => 22, - version => 20150628 -); - -sub getConfig {return %config;} -sub getShortDescr {return "Search terms from Cortana/search bar";} -sub getDescr {return "Written and voice command search terms from the Cortana/search bar";} -sub getRefs {return "Internal testing and verification";} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain -{ - my $class = shift; - my $hive = shift; - ::logMsg('Launching cortana v'.$VERSION); - ::rptMsg('cortana v'.$VERSION.' ('.getShortDescr().")"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - # my_enum ($root_key, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts"); - enum_recursively ($root_key, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts"); -} - -sub hexify -{ -my $data = shift; -my $l=''; -my $r=''; -my $n=0; -my $nd=''; -for (my $i=0; $i15) - - { - $nd.=sprintf("%-48s%s\n", $l,$r); - $l='';$r='';$n=0; - } -} -if ($n!=0) - { - $nd.=sprintf("%-48s%s\n", $l,$r); - - } -return $nd; -} - -sub enum_recursively -{ -my $root_key = shift; -my $key_path = shift; -my $rec_level = shift; -return if ($rec_level>3); -my $find = shift;$find = '.' if $find eq ''; -my $key; -my $key_printed=0; -my $sep = ' ' x 2; -my $cortana_string=".com/search"; - - -if ($key = $root_key->get_subkey($key_path)) -{ - #::rptMsg(" inside if ..key=".$key->get_name()); - - my $key_name = $key->get_name(); - if (($rec_level == 1) && (index($key_name, $cortana_string) != 0)){ - # ::rptMsg(" It does not contain CORTANA "); - return ; - } - $sep = ' ' x 4; - my @vals = $key->get_list_of_values(); - my %ac_vals; - foreach my $v (sort {lc($a) <=> lc($b)} @vals) - { - my $vd = $v->get_data(); - my $vt = $v->get_type_as_string(); - if ($vt !~ /REG_(DWORD|SZ|EXPAND_SZ)/) - { - $vd = hexify($vd); - } - $ac_vals{$v->get_name()}{'VT'} = $vt; - $ac_vals{$v->get_name()}{'VD'} = $vd; - } - - - - foreach my $a (sort {lc($a) <=> lc($b)} keys %ac_vals) - { - my $ax = $a; $ax = '(Default)' if $a eq ''; - my $vt = $ac_vals{$a}{'VT'}; - my $vd = $ac_vals{$a}{'VD'}; - - # ::rptMsg("for each a=".$a); - # ::rptMsg("for each ax=".$ax); - # ::rptMsg("for each vt=".$vt); - # ::rptMsg("for each vd=".$vd); - - if (($a.$vd) ne '' && ($ax.$a.$vd) =~/$find/is) - { - if ($key_printed==0) - { - ::rptMsg("\n"); - ::rptMsg($sep.$key_path); - ::rptMsg($sep.'LastWrite Time '.gmtime($key->get_timestamp())." (UTC)\n"); - $key_printed=1; - } - $sep = ' ' x 4; - ::rptMsg($sep.$ax); - $sep = ' ' x 6; - ::rptMsg($sep.$vt); - $sep = ' ' x 8; - if ($vt !~ /REG_(DWORD|SZ|EXPAND_SZ)/) - { - $vd =~ s/[\n]+/\n$sep/sg; - } - ::rptMsg($sep.$vd); - } - - } - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) - { - foreach my $s (@subkeys) - { - - #::rptMsg(" for each subkey=".@subkeys.", s=".$s.", s-name=".$s->get_name()); - #::rptMsg(" for each rec_level=".$rec_level.", find=".$find); - enum_recursively ($root_key , $key_path."\\".$s->get_name(), $rec_level + 1,$find); - } - } -} -else -{ - ::rptMsg($sep.$key_path.' not found.'); -} -} \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/cpldontload.pl b/thirdparty/rr-full/plugins/cpldontload.pl deleted file mode 100644 index 737e7afad50..00000000000 --- a/thirdparty/rr-full/plugins/cpldontload.pl +++ /dev/null @@ -1,74 +0,0 @@ -#----------------------------------------------------------- -# cpldontload.pl -# Check contents of user's Control Panel\don't load key -# -# Change history -# 20100116 - created -# -# References -# W32.Nekat - http://www.symantec.com/security_response/ -# writeup.jsp?docid=2008-011419-0705-99&tabid=2 -# http://www.2-viruses.com/remove-antispywarexp2009 -# -# Notes: Some malware appears to hide various Control Panel applets -# using this means. If some sort of malware/spyware is thought -# to be on the system, check the settings and note the key -# LastWrite time. -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package cpldontload; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100116); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's Control Panel don't load key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching cpldontload v.".$VERSION); - ::rptMsg("cpldontload v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = "Control Panel\\don\'t load"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @vals = $key->get_list_of_values(); - if (scalar @vals > 0) { - foreach my $v (@vals) { - my $str = sprintf "%-20s %-5s",$v->get_name(),$v->get_data(); - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/crashcontrol.pl b/thirdparty/rr-full/plugins/crashcontrol.pl index e7e38bd6ee8..634b4d00a30 100644 --- a/thirdparty/rr-full/plugins/crashcontrol.pl +++ b/thirdparty/rr-full/plugins/crashcontrol.pl @@ -2,6 +2,7 @@ # crashcontrol.pl # # History: +# 20200904 - MITRE updates # 20131210 - updated to include ref/values for Win8/2012 # 20081212 - created # @@ -17,11 +18,13 @@ package crashcontrol; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20131210); + output => "report", + version => 20200904); sub getConfig{return %config} diff --git a/thirdparty/rr-full/plugins/cred.pl b/thirdparty/rr-full/plugins/cred.pl new file mode 100644 index 00000000000..247267f1173 --- /dev/null +++ b/thirdparty/rr-full/plugins/cred.pl @@ -0,0 +1,74 @@ +#----------------------------------------------------------- +# cred.pl +# +# +# +# References: +# +# +# Change history: +# 20200730 - MITRE ATT&CK updates +# 20200427 - updated output date format +# 20200402 - created +# +# https://attack.mitre.org/techniques/T1112/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package cred; +use strict; + +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1112", + category => "defense evasion", + version => 20200730); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks for UseLogonCredential value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching cred v.".$VERSION); + ::rptMsg("cred v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path; + my $key; + +# System Hive +# First, need to get the value for the CurrentControlSet + my $ccs; + my $current; + if ($key = $root_key->get_subkey("Select")) { + $current = $key->get_value("Current")->get_data(); + $ccs = "ControlSet00".$current; + } +# https://www.praetorian.com/blog/mitigating-mimikatz-wdigest-cleartext-credential-theft?edition=2019 + $key_path = $ccs."\\Control\\SecurityProviders\\WDigest"; + eval { + if ($key = $root_key->get_subkey($key_path)){ + my $ulc = $key->get_value("UseLogonCredential")->get_data(); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(" UseLogonCredential value = ".$ulc); + ::rptMsg("The UseLogonCredential value set to \'1\' indicates that credentials are stored in memory in plain text.") + } + }; + ::rptMsg("UseLogonCredential value not found.") if ($@); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/cred_tln.pl b/thirdparty/rr-full/plugins/cred_tln.pl new file mode 100644 index 00000000000..0b9e3f3f53b --- /dev/null +++ b/thirdparty/rr-full/plugins/cred_tln.pl @@ -0,0 +1,72 @@ +#----------------------------------------------------------- +# cred.pl +# This plugin checks for the existence (and setting) of the UseLogonCredential value in +# the system hive. Because there is very little need to modify values beneath this key +# under normal circumstances, the key LastWrite time is assumed to be associated with the +# value being created and set (or changed to another value, as the case may be) +# +# +# References: +# +# +# Change history: +# 20200730 - MITRE ATT&CK Updates +# 20200402 - created +# +# https://attack.mitre.org/techniques/T1112/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package cred_tln; +use strict; + +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "tln", + MITRE => "T1112", + category => "malware", + version => 20200730); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks UseLogonCredential value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path; + my $key; + +# System Hive +# First, need to get the value for the CurrentControlSet + my $ccs; + my $current; + if ($key = $root_key->get_subkey("Select")) { + $current = $key->get_value("Current")->get_data(); + $ccs = "ControlSet00".$current; + } +# https://www.praetorian.com/blog/mitigating-mimikatz-wdigest-cleartext-credential-theft?edition=2019 + $key_path = $ccs."\\Control\\SecurityProviders\\WDigest"; + eval { + if ($key = $root_key->get_subkey($key_path)){ + my $ulc = $key->get_value("UseLogonCredential")->get_data(); + my $lw = $key->get_timestamp(); + ::rptMsg($lw."|REG|||[T1112] WDigest UseLogonCredential value = ".$ulc); +# ::rptMsg("The UseLogonCredential value set to \'1\' indicates that credentials are stored in memory in plain text.") + } + }; +# ::rptMsg("UseLogonCredential value not found.") if ($@); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/credentialsdelegation.pl b/thirdparty/rr-full/plugins/credentialsdelegation.pl new file mode 100644 index 00000000000..fcf990b24ba --- /dev/null +++ b/thirdparty/rr-full/plugins/credentialsdelegation.pl @@ -0,0 +1,93 @@ +#----------------------------------------------------------- +# credentialsdelegation.pl +# +# +# Change history: +# 20220307 - created +# +# References: +# https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-delegated-default-kerberos-and-ntlm-credentials-without-touching-lsass +# https://www.stigviewer.com/stig/windows_paw/2017-11-21/finding/V-78161 +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package credentialsdelegation; +use strict; + +my %config = (hive => "software", + category => "credential access", + MITRE => "T1555\.004", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220307); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get CredentialsDelegation values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching credentialsdelegation v.".$VERSION); + ::rptMsg("credentialsdelegation v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path = "Policies\\Microsoft\\Windows\\CredentialsDelegation"; + my $key = (); + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); +# start by getting key values + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-35s %-5s",$v->get_name(),$v->get_data()); + } + } + ::rptMsg(""); +# process subkeys + my @sk = ("AllowDefCredentialsWhenNTLMOnly","AllowDefaultCredentials"); + foreach my $s (@sk) { + if (my $k = $key->get_subkey($s)) { + ::rptMsg($key_path."\\".$s); + ::rptMsg("LastWrite time: ".::format8601Date($k->get_timestamp())."Z"); + ::rptMsg(""); + my @vals = $k->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-10s %-50s",$v->get_name(),$v->get_data()); + } + } + ::rptMsg(""); + } + } + ::rptMsg("Analysis Tip: Restricted remote administration protects administrator accounts by ensuring that reusable credentials"); + ::rptMsg("are not stored in memory on remote devices that could potentially be compromised. "); + ::rptMsg(""); + ::rptMsg("Ref: https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-delegated-default-kerberos-and-ntlm-credentials-without-touching-lsass"); + + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ctrlpnl.pl b/thirdparty/rr-full/plugins/ctrlpnl.pl deleted file mode 100644 index 8b1a63c12a1..00000000000 --- a/thirdparty/rr-full/plugins/ctrlpnl.pl +++ /dev/null @@ -1,145 +0,0 @@ -#----------------------------------------------------------- -# ctrlpnl.pl -# Get Control Panel info from the Software hive -# -# Change history: -# 20100116 - created -# -# References: -# http://support.microsoft.com/kb/292463 -# http://learning.infocollections.com/ebook%202/Computer/ -# Operating%20Systems/Windows/Windows.XP.Hacks/ -# 0596005113_winxphks-chp-2-sect-3.html -# http://msdn.microsoft.com/en-us/library/cc144195%28VS.85%29.aspx -# -# Notes: -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package ctrlpnl; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20100116); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get Control Panel info from Software hive"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my %comp; - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching ctrlpnl v.".$VERSION); - ::rptMsg("ctrlpnl v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Windows\\CurrentVersion\\Control Panel"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg(""); - -# Cpls section - if (my $cpl = $key->get_subkey("Cpls")) { - my @vals = $cpl->get_list_of_values(); - if (scalar @vals > 0) { - ::rptMsg("Cpls key"); - foreach my $v (@vals) { - my $str = sprintf "%-10s %-50s",$v->get_name(),$v->get_data(); - ::rptMsg($str); - } - ::rptMsg(""); - } - else { - ::rptMsg("Cpls key has no values."); - } - } - else { - ::rptMsg("Cpls key not found."); - } - -# don't load section -# The 'don't load' key prevents applets from being loaded -# Be sure to check the user's don't load key, as well - if (my $cpl = $key->get_subkey("don't load")) { - my @vals = $cpl->get_list_of_values(); - if (scalar @vals > 0) { - ::rptMsg("don't load key"); - foreach my $v (@vals) { - ::rptMsg($v->get_name()); - } - ::rptMsg(""); - } - else { - ::rptMsg("don't load key has no values."); - } - } - else { - ::rptMsg("don't load key not found."); - } - -# Extended Properties section - if (my $ext = $key->get_subkey("Extended Properties")) { - my @sk = $ext->get_list_of_subkeys(); - if (scalar @sk > 0) { - foreach my $s (@sk) { - my @vals = $s->get_list_of_values(); - if (scalar @vals > 0) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp)." UTC]"); - -# Ref: http://support.microsoft.com/kb/292463 - my %cat = (0x00000000 => "Other Control Panel Options", - 0x00000001 => "Appearance and Themes", - 0x00000002 => "Printers and Other Hardware", - 0x00000003 => "Network and Internet Connections", - 0x00000004 => "Sounds, Speech, and Audio Devices", - 0x00000005 => "Performance and Maintenance", - 0x00000006 => "Date, Time, Language, and Regional Options", - 0x00000007 => "Accessibility Options", - 0xFFFFFFFF => "No Category"); - my %prop; - foreach my $v (@vals) { - push(@{$prop{$v->get_data()}},$v->get_name()); - } - - foreach my $t (sort {$a <=> $b} keys %prop) { - (exists $cat{$t}) ? (::rptMsg($cat{$t})) : (::rptMsg("Category ".$t)); - foreach my $i (@{$prop{$t}}) { - ::rptMsg(" ".$i); - } - ::rptMsg(""); - } - } - } - ::rptMsg(""); - } - else { - ::rptMsg("Extended Properties key has no subkeys."); - } - } - else { - ::rptMsg("Extended Properties key not found."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/dafupnp.pl b/thirdparty/rr-full/plugins/dafupnp.pl index 9a36335e320..ef29bde040d 100644 --- a/thirdparty/rr-full/plugins/dafupnp.pl +++ b/thirdparty/rr-full/plugins/dafupnp.pl @@ -6,11 +6,15 @@ # (UPnP) data. DAFUPnP is used to stream media across a network. # # History: +# 20200904 - MITRE updates +# 20200525 - minor updates # 20180705 - updated, code tweaks # 20180628 - Created # # -# Author: M. Jones, mictjon@gmail.com +# Original Author: M. Jones, mictjon@gmail.com +# copyright 2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package dafupnp; use strict; @@ -19,8 +23,10 @@ package dafupnp; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180705); + MITRE => "", + output => "report", + category => "devices", + version => 20200904); my $VERSION = getVersion(); @@ -37,8 +43,8 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching dafupnp v.".$VERSION); - ::rptMsg("dafupnp v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("dafupnp v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my ($current,$ccs); diff --git a/thirdparty/rr-full/plugins/databasepath.pl b/thirdparty/rr-full/plugins/databasepath.pl new file mode 100644 index 00000000000..cc1abf7aad6 --- /dev/null +++ b/thirdparty/rr-full/plugins/databasepath.pl @@ -0,0 +1,73 @@ +#----------------------------------------------------------- +# databasepath.pl +# Get DatabasePath value from System hive +# +# Change history +# 20201021 - created +# +# References +# https://support.microsoft.com/en-us/help/172218/microsoft-tcp-ip-host-name-resolution-order +# +# copyright 2020 QAR, LLC +# H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package databasepath; +use strict; + +my %config = (hive => "System", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + category => "defense evasion", + MITRE => "T1564", + version => 20201021); + +sub getConfig{return %config} +sub getShortDescr { + return "Get DataBasePath value from System hive"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching databasepath v.".$VERSION); + ::rptMsg("databasepath v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + + my $key_path = $ccs."\\Services\\Tcpip\\Parameters"; + my $key = (); + if ($key = $root_key->get_subkey($key_path)) { + my $db = (); + eval { + $db = $key->get_value("DataBasePath")->get_data(); + ::rptMsg(sprintf "%-20s %-50s","DataBasePath",$db); + ::rptMsg(""); + ::rptMsg("Analysis Tip: A threat actor can change the location of the hosts file, and plant a malicious hosts file on"); + ::rptMsg("the system, preventing DNS queries from appearing on the network. This value should point to:"); + ::rptMsg("\"%SystemRoot%\\System32\\drivers\\etc\"."); + ::rptMsg(""); + ::rptMsg("Ref: https://support.microsoft.com/en-us/help/172218/microsoft-tcp-ip-host-name-resolution-order"); + }; + ::rptMsg("DataBasePath value not found.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/datatracing.pl b/thirdparty/rr-full/plugins/datatracing.pl new file mode 100644 index 00000000000..e5fb4b0d791 --- /dev/null +++ b/thirdparty/rr-full/plugins/datatracing.pl @@ -0,0 +1,80 @@ +#----------------------------------------------------------- +# datatracing.pl +# +# +# Change history +# 20201018 - created +# +# References +# https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008/cc765421(v=sql.100) +# https://www.hexacorn.com/blog/2020/10/17/beyond-good-ol-run-key-part-129/ +# +# https://attack.mitre.org/techniques/T1546/ +# +# Copyright 2020 Quantum Analytics Research, LLC +# H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package datatracing; +use strict; + +my %config = (hive => "software", + hasShortDescr => 1, + hasDescr => 1, + hasRefs => 0, + output => "report", + MITRE => "T1546", + category => "persistence", + version => 20201018); +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr { + return "Checks for MS SQL data tracing DLL"; +} +sub getShortDescr { + return "Checks for MS SQL data tracing DLL"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching datatracing v.".$VERSION); + ::rptMsg("datatracing v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + + my @paths = ("Microsoft\\BidInterface\\Loader", + "Wow6432Node\\Microsoft\\BidInterface\\Loader"); + + foreach my $key_path (@paths) { + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my $bid = (); + eval { + $bid = $key->get_value(":Path")->get_data(); + ::rptMsg(":Path value = ".$bid); + ::rptMsg(""); + }; + ::rptMsg("Analysis Tip: A data tracing DLL can be added to MS SQL, providing persistence via the \":Path\" value."); + ::rptMsg(""); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008/cc765421(v=sql.100)"); + } + else { +# ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); +} + +1; diff --git a/thirdparty/rr-full/plugins/dcom.pl b/thirdparty/rr-full/plugins/dcom.pl index e4459ed2b83..8d1b8f52c87 100644 --- a/thirdparty/rr-full/plugins/dcom.pl +++ b/thirdparty/rr-full/plugins/dcom.pl @@ -8,12 +8,14 @@ # # # Change history +# 20200904 - MITRE updates +# 20200525 - updated date output format # 20151203 - created # # References # http://blog.backslasher.net/setting-dynamic-rpc-port-ranges.html # -# Copyright (c) 2015 QAR, LLC +# Copyright (c) 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package dcom; @@ -24,9 +26,11 @@ package dcom; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - category => "system config", - version => 20151203); + MITRE => "", + output => "report", + category => "config", + version => 20200904); + my $VERSION = getVersion(); # Functions # @@ -43,8 +47,8 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching dcom v.".$VERSION); - ::rptMsg("dcom v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + ::rptMsg("dcom v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key; @@ -53,7 +57,7 @@ sub pluginmain { my $key_path = "Microsoft\\Rpc\\Internet"; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); eval { diff --git a/thirdparty/rr-full/plugins/ddm.pl b/thirdparty/rr-full/plugins/ddm.pl deleted file mode 100644 index e188d7b9af0..00000000000 --- a/thirdparty/rr-full/plugins/ddm.pl +++ /dev/null @@ -1,84 +0,0 @@ -#----------------------------------------------------------- -# ddm.pl -# -# History: -# 20081129 - created -# -# Note - Not really sure what this is for or could be used for, other -# than to show devices that had been connected to the system -# -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package ddm; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20081129); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get DDM data from Control Subkey"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching ddm v.".$VERSION); - ::rptMsg("ddm v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# Code for System file, getting CurrentControlSet - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - - my $key_path = $ccs."\\Control\\DDM"; - my $key; - my %dev; - if ($key = $root_key->get_subkey($key_path)) { - my @subkeys = $key->get_list_of_subkeys(); - if (scalar (@subkeys) > 0) { - foreach my $s (@subkeys) { - my $name = $s->get_name(); - my $tag = (split(/\./,$name,2))[1]; - $dev{$tag}{timestamp} = $s->get_timestamp(); - eval { - $dev{$tag}{make} = $s->get_value("MakeName")->get_data(); - $dev{$tag}{model} = $s->get_value("ModelName")->get_data(); - }; - } - foreach my $d (sort keys %dev) { - ::rptMsg(gmtime($dev{$d}{timestamp})."Z Device\.".$d." ".$dev{$d}{make}." ".$dev{$d}{model}); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); -# ::logMsg($key_path." not found."); - } - } - else { - ::logMsg("Current value not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ddo.pl b/thirdparty/rr-full/plugins/ddo.pl index 2abfb977460..474a10ea76b 100644 --- a/thirdparty/rr-full/plugins/ddo.pl +++ b/thirdparty/rr-full/plugins/ddo.pl @@ -1,13 +1,19 @@ #----------------------------------------------------------------------------------------- # ddo.pl +# Registry entries created by devices that support device stage # # History +# 20200904 - MITRE updates +# 20200525 - updated date output format # 20140414 - created # -# Registry entries created by devices that support device stage -# Reference: http://nicoleibrahim.com/part-4-usb-device-research-usb-first-insert-results/ +# +# Reference: +# http://nicoleibrahim.com/part-4-usb-device-research-usb-first-insert-results/ # -# # Author: Jasmine Chua, babymagic06@gmail.com +# Original Author: Jasmine Chua, babymagic06@gmail.com +# copyright 2020 QAR, LLC +# Updating author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------------------------------------- package ddo; use strict; @@ -16,8 +22,10 @@ package ddo; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20140414); + category => "devices", + MITRE => "", + output => "report", + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -34,8 +42,8 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching DDO v.".$VERSION); - ::rptMsg("DDO v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("DDO v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -45,7 +53,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("DeviceDisplayObjects"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time: ".gmtime($key->get_timestamp())." (UTC)\n"); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z\n"); my @vals; eval { @vals = $key->get_list_of_values(); diff --git a/thirdparty/rr-full/plugins/decaf.pl b/thirdparty/rr-full/plugins/decaf.pl deleted file mode 100644 index c39cb86d271..00000000000 --- a/thirdparty/rr-full/plugins/decaf.pl +++ /dev/null @@ -1,96 +0,0 @@ -#----------------------------------------------------------- -# decaf.pl -# Extracts the AcceptedEULA value for DECAF -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# Detect and Eliminate Computer Acquired Forensics -# http://en.wikipedia.org/wiki/DECAF -# -# Copyright (c) 2011-02-10 Brendan Coles -#----------------------------------------------------------- -# Require # -package decaf; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110210); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts the EULA value for DECAF."; -} -sub getRefs { - my %refs = ("Detect and Eliminate Computer Acquired Forensics:" => - "http://en.wikipedia.org/wiki/DECAF"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching decaf v.".$VERSION); - ::rptMsg("decaf v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\DECAFme"; - - # If # DECAF path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("DECAF"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from DECAF registry path # - my %keys; - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for DECAF registry path # - foreach my $v (@vals) { - ::rptMsg($v->get_name()." -> ".$v->get_data()); - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # DECAF isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/defbrowser.pl b/thirdparty/rr-full/plugins/defbrowser.pl deleted file mode 100644 index 569a9fecc3b..00000000000 --- a/thirdparty/rr-full/plugins/defbrowser.pl +++ /dev/null @@ -1,80 +0,0 @@ -#----------------------------------------------------------- -# defbrowser.pl -# Get default browser information - check #1 can apply to HKLM -# as well as to HKCU -# -# Change History: -# 20091116 - Added Check #1 -# 20081105 - created -# -# copyright 2009 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package defbrowser; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20091116); - -sub getConfig{return %config} - -sub getShortDescr { - return "Gets default browser setting from HKLM"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching defbrowser v.".$VERSION); - ::rptMsg("defbrowser v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Clients\\StartMenuInternet"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Default Browser Check #1"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $browser = $key->get_value("")->get_data(); - ::rptMsg("Default Browser : ".$browser); - } - else { - ::rptMsg($key_path." not found."); - } - - ::rptMsg(""); - - $key_path = "Classes\\HTTP\\shell\\open\\command"; - if (my $key = $root_key->get_subkey($key_path)) { - ::rptMsg("Default Browser Check #2"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $browser; - eval { - $browser = $key->get_value("")->get_data(); - }; - if ($@) { - ::rptMsg("Error locating default browser setting."); - } - else { - ::rptMsg("Default Browser = ".$browser); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/defender.pl b/thirdparty/rr-full/plugins/defender.pl new file mode 100644 index 00000000000..bf4abe17e88 --- /dev/null +++ b/thirdparty/rr-full/plugins/defender.pl @@ -0,0 +1,199 @@ +#----------------------------------------------------------- +# defender.pl +# +# Get Windows Defender settings from the Software hive +# +# Change history +# 20211027 - added Controls key check (were signatures removed?) +# 20210812 - added ThreatFileHashLogging check +# 20210705 - "Controlled Folder Access" update; assoc. w/ Kaseya REvil attack +# 20200904 - MITRE updates +# 20200427 - updated output date format +# 20200409 - updates +# 20191202 - updated to include Defender settings affected by Clop ransomware +# 20191018 - created +# +# References +# *Observed a case where a folder containing malware was added to Exclusions, causing +# Defender to bypass and not detect/quarantine the malware +# https://www.bleepingcomputer.com/news/security/clop-ransomware-tries-to-disable-windows-defender-malwarebytes/ +# +# https://attack.mitre.org/techniques/T1562/001/ +# +# Copyright 2021 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package defender; +use strict; + +my %config = (hive => "software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1562\.001", + category => "defense evasion", + version => 20211027); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Get Windows Defender settings"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching defender v.".$VERSION); + ::rptMsg("defender v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = "Microsoft\\Windows Defender"; + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + foreach my $i ("Paths","Extensions","Processes","TemporaryPaths","IpAddresses") { + eval { + if (my $excl = $key->get_subkey("Exclusions\\".$i)) { + my @vals = $excl->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg("Exclusions\\".$i." key LastWrite time: ".::format8601Date($excl->get_timestamp())."Z"); + foreach my $v (@vals) { + ::rptMsg(sprintf " %-50s %2d",$v->get_name(),$v->get_data()); + } + ::rptMsg(""); + } + } + }; + } + } + else { + ::rptMsg($key_path." not found."); + } +# Check Tamper Protection + if ($key = $root_key->get_subkey($key_path)) { + + eval { + my $tamp = $key->get_subkey("Features")->get_value("TamperProtection")->get_data(); + ::rptMsg("TamperProtection value = ".$tamp); + ::rptMsg("If TamperProtection value = 1, it's disabled"); + }; + } + +# 20211026 - check Signatures +# https://m365internals.com/2021/08/06/dfir-windows-and-active-directory-attacks-and-persistence/ +# Possible command: "C:\Program Files\Windows Defender\MpCmdRun.exe" -RemoveDefinitions -All + if ($key = $root_key->get_subkey($key_path."\\Features\\Controls")) { + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%04d %04d",$v->get_name(),$v->get_data()); + } + } + else { + ::rptMsg("The ".$key_path."\\Features\\Controls key has no values, indicating that signatures may have been removed."); + } + } +# + my $path_str = "Microsoft\\Windows Defender"; + my @key_paths = ($path_str, "Policies\\".$path_str); +# my $key_path = "Policies\\Microsoft\\Windows Defender"; + foreach my $key_path (@key_paths) { + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())); + + eval { + if (my $as = $key->get_value("DisableAntiSpyware")->get_data()) { + ::rptMsg("DisableAntiSpyware value = ".$as) if ($as == 1); + ::rptMsg(""); + } + }; + +# added 20210812 +# https://admx.help/?Category=SystemCenterEndpointProtection&Policy=Microsoft.Policies.Antimalware::system_center_endpoint_protection_threatfile_hashlogging + eval { + if (my $as = $key->get_value("ThreatFileHashLogging")->get_data()) { + ::rptMsg("ThreatFileHashLogging value = ".$as) if ($as == 1); + ::rptMsg(""); + } + }; + + if (my $block = $key->get_subkey("MpEngine")) { + eval { + if (my $b = $block->get_value("MpCloudBlockLevel")->get_data()) { + ::rptMsg("Key path: ".$key_path."\\MpEngine"); + ::rptMsg("LastWrite Time: ".::format8601Date($block->get_timestamp())."Z"); + ::rptMsg("MpEngine\\MpCloudBlockLevel value = ".$b); + ::rptMsg(""); + } + }; + } + + if (my $spy = $key->get_subkey("Spynet")) { + eval { + if (my $s = $spy->get_value("SpynetReporting")->get_data()) { + ::rptMsg("Key path: ".$key_path."\\Spynet"); + ::rptMsg("LastWrite Time: ".::format8601Date($spy->get_timestamp())."Z"); + ::rptMsg("Spynet\\SpynetReporting value = ".$s); + ::rptMsg(""); + } + }; + + eval { + if (my $samp = $spy->get_value("SubmitSamplesConsent")->get_data()) { + ::rptMsg("Spynet\\SubmitSamplesConsent value = ".$samp); + ::rptMsg(""); + } + }; + } + + if (my $t = $key->get_subkey("Real-Time Protection")) { + my @vals = ("DisableBehaviorMonitoring","DisableOnAccessProtection","DisableRealtimeMonitoring", + "DisableScanOnRealtimeEnable"); + ::rptMsg("Key path: ".$key_path."\\Real-Time Protection"); + ::rptMsg("LastWrite Time: ".::format8601Date($t->get_timestamp())."Z"); + foreach my $val (@vals) { + eval { + my $v = $t->get_value($val)->get_data(); + ::rptMsg($val." value = ".$v); + }; + } + ::rptMsg(""); + } +# Controlled Folder Access +# https://www.tenforums.com/tutorials/113380-how-enable-disable-controlled-folder-access-windows-10-a.html + if (my $c = $key->get_subkey("Windows Defender Exploit Guard")) { + ::rptMsg("Key path: ".$key_path."\\Windows Defender Exploit Guard"); + ::rptMsg("LastWrite Time: ".::format8601Date($c->get_timestamp())."Z"); + eval { + my $f = $c->get_value("Controlled Folder Access")->get_data(); + ::rptMsg("\"Controlled Folder Access\" value: ".$f); + ::rptMsg(""); + ::rptMsg("0 - Disabled"); + ::rptMsg("1 - Enabled"); + }; + ::rptMsg("\"Controlled Folder Access\" value not found") if ($@); + + } + } + else { +# ::rptMsg($key_path." not found."); + } + } +} + +1; diff --git a/thirdparty/rr-full/plugins/defenderautologger.pl b/thirdparty/rr-full/plugins/defenderautologger.pl new file mode 100644 index 00000000000..2610132003e --- /dev/null +++ b/thirdparty/rr-full/plugins/defenderautologger.pl @@ -0,0 +1,75 @@ +#----------------------------------------------------------- +# defender-autologger.pl +# Get WMI\AutoLogger settings for Defender +# +# Change history +# 20220303 - created +# +# References +# https://thedfirreport.com/2021/10/18/icedid-to-xinglocker-ransomware-in-24-hours/ +# +# MITRE: https://attack.mitre.org/techniques/T1562/002/ +# +# copyright 2022 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package defenderautologger; + +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + category => "defense evasion", + MITRE => "T1562\.001", + version => 20220303); + +sub getConfig{return %config} +sub getShortDescr { + return "Get Defender AutoLogger settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching defender-autologger v.".$VERSION); + ::rptMsg("defender-autologger v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE ATT&CK technique: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\WMI\\AutoLogger"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $a = $key->get_subkey("DefenderApiLogger")->get_value("Start")->get_data(); + ::rptMsg("DefenderApiLogger Start value: ".$a); + }; + + eval { + my $a = $key->get_subkey("DefenderAuditLogger")->get_value("Start")->get_data(); + ::rptMsg("DefenderAuditLogger Start value: ".$a); + }; + + ::rptMsg(""); + ::rptMsg("Analysis Tip: Threat actors, such as XingLocker, set the values to \"0\" to disable Defender logging."); + ::rptMsg(""); + ::rptMsg("Ref: https://thedfirreport.com/2021/10/18/icedid-to-xinglocker-ransomware-in-24-hours/"); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/defrag.pl b/thirdparty/rr-full/plugins/defrag.pl new file mode 100644 index 00000000000..fc94ff2e7ef --- /dev/null +++ b/thirdparty/rr-full/plugins/defrag.pl @@ -0,0 +1,72 @@ +#----------------------------------------------------------- +# defrag.pl +# +# History: +# 20201130 - created +# +# References: +# +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package defrag; +use strict; + +my %config = (hive => "system", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1027", + output => "report", + version => 20201130); + +sub getConfig{return %config} +sub getShortDescr { + return "Get Defrag LastRun value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching defrag v.".$VERSION); + ::rptMsg("defrag v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\Session Manager\\Configuration Manager\\Defrag"; + my $key = (); + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $last = $key->get_value("LastRun")->get_data(); + ::rptMsg("LastRun value : ".$last); + }; + ::rptMsg("LastRun value not found.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Defrag is very often run automatically on systems."); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/del.pl b/thirdparty/rr-full/plugins/del.pl index fdb82e73b3a..2825afc1a45 100644 --- a/thirdparty/rr-full/plugins/del.pl +++ b/thirdparty/rr-full/plugins/del.pl @@ -4,6 +4,8 @@ # # # Change history +# 20200904 - MITRE updates +# 20200515 - updated date output format # 20190506 - updated # 20140807 - created # @@ -11,20 +13,22 @@ # https://metacpan.org/pod/Parse::Win32Registry # https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md # -# -# copyright 2019 QAR, LLC +# https://attack.mitre.org/techniques/T1485/ +# +# copyright 2020 QAR, LLC # Author: H. Carvey #----------------------------------------------------------- package del; use strict; -my %config = (hive => "All", +my %config = (hive => "all", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, + output => "report", + MITRE => "T1485", category => "deleted", - version => 20190506); + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -58,7 +62,9 @@ sub pluginmain { my $reg = Parse::Win32Registry->new($file); ::logMsg("Launching del v.".$VERSION); ::rptMsg("del v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $entry_iter = $reg->get_entry_iterator; while (defined(my $entry = $entry_iter->get_next)) { @@ -146,7 +152,7 @@ sub parseKeyNode { $name = substr($data,$ofs + 0x4c,$len_name); ::rptMsg("Key name: ".$name); } - ::rptMsg("Key LastWrite time = ".gmtime($lw)." UTC"); + ::rptMsg("Key LastWrite time = ".::format8601Date($lw)."Z"); ::rptMsg(sprintf "Offset to parent: 0x%x",$parent_ofs); } } diff --git a/thirdparty/rr-full/plugins/del_tln.pl b/thirdparty/rr-full/plugins/del_tln.pl index 3a459766630..9922daa3cc6 100644 --- a/thirdparty/rr-full/plugins/del_tln.pl +++ b/thirdparty/rr-full/plugins/del_tln.pl @@ -4,6 +4,7 @@ # # # Change history +# 20200904 - MITRE updates # 20190506 - updated # 20140807 - created # @@ -18,13 +19,14 @@ package del_tln; use strict; -my %config = (hive => "All", +my %config = (hive => "all", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, + output => "tln", + MITRE => "T1485", category => "deleted", - version => 20190506); + version => 20200904); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/denydeviceids.pl b/thirdparty/rr-full/plugins/denydeviceids.pl new file mode 100644 index 00000000000..72a29975a15 --- /dev/null +++ b/thirdparty/rr-full/plugins/denydeviceids.pl @@ -0,0 +1,89 @@ +#----------------------------------------------------------- +# denydeviceids +# +# Change history: +# 20221023 - created +# +# Ref: +# https://superuser.com/questions/1189380/is-there-any-way-to-control-device-installation-restrictions-via-the-registry +# https://twitter.com/CyberRaiju/status/1584119443860647940 +# https://twitter.com/InfosecRDM/status/803041636506894336 (2016) +# +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package denydeviceids; +use strict; + +my %config = (hive => "Software", + category => "initial access", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1200", + version => 20221023); + +sub getConfig{return %config} +sub getShortDescr { + return "Check DenyDeviceIDs settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching denydeviceids v.".$VERSION); + ::rptMsg("denydeviceids v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my $key_path = ('Policies\\Microsoft\\Windows\\DeviceInstall\\Restrictions'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = ("DenyDeviceIDs","DenyDeviceIDsRetroactive"); + foreach my $v (@vals) { + eval { + my $x = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-25s %-4s",$v,$x); + }; + } + ::rptMsg(""); + + if (my $k = $key->get_subkey("DenyDeviceIDs")) { + my @vals = $k->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-5s %-45s",$v->get_name(),$v->get_data()); + } + } + } + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: "); + ::rptMsg(""); + ::rptMsg(""); + ::rptMsg("DenyDeviceIDsRetroactive corresponds to \"Also apply to matching devices that are already installed\" policy."); + ::rptMsg(""); + ::rptMsg(""); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/dependency_walker.pl b/thirdparty/rr-full/plugins/dependency_walker.pl deleted file mode 100644 index e636e432097..00000000000 --- a/thirdparty/rr-full/plugins/dependency_walker.pl +++ /dev/null @@ -1,95 +0,0 @@ -#----------------------------------------------------------- -# dependency_walker.pl -# Extracts Recent File List for Dependency Walker. -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# Dependency Walker Homepage -# http://www.dependencywalker.com/ -# -# Copyright (c) 2011-02-04 Brendan Coles -#----------------------------------------------------------- -# Require # -package dependency_walker; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110204); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts Recent File List for Dependency Walker."; -} -sub getRefs { - my %refs = ("Dependency Walker Homepage:" => - "http://www.dependencywalker.com/"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching dependency_walker v.".$VERSION); - ::rptMsg("dependency_walker v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\Microsoft\\Dependency Walker\\Recent File List"; - - # If # Dependency Walker path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Dependency Walker"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Dependency Walker registry path # - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Dependency Walker registry path # - foreach my $v (@vals) { - ::rptMsg($v->get_name()." -> ".$v->get_data()); - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Dependency Walker isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/devclass.pl b/thirdparty/rr-full/plugins/devclass.pl index 69a8533ad97..869310aa2d2 100644 --- a/thirdparty/rr-full/plugins/devclass.pl +++ b/thirdparty/rr-full/plugins/devclass.pl @@ -4,22 +4,26 @@ # hive (Disks and Volumes GUIDs) # # Change History: +# 20200904 - MITRE updates +# 20200525 - updated date output format # 20130630 - added additional device class check # 20100901 - spelling error in output corrected # 20080331 - created # -# copyright 2013-2014 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package devclass; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "", + category => "devices", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20130630); + output => "report", + version => 20200904); sub getConfig{return %config} @@ -56,7 +60,8 @@ sub pluginmain { return } # Get devices from the Disk GUID - $key_path = $ccs."\\Control\\DeviceClasses\\{53f56307-b6bf-11d0-94f2-00a0c91efb8b}"; + my $key_path = $ccs."\\Control\\DeviceClasses\\{53f56307-b6bf-11d0-94f2-00a0c91efb8b}"; + my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("DevClasses - Disks"); ::rptMsg($key_path); @@ -73,7 +78,7 @@ sub pluginmain { } foreach my $t (reverse sort {$a <=> $b} keys %disks) { - ::rptMsg(gmtime($t)." (UTC)"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $item (@{$disks{$t}}) { ::rptMsg(" $item"); } @@ -89,7 +94,8 @@ sub pluginmain { } ::rptMsg(""); # Get devices from the Volume GUID - $key_path = $ccs."\\Control\\DeviceClasses\\{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}"; + my $key_path = $ccs."\\Control\\DeviceClasses\\{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}"; + my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("DevClasses - Volumes"); ::rptMsg($key_path); @@ -106,7 +112,7 @@ sub pluginmain { } foreach my $t (reverse sort {$a <=> $b} keys %vols) { - ::rptMsg(gmtime($t)." (UTC)"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $item (@{$vols{$t}}) { ::rptMsg(" ParentIdPrefix: ".$item); } @@ -131,11 +137,11 @@ sub pluginmain { my @n = split(/#/,$name); if ($n[3] eq "USB") { ::rptMsg("Device : ".$n[4]); - ::rptMsg("LastWrite: ".gmtime($lw)." UTC"); + ::rptMsg("LastWrite: ".::format8601Date($lw)."Z"); } elsif ($n[3] eq "WpdBusEnumRoot") { ::rptMsg("Device : ".$n[8]." SN: ".$n[9]); - ::rptMsg("LastWrite: ".gmtime($lw)." UTC"); + ::rptMsg("LastWrite: ".::format8601Date($lw)."Z"); } else {} @@ -148,4 +154,4 @@ sub pluginmain { } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/devicecache.pl b/thirdparty/rr-full/plugins/devicecache.pl new file mode 100644 index 00000000000..7efee0ad711 --- /dev/null +++ b/thirdparty/rr-full/plugins/devicecache.pl @@ -0,0 +1,100 @@ +#----------------------------------------------------------- +# devicecache.pl +# +# +# Change history +# 20221018 - created +# +# References +# https://www.istrosec.com/blog/windows-10-timeline/ +# https://www.forensicfocus.com/webinars/windows-10-activity-timeline-an-investigators-gold-mine/ +# +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package devicecache; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20221018); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets DeviceCache entries"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %types = (1 => "XBox One", + 6 => "Apple iPhone", + 7 => "Apple iPad", + 8 => "Android Device", + 9 => "Windows 10 Desktop", + 11 => "Windows 10 Phone", + 12 => "Linux Device", + 13 => "Windows IoT", + 14 => "Surface Hub", + 15 => "Windows Laptop"); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching devicecache v.".$VERSION); + ::rptMsg("devicecache v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\TaskFlow\\DeviceCache'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("DeviceCache"); + ::rptMsg($key_path); +# ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @subkeys = $key->get_list_of_subkeys(); + + my @vals = ("DeviceName","DeviceMake","DeviceModel"); + + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg("Key: ".$s->get_name()); + ::rptMsg("LastWrite Time ".::format8601Date($s->get_timestamp())."Z"); + ::rptMsg(""); + foreach my $v (@vals) { + eval { + my $x = $s->get_value($v)->get_data(); + ::rptMsg(sprintf "%-15s %-25s",$v,$x); + }; + } + + eval { + my $x = $s->get_value("DeviceType")->get_data(); + ::rptMsg(sprintf "%-15s %-25s","DeviceType",$types{$x}); + }; + + ::rptMsg(""); + } + ::rptMsg("Analysis Tip: Multiple subkeys beneath the DeviceCache key may indicate that the user loggged into multiple"); + ::rptMsg("devices using the same Microsoft ID."); + ::rptMsg(""); + ::rptMsg("Ref: https://cellebrite.com/en/exploring-the-windows-activity-timeline-part-2-syncing-across-devices/"); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/deviceguard.pl b/thirdparty/rr-full/plugins/deviceguard.pl new file mode 100644 index 00000000000..5d4da67362a --- /dev/null +++ b/thirdparty/rr-full/plugins/deviceguard.pl @@ -0,0 +1,118 @@ +#----------------------------------------------------------- +# deviceguard.pl +# +# History: +# 20201025 - created +# +# References: +# https://docs.microsoft.com/en-us/windows/security/identity-protection/credential-guard/credential-guard-manage +# https://docs.microsoft.com/en-us/windows/security/threat-protection/device-guard/enable-virtualization-based-protection-of-code-integrity +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package deviceguard; +use strict; + +my %config = (hive => "System, Software", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1562\.001", + version => 20201025); + +sub getConfig{return %config} +sub getShortDescr { + return "Check Device Guard settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching deviceguard v.".$VERSION); + ::rptMsg("deviceguard v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + + my %g = ::guessHive($hive); + my $guess = (keys %g)[0]; + + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# hive + if ($guess eq "system") { + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\DeviceGuard"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-40s %-20s",$v->get_name(),$v->get_data()); + } + } + } + else { + ::rptMsg($key_path." not found."); + } + + $key_path = $ccs."\\Control\\DeviceGuard\\Scenarios"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + ::rptMsg(""); + ::rptMsg("Scenarios"); + foreach my $s (@subkeys) { + ::rptMsg(" ".$s->get_name()); + ::rptMsg(" LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + my @vals = $s->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf " %-25s %-10s",$v->get_name(),$v->get_data()); + } + } + ::rptMsg(""); + } + } + } + else { + ::rptMsg($key_path." not found."); + } + + } + elsif ($guess eq "software") { + my $key_path = "Policies\\Microsoft\\Windows\\DeviceGuard"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-40s %-20s",$v->get_name(),$v->get_data()); + } + } + } + else { + ::rptMsg($key_path." not found."); + } + } + +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/dfrg.pl b/thirdparty/rr-full/plugins/dfrg.pl deleted file mode 100644 index 327f5838156..00000000000 --- a/thirdparty/rr-full/plugins/dfrg.pl +++ /dev/null @@ -1,65 +0,0 @@ -#----------------------------------------------------------- -# dfrg.pl -# Gets contents of Dfrg\BootOptimizeFunction key -# -# Change history: -# 20110321 - created -# -# References -# http://technet.microsoft.com/en-us/library/cc784391%28WS.10%29.aspx -# -# copyright 2011 Quantum Analytics Research, LLC (keydet89@yahoo.com) -#----------------------------------------------------------- -package dfrg; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20110321); - -sub getConfig{return %config} - -sub getShortDescr { - return "Gets content of Dfrg BootOptim. key"; -} -sub getDescr{} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching dfrg v.".$VERSION); - ::rptMsg("dfrg v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Dfrg\\BootOptimizeFunction"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Dfrg"); - ::rptMsg($key_path); - ::rptMsg(""); - - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - ::rptMsg(sprintf "%-20s %-20s",$v->get_name(),$v->get_data()); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/diag_sr.pl b/thirdparty/rr-full/plugins/diag_sr.pl deleted file mode 100644 index 47ae183a962..00000000000 --- a/thirdparty/rr-full/plugins/diag_sr.pl +++ /dev/null @@ -1,81 +0,0 @@ -#----------------------------------------------------------- -# diag_sr.pl -# -# History: -# 20120515: created -# -# -# copyright 2012 Quantum Analytics Research, LLC -# Author: H. Carvey -#----------------------------------------------------------- -package diag_sr; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20120515); - -sub getConfig{return %config} -sub getShortDescr { - return "Get Diag\\SystemRestore values and data"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching diag_sr v.".$VERSION); - ::rptMsg("diag_sr v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my ($current,$ccs); - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - my $volsnap_path = $ccs."\\Services\\VSS\\Diag\\SystemRestore"; - my $volsnap; - if ($volsnap = $root_key->get_subkey($volsnap_path)) { - my @vals = $volsnap->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - my $t = gmtime(parseData($v->get_data())); - - ::rptMsg(sprintf "%-25s %-50s",$t,$name); - - } - } - else { - ::rptMsg($volsnap_path." has no values."); - } - } - else { - ::rptMsg($volsnap_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub parseData { - my $data = shift; - my ($t0,$t1) = unpack("VV",substr($data,0x08,8)); - return ::getTime($t0,$t1); -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/diagnostics.pl b/thirdparty/rr-full/plugins/diagnostics.pl new file mode 100644 index 00000000000..c30231fe914 --- /dev/null +++ b/thirdparty/rr-full/plugins/diagnostics.pl @@ -0,0 +1,77 @@ +#----------------------------------------------------------- +# diagnostics +# +# Change History: +# 20220531 - created +# +# References +# https://twitter.com/gentilkiwi/status/1531384447219781634 +# https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.ScriptedDiagnostics::ScriptedDiagnosticsExecutionPolicy +# +# copyright 2022 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package diagnostics; +use strict; + +my %config = (hive => "software", + MITRE => "T1203", + category => "execution", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220531); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get ScriptedDiagnostics settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching diagnostics v.".$VERSION); + ::rptMsg("diagnostics v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key_path = "Policies\\Microsoft\\Windows\\ScriptedDiagnostics"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $e = $key->get_value("EnableDiagnostics")->get_data(); + ::rptMsg("EnableDiagnostics value: ".$e); + + }; + + eval { + my $v = $key->get_value("ValidateTrust")->get_data(); + ::rptMsg("ValidateTrust value : ".$v); + + }; + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: \"EnableDiagnostics\" set to \"0\" disables user access to run the troubleshooting wizard."); + ::rptMsg("This is a work-around that MS confirmed prevents the MSDT-Follina vulnerability from 27 May 2022."); + ::rptMsg("#CVE-2022-30190"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/direct.pl b/thirdparty/rr-full/plugins/direct.pl index a4de49207a3..7e87f149fb1 100644 --- a/thirdparty/rr-full/plugins/direct.pl +++ b/thirdparty/rr-full/plugins/direct.pl @@ -5,21 +5,29 @@ # looks to see if there is a MostRecentApplication subkey; if there is, it # then tries to retrieve the "Name" value/data # +# Ref: +# https://twitter.com/SBousseaden/status/1171461656724955143 +# # History: +# 20200904 - MITRE updates +# 20200515 - updated date output format +# 20190911 - added ref # 20120513 - created # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2019-2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package direct; use strict; -my %config = (hive => "Software", +my %config = (hive => "software", hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120513); + MITRE => "", + output => "report", + category => "program execution", + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -55,7 +63,7 @@ sub pluginmain { eval { my $app; $app = $s->get_subkey("MostRecentApplication"); - my $app_lw = gmtime($app->get_timestamp()); + my $app_lw = ::format8601Date($app->get_timestamp())."Z"; my $app_name = $app->get_value("Name")->get_data(); ::rptMsg(sprintf "%-25s %-50s",$app_lw,$s->get_name()."\\".$app->get_name()." - ".$app_name); diff --git a/thirdparty/rr-full/plugins/direct_tln.pl b/thirdparty/rr-full/plugins/direct_tln.pl index c76dfec3eb2..38955a34efb 100644 --- a/thirdparty/rr-full/plugins/direct_tln.pl +++ b/thirdparty/rr-full/plugins/direct_tln.pl @@ -5,10 +5,15 @@ # looks to see if there is a MostRecentApplication subkey; if there is, it # then tries to retrieve the "Name" value/data # +# Ref: +# https://twitter.com/SBousseaden/status/1171461656724955143 +# # History: +# 20200904 - MITRE updates +# 20190911 - added ref # 20120608 - created # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2019-2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package direct_tln; @@ -18,8 +23,10 @@ package direct_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120608); + MITRE => "", + output => "tln", + category => "program execution", + version => 20200904); sub getConfig{return %config} sub getShortDescr { @@ -36,7 +43,7 @@ sub pluginmain { my $hive = shift; my @keys = ('Microsoft','Wow6432Node\\Microsoft'); - ::rptMsg("Launching direct v.".$VERSION); +# ::rptMsg("Launching direct v.".$VERSION); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; diff --git a/thirdparty/rr-full/plugins/disable445.pl b/thirdparty/rr-full/plugins/disable445.pl new file mode 100644 index 00000000000..4474902f039 --- /dev/null +++ b/thirdparty/rr-full/plugins/disable445.pl @@ -0,0 +1,74 @@ +#----------------------------------------------------------- +# disable445.pl +# +# History: +# 20220921 - created +# +# References: +# https://answers.microsoft.com/en-us/windows/forum/all/windows-10-0x800704cf-error/cb4c3390-9fe9-4a4d-9f1c-c4651007c9b9 +# https://help.adobe.com/en_US/AEMForms/InstallWebSphere/WS1a95df6a070ac5e3-61aa016812fb665f150-7ff8.2.html +# https://social.technet.microsoft.com/Forums/windows/en-US/84084cc8-52f9-40ce-b0b2-539ba2d7eb21/close-port-445-via-registry?forum=w7itprosecurity +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package disable445; +use strict; + +my %config = (hive => "System", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1562\.001", + version => 20220921); + +sub getConfig{return %config} +sub getShortDescr { + return "Determine if SMB over NetBIOS is disabled"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching disable445 v.".$VERSION); + ::rptMsg("disable445 v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my $key; + my $key_path = $ccs."\\Services\\NetBT\\Parameters"; + if ($key = $root_key->get_subkey($key_path)) { + + eval { + my $d = $key->get_value("SMBDeviceEnabled")->get_data(); + ::rptMsg("SMBDeviceEnabled value: ".$d); + + }; + ::rptMsg("SMBDeviceEnabled value not found.") if ($@); + ::rptMsg(""); + ::rptMsg("Analysis Tip: The \"SMBDeviceEnabled\" value controls whether port 445 is open. If the value does not"); + ::rptMsg("exist, or is set to 1, it's enabled. If the value is set to 0, it's disabled. "); + ::rptMsg(""); + ::rptMsg("Ref: https://superuser.com/questions/629648/how-to-disable-feature-that-opened-port-445-on-windows-server"); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/disablelastaccess.pl b/thirdparty/rr-full/plugins/disablelastaccess.pl index 5fb09934ece..789873a046f 100644 --- a/thirdparty/rr-full/plugins/disablelastaccess.pl +++ b/thirdparty/rr-full/plugins/disablelastaccess.pl @@ -2,6 +2,8 @@ # disablelastaccess.pl # # History: +# 20200911 - MITRE updates +# 20200517 - updated date output format # 20181207 - updated for Win10 v.1803 (Maxim, David Cohen) # 20090118 - # @@ -12,17 +14,21 @@ # http://support.microsoft.com/kb/555041 # http://support.microsoft.com/kb/894372 # -# copyright 2008 H. Carvey, keydet89@yahoo.com +# https://attack.mitre.org/techniques/T1564/004/ +# +# copyright 2020 H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package disablelastaccess; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "T1564\.004", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20181207); + output => "report", + version => 20200911); sub getConfig{return %config} @@ -45,8 +51,8 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching disablelastaccess v.".$VERSION); - ::rptMsg("disablelastaccess v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("disablelastaccess v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -68,6 +74,7 @@ sub pluginmain { my @vals = $key->get_list_of_values(); my $found = 0; if (scalar(@vals) > 0) { + ::rptMsg("Key LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); foreach my $v (@vals) { if ($v->get_name() eq "NtfsDisableLastAccessUpdate") { my $dat = $v->get_data(); diff --git a/thirdparty/rr-full/plugins/disablemru.pl b/thirdparty/rr-full/plugins/disablemru.pl index 166c07fbccf..4c504a7db19 100644 --- a/thirdparty/rr-full/plugins/disablemru.pl +++ b/thirdparty/rr-full/plugins/disablemru.pl @@ -1,27 +1,39 @@ #----------------------------------------------------------- # disablemru.pl +# Check config settings that could be used to minimize/obviate artifacts +# associated with user activity; while most of the artifacts are likely on +# a per-user basis, included check of Software hive, just in case # # Change history +# 20230710 - updated output +# 20230106 - added check to disable UserAssist +# 20200911 - MITRE updates +# 20190924 - updated to include Software hive # 20180807 - created # # References # *Provided in the code +# https://blog.didierstevens.com/2007/09/25/update-disabling-userassist-logging-for-windows-vista/ +# https://attack.mitre.org/techniques/T1562/001/ # -# copyright 2018 H. Carvey +# copyright 2023 QAR,LLC +# author: H. Carvey keydet89@yahoo.com #----------------------------------------------------------- package disablemru; use strict; -my %config = (hive => "NTUSER\.DAT", +my %config = (hive => "NTUSER\.DAT, Software", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180807); + category => "defense evasion", + MITRE => "T1562\.001", + output => "report", + version => 20230710); sub getConfig{return %config} sub getShortDescr { - return "Checks settings disabling user's MRUs"; + return "Checks settings disabling user's MRUs, UserAssist, JumpLists"; } sub getDescr{} sub getRefs {} @@ -32,70 +44,148 @@ sub getShortDescr { sub pluginmain { my $class = shift; - my $ntuser = shift; + my $hive = shift; ::logMsg("Launching disablemru v.".$VERSION); - ::rptMsg("disablemru v.".$VERSION); # banner - ::rptMsg("- ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); + ::rptMsg("disablemru v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - + my $key; + my $key_path; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my $key_path = (); + if ($hive_guess eq "software") { + $key_path = "Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"; + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"; + } + else {} + # Windows 10 JumpLists # https://winaero.com/blog/disable-jump-lists-windows-10/ - my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced'; - my $key; +# https://ss64.com/nt/syntax-reghacks.html + ::rptMsg("Query the ".$key_path." key for the Start_TrackDocs value to"); + ::rptMsg("determine if JumpLists are disabled, and the Start_TrackProgs and Start_TrackEnabled values to determine "); + ::rptMsg("if UserAssist entries are disabled."); + ::rptMsg(""); + ::rptMsg("Ref: https://book.hacktricks.xyz/generic-methodologies-and-resources/basic-forensic-methodology/anti-forensic-techniques"); if ($key = $root_key->get_subkey($key_path)) { eval { my $start = $key->get_value("Start_TrackDocs")->get_data(); - + ::rptMsg($key_path." Start_TrackDocs value = ".$start); + }; + ::rptMsg($key_path." Start_TrackDocs value not found.") if ($@); + +# https://book.hacktricks.xyz/generic-methodologies-and-resources/basic-forensic-methodology/anti-forensic-techniques +# https://github.com/githubfoam/forensics-experience/blob/master/README.md +# https://blog.didierstevens.com/2007/09/08/disabling-userassist-logging-for-windows-vista/ +# The following two values together will disable populating UserAssist entries + eval { + my $s = $key->get_value("Start_TrackProgs")->get_data(); + ::rptMsg($key_path." Start_TrackProgs value = ".$s); + ::rptMsg("0 - disabled"); + ::rptMsg("1 - enabled"); }; + ::rptMsg($key_path." Start_TrackProgs value not found.") if ($@); + eval { + my $s = $key->get_value("Start_TrackEnabled")->get_data(); + ::rptMsg($key_path." Start_TrackEnabled value = ".$s); + ::rptMsg("0 - disabled"); + ::rptMsg("1 - enabled"); + }; + ::rptMsg($key_path." Start_TrackEnabled value not found.") if ($@); } else { - ::rptMsg($key_path." not found."); + ::rptMsg($key_path." key not found."); } - + +# Note: For below code, left Software hive check in place on purpose, even though it's probably not necessary + my $key_path = (); + if ($hive_guess eq "software") { + $key_path = "Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32"; + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32"; + } + else {} + ::rptMsg(""); # https://answers.microsoft.com/en-us/windows/forum/windows_xp-security/how-do-i-disable-most-recent-used-list-in-run/dab29225-4222-4412-8bc3-0516cee65a78 - $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32'; + ::rptMsg("Query NoFileMRU value in".$key_path." key to determine if"); + ::rptMsg("maintaining file MRU is disabled."); + ::rptMsg(""); + ::rptMsg("Ref: https://admx.help/?Category=Windows_11_2022&Policy=Microsoft.Policies.WindowsExplorer::NoFileMRU"); if ($key = $root_key->get_subkey($key_path)) { eval { my $file = $key->get_value("NoFileMRU")->get_data(); + ::rptMsg($key_path." NoFileMRU value = ".$file); if ($file == 1) { ::rptMsg("NoFileMRU = 1; Recording for Comdlg32 disabled"); } }; + ::rptMsg($key_path." NoFileMRU value not found.") if ($@); } else { - ::rptMsg($key_path." not found."); + ::rptMsg($key_path." key not found."); } + ::rptMsg(""); + my $key_path = (); + if ($hive_guess eq "software") { + $key_path = "Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"; + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"; + } + else {} # http://systemmanager.ru/win2k_regestry.en/92853.htm - $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer'; +# https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.StartMenu::NoRecentDocsMenu + ::rptMsg("Query NoRecentDocsMenu value in ".$key_path." key to determine if"); + ::rptMsg("maintaining recent docs MRU is disabled."); + ::rptMsg(""); + ::rptMsg("Ref: https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.StartMenu::NoRecentDocsMenu"); + ::rptMsg(""); if ($key = $root_key->get_subkey($key_path)) { - my $mru; + eval { - $mru = $key->get_value("NoRecentDocsMenu")->get_data(); + my $mru = $key->get_value("NoRecentDocsMenu")->get_data(); + ::rptMsg($key_path." NoRecentDocsMenu value = ".$mru); if ($mru == 1) { ::rptMsg("NoRecentDocsMenu = 1; No Documents menu in Start menu"); } }; - + ::rptMsg($key_path." NoRecentDocsMenu value not found.") if ($@); + eval { - $mru = $key->get_value("ClearRecentDocsOnExit")->get_data(); + my $mru = $key->get_value("ClearRecentDocsOnExit")->get_data(); + ::rptMsg($key_path." ClearRecentDocsOnExit value = ".$mru); if ($mru == 1) { ::rptMsg("ClearRecentDocsOnExit = 1; RecentDocs cleared on exit"); } }; + ::rptMsg($key_path." ClearRecentDocsOnExit value not found.") if ($@); eval { - $mru = $key->get_value("NoRecentDocsHistory")->get_data(); + my $mru = $key->get_value("NoRecentDocsHistory")->get_data(); + ::rptMsg($key_path." NoRecentDocsHistory value = ".$mru); if ($mru == 1) { ::rptMsg("NoRecentDocsHistory = 1; No RecentDocs history"); } }; - + ::rptMsg($key_path." NoRecentDocsHistory value not found.") if ($@); } else { - ::rptMsg($key_path." not found."); + ::rptMsg($key_path." key not found."); } } diff --git a/thirdparty/rr-full/plugins/disableonedrive.pl b/thirdparty/rr-full/plugins/disableonedrive.pl new file mode 100644 index 00000000000..dbed001afa7 --- /dev/null +++ b/thirdparty/rr-full/plugins/disableonedrive.pl @@ -0,0 +1,65 @@ +#----------------------------------------------------------- +# disableonedrive.pl +# +# Change history: +# 20220614 - created +# +# References: +# https://support.microsoft.com/en-us/office/onedrive-won-t-start-0c158fa6-0cd8-4373-98c8-9179e24f10f2 +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package disableonedrive; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + MITRE => "T1562\.001", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220614); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check DisableFileSyncNGSC value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching disableonedrive v.".$VERSION); + ::rptMsg("disableonedrive v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + my $key_path = "Policies\\Microsoft\\Windows\\OneDrive"; + if ($key = $root_key->get_subkey($key_path)) { + eval { + my $c = $key->get_value("DisableFileSyncNGSC")->get_data(); + ::rptMsg("DisableFileSyncNGSC value: ".$c); + ::rptMsg(""); + ::rptMsg("Analysis Tip: The DisableFileSyncNGSC set to \"1\" will disable OneDrive."); + }; + ::rptMsg($key_path."\\DisableFileSyncNGSC value not found.") if ($@); + + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/disableproxy.pl b/thirdparty/rr-full/plugins/disableproxy.pl new file mode 100644 index 00000000000..05fa3fe4af6 --- /dev/null +++ b/thirdparty/rr-full/plugins/disableproxy.pl @@ -0,0 +1,73 @@ +#----------------------------------------------------------- +# disableproxy.pl +# Get disableproxy settings +# +# Change history: +# 20211025 - created +# +# References: +# https://twitter.com/PythonResponder/status/1451657791970623490 +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey +#----------------------------------------------------------- +package disableproxy; +use strict; + +my %config = (hive => "software", + category => "config", + MITRE => "", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20211025); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get disableproxy settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching disableproxy v.".$VERSION); + ::rptMsg("disableproxy v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my @paths = ("Microsoft\\Windows\\CurrentVersion\\Internet Settings", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + eval { + my $a = $key->get_value("DisableProxyAuthenticationSchemes")->get_data(); + ::rptMsg("DisableProxyAuthenticationSchemes value : ".$a); + ::rptMsg("4 - Disable NTLM"); + }; + } + else { +# ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: A value of 4 indicates that NTLM is disabled"); +# ::rptMsg(""); +# ::rptMsg(""); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/disableremotescm.pl b/thirdparty/rr-full/plugins/disableremotescm.pl new file mode 100644 index 00000000000..6ba07d51f66 --- /dev/null +++ b/thirdparty/rr-full/plugins/disableremotescm.pl @@ -0,0 +1,76 @@ +#----------------------------------------------------------- +# disableremotescm.pl +# Plugin for Registry Ripper; Access System hive file to get the +# computername +# +# Change history +# 20200831 - MITRE updates +# 20200513 - created +# +# References +# https://twitter.com/0gtweet/status/1260213942535757824 +# https://docs.microsoft.com/en-us/windows/win32/services/services-and-rpc-tcp +# +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package disableremotescm; +use strict; + +my %config = (hive => "system", + hasShortDescr => 1, + category => "config", + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20200831); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets DisableRemoteScmEndpoints value from System hive"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching disableremotescm v.".$VERSION); + ::rptMsg("disableremotescm v.".$VERSION); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my ($current,$ccs); + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + $current = $key->get_value("Current")->get_data(); + $ccs = "ControlSet00".$current; + my $cn_path = $ccs."\\Control"; + my $cn; + if ($cn = $root_key->get_subkey($cn_path)) { + eval { + my $dis = $cn->get_value("DisableRemoteScmEndpoints")->get_data(); + ::rptMsg("DisableRemoteScmEndpoints = ".$dis); + }; + ::rptMsg("DisableRemoteScmEndpoints value not found.") if ($@); + } + else { + ::rptMsg($cn_path." not found."); + } + } + else { + ::rptMsg($key_path." not found."); + } + +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/disablesr.pl b/thirdparty/rr-full/plugins/disablesr.pl index 8c598953937..282758d332b 100644 --- a/thirdparty/rr-full/plugins/disablesr.pl +++ b/thirdparty/rr-full/plugins/disablesr.pl @@ -3,22 +3,28 @@ # Gets the value that turns System Restore either on or off # # Change History -# 20120914 +# 20200515 - updated date output format +# 20120914 - created # # References # Registry Keys and Values for the System Restore Utility http://support.microsoft.com/kb/295659 # -# copyright 2012 Corey Harrell (Journey Into Incident Response) +# https://attack.mitre.org/techniques/T1562/001/ +# +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package disablesr; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "T1562\.001", + category => "defense evasion", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20120914); + output => "report", + version => 20200911); sub getConfig{return %config} @@ -36,8 +42,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching disablesr v.".$VERSION); - ::rptMsg("disablesr v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + ::rptMsg("disablesr v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -45,7 +53,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my $disable; @@ -64,8 +72,6 @@ sub pluginmain { } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } - } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/disabletools.pl b/thirdparty/rr-full/plugins/disabletools.pl new file mode 100644 index 00000000000..7261a09888b --- /dev/null +++ b/thirdparty/rr-full/plugins/disabletools.pl @@ -0,0 +1,88 @@ +#----------------------------------------------------------- +# disabletools.pl +# Check settings that disable access to tools +# +# Change history +# 20220114 - created +# +# References +# https://docs.microsoft.com/en-us/troubleshoot/windows-server/system-management-components/task-manager-disabled-by-administrator +# http://systemmanager.ru/win2k_regestry.en/93466.htm +# https://blog.malwarebytes.com/detections/pum-optional-disableregistrytools/ +# +# copyright 2022 QAR,LLC +# author: H. Carvey keydet89@yahoo.com +#----------------------------------------------------------- +package disabletools; +use strict; + +my %config = (hive => "NTUSER\.DAT, Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + category => "defense evasion", + MITRE => "T1562\.001", + version => 20220114); + +sub getConfig{return %config} +sub getShortDescr { + return "Check settings disabling access to tools"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching disabletools v.".$VERSION); + ::rptMsg("disabletools v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path; + + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + + if ($hive_guess eq "software") { + $key_path = "Microsoft\\Windows\\Policies\\System"; + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Software\\Microsoft\\Windows\\Policies\\System"; + } + else {} + + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-15s %-5s",$v->get_name(),$v->get_data()); + } + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Access to Registry Tools, the Task Manager, etc., can be disabled via GPOs or direct access"); + ::rptMsg("to the Registry\. Admins may disable access as a matter of policy, or threat actors may disable access as a"); + ::rptMsg("means of hampering response\."); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/dllsearch.pl b/thirdparty/rr-full/plugins/dllsearch.pl index 35a8388e241..12c30c4494c 100644 --- a/thirdparty/rr-full/plugins/dllsearch.pl +++ b/thirdparty/rr-full/plugins/dllsearch.pl @@ -1,28 +1,31 @@ #----------------------------------------------------------- # dllsearch.pl # -# References: -# http://support.microsoft.com/kb/2264107 +# History: +# 20210705 - created # -# Change History: -# 20100824: created -# -# copyright 2010 Quantum Analytics Research, LLC +# References: +# https://attack.mitre.org/techniques/T1574/001/ +# https://www.tenable.com/plugins/nessus/48763 +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package dllsearch; use strict; -my %config = (hive => "System", - osmask => 22, +my %config = (hive => "system", + output => "report", + category => "persistence", # also, privilege escalation, defense evasion hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20100824); + MITRE => "T1574\.001", + version => 20210705); sub getConfig{return %config} - sub getShortDescr { - return "Get crash control information"; + return "Check values that impact DLL Search Order loading"; } sub getDescr{} sub getRefs {} @@ -30,42 +33,49 @@ sub getShortDescr { sub getVersion {return $config{version};} my $VERSION = getVersion(); +my $root_key = (); sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching dllsearch v.".$VERSION); - ::rptMsg("dllsearch v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("dllsearch v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# Code for System file, getting CurrentControlSet - my $current; - my $key_path = 'Select'; + $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\Session Manager"; my $key; if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $cc_path = "ControlSet00".$current."\\Control\\Session Manager"; - my $cc; - if ($cc = $root_key->get_subkey($cc_path)) { - ::rptMsg("dllsearch v.".$VERSION); - ::rptMsg(""); - my $found = 1; - eval { - my $cde = $cc->get_value("CWDIllegalInDllSearch")->get_data(); - $found = 0; - ::rptMsg(sprintf "CWDIllegalInDllSearch = 0x%x",$cde); - }; - ::rptMsg("CWDIllegalInDllSearch value not found.") if ($found); - } - else { - ::rptMsg($cc_path." not found."); - } + eval { + my $i = $key->get_value("CWDIllegalInDllSearch")->get_data(); + ::rptMsg(sprintf "CWDIllegalInDllSearch value: 0x%8x",$i); + }; + ::rptMsg("CWDIllegalInDllSearch value not found.") if ($@); + + eval { + my $i = $key->get_value("SafeDLLSearchMode")->get_data(); + ::rptMsg("SafeDLLSearchMode value : ".$i); + }; + ::rptMsg("SafeDLLSearchMode not found.") if ($@); } else { - ::rptMsg($key_path." not found."); + ::rptMsg($key_path." value not found."); } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Both values impact DLL search order processing."); + ::rptMsg(""); + ::rptMsg("CWDIllegalInDllSearch:"); + ::rptMsg("0xFFFFFFFF - Removes the current working directory from the default DLL search order"); + ::rptMsg("0x00000001 - Blocks a DLL Load from CWD if CWD is set to a WebDAV folder"); + ::rptMsg("0x00000002 - Blocks a DLL Load from CWD if CWD is set to a remote folder"); + ::rptMsg(""); + ::rptMsg("SafeDLLSearchMode:"); + ::rptMsg("1 - Enabled; forces system to search the %SystemRoot% path before the applications CWD"); } + 1; diff --git a/thirdparty/rr-full/plugins/dnschanger.pl b/thirdparty/rr-full/plugins/dnschanger.pl deleted file mode 100644 index 4cdd8c28f0f..00000000000 --- a/thirdparty/rr-full/plugins/dnschanger.pl +++ /dev/null @@ -1,94 +0,0 @@ -#----------------------------------------------------------- -# dnschanger.pl -# DNSChanger malware modifies the NameServer and/or DhcpNameServer values -# within the Registry for the interfaces. -# -# Change history -# 20120203 - created -# -# Need to add grep() for ranges: -# start range end range -# 85.255.112.0 85.255.127.255 -# 67.210.0.0 67.210.15.255 -# 93.188.160.0 93.188.167.255 -# 77.67.83.0 77.67.83.255 -# 213.109.64.0 213.109.79.255 -# 64.28.176.0 64.28.191.255 -# -# Note: these may not be the only ranges used. The best use of the -# plugin is to know what your ranges are, and eyeball the output of -# the plugin. -# -# References -# https://twitter.com/#!/saved-search/%23DFIR -# -# copyright 2012 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package dnschanger; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20120203); - -sub getConfig{return %config} -sub getShortDescr { - return "Check for indication of DNSChanger infection."; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my %nics; - my $ccs; - ::logMsg("Launching dnschanger v.".$VERSION); - ::rptMsg("dnschanger v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - eval { - $current = $root_key->get_subkey("Select")->get_value("Current")->get_data(); - }; - my @nics; - my $key_path = "ControlSet00".$current."\\Services\\Tcpip\\Parameters\\Interfaces"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my @guids = $key->get_list_of_subkeys(); - if (scalar @guids > 0) { - foreach my $g (@guids) { - ::rptMsg("Adapter: ".$g->get_name()); - ::rptMsg("LastWrite Time: ".gmtime($g->get_timestamp())." Z"); - eval { - my @vals = $g->get_list_of_values(); - foreach my $v (@vals) { - my $name = $v->get_name(); - next unless ($name =~ m/NameServer$/); - my $data = $v->get_data(); - ::rptMsg(sprintf " %-28s %-20s",$name,$data); - } - ::rptMsg(""); - }; - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/dnsclient.pl b/thirdparty/rr-full/plugins/dnsclient.pl new file mode 100644 index 00000000000..96b81c865c2 --- /dev/null +++ b/thirdparty/rr-full/plugins/dnsclient.pl @@ -0,0 +1,65 @@ +#----------------------------------------------------------- +# dnsclient.pl +# +# Change history: +# 20210504 - created +# +# Ref: +# https://tcm-sec.com/the-dangers-of-llmnr-nbt-ns/ +# +# copyright 2021 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package dnsclient; +use strict; + +my %config = (hive => "software", + category => "", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 202010504); + +sub getConfig{return %config} +sub getShortDescr { + return "Check if LLMNR/NBT-NS is disabled"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching dnsclient v.".$VERSION); + ::rptMsg("dnsclient v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); +# ::rptMsg("MITRE ATT&CK sub-technique T1546\.010"); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = "Software\\Policies\\Microsoft\\Windows NT\\DNSClient"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $m = $key->get_value("EnableMulticast")->get_value(); + ::rptMsg("EnableMulticast value: ".$m); + }; + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: An \"EnableMulticast\" value of 0 disables LLMNR/NBT-NS, which are alternate methods of host ID"); + ::rptMsg("if DNS resolution fails, and can be used to collect password hashes, or relay credentials."); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/doctoidmapping.pl b/thirdparty/rr-full/plugins/doctoidmapping.pl new file mode 100644 index 00000000000..74781358632 --- /dev/null +++ b/thirdparty/rr-full/plugins/doctoidmapping.pl @@ -0,0 +1,95 @@ +#----------------------------------------------------------- +# doctoidmapping.pl +# Value names extracted by this plugin appear to be associated with what the user +# types into Outlook search bar +# +# Change history +# 20201028 - created +# +# References +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package doctoidmapping; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "user activity", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20201020); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets user's DocToIdMapping value names"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching doctoidmapping v.".$VERSION); + ::rptMsg("doctoidmapping v.".$VERSION); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my @version; + my $office_version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + + ::rptMsg("MSOffice version could not be found.") if ($office_version == ""); + + eval { + if (my $doc = $key->get_subkey($office_version."\\Common\\Identity\\DocToIdMapping")) { + my @subkeys = $doc->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + my @vals = $s->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(" ".$v->get_name()); + } + } + } + } + } + else { + ::rptMsg("DocToIdMapping key not found\."); + } + }; + ::rptMsg(""); + ::rptMsg("Analysis Tip: Value names have been found to align with items the user typed into the Outlook Search field."); +# ::rptMsg(""); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/domains.pl b/thirdparty/rr-full/plugins/domains.pl deleted file mode 100644 index 5d6cf5f9b57..00000000000 --- a/thirdparty/rr-full/plugins/domains.pl +++ /dev/null @@ -1,76 +0,0 @@ -#----------------------------------------------------------- -# domains.pl -# -# -# Change history -# 20100116 - Created -# -# References -# http://support.microsoft.com/kb/919748 -# http://support.microsoft.com/kb/922704 -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package domains; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100116); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents Internet Settings\\ZoneMap\\Domains key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching domains v.".$VERSION); - ::rptMsg("domains v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ZoneMap"; - my $key; - if ($key = $root_key->get_subkey($key_path."\\Domains")) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())." (UTC)]"); - - my @vals = $s->get_list_of_values(); - if (scalar @vals > 0) { - foreach my $v (@vals) { - ::rptMsg(" ".$v->get_name()." -> ".$v->get_data); - } - } - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/driverinstall.pl b/thirdparty/rr-full/plugins/driverinstall.pl new file mode 100644 index 00000000000..75bcc3fae6f --- /dev/null +++ b/thirdparty/rr-full/plugins/driverinstall.pl @@ -0,0 +1,74 @@ +#----------------------------------------------------------- +# driverinstall +# +# Change history: +# 20221024 - created +# +# Ref: +# https://twitter.com/wdormann/status/1413889342372724740 +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package driverinstall; +use strict; + +my %config = (hive => "software", + category => "", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20221024); + +sub getConfig{return %config} +sub getShortDescr { + return "Check driverinstall settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching driverinstall v.".$VERSION); + ::rptMsg("driverinstall v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + + my $key_path = ('Policies\\Microsoft\\Windows\\DriverInstall\\Restrictions\\AllowUserDeviceClasses'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + eval { + my $x = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-4s %-45s",$v->get_name(),$x); + }; + } + } + else { + ::rptMsg($key_path." has no values."); + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Values beneath the AllowUserDeviceClasses key allow for users/non-admins to install the devices."); + ::rptMsg("This can present significant risk to the system, and potentially the infrastructure."); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/drivers32.pl b/thirdparty/rr-full/plugins/drivers32.pl deleted file mode 100644 index 0b2cb39599c..00000000000 --- a/thirdparty/rr-full/plugins/drivers32.pl +++ /dev/null @@ -1,96 +0,0 @@ -#----------------------------------------------------------- -# drivers32 -# Get values from Drivers32 key -# -# History -# 20130408 - created by copying then modifying the soft_run plug-in -# -# References -# Location of Windows NT Multimedia Drivers in the Registry -# http://support.microsoft.com/kb/126054 -# -# copyright 2013 Corey Harrell (jIIr) -#----------------------------------------------------------- -package drivers32; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20130408); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get values from the Drivers32 key"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Location of Windows NT Multimedia Drivers in the Registry" => - "http://support.microsoft.com/kb/126054"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching drivers32 v.".$VERSION); - ::rptMsg("drivers32 v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my @paths = ("Microsoft\\Windows NT\\CurrentVersion\\Drivers32", - "Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32", - ); - - foreach my $key_path (@paths) { - - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my %vals = getKeyValues($key); - if (scalar(keys %vals) > 0) { - foreach my $v (keys %vals) { - ::rptMsg(" ".$v." - ".$vals{$v}); - } - ::rptMsg(""); - } - else { - ::rptMsg($key_path." has no values."); - } - - } - else { - ::rptMsg($key_path." not found."); - ::rptMsg(""); - } - } -} - -sub getKeyValues { - my $key = shift; - my %vals; - - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - - } - return %vals; -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/drwatson.pl b/thirdparty/rr-full/plugins/drwatson.pl deleted file mode 100644 index 7b95ccf389e..00000000000 --- a/thirdparty/rr-full/plugins/drwatson.pl +++ /dev/null @@ -1,78 +0,0 @@ -#----------------------------------------------------------- -# drwatson.pl -# Author: Don C. Weber -# Plugin for Registry Ripper; Access Software hive file to get the -# Dr. Watson settings from Software hive -# -# Change history -# -# -# References -# Dr Watson: http://www.windowsnetworking.com/kbase/WindowsTips/Windows2000/RegistryTips/RegistryTools/DrWatson.html -# -# Author: Don C. Weber, http://www.cutawaysecurity.com/blog/cutaway-security -#----------------------------------------------------------- -package drwatson; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20081219); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets Dr. Watson settings from Software hive"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching drwatson v.".$VERSION); - ::rptMsg("drwatson v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\AeDebug"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ($key->get_value('Auto') == 0x0) ? ::rptMsg("Debugging is Disabled") : ::rptMsg("Debugging is Enabled"); - eval { - ::rptMsg("Debugger: ".$key->get_value('Debugger')->get_data()); - }; - - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - ::rptMsg(""); - $key_path = "Microsoft\\DrWatson"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ($key->get_value('LogFilePath')) ? ::rptMsg("DrWatson LogFile Path location: ".$key->get_value('LogFilePath')->get_data()) : ::rptMsg("DrWatson LogFile Path location: %SystemRoot%\\Documents and Settings\\All Users\\Documents\\DrWatson"); - ($key->get_value('CreateCrashDump') == 0x0) ? ::rptMsg("CreateCrashDump is Disabled") : ::rptMsg("CreateCrashDump is Enabled"); - ($key->get_value('CrashDumpFile')) ? ::rptMsg("Crash Dump Path and Name: ".$key->get_value('CrashDumpFile')->get_data()) : ::rptMsg("CrashDumpFile is not set"); - ($key->get_value('AppendToLogFile') == 0x0) ? ::rptMsg("AppendToLogFile is set to create a new file each time") : ::rptMsg("AppendToLogFile is set to append"); - - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - ::rptMsg(""); - ::rptMsg("Analysis Tips: For Dr. Watson settings information check: http://www.windowsnetworking.com/kbase/WindowsTips/Windows2000/RegistryTips/RegistryTools/DrWatson.html"); -} - -1; diff --git a/thirdparty/rr-full/plugins/duo.pl b/thirdparty/rr-full/plugins/duo.pl new file mode 100644 index 00000000000..5190a93b890 --- /dev/null +++ b/thirdparty/rr-full/plugins/duo.pl @@ -0,0 +1,77 @@ +#----------------------------------------------------------- +# duo +# +# +# +# Change history: +# 20220927 - created +# +# Ref: +# https://www.mandiant.com/resources/blog/abusing-duo-authentication-misconfigurations +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package duo; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1562\.001", + version => 20220927); + +sub getConfig{return %config} +sub getShortDescr { + return "Get DUO config"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching duo v.".$VERSION); + ::rptMsg("duo v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my @paths = ('Duo Security\\DuoCredProv', + 'Policies\\Duo Security\\DuoCredProv'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-20s %-2s",$v->get_name(),$v->get_data()); + } + } + } + else { + ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Users with admin privileges can modify the DUO config settings."); +# ::rptMsg(""); + ::rptMsg("Ex: FailOpen = 1 tells the system to fail open if DUO is offline"); + ::rptMsg(""); + ::rptMsg("Ref: https://www.mandiant.com/resources/blog/abusing-duo-authentication-misconfigurations"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/elevatedinstall.pl b/thirdparty/rr-full/plugins/elevatedinstall.pl new file mode 100644 index 00000000000..9ab4149f7f9 --- /dev/null +++ b/thirdparty/rr-full/plugins/elevatedinstall.pl @@ -0,0 +1,109 @@ +#----------------------------------------------------------- +# elevatedinstall.pl +# If the AlwaysInstallElevated value is not set to "1" under both of +# the preceding registry keys, the installer uses elevated privileges to +# install managed applications and uses the current user's privilege level +# for unmanaged applications. +# +# +# Change history: +# 20220831 - created +# +# References: +# https://twitter.com/malmoeb/status/1564629592723361794 +# https://docs.microsoft.com/en-us/windows/win32/msi/alwaysinstallelevated +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package elevatedinstall; +use strict; + +my %config = (hive => "software,ntuser\.dat", + category => "privilege escalation", + MITRE => "T1548", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220831); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check AlwaysInstallElevated value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching elevatedinstall v.".$VERSION); + ::rptMsg("elevatedinstall v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + my $key; + my $key_path = (); + + if ($hive_guess eq "software") { + $key_path = "Policies\\Microsoft\\Windows\\Installer"; + if ($key = $root_key->get_subkey($key_path)) { + eval { + my $c = $key->get_value("AlwaysInstallElevated")->get_data(); + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + ::rptMsg("AlwaysInstallElevated value: ".$c); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the AlwaysInstallElevated value is set to \"1\", the Installer uses elevated "); + ::rptMsg("privileges to install managed applications\."); + }; + ::rptMsg($key_path."\\AlwaysInstallElevated value not found.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Software\\Policies\\Microsoft\\Windows\\Installer"; + if ($key = $root_key->get_subkey($key_path)) { + eval { + my $c = $key->get_value("AlwaysInstallElevated")->get_data(); + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + ::rptMsg("AlwaysInstallElevated value: ".$c); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the AlwaysInstallElevated value is set to \"1\", the Installer uses elevated "); + ::rptMsg("privileges to install managed applications\."); + }; + ::rptMsg($key_path."\\AlwaysInstallElevated value not found.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } + } + else {} + + + +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/emdmgmt.pl b/thirdparty/rr-full/plugins/emdmgmt.pl index 8a48fe8496c..a78f975039c 100644 --- a/thirdparty/rr-full/plugins/emdmgmt.pl +++ b/thirdparty/rr-full/plugins/emdmgmt.pl @@ -1,8 +1,19 @@ #----------------------------------------------------------- # emdmgmt.pl # +# History +# 20200911 - MITRE updates +# 20200511 - updated date output format +# 20190827 - updated +# 20120207 - created # -# copyright 2012 Quantum Analytics Research, LLC +# This plugin does not lend itself to a *_tln version; in practice, many of the subkey LastWrite +# times are the same, or close together, indicating that some other action or event, besides +# connecting/disconnecting the device causes it to be updated. Often, the "Last Tested Time" may +# fall significantly outside the time window of interest. As such, it is best to use the volume +# information to tie to other data sources. +# +# copyright 2019 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package emdmgmt; @@ -12,8 +23,10 @@ package emdmgmt; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120207); + MITRE => "", + output => "report", + category => "devices", + version => 20200911); sub getConfig{return %config} sub getShortDescr { @@ -39,7 +52,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("EMDMgmt"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @sk = $key->get_list_of_subkeys(); foreach my $s (@sk) { @@ -47,7 +60,7 @@ sub pluginmain { if ($name =~ m/^_\?\?_USBSTOR/) { my ($usb,$sn,$vol) = (split(/#/,$name,4))[1,2,3]; ::rptMsg($usb); - ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); +# ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); ::rptMsg(" SN: ".$sn); $vol =~ s/{53f56307-b6bf-11d0-94f2-00a0c91efb8b}//; my ($volname,$vsn) = split(/_/,$vol,2); @@ -58,11 +71,14 @@ sub pluginmain { } ::rptMsg(" Vol Name: ".$volname) if ($volname ne ""); ::rptMsg(" VSN: ".$vsn); - my $last = $s->get_value_data("LastTestedTime"); - my ($lo,$hi) = unpack("VV",$last); - if ($lo != 0 && $hi != 0) { - ::rptMsg(" LastTestedTime: ".gmtime(::getTime($lo,$hi))." Z"); - } + + eval { + my $last = $s->get_value_data("LastTestedTime"); + my ($t0,$t1) = unpack("VV",$last); + if ($t0 != 0 && $t1 != 0) { + ::rptMsg(" LastTestedTime: ".::format8601Date(::getTime($t0,$t1))."Z"); + } + }; ::rptMsg(""); } else { @@ -77,14 +93,16 @@ sub pluginmain { } $volname = "Unknown Volume" unless ($volname ne ""); ::rptMsg($volname); - ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); +# ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); ::rptMsg(" VSN: ".$vsn); - my $last = $s->get_value_data("LastTestedTime"); - my ($lo,$hi) = unpack("VV",$last); - if ($lo != 0 && $hi != 0) { - ::rptMsg(" LastTestedTime: ".gmtime(::getTime($lo,$hi))." Z"); - } + eval { + my $last = $s->get_value_data("LastTestedTime"); + my ($t0,$t1) = unpack("VV",$last); + if ($t0 != 0 && $t1 != 0) { + ::rptMsg(" LastTestedTime: ".::format8601Date(::getTime($t0,$t1))."Z"); + } + }; ::rptMsg(""); } } diff --git a/thirdparty/rr-full/plugins/enablelinkedconn.pl b/thirdparty/rr-full/plugins/enablelinkedconn.pl new file mode 100644 index 00000000000..291c808517a --- /dev/null +++ b/thirdparty/rr-full/plugins/enablelinkedconn.pl @@ -0,0 +1,78 @@ +#----------------------------------------------------------- +# enablelinkedconn.pl +# +# Change history: +# 20220707 - added CISA alert to references +# 20220214 - updated with BlackByte info +# 20201028 - created +# +# Ref: +# https://docs.microsoft.com/en-us/troubleshoot/windows-client/networking/mapped-drives-not-available-from-elevated-command +# https://www.bleepingcomputer.com/news/security/ako-ransomware-another-day-another-infection-attacking-businesses/ +# https://redcanary.com/blog/blackbyte-ransomware/ <- added 02142022 (BlackByte; use with remoteuac.pl) +# https://www.cisa.gov/uscert/ncas/alerts/aa22-181a +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package enablelinkedconn; +use strict; + +my %config = (hive => "Software", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1112", + output => "report", + version => 20220707); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets EnableLinkedConnections value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching enablelinkedconn v.".$VERSION); + ::rptMsg("enablelinkedconn v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my $key_path = 'Microsoft\\Windows\\CurrentVersion\\Policies\\System'; + + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + eval { + my $en = $key->get_value("EnableLinkedConnections")->get_data(); + ::rptMsg("EnableLinkedConnections value = ".$en); + + }; + ::rptMsg("EnableLinkedConnections value not found.") if ($@); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: When UAC is enabled, the system creates two logon sessions at user logon. Both logon sessions are linked"); + ::rptMsg("to one another. One session represents the user during an elevated session, and the other session where you run under "); + ::rptMsg("least user rights."); + ::rptMsg(""); + ::rptMsg("When drive mappings are created, the system creates symbolic link objects (DosDevices) that associate the drive letters"); + ::rptMsg(" to the UNC paths. These objects are specific for a logon session and are not shared between logon sessions."); + ::rptMsg(""); + ::rptMsg("This setting has been seen being enabled by AKO, BlackByte, and MedusaLocker ransomware actors/samples."); +} +1; diff --git a/thirdparty/rr-full/plugins/environment.pl b/thirdparty/rr-full/plugins/environment.pl index 5bfb84f6718..613f45134fe 100644 --- a/thirdparty/rr-full/plugins/environment.pl +++ b/thirdparty/rr-full/plugins/environment.pl @@ -1,90 +1,116 @@ #----------------------------------------------------------- # environment.pl -# Extracts user's Environment paths from NTUSER.DAT +# Extracts environment variables from NTUSER.DAT and System hives # # Change history -# 20150910 - added check for specific value, per Hexacorn blog -# 20110830 [fpi] + banner, no change to the version number +# 20201113 - minor updates +# 20200911 - MITRE updates +# 20200512 - created # # References # http://www.hexacorn.com/blog/2014/11/14/beyond-good-ol-run-key-part-18/ +# UserInitMprLogonScript value - https://eqllib.readthedocs.io/en/latest/analytics/54fff7e8-f81d-4169-b820-4cbff0133e2d.html +# - https://www.cybereason.com/blog/back-to-the-future-inside-the-kimsuky-kgh-spyware-suite +# Cor_profiler values - https://redcanary.com/blog/cor_profiler-for-persistence/ +# Seen used by Blue Mockingbird - https://redcanary.com/blog/blue-mockingbird-cryptominer/ # -# Copyright (c) 2011-02-04 Brendan Coles +# +# https://attack.mitre.org/techniques/T1037/001/ +# +# Copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package environment; use strict; -my %config = (hive => "NTUSER\.DAT", +my %config = (hive => "System, NTUSER\.DAT", hasShortDescr => 1, + category => "persistence", hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20150910); + output => "report", + MITRE => "T1037\.007", + version => 20201113); + my $VERSION = getVersion(); -# Functions # sub getDescr {} sub getRefs {} sub getConfig {return %config} sub getHive {return $config{hive};} sub getVersion {return $config{version};} sub getShortDescr { - return "Extracts user's Environment paths from NTUSER.DAT"; + return "Get environment vars from NTUSER\.DAT & System hives"; } sub pluginmain { - - # Declarations # my $class = shift; my $hive = shift; - # Initialize # ::logMsg("Launching environment v.".$VERSION); - ::rptMsg("environment v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + ::rptMsg("environment v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Environment"; - # If # Environment path exists # + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + + my $key = (); + my $key_path = (); + + my @val_names = ("UserInitMprLogonScript","cor_enable_profiling","cor_profiler","cor_profiler_path"); + + if ($hive_guess eq "system") { + my $ccs = (); + if ($key = $root_key->get_subkey('Select')) { + $ccs = "ControlSet00".$key->get_value("Current")->get_data(); + } + $key_path = $ccs."\\Control\\Session Manager\\Environment"; + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Environment"; + } + else { + $key_path = "Environment"; + } + if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Environment"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); - - # Extract # all keys from Environment registry path # my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # if (scalar(@vals) > 0) { - # Extract # all key names+values for Environment registry path # foreach my $v (@vals) { my $name = $v->get_name(); - ::rptMsg($name." -> ".$v->get_data()); + ::rptMsg(sprintf "%-25s %-50s",$name,$v->get_data()); - if ($name eq "UserInitMprLogonScript") { - ::rptMsg("**ALERT: UserInitMprLogonScript value found: ".$v->get_data()); + foreach my $n (@val_names) { + if ($name eq $n) { + ::rptMsg("**ALERT: ".$n." value found: ".$v->get_data()); + } } - } - - # Error # key value is null # + ::rptMsg(""); + ::rptMsg("Analysis Tip: Threat actors, such as Kimsuky (see Cybereason reference below) have been observed using the"); + ::rptMsg("\"UserInitMprLogonScript\" value for persistence, by including a script in the value data."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.cybereason.com/blog/back-to-the-future-inside-the-kimsuky-kgh-spyware-suite"); } else { ::rptMsg($key_path." has no values."); } - - # Error # Environment isn't here, try another castle # } else { ::rptMsg($key_path." not found."); } - # Return # obligatory new-line # - ::rptMsg(""); +# ::rptMsg(""); } -# Error # oh snap! # + 1; diff --git a/thirdparty/rr-full/plugins/eraser.pl b/thirdparty/rr-full/plugins/eraser.pl deleted file mode 100644 index d41408a91aa..00000000000 --- a/thirdparty/rr-full/plugins/eraser.pl +++ /dev/null @@ -1,67 +0,0 @@ -#----------------------------------------------------------- -# eraser.pl -# Gets Eraser User Settings -# -# Change history -# 20180708 - Created (based on ccleaner.pl plugin) -# -# References -# -# Author: Hadar Yudovich <@hadar0x> -#----------------------------------------------------------- -package eraser; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20180708); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets User's Eraser Settings"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching Eraser v.".$VERSION); - ::rptMsg("Eraser v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); # creates a Win32Registry object - my $root_key = $reg->get_root_key; - my $key_path = "Software\\Eraser\\Eraser 6"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my %eraserkeys; - my @eraservals = $key->get_list_of_values(); - if (scalar(@eraservals) > 0) { - foreach my $val (@eraservals) { - $eraserkeys{$val->get_name()} = $val->get_data(); - } - foreach my $keyval (sort keys %eraserkeys) { - ::rptMsg($keyval." -> ".$eraserkeys{$keyval}); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." does not exist."); - } - ::rptMsg(""); -} - -1; diff --git a/thirdparty/rr-full/plugins/esent.pl b/thirdparty/rr-full/plugins/esent.pl deleted file mode 100644 index cea3dfad46d..00000000000 --- a/thirdparty/rr-full/plugins/esent.pl +++ /dev/null @@ -1,80 +0,0 @@ -#----------------------------------------------------------- -# esent -# Get contents of Esent\Process key from Software hive -# -# Note: Not sure why I wrote this one; just thought it might come -# in handy as info about this key is developed. -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package esent; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20101202); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get ESENT\\Process key contents"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching esent v.".$VERSION); - ::rptMsg("esent v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\ESENT\\Process"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @sk = $key->get_list_of_subkeys(); - - if (scalar(@sk) > 0) { - my %esent; - - foreach my $s (@sk) { - my $sk = $s->get_subkey("DEBUG"); -# my $lw = $s->get_timestamp(); - my $lw = $sk->get_timestamp(); - - my $name = $s->get_name(); - - push(@{$esent{$lw}},$name); - } - - foreach my $t (reverse sort {$a <=> $b} keys %esent) { - ::rptMsg(gmtime($t)." (UTC)"); - foreach my $item (@{$esent{$t}}) { - ::rptMsg(" $item"); - } - } - - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/etos.pl b/thirdparty/rr-full/plugins/etos.pl deleted file mode 100644 index 2cee3abf67d..00000000000 --- a/thirdparty/rr-full/plugins/etos.pl +++ /dev/null @@ -1,75 +0,0 @@ -#----------------------------------------------------------- -# at.pl -# -# -# Change history -# 20150325 - created -# -# Ref: -# http://www.secureworks.com/cyber-threat-intelligence/threats/threat-group-3279-targets-the-video-game-industry/ -# -# Per the above reference, if the plugin produces a list of values for either of keys checked, -# the analyst should consider checking the value data, as they may be XOR-encoded data read, -# decoded and used by the malware. -# -# Copyright (c) 2015 QAR,LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package etos; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - category => "malware", - version => 20150325); - -my $VERSION = getVersion(); - -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getDescr {} -sub getShortDescr {return "Checks Software hive for indicators of Etos malware";} -sub getRefs {} - -sub pluginmain { - my $class = shift; - my $hive = shift; - - ::logMsg("Launching etos v.".$VERSION); - ::rptMsg("etos v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()); - ::rptMsg(""); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my @paths = ('ODBC.INI', 'ODBC\\ODBC.INI'); - - foreach my $key_path (@paths) { - - if ($key = $root_key->get_subkey($key_path)) { - - my @val = $key->get_list_of_values(); - if (scalar @val > 0) { - my $lw = $key->get_timestamp(); - ::rptMsg("LastWrite: ".gmtime($lw)); - foreach my $v (@val) { - my $name = $v->get_name(); - ::rptMsg(" ".$name); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - - } - } -} - -1; diff --git a/thirdparty/rr-full/plugins/eventlog.pl b/thirdparty/rr-full/plugins/eventlog.pl deleted file mode 100644 index 13524d47e84..00000000000 --- a/thirdparty/rr-full/plugins/eventlog.pl +++ /dev/null @@ -1,158 +0,0 @@ -#----------------------------------------------------------- -# eventlog.pl -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package eventlog; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20090112); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get EventLog configuration info"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching eventlog v.".$VERSION); - ::rptMsg("eventlog v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# Code for System file, getting CurrentControlSet - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - - my $evt_path = "ControlSet00".$current."\\Services\\Eventlog"; - my $evt; - if ($evt = $root_key->get_subkey($evt_path)) { - ::rptMsg(""); - my @subkeys = $evt->get_list_of_subkeys(); - if (scalar (@subkeys) > 0) { - foreach my $s (@subkeys) { - my $logname = $s->get_name(); - ::rptMsg($logname." \\ ".scalar gmtime($s->get_timestamp())."Z"); - eval { - my $file = $s->get_value("File")->get_data(); - ::rptMsg(" File = ".$file); - }; - - eval { - my $display = $s->get_value("DisplayNameFile")->get_data(); - ::rptMsg(" DisplayNameFile = ".$display); - }; - - eval { - my $max = $s->get_value("MaxSize")->get_data(); - ::rptMsg(" MaxSize = ".processSize($max)); - }; - - eval { - my $ret = $s->get_value("Retention")->get_data(); - ::rptMsg(" Retention = ".processRetention($ret)); - }; - -# AutoBackupLogFiles; http://support.microsoft.com/kb/312571/ - eval { - my $auto = $s->get_value("AutoBackupLogFiles")->get_data(); - ::rptMsg(" AutoBackupLogFiles = ".$auto); - }; - -# Check WarningLevel value on Security EventLog; http://support.microsoft.com/kb/945463 - eval { - if ($logname eq "Security") { - my $wl = $s->get_value("WarningLevel")->get_data(); - ::rptMsg(" WarningLevel = ".$wl); - } - }; - - ::rptMsg(""); - } - - } - else { - ::rptMsg($evt_path." has no subkeys."); - } - } - else { - ::rptMsg($evt_path." not found."); - ::logMsg($evt_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; - -sub processSize { - my $sz = shift; - - my $kb = 1024; - my $mb = $kb * 1024; - my $gb = $mb * 1024; - - if ($sz > $gb) { - my $d = $sz/$gb; - my $l = length((split(/\./,$d,2))[0]) + 2; - return sprintf "%$l.2fGB",$d; - } - elsif ($sz > $mb) { - my $d = $sz/$mb; - my $l = length((split(/\./,$d,2))[0]) + 2; - return sprintf "%$l.2fMB",$d; - } - elsif ($sz > $kb) { - my $d = $sz/$kb; - my $l = length((split(/\./,$d,2))[0]) + 2; - return sprintf "%$l.2fKB",$d; - } - else {return $sz."B"}; -} - -sub processRetention { -# Retention maintained in seconds -# http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/ -# regentry/30709.mspx?mfr=true - my $ret = shift; - - my $min = 60; - my $hr = $min * 60; - my $day = $hr * 24; - - if ($ret > $day) { - my $d = $ret/$day; - my $l = length((split(/\./,$d,2))[0]) + 2; - return sprintf "%$l.2f days",$d; - } - elsif ($ret > $hr) { - my $d = $ret/$hr; - my $l = length((split(/\./,$d,2))[0]) + 2; - return sprintf "%$l.2f hr",$d; - } - elsif ($ret > $min) { - my $d = $ret/$min; - my $l = length((split(/\./,$d,2))[0]) + 2; - return sprintf "%$l.2f min",$d; - } - else {return $ret." sec"}; -} \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/eventlogs.pl b/thirdparty/rr-full/plugins/eventlogs.pl deleted file mode 100644 index f95ebbc8888..00000000000 --- a/thirdparty/rr-full/plugins/eventlogs.pl +++ /dev/null @@ -1,108 +0,0 @@ -#----------------------------------------------------------- -# eventlogs.pl -# Author: Don C. Weber -# Plugin for Registry Ripper; Access System hive file to get the -# Event Log settings from System hive -# -# Change history -# -# -# References -# Eventlog Key: http://msdn.microsoft.com/en-us/library/aa363648(VS.85).aspx -# -# Author: Don C. Weber, http://www.cutawaysecurity.com/blog/cutaway-security -#----------------------------------------------------------- -package eventlogs; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20081219); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets Event Log settings from System hive"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching eventlogs v.".$VERSION); - ::rptMsg("eventlogs v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - my $win_path = $ccs."\\Services\\Eventlog"; - my $win; - if ($win = $root_key->get_subkey($win_path)) { - ::rptMsg("EventLog Configuration"); - ::rptMsg($win_path); - ::rptMsg("LastWrite Time ".gmtime($win->get_timestamp())." (UTC)"); - my $cn; - if (defined($win->get_value("ComputerName"))) { - if ($cn = $win->get_value("ComputerName")->get_data()) { - ::rptMsg("ComputerName = ".$cn); - } - } - else { - ::rptMsg("ComputerName value not found."); - } - } - else { - ::rptMsg($win_path." not found."); - } - -# Cycle through each type of log - my $logname; - my $evpath; - my $evlog; - my @list_logs = $win->get_list_of_subkeys(); - foreach $logname (@list_logs){ - ::rptMsg(""); - $evpath = $win_path."\\".$logname->get_name(); - if ($evlog = $root_key->get_subkey($evpath)) { - ::rptMsg(" ".$logname->get_name()." EventLog"); - ::rptMsg(" ".$evpath); - ::rptMsg(" LastWrite Time ".gmtime($evlog->get_timestamp())." (UTC)"); - ::rptMsg(" Configuration Settings"); - if (defined($evlog->get_value('File'))) { - ::rptMsg(" Log location: ".$evlog->get_value('File')->get_data()); - } - if (defined($evlog->get_value('MaxSize'))) { - ::rptMsg(" Log Size: ".$evlog->get_value('MaxSize')->get_data()." Bytes"); - } - if (defined($evlog->get_value('AutoBackupLogFiles'))) { - ($evlog->get_value('AutoBackupLogFiles') == 0x0) ? ::rptMsg(" AutoBackupLogFiles is Disabled") : ::rptMsg(" AutoBackupLogFiles is Enabled") - } - } - else { - ::rptMsg($logname->get_name()." Event Log not found."); - } - } - ::rptMsg(""); - ::rptMsg("Analysis Tips: For Event Log settings information check: http://msdn.microsoft.com/en-us/library/aa363648(VS.85).aspx"); - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/eventsasp.pl b/thirdparty/rr-full/plugins/eventsasp.pl new file mode 100644 index 00000000000..f526f5d13bf --- /dev/null +++ b/thirdparty/rr-full/plugins/eventsasp.pl @@ -0,0 +1,89 @@ +#----------------------------------------------------------- +# eventsasp.pl +# The contents of the key queried point to what's executed when someone clicks on +# the "Event Log Online Help" link when viewing the Event Properties dialog +# +# Change history +# 20230217 - updated (reference, added value) +# 20220613 - created +# +# References +# https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.EventViewer::EventViewer_RedirectionProgramCommandLineParameters +# https://www.stigviewer.com/stig/windows_server_2012_member_server/2014-01-07/finding/V-15672 +# https://www.hexacorn.com/blog/2019/02/15/beyond-good-ol-run-key-part-103/ +# +# Copyright 2023 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package eventsasp; +use strict; + +my %config = (hive => "software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1204\.001", + category => "user execution", + output => "report", + version => 20230217); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return ""; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching eventsasp v.".$VERSION); + ::rptMsg("eventsasp v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + + my @paths = ("Policies\\Microsoft\\EventViewer", + "Microsoft\\Windows NT\\CurrentVersion\\Event Viewer"); + + foreach my $key_path (@paths) { + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @values = ("MicrosoftEventVwrDisableLinks", + "MicrosoftRedirectionURL", + "MicrosoftRedirectionProgram", + "MicrosoftRedirectionProgramCommandLineParameters", + "ConfirmURL"); + + foreach my $v (@values) { + eval { + my $t = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-50s %-30s",$v,$t); + }; + } + } + else { +# ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: The settings queried by this plugin address what occurs when a user clicks the \"Event Log Online Help\""); + ::rptMsg("link in the Event Properties dialog; this can lead to system compromise."); + ::rptMsg(""); + ::rptMsg("To disable this capability, the MicrosoftEventVwrDisableLinks value must be set to \"0\""); + ::rptMsg("Ref: https://www.stigviewer.com/stig/windows_server_2012_member_server/2014-01-07/finding/V-15672"); + +} + +1; diff --git a/thirdparty/rr-full/plugins/eventtranscript.pl b/thirdparty/rr-full/plugins/eventtranscript.pl new file mode 100644 index 00000000000..848a1ec3a5f --- /dev/null +++ b/thirdparty/rr-full/plugins/eventtranscript.pl @@ -0,0 +1,85 @@ +#----------------------------------------------------------- +# eventtranscript.pl +# Get EventTranscript\.db settings +# +# Change history: +# 20210927 - created +# +# References: +# https://github.com/rathbuna/EventTranscript.db-Research +# https://www.kroll.com/en/insights/publications/cyber/forensically-unpacking-eventtranscript/enabling-eventtranscript +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey +#----------------------------------------------------------- +package eventtranscript; +use strict; + +my %config = (hive => "software", + category => "config", + MITRE => "", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210927); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get EventTranscript\.db settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching eventtranscript v.".$VERSION); + ::rptMsg("eventtranscript v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my @paths = ("Microsoft\\Windows\\CurrentVersion\\Policies\\DataCollection", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\DataCollection", + "Policies\\Microsoft\\Windows\\DataCollection"); + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + eval { + my $a = $key->get_value("AllowTelemetry")->get_data(); + ::rptMsg("AllowTelemetry value : ".$a); + ::rptMsg("1 - Basic"); + ::rptMsg("3 - Full"); + }; + + eval { + my $m = $key->get_value("MaxTelemetryAllowed")->get_data(); + ::rptMsg("MaxTelemetryAllowed value: ".$m); + ::rptMsg("1 - Basic"); + ::rptMsg("3 - Full"); + }; + } + else { +# ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Values within the DataCollection key control what's logged to EventsTranscript\.db."); +# ::rptMsg(""); +# ::rptMsg(""); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/execpolicy.pl b/thirdparty/rr-full/plugins/execpolicy.pl index 731aedcf9c4..6620d34606e 100644 --- a/thirdparty/rr-full/plugins/execpolicy.pl +++ b/thirdparty/rr-full/plugins/execpolicy.pl @@ -2,24 +2,27 @@ # execpolicy # # Change history: +# 20200911 - MITRE updates +# 20200517 - updated date output format # 20180618 - created # # Ref: # https://blogs.technet.microsoft.com/operationsguy/2011/04/21/remotely-tweak-powershell-execution-policies-without-powershell-remoting/ # -# copyright 2018 QAR,LLC +# copyright 2020 QAR,LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package execpolicy; use strict; my %config = (hive => "Software", - category => "config", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180618); + MITRE => "", + output => "report", + version => 20200911); sub getConfig{return %config} sub getShortDescr { @@ -45,6 +48,9 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); my $policy = ""; eval { $policy = $key->get_value("ExecutionPolicy")->get_data(); diff --git a/thirdparty/rr-full/plugins/feature_block.pl b/thirdparty/rr-full/plugins/feature_block.pl new file mode 100644 index 00000000000..a90d13d3c24 --- /dev/null +++ b/thirdparty/rr-full/plugins/feature_block.pl @@ -0,0 +1,80 @@ +#----------------------------------------------------------- +# feature_block.pl +# +# +# Change history: +# 20230724 - created +# +# References: +# https://www.microsoft.com/en-us/security/blog/2023/07/11/storm-0978-attacks-reveal-financial-and-espionage-motives/ +# https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36884 +# +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package feature_block; +use strict; + +my %config = (hive => "software", + category => "lateral movement", + MITRE => "T1210", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20230724); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get FEATURE_BLOCK_CROSS_PROTOCOL_FILE_NAVIGATION key values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching feature_block v.".$VERSION); + ::rptMsg("feature_block v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path = "Software\\Policies\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BLOCK_CROSS_PROTOCOL_FILE_NAVIGATION"; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("Key path: ".$key_path); + ::rptMsg("Key LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-25s %-4d",$v->get_name(),$v->get_data()); + } + } + else { + ::rptMsg($key_path." has no values\."); + } + } + else { + ::rptMsg($key_path." not found"); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: For some MS customers, the \"Block all Office applications from creating child processes\""); + ::rptMsg("attack surface reduction rule will reportedly protected them from attempts to exploit CVE-2023-36884. For"); + ::rptMsg("customers who cannot take advantage of these protections can set key values to \"1\" to avoid exploitation."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.microsoft.com/en-us/security/blog/2023/07/11/storm-0978-attacks-reveal-financial-and-espionage-motives/"); + ::rptMsg("Ref: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36884"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/featureusage.pl b/thirdparty/rr-full/plugins/featureusage.pl new file mode 100644 index 00000000000..9467d840a5b --- /dev/null +++ b/thirdparty/rr-full/plugins/featureusage.pl @@ -0,0 +1,95 @@ +#----------------------------------------------------------- +# featureusage.pl +# +# +# Change history +# 20200911 - MITRE updates +# 20200511 - update date output format +# 20190919 - created +# +# Note: at this point, the context of the data is not really understood... +# +# References +# https://www.crowdstrike.com/blog/how-to-employ-featureusage-for-windows-10-taskbar-forensics/ +# +# https://attack.mitre.org/techniques/T1059/ +# +# Copyright 2020 QAR, LLC +# H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package featureusage; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1059", + category => "program execution", + version => 20200911); +my $VERSION = getVersion(); + +# Functions # +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Extracts user's FeatureUsage data"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching featureusage v.".$VERSION); + ::rptMsg("featureusage v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + + my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FeatureUsage"; + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my ($t0,$t1) = unpack("VV",$key->get_value("KeyCreationTime")->get_data()); + ::rptMsg("KeyCreationTime: ".::format8601Date(::getTime($t0,$t1))."Z"); + ::rptMsg(""); + }; + + eval { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my $subkey_name = $s->get_name(); + if (my $app = $key->get_subkey($subkey_name)) { + my @vals = $app->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg("***".$subkey_name." values***"); + foreach my $val (@vals) { + my $name = $val->get_name(); + my $data = $val->get_data(); + ::rptMsg(sprintf "%-80s ".$data,$name); + } + } + } + ::rptMsg(""); + } + } + }; + + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; diff --git a/thirdparty/rr-full/plugins/fileexts.pl b/thirdparty/rr-full/plugins/fileexts.pl deleted file mode 100644 index 732b43a08bf..00000000000 --- a/thirdparty/rr-full/plugins/fileexts.pl +++ /dev/null @@ -1,75 +0,0 @@ -#----------------------------------------------------------- -# fileexts.pl -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package fileexts; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20080818); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get user FileExts values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching fileexts v.".$VERSION); - ::rptMsg("fileexts v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("fileexts"); - ::rptMsg($key_path); - ::rptMsg(""); - - my @sk = $key->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - my $name = $s->get_name(); - next unless ($name =~ m/^\.\w+/); - - eval { - my $data = $s->get_subkey("OpenWithList")->get_value("MRUList")->get_data(); - if ($data =~ m/^\w/) { - ::rptMsg("File Extension: ".$name); - ::rptMsg("LastWrite: ".gmtime($s->get_subkey("OpenWithList")->get_timestamp())); - ::rptMsg("MRUList: ".$data); - my @list = split(//,$data); - foreach my $l (@list) { - my $valdata = $s->get_subkey("OpenWithList")->get_value($l)->get_data(); - ::rptMsg(" ".$l." => ".$valdata); - } - ::rptMsg(""); - } - }; - } - } - else { - ::rptMsg($key_path." does not have subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/filehistory.pl b/thirdparty/rr-full/plugins/filehistory.pl deleted file mode 100644 index 3bc9003f575..00000000000 --- a/thirdparty/rr-full/plugins/filehistory.pl +++ /dev/null @@ -1,95 +0,0 @@ -#----------------------------------------------------------- -# filehistory.pl -# Get filehistory settings -# -# Change history -# 20120722 - updated %config hash -# 20120620 - updated/modified by H. Carvey -# 20120607 - created by K. Johnson -# -# References -# This RegRipper plugin was created based on research I have done on -# the FileHistory Feature of Windows 8. -# http://randomthoughtsofforensics.blogspot.com/ -# -# FileHistoy Plugin copyright 2012 K. Johnson -# Edited by H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package filehistory; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hivemask => 16, - output => "report", - category => "", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 32, #Windows 8 - version => 20120620); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets filehistory settings"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching filehistory v.".$VERSION); - ::rptMsg("filehistory v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\FileHistory"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - - if ($v->get_name() eq "ProtectedUpToTime") { - my @t = unpack("VV",$v->get_data()); - my $pft = ::getTime($t[0],$t[1]); - ::rptMsg(" ProtectedUpToTime = ".gmtime($pft)." (UTC)"); - } - - if ($v->get_name() eq "ReassociationPerformed") { - ::rptMsg(sprintf "%-20s 0x%x","ReassociationPerformed",$v->get_data()); - } - - if ($v->get_name() eq "RestoreAllowed") { - ::rptMsg(sprintf "%-20s 0x%x","RestoreAllowed",$v->get_data()); - } - - if ($v->get_name() eq "SearchRebuildRequired") { - ::rptMsg(sprintf "%-20s 0x%x","SearchRebuildRequired",$v->get_data()); - } - - if ($v->get_name() eq "TargetChanged") { - ::rptMsg(sprintf "%-20s 0x%x","TargetChanged",$v->get_data()); - } - } - } - else { - ::rptMsg($key_path." has no values."); - ::rptMsg("File History may not be configured for this user."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/fileless.pl b/thirdparty/rr-full/plugins/fileless.pl index f68f4a1278a..d8dbbdf51a7 100644 --- a/thirdparty/rr-full/plugins/fileless.pl +++ b/thirdparty/rr-full/plugins/fileless.pl @@ -4,6 +4,9 @@ # # # Change history +# 20200911 - MITRE updates +# 20200525 - updated date output format +# 20160120 - added display of value name # 20150110 - updated with additional detection # 20150101 - Created # @@ -12,8 +15,9 @@ # http://www.malwaretech.com/2014/12/phase-bot-fileless-rootkit.html # http://www.kernelmode.info/forum/viewtopic.php?f=16&t=3669 # +# https://attack.mitre.org/techniques/T1059/001/ # -# copyright 2015 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package fileless; @@ -23,8 +27,10 @@ package fileless; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20150110); + output => "report", + MITRE => "T1059\.001", + category => "persistence", + version => 20200911); sub getConfig{return %config} sub getShortDescr { @@ -45,8 +51,10 @@ sub pluginmain { my $reg = Parse::Win32Registry->new($file); my $root_key = $reg->get_root_key; ::logMsg("Launching fileless v.".$VERSION); - ::rptMsg("fileless v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("fileless v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); traverse($root_key); } @@ -59,13 +67,14 @@ sub traverse { if ($type == 1 || $type == 2) { my $data = $val->get_data(); $data = lc($data); - if ($data =~ m/^rundll32 javascript/ || $data =~ m/^mshta/) { + if ($data =~ m/^rundll32 javascript/ || $data =~ m/^mshta/ || grep(/powershell/,$data)) { ::rptMsg("**Possible fileless malware found\."); my $path = $key->get_path(); my @p = split(/\\/,$path); $path = join('\\',@p[1..(scalar(@p) - 1)]); ::rptMsg($path); - ::rptMsg("LastWrite time: ".gmtime($ts)." UTC"); + ::rptMsg("LastWrite time: ".::format8601Date($ts)."Z"); + ::rptMsg("Value Name: ".$val->get_name()); ::rptMsg("Data: ".$data); ::rptMsg(""); } diff --git a/thirdparty/rr-full/plugins/findexes.pl b/thirdparty/rr-full/plugins/findexes.pl index 0d10ae22f4b..7dd44132336 100644 --- a/thirdparty/rr-full/plugins/findexes.pl +++ b/thirdparty/rr-full/plugins/findexes.pl @@ -7,19 +7,26 @@ # LastWrite time, and length of the data # # Change history +# 20200911 - MITRE updates +# 20200525 - updated date output format # 20090728 - Created +# +# https://attack.mitre.org/techniques/T1564/ # -# copyright 2009 H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package findexes; use strict; -my %config = (hive => "All", +my %config = (hive => "all", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20090728); + MITRE => "T1564", + category => "defense evasion", + output => "report", + version => 20200911); sub getConfig{return %config} sub getShortDescr { @@ -42,12 +49,14 @@ sub pluginmain { my $reg = Parse::Win32Registry->new($file); my $root_key = $reg->get_root_key; ::logMsg("Launching findexes v.".$VERSION); - ::rptMsg("findexes v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("findexes v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); traverse($root_key); # Data structure containing findings is a hash of hashes foreach my $k (keys %vals) { - ::rptMsg("Key: ".$k." LastWrite time: ".gmtime($vals{$k}{lastwrite})); + ::rptMsg("Key: ".$k." LastWrite time: ".::format8601Date($vals{$k}{lastwrite})."Z"); foreach my $i (keys %{$vals{$k}}) { next if ($i eq "lastwrite"); ::rptMsg(" Value: ".$i." Length: ".$vals{$k}{$i}." bytes"); diff --git a/thirdparty/rr-full/plugins/foxitrdr.pl b/thirdparty/rr-full/plugins/foxitrdr.pl deleted file mode 100644 index 429a99d9a93..00000000000 --- a/thirdparty/rr-full/plugins/foxitrdr.pl +++ /dev/null @@ -1,228 +0,0 @@ -#----------------------------------------------------------- -# foxitrdr.pl -# Plugin for Registry Ripper -# -# Parse Foxit Reader MRU keys: -# - HKCU\SOFTWARE\Foxit Software\Foxit Reader X.0\MRU\File MRU -# - HKCU\SOFTWARE\Foxit Software\Foxit Reader X.0\MRU\Place MRU -# - HKCU\SOFTWARE\Foxit Software\Foxit Reader X.0\Preferences\History\LastOpen -# -# The script is based on: -# - adoberdr.pl by H. Carvey -# - iexplore.pl by E. Rye esten@ryezone.net -# http://www.ryezone.net/regripper-and-internet-explorer-1 -# -# Change history -# 20170326 - First release -# -# References -# https://forensenellanebbia.blogspot.it/2017/04/regripper-plugin-to-parse-foxit-reader.html -# -# copyright 2017 Gabriele Zambelli -#----------------------------------------------------------- - -package foxitrdr; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20170326); - -sub getShortDescr { return "Get values from the user's Foxit Reader key"; } - -sub getDescr {} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::rptMsg("foxitrdr v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - # First, let's find out which version of Foxit Reader is installed - my $version; - my $tag = 0; - my @globalitems = (); - my @versions = ("4\.0","5\.0","6\.0","7\.0","8\.0","9\.0","10\.0","11\.0","12\.0","13\.0","14\.0","15\.0"); - foreach my $ver (@versions) { - my $key_path = "Software\\Foxit Software\\Foxit Reader ".$ver.""; - if (defined($root_key->get_subkey($key_path))) { - $version = $ver; - $tag = 1; - } - } - - if ($tag) { - ::rptMsg("Foxit Reader version ".$version." located."); - my $key_path = "Software\\Foxit Software\\Foxit Reader ".$version.""; - my $key; - if ($key = $root_key->get_subkey($key_path."\\MRU")) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my %vals = getKeyValues($key); - if (scalar(keys %vals) > 0) { - foreach my $v (keys %vals) { - ::rptMsg("\t".$v." -> ".$vals{$v}); - } - } - else { - } - my @sk = $key->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - ::rptMsg(""); - ::rptMsg($key_path."\\".$s->get_name()); - ::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); - my %vals = getKeyValues($s); - ::rptMsg("Note: All value names are listed in MRUList order.\n"); - foreach my $v (sort { substr($a, 4) <=> substr($b, 4) } keys %vals) { - $vals{$v} =~ s/\[F000000000\]\*//g; - ::rptMsg("\t".$v." -> ".$vals{$v}); - my $temp = ($v." -> ".$vals{$v}); - if (substr($temp, -4) =~ /\.pdf/i) { - push (@globalitems, $temp); - } - } - } - } - else { - ::rptMsg(""); - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - } - else { - ::rptMsg("Foxit Reader version not found."); - } - - if ($tag) { - my $key_path = "Software\\Foxit Software\\Foxit Reader ".$version.""; - my $key; - if ($key = $root_key->get_subkey($key_path."\\Preferences\\History\\LastOpen")) { - ::rptMsg("\n\n".$key_path."\\Preferences\\History\\LastOpen"); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my %vals = getKeyValues($key); - if (scalar(keys %vals) > 0) { - foreach my $v (keys %vals) { - ::rptMsg("\t".$v." -> ".$vals{$v}); - } - } - else { - } - my @sk = $key->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - ::rptMsg(""); - ::rptMsg($key_path."\\Preferences\\History\\LastOpen\\".$s->get_name()); - ::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); - my %vals = getKeyValues($s); - foreach my $v (keys %vals) { - if ($v =~ m/^Scale/) { - ::rptMsg("\t".$v." -> ".sprintf("%.2f",($vals{$v}*100))."%"); - } - if ($v =~ m/^Page/) { - #Page: counter starts at 0 (page 0 is the first page of the PDF) - ::rptMsg("\tLast Page Read -> ".($vals{$v}+1)); - } - if ($v =~ m/^zoomToMode/) { - # zoomToMode 1 = Zoom - # zoomToMode 2 = Actual Page - # zoomToMode 3 = Fit Page - # zoomToMode 4 = Fit Width - # zoomToMode 7 = Fit Visible - if ($vals{$v} == 1) { - ::rptMsg("\t".$v." -> ".$vals{$v}." [Zoom]"); - } - elsif ($vals{$v} == 2) { - ::rptMsg("\t".$v." -> ".$vals{$v}." [Actual Page]"); - } - elsif ($vals{$v} == 3) { - ::rptMsg("\t".$v." -> ".$vals{$v}." [Fit Page]"); - } - elsif ($vals{$v} == 4) { - ::rptMsg("\t".$v." -> ".$vals{$v}." [Fit Width]"); - } - elsif ($vals{$v} == 7) { - ::rptMsg("\t".$v." -> ".$vals{$v}." [Fit Visible]"); - } - else { - ::rptMsg("\t".$v." -> ".$vals{$v}); - } - } - if ($v =~ m/^FileName/) { - ::rptMsg("\tFileName (Short) -> ".$vals{$v}); - my $number = $s->get_name(); - foreach my $gi (@globalitems) { - if ($gi =~ /Item $number /) { - $gi =~ s/\Item $number ->//g; - ::rptMsg("\tFileName (Long ) ->".$gi); - } - } - } - if ($v =~ m/^Mode/) { - #Mode 0 = Single Page (View one page at a time) - #Mode 1 = Continuous (view pages continuously with scrolling enabled) - #Mode 2 = Facing (View two pages side by side) - #Mode 3 = Continuous facing (View pages side-by-side with continuous scrolling enabled) - if ($vals{$v} == 0) { - ::rptMsg("\t".$v." -> ".$vals{$v}." [Single Page = View one page at a time]"); - } - elsif ($vals{$v} == 1) { - ::rptMsg("\t".$v." -> ".$vals{$v}." [Continuous = View pages continuously with scrolling enabled]"); - } - elsif ($vals{$v} == 2) { - ::rptMsg("\t".$v." -> ".$vals{$v}." [Facing = View two pages side by side]"); - } - elsif ($vals{$v} == 3) { - ::rptMsg("\t".$v." -> ".$vals{$v}." [Continuous facing = View pages side-by-side with continuous scrolling enabled]"); - } - else { - ::rptMsg("\t".$v." -> ".$vals{$v}); - } - } - } - } - } - else { - ::rptMsg(""); - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - } - else { - } -} - -sub getKeyValues { - my $key = shift; - my %vals; - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - } - return %vals; -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/fsdepends.pl b/thirdparty/rr-full/plugins/fsdepends.pl new file mode 100644 index 00000000000..b932a3e7c71 --- /dev/null +++ b/thirdparty/rr-full/plugins/fsdepends.pl @@ -0,0 +1,87 @@ +#----------------------------------------------------------- +# fsdepends.pl +# get VHDX settings +# +# History +# 20220809 - created +# +# References +# https://apprize.best/microsoft/internals_1/2.html +# "VHDs can be contained within a VHD, so Windows limits the number of nesting levels of VHDs that +# it will present to the system as a disk to two, with the maximum number of nesting levels specified +# by the registry value HKLM\System\CurrentControlSet\Services\FsDepends\Parameters\VirtualDiskMaxTreeDepth. +# +# Mounting VHDs can be prevented by setting the registry value +# HKLM\System\CurrentControlSet\Services\FsDepends\Parameters\VirtualDiskNoLocalMount to 1." +# https://insights.sei.cmu.edu/blog/the-dangers-of-vhd-and-vhdx-files/ +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package fsdepends; +use strict; +my %config = (hive => "System", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1553\.005", + category => "defense evasion", + output => "report", + version => 20220809); + +sub getConfig{return %config} +sub getShortDescr { + return "Get VHD[X] Settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $key; + + ::logMsg("Launching fsdepends v.".$VERSION); + ::rptMsg("fsdepends v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services\\FsDepends\\Parameters"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = ("VirtualDiskExpandOnMount", "VirtualDiskMaxTreeDepth","VirtualDiskNoLocalMount"); + + foreach my $v (@vals) { + eval { + my $i = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-25s 0x%04x",$v,$i); + }; + ::rptMsg("Error getting ".$v." value: ".$@) if ($@); + } + + + ::rptMsg(""); + ::rptMsg("Analysis Tip: The values listed impact how Windows handles VHD[X] files, which can be used to bypass security measures,"); + ::rptMsg("including AV and MOTW."); + ::rptMsg(""); + ::rptMsg("VirtualDiskMaxTreeDepth determines how deep to do with embedding VHD files."); + ::rptMsg("VirtualDiskNoLocalMount set to 1 prevents mounting of VHD[X] files."); + ::rptMsg(""); + ::rptMsg("Ref: https://insights.sei.cmu.edu/blog/the-dangers-of-vhd-and-vhdx-files/"); + } + else { + ::rptMsg($key_path." not found."); + } +} +1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/fvestats.pl b/thirdparty/rr-full/plugins/fvestats.pl new file mode 100644 index 00000000000..1890020e2f4 --- /dev/null +++ b/thirdparty/rr-full/plugins/fvestats.pl @@ -0,0 +1,82 @@ +#----------------------------------------------------------- +# fvestats.pl +# Get BitLocker settings, including when it was enabled +# +# History: +# 20220704 - created +# +# References: +# https://twitter.com/0gtweet/status/1418322629996564480 +# https://fptu-ethical-hackers-club.github.io/posts/ACSC2021-Forensics/ +# https://thedfirreport.com/2021/11/15/exchange-exploit-leads-to-domain-wide-ransomware/ +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package fvestats; +use strict; + +my %config = (hive => "system", + output => "report", + category => "impact", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1486", + version => 20220704); + +sub getConfig{return %config} +sub getShortDescr { + return "Get BitLocker settings (when enabled, etc.)"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching fvestats v.".$VERSION); + ::rptMsg("fvestats v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + + my $key_path = $ccs."\\Control\\FVEStats"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Keypath: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my ($t0,$t1) = unpack("VV",$key->get_value("OsvEncryptInit")->get_data()); + my $t = ::getTime($t0,$t1); + ::rptMsg("OsvEncryptInit : ".::format8601Date($t)."Z"); + }; + + eval { + my ($t0,$t1) = unpack("VV",$key->get_value("OsvEncryptComplete")->get_data()); + my $t = ::getTime($t0,$t1); + ::rptMsg("OsvEncryptComplete : ".::format8601Date($t)."Z"); + }; + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: In July, 2021, the Hades Ransomware was reportedly observed using BitLocker to encrypt devices."); + ::rptMsg("As such, these artifacts may be useful in determining a timeline of activity, or developing pivot points for analysis."); +# ::rptMsg(""); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/fw_config.pl b/thirdparty/rr-full/plugins/fw_config.pl deleted file mode 100644 index 4b90dacfd91..00000000000 --- a/thirdparty/rr-full/plugins/fw_config.pl +++ /dev/null @@ -1,118 +0,0 @@ -#----------------------------------------------------------- -# fw_config -# -# References -# http://technet2.microsoft.com/WindowsServer/en/library/47f25d7d- -# 882b-4f87-b05f-31e5664fc15e1033.mspx?mfr=true -# -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package fw_config; -use strict; - -my %config = (hive => "System", - osmask => 20, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20080328); - -sub getConfig{return %config} - -sub getShortDescr { - return "Gets the Windows Firewall config from the System hive"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching fw_config v.".$VERSION); - ::rptMsg("fw_config v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# Code for System file, getting CurrentControlSet - my $current; - my $ccs; - my $select_path = 'Select'; - my $sel; - if ($sel = $root_key->get_subkey($select_path)) { - $current = $sel->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - else { - ::rptMsg($select_path." could not be found."); - ::logMsg($select_path." could not be found."); - return; - } - - my @profiles = ("DomainProfile","StandardProfile"); - foreach my $profile (@profiles) { - my $key_path = $ccs."\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\".$profile; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Windows Firewall Configuration"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my %vals = getKeyValues($key); - if (scalar(keys %vals) > 0) { - foreach my $v (keys %vals) { - ::rptMsg("\t".$v." -> ".$vals{$v}); - } - } - else { -# ::rptMsg($key_path." has no values."); - } - - my @configs = ("RemoteAdminSettings", - "IcmpSettings", - "GloballyOpenPorts\\List", - "AuthorizedApplications\\List"); - - foreach my $config (@configs) { - eval { - my %vals = getKeyValues($key->get_subkey($config)); - if (scalar(keys %vals) > 0) { - ::rptMsg(""); - ::rptMsg($key_path."\\".$config); - ::rptMsg("LastWrite Time ".gmtime($key->get_subkey($config)->get_timestamp())." (UTC)"); - foreach my $v (keys %vals) { - ::rptMsg("\t".$v." -> ".$vals{$v}); - } - } - }; - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - ::rptMsg(""); - } # end foreach -} - -sub getKeyValues { - my $key = shift; - my %vals; - - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - - } - return %vals; -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/gauss.pl b/thirdparty/rr-full/plugins/gauss.pl deleted file mode 100644 index ddf610cad87..00000000000 --- a/thirdparty/rr-full/plugins/gauss.pl +++ /dev/null @@ -1,66 +0,0 @@ -#----------------------------------------------------------- -# gauss.pl -# Checks Software hive for existance of TimeStampforUI value -# beneath the Reliability key within the Software hive. According -# to the Kasperky write-up for the malware, the configuration file is -# written to a binary value named "TimeStampforUI". -# -# copyright 2012 Quantum Analytics Research, LLC -# Author H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package gauss; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20120809); - -sub getConfig{return %config} -sub getShortDescr { - return "Checks Reliability key for TimeStampforUI value"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching gauss v.".$VERSION); - ::rptMsg("Launching gauss v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my @key_paths = ('Microsoft\\Windows\\CurrentVersion\\Reliability', - 'Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Reliability'); - ::rptMsg("gauss v\.".$VERSION); - foreach my $key_path (@key_paths) { - my $key; - my $notfound = 1; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my @vals = $key->get_list_of_values(); - foreach my $v (@vals) { - my $name = $v->get_name(); - if ($name eq "TimeStampforUI") { - ::rptMsg("TimeStampforUI value found."); - $notfound = 0; - } - } - ::rptMsg("TimeStampforUI value not found.") if ($notfound); - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/gpohist.pl b/thirdparty/rr-full/plugins/gpohist.pl index 58645cfdf97..f92e82c36f8 100644 --- a/thirdparty/rr-full/plugins/gpohist.pl +++ b/thirdparty/rr-full/plugins/gpohist.pl @@ -3,24 +3,27 @@ # # # History +# 20200911 - MITRE updates +# 20200525 - updated date output format # 20150521 - created # # References # https://support.microsoft.com/en-us/kb/201453 # -# copyright 2015 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package gpohist; use strict; -my %config = (hive => "Software","NTUSER\.DAT", - osmask => 22, - category => "settings", +my %config = (hive => "Software, NTUSER\.DAT", + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20150521); + output => "report", + version => 20200911); sub getConfig{return %config} @@ -65,7 +68,7 @@ sub pluginmain { my @subkeys2 = $sk1->get_list_of_subkeys(); if (scalar(@subkeys2) > 0) { foreach my $sk2 (@subkeys2) { - ::rptMsg(" ".$sk2->get_name()." LastWrite time: ".gmtime($sk2->get_timestamp())." UTC"); + ::rptMsg(" ".$sk2->get_name()." LastWrite time: ".::format8601Date($sk2->get_timestamp())."Z"); ::rptMsg(" DisplayName: ".$sk2->get_value("DisplayName")->get_data()); ::rptMsg(" FileSysPath: ".$sk2->get_value("FileSysPath")->get_data()); ::rptMsg(" Link : ".$sk2->get_value("Link")->get_data()); diff --git a/thirdparty/rr-full/plugins/gpohist_tln.pl b/thirdparty/rr-full/plugins/gpohist_tln.pl index fd1a8da5b2c..ede7f3104e8 100644 --- a/thirdparty/rr-full/plugins/gpohist_tln.pl +++ b/thirdparty/rr-full/plugins/gpohist_tln.pl @@ -3,6 +3,7 @@ # # # History +# 20200911 - MITRE updates # 20150529 - created # # References @@ -14,13 +15,14 @@ package gpohist_tln; use strict; -my %config = (hive => "Software","NTUSER\.DAT", - osmask => 22, - category => "settings", +my %config = (hive => "Software, NTUSER\.DAT", + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20150529); + output => "report", + version => 20200911); sub getConfig{return %config} diff --git a/thirdparty/rr-full/plugins/gthist.pl b/thirdparty/rr-full/plugins/gthist.pl deleted file mode 100644 index c52f2ebd3bb..00000000000 --- a/thirdparty/rr-full/plugins/gthist.pl +++ /dev/null @@ -1,72 +0,0 @@ -#----------------------------------------------------------- -# gthist.pl -# Google Toolbar Search History plugin -# -# -# Change history -# 20100218 - created -# -# References -# -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package gthist; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100218); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets Google Toolbar Search History"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - my %hist; - ::logMsg("Launching gthist v.".$VERSION); - ::rptMsg("gthist v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Google\\NavClient\\1.1\\History'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my @vals = $key->get_list_of_values(); - if (scalar @vals > 0) { - ::rptMsg(""); - foreach my $v (@vals) { - my $tv = unpack("V",$v->get_data()); - $hist{$tv} = $v->get_name(); - } - - foreach my $t (reverse sort {$a <=> $b} keys %hist) { - my $str = gmtime($t)." ".$hist{$t}; - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/gtwhitelist.pl b/thirdparty/rr-full/plugins/gtwhitelist.pl deleted file mode 100644 index 03cc268743c..00000000000 --- a/thirdparty/rr-full/plugins/gtwhitelist.pl +++ /dev/null @@ -1,75 +0,0 @@ -#----------------------------------------------------------- -# gtwhitelist.pl -# Google Toolbar Search History plugin -# -# -# Change history -# 20100218 - created -# -# References -# -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package gtwhitelist; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100218); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets Google Toolbar whitelist values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - my %hist; - ::logMsg("Launching gtwhitelist v.".$VERSION); - ::rptMsg("gtwhitelist v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Google\\Google Toolbar\\4.0\\whitelist'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my $allow2; - eval { - $allow2 = $key->get_value("allow2")->get_data(); - my @vals = split(/\|/,$allow2); - ::rptMsg(""); - ::rptMsg("whitelist"); - foreach my $v (@vals) { - next if ($v eq ""); - ::rptMsg(" ".$v); - } - ::rptMsg(""); - }; - - my $lastmod; - eval { - $lastmod = $key->get_value("lastmod")->get_data(); - ::rptMsg("lastmod ".gmtime($lastmod)." (UTC)"); - }; - - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/guestauth.pl b/thirdparty/rr-full/plugins/guestauth.pl new file mode 100644 index 00000000000..daeadabde4d --- /dev/null +++ b/thirdparty/rr-full/plugins/guestauth.pl @@ -0,0 +1,69 @@ +#----------------------------------------------------------- +# guestauth.pl +# +# History: +# 20201105 - created +# +# References: +# https://twitter.com/NerdPyle/status/1060618344661827584 +# https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/guest-access-in-smb2-is-disabled-by-default +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package guestauth; +use strict; + +my %config = (hive => "system", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1112", + output => "report", + version => 20201105); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets AllowInsecureGuestAuth value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching guestauth v.".$VERSION); + ::rptMsg("guestauth v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services\\LanmanWorkstation\\Parameters"; + my $key = (); + + if ($key = $root_key->get_subkey($key_path)) { + + eval { + my $g = $key->get_value("AllowInsecureGuestAuth")->get_data(); + ::rptMsg("AllowInsecureGuestAuth value = ".$g); + ::rptMsg(""); + ::rptMsg("Analsyis Tip: If the value is set to \"0\", insecure guest access is disabled. If the value is set to \"1\", insecure guest access is enabled."); + }; + ::rptMsg("AllowInsecureGuestAuth value not found.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/handler.pl b/thirdparty/rr-full/plugins/handler.pl deleted file mode 100644 index 2a4de5ee2f0..00000000000 --- a/thirdparty/rr-full/plugins/handler.pl +++ /dev/null @@ -1,61 +0,0 @@ -#----------------------------------------------------------- -# handler.pl -# -# Several pieces of malware will modify the HKCR\Network\SharingHandler key -# default value, pointing it to something other than ntshrui.dll -# -# -# References: -# http://www.trendmicro.com/vinfo/us/threat-encyclopedia/malware/worm_cosmu.elg -# -# Change history: -# 20150826 - created -# -# copyright 2015 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package handler; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - category => "malware", - version => 20150826); - -sub getConfig{return %config} -sub getShortDescr { - return "Checks HKCR/Network/SharingHandler (default) value"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching handler v.".$VERSION); - ::rptMsg("handler v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Classes\\Network\\SharingHandler"; - - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - ::rptMsg("(Default) value = ".$key->get_value("")->get_data()); - - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/haven_and_hearth.pl b/thirdparty/rr-full/plugins/haven_and_hearth.pl deleted file mode 100644 index db92c644a29..00000000000 --- a/thirdparty/rr-full/plugins/haven_and_hearth.pl +++ /dev/null @@ -1,108 +0,0 @@ -#----------------------------------------------------------- -# haven_and_hearth.pl -# Extracts the username and savedtoken for Haven & Hearth -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# Haven & Hearth Homepage -# http://www.havenandhearth.com/ -# -# Copyright (c) 2011-02-04 Brendan Coles -#----------------------------------------------------------- -# Require # -package haven_and_hearth; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110204); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts the username and savedtoken for Haven & Hearth."; -} -sub getRefs { - my %refs = ("Haven & Hearth Homepage:" => - "http://www.havenandhearth.com/"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - my @interesting_keys = ( - "username", - "password", - "savedtoken" - ); - - # Initialize # - ::logMsg("Launching haven_and_hearth v.".$VERSION); - ::rptMsg("haven_and_hearth v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\JavaSoft\\Prefs\\haven"; - - # If # Haven & Hearth path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Haven & Hearth"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Haven & Hearth registry path # - my %keys; - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Haven & Hearth registry path # - foreach my $v (@vals) { - $keys{$v->get_name()} = $v->get_data(); - } - - # Return # all key names+values for interesting keys # - foreach my $var (@interesting_keys) { - if (exists $keys{$var}) { - ::rptMsg($var." -> ".$keys{$var}); - } - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Haven & Hearth isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/heap.pl b/thirdparty/rr-full/plugins/heap.pl new file mode 100644 index 00000000000..dcaf93d2735 --- /dev/null +++ b/thirdparty/rr-full/plugins/heap.pl @@ -0,0 +1,80 @@ +#----------------------------------------------------------- +# heap.pl +# +# +# Change history +# 20220721 - added reference +# 20200911 - MITRE updates +# 20200427 - updated output date format +# 20200410 - created +# +# Ref: +# https://channel9.msdn.com/Shows/Going+Deep/RADAR-Windows-Automatic-Memory-Leak-Detection +# http://windowsir.blogspot.com/2011/09/registry-stuff.html +# https://harelsegev.github.io/posts/the-mystery-of-the-heapleakdetection-registry-key/ +# +# +# Copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package heap; +use strict; + +my %config = (hive => "Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + category => "config", + output => "report", + version => 20200911); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr {return "Checks HeapLeakDetection\\DiagnosedApplications Subkeys";} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching heap v.".$VERSION); + ::rptMsg("heap v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = 'Microsoft\\RADAR\\HeapLeakDetection\\DiagnosedApplications'; + + if ($key = $root_key->get_subkey($key_path)) { + + my @sk = $key->get_list_of_subkeys(); + if (scalar @sk > 0) { + foreach my $s (@sk) { + my $name = $s->get_name(); + my $lw = $s->get_timestamp(); + ::rptMsg($name." - LastWrite time: ".::format8601Date($lw)."Z"); + + eval { + if (my $v = $s->get_value("LastDetectionTime")->get_data()) { + my ($t0,$t1) = unpack("VV",$v); + my $last = ::getTime($t0,$t1); + ::rptMsg(" LastDetectionTime: ".::format8601Date($last)."Z"); + } + }; + ::rptMsg(""); + } + ::rptMsg("Ref: https://harelsegev.github.io/posts/the-mystery-of-the-heapleakdetection-registry-key/"); + } + } + else { + + } +} + +1; diff --git a/thirdparty/rr-full/plugins/hello.pl b/thirdparty/rr-full/plugins/hello.pl new file mode 100644 index 00000000000..12c98ad6325 --- /dev/null +++ b/thirdparty/rr-full/plugins/hello.pl @@ -0,0 +1,79 @@ +#----------------------------------------------------------- +# hello.pl +# Get Active Setup StubPath values +# +# Change history: +# 20210315 - created +# +# References: +# https://www.thewindowsclub.com/users-must-enter-a-username-and-password-to-use-this-computer-missing +# https://winaero.com/enable-passwordless-sign-in-for-microsoft-accounts/ +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, 2013 +#----------------------------------------------------------- +package hello; +use strict; + +my %config = (hive => "software", + category => "config", + MITRE => "", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210315); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check to see if \"Require Windows Hello Sign-in\" is enabled."; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching hello v.".$VERSION); + ::rptMsg("hello v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\PasswordLess\\Device"; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $a = $key->get_value("DevicePasswordLessBuildVersion")->get_data(); + ::rptMsg("DevicePasswordLessBuildVersion value: ".$a); + }; + + } + else { + ::rptMsg(""); + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Starting with Win10 Build 18936, you can enable a new Passwordless Sign-in feature, allowing you to"); + ::rptMsg("switch MS accounts on Win10 devices to using modern authentication with Windows Hello Face, Fingerprint, or PIN."); + ::rptMsg("This can help investigators understand the authentication mechanisms available on the system."); + ::rptMsg(""); + ::rptMsg("0 - Windows Hello sign-in feature disabled"); + ::rptMsg(" The \"User must enter username and password\" option should be visible in netplwiz."); + ::rptMsg("2 - Passwordless sign-in feature enabled"); + ::rptMsg("Ref: https://www.thewindowsclub.com/users-must-enter-a-username-and-password-to-use-this-computer-missing"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/hibernate.pl b/thirdparty/rr-full/plugins/hibernate.pl deleted file mode 100644 index e81acb8c73a..00000000000 --- a/thirdparty/rr-full/plugins/hibernate.pl +++ /dev/null @@ -1,80 +0,0 @@ -#----------------------------------------------------------- -# hibernate.pl -# -# Ref: -# http://support.microsoft.com/kb/293399 & testing -# -# copyright 2008-2009 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package hibernate; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20081216); - -sub getConfig{return %config} - -sub getShortDescr { - return "Check hibernation status"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching hibernate v.".$VERSION); - ::rptMsg("hibernate v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# Code for System file, getting CurrentControlSet - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - - my $power_path = $ccs."\\Control\\Session Manager\\Power"; - my $power; - if ($power = $root_key->get_subkey($power_path)) { - - my $heur; - eval { - my $bin_val = $power->get_value("Heuristics")->get_data(); - $heur = (unpack("v*",$bin_val))[3]; - if ($heur == 0) { - ::rptMsg("Hibernation disabled."); - } - elsif ($heur == 1) { - ::rptMsg("Hibernation enabled."); - } - else { - ::rptMsg("Unknown hibernation value: ".$heur); - } - - }; - ::rptMsg("Error reading Heuristics value.") if ($@); - - } - else { - ::rptMsg($power_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); -# ::logMsg($key_path." not found."); - } - -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/hiddentasks.pl b/thirdparty/rr-full/plugins/hiddentasks.pl new file mode 100644 index 00000000000..f19bdd9eed1 --- /dev/null +++ b/thirdparty/rr-full/plugins/hiddentasks.pl @@ -0,0 +1,88 @@ +#----------------------------------------------------------- +# hiddentasks.pl +# +# Change history +# 20220413 - updated code for clarity +# 20220412 - created +# +# Refs: +# https://www.microsoft.com/security/blog/2022/04/12/tarrask-malware-uses-scheduled-tasks-for-defense-evasion/ +# +# +# Copyright (c) 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package hiddentasks; +use strict; + +my %config = (hive => "software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1070", #indicator removal from host + category => "defense evasion", + version => 20220413); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr {return "Checks TaskCache\\Tree subkeys for evidence of hiding tasks";} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching hiddentasks v.".$VERSION); + ::rptMsg("hiddentasks v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE ATT&CK technique ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = 'Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tree'; + if ($key = $root_key->get_subkey($key_path)) { + traverse($key); + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: MS DART identified Tarrask malware, part of Hafnium, deleting the \"SD\" value to remain hidden"); + ::rptMsg("from view while persisting on systems."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.microsoft.com/security/blog/2022/04/12/tarrask-malware-uses-scheduled-tasks-for-defense-evasion/"); +} + +sub traverse { + my $key = shift; + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_name(); +# ::rptMsg("Key: ".$name); + eval { + my $sd = $s->get_value("SD")->get_data(); + }; + if ($@) { + ::rptMsg("Task ".$name." has no SD value!"); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + ::rptMsg(""); + } + } + } + + foreach my $subkey (@subkeys) { + traverse($subkey); + } +} + + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ide.pl b/thirdparty/rr-full/plugins/ide.pl deleted file mode 100644 index 43f47c3fee1..00000000000 --- a/thirdparty/rr-full/plugins/ide.pl +++ /dev/null @@ -1,123 +0,0 @@ -#----------------------------------------------------------- -# ide.pl -# Get IDE device info from the System hive file -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package ide; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20080418); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get IDE device info from the System hive file"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching ide v.".$VERSION); - ::rptMsg("ide v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - ::rptMsg("IDE"); - -# Code for System file, getting CurrentControlSet - my $current; - my $ccs; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - else { - ::logMsg("Could not find ".$key_path); - return - } - - $key_path = $ccs."\\Enum\\IDE"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - ::rptMsg(""); - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())."]"); - my @sk = $s->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s2 (@sk) { - ::rptMsg($s2->get_name()." [".gmtime($s2->get_timestamp())." (UTC)]"); - eval { - ::rptMsg("FriendlyName : ".$s2->get_value("FriendlyName")->get_data()); - }; - ::rptMsg(""); - } - } - - } - } - else { - ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - $key_path = $ccs."\\Control\\DeviceClasses\\{53f56307-b6bf-11d0-94f2-00a0c91efb8b}"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("DevClasses - Disks"); - ::rptMsg($key_path); - my %disks; - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - my $name = $s->get_name(); - next unless (grep(/IDE/,$name)); - my $lastwrite = $s->get_timestamp(); - my ($dev, $serial) = (split(/#/,$name))[4,5]; - push(@{$disks{$lastwrite}},$dev.",".$serial); - } - - if (scalar(keys %disks) == 0) { - ::rptMsg("No IDE subkeys were found."); - return; - } - ::rptMsg(""); - foreach my $t (reverse sort {$a <=> $b} keys %disks) { - ::rptMsg(gmtime($t)." (UTC)"); - foreach my $item (@{$disks{$t}}) { - ::rptMsg("\t$item"); - } - } - } - else { - ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/identities.pl b/thirdparty/rr-full/plugins/identities.pl index ec4cd1eaa88..b03f440c33f 100644 --- a/thirdparty/rr-full/plugins/identities.pl +++ b/thirdparty/rr-full/plugins/identities.pl @@ -3,12 +3,17 @@ # # # Change history +# 20200911 - MITRE updates +# 20200525 - updated date output format # 20151211 - created # # References # https://www.fireeye.com/blog/threat-research/2015/12/fin1-targets-boot-record.html +# - file content saved to Registry values # -# Copyright 2015 QAR LLC +# https://attack.mitre.org/techniques/T1078/ - Valid Accounts +# +# Copyright 2020 QAR LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package identities; @@ -18,8 +23,11 @@ package identities; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20151211); + MITRE => "T1078", + category => "persistence", + output => "report", + version => 20200911); + my $VERSION = getVersion(); sub getDescr {} @@ -28,7 +36,7 @@ package identities; sub getHive {return $config{hive};} sub getVersion {return $config{version};} sub getShortDescr { - return "Extracts values from Identities key; NTUSER.DAT"; + return "Extracts values from Identities key; NTUSER\.DAT"; } sub pluginmain { @@ -36,8 +44,10 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching identities v.".$VERSION); - ::rptMsg("identities v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + ::rptMsg("identities v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key; @@ -45,7 +55,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); diff --git a/thirdparty/rr-full/plugins/ie_main.pl b/thirdparty/rr-full/plugins/ie_main.pl deleted file mode 100644 index f471484cf30..00000000000 --- a/thirdparty/rr-full/plugins/ie_main.pl +++ /dev/null @@ -1,84 +0,0 @@ -#----------------------------------------------------------- -# ie_main.pl -# Checks keys/values set by new version of Trojan.Clampi -# -# Change history -# 20091019 - created -# -# -# References -# http://support.microsoft.com/kb/895339 -# http://support.microsoft.com/kb/176497 -# -# copyright 2009 H. Carvey -#----------------------------------------------------------- -package ie_main; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20091019); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets values beneath user's Internet Explorer\\Main key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching ie_main v.".$VERSION); - ::rptMsg("ie_main v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Internet Explorer\\Main'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my %main; - - my @vals = $key->get_list_of_values(); - - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); - next if ($name eq "Window_Placement"); - - $data = unpack("V",$data) if ($name eq "Do404Search"); - - if ($name eq "IE8RunOnceLastShown_TIMESTAMP" || $name eq "IE8TourShownTime") { - my ($t0,$t1) = unpack("VV",$data); - $data = gmtime(::getTime($t0,$t1))." UTC"; - } - $main{$name} = $data; - } - - foreach my $n (keys %main) { - my $str = sprintf "%-35s %-20s",$n,$main{$n}; - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ie_settings.pl b/thirdparty/rr-full/plugins/ie_settings.pl deleted file mode 100644 index c1063abc806..00000000000 --- a/thirdparty/rr-full/plugins/ie_settings.pl +++ /dev/null @@ -1,154 +0,0 @@ -#! c:\perl\bin\perl.exe -#----------------------------------------------------------- -# ie_settings.pl -# Gets IE settings -# -# Change history -# 20130731 - added check for "ClearBrowsingHistoryOnExit" -# 20130328 - added "AutoConfigURL" value info -# 20130223 - updated -# 20091016 - created -# -# References -# http://blog.digital-forensics.it/2012/05/exploring-internet-explorer-with.html -# -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package ie_settings; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20130731); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets important user IE settings"; -} -sub getDescr{} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching ie_settings v.".$VERSION); - ::rptMsg("ie_settings v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $ua; - eval { - $ua = $key->get_value("User Agent")->get_data(); - ::rptMsg("User Agent = ".$ua); - }; - - my $zonessecupgrade; - eval { - $zonessecupgrade = $key->get_value("ZonesSecurityUpgrade")->get_data(); - my ($z0,$z1) = unpack("VV",$zonessecupgrade); - ::rptMsg("ZonesSecurityUpgrade = ".gmtime(::getTime($z0,$z1))." (UTC)"); - }; - - my $daystokeep; - eval { - $daystokeep = $key->get_subkey("Url History")->get_value("DaysToKeep")->get_data(); - ::rptMsg("DaysToKeep = ".$daystokeep); - }; - if ($@) { - ::rptMsg("DaysToKeep value not found - default is 20 days"); - } -# added check for "delete history on exit" setting 20130731 - my $clear; - eval { - $clear = $key->get_subkey("Privacy")->get_value("ClearBrowsingHistoryOnExit")->get_data(); - ::rptMsg("ClearBrowsingHistoryOnExit = ".$clear); -# 1 = enabled - }; - -# AutoConfigURL -# ref: http://technet.microsoft.com/en-us/library/cc736412%28v=ws.10%29.aspx -# http://blog.spiderlabs.com/2012/04/brazilian-banking-malware-pay-your-bill-slacker-.html - eval { - my $auto = $key->get_value("AutoConfigURL")->get_data(); - ::rptMsg("AutoConfigURL: ".$auto); - ::rptMsg("**Possible malware indicator found!!"); - }; - - } - else { - ::rptMsg($key_path." not found."); - } -#----------------------------------------------------------- -# Windows Search integration into IE -# Windows Search indexes URLs for autocompletion -# -# Ref: -# http://www.ghacks.net/2011/03/17/disable-indexing-of-internet-explorer-web-history-by-windows-search/ -# -# -#----------------------------------------------------------- - $key_path = 'Software\\Microsoft\\Internet Explorer\\Main\\WindowsSearch'; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - eval { - my $v = $key->get_value("Version")->get_data(); - ::rptMsg("Version = ".$v); - }; - - ::rptMsg(""); -# Gets information about when the IE history was last cleared by the user - my $cleared; - eval { - $cleared = $key->get_value("Cleared")->get_data(); - if ($cleared == 1) { - ::rptMsg("Cleared = 1"); - my @t = unpack("VV",$key->get_value("Cleared_TIMESTAMP")->get_data()); - my $cl_ts = ::getTime($t[0],$t[1]); - ::rptMsg("Cleared_TIMESTAMP = ".gmtime($cl_ts)." UTC"); - ::rptMsg("Analysis Tip: The \'Cleared\' value indicates that the user account "); - ::rptMsg("was used to clear the IE browser history, and the timestamp value indicates"); - ::rptMsg("when this occurred\."); - } - }; - if ($@) { - ::rptMsg("\'Cleared\' value not found\."); - } - ::rptMsg(""); - eval { - my @v = unpack("VV",$key->get_value("LastCrawl")->get_data()); - my $crawl = ::getTime($v[0],$v[1]); - ::rptMsg("LastCrawl = ".gmtime($crawl)." UTC"); - }; - - eval { - my @v = unpack("VV",$key->get_value("UpgradeTime")->get_data()); - my $up = ::getTime($v[0],$v[1]); - ::rptMsg("UpgradeTime = ".gmtime($up)." UTC"); - }; - - eval { - my $path = $key->get_value("User Favorites Path")->get_data(); - ::rptMsg("User Favorites Path = ".$path); - }; - - } -} -1; diff --git a/thirdparty/rr-full/plugins/ie_version.pl b/thirdparty/rr-full/plugins/ie_version.pl deleted file mode 100644 index e54eb5b4448..00000000000 --- a/thirdparty/rr-full/plugins/ie_version.pl +++ /dev/null @@ -1,78 +0,0 @@ -#----------------------------------------------------------- -# ie_version -# Get IE version and build -# -# History -# 20140717 - updated to get svcUpdateVersion info -# 20091016 - created -# -# -# copyright 2014 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package ie_version; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20140717); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get IE version and build"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching ie_version v.".$VERSION); - ::rptMsg("ie_version v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Internet Explorer"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $version; - my $build; - eval { - $build = $key->get_value("Build")->get_data(); - ::rptMsg("IE Build = ".$build); - }; - - eval { - $version= $key->get_value("Version")->get_data(); - ::rptMsg("IE Version = ".$version); - }; - - eval { - my $svc_version= $key->get_value("svcUpdateVersion")->get_data(); - ::rptMsg("svcUpdateVersion (IE 10/11) = ".$svc_version); - }; - - eval { - my $svc_version2= $key->get_value("svcVersion")->get_data(); - ::rptMsg("svcVersion (IE 10/11) = ".$svc_version2); - }; - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ie_zones.pl b/thirdparty/rr-full/plugins/ie_zones.pl deleted file mode 100644 index a78831049f2..00000000000 --- a/thirdparty/rr-full/plugins/ie_zones.pl +++ /dev/null @@ -1,111 +0,0 @@ -#----------------------------------------------------------- -# ie_zones.pl -# Checks keys/values set by new version of Trojan.Clampi -# -# Change history -# 20140611 - created -# -# -# References -# http://support.microsoft.com/kb/182569 -# -# Info on ZoneMaps: -# http://blogs.technet.com/b/heyscriptingguy/archive/2005/05/02/how-can-i-add-a-site-to-internet-explorer-s-restricted-sites-zone.aspx -# -# copyright 2014 H. Carvey -#----------------------------------------------------------- -package ie_zones; -use strict; - -my %config = (hive => "NTUSER\.DAT,Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20140611); - -sub getConfig{return %config} -sub getShortDescr { - return "Get IE Zone settings"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching ie_zones v.".$VERSION); - ::rptMsg("ie_zones v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - my ($key,$key_path,$zone); - - my %zones = (0 => "Permitted", - 1 => "Prompt", - 3 => "Prohibited"); - - - my @paths = ('Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - 'Microsoft\\Windows\\CurrentVersion\\Internet Settings'); - - foreach $key_path (@paths) { - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); -# Get Zones and various security settings - foreach my $n (0..4) { - if (defined($key->get_subkey('Zones\\'.$n))) { - $zone = $key->get_subkey('Zones\\'.$n); - if (defined($zone->get_value("PMDisplayName"))) { - ::rptMsg("Zone ".$n.": ".$zone->get_value("PMDisplayName")->get_data()." - ".$zone->get_value("Description")->get_data()); - } else { - ::rptMsg("Zone ".$n.": ".$zone->get_value("DisplayName")->get_data()." - ".$zone->get_value("Description")->get_data()); - } - ::rptMsg("LastWrite: ".gmtime($zone->get_timestamp()." UTC")); - - my @vals = $zone->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - next unless (length($name) == 4 && $name ne "Icon"); - my $data = $v->get_data(); - $name = "**".$name if ($name eq "1609" && $data == 0); - my $str = sprintf "%6s 0x%08x",$name,$data; - # ::rptMsg(" ".$name." ".$data." ".$zones{$data}); - ::rptMsg($str." ".$zones{$data}); - } - } - ::rptMsg(""); - } - } -# Now, get ZoneMap settings - if (defined($key->get_subkey('ZoneMap\\Domains'))) { - my $zonemap = $key->get_subkey('ZoneMap\\Domains'); - my @domains = $zonemap->get_list_of_subkeys(); - if (scalar(@domains) > 0) { - foreach my $d (@domains) { - ::rptMsg("Domain: ".$d->get_name()); - - my @vals = $d->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - ::rptMsg(" ".$v->get_name()." ".$v->get_data()); - } - } - ::rptMsg(""); - } - } - } - } - else { -# ::rptMsg($key_path." not found."); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/iejava.pl b/thirdparty/rr-full/plugins/iejava.pl deleted file mode 100644 index afbaa113ebd..00000000000 --- a/thirdparty/rr-full/plugins/iejava.pl +++ /dev/null @@ -1,82 +0,0 @@ -#----------------------------------------------------------- -# iejava.pl -# -# Category: Malware -# -# History -# 20130429 - added alertMsg() functionality -# 20130214 - created -# -# References -# http://www.greyhathacker.net/?p=610 -# -# See also: http://support.microsoft.com/kb/2751647 -# -# Notes: this was seen on a system that was infected with ZeroAccess; during -# the infection process, the key in question was set and the Flags value was -# set to 1. -# -# copyright 2013, Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package iejava; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - category => "malware", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20130429); - -sub getConfig{return %config} - -sub getShortDescr { - return "Checks NTUSER for status of kill bit for IE Java ActiveX control"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - - ::logMsg("Launching iejava v.".$VERSION); - ::rptMsg("iejava v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Ext\\Settings\\{8AD9C840-044E-11D1-B3E9-00805F499D93}"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $flags; - eval { - $flags = $key->get_value("Flags")->get_data(); - ::rptMsg("Flags: ".$flags); - if ($flags == 1) { - ::rptMsg(" If the Flags value is set to 1, the IE Java ActiveX control is disabled,"); - ::rptMsg(" as if thru IE's \"Manage Add-ons\"\. Note: this NOT setting the kill bit."); - ::alertMsg("ALERT: ".$key_path." Flag value set to 1; IE Java ActiveX control disabled\."); - } - - }; - if ($@) { - ::rptMsg("Flags value not found\."); - } - - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/imagedev.pl b/thirdparty/rr-full/plugins/imagedev.pl index 20a12353d0f..11a5a4ce516 100644 --- a/thirdparty/rr-full/plugins/imagedev.pl +++ b/thirdparty/rr-full/plugins/imagedev.pl @@ -1,23 +1,26 @@ #----------------------------------------------------------- -# imagedev.pl +# imagedev.pl - Get Still Image Capture Devices # # History: +# 20200911 - MITRE updates # 20140104 - changed "FriendlyName" to "DeviceDesc" (value) # 20080813 - created # # -# copyright 2014 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package imagedev; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "", + category => "devices", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20140104); + output => "report", + version => 20200911); sub getConfig{return %config} @@ -76,8 +79,6 @@ sub pluginmain { $desc = $s->get_value("DeviceDesc")->get_data(); ::rptMsg(" ".$desc); }; - - } } else { diff --git a/thirdparty/rr-full/plugins/imagefile.pl b/thirdparty/rr-full/plugins/imagefile.pl index 6fa9b55176e..fbf83cb3423 100644 --- a/thirdparty/rr-full/plugins/imagefile.pl +++ b/thirdparty/rr-full/plugins/imagefile.pl @@ -6,30 +6,41 @@ # CWDIllegalInDllSearch: http://support.microsoft.com/kb/2264107 # http://carnal0wnage.attackresearch.com/2012/04/privilege-escalation-via-sticky-keys.html # 'Auto' value - https://docs.microsoft.com/en-us/windows/desktop/debug/configuring-automatic-debugging +# https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/dn408187(v=ws.11) +# https://cybellum.com/doubleagentzero-day-code-injection-and-persistence-technique/ +# https://twitter.com/0gtweet/status/1336035383948275714 # # Change history: +# 20201207 - added check of HKCU, rewrote most of the code to check for values +# 20200730 - MITRE ATT&CK updates +# 20200515 - updated date output format +# 20190829 - added check for AuditLevel value # 20190511 - added search for 'auto' value # 20131007 - added Carnal0wnage reference # 20130425 - added alertMsg() functionality # 20130410 - added Wow6432Node support # 20100824 - added check for "CWDIllegalInDllSearch" value # -# copyright 2013 Quantum Analytics Research, LLC +# https://attack.mitre.org/techniques/T1546/012/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package imagefile; use strict; -my %config = (hive => "Software", +my %config = (hive => "Software, NTUSER\.DAT", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - category => "malware", - version => 20190511); + MITRE => "T1546\.012", + category => "persistence", + output => "report", + version => 20200730); sub getConfig{return %config} sub getShortDescr { - return "Checks IFEO subkeys for Debugger & CWDIllegalInDllSearch values"; + return "Checks Image File Execution Options subkeys values"; } sub getDescr{} sub getRefs {} @@ -37,25 +48,42 @@ sub getShortDescr { sub getVersion {return $config{version};} my $VERSION = getVersion(); +my @vals = ("Debugger","GlobalFlag","VerifierDlls","Auto","AuditLevel","CWDIllegalInDllSearch"); +my %key_values = (); sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching imagefile v.".$VERSION); - ::rptMsg("imagefile v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("imagefile v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - my @paths = ("Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options", - "Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options"); + my @paths = (); + + if ($hive_guess eq "software") { + @paths = ("Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options", + "Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options"); + } + elsif ($hive_guess eq "ntuser") { + @paths = ("Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options", + "Software\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options"); + } + else {} foreach my $key_path (@paths) { my $key; if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); -# ::rptMsg(""); - my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { my %debug; @@ -63,54 +91,39 @@ sub pluginmain { foreach my $s (@subkeys) { my $name = $s->get_name(); next if ($name =~ m/^$i/i); - my $debugger; - eval { - $debugger = $s->get_value("Debugger")->get_data(); - }; -# If the eval{} throws an error, it's b/c the Debugger value isn't -# found within the key, so we don't need to do anything w/ the error - if ($debugger ne "") { - $debug{$name}{debug} = $debugger; - $debug{$name}{lastwrite} = $s->get_timestamp(); - } - - my $dllsearch = ""; - eval { - $dllsearch = $s->get_value("CWDIllegalInDllSearch")->get_data(); - }; -# 20190511 - added search for 'auto' value - eval { - $debug{$name}{auto} = $s->get_value("Auto")->get_data(); - }; - -# If the eval{} throws an error, it's b/c the Debugger value isn't -# found within the key, so we don't need to do anything w/ the error - if ($dllsearch ne "") { - $debug{$name}{dllsearch} = sprintf "0x%x",$dllsearch; - $debug{$name}{lastwrite} = $s->get_timestamp(); + + foreach my $v (@vals) { + eval { + $key_values{$v} = $s->get_value($v)->get_data(); + }; } - } - - if (scalar (keys %debug) > 0) { - foreach my $d (keys %debug) { - ::rptMsg($d." LastWrite: ".gmtime($debug{$d}{lastwrite})); - ::rptMsg(" Debugger : ".$debug{$d}{debug}) if (exists $debug{$d}{debug}); - ::rptMsg(" Auto : ".$debug{$d}{auto}) if (exists $debug{$d}{auto}); - ::rptMsg(" CWDIllegalInDllSearch: ".$debug{$d}{dllsearch}) if (exists $debug{$d}{dllsearch}); + + if (scalar keys %key_values > 0) { + foreach my $k (keys %key_values) { + ::rptMsg($name); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp()."Z")); + if ($k eq "CWDIllegalInDllSearch" || $k eq "GlobalFlag") { + ::rptMsg(sprintf "%-25s 0x%x",$k,$key_values{$k}); + } + else { + ::rptMsg(sprintf "%-25s %-50s",$k,$key_values{$k}); + } + } + %key_values = (); + ::rptMsg(""); } } - else { - ::rptMsg("No Debugger/CWDIllegalInDllSearch values found."); - } - ::rptMsg(""); } else { - ::rptMsg($key_path." has no subkeys."); +# ::rptMsg($key_path." has no subkeys."); } } else { - ::rptMsg($key_path." not found."); +# ::rptMsg($key_path." not found."); } } + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the plugin responds with any value names and data, including but not limited to the Debugger value"); + ::rptMsg(" those value names should be explored and analyzed further."); } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/imgburn1.pl b/thirdparty/rr-full/plugins/imgburn1.pl deleted file mode 100644 index a73a8ce9e08..00000000000 --- a/thirdparty/rr-full/plugins/imgburn1.pl +++ /dev/null @@ -1,211 +0,0 @@ -#----------------------------------------------------------- -# imgburn1.pl -# -# Gets user's ImgBurn recent files and configured paths -# -# History -# 20180630 - created -# -# References -# http://forum.imgburn.com/index.php?/forum/4-guides/ -# -# -# copyright 2018 Michael Godfrey mgodfrey [at] gmail.com -#----------------------------------------------------------- -package imgburn1; -use strict; - - -my %config = -( - hive => "NTUSER\.DAT", - hasShortDescr => 0, - hasDescr => 1, - hasRefs => 1, - osmask => 29, - version => 20180630 -); - -sub getConfig {return %config;} -sub getDescr {return "Gets user's ImgBurn MRU files and paths from NTUSER";} -sub getRefs {return "n/a";} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching imgburn1 v.".$VERSION); - ::rptMsg('imgburn1 v'.$VERSION.' ('.getDescr().")"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = 'Software\\ImgBurn'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $id; - eval { - $id = $key->get_value("InstallDirectory")->get_data(); - - }; - if ($@) { - ::rptMsg("InstallDirectory value not found."); - } - else { - ::rptMsg("InstallDirectory = ".$id); - } - - - my $bq; - eval { - $bq = $key->get_value("IBQ_MRUFile")->get_data(); - - }; - if ($@) { - ::rptMsg("IBQ_MRUFile value not found."); - } - else { - ::rptMsg("IBQ_MRUFile = ".$bq); - } - - - my $rf; - eval { - $rf = $key->get_value("ISOREAD_RecentFiles_Destination")->get_data(); - - }; - if ($@) { - ::rptMsg("ISOREAD_RecentFiles_Destination value not found."); - } - else { - ::rptMsg("ISOREAD_RecentFiles_Destination = ".$rf); - } - - - my $rs; - eval { - $rs = $key->get_value("ISOWRITE_RecentFiles_Source")->get_data(); - - }; - if ($@) { - ::rptMsg("ISOWRITE_RecentFiles_Source value not found."); - } - else { - ::rptMsg("ISOWRITE_RecentFiles_Source = ".$rs); - } - - - my $sf; - eval { - $sf = $key->get_value("ISOBUILD_MRUSourceFolder")->get_data(); - - }; - if ($@) { - ::rptMsg("ISOBUILD_MRUSourceFolder value not found."); - } - else { - ::rptMsg("ISOBUILD_MRUSourceFolder = ".$sf); - } - - - my $fs; - eval { - $fs = $key->get_value("ISOBUILD_RecentFiles_Source")->get_data(); - - }; - if ($@) { - ::rptMsg("ISOBUILD_RecentFiles_Source value not found."); - } - else { - ::rptMsg("ISOBUILD_RecentFiles_Source = ".$fs); - } - - - my $fd; - eval { - $fd = $key->get_value("ISOBUILD_RecentFiles_Destination")->get_data(); - - }; - if ($@) { - ::rptMsg("ISOBUILD_RecentFiles_Destination value not found."); - } - else { - ::rptMsg("ISOBUILD_RecentFiles_Destination = ".$fd); - } - - - my $fd; - eval { - $fd = $key->get_value("ISOBUILD_Recentfolders_Destination")->get_data(); - - }; - if ($@) { - ::rptMsg("ISOBUILD_RecentFolders_Destination value not found."); - } - else { - ::rptMsg("ISOBUILD_RecentFolders_Destination = ".$fd); - } - - - my $if; - eval { - $if = $key->get_value("FILELOCATIONS_ImageFiles")->get_data(); - - }; - if ($@) { - ::rptMsg("FILELOCATIONS_ImageFiles value not found."); - } - else { - ::rptMsg("FILELOCATIONS_ImageFiles = ".$if); - } - - my $lf; - eval { - $lf = $key->get_value("FILELOCATIONS_LogFiles")->get_data(); - - }; - if ($@) { - ::rptMsg("FILELOCATIONS_LogFiles value not found."); - } - else { - ::rptMsg("FILELOCATIONS_LogFiles = ".$lf); - } - - - my $pf; - eval { - $pf = $key->get_value("FILELOCATIONS_ProjectFiles")->get_data(); - - }; - if ($@) { - ::rptMsg("FILELOCATIONS_ProjectFiles value not found."); - } - else { - ::rptMsg("FILELOCATIONS_ProjectFiles = ".$pf); - } - - - my $qf; - eval { - $qf = $key->get_value("FILELOCATIONS_QueueFiles")->get_data(); - - }; - if ($@) { - ::rptMsg("FILELOCATIONS_QueueFiles value not found."); - } - else { - ::rptMsg("FILELOCATIONS_QueueFiles = ".$qf); - } - - - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/improviders.pl b/thirdparty/rr-full/plugins/improviders.pl new file mode 100644 index 00000000000..03d41c2344f --- /dev/null +++ b/thirdparty/rr-full/plugins/improviders.pl @@ -0,0 +1,81 @@ +#----------------------------------------------------------- +# improviders.pl +# Extracts IM Providers info from NTUSER.DAT +# +# Change history +# 20201015 - created +# +# References +# +# Copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package improviders; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + category => "user activity", + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20201015); + +my $VERSION = getVersion(); + +sub getDescr {} +sub getRefs {} +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getShortDescr { + return "Get IM providers from NTUSER\.DAT"; +} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching improviders v.".$VERSION); + ::rptMsg("improviders v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key = (); + my $key_path = "Software\\IM Providers"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $app = $key->get_value("DefaultIMApp")->get_data(); + ::rptMsg("DefaultIMApp = ".$app); + ::rptMsg(""); + }; + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + + eval { + my $up = $s->get_value("UpAndRunning")->get_data(); + ::rptMsg("UpAndRunning value = ".$up); + }; + + eval { + my $pid = $s->get_value("ProcessID")->get_data(); + ::rptMsg("ProcessID value = ".$pid); + }; + + + ::rptMsg(""); + } + } + } +} + +1; diff --git a/thirdparty/rr-full/plugins/init_dlls.pl b/thirdparty/rr-full/plugins/init_dlls.pl deleted file mode 100644 index cf6ef642079..00000000000 --- a/thirdparty/rr-full/plugins/init_dlls.pl +++ /dev/null @@ -1,79 +0,0 @@ -#----------------------------------------------------------- -# init_dlls.pl -# Plugin to assist in the detection of malware per Mark Russinovich's -# blog post (References, below) -# -# Change History: -# 20110309 - created -# -# References -# http://blogs.technet.com/b/markrussinovich/archive/2011/02/27/3390475.aspx -# -# copyright 2011 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package init_dlls; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20110309); - -sub getConfig{return %config} - -sub getShortDescr { - return "Check for odd **pInit_Dlls keys"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my @init; - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching init_dlls v.".$VERSION); - ::rptMsg("init_dlls v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Windows"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("init_dlls"); - ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - next if ($name eq "AppInit_DLLs"); - push(@init,$name) if ($name =~ m/Init_DLLs$/); - } - - if (scalar @init > 0) { - foreach my $n (@init) { - ::rptMsg($n); - } - } - else { - ::rptMsg("No additional values named *Init_DLLs located."); - } - - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/injectdll64.pl b/thirdparty/rr-full/plugins/injectdll64.pl new file mode 100644 index 00000000000..22ffa7ac159 --- /dev/null +++ b/thirdparty/rr-full/plugins/injectdll64.pl @@ -0,0 +1,77 @@ +#----------------------------------------------------------- +# injectdll64.pl +# Analysis provided at the SneakyMonkey site indicates that when the injectDll64 Trickbot +# module is run, the CertificateTransparencyEnforcementDisabledForUrls key is populated in +# order to weaken Chrome security - NOTE: this may be unique to one variant of the module +# +# Change history +# 20200911 - MITRE updates +# 20200427 - updated output date format +# 20200410 - created +# +# References +# https://sneakymonkey.net/2019/05/22/trickbot-analysis/ +# https://getadmx.com/HKCU/Software/Policies/Google/Chrome/CertificateTransparencyEnforcementDisabledForUrls +# https://www.chromium.org/administrators/policy-list-3#CertificateTransparencyEnforcementDisabledForUrls +# +# Copyright 2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package injectdll64; +use strict; + +my %config = (hive => "NTUSER\.DAT, Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + category => "malware", + output => "report", + version => 20200911); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Retrieve values set to weaken Chrome security"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching injectdll64 v.".$VERSION); + ::rptMsg("injectdll64 v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_name = "CertificateTransparencyEnforcementDisabledForUrls"; + my @paths = ("Software\\Policies\\Google\\Chrome\\".$key_name, + "Policies\\Google\\Chrome\\".$key_name); + + foreach my $key_path (@paths) { + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + ::rptMsg(" ".$v->get_name()." : ".$v->get_data()); + } + } else { + ::rptMsg($key_path." found, has no values."); + } + } + else { + ::rptMsg($key_path." not found."); + } + } +} +1; diff --git a/thirdparty/rr-full/plugins/inprocserver.pl b/thirdparty/rr-full/plugins/inprocserver.pl index 36f5b9f9b71..80e852ef464 100644 --- a/thirdparty/rr-full/plugins/inprocserver.pl +++ b/thirdparty/rr-full/plugins/inprocserver.pl @@ -3,6 +3,9 @@ # # # History +# 20201005 - MITRE update +# 20200427 - updated output date format; removed alert functionality +# 20191211 - removed Lurk check # 20141126 - minor updates # 20141112 - added support for Wow6432Node # 20141103 - updated to include detection for PowerLiks @@ -13,9 +16,6 @@ # 20130212 - fixed retrieving LW time from correct key # 20121213 - created # -# To-Do: -# - add support for NTUSER.DAT (XP) and USRCLASS.DAT (Win7) -# # References # http://www.sophos.com/en-us/why-sophos/our-people/technical-papers/zeroaccess-botnet.aspx # Apparently, per Sophos, ZeroAccess remains persistent by modifying a CLSID value that @@ -25,19 +25,20 @@ # http://www.secureworks.com/cyber-threat-intelligence/threats/malware-analysis-of-the-lurk-downloader/ # https://blog.gdatasoftware.com/blog/article/com-object-hijacking-the-discreet-way-of-persistence.html # -# copyright 2012-2014, QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package inprocserver; use strict; my %config = (hive => "Software","NTUSER\.DAT","USRCLASS\.DAT", - osmask => 22, - category => "malware", + MITRE => "T1546", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20141126); + output => "report", + version => 20201005); sub getConfig{return %config} @@ -58,8 +59,10 @@ sub pluginmain { my %susp = (); ::logMsg("Launching inprocserver v.".$VERSION); - ::rptMsg("inprocserver v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("inprocserver v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my @paths = ("Classes\\CLSID","Classes\\Wow6432Node\\CLSID","CLSID","Wow6432Node\\CLSID"); @@ -75,23 +78,7 @@ sub pluginmain { if (scalar(@sk) > 0) { foreach my $s (@sk) { my $name = $s->get_name(); - -#Check for Lurk infection (see Dell SecureWorks ref link) - if ($name eq "{A3CCEDF7-2DE2-11D0-86F4-00A0C913F750}" || $name eq "{a3ccedf7-2de2-11d0-86f4-00a0c913f750}") { - - my $l = $s->get_subkey("InprocServer32")->get_value("")->get_data(); - $l =~ tr/[A-Z]/[a-z]/; - if ($l eq "c:\\windows\\system32\\pngfilt\.dll" || $l eq "c:\\windows\\syswow64\\pngfilt\.dll") { - ::rptMsg("Possible Lurk infection found!"); - ::rptMsg(" ".$l); - } - } - - eval { - my $n = $s->get_subkey("InprocServer32")->get_value("")->get_data(); - alertCheckPath($n); - }; - + # Powerliks # http://www.symantec.com/connect/blogs/trojanpoweliks-threat-inside-system-registry # http://msdn.microsoft.com/en-us/library/windows/desktop/ms683844(v=vs.85).aspx @@ -118,21 +105,4 @@ sub pluginmain { } } -#----------------------------------------------------------- -# alertCheckPath() -#----------------------------------------------------------- -sub alertCheckPath { - my $path = shift; - $path =~ tr/[A-Z]/[a-z]/; - - my @alerts = ("recycle","globalroot","temp","system volume information","appdata", - "application data","programdata","c:\\users"); - - foreach my $a (@alerts) { - if (grep(/$a/,$path)) { - ::alertMsg("ALERT: inprocserver: ".$a." found in path: ".$path); - } - } -} - 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/installedcomp.pl b/thirdparty/rr-full/plugins/installedcomp.pl index 0382765e955..a7cd8c5a4ab 100644 --- a/thirdparty/rr-full/plugins/installedcomp.pl +++ b/thirdparty/rr-full/plugins/installedcomp.pl @@ -23,11 +23,12 @@ package installedcomp; use strict; my %config = (hive => "Software", - category => "malware", + category => "malware", osmask => 22, hasShortDescr => 1, hasDescr => 0, hasRefs => 0, + output => "report", version => 20130911); sub getConfig{return %config} diff --git a/thirdparty/rr-full/plugins/installelevated.pl b/thirdparty/rr-full/plugins/installelevated.pl new file mode 100644 index 00000000000..41213485857 --- /dev/null +++ b/thirdparty/rr-full/plugins/installelevated.pl @@ -0,0 +1,92 @@ +#----------------------------------------------------------- +# installelevated.pl +# +# +# Change history +# 20230703 - created +# +# References +# https://twitter.com/malmoeb/status/1564629592723361794 +# https://learn.microsoft.com/en-us/windows/win32/msi/alwaysinstallelevated +# https://juggernaut-sec.com/alwaysinstallelevated/ +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package installelevated; +use strict; + +my %config = (hive => "software, ntuser\.dat", + category => "privilege escalation", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546\.016", + output => "report", + version => 20230703); + +sub getConfig{return %config} +sub getShortDescr { + return "Check AlwaysInstallElevated value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching installelevated v.".$VERSION); + ::rptMsg("installelevated v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + + my $key_path = (); + my $key; + + if ($hive_guess eq "software") { + $key_path = 'Policies\\Microsoft\\Windows\\Installer'; + } + elsif ($hive_guess eq "ntuser") { + $key_path = 'Software\\Policies\\Microsoft\\Windows\\Installer'; + } + else {} + + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("installelevated"); + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $a = $key->get_value("AlwaysInstallElevated")->get_data(); + ::rptMsg("AlwaysInstallElevated value: ".$a); + if ($a == 1) { + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the \"AlwaysInstallElevated\" value is set to 1, an attacker can escalate privileges"); + ::rptMsg("to SYSTEM."); + ::rptMsg(""); + ::rptMsg("Ref: https://learn.microsoft.com/en-us/windows/win32/msi/alwaysinstallelevated"); + } + }; + } + else { + ::rptMsg($key_path." key not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/installer.pl b/thirdparty/rr-full/plugins/installer.pl index 09d992bc8fe..e93730a8cdc 100644 --- a/thirdparty/rr-full/plugins/installer.pl +++ b/thirdparty/rr-full/plugins/installer.pl @@ -4,20 +4,24 @@ # Publisher values from Installer\UserData subkeys # # History +# 20200803 - minor updates +# 20200517 - updated date output format # 20120917 - created # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package installer; use strict; -my %config = (hive => "Software", +my %config = (hive => "software", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20120917); + category => "config", + MITRE => "", + output => "report", + version => 20200803); sub getConfig{return %config} sub getShortDescr { @@ -35,7 +39,7 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching installer v.".$VERSION); ::rptMsg("Launching installer v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -68,43 +72,46 @@ sub processSubkeys { my $key = shift; my $name = $key->get_name(); - my @subkeys = $key->get_subkey("Products")->get_list_of_subkeys(); + if (my $prod = $key->get_subkey("Products")) { - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { + my @subkeys = $prod->get_list_of_subkeys(); + + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { - my ($display, $date, $version, $publisher); - my $str; - my $lw = $s->get_timestamp(); - ::rptMsg("Key : ".$s->get_name()); - ::rptMsg("LastWrite: ".gmtime($lw)); - eval { - $date = $s->get_subkey("InstallProperties")->get_value("InstallDate")->get_data(); - $str = $date." - "; - }; + my ($display, $date, $version, $publisher); + my $str; + my $lw = $s->get_timestamp(); + ::rptMsg("Key : ".$s->get_name()); + ::rptMsg("LastWrite: ".::format8601Date($lw)."Z"); + eval { + $date = $s->get_subkey("InstallProperties")->get_value("InstallDate")->get_data(); + $str = $date." - "; + }; - eval { - $display = $s->get_subkey("InstallProperties")->get_value("DisplayName")->get_data(); - $str .= $display; - }; + eval { + $display = $s->get_subkey("InstallProperties")->get_value("DisplayName")->get_data(); + $str .= $display; + }; - eval { - $version = $s->get_subkey("InstallProperties")->get_value("DisplayVersion")->get_data(); - $str .= " ".$version; - }; + eval { + $version = $s->get_subkey("InstallProperties")->get_value("DisplayVersion")->get_data(); + $str .= " ".$version; + }; - eval { - $publisher = $s->get_subkey("InstallProperties")->get_value("Publisher")->get_data(); - $str .= " (".$publisher.") "; - }; + eval { + $publisher = $s->get_subkey("InstallProperties")->get_value("Publisher")->get_data(); + $str .= " (".$publisher.") "; + }; - ::rptMsg($str); - ::rptMsg(""); - } + ::rptMsg($str); + ::rptMsg(""); + } - } - else { - ::rptMsg("Key ".$name." has no subkeys."); + } + else { + ::rptMsg("Key ".$name." has no subkeys."); + } } } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/installerlogging.pl b/thirdparty/rr-full/plugins/installerlogging.pl new file mode 100644 index 00000000000..4e6db435363 --- /dev/null +++ b/thirdparty/rr-full/plugins/installerlogging.pl @@ -0,0 +1,69 @@ +#----------------------------------------------------------- +# installerlogging.pl +# Attempts to get InstallDate, DisplayName, DisplayVersion, and +# Publisher values from Installer\UserData subkeys +# +# History +# 20230213 - created +# +# Ref: +# https://learn.microsoft.com/ja-jp/troubleshoot/windows-client/application-management/enable-windows-installer-logging +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package installerlogging; +use strict; + +my %config = (hive => "software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + category => "config", + MITRE => "", + output => "report", + version => 20230213); + +sub getConfig{return %config} +sub getShortDescr { + return "Determines product/MSI install logging"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::logMsg("Launching installerlogging v.".$VERSION); + ::rptMsg("Launching installerlogging v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key_path = 'Policies\\Microsoft\\Windows\\Installer'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("Installer"); + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg(""); + + eval { + my $l = $key->get_value("logging")->get_data(); + ::rptMsg("logging value: ".$l); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Parse the REG_SZ value based on the below reference."); + ::rptMsg(""); + ::rptMsg("Ref: https://learn.microsoft.com/ja-jp/troubleshoot/windows-client/application-management/enable-windows-installer-logging"); + }; + + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/installproperties.pl b/thirdparty/rr-full/plugins/installproperties.pl new file mode 100644 index 00000000000..c5fdb3bc107 --- /dev/null +++ b/thirdparty/rr-full/plugins/installproperties.pl @@ -0,0 +1,89 @@ +#----------------------------------------------------------- +# installproperties +# +# Change history: +# 20221031 - created +# +# Ref: +# https://twitter.com/SBousseaden/status/1586862562624299010 +# https://twitter.com/Arkbird_SOLG/status/1131178793350193153 +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package installproperties; +use strict; + +my %config = (hive => "software", + category => "execution", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1204\.002", + version => 20221031); + +sub getConfig{return %config} +sub getShortDescr { + return "Get InstallProperties settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching installproperties v.".$VERSION); + ::rptMsg("installproperties v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my $key_path = ('Microsoft\\Windows\\CurrentVersion\\Installer\\UserData'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys1 = $key->get_list_of_subkeys(); + if (scalar @subkeys1 > 0) { + foreach my $sk1 (@subkeys1) { + my $key_path2 = $key_path."\\".$sk1->get_name()."\\Products"; + if (my $key2 = $root_key->get_subkey($key_path2)) { + my @subkeys2 = $key2->get_list_of_subkeys(); + if (scalar @subkeys2 > 0) { + foreach my $sk2 (@subkeys2) { + + eval { + my $d = $sk2->get_subkey("InstallProperties")->get_value("DisplayName")->get_data(); + ::rptMsg("DisplayName: ".$d); + }; + + eval { + my $d = $sk2->get_subkey("InstallProperties")->get_value("InstallDate")->get_data(); + ::rptMsg(" InstallDate: ".$d); +# ::rptMsg(" Key LastWrite Time ".::format8601Date($sk2->get_timestamp())."Z"); + }; + + eval { + my $d = $sk2->get_subkey("InstallProperties")->get_value("InstallSource")->get_data(); + ::rptMsg(" InstallSource: ".$d); + }; + + ::rptMsg(""); + } + } + } + } + } + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/internet_explorer_cu.pl b/thirdparty/rr-full/plugins/internet_explorer_cu.pl deleted file mode 100644 index 2260da51958..00000000000 --- a/thirdparty/rr-full/plugins/internet_explorer_cu.pl +++ /dev/null @@ -1,575 +0,0 @@ -#------------------------------------------------------------------------------ -# internet_explorer_cu.pl -# NTUSER.DAT Internet Explorer key parser -# Try to get useful information on IE -# Note: it's not tested against all IE versions available -# WARNING: there exist a huge work to be done, IE settings -# are a lot and they are sparse in registries -# -# Change history -# 20120426 [fpi] % created and working on -# 20120513 [fpi] % first release -# 20120528 [fpi] % released to public -# -# References -# "Geoff Chappell - Internet Explorer Registry API " => -# "http://www.geoffchappell.com/studies/windows/ie/iertutil/api/ierapi/index.htm", -# "Internet Explorer Maintenance Extension Tools and Settings" -# http://technet.microsoft.com/en-us/library/cc736412%28v=ws.10%29.aspx -# "Introduction to Web Storage" -# http://msdn.microsoft.com/en-us/library/cc197062%28v=vs.85%29.aspx -# "How can I configure my Internet Explorer browser settings after I have removed malicious software from my computer?" -# http://support.microsoft.com/kb/895339 -# "How to Change the Internet Explorer Window Title" -# http://support.microsoft.com/kb/176497 -# -# The plugin will not parse *every* IE subkeys. The list of subkeys I was able -# to found inside my NTUSER.DAT registries (a join of XP, Vista, 7) is following. Note that: -# (P) means parsed, (*) means not parsed but interesting (a TODO), nothing means not parsed. -# -# Registries coming from (and tested on): -# (A) Windows7 Professional 32bit - IE 9.0.8112.16421 -# (B) Windows7 Ultimate 64bit - IE 9.0.8112.16421 -# (C) Windows XP Home 32bit - IE 8.0.6001.18702 -# (D) Windows Vista 64bit - IE 7.0.6002.18005 -# -# HKCU\Software\Microsoft\Internet Explorer subkeys list: -# -# Activities (*) [ A ] -# ApprovedExtensions (*) [ B ] -# ApproveExtensionsMigration (*) [ A B ] -# AutoComplete (P) [ A ] -# BrowserEmulation [ A B C ] -# CaretBrowsing [ A ] -# CommandBar [ A B C D ] -# Default HTML Editor [ C D ] -# Default MHTML Editor [ D ] -# Desktop [ A B C D ] -# Document Windows [ A B C D ] -# DOMStorage (P) [ A B C ] -# Download (*) [ A B C D ] -# DxTrans [ A ] -# Expiration [ A ] -# Explorer Bars [ A ] -# Extensions (*) [ A B C D ] -# Feed Discovery [ A ] -# Feeds [ A D ] -# Geolocation (*) [ A ] -# GPActivities [ A ] -# GPU [ A B ] -# Help_Menu_URLs [ A B C D ] -# IEDevTools (*) [ A B ] -# IETld (P) [ A B C ] -# InformationBar [ C D ] -# IntelliForms (*) [ A B C D ] -# International (*) [ A B C D ] -# InternetRegistry [ A B C D ] -# LinksBar [ A B C ] -# LinksExplorer [ A C D ] -# LowRights [ B D ] -# LowRegistry [ A B C D ] -# Main (P) [ A B C D ] -# MAO Settings [ A B C ] -# Media [ A C D ] -# MenuExt (*) [ A B C D ] -# MINIE [ A B ] -# New Windows [ A B C D ] -# PageSetup [ A B C D ] -# PhishingFilter (*) [ A B C D ] -# Privacy (P) [ A C ] (user settings ndr) -# ProtocolExecute [ A ] -# Recovery (P) [ A B C ] -# Safety [ A ] -# SearchScopes (*) [ A B C D ] -# SearchUrl [ A B C D ] -# Security (*) [ A B C D ] -# Services [ A B C D ] (empty? ndr) -# Settings [ A B C D ] -# Setup [ A B D ] -# SiteMode [ A B C D ] -# SQM (*) [ A B C ] -# Styles [ A ] -# Suggested Sites (P) [ A B C ] -# TabbedBrowsing [ A B C D ] -# TaskbarPreview [ A ] -# Text Scaling [ A ] -# Toolbar [ A B C D ] -# TypedURLs [ B C ] (hum?! ndr) -# UpgradeIEAd [ A ] -# URLSearchHooks (*) [ A B C D ] -# User Preferences (*) [ A B C ] -# View Source Editor [ A ] -# Zoom [ A B C D ] -# -# copyright 2012 F. Picasso francesco.picasso@gmail.com -#------------------------------------------------------------------------------ -package internet_explorer_cu; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20120528); - -sub getConfig{return %config} -sub getShortDescr { - return "Get HKCU information on Internet Explorer"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Geoff Chappell - Internet Explorer Registry API " => - "http://www.geoffchappell.com/studies/windows/ie/iertutil/api/ierapi/index.htm", - "Internet Explorer Maintenance Extension Tools and Settings" => - "http://technet.microsoft.com/en-us/library/cc736412%28v=ws.10%29.aspx", - "Introduction to Web Storage" => - "http://msdn.microsoft.com/en-us/library/cc197062%28v=vs.85%29.aspx", - "How can I configure my Internet Explorer browser settings after I have removed malicious software from my computer?" => - "http://support.microsoft.com/kb/895339", - "How to Change the Internet Explorer Window Title" => - "http://support.microsoft.com/kb/176497" - ); -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -my $VERSION = getVersion(); - -#------------------------------------------------------------------------------ - -my $tab0 = ""; -my $tab2 = " "; -my $tab4 = " "; -my $tab6 = " "; -my $tab8 = " "; - -my $align10 = "%-10s"; -my $align15 = "%-15s"; -my $align20 = "%-20s"; -my $align25 = "%-25s"; -my $align30 = "%-30s"; -my $align40 = "%-40s"; - -#------------------------------------------------------------------------------ - -my %IE_MAIN_TRANSLATE = ( - "AdminTabProcs" => \&trBool, - "AllowWindowReuse" => \&trBool, - "AlwaysShowMenus" => \&trBool, - "AutoSearch" => \&trBool, - "Cleanup HTCs" => \&trBool, - "CompatibilityFlags" => \&trNumHex, - "Display Inline Videos" => \&trBool, - "DNSPreresolution" => \&trNumHex, - "Do404Search" => \&trDo404Search, - "DOMStorage" => \&trBool, - "DownloadWindowPlacement" => \&trSkip, - "EnableSearchPane" => \&trBool, - "ForceGDIPlus" => \&trBool, - "FrameMerging" => \&trBool, - "FrameShutdownDelay" => \&trBool, - "FrameTabWindow" => \&trBool, - "GotoIntranetSiteForSingleWordEntry" => \&trBool, - "HangRecovery" => \&trBool, - "HistoryViewType" => \&trHex, - "IE8RunOnceCompletionTime" => \&trFileTime, - "IE8RunOnceLastShown" => \&trBool, - "IE8RunOnceLastShown_TIMESTAMP" => \&trFileTime, - "IE8RunOncePerInstallCompleted" => \&trBool, - "IE8TourShown" => \&trBool, - "IE8TourShownTime" => \&trFileTime, - "IE9RecommendedSettingsNo" => \&trBool, - "IE9RunOnceCompletionTime" => \&trFileTime, - "IE9RunOnceLastShown" => \&trBool, - "IE9RunOncePerInstallCompleted" => \&trBool, - "IE9TourNoShow" => \&trBool, - "IE9TourShown" => \&trBool, - "IE9TourShownTime" => \&trFileTime, - "MinIEEnabled" => \&trBool, - "NoUpdateCheck" => \&trBool, - "NscSingleExpand" => \&trBool, - "Q300829" => \&trBool, - "SearchControlWidth" => \&trSkip, - "SessionMerging" => \&trBool, - "Show image placeholders" => \&trBool, - "ShutdownWaitForOnUnload" => \&trBool, - "SmoothScroll" => \&trSkip, - "Start Page Redirect Cache_TIMESTAMP" => \&trFileTime, - "StatusBarWeb" => \&trBool, - "SuppressScriptDebuggerDialog" => \&trBool, - "TabShutdownDelay" => \&trNumHex, - "Use Stylesheets" => \&trBool, - "UseHR" => \&trBool, - "UseThemes" => \&trBool, - "Window_Placement" => \&trSkip, - "XDomainRequest" => \&trBool, - "XMLHTTP" => \&trBool -); - -my %IE_MAIN_WINSEARCH_TRANSLATE = ( - "AutoCompleteGroups" => \&trNumHex, - "Cleared" => \&trBool, - "Cleared_TIMESTAMP" => \&trFileTime, - "ConfiguredScopes" => \&trNumHex, - "Disabled" => \&trBool, - "EnabledScopes" => \&trNumHex, - "LastCrawl" => \&trFileTime, - "UpgradeTime" => \&trFileTime -); - -my %IE_PRIVACY_TRANSLATE = ( - "CleanDownloadHistory" => \&trBool, - "CleanInPrivateBlocking" => \&trBool, - "CleanPassword" => \&trBool, - "CleanTrackingProtection" => \&trBool, - "ClearBrowsingHistoryOnExit" => \&trBool, - "UseAllowList" => \&trBool -); - -my %IE_RECOVERY_TRANSLATE = ( - "AutoRecover" => \&trBool, - "NoReopenLastSession" => \&trBool -); - -my %IE_SUGGSITES_TRANSLATE = ( - "MigrationTime" => \&trFileTime, - "ObjectsCreated" => \&trBool, - "ObjectsCreated_TIMESTAMP" => \&trFileTime -); - -#------------------------------------------------------------------------------ - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg( "Launching internet_explorer_cu v.".$VERSION ); - ::rptMsg( "internet_explorer_cu v.".$VERSION ); - ::rptMsg( "(".getHive().") ".getShortDescr()."\n" ); - - my $reg = Parse::Win32Registry->new( $hive ); - my $root_key = $reg->get_root_key; - my $key_path_ie = "Software\\Microsoft\\Internet Explorer"; - my $key_path = $key_path_ie; - my $key; - my $tab; my $align; - my $vdata; my $vname; - - # 20120426 [fpi] : getting the main key - $key = $root_key->get_subkey( $key_path ); - if ( not $key ) { - ::rptMsg( $key_path." not found." ); - ::logMsg( $key_path." not found." ); - return; - } - - # 20120426 [fpi] : getting, if available, the DownloadDirectory - $tab = $tab2; - $align = $align10; - $vname = "Download Directory"; - ::rptMsg( $key_path ); - ::rptMsg( "LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - $vdata = getValueData( $key, $vname, undef ); - ::rptMsg( sprintf( $tab."$align = '%s'", $vname, $vdata ) ); - ::rptMsg( "" ); - - # --------------------------------------------------------------- - # 20120426 [fpi] : not parsing "ApprovedExtensionsMigration" and - # "ApprovedExtensions" subkeys, which could be - # useful for malware removal and/or for IE timestamping - # Ref: "Internet Explorer Maintenance Extension Tools and Settings" - # http://technet.microsoft.com/en-us/library/cc736412%28v=ws.10%29.aspx - - # --------------------------------------------------------------- - # 20120426 [fpi] : parsing, if available, the AutoComplete subkey - $key_path = $key_path_ie."\\AutoComplete"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( $key_path ); - ::rptMsg( "LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValues( $key, $tab2, $align10 ); - } - else { - ::rptMsg( $key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - ::rptMsg( "" ); - - # --------------------------------------------------------------- - # 20120426 [fpi] : parsing "DOMstorage", no informations (apart guessing) on the Total - # subkey and values - # Ref: "Introduction to Web Storage" - # http://msdn.microsoft.com/en-us/library/cc197062%28v=vs.85%29.aspx - $key_path = $key_path_ie."\\DOMStorage"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( $key_path ); - ::rptMsg( "LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - ::rptMsg( "Subkeys:" ); - rptAllSubKeys( $key, $tab2, $align20 ); - } - else { - ::rptMsg( $key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - ::rptMsg( "" ); - - # --------------------------------------------------------------- - # 20120502 [fpi] : parsing "IETld", no informations found, guessing - # I sometimes noticed a discrepancy in the last WORD (16bit) - # value between SOFTWARE key and NTUSER key (??) - $key_path = $key_path_ie."\\IETld"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( $key_path ); - ::rptMsg( "LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - $vname = "IETldDllVersionHigh"; - $vdata = getValueData( $key, $vname, undef, 1 ); - my ($vhi1, $vhi2) = ("????", "????"); - if ( defined $vdata ) { $vhi1 = $vdata >> 16; $vhi2 = $vdata & 0x0000FFFF; } - $vname = "IETldDllVersionLow"; - $vdata = getValueData( $key, $vname, undef, 1 ); - my ($vlo1, $vlo2) = ("????", "????"); - if ( defined $vdata ) { $vlo1 = $vdata >> 16; $vlo2 = $vdata & 0x0000FFFF; } - ::rptMsg( $tab2."Internet Explorer version = $vhi1.$vhi2.$vlo1.$vlo2" ); - } - else { - ::rptMsg( $key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - ::rptMsg( "" ); - - # --------------------------------------------------------------- - # 20120502 [fpi] : parsing "Main" and "WindowsSearch" subkey. - # Not parsing subkeys "FeatureControl" (could be relevant for - # the security settings) and "Touch". - $key_path = $key_path_ie."\\Main"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( $key_path ); - ::rptMsg( "LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValuesTrans( $key, \%IE_MAIN_TRANSLATE, $tab2, $align40 ); - #--- Windows Search subkey - $key_path .= "\\WindowsSearch"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( "" ); - ::rptMsg( $tab2.$key_path ); - ::rptMsg( $tab2."LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValuesTrans( $key, \%IE_MAIN_WINSEARCH_TRANSLATE, $tab4, $align25 ); - } - else { - ::rptMsg( $tab.$key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - } - else { - ::rptMsg( $key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - ::rptMsg( "" ); - - # --------------------------------------------------------------- - # 20120502 [fpi] : parsing "Privacy", no info here apart guessing. Tests were - # made on Win7 systems: the presence of this key should attest - # that the user changed the Privacy settings; the absence that - # IE is using defaults settings. Counterchecks welcome. - $key_path = $key_path_ie."\\Privacy"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( $key_path ); - ::rptMsg( "LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValuesTrans( $key, \%IE_PRIVACY_TRANSLATE, $tab2, $align30 ); - } - else { - ::rptMsg( $key_path." not found (IE should use the default Privacy settings)" ); - ::logMsg( $key_path." not found." ); - } - ::rptMsg( "" ); - - # --------------------------------------------------------------- - # 20120502 [fpi] : parsing "Recovery", no information just parsing - $key_path = $key_path_ie."\\Recovery"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( $key_path ); - ::rptMsg( "LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValuesTrans( $key, \%IE_RECOVERY_TRANSLATE, $tab2, $align25 ); - #--- Subkeys - $key_path = $key_path_ie."\\Recovery"."\\Active"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( "\n".$tab2.$key_path ); - ::rptMsg( $tab2."LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValues( $key, $tab4, $align25 ); - } - else { - ::rptMsg( "\n".$tab2.$key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - $key_path = $key_path_ie."\\Recovery"."\\AdminActive"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( "\n".$tab2.$key_path ); - ::rptMsg( $tab2."LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValues( $key, $tab4, $align25 ); - } - else { - ::rptMsg( "\n".$tab2.$key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - $key_path = $key_path_ie."\\Recovery"."\\PendingDelete"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( "\n".$tab2.$key_path ); - ::rptMsg( $tab2."LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValues( $key, $tab4, $align25 ); - } - else { - ::rptMsg( "\n".$tab2.$key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - } - else { - ::rptMsg( $key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - ::rptMsg( "" ); - - # --------------------------------------------------------------- - # 20120502 [fpi] : parsing "Suggested Site", lot of web info regarding - # the privacy issue derived from this feature. But almost - # every privacy issue is a good source for an analyst ;) - $key_path = $key_path_ie."\\Suggested Sites"; - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( $key_path ); - ::rptMsg( "LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValuesTrans( $key, \%IE_SUGGSITES_TRANSLATE, $tab2, $align30 ); - } - else { - ::rptMsg( $key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - ::rptMsg( "" ); -} - -#------------------------------------------------------------------------------ - -sub trBool -{ - my $data = shift; my $temp = "true "; - if ( $data != 0 and $data != 1 ) { - $temp = "$data (WARNING: expected a boolean '0|1'!)"; - return $temp; - } - $temp = "false" if ( $data == 0 ); - $temp .= " [$data]"; - return $temp; -} - -sub trDo404Search -{ - my $data = shift; my $temp; - $temp = unpack( "V" , $data ); - return $temp." [0x".unpack( "H*", $data )."]"; -} - -sub trFileTime -{ - my $data = shift; - my ( $t0, $t1 ) = unpack( "VV",$data ); - $data = gmtime( ::getTime( $t0, $t1 ) )." UTC"; - return $data; -} - -sub trHex -{ - my $data = shift; - $data = unpack( "H*", $data ); - return "0x".$data; -} - -sub trNumHex -{ - my $data = shift; - return sprintf( "%u [0x%08X]", $data, $data ); -} - -sub trSkip -{ - return ""; -} - -#------------------------------------------------------------------------------ - -sub getKeyValues { - my $key = shift; - my %vals; - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - } - return %vals; -} - -#------------------------------------------------------------------------------ - -sub getValueData -{ - # key, value name, translator, use stub - my $key = shift; my $vn = shift; - my $trans = shift; my $stub = shift; - my $vd; my $vo; - $vo = $key->get_value( $vn ); - if ( not defined $vo ) { - return undef unless defined $stub; - $vd = ""; - } - else { - $vd = $vo->get_data(); - if ( defined $trans ) { - $vd = $trans->( $vd ); - } - } - return $vd; -} - -#------------------------------------------------------------------------------ - -sub rptAllSubKeys -{ - # key, tab, align - my @subkeys = $_[0]->get_list_of_subkeys(); - foreach my $k (@subkeys) { - ::rptMsg( sprintf( $_[1]."$_[2] --- %s", - $k->get_name() ) . gmtime( $k->get_timestamp() ) . " UTC" ); - } -} - -#------------------------------------------------------------------------------ - -sub rptAllKeyValues -{ - # key, tab, align - my @vals = sort {lc $a->get_name() cmp lc $b->get_name} $_[0]->get_list_of_values(); - foreach my $v (@vals) { - my $val = $v->get_name(); - my $data = $v->get_data(); - ::rptMsg( sprintf( $_[1]."$_[2] = %s", $val, $data ) ); - } -} -#------------------------------------------------------------------------------ - -sub rptAllKeyValuesTrans -{ - # key, ttlb, tab, align, - my $key = shift; my $ttlb = shift; - my $tab = shift; my $align = shift; - my $vname; my $vdata; my $trans; - - my @vals = sort {lc $a->get_name() cmp lc $b->get_name} $key->get_list_of_values(); - foreach my $v (@vals) { - $vname = $v->get_name(); - $vdata = $v->get_data(); - $trans = ${$ttlb}{$vname}; - $vdata = $trans->( $vdata ) if ( defined $trans ); - ::rptMsg( sprintf( $tab."$align = %s", $vname, $vdata ) ); - } -} - -#------------------------------------------------------------------------------ -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/internet_settings_cu.pl b/thirdparty/rr-full/plugins/internet_settings_cu.pl deleted file mode 100644 index 961d42d2bad..00000000000 --- a/thirdparty/rr-full/plugins/internet_settings_cu.pl +++ /dev/null @@ -1,534 +0,0 @@ -#------------------------------------------------------------------------------ -# internet_settings_cu.pl -# NTUSER.DAT Internet Settings key parser -# Note: it's not tested against all IE versions available, neither -# it parses all available keys/subkeys -# -# Change history -# 20120513 [fpi] % created and working on -# 20120515 [fpi] % first release -# 20120528 [fpi] % released to public -# -# References -# "Internet Explorer 6.0 Registry Settings" -# http://msdn.microsoft.com/en-us/library/ms902093.aspx -# "WinInet Registry Settings" -# http://msdn.microsoft.com/en-us/library/aa918417.aspx -# -# copyright 2012 F. Picasso francesco.picasso@gmail.com -#------------------------------------------------------------------------------ -package internet_settings_cu; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20120528); - -sub getConfig{return %config} -sub getShortDescr { - return "Get HKCU information on Internet Settings"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Internet Explorer 6.0 Registry Settings" => - "http://msdn.microsoft.com/en-us/library/ms902093.aspx", - "WinInet Registry Settings" => - "http://msdn.microsoft.com/en-us/library/aa918417.aspx" - ); -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -my $VERSION = getVersion(); - -#------------------------------------------------------------------------------ - -my $tab0 = ""; -my $tab2 = " "; -my $tab4 = " "; -my $tab6 = " "; -my $tab8 = " "; - -my $align10 = "%-10s"; -my $align15 = "%-15s"; -my $align20 = "%-20s"; -my $align25 = "%-25s"; -my $align30 = "%-30s"; -my $align40 = "%-40s"; - -#------------------------------------------------------------------------------ - -my %PARSED_SUBKEYS = ( - "5.0" => \&cb50, - "CACHE" => \&cbCACHE, - "P3P" => \&cbP3P, - "Url History" => \&cbUrlHistory, - "Wpad" => \&cbWpad, - "ZoneMap" => \&cbZoneMap -); - -my %INTERNET_SETTINGS = ( - "AutoConfigProxy" => undef, - "BackgroundConnections" => \&trBool, - "CertificateRevocation" => \&trBool, - "CoInternetCombineIUriCacheSize" => \&trNumHex, - "CreateUriCacheSize" => \&trNumHex, - "DisableCachingOfSSLPages" => \&trBool, - "EmailName" => undef, - "EnableAutodial" => \&trBool, - "EnableHttp1_1" => \&trBool, - "EnableNegotiate" => \&trBool, - "EnablePunycode" => \&trBool, - "GlobalUserOffline" => \&trBool, - "IE5_UA_Backup_Flag" => undef, - "MigrateProxy" => \&trBool, - "MimeExclusionListForCache" => undef, - "NoNetAutodial" => \&trBool, - "PrivacyAdvanced" => \&trBool, - "PrivDiscUiShown" => \&trBool, - "ProxyEnable" => \&trBool, - "ProxyHttp1.1" => \&trBool, - "ProxyOverride" => undef, - "SecureProtocols" => \&trNumHex, - "SecurityIdIUriCacheSize" => \&trNumHex, - "ShowPunycode" => \&trBool, - "SpecialFoldersCacheSize" => \&trNumHex, - "SyncMode5" => \&trSyncMode5, - "UrlEncoding" => \&trBool, - "User Agent" => undef, - "UseSchannelDirectly" => \&trHex, - "WarnOnIntranet" => \&trBool, - "WarnOnPost" => \&trHex, - "WarnonZoneCrossing" => \&trBool, - "ZonesSecurityUpgrade" => \&trFileTime -); - -my %CACHE_VALUES = ( - "LastScavenge" => \&trBool, - "LastScavenge_TIMESTAMP" => \&trFileTime, - "Persisten" => \&trBool -); - -my %WPAD_VALUES = ( - "WpadDecision" => undef, - "WpadDecisionReason" => undef, - "WpadDecisionTime" => \&trFileTime, - "WpadNetworkName" => undef -); - -#------------------------------------------------------------------------------ - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg( "Launching internet_settings_cu v.".$VERSION ); - ::rptMsg( "internet_settings_cu v.".$VERSION ); - ::rptMsg( "(".getHive().") ".getShortDescr()."\n" ); - - my $reg = Parse::Win32Registry->new( $hive ); - my $root_key = $reg->get_root_key; - my $key_path_main = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"; - my $key_path = $key_path_main; - my $key; - my $tab; my $align; - my $vdata; my $vname; - my @subkeys; my $subkey; my @subkeysnp; - my $callback; - - # --------------------------------------------------------------- - # 20120513 [fpi] : getting the main key - $key = $root_key->get_subkey( $key_path ); - if ( not $key ) { - ::rptMsg( $key_path." not found." ); - ::logMsg( $key_path." not found." ); - return; - } - - # --------------------------------------------------------------- - # 20120513 [fpi] : parsing all values inside the main key - if ( $key = $root_key->get_subkey( $key_path ) ) { - ::rptMsg( $key_path ); - ::rptMsg( "LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValuesTrans( $key, \%INTERNET_SETTINGS, $tab2, $align30 ); - } - else { - ::rptMsg( $key_path." not found." ); - ::logMsg( $key_path." not found." ); - } - ::rptMsg(); - - # --------------------------------------------------------------- - # 20120513 [fpi] : getting all the first level subkeys, parsing some of them - # and reporting all subkeys parsed and not parsed as list - @subkeys = sort {lc $a->get_name() cmp lc $b->get_name} $key->get_list_of_subkeys(); - foreach my $subkey ( @subkeys ) { - $callback = $PARSED_SUBKEYS{ $subkey->get_name() }; - if ( defined $callback ) { - ::rptMsg(); - $key_path = $key_path_main."\\".$subkey->get_name(); - ::rptMsg( ' *'.$key_path ); - ::rptMsg( $tab2."LastWrite Time ".gmtime( $subkey->get_timestamp() )." (UTC)" ); - $callback->( $key_path, $subkey, $tab2, $align25 ); - } - else { - push @subkeysnp, $subkey; - } - } - - ::rptMsg( "\nSubkeys not parsed in '$key_path_main'\n" ); - foreach my $subkey ( @subkeysnp ) { - ::rptMsg( sprintf( $tab4."$align20 --- %s", - $subkey->get_name() ) . gmtime( $subkey->get_timestamp() ) . " UTC" ); - } - ::rptMsg( "" ); -} - -#------------------------------------------------------------------------------ - -sub trBool -{ - my $data = shift; my $temp = "true "; - if ( $data != 0 and $data != 1 ) { - $temp = "$data (WARNING: expected a boolean '0|1'!)"; - return $temp; - } - $temp = "false" if ( $data == 0 ); - $temp .= " [$data]"; - return $temp; -} - -sub trFileTime -{ - my $data = shift; - my ( $t0, $t1 ) = unpack( "VV",$data ); - $data = gmtime( ::getTime( $t0, $t1 ) )." UTC"; - return $data; -} - -sub trHex -{ - my $data = shift; - $data = unpack( "H*", $data ); - return "0x".$data; -} - -sub trNumHex -{ - my $data = shift; - return sprintf( "%u [0x%08X]", $data, $data ); -} - -sub trSkip -{ - return ""; -} - -sub trSyncMode5 -{ - my $data = shift; my $ret; - $ret = sprintf( "%u ", $data ); - if ( $data == 4 ) { $ret .= "(automatically check for updated Web pages)"; } - elsif ( $data == 3 ) { $ret .= "(always check for updated Web pages)"; } - elsif ( $data == 2 ) { $ret .= "(check one per session for updated Web pages)"; } - elsif ( $data == 0 ) { $ret .= "(never check for updated Web pages, use cached pages)"; } - else { $ret .= "(unknown value)"; } - return $ret; -} - -#------------------------------------------------------------------------------ - -sub getKeyValues { - my $key = shift; - my %vals; - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - } - return %vals; -} - -#------------------------------------------------------------------------------ - -sub getValueData -{ - # key, value name, translator, use stub - my $key = shift; my $vn = shift; - my $trans = shift; my $stub = shift; - my $vd; my $vo; - $vo = $key->get_value( $vn ); - if ( not defined $vo ) { - return undef unless defined $stub; - $vd = ""; - } - else { - $vd = $vo->get_data(); - if ( defined $trans ) { - $vd = $trans->( $vd ); - } - } - return $vd; -} - -#------------------------------------------------------------------------------ - -sub rptAllSubKeys -{ - # key, tab, align - my @subkeys = $_[0]->get_list_of_subkeys(); - foreach my $k (@subkeys) { - ::rptMsg( sprintf( $_[1]."$_[2] --- %s", - $k->get_name() ) . gmtime( $k->get_timestamp() ) . " UTC" ); - } -} - -#------------------------------------------------------------------------------ - -sub rptAllKeyValues -{ - # key, tab, align - my @vals = sort {lc $a->get_name() cmp lc $b->get_name} $_[0]->get_list_of_values(); - foreach my $v (@vals) { - my $val = $v->get_name(); - my $data = $v->get_data(); - $val = '(default)' if ( $val eq "" ); - ::rptMsg( sprintf( $_[1]."$_[2] = %s", $val, $data ) ); - } -} -#------------------------------------------------------------------------------ - -sub rptAllKeyValuesTrans -{ - # key, ttlb, tab, align, - my $key = shift; my $ttlb = shift; - my $tab = shift; my $align = shift; - my $vname; my $vdata; my $trans; - - my @vals = sort {lc $a->get_name() cmp lc $b->get_name} $key->get_list_of_values(); - foreach my $v (@vals) { - $vname = $v->get_name(); - $vname = '(default)' if ( $vname eq "" ); - $vdata = $v->get_data(); - $trans = ${$ttlb}{$vname}; - $vdata = $trans->( $vdata ) if ( defined $trans ); - ::rptMsg( sprintf( $tab."$align = %s", $vname, $vdata ) ); - } -} - -#------------------------------------------------------------------------------ - -sub cbZoneMap -{ - my $rkeypath = shift; my $rkey = shift; my $tab = shift; my $align = shift; - my @NETID; my @MACS; my @subkeys; my $subkey; - - rptAllKeyValues( $rkey, $tab.$tab2, $align ); - - ::rptMsg( $tab.$tab2."-- 'ZoneMap' subkeys -- not parsed:" ); - foreach my $subkey ( $rkey->get_list_of_subkeys() ) { - ::rptMsg( sprintf( $tab.$tab4."$align25 %s", - $subkey->get_name() ) . gmtime( $subkey->get_timestamp() ) . " UTC" ); - } -} - -#------------------------------------------------------------------------------ - -sub rptAllSubKeysWpad -{ - # key, tab, align - my @subkeys = $_[0]->get_list_of_subkeys(); - if ( not scalar( @subkeys ) ) { - ::rptMsg( sprintf( $_[1]."$_[2] %s", "-- MAC SUBKEYS --", "*no* MAC subkeys (unidentified network)" ) ); - return; - } - ::rptMsg( sprintf( $_[1]."$_[2] %s", "-- MAC SUBKEYS --", "" ) ); - foreach my $k (@subkeys) { - ::rptMsg( sprintf( $_[1]."$_[2] LastWritten %s", - $k->get_name() ) . gmtime( $k->get_timestamp() ) . " UTC" ); - } -} - -sub cbWpad -{ - my $rkeypath = shift; my $rkey = shift; my $tab = shift; my $align = shift; - my @NETID; my @MACS; my @subkeys; my $subkey; - - # 20120515 [fpi] : divide ID from MACs (brutally rustic raw algo... TBR) - @subkeys = $rkey->get_list_of_subkeys(); - foreach $subkey ( @subkeys ) { - my $kname = $subkey->get_name(); - if ( ( substr( $kname, 0, 1 ) eq '{' ) and ( substr( $kname, -1, 1 ) eq '}' ) ) { - push @NETID, $subkey; - } - elsif ( length $kname == 17 ) { - push @MACS, $subkey; - } - else { - ::logMsg( "Unexpected key '$kname' in $rkeypath" ); - } - } - $tab .= $tab2; - - @NETID = sort {$b->get_timestamp >= $a->get_timestamp} @NETID; - foreach my $subkey ( @NETID ) { - ::rptMsg(); - ::rptMsg( $tab."NETWORK SUBKEY: ".$subkey->get_name() ); - ::rptMsg( $tab."LastWrite Time ".gmtime( $subkey->get_timestamp() )." (UTC)" ); - rptAllKeyValuesTrans( $subkey, \%WPAD_VALUES, $tab.$tab2, $align ); - rptAllSubKeysWpad( $subkey, $tab.$tab2, $align ); - } - - @MACS = sort {$a->get_timestamp >= $b->get_timestamp} @MACS; - foreach my $subkey ( @MACS ) { - ::rptMsg(); - ::rptMsg( $tab."MACs SUBKEY: ".$subkey->get_name() ); - ::rptMsg( $tab."LastWrite Time ".gmtime( $subkey->get_timestamp() )." (UTC)" ); - rptAllKeyValuesTrans( $subkey, \%WPAD_VALUES, $tab.$tab2, $align ); - } - ::rptMsg(); -} - -#------------------------------------------------------------------------------ - -sub cbUrlHistory -{ - my $rkeypath = shift; my $rkey = shift; my $tab = shift; my $align = shift; - - rptAllKeyValues( $rkey, $tab.$tab2, $align ); - ::rptMsg(); -} - -#------------------------------------------------------------------------------ - -sub cbP3P -{ - my $rkeypath = shift; my $rkey = shift; my $tab = shift; my $align = shift; - my $key; my @subkeys; my $subkey; my $lkeypath; - - if ( $key = $rkey->get_subkey( "History" ) ) - { - ::rptMsg(); - $lkeypath = $rkeypath."\\History"; - ::rptMsg( $tab.$lkeypath ); - ::rptMsg( $tab."LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - - @subkeys = $key->get_list_of_subkeys(); - ::rptMsg( $tab."ANALYST NOTE:" ); - if ( scalar( @subkeys ) > 0 ) { - ::rptMsg( $tab.$tab2.sprintf( "There are ". - "%u per-domain cookie decisions subkeys, check them", scalar( @subkeys ) ) ); - } - else { - ::rptMsg( $tab.$tab2."No per-domain cookie decisions subkeys are present" ); - } - } - else { - ::rptMsg( $tab.$lkeypath." not present" ); - ::logMsg( $lkeypath." not present" ); - } - ::rptMsg(); -} - -#------------------------------------------------------------------------------ - -sub cbCACHE -{ - my $rkeypath = shift; my $rkey = shift; my $tab = shift; my $align = shift; - rptAllKeyValuesTrans( $rkey, \%CACHE_VALUES, $tab.$tab2, $align ); - ::rptMsg(); -} - -#------------------------------------------------------------------------------ - -sub parseCacheKeyValues -{ - my $key = shift; my $tab = shift; my $align = shift; - my $vname; my $vdata; - - my @vals = sort {lc $a->get_name() cmp lc $b->get_name} $key->get_list_of_values(); - - foreach my $v (@vals) { - $vname = $v->get_name(); - $vdata = $v->get_data(); - if ( $vname eq "CacheLimit" ) { - ::rptMsg( sprintf( $tab."$align = %u KB", $vname, $vdata ) ); - } - elsif ( $vname eq "CacheOptions" ) { - ::rptMsg( sprintf( $tab."$align = 0x%X", $vname, $vdata ) ); - } - elsif ( $vname eq "CacheRepair" ) { - ::rptMsg( sprintf( $tab."$align = 0x%X", $vname, $vdata ) ); - } - else { - ::rptMsg( sprintf( $tab."$align = %s", $vname, $vdata ) ); - } - } -} - -sub parseCacheKeys -{ - my $rkeypath = shift; my $rkey = shift; my $tab = shift; my $align = shift; - my $subkeyname = shift; - my $key; my $lkeypath; - my @subkeys; my $subkey; - - if ( $key = $rkey->get_subkey( $subkeyname ) ) { - ::rptMsg(); - ::rptMsg( $tab.$rkeypath."\\".$subkeyname ); - ::rptMsg( $tab."LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - rptAllKeyValues( $key, $tab.$tab2, $align ); - ::rptMsg(); - - $lkeypath = $rkeypath."\\".$subkeyname; - @subkeys = sort {lc $a->get_name() cmp lc $b->get_name} $key->get_list_of_subkeys(); - foreach $subkey ( @subkeys ) { - if ( $subkey->get_name() ne "Extensible Cache" ) { - ::rptMsg( $tab.$lkeypath."\\".$subkey->get_name() ); - ::rptMsg( $tab."LastWrite Time ".gmtime( $subkey->get_timestamp() )." (UTC)" ); - parseCacheKeyValues( $subkey, $tab.$tab2, $align ); - ::rptMsg(); - } - } - - if ( $key = $key->get_subkey( "Extensible Cache" ) ) { - ::rptMsg(); - $lkeypath .= "\\Extensible Cache"; - ::rptMsg( $tab.$lkeypath ); - ::rptMsg( $tab."LastWrite Time ".gmtime( $key->get_timestamp() )." (UTC)" ); - ::rptMsg(); - - @subkeys = sort {lc $a->get_name() cmp lc $b->get_name} $key->get_list_of_subkeys(); - foreach $subkey ( @subkeys ) { - ::rptMsg( $tab.$lkeypath."\\".$subkey->get_name() ); - ::rptMsg( $tab."LastWrite Time ".gmtime( $subkey->get_timestamp() )." (UTC)" ); - parseCacheKeyValues( $subkey, $tab.$tab2, $align ); - ::rptMsg(); - } - } - else { ::rptMsg( $tab."subkey 'Extensible Cache' not present" ); ::rptMsg(); } - } - else { - ::rptMsg( $tab.$rkeypath."\\".$subkeyname." not found." ); - ::rptMsg(); - ::logMsg( $rkeypath."\\".$subkeyname." not found." ); - } -} - -sub cb50 -{ - my $rkeypath = shift; my $rkey = shift; my $tab = shift; my $align = shift; - - parseCacheKeys( $rkeypath, $rkey, $tab, $align, "Cache" ); - parseCacheKeys( $rkeypath, $rkey, $tab, $align, "LowCache" ); - - # NSCookieUpgrade and User Agent keys not parsed (TBR) -} - -#------------------------------------------------------------------------------ -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ips.pl b/thirdparty/rr-full/plugins/ips.pl new file mode 100644 index 00000000000..09e5b4a0ab0 --- /dev/null +++ b/thirdparty/rr-full/plugins/ips.pl @@ -0,0 +1,119 @@ +#----------------------------------------------------------- +# ips.pl +# Check System hive for IPAddresses and domains, including those for +# DHCP +# +# +# Change history +# 20200911 - MITRE updates +# 20200518 - created +# +# References +# +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package ips; +use strict; + +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + category => "config", + output => "report", + version => 20200911); + +sub getConfig{return %config} +sub getShortDescr { + return "Get IP Addresses and domains (DHCP,static)"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my %nics; + my $ccs; + ::logMsg("Launching ips v.".$VERSION); + ::rptMsg("ips v.".$VERSION); # banner + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $current; + eval { + $current = ::getCCS($root_key); + }; + + my $key_path = $current."\\Services\\Tcpip\\Parameters\\Interfaces"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @subkey1 = $key->get_list_of_subkeys(); + if (scalar @subkey1 > 0) { + + ::rptMsg(sprintf "%-20s %-30s","IPAddress","Domain"); + + foreach my $s1 (@subkey1) { + + getIPs($s1); + + my @subkey2 = $s1->get_list_of_subkeys(); + if (scalar @subkey2 > 0) { + foreach my $s2 (@subkey2) { + getIPs($s2); + + } + } + } + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +sub getIPs { + my $key = shift; + + my $dh = (); + my $dhdom = (); + my $hint = (); + my $ip = (); + my $dom = (); + + eval { + $dh = $key->get_value("DhcpIPAddress")->get_data(); + }; + + eval { + $dhdom = $key->get_value("DhcpDomain")->get_data(); + }; + + eval { + $hint = $key->get_value("DhcpNetworkHint")->get_data(); + $hint = pack("h*",reverse $hint); + }; + + ::rptMsg(sprintf "%-20s %-30s %-30s",$dh,$dhdom,"Hint: ".$hint) if ($dh); + + + eval { + $ip = $key->get_value("IPAddress")->get_data(); + }; + + eval { + $dom = $key->get_value("Domain")->get_data(); + }; + ::rptMsg(sprintf "%-20s %-30s",$ip,$dom) if ($ip); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/iso.pl b/thirdparty/rr-full/plugins/iso.pl new file mode 100644 index 00000000000..5edd45916f9 --- /dev/null +++ b/thirdparty/rr-full/plugins/iso.pl @@ -0,0 +1,84 @@ +#----------------------------------------------------------- +# iso.pl +# Plugin to extract ISO file mounting settings +# +# History +# 20220829 - created +# +# References +# https://malicious.link/post/2022/blocking-iso-mounting/ +# +# copyright 2022, QAR LLC +# H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package iso; +use strict; + +my %config = (hive => "Software", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + category => "persistence", + MITRE => "T1546\.001", + output => "report", + version => 20220829); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get shell\\open\\command settings for various file types"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching iso v.".$VERSION); + ::rptMsg("iso v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key = (); + my $key_path = "Classes"; + my @types = ("Windows\.IsoFile","Windows\.VhdFile"); + + if ($key = $root_key->get_subkey($key_path)) { + foreach my $t (@types) { + + eval { + my $path = $t."\\shell\\mount\\command"; + my $cmd = $key->get_subkey($path)->get_value("")->get_data(); + ::rptMsg($path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_subkey($path)->get_timestamp())."Z"); + ::rptMsg("Cmd: ".$cmd); + }; + + if ($t eq "Windows\.IsoFile") { + eval { + my $path = $t."\\shell\\mount\\command"; + if (my $p = $key->get_subkey($path)->get_value("ProgrammaticAccessOnly")) { + ::rptMsg("ProgrammaticAccessOnly value found\."); + } + else { + ::rptMsg("ProgrammaticAccessOnly value not found\."); + } + }; + } + ::rptMsg(""); + } + } + ::rptMsg("Analysis Tip: MS has default settings for mounting various file types (ISO,IMG,VHD)\. The addition of the "); + ::rptMsg("\"ProgrammaticAccessOnly\" value removes the context menu for ISO/IMG files."); + ::rptMsg(""); + ::rptMsg("Ref: https://malicious.link/post/2022/blocking-iso-mounting/"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/javafx.pl b/thirdparty/rr-full/plugins/javafx.pl deleted file mode 100644 index b7dae6f3c15..00000000000 --- a/thirdparty/rr-full/plugins/javafx.pl +++ /dev/null @@ -1,69 +0,0 @@ -#----------------------------------------------------------- -# javafx.pl -# Plugin written based on Cory Harrell's Exploit Artifacts posts at -# http://journeyintoir.blogspot.com/ -# -# Change history -# 20110322 - created -# -# References -# http://java.sun.com/j2se/1.4.2/runtime_win32.html -# -# copyright 2011 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package javafx; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20110322); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's JavaFX key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching javafx v.".$VERSION); - ::rptMsg("javafx v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\JavaSoft\\Java Update\\Policy\\JavaFX"; - my $key; - my @vals; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("javafx v.".$VERSION); - ::rptMsg($key_path); - ::rptMsg("LastWrite time: ".gmtime($key->get_timestamp())); - ::rptMsg(""); - @vals = $key->get_list_of_values(); - - if (scalar(@vals) > 0) { -# First, read in all of the values and the data - foreach my $v (@vals) { - ::rptMsg(sprintf "%-25s %-20s",$v->get_name(), $v->get_data()); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/javasoft.pl b/thirdparty/rr-full/plugins/javasoft.pl deleted file mode 100644 index c9f070d28fa..00000000000 --- a/thirdparty/rr-full/plugins/javasoft.pl +++ /dev/null @@ -1,64 +0,0 @@ -#----------------------------------------------------------- -# javasoft.pl -# -# History -# 20130216 - created -# -# References -# http://labs.alienvault.com/labs/index.php/2013/new-year-new-java-zeroday/ -# http://nakedsecurity.sophos.com/how-to-disable-java-internet-explorer/ -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package javasoft; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20130216); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of JavaSoft/UseJava2IExplorer value"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching javasoft v.".$VERSION); - ::rptMsg("Launching javasoft v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my @k = ('JavaSoft\\Java Plug-in','Wow6432Node\\JavaSoft\\Java Plug-in'); - foreach my $key_path (@k) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $ie; - eval { - $ie = $key->get_value("UseJava2IExplorer")->get_data(); - ::rptMsg(sprintf "UseJava2IExplorer = 0x%x",$ie); - }; - ::rptMsg("UseJava2IExplorer value not found\.") if ($@); - ::rptMsg(""); - } - else { - ::rptMsg("Key ".$key_path." not found."); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/jumplistdata.pl b/thirdparty/rr-full/plugins/jumplistdata.pl index 29f0201a5b9..46688fe12f4 100644 --- a/thirdparty/rr-full/plugins/jumplistdata.pl +++ b/thirdparty/rr-full/plugins/jumplistdata.pl @@ -3,12 +3,14 @@ # # # Change history +# 20200927 - MITRE update +# 20200517 - updated date output format # 20180611 - created (per request submitted by John McCash) # # References # https://twitter.com/sv2hui/status/1005763370186891269 # -# copyright 2018 QAR, LLC +# copyright 2020 QAR, LLC # author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package jumplistdata; @@ -18,8 +20,10 @@ package jumplistdata; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180611); + category => "execution", + MITRE => "T1204", + output => "report", + version => 20200927); sub getConfig{return %config} sub getShortDescr { @@ -37,7 +41,9 @@ sub pluginmain { my $ntuser = shift; ::logMsg("Launching jumplistdata v.".$VERSION); ::rptMsg("jumplistdata v.".$VERSION); - ::rptMsg("- ".getShortDescr()."\n"); + ::rptMsg(getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -52,7 +58,7 @@ sub pluginmain { my $name = $v->get_name(); my @t = unpack("VV",$v->get_data()); my $w = ::getTime($t[0],$t[1]); - ::rptMsg(gmtime($w)." UTC $name"); + ::rptMsg(::format8601Date($w)."Z $name"); } } diff --git a/thirdparty/rr-full/plugins/kankan.pl b/thirdparty/rr-full/plugins/kankan.pl deleted file mode 100644 index a886250ff98..00000000000 --- a/thirdparty/rr-full/plugins/kankan.pl +++ /dev/null @@ -1,93 +0,0 @@ -#----------------------------------------------------------- -# kankan.pl -# Looks for and retrieves Office Addins from Software/NTUSER.DAT -# hives; Win32/KanKan uses one as a persistence mech. -# -# Change history -# 20131011 - created -# -# References -# http://www.welivesecurity.com/2013/10/11/win32kankan-chinese-drama/ -# http://msdn.microsoft.com/en-us/library/bb386106.aspx -# -# Copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package kankan; -use strict; - -my %config = (hive => "NTUSER\.DAT, Software", - hasShortDescr => 1, - hasDescr => 1, - hasRefs => 1, - osmask => 22, - category => "malware", - version => 20131011); -my $VERSION = getVersion(); - -# Functions # -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -sub getShortDescr { - return "Extracts Office app Addin Settings"; -} -sub getRefs {} - -sub pluginmain { - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching kankan v.".$VERSION); - ::rptMsg("kankan v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - - my @apps = ("Word","Excel","PowerPoint"); - - my @paths = ("Software\\Microsoft\\Office", - "Wow6432Node\\Software\\Microsoft\\Office", -# Software hive - "Microsoft\\Office", - "Wow6432Node\\Microsoft\\Office"); - - foreach my $key_path (@paths) { - foreach my $app (@apps) { - if ($key = $root_key->get_subkey($key_path."\\".$app."\\Addins")) { - my @subkeys = $key->get_list_of_subkeys(); - - if (scalar(@subkeys) > 0) { - ::rptMsg($app." Addins"); - foreach my $s (@subkeys) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())."]"); - - eval { - my $desc = $s->get_value("Description")->get_data(); - ::rptMsg(" Description : ".$desc); - }; - - eval { - my $fr = $s->get_value("FriendlyName")->get_data(); - ::rptMsg(" FriendlyName: ".$fr); - }; - - eval { - my $load = $s->get_value("LoadBehavior")->get_data(); - ::rptMsg(" LoadBehavior: ".$load); - }; - ::rptMsg(""); - } - } - ::rptMsg(""); - } - } - } - ::rptMsg("Tip: At least one identified variant of Win32/KanKan creates an Addin named"); - ::rptMsg("InputEnhance\.Connect"); -} - -1; diff --git a/thirdparty/rr-full/plugins/kb950582.pl b/thirdparty/rr-full/plugins/kb950582.pl deleted file mode 100644 index 6b5babc2c8d..00000000000 --- a/thirdparty/rr-full/plugins/kb950582.pl +++ /dev/null @@ -1,92 +0,0 @@ -#----------------------------------------------------------- -# kb950582.pl -# Get autorun settings WRT KB950582 -# -# Change history -# 18 Dec 2008 - Updated to new name; added checks for Registry -# keys -# -# References -# http://support.microsoft.com/kb/953252 -# http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit -# /regentry/91525.mspx?mfr=true -# -# copyright 2008-2009 H. Carvey -#----------------------------------------------------------- -package kb950582; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20081212); - -sub getConfig{return %config} -sub getShortDescr { - return "KB950582 - Gets autorun settings from HKLM hive"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching kb950582 v.".$VERSION); - ::rptMsg("kb950582 v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - eval { - my $path = "Microsoft\\Windows\\CurrentVersion\\Uninstall\\KB950582"; - if (my $kbkey = $root_key->get_subkey($path)) { - my $install = $kbkey->get_value("InstallDate")->get_data(); - ::rptMsg("KB950528 Uninstall Key ".gmtime($kbkey->get_timestamp())); - ::rptMsg(" InstallDate = ".$install."\n"); - } - }; - ::rptMsg("Uninstall\\KB950528 does not appear to be installed.\n") if ($@); - - eval { - my $path = "Microsoft\\Updates\\Windows XP\\SP4\\KB950582"; - if (my $kbkey = $root_key->get_subkey($path)) { - my $install = $kbkey->get_value("InstalledDate")->get_data(); - ::rptMsg("KB950528 Update Key ".gmtime($kbkey->get_timestamp())); - ::rptMsg(" InstalledDate = ".$install."\n"); - } - }; - ::rptMsg("KB950528 does not appear to be installed.\n") if ($@); - - my $key_path = "Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - - eval { - my $nodrive = $key->get_value("NoDriveTypeAutoRun")->get_data(); - my $str = sprintf "%-20s 0x%x","NoDriveTypeAutoRun",$nodrive; - ::rptMsg($str); - }; - ::rptMsg("NoDriveTypeAutoRun value may not exist: ".$@) if ($@); - -# http://support.microsoft.com/kb/953252 - eval { - my $honor = $key->get_value("HonorAutorunSetting")->get_data(); - my $str = sprintf "%-20s 0x%x","HonorAutorunSetting",$honor; - ::rptMsg($str); - }; - ::rptMsg("HonorAutorunSetting not found.") if ($@); - ::rptMsg(""); - ::rptMsg("Autorun settings in the HKLM hive take precedence over those in"); - ::rptMsg("the HKCU hive."); - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/kbdcrash.pl b/thirdparty/rr-full/plugins/kbdcrash.pl deleted file mode 100644 index ef5b221f726..00000000000 --- a/thirdparty/rr-full/plugins/kbdcrash.pl +++ /dev/null @@ -1,67 +0,0 @@ -#----------------------------------------------------------- -# kbdcrash.pl -# -# Ref: -# http://support.microsoft.com/kb/244139 -# -# copyright 2008-2009 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package kbdcrash; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20081212); - -sub getConfig{return %config} - -sub getShortDescr { - return "Checks to see if system is config to crash via keyboard"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my $enabled = 0; - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching kbdcrash v.".$VERSION); - ::rptMsg("kbdcrash v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# Code for System file, getting CurrentControlSet - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $svc = "ControlSet00".$current."\\Services"; - - eval { - my $ps2 = $svc->get_subkey("i8042prt\\Parameters")->get_value("CrashOnCtrlScroll")->get_data(); - ::rptMsg("CrashOnCtrlScroll set for PS2 keyboard") if ($ps2 == 1); - $enabled = 1 if ($ps2 == 1); - }; - - eval { - my $usb = $svc->get_subkey("kbdhid\\Parameters")->get_value("CrashOnCtrlScroll")->get_data(); - ::rptMsg("CrashOnCtrlScroll set for USB keyboard") if ($usb == 1); - $enabled = 1 if ($usb == 1); - }; - ::rptMsg("CrashOnCtrlScroll not set"); - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/kdc.pl b/thirdparty/rr-full/plugins/kdc.pl new file mode 100644 index 00000000000..21f53e43e8e --- /dev/null +++ b/thirdparty/rr-full/plugins/kdc.pl @@ -0,0 +1,80 @@ +#----------------------------------------------------------- +# kdc.pl +# +# History: +# 20210312 - created +# +# References: +# https://twitter.com/PyroTek3/status/1336720280316760066 +# https://support.microsoft.com/en-us/topic/kb4598347-managing-deployment-of-kerberos-s4u-changes-for-cve-2020-17049-569d60b7-3267-e2b0-7d9b-e46d770332ab +# https://msrc.microsoft.com/update-guide/vulnerability/CVE-2020-17049 +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package kdc; +use strict; + +my %config = (hive => "System", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562", + output => "report", + version => 20210312); + +sub getConfig{return %config} +sub getShortDescr { + return "Get values related to \"Bronze Bit\" from KDC Service key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my @temps; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching kdc v.".$VERSION); + ::rptMsg("kdc v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $current; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services\\Kdc"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $n = $key->get_value("NonForwardableDelegation")->get_data(); + ::rptMsg("NonForwardableDelegation value: ".$n); + + }; + + eval { + my $n = $key->get_value("PerformTicketSignature")->get_data(); + ::rptMsg("PerformTicketSignature value: ".$n); + ::rptMsg(""); + ::rptMsg("0: Disables Kerberos Signatures"); + ::rptMsg("1: Enables Deployment Mode"); + ::rptMsg("2: Enables Enforcement Mode"); + ::rptMsg("Ref: https://support.microsoft.com/en-us/topic/kb4598347-managing-deployment-of-kerberos-s4u-changes-for-cve-2020-17049-569d60b7-3267-e2b0-7d9b-e46d770332ab"); + }; + + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/killsuit.pl b/thirdparty/rr-full/plugins/killsuit.pl new file mode 100644 index 00000000000..a5641788e41 --- /dev/null +++ b/thirdparty/rr-full/plugins/killsuit.pl @@ -0,0 +1,61 @@ +#----------------------------------------------------------- +# killsuit +# +# Change history: +# 20201005 - MITRE update +# 20200427 - updated output date format +# 20200414 - created +# +# Ref: +# https://img.en25.com/Web/FSecure/%7B1d240f2a-dcbb-4b0c-9da9-e27a283aed02%7D_2019-07-23-FSecure-Whitepaper-Killsuit-01.pdf +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package killsuit; +use strict; + +my %config = (hive => "Software", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20201005); + +sub getConfig{return %config} +sub getShortDescr { + return "Check for indications of Danderspritz Killsuit installation"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching killsuit v.".$VERSION); + ::rptMsg("killsuit v.".$VERSION); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $key_path = ('Microsoft\\Windows\\CurrentVersion\\OemMgmt'); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Danderspitz KillSuit is known to store hashes beneath this key. This may be an indicator that KillSuit is"); + ::rptMsg("installed on the system."); + ::rptMsg("Ref: https://img.en25.com/Web/FSecure/%7B1d240f2a-dcbb-4b0c-9da9-e27a283aed02%7D_2019-07-23-FSecure-Whitepaper-Killsuit-01\.pdf"); + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/killsuit_tln.pl b/thirdparty/rr-full/plugins/killsuit_tln.pl new file mode 100644 index 00000000000..3b103cf0a93 --- /dev/null +++ b/thirdparty/rr-full/plugins/killsuit_tln.pl @@ -0,0 +1,58 @@ +#----------------------------------------------------------- +# killsuit_tln +# +# Change history: +# 20201005 - MITRE update +# 20200414 - created +# +# Ref: +# https://img.en25.com/Web/FSecure/%7B1d240f2a-dcbb-4b0c-9da9-e27a283aed02%7D_2019-07-23-FSecure-Whitepaper-Killsuit-01.pdf +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package killsuit_tln; +use strict; + +my %config = (hive => "Software", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "tln", + version => 20201005); + +sub getConfig{return %config} +sub getShortDescr { + return "Check for indications of Danderspritz Killsuit installation"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::rptMsg("Launching killsuit v.".$VERSION); +# ::rptMsg("killsuit v.".$VERSION); # banner +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $key_path = ('Microsoft\\Windows\\CurrentVersion\\OemMgmt'); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { +# ::rptMsg($key_path); +# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg($key->get_timestamp()."|REG|||M... Possible Killsuit Infection - ".$key_path); + + } + else { +# ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/knowndev.pl b/thirdparty/rr-full/plugins/knowndev.pl index 10b18603877..4bf4aeb56ba 100644 --- a/thirdparty/rr-full/plugins/knowndev.pl +++ b/thirdparty/rr-full/plugins/knowndev.pl @@ -2,6 +2,8 @@ # knowndev.pl # # History +# 20200927 - MITRE update +# 20200515 - updated date output format # 20190714 - updated # 20140414 - created # @@ -18,8 +20,10 @@ package knowndev; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20190714); + MITRE => "", + output => "report", + category => "devices", + version => 20200927); sub getConfig{return %config} sub getShortDescr { @@ -47,13 +51,13 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("KnownDevices"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)\n"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z\n"); my @subkeys = $key->get_list_of_subkeys(); if (scalar @subkeys > 0) { foreach my $s (@subkeys) { my $name = $s->get_name(); - my $lw = gmtime($s->get_timestamp()); - ::rptMsg($name." ".$lw." Z"); + my $lw = ::format8601Date($s->get_timestamp()); + ::rptMsg($name." ".$lw."Z"); eval { my $label = $s->get_value("Label")->get_data(); diff --git a/thirdparty/rr-full/plugins/labconfig.pl b/thirdparty/rr-full/plugins/labconfig.pl new file mode 100644 index 00000000000..e416f02cb7c --- /dev/null +++ b/thirdparty/rr-full/plugins/labconfig.pl @@ -0,0 +1,101 @@ +#----------------------------------------------------------- +# labconfig.pl +# Get bypass settings to install Win11 +# +# History +# 20220819 - updated with MoSetup key +# 20220816 - created +# +# References +# https://github.com/St1ckys/Win11/blob/main/BypassTPMCheck%26SecureBoot.reg +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package labconfig; +use strict; +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1601", #Modify System Image + category => "defense evasion", + version => 20220819); + +sub getConfig{return %config} +sub getShortDescr { + return "Get Win11 install bypass settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $key; + my $ccs = (); + ::logMsg("Launching labconfig v.".$VERSION); + ::rptMsg("labconfig v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + + my $key_path = $ccs."\\Setup\\LabConfig"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = ("BypassTPMCheck", "BypassSecureBootCheck","BypassRAMCheck"); + + foreach my $v (@vals) { + eval { + my $i = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-25s 0x%04x",$v,$i); + }; + ::rptMsg("Error getting ".$v." value: ".$@) if ($@); + } + + + ::rptMsg(""); + ::rptMsg("Analysis Tip: The values listed allow the user to bypass checks to install Win11 on an unsupported system"); + ::rptMsg("configuration."); + ::rptMsg(""); + ::rptMsg("Ref: https://github.com/St1ckys/Win11/blob/main/BypassTPMCheck%26SecureBoot\.reg"); + } + else { + ::rptMsg($key_path." not found."); + } +# added 20220819 +# https://www.pcmag.com/news/microsoft-offers-tpm-20-bypass-to-install-windows-11-on-unsupported-pcs + my $key_path = $ccs."\\Setup\\MoSetup"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $a = $key->get_value("AllowUpgradesWithUnsupportedTPMOrCPU")->get_data(); + ::rptMsg("AllowUpgradesWithUnsupportedTPMOrCPU value: ".$a); + + }; + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the \"AllowUpgradesWithUnsupportedTPMOrCPU\" is set to \"1\", the TPM 2.0 requirement for"); + ::rptMsg("Windows 11 is bypassed."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.pcmag.com/news/microsoft-offers-tpm-20-bypass-to-install-windows-11-on-unsupported-pcs"); + + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/landesk.pl b/thirdparty/rr-full/plugins/landesk.pl index 242295cf077..f270b7b4178 100644 --- a/thirdparty/rr-full/plugins/landesk.pl +++ b/thirdparty/rr-full/plugins/landesk.pl @@ -6,6 +6,8 @@ # https://community.landesk.com/docs/DOC-3249 # # Change history +# 20201005 - MITRE update +# 20200517 - updated date output format # 20160823 - added "Current Duration" parsing # 20160822 - updated based on client engagement # 20130326 - added Wow6432Node path @@ -14,16 +16,19 @@ # # Orignal copyright 2009 Don C. Weber # Updated copyright 2013 QAR, LLC +# Updated copyright 2020 QAR, LLC #----------------------------------------------------------- package landesk; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "T1204", + category => "execution", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20160823); + output => "report", + version => 20201005); sub getConfig{return %config} @@ -42,6 +47,8 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching landesk v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -58,11 +65,11 @@ sub pluginmain { if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { ::rptMsg($s->get_name()); - ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); + ::rptMsg(" LastWrite: ".::format8601Date($s->get_timestamp())."Z"); eval { @ts = unpack("VV",$s->get_value("Last Started")->get_data()); - ::rptMsg(" Last Started: ".gmtime(::getTime($ts[0],$ts[1]))." Z"); + ::rptMsg(" Last Started: ".::format8601Date(::getTime($ts[0],$ts[1]))."Z"); }; eval { @@ -88,7 +95,7 @@ sub pluginmain { eval { @ts = unpack("VV",$s->get_value("First Started")->get_data()); - ::rptMsg(" First Started: ".gmtime(::getTime($ts[0],$ts[1]))." Z"); + ::rptMsg(" First Started: ".::format8601Date(::getTime($ts[0],$ts[1]))."Z"); }; eval { @@ -121,7 +128,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg(""); ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); + ::rptMsg("LastWrite: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); @@ -129,7 +136,7 @@ sub pluginmain { foreach my $v (@vals) { my $name = $v->get_name(); my $data = $v->get_data(); - ::rptMsg($data." Logon: ".gmtime($name)); + ::rptMsg($data." Logon: ".::format8601Date($name)."Z"); } } diff --git a/thirdparty/rr-full/plugins/landesk_tln.pl b/thirdparty/rr-full/plugins/landesk_tln.pl index 8e627b9a1cd..8478b5e6d13 100644 --- a/thirdparty/rr-full/plugins/landesk_tln.pl +++ b/thirdparty/rr-full/plugins/landesk_tln.pl @@ -4,6 +4,7 @@ # # # Change history +# 20201005 - MITRE update # 20160822 - minor updates based on client engagement # 20130214 - updated with Logon info # 20090729 - updates, H. Carvey @@ -15,11 +16,13 @@ package landesk_tln; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "T1204", + category => "execution", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20130214); + output => "tln", + version => 20201005); sub getConfig{return %config} diff --git a/thirdparty/rr-full/plugins/lastloggedon.pl b/thirdparty/rr-full/plugins/lastloggedon.pl index 9a197ba3316..a2ec6fcbb67 100644 --- a/thirdparty/rr-full/plugins/lastloggedon.pl +++ b/thirdparty/rr-full/plugins/lastloggedon.pl @@ -1,26 +1,30 @@ #----------------------------------------------------------- # lastloggedon # -# -# References -# # # History: +# 20201007 - MITRE update +# 20200517 - updated date output format # 20180614 - Updated by Michael Godfrey # 20160531 - created # -# copyright 2018 Quantum Analytics Research, LLC +# Ref: +# https://attack.mitre.org/techniques/T1078/ +# +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package lastloggedon; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "software", + MITRE => "T1078", + category => "user activity", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20160531); + output => "report", + version => 20201007); sub getConfig{return %config} @@ -51,7 +55,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("LastLoggedOn"); ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); + ::rptMsg("LastWrite: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); eval { diff --git a/thirdparty/rr-full/plugins/latentbot.pl b/thirdparty/rr-full/plugins/latentbot.pl deleted file mode 100644 index 83fd6549a0f..00000000000 --- a/thirdparty/rr-full/plugins/latentbot.pl +++ /dev/null @@ -1,88 +0,0 @@ -#----------------------------------------------------------- -# latentbot.pl -# -# -# Change History -# 20151213 - created -# -# References: -# https://www.fireeye.com/blog/threat-research/2015/12/latentbot_trace_me.html -# -# -# copyright 2015 Quantum Analytics Research, -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package latentbot; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20151213); - -sub getConfig{return %config} - -sub getShortDescr { - return "Check NTUSER.DAT for indications of LatentBot"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching latentbot v.".$VERSION); - ::rptMsg("latentbot v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows"; - - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - -# 20151213: LatentBot persists via the 'load' value -# https://www.fireeye.com/blog/threat-research/2015/12/latentbot_trace_me.html -# look for odd value data, such as "dlrznz68mkaa.exe" - my $load; - eval { - $load = $key->get_value("load")->get_data(); - ::rptMsg("load value = ".$load); - ::alertMsg("ALERT: user_run: ".$key_path." load value found: ".$load) unless ($load eq ""); - }; - if ($@) { - ::rptMsg("load value not found."); - } - } - ::rptMsg(""); -# Look for odd, randomly named subkeys, which may indicate the existence of the -# modules; the names aren't actually random, but XOR encoded - $key_path = "Software\\Google\\Update\\network\\secure"; - if ($key = $root_key->get_subkey($key_path)) { - - my @subkeys; - eval { - @subkeys = $key->get_list_of_subkeys(); - if (scalar @subkeys > 0) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - foreach my $s (@subkeys) { - ::rptMsg(" ".$s->get_name()." - ".gmtime($s->get_timestamp())." (UTC)"); - } - } - else { - - } - }; - } -} -1; diff --git a/thirdparty/rr-full/plugins/lazyshell.pl b/thirdparty/rr-full/plugins/lazyshell.pl deleted file mode 100644 index f5393b6d88c..00000000000 --- a/thirdparty/rr-full/plugins/lazyshell.pl +++ /dev/null @@ -1,69 +0,0 @@ -#----------------------------------------------------------- -# lazyshell -# -# Change history: -# 20131007 - created -# -# Ref: -# -# -# copyright 2013 QAR,LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package lazyshell; -use strict; - -my %config = (hive => "Software", - category => "malware", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20131007); - -sub getConfig{return %config} -sub getShortDescr { - return "Checks for keys/values assoc. with LazyShell"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::rptMsg("Launching lazyshell v.".$VERSION); - ::rptMsg("lazyshell v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my @paths = ('Microsoft\\Windows\\CurrentVersion\\Wordpad\\ComChecks\\Safelist', - 'Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Wordpad\\ComChecks\\Safelist'); - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - foreach my $key_path (@paths) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - eval { - my $cc = $key->get_value("CategoryCount")->get_data(); - ::rptMsg("CategoryCount value found\."); - }; - - eval { - my $r = $key->get_value("ResetAU")->get_data(); - ::rptMsg("ResetAU value found\."); - }; - ::rptMsg(""); - } - else { - ::rptMsg($key_path." not found."); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/legacy.pl b/thirdparty/rr-full/plugins/legacy.pl deleted file mode 100644 index 86a3b8b6563..00000000000 --- a/thirdparty/rr-full/plugins/legacy.pl +++ /dev/null @@ -1,107 +0,0 @@ -#----------------------------------------------------------- -# legacy.pl -# -# -# Change history -# 20120524 -# 20090429 - created -# -# Reference: http://support.microsoft.com/kb/310592 -# -# -# Analysis Tip: -# The keys of interested begin with LEGACY_, for example, -# "LEGACY_EVENTSYSTEM". The LastWrite time on this key seems to indicate -# the first time that the serivce was launched. The LastWrite time on -# keys named, for example, "LEGACY_EVENTSYSTEM\0000", appear to indicate -# the most recent time that the service was launched. One example to look -# for is services related to malware/lateral movement, such as PSExec. -# -# copyright 2012 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package legacy; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20120524); - -sub getConfig{return %config} -sub getShortDescr { - return "Lists LEGACY_* entries in Enum\\Root key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching legacy v.".$VERSION); # message - ::rptMsg("legacy v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key(); -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - my $root_path = $ccs."\\Enum\\Root"; - - my %legacy; - if (my $root = $root_key->get_subkey($root_path)) { - my @sk = $root->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - my $name = $s->get_name(); - next unless ($name =~ m/^LEGACY_/); - push(@{$legacy{$s->get_timestamp()}},$name); - - eval { - my @s_sk = $s->get_list_of_subkeys(); - if (scalar(@s_sk) > 0) { - foreach my $s_s (@s_sk) { - - my $desc; - eval { - $desc = $s_s->get_value("DeviceDesc")->get_data(); - push(@{$legacy{$s_s->get_timestamp()}},$name."\\".$s_s->get_name()." - ".$desc); - }; - push(@{$legacy{$s_s->get_timestamp()}},$name."\\".$s_s->get_name()) if ($@); - } - } - }; - } - } - else { - ::rptMsg($root_path." has no subkeys."); - } - - foreach my $t (reverse sort {$a <=> $b} keys %legacy) { - ::rptMsg(gmtime($t)." (UTC)"); - foreach my $item (@{$legacy{$t}}) { - ::rptMsg(" ".$item); - } - } - } - else { - ::rptMsg($root_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/legacy_tln.pl b/thirdparty/rr-full/plugins/legacy_tln.pl deleted file mode 100644 index 24ad3d7ae0c..00000000000 --- a/thirdparty/rr-full/plugins/legacy_tln.pl +++ /dev/null @@ -1,108 +0,0 @@ -#----------------------------------------------------------- -# legacy_tln.pl -# -# -# Change history -# 20120620 - modified legacy.pl to legacy_tln.pl -# 20090429 - legacy.pl created -# -# Reference: http://support.microsoft.com/kb/310592 -# -# -# Analysis Tip: -# The keys of interested begin with LEGACY_, for example, -# "LEGACY_EVENTSYSTEM". The LastWrite time on this key seems to indicate -# the first time that the serivce was launched. The LastWrite time on -# keys named, for example, "LEGACY_EVENTSYSTEM\0000", appear to indicate -# the most recent time that the service was launched. One example to look -# for is services related to malware/lateral movement, such as PSExec. -# -# copyright 2012 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package legacy_tln; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20120620); - -sub getConfig{return %config} -sub getShortDescr { - return "Lists LEGACY_* entries in Enum\\Root key in TLN format"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key(); -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - my $root_path = $ccs."\\Enum\\Root"; - - my %legacy; - if (my $root = $root_key->get_subkey($root_path)) { - my @sk = $root->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - my $name = $s->get_name(); - next unless ($name =~ m/^LEGACY_/); - push(@{$legacy{$s->get_timestamp()}},$name); - - eval { - my @s_sk = $s->get_list_of_subkeys(); - if (scalar(@s_sk) > 0) { - foreach my $s_s (@s_sk) { - - my $desc; - eval { - $desc = $s_s->get_value("DeviceDesc")->get_data(); - push(@{$legacy{$s_s->get_timestamp()}},$name."\\".$s_s->get_name()." - ".$desc); - }; - push(@{$legacy{$s_s->get_timestamp()}},$name."\\".$s_s->get_name()) if ($@); - } - } - }; - } - } - else { - ::rptMsg($root_path." has no subkeys."); - } - - foreach my $t (reverse sort {$a <=> $b} keys %legacy) { - foreach my $item (@{$legacy{$t}}) { - ::rptMsg($t."|REG|||[Program Execution] - $item"); - } - -# ::rptMsg(gmtime($t)." (UTC)"); -# foreach my $item (@{$legacy{$t}}) { -# ::rptMsg(" ".$item); -# } - } - } - else { - ::rptMsg($root_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/licenses.pl b/thirdparty/rr-full/plugins/licenses.pl index 4409c8bb03f..5a2bc1d8013 100644 --- a/thirdparty/rr-full/plugins/licenses.pl +++ b/thirdparty/rr-full/plugins/licenses.pl @@ -5,20 +5,24 @@ # Keylogger. # # History +# 20201005 - MITRE update +# 20200526 - updated date output format # 20120305 - created # -# -# copyright 2012, Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package licenses; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20120305); + output => "report", + version => 20201005); sub getConfig{return %config} @@ -46,7 +50,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); diff --git a/thirdparty/rr-full/plugins/link_click.pl b/thirdparty/rr-full/plugins/link_click.pl new file mode 100644 index 00000000000..0dc71b02b20 --- /dev/null +++ b/thirdparty/rr-full/plugins/link_click.pl @@ -0,0 +1,89 @@ +#----------------------------------------------------------- +# link_click.pl +# Display last link user clicked in Office document or Outlook +# +# Change history +# 20200730 - MITRE ATT&CK updates +# 20200518 - created +# +# References +# +# https://attack.mitre.org/techniques/T1204/001/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package link_click; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "execution", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1204\.001", + output => "report", + version => 20200730); + +sub getConfig{return %config} +sub getShortDescr { + return "Get UseRWHlinkNavigation value data"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getMitre {return $config{MITRE};} + +my $VERSION = getVersion(); +my $office_version; + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching link_click v.".$VERSION); + ::rptMsg("link_click v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg("link_click v.".$VERSION); + ::rptMsg("MITRE ATT&CK subtechnique ".getMitre()); + ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + +# Check for UseRWHlinkNavigation value +# https://support.microsoft.com/en-us/help/4013793/specified-message-identity-is-invalid-error-when-you-open-delivery-rep + eval { + if (my $id = $key->get_subkey($office_version."\\Common\\Internet")) { + my $lw = $id->get_timestamp(); + my $rw = $id->get_value("UseRWHlinkNavigation")->get_data(); + ::rptMsg("Software\\Microsoft\\Office\\".$office_version."\\Common\\Internet"); + ::rptMsg("LastWrite time: ".::format8601Date($lw)."Z"); + ::rptMsg("UseRWHlinkNavigation value = ".$rw); + } + }; + +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/listsoft.pl b/thirdparty/rr-full/plugins/listsoft.pl index 9cecce0e7a0..213e0cfe48e 100644 --- a/thirdparty/rr-full/plugins/listsoft.pl +++ b/thirdparty/rr-full/plugins/listsoft.pl @@ -6,9 +6,12 @@ # and listing them in order by LastWrite time. # # Change history +# 20201005 - MITRE update +# 20200517 - updated date output format +# 20080324 - created # -# -# copyright 2008 H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package listsoft; use strict; @@ -17,8 +20,10 @@ package listsoft; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080324); + MITRE => "", + output => "report", + category => "config", #installed software + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -55,16 +60,16 @@ sub pluginmain { foreach my $t (reverse sort {$a <=> $b} keys %soft) { foreach my $item (@{$soft{$t}}) { - ::rptMsg(gmtime($t)."Z \t".$item); + ::rptMsg(::format8601Date($t)."Z \t".$item); } } } else { - ::logMsg($key_path." has no subkeys."); + ::rptMsg($key_path." has no subkeys."); } } else { - ::logMsg("Could not access ".$key_path); + ::rptMsg("Could not access ".$key_path); } } diff --git a/thirdparty/rr-full/plugins/liveContactsGUID.pl b/thirdparty/rr-full/plugins/liveContactsGUID.pl deleted file mode 100644 index 1a874efd56c..00000000000 --- a/thirdparty/rr-full/plugins/liveContactsGUID.pl +++ /dev/null @@ -1,66 +0,0 @@ -#----------------------------------------------------------- -# liveContactsGUID.pl -# -# Change history -# 20110221 [pbo] % created -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# (C) 2011 Pierre-Yves Bonnetain - B&A Consultants -# expert-judiciaire@ba-consultants.fr -#----------------------------------------------------------- -package liveContactsGUID; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20110221); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets user Windows Live Messenger GUIDs"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching liveContactsGUID v." . $VERSION); - ::rptMsg("liveContactsGUID v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows Live Contacts\\Database"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @subvals = $key->get_list_of_values(); - if (scalar(@subvals) > 0) { - foreach my $valeur (@subvals) { - ::rptMsg($valeur->get_data . " : " . $valeur->get_name); - } - } else { - ::rptMsg($key_path." has no subvalues."); - ::logMsg($key_path." has no subvalues."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} - -1; diff --git a/thirdparty/rr-full/plugins/load.pl b/thirdparty/rr-full/plugins/load.pl index dbfce705576..92231a71e36 100644 --- a/thirdparty/rr-full/plugins/load.pl +++ b/thirdparty/rr-full/plugins/load.pl @@ -5,13 +5,16 @@ # by malware. # # Change history +# 20200921 - MITRE updates +# 20200517 - updated date output format # 20100811 - created # # References +# https://twitter.com/HuntressLabs/status/960507315630768128 # http://support.microsoft.com/kb/103865 # http://security.fnal.gov/cookbook/WinStartup.html # -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC #----------------------------------------------------------- package load; use strict; @@ -20,8 +23,10 @@ package load; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20100811); + MITRE => "T1547\.001", + category => "persistence", + output => "report", + version => 20200921); sub getConfig{return %config} sub getShortDescr { @@ -38,8 +43,10 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching load v.".$VERSION); - ::rptMsg("load v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("load v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -48,7 +55,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("load"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { ::rptMsg(""); diff --git a/thirdparty/rr-full/plugins/localdumps.pl b/thirdparty/rr-full/plugins/localdumps.pl new file mode 100644 index 00000000000..c18036d77f4 --- /dev/null +++ b/thirdparty/rr-full/plugins/localdumps.pl @@ -0,0 +1,94 @@ +#----------------------------------------------------------- +# localdumps.pl +# Get WER LocalDumps settings +# +# Change history: +# 20220419 - updated references +# 20210107 - created +# +# References: +# https://twitter.com/Hexacorn/status/1346579978549399552 +# https://twitter.com/daniel_bilar/status/988925269229568000 +# https://docs.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps +# https://bmcder.com/blog/extracting-cobalt-strike-from-windows-error-reporting (added 20220419) +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, 2013 +#----------------------------------------------------------- +package localdumps; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + MITRE => "T1562\.001", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220419); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get WER LocalDumps settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching localdumps v.".$VERSION); + ::rptMsg("localdumps v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my @paths = ("Microsoft\\Windows\\Windows Error Reporting\\LocalDumps"); + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg(""); + + eval { + my $folder = $key->get_value("DumpFolder")->get_value(); + ::rptMsg("DumpFolder value = ".$folder); + ::rptMsg(""); + }; + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + + eval { + my $folder = $s->get_value("DumpFolder")->get_value(); + ::rptMsg($s->get_name()." DumpFolder value = ".$folder); + ::rptMsg(""); + }; + + } + } + } + else { + ::rptMsg($key_path." not found."); + ::rptMsg(""); + } + } + ::rptMsg("Analysis Tip: The location where user-mode dumps are written can be configured, either universally, or for "); + ::rptMsg("specific applications. This means that a dump can be written to a UNC path, controlled by the threat actor."); + ::rptMsg(""); + ::rptMsg("Ref: https://bmcder.com/blog/extracting-cobalt-strike-from-windows-error-reporting"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/locale.pl b/thirdparty/rr-full/plugins/locale.pl new file mode 100644 index 00000000000..431d4e00196 --- /dev/null +++ b/thirdparty/rr-full/plugins/locale.pl @@ -0,0 +1,109 @@ +#----------------------------------------------------------- +# locale.pl +# Extracts locale settings from NTUSER.DAT and System hives +# +# Change history +# 20220225 - created +# +# References +# +# +# https://attack.mitre.org/techniques/T1614/001/ +# +# Copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package locale; +use strict; + +my %config = (hive => "System, NTUSER\.DAT", + hasShortDescr => 1, + category => "discovery", + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1614\.001", + version => 20220225); + +my $VERSION = getVersion(); + +sub getDescr {} +sub getRefs {} +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getShortDescr { + return "Get locale settings from NTUSER\.DAT & System hives"; +} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching locale v.".$VERSION); + ::rptMsg("locale v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + + my $key = (); + my $key_path = (); + + if ($hive_guess eq "system") { + my $ccs = ::getCCS($root_key); + $key_path = $ccs."\\Control\\Nls\\Language"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $id = $key->get_value("InstallLanguage")->get_data(); + ::rptMsg(sprintf "InstallLanguage = ".$id." (".hex($id).")"); + }; + eval { + my $id = $key->get_value("Default")->get_data(); + ::rptMsg(sprintf "Default = ".$id." (".hex($id).")"); + }; + + } + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Control Panel\\International"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $l = $key->get_value("Locale")->get_data(); + ::rptMsg("Locale = ".$l." (".hex($l).")"); + }; + + eval { + my $l = $key->get_value("LocaleName")->get_data(); + ::rptMsg("LocaleName = ".$l); + }; + + } + } + else { +# + } + + ::rptMsg(""); + ::rptMsg("Analysis Tip: Malware, in particular ransomware, has been observed checking for execution based on the"); + ::rptMsg("locale of the system. This information can be used to determine execution flow, in EXEs, scripts, etc."); + +} + +1; diff --git a/thirdparty/rr-full/plugins/location.pl b/thirdparty/rr-full/plugins/location.pl new file mode 100644 index 00000000000..bb52764a269 --- /dev/null +++ b/thirdparty/rr-full/plugins/location.pl @@ -0,0 +1,104 @@ +#----------------------------------------------------------- +# location +# +# Change history: +# 20211116 - created +# +# Ref: +# +# +# copyright 2021 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package location; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "user activity", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20211116); + +sub getConfig{return %config} +sub getShortDescr { + return "Get apps that use Location services"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching location v.".$VERSION); + ::rptMsg("location v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore\\location\\NonPackaged'; + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + eval { + my $val = $key->get_value("Value")->get_data(); + ::rptMsg("location key Value value: ".$val); + ::rptMsg(""); + }; + + + my @sk1 = $key->get_list_of_subkeys(); + if (scalar @sk1 > 0) { + foreach my $s1 (@sk1) { + processKey($s1); + } + } + else { + ::rptMsg($key_path." has no subkeys."); + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: This plugin provides info about non-packaged apps that make use of location servers, as well as whether"); + ::rptMsg("location services are allowed or denied."); +} + +sub processKey { + my $key = shift; + my $name = $key->get_name(); + + my $start = (); + my $stop = (); + + eval { + my $s = $key->get_value("LastUsedTimeStart")->get_data(); + my ($t0,$t1) = unpack("VV",$s); + $start = ::getTime($t0,$t1); + }; + + eval { + my $s = $key->get_value("LastUsedTimeStop")->get_data(); + my ($t0,$t1) = unpack("VV",$s); + $stop = ::getTime($t0,$t1); + }; + + + if ($start && $stop) { + ::rptMsg($name); + ::rptMsg(sprintf "%-20s %-20s","LastWrite time",::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(sprintf "%-20s %-20s","LastUsedTimeStart",::format8601Date($start)."Z"); + ::rptMsg(sprintf "%-20s %-20s","LastUsedTimeStop",::format8601Date($stop)."Z"); + ::rptMsg(""); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/logmein.pl b/thirdparty/rr-full/plugins/logmein.pl deleted file mode 100644 index 9e7f7132918..00000000000 --- a/thirdparty/rr-full/plugins/logmein.pl +++ /dev/null @@ -1,81 +0,0 @@ -#----------------------------------------------------------- -# logmein.pl -# -# -# -# -# -# Change history -# 20161011 - created -# -# Copyright 2016 QAR, LLC -#----------------------------------------------------------- -package logmein; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20161011); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get list of login times via LogMeIn"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching logmein v.".$VERSION); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# updated added 20130326 - my @paths = ("LogMeIn\\V5\\PerBrowser", - "Wow6432Node\\LogMeIn\\V5\\PerBrowser"); - - foreach my $key_path (@paths) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg(""); - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { -# ::rptMsg($s->get_name()); -# ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); - - my @ts = (); - my $t = ""; - my $u = ""; - - eval { - $u = $s->get_value("LASTUSERNAME")->get_data(); - }; - - eval { - @ts = unpack("VV",$s->get_value("LastUsed")->get_data()); - $t = ::getTime($ts[0],$ts[1]); - }; - ::rptMsg(gmtime($t)." Z - User: ".$u." logged in via LogMeIn"); - } - } - else { - ::rptMsg($key_path." does not appear to have any subkeys.") - } - } - else { -# ::rptMsg($key_path." not found."); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/logmein_tln.pl b/thirdparty/rr-full/plugins/logmein_tln.pl deleted file mode 100644 index 63034497f9d..00000000000 --- a/thirdparty/rr-full/plugins/logmein_tln.pl +++ /dev/null @@ -1,81 +0,0 @@ -#----------------------------------------------------------- -# logmein_tln.pl -# -# -# -# -# -# Change history -# 20161011 - created -# -# Copyright 2016 QAR, LLC -#----------------------------------------------------------- -package logmein_tln; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20161011); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get list of login times via LogMeIn"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching logmein_tln v.".$VERSION); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# updated added 20130326 - my @paths = ("LogMeIn\\V5\\PerBrowser", - "Wow6432Node\\LogMeIn\\V5\\PerBrowser"); - - foreach my $key_path (@paths) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg($key_path); -# ::rptMsg(""); - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { -# ::rptMsg($s->get_name()); -# ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); - - my @ts = (); - my $t = ""; - my $u = ""; - - eval { - $u = $s->get_value("LASTUSERNAME")->get_data(); - }; - - eval { - @ts = unpack("VV",$s->get_value("LastUsed")->get_data()); - $t = ::getTime($ts[0],$ts[1]); - }; - ::rptMsg($t."|REG|||".$u." logged in via LogMeIn"); - } - } - else { -# ::rptMsg($key_path." does not appear to have any subkeys.") - } - } - else { -# ::rptMsg($key_path." not found."); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/logonstats.pl b/thirdparty/rr-full/plugins/logonstats.pl index e8fd5a9add5..7ff73c161ca 100644 --- a/thirdparty/rr-full/plugins/logonstats.pl +++ b/thirdparty/rr-full/plugins/logonstats.pl @@ -2,12 +2,14 @@ # LogonStats # # Change history +# 20200925 - MITRE update +# 20200517 - minor updates # 20180128 - created # # References # https://twitter.com/jasonshale/status/623081308722475009 # -# copyright 2018 H. Carvey, keydet89@yahoo.com +# copyright 2020 H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package logonstats; use strict; @@ -16,8 +18,10 @@ package logonstats; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180128); + MITRE => "", + category => "user activity", + output => "report", + version => 20200925); sub getConfig{return %config} sub getShortDescr { @@ -34,8 +38,8 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching logonstats v.".$VERSION); - ::rptMsg("logonstats v.".$VERSION); # banner - ::rptMsg("- ".getShortDescr()."\n"); # banner + ::rptMsg("logonstats v.".$VERSION); + ::rptMsg(getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -45,14 +49,15 @@ sub pluginmain { eval { my $flt = $key->get_value("FirstLogonTime")->get_data(); - my $str = convertSystemTime($flt); - ::rptMsg("FirstLogonTime: ".$str); + my ($i,$g) = ::convertSystemTime($flt); + ::rptMsg("FirstLogonTime : ".$i."Z"); }; + ::rptMsg("FirstLogonTime error: ".$@) if ($@); eval { my $oc = $key->get_value("FirstLogonTimeOnCurrentInstallation")->get_data(); - my $i = convertSystemTime($oc); - ::rptMsg("FirstLogonTimeOnCurrentInstallation: ".$i); + my ($i,$g) = ::convertSystemTime($oc); + ::rptMsg("FirstLogonTimeOnCurrentInstallation: ".$i."Z"); }; } else { @@ -61,22 +66,4 @@ sub pluginmain { } - -#----------------------------------------------------------- -# convertSystemTime() -# Converts 128-bit SYSTEMTIME object to readable format -#----------------------------------------------------------- -sub convertSystemTime { - my $date = $_[0]; - my @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul", - "Aug","Sep","Oct","Nov","Dec"); - my @days = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat"); - my ($yr,$mon,$dow,$dom,$hr,$min,$sec,$ms) = unpack("v*",$date); - $hr = "0".$hr if ($hr < 10); - $min = "0".$min if ($min < 10); - $sec = "0".$sec if ($sec < 10); - my $str = $days[$dow]." ".$months[$mon - 1]." ".$dom." ".$hr.":".$min.":".$sec." ".$yr; - return $str; -} - 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/logonusername.pl b/thirdparty/rr-full/plugins/logonusername.pl deleted file mode 100644 index 4e255c20233..00000000000 --- a/thirdparty/rr-full/plugins/logonusername.pl +++ /dev/null @@ -1,70 +0,0 @@ -#! c:\perl\bin\perl.exe -#----------------------------------------------------------- -# logonusername.pl -# Plugin for Registry Ripper, NTUSER.DAT edition - gets the -# "Logon User Name" value -# -# Change history -# -# -# -# copyright 2008 H. Carvey -#----------------------------------------------------------- -package logonusername; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080324); - -sub getConfig{return %config} -sub getShortDescr { - return "Get user's Logon User Name value"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching logonusername v.".$VERSION); - ::rptMsg("logonusername v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $logon_name = "Logon User Name"; - - my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - ::rptMsg("Logon User Name"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time [".gmtime($key->get_timestamp())." (UTC)]"); - foreach my $v (@vals) { - if ($v->get_name() eq $logon_name) { - ::rptMsg($logon_name." = ".$v->get_data()); - } - } - } - else { - ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/lsa.pl b/thirdparty/rr-full/plugins/lsa.pl new file mode 100644 index 00000000000..9fc5bf6ad52 --- /dev/null +++ b/thirdparty/rr-full/plugins/lsa.pl @@ -0,0 +1,142 @@ +#----------------------------------------------------------- +# lsa.pl +# +# Change history +# 20220302 - added RunAsPPL documentation +# 20210623 - added "Smoke Ham" check +# 20201025 - added Credential Guard check +# 20200831 - Added check for DisableRestrictedAdmin value +# 20200519 - added RunAsPPL value +# 20200517 - updated date output format +# 20140730 - added "EveryoneIncludesAnonymous" +# 20130307 - created +# +# Reference: +# http://carnal0wnage.attackresearch.com/2013/09/stealing-passwords-every-time-they.html +# https://www.csoonline.com/article/3393268/how-to-outwit-attackers-using-two-windows-registry-settings.html +# https://docs.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/configuring-additional-lsa-protection +# https://www.stigviewer.com/stig/windows_paw/2017-11-21/finding/V-78161 +# https://labs.f-secure.com/blog/catching-lazarus-threat-intelligence-to-real-detection-logic-part-two <- Credential Guard check +# +# https://attack.mitre.org/techniques/T1003/001/ +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package lsa; + +my %config = (hive => "system", + hasShortDescr => 1, + category => "credential access", + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1003\.001", + version => 20220302); + +sub getConfig{return %config} +sub getShortDescr { + return "Lists specific contents of LSA key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my @pkgs = ("Authentication Packages", "Notification Packages", "Security Packages", + "EveryoneIncludesAnonymous"); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching lsa v.".$VERSION); + ::rptMsg("lsa v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key(); +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $current; + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + $current = $key->get_value("Current")->get_data(); + my $ccs = "ControlSet00".$current; + + $key_path = $ccs.'\\Control\\LSA'; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + +# documentation added 20220302 +# https://itm4n.github.io/lsass-runasppl/ + eval { + my $run = $key->get_value("RunAsPPL")->get_data(); + ::rptMsg("RunAsPPL value = ".$run); + ::rptMsg(""); + ::rptMsg("Per CSOOnline article, setting of \"1\" helps protect against pass-the-hash"); + ::rptMsg("and mimikatz-style attacks"); + ::rptMsg(""); + }; + + eval { + my $admin = $key->get_value("DisableRestrictedAdmin")->get_data(); + ::rptMsg("DisableRestrictedAdmin value = ".$admin); + ::rptMsg("A value of \"1\" serves as an additional safeguard against pass-the-hash attacks."); + ::rptMsg(""); + }; + +# Credential Guard check, added 20201025 +# https://labs.f-secure.com/blog/catching-lazarus-threat-intelligence-to-real-detection-logic-part-two +# https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-deviceguard-unattend-lsacfgflags + eval { + my $cg = $key->get_value("LsaCfgFlags")->get_data(); + ::rptMsg("LsaCfgFlags value = ".$cg); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If LsaCfgFlags is \"0\", Credential Guard has been disabled."); + ::rptMsg(""); + }; + +# LimitBlankPasswordUse check added 20210623 +# https://www.fireeye.com/blog/threat-research/2021/06/darkside-affiliate-supply-chain-software-compromise.html +# DarkSide affiliate UNC2465 was observed using the "Smoked Ham" backdoor, which: +# - creates a user account, adds it to local admins, and hides it from view on the Welcome Screen +# - enables lateral movement via RDP +# - limits blank passwords to console logins only, and enables the UseLogonCredential value + eval { + my $l = $key->get_value("LimitBlankPasswordUse")->get_data(); + ::rptMsg("LimitBlankPasswordUse value = ".$l); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If LimitBlankPasswordUse is \"1\", functionality is enabled to limit local account use of blank "); + ::rptMsg("passwords to console logon only."); + ::rptMsg(""); + }; + + foreach my $v (@pkgs) { + eval { + my $d = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-25s: ".$d,$v); + }; + } + ::rptMsg(""); + ::rptMsg("Analysis Tips:"); + ::rptMsg("- Check Notification Packages value for unusual entries."); + ::rptMsg("- EveryoneIncludesAnonymous = 0 means that Anonymous users do not have the same"); + ::rptMsg(" privileges as the Everyone Group."); + } + else { + ::rptMsg($key_path." not found."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/lsa_packages.pl b/thirdparty/rr-full/plugins/lsa_packages.pl deleted file mode 100644 index 5f138792173..00000000000 --- a/thirdparty/rr-full/plugins/lsa_packages.pl +++ /dev/null @@ -1,82 +0,0 @@ -#----------------------------------------------------------- -# lsa_packages.pl -# -# -# Change history -# 20140730 - added "EveryoneIncludesAnonymous" -# 20130307 - created -# -# Reference: -# http://carnal0wnage.attackresearch.com/2013/09/stealing-passwords-every-time-they.html -# -# Category: Autostart -# -# -# copyright 2014 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package lsa_packages; - -my %config = (hive => "System", - hasShortDescr => 1, - category => "malware", - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20140730); - -sub getConfig{return %config} -sub getShortDescr { - return "Lists various *Packages key contents beneath LSA key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my @pkgs = ("Authentication Packages", "Notification Packages", "Security Packages", - "EveryoneIncludesAnonymous"); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching lsa_packages v.".$VERSION); - ::rptMsg("lsa_packages v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key(); -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - - $key_path = $ccs.'\\Control\\LSA'; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())." UTC"); - ::rptMsg(""); - - foreach my $v (@pkgs) { - eval { - my $d = $key->get_value($v)->get_data(); - ::rptMsg(sprintf "%-23s: ".$d,$v); - }; - } - } - else { - ::rptMsg($key_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/lsasecrets.pl b/thirdparty/rr-full/plugins/lsasecrets.pl deleted file mode 100644 index 602621dc1c7..00000000000 --- a/thirdparty/rr-full/plugins/lsasecrets.pl +++ /dev/null @@ -1,67 +0,0 @@ -#----------------------------------------------------------- -# lsasecrets.pl -# Get update times for LSA Secrets from the Security hive file -# -# History -# 20140408 - updated to handle instances where the keys/values are not found -# - this seems to occur in cases of Windows 7 -# 20100219 - created -# -# References -# http://moyix.blogspot.com/2008/02/decrypting-lsa-secrets.html -# -# copyright 2014 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package lsasecrets; -use strict; - -my %config = (hive => "Security", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20140408); - -sub getConfig{return %config} -sub getShortDescr { - return "TEST - Get update times for LSA Secrets"; -} -sub getDescr{} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching lsasecrets v.".$VERSION); - ::rptMsg("lsasecrets v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Policy\\Secrets"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Domain secret - \$MACHINE\.ACC"); -# http://support.microsoft.com/kb/175468 - eval { - my $c = $key->get_subkey("\$MACHINE\.ACC\\CupdTime")->get_value("")->get_data(); - my @v = unpack("VV",$c); - my $cupd = gmtime(::getTime($v[0],$v[1])); - ::rptMsg("CupdTime = ".$cupd); - - my $o = $key->get_subkey("\$MACHINE\.ACC\\OupdTime")->get_value("")->get_data(); - @v = unpack("VV",$c); - my $oupd = gmtime(::getTime($v[0],$v[1])); - ::rptMsg("OupdTime = ".$oupd); - }; - ::rptMsg("\$MACHINE\.ACC key not found") if ($@); - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/lsass_auditlevel.pl b/thirdparty/rr-full/plugins/lsass_auditlevel.pl new file mode 100644 index 00000000000..fbbbb76020f --- /dev/null +++ b/thirdparty/rr-full/plugins/lsass_auditlevel.pl @@ -0,0 +1,69 @@ +#----------------------------------------------------------- +# lsass_auditlevel +# Check AuditLevel for LSASS.exe +# +# Change history: +# 20220119 - created +# +# Ref: +# https://docs.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/configuring-additional-lsa-protection +# +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package lsass_auditlevel; +use strict; + +my %config = (hive => "software", + output => "report", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1003\.001", + category => "credential access", + version => 20220119); + +sub getConfig{return %config} +sub getShortDescr { + return "Check AuditLevel value for LSASS"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching lsass_auditlevel v.".$VERSION); + ::rptMsg("lsass_auditlevel v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\LSASS\.exe"; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + eval { + my $a = $key->get_value("AuditLevel")->get_data(); + ::rptMsg("AuditLevel value: ".$a); + }; + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: An \"AuditLevel\" value of 0x08 will result in event ID 3065 and 3066 records being generated to the"); + ::rptMsg("Microsoft-Windows-CodeIntegrity Event Log file, indicating attempts to access the lsass process without meeting"); + ::rptMsg("shared section security or code signing requirements, respectively\. Per the reference, use this plugin in "); + ::rptMsg("combination with the \"lsa\.pl\" plugin\."); + ::rptMsg(""); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/configuring-additional-lsa-protection"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/lxss.pl b/thirdparty/rr-full/plugins/lxss.pl new file mode 100644 index 00000000000..e2633cd22e1 --- /dev/null +++ b/thirdparty/rr-full/plugins/lxss.pl @@ -0,0 +1,87 @@ +#----------------------------------------------------------- +# lxss.pl +# +# Change history +# 20200927 - MITRE update +# 20200511 - updated date output format +# 20190813 - created +# +# References +# https://attack.mitre.org/techniques/T1564/006/ +# +# copyright 2019-2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package lxss; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1564\.006", + version => 20200927); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets WSL config."; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching lxss v.".$VERSION); + ::rptMsg("lxss v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Lxss'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("Lxss"); + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $def = $key->get_value("DefaultDistribution")->get_data(); + ::rptMsg("DefaultDistribution: ".$def); + }; + + ::rptMsg(""); + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $sk (@subkeys) { + ::rptMsg($sk->get_name()); + ::rptMsg("LastWrite: ".::format8601Date($sk->get_timestamp())."Z"); + + eval { + my $dist = $sk->get_value("DistributionName")->get_data(); + ::rptMsg("DistributionName: ".$dist); + }; + + eval { + my $kern = $sk->get_value("KernelCommandLine")->get_data(); + ::rptMsg("KernelCommandLine: ".$kern); + }; + ::rptMsg(""); + } + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/lxss_tln.pl b/thirdparty/rr-full/plugins/lxss_tln.pl new file mode 100644 index 00000000000..63197d855e1 --- /dev/null +++ b/thirdparty/rr-full/plugins/lxss_tln.pl @@ -0,0 +1,86 @@ +#----------------------------------------------------------- +# lxss_tln.pl +# Plugin for Registry Ripper +# Windows\CurrentVersion\Applets Recent File List values +# +# Change history +# 20200927 - MITRE update +# 20190813 - created +# +# References +# +# +# copyright 2019-2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package lxss_tln; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "tln", + MITRE => "T1564\.006", + version => 20200927); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets WSL config."; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; +# ::logMsg("Launching lxss v.".$VERSION); +# ::rptMsg("lxss v.".$VERSION); +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Lxss'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { +# ::rptMsg("Lxss"); +# ::rptMsg($key_path); +# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." Z"); + +# eval { +# my $def = $key->get_value("DefaultDistribution")->get_data(); +# ::rptMsg("DefaultDistribution: ".$def); +# }; +# ::rptMsg(""); + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $sk (@subkeys) { +# ::rptMsg($sk->get_name()); + my $lw = $sk->get_timestamp(); + + my $str; + eval { + my $dist = $sk->get_value("DistributionName")->get_data(); + $str .= " ".$dist; + }; + + eval { + my $kern = $sk->get_value("KernelCommandLine")->get_data(); + $str .= " (".$kern.")"; + }; + ::rptMsg($lw."|REG|||Lxss - ".$str); + } + } + } + else { +# ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/macaddr.pl b/thirdparty/rr-full/plugins/macaddr.pl index 65bdec97ccc..a5fe89c0fd2 100644 --- a/thirdparty/rr-full/plugins/macaddr.pl +++ b/thirdparty/rr-full/plugins/macaddr.pl @@ -5,26 +5,31 @@ # code # # History: +# 20210319 - added NetworkSetup2 check +# 20201005 - MITRE update +# 20200515 - updated date output format # 20190506 - updated # 20090118 - created # -# copyright 2019, QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package macaddr; use strict; -my %config = (hive => "System,Software", - osmask => 22, +my %config = (hive => "system,software", + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20190506); + report => "report", + version => 20210319); sub getConfig{return %config} sub getShortDescr { - return " -- "; + return "Various checks for MAC address(es)"; } sub getDescr{} sub getRefs {} @@ -37,8 +42,8 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching macaddr v.".$VERSION); - ::rptMsg("macaddr v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("macaddr v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $guess = guessHive($hive); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -65,11 +70,12 @@ sub pluginmain { eval { $na = $key->get_subkey($name)->get_value("NetworkAddress")->get_data(); ::rptMsg(" ".$name.": NetworkAddress = ".$na); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); $found = 1; }; } - ::rptMsg("No NetworkAddress value found.") if ($found == 0); +# ::rptMsg("No NetworkAddress value found.") if ($found == 0); + ::rptMsg(""); } else { ::rptMsg($key_path." has no subkeys."); @@ -82,6 +88,31 @@ sub pluginmain { else { ::rptMsg($key_path." not found."); } +# added 20210319 + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\NetworkSetup2\\Interfaces"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + + eval { + my $addr = $s->get_subkey("Kernel")->get_value("CurrentAddress")->get_data(); + $addr = join(':',unpack("(H2)*",$addr)); + $addr =~ tr/a-z/A-Z/; + ::rptMsg("Interface : ".$s->get_subkey("Kernel")->get_value("IfAlias")->get_data()); + ::rptMsg("CurrentAddress: ".$addr); + ::rptMsg(""); + }; + } + } + } + else { + ::rptMsg($key_path." not found."); + } + } elsif ($guess eq "Software") { my $key_path = "Microsoft\\Windows Genuine Advantage"; diff --git a/thirdparty/rr-full/plugins/maint.pl b/thirdparty/rr-full/plugins/maint.pl new file mode 100644 index 00000000000..0cb82de51b9 --- /dev/null +++ b/thirdparty/rr-full/plugins/maint.pl @@ -0,0 +1,73 @@ +#----------------------------------------------------------- +# maint.pl +# +# +# Change history: +# 20210326 - created +# +# References: +# https://twitter.com/jeffmcjunkin/status/967109511575044096 +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, 2013 +#----------------------------------------------------------- +package maint; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + MITRE => "T1562\.001", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210326); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check for MaintenanceDisabled value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching maint v.".$VERSION); + ::rptMsg("maint v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Schedule\\Maintenance"; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $m = $key->get_value("MaintenanceDisabled")->get_data(); + ::rptMsg("MaintenanceDisabled value: ".$m); + }; + ::rptMsg("MaintenanceDisabled value not found.") if ($@); + + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the \"MaintenanceDisabled\" value is set to 1, maintenance functions such as malware scans, defrag, "); + ::rptMsg("etc., will be disabled. Windows Updates are not affected."); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/malware.pl b/thirdparty/rr-full/plugins/malware.pl deleted file mode 100644 index c9297440b5e..00000000000 --- a/thirdparty/rr-full/plugins/malware.pl +++ /dev/null @@ -1,545 +0,0 @@ -#----------------------------------------------------------- -# malware.pl -# -# This plugin is essentially a 'container' for a lot of other individual -# plugins, running the queries against any hive. -# -# References: -# -# -# Change history: -# 20190527 - updates -# 20190107 - added remote UAC bypass check -# 20180702 - added values to check for MS Defender being disabled -# 20161210 - added WebRoot check -# 20160615 - added new Sofacy persistence -# 20160412 - added Ramdo checks -# 20160217 - added check for Locky ransomware -# 20160127 - added Helminth entry -# 20151203 - added DCOM port config detection -# 20151013 - added Warood.B -# 20151012 - 9002 ref/checks added -# 20151008 - added keys -# 20150828 - created -# -# copyright 2018 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package malware; -use strict; - -my %config = (hive => "All", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - category => "malware", - version => 20190527); - -sub getConfig{return %config} -sub getShortDescr { - return "Checks for malware-related keys/values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching malware v.".$VERSION); - ::rptMsg("malware v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path; - my $key; - -# Security Hive -# This is the same code as the secrets.pl plugin - provides an indication -# regarding the use of GSecDump on systems; see "The Art of Memory Forensics", -# - - eval { - $key_path = "Policy\\Secrets"; - $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - } - }; - - -# System Hive -# First, need to get the value for the CurrentControlSet - my $ccs; - my $current; - eval { - if ($key = $root_key->get_subkey("Select")) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - }; - -# If we've got a populated $ccs value, other checks will now likely work -# Look for known/observed PlugX services - my @services = ("RasTLS","Macfee MC","usta","emproxy","mproxysvr3","gzQkNtWeabrwf","brwTRsulGqj","sock5proxy"); - eval { - foreach my $svc (@services) { - if ($key = $root_key->get_subkey($ccs."\\services\\".$svc)) { - ::rptMsg("Possible PlugX variant found in ".$svc." service"); - - eval { - ::rptMsg(" ImagePath : ".$key->get_value("ImagePath")->get_data()); - }; - - eval { - ::rptMsg(" Description: ".$key->get_value("Description")->get_data()); - }; - - } - } - }; - -# Added 20190527 -# https://www.praetorian.com/blog/mitigating-mimikatz-wdigest-cleartext-credential-theft?edition=2019 - eval { - $key_path = "Control\\SecurityProviders\\WDigest"; - if ($key = $root_key->get_subkey($key_path)){ - my $ulc = $key->get_value("UseLogonCredential")->get_data(); - ::rptMsg(" UseLogonCredential value = ".$ulc); - } - }; - -# Software Hive - -# Added 20190527 -# https://www.stigviewer.com/stig/windows_7/2013-03-14/finding/V-3470 - eval { - $key_path = "Policies\\Microsoft\\Windows NT\\Terminal Services\\"; - if ($key = $root_key->get_subkey($key_path)) { - my $fallow = $key->get_value("fAllowUnsolicited")->get_data(); - ::rptMsg(" fAllowUnsolicited value = ".$fallow); - } - }; - - -# Check for several PlugX variants -# http://www.symantec.com/security_response/earthlink_writeup.jsp?docid=2013-112101-0135-99 -# http://www.trendmicro.com/vinfo/us/threat-encyclopedia/malware/PLUGX - eval { - $key_path = "Classes\\FAST"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible PlugX variant (".$key_path.") found."); - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - } - }; - - eval { - $key_path = "Classes\\XXXX"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible PlugX variant (".$key_path.") found."); - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - } - }; - - eval { - $key_path = "BINARY"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible PlugX variant (".$key_path.") found."); - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - if ($key->get_value("SXLOC\.ZAP")) { - ::rptMsg("Value SXLOC\.ZAP found."); - } - } - }; - -# https://www.sophos.com/en-us/threat-center/threat-analyses/viruses-and-spyware/Troj~DwnLdr-GWF/detailed-analysis.aspx - eval { - $key_path = "Begin"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible Downloader variant (".$key_path.") found."); - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - } - }; - - -# check Classes\Network\SharingHandler default value for modification -# in most cases, it's "ntshrui.dll" -# http://www.trendmicro.com/vinfo/us/threat-encyclopedia/malware/worm_cosmu.elg - eval { - $key_path = "Classes\\Network\\SharingHandler"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg(" LastWrite Time : ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(" (Default) value: ".$key->get_value("")->get_data()); - ::rptMsg("If the (Default) value is not ntshrui\.dll, there may be an infection."); - } - ::rptMsg(""); - }; - -# Poison Ivy variant -# http://blog.jpcert.or.jp/2015/07/poisonivy-adapts-to-communicate-through-authentication-proxies.html - eval { - $key_path = "Classes\\BJ\\Static"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - if ($key->get_value("MessageFile")) { - ::rptMsg("MessageFile value found."); - } - ::rptMsg(""); - } - }; - -# Warood.A -# https://www.microsoft.com/security/portal/threat/encyclopedia/entry.aspx?Name=Backdoor:Win32/Warood.A#tab=2 - eval { - $key_path = "Clients\\Netrau"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - if ($key->get_value("HostGUID") || $key->get_value("InstallTime")) { - ::rptMsg("Warood.A value(s) found."); - } - ::rptMsg(""); - } - }; - -# Warood.B -# https://www.microsoft.com/security/portal/threat/encyclopedia/entry.aspx?Name=Backdoor:Win32/Warood.B#tab=2 - eval { - $key_path = "Clients\\sdata"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - if ($key->get_value("sdata")) { - ::rptMsg("sdata value found."); - } - ::rptMsg(""); - } - }; - -# From FireEye APT30 report, ShipShape malware - eval { - $key_path = "Microsoft\\ShipUp"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible ShipShape malware found: ".$key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - if ($key->get_value("lnk")) { - ::rptMsg("lnk value found."); - } - ::rptMsg(""); - } - }; - -# From FireEye APT30 report, SpaceShip malware - eval { - $key_path = "Microsoft\\ShipTr"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible SpaceShip malware found: ".$key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - if ($key->get_value("lnk")) { - ::rptMsg("lnk value found."); - } - ::rptMsg(""); - } - }; - -# From MIRCon 2014 presentation on WMI -# HKLM/Software/Microsoft/WBEM/ESS///./root/CIMV2/Win32ClockProvider -# $$$PROTO.HIV\Microsoft\WBEM\ESS\//./root/CIMV2\Win32ClockProvider - eval { - $key_path = "Microsoft\\WBEM\\ESS\\//./root/CIMV2\\Win32ClockProvider"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg("Possible use of WMI time trigger found."); - ::rptMsg(""); - } - }; - - -# Bledoor/RbDoor - added 20151117 -# https://www.microsoft.com/security/portal/threat/encyclopedia/entry.aspx?Name=Trojan:Win64/Bledoor.A#tab=2 - eval { - $key_path = "Microsoft\\HTMLHelp"; - if ($key = $root_key->get_subkey($key_path)) { - if ($key->get_value("data")) { - ::rptMsg("Possible BleDoor/Rbdoor malware found: ".$key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg("data value found: ".$key->get_value("data")->get_value()); - } - ::rptMsg(""); - } - }; - -# Detect DCOM port change -# https://www.blackhat.com/docs/us-15/materials/us-15-Graeber-Abusing-Windows-Management -# -Instrumentation-WMI-To-Build-A-Persistent%20Asynchronous-And-Fileless-Backdoor-wp.pdf -# http://blog.backslasher.net/setting-dynamic-rpc-port-ranges.html - eval { - $key_path = "Microsoft\\Rpc\\Internet"; - if ($key = $root_key->get_subkey($key_path)) { - if ($key->get_value("Ports")) { - ::rptMsg("Possible DCOM port config change found: ".$key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg("Ports value: ".$key->get_value("Ports")->get_value()); - } - ::rptMsg(""); - } - }; - -# WebRoot Threat checks - eval { - $key_path = "WRData\\Threats\\History"; - if ($key = $root_key->get_subkey($key_path)) { - my @vals = $key->get_list_of_values(); - if (scalar @vals > 0) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time : ".gmtime($key->get_timestamp())." UTC"); - foreach my $v (@vals) { - ::rptMsg($v->get_name()." - ".$v->get_data()); - } - } - else { - ::rptMsg($key_path." has no values."); - } - ::rptMsg(""); - } - }; - eval { - $key_path = "Wow6432Node\\WRData\\Threats\\History"; - if ($key = $root_key->get_subkey($key_path)) { - my @vals = $key->get_list_of_values(); - if (scalar @vals > 0) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time : ".gmtime($key->get_timestamp())." UTC"); - foreach my $v (@vals) { - ::rptMsg($v->get_name()." - ".$v->get_data()); - } - } - else { - ::rptMsg($key_path." has no values."); - } - ::rptMsg(""); - } - }; - -# https://www.ghacks.net/2015/10/25/how-to-disable-windows-defender-in-windows-10-permanently/ - eval { - $key_path = "Policies\\Microsoft\\Windows Defender"; - if ($key = $root_key->get_subkey($key_path)) { - my $dis = $key->get_value("DisableAntiSpyware")->get_data(); - if ($dis == 1) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time : ".gmtime($key->get_timestamp())." UTC"); - ::rptMsg("DisableAntiSpyware value = 1"); - } - } - }; - - eval { - $key_path = "Policies\\Microsoft\\Windows Defender\\Real-Time Protection"; - if ($key = $root_key->get_subkey($key_path)) { - my $dis = $key->get_value("DisableRealtimeMonitoring")->get_data(); - if ($dis == 1) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time : ".gmtime($key->get_timestamp())." UTC"); - ::rptMsg("DisableRealtimeMonitoring value = 1"); - } - } - }; - -# Remote UAC bypass -# https://support.microsoft.com/en-us/help/951016/description-of-user-account-control-and-remote-restrictions-in-windows - eval { - $key_path = "Microsoft\\Windows\\CurrentVersion\\Policies\\System"; - if ($key = $root_key->get_subkey($key_path)) { - my $uac = $key->get_value("LocalAccountTokenFilterPolicy")->get_data(); - if ($uac == 1) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time : ".gmtime($key->get_timestamp())." UTC"); - ::rptMsg("LocalAccountTokenFilterPolicy value = 1; remote UAC bypass"); - } - } - }; - -# NTUSER.DAT/USRCLASS.DAT - -# Possible PlugX -# http://www.symantec.com/security_response/earthlink_writeup.jsp?docid=2013-112101-0135-99 - eval { - $key_path = "Software\\BINARY"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible PlugX variant (".$key_path.") found."); - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - if ($key->get_value("SXLOC\.ZAP")) { - ::rptMsg("Value SXLOC\.ZAP found."); - } - } - }; - -# Nflog, et al. -# http://www.microsoft.com/security/portal/threat/encyclopedia/entry.aspx?name=TROJAN:WIN32/NFLOG.A#tab=2 -# https://www.sophos.com/en-us/threat-center/threat-analyses/viruses-and-spyware/Troj~DwnLdr-GWF/detailed-analysis.aspx - eval { - $key_path = "Software\\Microsoft\\Clock"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible Nflog variant (".$key_path.") found."); - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - if ($key->get_value("HID")) { - ::rptMsg("Value HID found: ".$key->get_value("HID")->get_data()); - } - } - }; - -# 9002 RAT -# http://researchcenter.paloaltonetworks.com/2015/09/chinese-actors-use-3102-malware-in-attacks-on-us-government-and-eu-media/ -# http://blog.cylance.com/another-9002-trojan-variant - eval { - $key_path = "Software\\TransPan"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible 9002 RAT variant (".$key_path.") found."); - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - if ($key->get_value("RunPath") || $key->get_value("mshtm")) { - ::rptMsg(" Possible 9002 config value(s) found."); - } - } - }; - -# From FireEye report on APT30/BackSpace RAT - eval { - $key_path = "Software\\Microsoft\\CurrentHalInf"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible BACKSPACE RAT variant (".$key_path.") found."); - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - if ($key->get_value("hFlag")) { - ::rptMsg(" Possible hFlag value found: ".$key->get_value("hFlag")->get_data()); - } - } - }; - - eval { - $key_path = "Software\\Microsoft\\CurrentPnpSetup"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible BACKSPACE RAT variant (".$key_path.") found."); - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - if ($key->get_value("lnk") || $key->get_value("hostid")) { - ::rptMsg(" Possible BACKSPACE value(s) found."); - } - } - }; - -# TEST - this addition was derived from malware write-ups, which may not be correct -# Helminth -# http://www.threatexpert.com/report.aspx?md5=3448c57a2dfc824098fca500478ab405 -# http://www.trendmicro.no/vinfo/no/threat-encyclopedia/malware/troj_battoexe.dv - eval { - $key_path = "Software\\Microsoft\\Wbem\\WMIC"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - if ($key->get_value("WMICLC")) { - ::rptMsg(sprintf " WMICLC: 0x%x",$key->get_value("WMICLC")->get_data()); - } - - if ($key->get_value("mofcompMUIStatus")) { - ::rptMsg(sprintf " mofcompMUIStatus: 0x%x",$key->get_value("mofcompMUIStatus")->get_data()); - } - } - }; - -# https://www.carbonblack.com/2016/01/31/tackling-latentbot-look-big-picture-not-just-individual-functions/ - eval { - $key_path = "Software\\Google\\Update\\network\\secure"; - if ($key = $root_key->get_subkey($key_path)) { - if ($key->get_value("0")) { - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - ::rptMsg(" Possible LatentBot malware located."); - ::rptMsg(sprintf " 0: 0x%x",$key->get_value("0")->get_data()); - } - - if ($key->get_value("1")) { - ::rptMsg(sprintf " 1: 0x%x",$key->get_value("1")->get_data()); - } - } - }; -# Locky check -# http://www.bleepingcomputer.com/news/security/the-locky-ransomware-encrypts-local-files-and-unmapped-network-shares/ - eval { - $key_path = "Software\\Locky"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(" LastWrite time: ".gmtime($key->get_timestamp())); - if ($key->get_value("id")) { - ::rptMsg(" Possible Locky ransomware located."); - ::rptMsg(" Value 'id': ".$key->get_value("id")->get_data()); - } - } - }; - -# Ramdo checks, added 20160412 -# https://blogs.technet.microsoft.com/mmpc/2014/04/02/msrt-april-2014-ramdo/ -# https://www.symantec.com/security_response/writeup.jsp?docid=2014-021912-3653-99&tabid=2 - eval { - my @val_names = ("tLast_ReadedSpec", "tLastCollab_doc"); - $key_path = "Software\\Adobe\\Adobe ARM\\1.0\\ARM"; - if ($key = $root_key->get_subkey($key_path)) { - foreach my $val (@val_names) { - if (my $v = $key->get_value($val)) { - ::rptMsg("Possible Ramdo value found."); - ::rptMsg(" ".$val." = ".$v->get_data()); - } - } - } - }; - - eval { - my @versions = ("9.0", "10.0","11.0","12.0","13.0"); - my @val_names = ("iTestPropulsion", "iTestShears"); - foreach my $version (@versions) { - $key_path = "Software\\Adobe\\Adobe Reader\\".$version."\\IPM"; - if ($key = $root_key->get_subkey($key_path)) { - foreach my $val (@val_names) { - if (my $v = $key->get_value($val)) { - ::rptMsg("Possible Ramdo value found: ".$val." = ".$v->get_data()); - } - } - } - } - }; - - eval { - $key_path = "Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION"; - if ($key = $root_key->get_subkey($key_path)) { - if ($key->get_value("twunk_32.exe")->get_data() == 9000) { - ::rptMsg("Possible Ramdo value found: twunk_32.exe = 9000"); - } - - if ($key->get_value("winhlp32.exe")->get_data() == 9000) { - ::rptMsg("Possible Ramdo value found: winhlp32.exe = 9000"); - } - - } - }; - -# Sofacy -# http://researchcenter.paloaltonetworks.com/2016/06/unit42-new-sofacy-attacks-against-us-government-agency/ -# http://www.trendmicro.com/vinfo/us/threat-encyclopedia/malware/troj_cve20151641.bzd -# http://www.hexacorn.com/blog/2014/04/16/beyond-good-ol-run-key-part-10/ - eval { - $key_path = "Software\\Microsoft\\Office test\\Special\\Perf"; - if ($key = $root_key->get_subkey($key_path)) { - my $bte; - if ($bte = $key->get_value("")->get_data()) { - ::rptMsg("Possible Sofacy value found: ".$bte); - ::rptMsg("**Be sure to examine the ".$bte." file\."); - } - } - }; - -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/menuorder.pl b/thirdparty/rr-full/plugins/menuorder.pl deleted file mode 100644 index 145d67c1b9a..00000000000 --- a/thirdparty/rr-full/plugins/menuorder.pl +++ /dev/null @@ -1,384 +0,0 @@ -#----------------------------------------------------------- -# menuorder.pl -# Plugin for Registry Ripper -# -# Change history -# 20121005 - created Tested on XP & Win7 only (not Vista) -# -# References: -# http://kurtaubuchon.blogspot.com/2011/11/start-menu-and-ie-favorites-artifacts.html -# http://journeyintoir.blogspot.com/2013/04/plugin-menuorder.html -# -# copyright 2012 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package menuorder; -use strict; -use Time::Local; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20121005); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's MenuOrder subkeys"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching menuorder v.".$VERSION); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - ::rptMsg("menuorder v.".$VERSION); - ::rptMsg(""); -# LastVistedMRU - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder"; - my $key; - my @vals; - if ($key = $root_key->get_subkey($key_path)) { - - eval { - my $start = $key->get_subkey("Start Menu2"); - recurseKeys($start,""); - - }; -# ::rptMsg("Error: ".$@) if ($@); - - eval { - my $fav = $key->get_subkey("Favorites"); - recurseKeys2($fav,""); - - }; -# ::rptMsg("Error: ".$@) if ($@); - - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub recurseKeys { - my $key = shift; - my $name = shift; - - ::rptMsg($name."\\".$key->get_name()); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())." Z"); - - my $order; - eval { - $order = $key->get_value("Order")->get_data(); - my @dat = split(/AugM/,$order); -# $dat[0] appears to be a header of some kind. -# ::rptMsg("Entries: ".unpack("V",substr($dat[0],0x10,4))); -# Within each section, starting with $dat[1], the 2nd DWORD appears to be the number of -# entries recorded in that section. - foreach my $n (1..(scalar(@dat) - 1)) { - my %item = parseAugM($dat[$n]); - ::rptMsg(" ".$item{name}); - } - }; - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - recurseKeys($s,$name."\\".$key->get_name()); - } - } - else { -# No subkeys - } - -} - - -sub recurseKeys2 { - my $key = shift; - my $name = shift; - - ::rptMsg($name."\\".$key->get_name()); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())." Z"); - - my $order; - eval { - $order = $key->get_value("Order")->get_data(); -# ::rptMsg(" - Order value found."); - parseOrder2($order); - - }; -# ::rptMsg("Error: ".$@) if ($@); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - recurseKeys2($s,$name."\\".$key->get_name()); - } - } - else { -# No subkeys - } - -} - -#----------------------------------------------------------- -# parseOrder2() -# -#----------------------------------------------------------- -sub parseOrder2 { - my $data = shift; - my $ofs = 0x1c; - - my $num = unpack("V",substr($data,0x10,4)); - - foreach my $n (1..$num) { - my $sz = unpack("v",substr($data,$ofs,2)); - my $dat = substr($data,$ofs,$sz); - my %item = parseItem($dat); - ::rptMsg(" ".$item{name}); - $ofs += ($sz + 0x0e); - } -} - -#----------------------------------------------------------- -# parseAugM() -# -#----------------------------------------------------------- -sub parseAugM { - my $data = shift; - my %item = (); - - if (unpack("V",substr($data,0,4)) == 0x2) { - - my @mdate = unpack("VV",substr($data,0x10,4)); - my $tag = 1; - my $cnt = 0; - my $str = ""; - while($tag) { - my $s = substr($data,0x16 + $cnt,1); - return %item unless (defined $s); - if ($s =~ m/\x00/ && ((($cnt + 1) % 2) == 0)) { - $tag = 0; - } - else { - $str .= $s; - $cnt++; - } - } - my $ofs = 0x16 + $cnt + 1; - my $shortname = $str; - - my $data2 = substr($data,$ofs,unpack("v",substr($data,$ofs,2))); - my $sz = unpack("v",substr($data2,0,2)); - $item{version} = unpack("v",substr($data2,2,2)); - my $ext = unpack("v",substr($data2,4,2)); - - $ofs = 0x08; -# Get creation time values; -# my @m = unpack("vv",substr($data,$ofs,4)); - $ofs += 4; -# Get last access time values -# @m = unpack("vv",substr($data,$ofs,4)); - $ofs += 4; - $ofs += 4; - - $tag = 1; - $cnt = 0; - $str = ""; - while ($tag) { - my $s = substr($data2,$ofs + $cnt,2); - return %item unless (defined $s); - if (unpack("v",$s) == 0) { - $tag = 0; - } - else { - $str .= $s; - $cnt += 2; - } - } - $str =~ s/\x00//g; - $item{name} = $str; - $ofs += $cnt; -# ::rptMsg(sprintf " - Ofs: 0x%x Remaining Data: 0x%x",$ofs,$sz - $ofs); - - if (($sz - $ofs) > 0x10) { - my $str = substr($data2,$ofs,$sz - $ofs); - $str =~ s/^\x00+//; - my $s = (split(/\x00/,$str,2))[0]; - $item{name} .= " (".$s.")"; - } - - } - else { - - } - return %item; -} - -#----------------------------------------------------------- -# parseItem() -# -#----------------------------------------------------------- -sub parseItem { - my $data = shift; - my %item = (); - - my $ofs = 0x08; - my @mdate = unpack("VV",substr($data,$ofs,4)); - $ofs += 6; - - my $tag = 1; - my $cnt = 0; - my $str = ""; - while($tag) { - my $s = substr($data,$ofs + $cnt,1); - return %item unless (defined $s); - if ($s =~ m/\x00/ && ((($cnt + 1) % 2) == 0)) { - $tag = 0; - } - else { - $str .= $s; - $cnt++; - } - } - $ofs += ($cnt + 1); - $item{shortname} = $str; - - my $data2 = substr($data,$ofs,unpack("v",substr($data,$ofs,2))); - my $sz = unpack("v",substr($data2,0,2)); - $item{version} = unpack("v",substr($data2,2,2)); - - my $ext = unpack("v",substr($data2,4,2)); - - $ofs = 0x08; -# Get creation time values; -# my @m = unpack("vv",substr($data,$ofs,4)); - $ofs += 4; -# Get last access time values -# my @m = unpack("vv",substr($data,$ofs,4)); - $ofs += 4; -# Check the version - my $jmp; - if ($item{version} == 0x03) { - $jmp = 4; - } - elsif ($item{version} == 0x07) { - $jmp = 22; - } - elsif ($item{version} == 0x08) { - $jmp = 26; - } - else {} - - $ofs += $jmp; - - $tag = 1; - $cnt = 0; - $str = ""; - while ($tag) { - my $s = substr($data2,$ofs + $cnt,2); - return %item unless (defined $s); - if (unpack("v",$s) == 0) { - $tag = 0; - } - else { - $str .= $s; - $cnt += 2; - } - } - $str =~ s/\x00//g; - $item{name} = $str; - $ofs += $cnt; - - return %item; -} - -#----------------------------------------------------------- -# printData() -# subroutine used primarily for debugging; takes an arbitrary -# length of binary data, prints it out in hex editor-style -# format for easy debugging -#----------------------------------------------------------- -sub printData { - my $data = shift; - my $len = length($data); - my $tag = 1; - my $cnt = 0; - - my $loop = $len/16; - $loop++ if ($len%16); - - foreach my $cnt (0..($loop - 1)) { -# while ($tag) { - my $left = $len - ($cnt * 16); - - my $n; - ($left < 16) ? ($n = $left) : ($n = 16); - - my $seg = substr($data,$cnt * 16,$n); - my @str1 = split(//,unpack("H*",$seg)); - - my @s3; - my $str = ""; - - foreach my $i (0..($n - 1)) { - $s3[$i] = $str1[$i * 2].$str1[($i * 2) + 1]; - - if (hex($s3[$i]) > 0x1f && hex($s3[$i]) < 0x7f) { - $str .= chr(hex($s3[$i])); - } - else { - $str .= "\."; - } - } - my $h = join(' ',@s3); - ::rptMsg(sprintf "0x%08x: %-47s ".$str,($cnt * 16),$h); - } -} - -#----------------------------------------------------------- -# convertDOSDate() -# subroutine to convert 4 bytes of binary data into a human- -# readable format. Returns both a string and a Unix-epoch -# time. -#----------------------------------------------------------- -sub convertDOSDate { - my $date = shift; - my $time = shift; - - if ($date == 0x00 || $time == 0x00){ - return (0,0); - } - else { - my $sec = ($time & 0x1f) * 2; - $sec = "0".$sec if (length($sec) == 1); - if ($sec == 60) {$sec = 59}; - my $min = ($time & 0x7e0) >> 5; - $min = "0".$min if (length($min) == 1); - my $hr = ($time & 0xF800) >> 11; - $hr = "0".$hr if (length($hr) == 1); - my $day = ($date & 0x1f); - $day = "0".$day if (length($day) == 1); - my $mon = ($date & 0x1e0) >> 5; - $mon = "0".$mon if (length($mon) == 1); - my $yr = (($date & 0xfe00) >> 9) + 1980; - my $gmtime = timegm($sec,$min,$hr,$day,($mon - 1),$yr); - return ("$yr-$mon-$day $hr:$min:$sec",$gmtime); -# return gmtime(timegm($sec,$min,$hr,$day,($mon - 1),$yr)); - } -} - - - -1; diff --git a/thirdparty/rr-full/plugins/minint.pl b/thirdparty/rr-full/plugins/minint.pl new file mode 100644 index 00000000000..ce28091daf2 --- /dev/null +++ b/thirdparty/rr-full/plugins/minint.pl @@ -0,0 +1,65 @@ +#----------------------------------------------------------- +# minint.pl +# Detects if OS was told it is WinPE +# +# Change history +# 20200831 - created +# +# References +# https://twitter.com/0gtweet/status/1182516740955226112 +# https://blog.sec-labs.com/2019/10/hunting-for-minint-security-audit-block-in-registry/ +# https://www.quppa.net/blog/2016/04/14/beware-of-the-minint-registry-key/ +# +# MITRE: https://attack.mitre.org/techniques/T1562/002/ +# +# copyright 2020 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package minint; + +my %config = (hive => "System", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + category => "defense evasion", + MITRE => "T1562\.002", + version => 20200831); + +sub getConfig{return %config} +sub getShortDescr { + return "MiniNT key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching minint v.".$VERSION); + ::rptMsg("minint v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\MiniNT"; + if ($key = $root_key->get_subkey($key_path)) { + my $lw = ::format8601Date($key->get_timestamp())."Z"; + ::rptMsg($key_path." key found, LastWrite: ".$lw); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the MiniNt key is found, then it may have been added to make Windows think it is"); + ::rptMsg("WinPE; this can inhibit logging."); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/mixer.pl b/thirdparty/rr-full/plugins/mixer.pl index 62a299b57f1..4f7601baeeb 100644 --- a/thirdparty/rr-full/plugins/mixer.pl +++ b/thirdparty/rr-full/plugins/mixer.pl @@ -4,15 +4,16 @@ # to indicate that malware (DarkComet) that includes the option to listen # in on the user may have been active # -# Category: Malware # # Change history +# 20200922 - MITRE update +# 20200517 - updated date output format # 20141112 - created # # References # http://www.ghettoforensics.com/2014/11/dj-forensics-analysis-of-sound-mixer.html # -# copyright 2014 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package mixer; @@ -22,8 +23,10 @@ package mixer; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20141112); + MITRE => "", + output => "report", + category => "devices", + version => 20200922); sub getConfig{return %config} sub getShortDescr { @@ -41,8 +44,8 @@ sub pluginmain { my $ntuser = shift; ::logMsg("Launching mixer v.".$VERSION); - ::rptMsg("mixer v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("mixer v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -61,7 +64,7 @@ sub pluginmain { my ($p1,$p2) = split(/\|/,$def,2); my $dev = (split(/}\./,$p1,2))[1]; my $app = (split(/%b/,$p2,2))[0]; - ::rptMsg(gmtime($lw).",".$app.",".$dev); + ::rptMsg(::format8601Date($lw)."Z,".$app.",".$dev); }; } } diff --git a/thirdparty/rr-full/plugins/mixer_tln.pl b/thirdparty/rr-full/plugins/mixer_tln.pl index 9d65ae9ed43..ee0ab6f62cf 100644 --- a/thirdparty/rr-full/plugins/mixer_tln.pl +++ b/thirdparty/rr-full/plugins/mixer_tln.pl @@ -4,9 +4,9 @@ # to indicate that malware (DarkComet) that includes the option to listen # in on the user may have been active # -# Category: Malware # # Change history +# 20200922 - MITRE update # 20141112 - created # # References @@ -22,8 +22,10 @@ package mixer_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20141112); + output => "tln", + MITRE => "", + category => "devices", + version => 20200922); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/mmc.pl b/thirdparty/rr-full/plugins/mmc.pl index 5de0cd1c3b8..3bcf31bbfa4 100644 --- a/thirdparty/rr-full/plugins/mmc.pl +++ b/thirdparty/rr-full/plugins/mmc.pl @@ -4,12 +4,15 @@ # Microsoft Management Console Recent File List values # # Change history -# +# 20200922 - MITRE update +# 20200517 - updated date output format +# 20080324 - created # # References # # -# copyright 2008 H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package mmc; use strict; @@ -18,8 +21,10 @@ package mmc; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080324); + MITRE => "T1059", + category => "execution", + output => "report", + version => 20200922); sub getConfig{return %config} sub getShortDescr { @@ -36,8 +41,10 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching mmc v.".$VERSION); - ::rptMsg("mmc v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("mmc v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -46,7 +53,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("MMC - Recent File List"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { my %files; @@ -65,12 +72,10 @@ sub pluginmain { } else { ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } diff --git a/thirdparty/rr-full/plugins/mmc_tln.pl b/thirdparty/rr-full/plugins/mmc_tln.pl index 0ea4337ada5..b2b05da0635 100644 --- a/thirdparty/rr-full/plugins/mmc_tln.pl +++ b/thirdparty/rr-full/plugins/mmc_tln.pl @@ -4,6 +4,7 @@ # Microsoft Management Console Recent File List values # # Change history +# 20200922 - MITRE update # 20120828 - updated, transitioned to TLN format output # 20080324 - created # @@ -20,8 +21,10 @@ package mmc_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120828); + output => "tln", + MITRE => "T1059", + category => "program execution", + version => 20200922); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/mmo.pl b/thirdparty/rr-full/plugins/mmo.pl index 06b94c32dc1..4d70eabc578 100644 --- a/thirdparty/rr-full/plugins/mmo.pl +++ b/thirdparty/rr-full/plugins/mmo.pl @@ -4,6 +4,8 @@ # Category: AutoStart, Malware # # History +# 20200922 - MITRE update +# 20200517 - updated date output format # 20130217 - updated with Trojan.Swaylib detection # 20130214 created # @@ -14,19 +16,20 @@ # http://blog.fireeye.com/research/2013/02/the-number-of-the-beast.html # http://www.joesecurity.org/reports/report-f3b9663a01a73c5eca9d6b2a0519049e.html # -# copyright 2013, Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package mmo; use strict; my %config = (hive => "NTUSER\.DAT", - osmask => 22, + MITRE => "T1546", hasShortDescr => 1, - category => "malware", + category => "persistence", hasDescr => 0, hasRefs => 0, - version => 20130217); + output => "report", + version => 20200922); sub getConfig{return %config} @@ -45,8 +48,10 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching mmo v.".$VERSION); - ::rptMsg("mmo v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("mmo v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -54,7 +59,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { @@ -72,7 +77,8 @@ sub pluginmain { } # Section added 17 Feb 2013, to address Trojan.Swaylib # - $key_path = "Software\\Microsoft\\CTF\\LangBarAddIn"; + my $key_path = "Software\\Microsoft\\CTF\\LangBarAddIn"; + my $key; if ($key = $root_key->get_subkey($key_path)) { my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { @@ -80,7 +86,7 @@ sub pluginmain { ::rptMsg($key_path); foreach my $s (@subkeys) { ::rptMsg(" ".$s->get_name()); - ::rptMsg(" LastWrite time: ".gmtime($s->get_timestamp())); + ::rptMsg(" LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); ::rptMsg(""); my $path; @@ -105,4 +111,4 @@ sub pluginmain { ::rptMsg($key_path." not found\."); } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/mndmru.pl b/thirdparty/rr-full/plugins/mndmru.pl index 4fbbf45a8a9..236413a857f 100644 --- a/thirdparty/rr-full/plugins/mndmru.pl +++ b/thirdparty/rr-full/plugins/mndmru.pl @@ -1,15 +1,16 @@ #----------------------------------------------------------- # mndmru.pl -# Plugin for Registry Ripper, # Map Network Drive MRU parser # # Change history -# +# 20200922 - MITRE update +# 20200517 - updated date output format +# 20080324 - created # # References # # -# copyright 2008 H. Carvey +# copyright 2020 H. Carvey #----------------------------------------------------------- package mndmru; use strict; @@ -18,8 +19,10 @@ package mndmru; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080324); + output => "report", + MITRE => "T1021\.002", + category => "lateral movement", + version => 20200922); sub getConfig{return %config} sub getShortDescr { @@ -36,8 +39,10 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching mndmru v.".$VERSION); - ::rptMsg("mndmru v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("mndmru v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -46,7 +51,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("Map Network Drive MRU"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { my %mnd; @@ -67,12 +72,10 @@ sub pluginmain { } else { ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } diff --git a/thirdparty/rr-full/plugins/mndmru_tln.pl b/thirdparty/rr-full/plugins/mndmru_tln.pl index 08ef7b707eb..f0bb84c74cd 100644 --- a/thirdparty/rr-full/plugins/mndmru_tln.pl +++ b/thirdparty/rr-full/plugins/mndmru_tln.pl @@ -4,6 +4,7 @@ # Map Network Drive MRU parser # # Change history +# 20200922 - MITRE update # 20120829 - updated to TLN # 20080324 - mndmru.pl created # @@ -20,8 +21,10 @@ package mndmru_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120829); + MITRE => "T1021\.002", + category => "lateral movement", + output => "tln", + version => 20200922); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/mountdev.pl b/thirdparty/rr-full/plugins/mountdev.pl index 319f1f2be0c..02dee015274 100644 --- a/thirdparty/rr-full/plugins/mountdev.pl +++ b/thirdparty/rr-full/plugins/mountdev.pl @@ -4,6 +4,9 @@ # MountedDevices # # Change history +# 20221129 - updated output format +# 20200921 - MITRE updates +# 20200517 - updated date output format # 20130530 - updated to output Disk Signature in correct format, thanks to # info provided by Tom Yarrish (see ref.) # 20080324 - created @@ -11,7 +14,7 @@ # References # http://blogs.technet.com/b/markrussinovich/archive/2011/11/08/3463572.aspx # -# copyright 2013 QAR, LLC +# copyright 2022 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package mountdev; @@ -21,12 +24,14 @@ package mountdev; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20130530); + MITRE => "", + output => "report", + category => "devices", + version => 20221129); sub getConfig{return %config} sub getShortDescr { - return "Return contents of System hive MountedDevices key"; + return "Return contents of HKLM\\System\\MountedDevices key"; } sub getDescr{} sub getRefs {} @@ -40,16 +45,19 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching mountdev v.".$VERSION); ::rptMsg("mountdev v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); #banner - ::rptMsg(""); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key_path = 'MountedDevices'; my $key; - my %md; + + my %devices = (); + my %volumes = (); + my %drives = (); + if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite time = ".gmtime($key->get_timestamp())."Z"); + ::rptMsg("LastWrite time = ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { @@ -58,27 +66,52 @@ sub pluginmain { my $len = length($data); if ($len == 12) { my $sig = _translateBinary(substr($data,0,4)); - ::rptMsg($v->get_name()); - ::rptMsg(" Drive Signature = ".$sig); - + $drives{$v->get_name()} = "Drive Signature: ".$sig; } - elsif ($len > 12) { - $data =~ s/\x00//g; - push(@{$md{$data}},$v->get_name()); + elsif ($len == 24) { + my $d = ::parseGUID(substr($data,8,16)); + $volumes{$v->get_name()} = "Volume GUID: ".$d; + } + elsif ($len > 0x50) { + $data =~ s/\00//g; + $devices{$v->get_name()} = $data; } else { ::logMsg("mountdev v.".$VERSION."\tData length = $len"); } } - ::rptMsg(""); - foreach my $m (keys %md) { - ::rptMsg("Device: ".$m); - foreach my $item (@{$md{$m}}) { - ::rptMsg(" ".$item); + if (scalar(keys %drives) > 0) { + ::rptMsg("Drives"); + foreach my $k (keys %drives) { + ::rptMsg(sprintf "-25s %-25s",$k,$drives{$k}); + + } + ::rptMsg(""); + } + + if (scalar(keys %devices) > 0) { + ::rptMsg("Devices"); + foreach my $k (keys %devices) { + ::rptMsg(sprintf "%-55s %-70s", $k, $devices{$k}); + + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Look for MSFT Virtual_DVD-ROM devices that map to drive letters, as well as USB devices."); + ::rptMsg(""); + } + + if (scalar(keys %volumes) > 0) { + ::rptMsg("Volumes"); + foreach my $k (keys %volumes) { + ::rptMsg(sprintf "%-15s %-30s",$k,$volumes{$k}); + } ::rptMsg(""); + ::rptMsg("Analysis Tip: Map Volume GUIDs to user's BitBucket\\Volume subkeys, to get max capacity settings."); + ::rptMsg(""); } + } else { ::rptMsg($key_path." has no values."); diff --git a/thirdparty/rr-full/plugins/mountdev2.pl b/thirdparty/rr-full/plugins/mountdev2.pl index 075136e7746..638322ede34 100644 --- a/thirdparty/rr-full/plugins/mountdev2.pl +++ b/thirdparty/rr-full/plugins/mountdev2.pl @@ -4,6 +4,8 @@ # MountedDevices # # Change history +# 20200921 - MITRE update +# 20200517 - updated date output format # 20140721 - update provided by Espen Øyslebø # 20130530 - updated to output Disk Signature in correct format, thanks to # info provided by Tom Yarrish (see ref.) @@ -15,7 +17,7 @@ # References # http://blogs.technet.com/b/markrussinovich/archive/2011/11/08/3463572.aspx # -# copyright 2013 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package mountdev2; @@ -47,12 +49,14 @@ sub squad { } -my %config = (hive => "System", +my %config = (hive => "system", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20140721); + MITRE => "", + output => "report", + category => "devices", + version => 20200921); sub getConfig{return %config} sub getShortDescr { @@ -79,7 +83,7 @@ sub pluginmain { my (%md,%dos,%vol,%offset,%macs); if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite time = ".gmtime($key->get_timestamp())."Z"); + ::rptMsg("LastWrite time = ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { @@ -104,7 +108,7 @@ sub pluginmain { $offset{$v->get_name()} = $o; } elsif ($len > 12) { - $data =~ s/\x00//g; + $data =~ s/\00//g; push(@{$md{$data}},$v->get_name()); } else { @@ -120,15 +124,15 @@ sub pluginmain { } ::rptMsg(""); foreach my $v (sort keys %vol) { - next unless ($v =~ m/^\\\?\?\\Volume\{/); + next unless ($v =~ m/^\\\?\?\\Volume{/); my $id = $v; - $id =~ s/^\\\?\?\\Volume\{//; + $id =~ s/^\\\?\?\\Volume{//; $id =~ s/}$//; $id =~ s/-//g; my $l = hex(substr($id,0,8)); my $m = hex(substr($id,8,4)); my $h = hex(substr($id,12,4)) & 0x0fff; - $h = $m | $h << 16; + my $h = $m | $h << 16; my $t = (::getTime($l,$h) - 574819200); ::rptMsg($v); ::rptMsg(" ".gmtime($t)); @@ -141,7 +145,7 @@ sub pluginmain { if ($item =~ m/^\\\?\?\\Volume/) { my $id = $item; - $id =~ s/^\\\?\?\\Volume\{//; + $id =~ s/^\\\?\?\\Volume{//; $id =~ s/}$//; # $id =~ s/-//g; # my $l = hex(substr($id,0,8)); @@ -188,4 +192,4 @@ sub _translateBinary { return join(' ',reverse @list); } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/mountdev2.pl.old b/thirdparty/rr-full/plugins/mountdev2.pl.old deleted file mode 100755 index 1ec88e43886..00000000000 --- a/thirdparty/rr-full/plugins/mountdev2.pl.old +++ /dev/null @@ -1,152 +0,0 @@ -#----------------------------------------------------------- -# mountdev2.pl -# Plugin for Registry Ripper; Access System hive file to get the -# MountedDevices -# -# Change history -# 20130530 - updated to output Disk Signature in correct format, thanks to -# info provided by Tom Yarrish (see ref.) -# 20120403 - commented out time stamp info from volume GUIDs, added -# listing of unique MAC addresses -# 20120330 - updated to parse the Volume GUIDs to get the time stamps -# 20091116 - changed output -# -# References -# http://blogs.technet.com/b/markrussinovich/archive/2011/11/08/3463572.aspx -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package mountdev2; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20120403); - -sub getConfig{return %config} -sub getShortDescr { - return "Return contents of System hive MountedDevices key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching mountdev2 v.".$VERSION); - ::rptMsg(""); - ::rptMsg("mountdev2 v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = 'MountedDevices'; - my $key; - my (%md,%dos,%vol,%macs); - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite time = ".gmtime($key->get_timestamp())."Z"); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $data = $v->get_data(); - my $len = length($data); - if ($len == 12) { - my $sig = _translateBinary(substr($data,0,4)); -# my $sig = _translateBinary($data); - $vol{$v->get_name()} = $sig; - } - elsif ($len > 12) { - $data =~ s/\00//g; - push(@{$md{$data}},$v->get_name()); - } - else { - ::logMsg("mountdev2 v.".$VERSION."\tData length = $len"); - } - } - - ::rptMsg(sprintf "%-50s %-20s","Volume","Disk Sig"); - ::rptMsg(sprintf "%-50s %-20s","-------","--------"); - foreach my $v (sort keys %vol) { - my $str = sprintf "%-50s %-20s",$v,$vol{$v}; - ::rptMsg($str); - } - ::rptMsg(""); - foreach my $v (sort keys %vol) { - next unless ($v =~ m/^\\\?\?\\Volume{/); - my $id = $v; - $id =~ s/^\\\?\?\\Volume{//; - $id =~ s/}$//; - $id =~ s/-//g; - my $l = hex(substr($id,0,8)); - my $m = hex(substr($id,8,4)); - my $h = hex(substr($id,12,4)) & 0x0fff; - my $h = $m | $h << 16; - my $t = (::getTime($l,$h) - 574819200); - ::rptMsg($v); - ::rptMsg(" ".gmtime($t)); - } - - ::rptMsg(""); - foreach my $m (sort keys %md) { - ::rptMsg("Device: ".$m); - foreach my $item (@{$md{$m}}) { - - if ($item =~ m/^\\\?\?\\Volume/) { - my $id = $item; - $id =~ s/^\\\?\?\\Volume{//; - $id =~ s/}$//; -# $id =~ s/-//g; -# my $l = hex(substr($id,0,8)); -# my $m = hex(substr($id,8,4)); -# my $h = hex(substr($id,12,4)) & 0x0fff; -# my $h = $m | $h << 16; -# my $t = (::getTime($l,$h) - 574819200); -# $item .= " ".gmtime($t); - my $m = (split(/-/,$id,5))[4]; - $m = uc($m); - $m = join(':',unpack("(A2)*",$m)); - $macs{$m} = 1; - } - - ::rptMsg(" ".$item); - } - ::rptMsg(""); - } - ::rptMsg(""); - ::rptMsg("Unique MAC Addresses:"); - foreach (keys %macs) { - ::rptMsg($_); - } - } - else { - ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} - -sub _translateBinary { - my $str = unpack("H*",$_[0]); - my $len = length($str); - my @nstr = split(//,$str,$len); - my @list = (); - foreach (0..($len/2)) { - push(@list,$nstr[$_*2].$nstr[($_*2)+1]); - } - return join(' ',reverse @list); -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/mp2.pl b/thirdparty/rr-full/plugins/mp2.pl index 5922ed160b7..f2fa96b46e4 100644 --- a/thirdparty/rr-full/plugins/mp2.pl +++ b/thirdparty/rr-full/plugins/mp2.pl @@ -4,6 +4,8 @@ # MountPoints2 key parser # # Change history +# 20200921 - MITRE update +# 20200526 - updated date output format # 20120330 - updated to include parsing of UUID v1 GUIDs to get unique # MAC addresses # 20091116 - updated output/sorting; added getting @@ -12,8 +14,9 @@ # # References # http://support.microsoft.com/kb/932463 +# https://attack.mitre.org/techniques/T1021/002/ # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey #----------------------------------------------------------- package mp2; @@ -23,8 +26,10 @@ package mp2; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120330); + output => "report", + MITRE => "T1021\.002", + category => "user activity", + version => 20200921); sub getConfig{return %config} sub getShortDescr { @@ -41,8 +46,10 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching mp2 v.".$VERSION); - ::rptMsg("mp2 v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("mp2 v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my %drives; my %volumes; my %remote; @@ -56,7 +63,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("MountPoints2"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @subkeys = $key->get_list_of_subkeys(); if (scalar @subkeys > 0) { foreach my $s (@subkeys) { @@ -90,7 +97,7 @@ sub pluginmain { ::rptMsg(""); ::rptMsg("Remote Drives:"); foreach my $t (reverse sort {$a <=> $b} keys %remote) { - ::rptMsg(gmtime($t)." (UTC)"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $item (@{$remote{$t}}) { ::rptMsg(" $item"); } @@ -99,7 +106,7 @@ sub pluginmain { ::rptMsg(""); ::rptMsg("Volumes:"); foreach my $t (reverse sort {$a <=> $b} keys %volumes) { - ::rptMsg(gmtime($t)." (UTC)"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $item (@{$volumes{$t}}) { ::rptMsg(" $item"); } @@ -108,7 +115,7 @@ sub pluginmain { ::rptMsg("Drives:"); foreach my $t (reverse sort {$a <=> $b} keys %drives) { my $d = join(',',(@{$drives{$t}})); - ::rptMsg(gmtime($t)." (UTC) - ".$d); + ::rptMsg(::format8601Date($t)."Z - ".$d); } ::rptMsg(""); ::rptMsg("Unique MAC Addresses:"); diff --git a/thirdparty/rr-full/plugins/mp3.pl b/thirdparty/rr-full/plugins/mp2_tln.pl similarity index 80% rename from thirdparty/rr-full/plugins/mp3.pl rename to thirdparty/rr-full/plugins/mp2_tln.pl index 479ada6e5a3..ed335829af5 100644 --- a/thirdparty/rr-full/plugins/mp3.pl +++ b/thirdparty/rr-full/plugins/mp2_tln.pl @@ -1,9 +1,11 @@ #----------------------------------------------------------- -# mp3.pl +# mp2_tln.pl # Plugin for Registry Ripper, # MountPoints2 key parser # # Change history +# 20200921 - MITRE update +# 20200525 - renamed from mp3.pl # 20120330 - updated to include parsing of UUID v1 GUIDs to get unique # MAC addresses # 20091116 - updated output/sorting; added getting @@ -13,18 +15,20 @@ # References # http://support.microsoft.com/kb/932463 # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey #----------------------------------------------------------- -package mp3; +package mp2_tln; use strict; my %config = (hive => "NTUSER\.DAT", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120330); + output => "tln", + MITRE => "T1021\.002", + category => "user activity", + version => 20200921); sub getConfig{return %config} sub getShortDescr { @@ -40,9 +44,9 @@ sub getShortDescr { sub pluginmain { my $class = shift; my $ntuser = shift; - ::logMsg("Launching mp3 v.".$VERSION); - ::rptMsg("mp3 v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner +# ::logMsg("Launching mp3 v.".$VERSION); +# ::rptMsg("mp3 v.".$VERSION); # banner +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner my %drives; my %volumes; my %remote; @@ -83,7 +87,7 @@ sub pluginmain { } foreach my $t (reverse sort {$a <=> $b} keys %volumes) { foreach my $id (@{$volumes{$t}}) { - ::rptMsg($t."|REG|Server|User|".$id." Volume MP2 key LastWrite"); + ::rptMsg($t."|REG|||".$id." Volume MP2 key LastWrite"); my $id2 = $id; $id =~ s/^{//; $id =~ s/}$//; @@ -92,10 +96,10 @@ sub pluginmain { my $l = hex(substr($id,0,8)); my $m = hex(substr($id,8,4)); my $h = hex(substr($id,12,4)) & 0x0fff; - $h = $m | $h << 16; + my $h = $m | $h << 16; my $t2 = (::getTime($l,$h) - 574819200); - ::rptMsg($t2."|REG|Server|User|".$id2." Vol GUID date"); + ::rptMsg($t2."|REG|||".$id2." Vol GUID date"); } } @@ -110,4 +114,4 @@ sub pluginmain { } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/mpmru.pl b/thirdparty/rr-full/plugins/mpmru.pl index 2244e27ed9a..ca78e5b0ea9 100644 --- a/thirdparty/rr-full/plugins/mpmru.pl +++ b/thirdparty/rr-full/plugins/mpmru.pl @@ -1,15 +1,17 @@ #----------------------------------------------------------- # mpmru.pl -# Plugin for Registry Ripper, NTUSER.DAT edition - gets the # Media Player RecentFileList values # # Change history -# +# 20200921 - MITRE update +# 20200517 - updated date output format +# 20080324 - created # # References # # -# copyright 2008 H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package mpmru; use strict; @@ -18,8 +20,10 @@ package mpmru; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080324); + MITRE => "", + output => "report", + category => "user activity", + version => 20200921); sub getConfig{return %config} sub getShortDescr { @@ -46,7 +50,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("Media Player - RecentFileList"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { my %files; @@ -65,12 +69,10 @@ sub pluginmain { } else { ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } diff --git a/thirdparty/rr-full/plugins/mpnotify.pl b/thirdparty/rr-full/plugins/mpnotify.pl new file mode 100644 index 00000000000..c2225250a60 --- /dev/null +++ b/thirdparty/rr-full/plugins/mpnotify.pl @@ -0,0 +1,76 @@ +#----------------------------------------------------------- +# mpnotify.pl +# Get WinLogon mpnotify setting +# +# Change history: +# 20230702 - added reference +# 20211025 - created +# +# References: +# https://twitter.com/0gtweet/status/1372550832416260103 +# https://persistence-info.github.io/Data/mpnotify.html +# +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey +#----------------------------------------------------------- +package mpnotify; +use strict; + +my %config = (hive => "software", + category => "persistence", + MITRE => "T1546", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20230702); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get WinLogon mpnotify setting"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching mpnotify v.".$VERSION); + ::rptMsg("mpnotify v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my @paths = ("Microsoft\\Windows NT\\CurrentVersion\\WinLogon"); + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + eval { + my $a = $key->get_value("mpnotify")->get_data(); + ::rptMsg("mpnotify value : ".$a); + }; + } + else { +# ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: The mpnotify value registers an RPC endpoint, WinLogon binds to it and passes the password. The EXE will"); + ::rptMsg("launch and exit after approx. 30 sec."); + ::rptMsg(""); + ::rptMsg("Ref: https://persistence-info.github.io/Data/mpnotify.html"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/mrt.pl b/thirdparty/rr-full/plugins/mrt.pl deleted file mode 100644 index a5f8e227911..00000000000 --- a/thirdparty/rr-full/plugins/mrt.pl +++ /dev/null @@ -1,74 +0,0 @@ -#----------------------------------------------------------- -# mrt.pl -# -# Per http://support.microsoft.com/kb/891716/, whenever MRT is run, a new -# GUID is written to the Version value. Check the KB article to compare -# GUIDs against the last time the tool was run. Also be sure to check the -# MRT logs in %WinDir%\Debug (mrt.log) -# -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package mrt; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20080804); - -sub getConfig{return %config} - -sub getShortDescr { - return "Check to see if Malicious Software Removal Tool has been run"; -} -sub getDescr{} -sub getRefs {"Deployment of the Microsoft Windows Malicious Software Removal Tool" => - "http://support.microsoft.com/kb/891716/", - "The Microsoft Windows Malicious Software Removal Tool" => "http://support.microsoft.com/?kbid=890830"} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching mrt v.".$VERSION); - ::rptMsg("mrt v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - - my $key_path = "Microsoft\\RemovalTools\\MRT"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Key Path: ".$key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $version; - eval { - $version = $key->get_value("Version")->get_data(); - }; - if ($@) { - ::rptMsg("Error getting Version information: ".$@); - - } - else { - ::rptMsg("Version: ".$version); - ::rptMsg(""); - ::rptMsg("Analysis Tip: Go to http://support.microsoft.com/kb/891716/ to see when MRT"); - ::rptMsg("was last run. According to the KB article, each time MRT is run, a new GUID"); - ::rptMsg("is written to the Version value."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/msedge_win10.pl b/thirdparty/rr-full/plugins/msedge_win10.pl deleted file mode 100644 index 223e1b892c3..00000000000 --- a/thirdparty/rr-full/plugins/msedge_win10.pl +++ /dev/null @@ -1,147 +0,0 @@ -#----------------------------------------------------------- -# msedge_win10.pl -# Plugin for RegRipper -# -# Parses Microsoft Edge (Windows App) key: -# -USRCLASS.DAT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage\microsoft.microsoftedge_8wekyb3d8bbwe\MicrosoftEdge\TypedURLs -# -USRCLASS.DAT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage\microsoft.microsoftedge_8wekyb3d8bbwe\MicrosoftEdge\TypedURLsTime -# -USRCLASS.DAT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage\microsoft.microsoftedge_8wekyb3d8bbwe\MicrosoftEdge\TypedURLsVisitCount -# -# On a live machine, the key path is found under HKEY_CLASSES_ROOT -# -# The script code is based on: -# - adoberdr.pl/landesk.pl by H. Carvey -# - iexplore.pl by E. Rye esten@ryezone.net -# http://www.ryezone.net/regripper-and-internet-explorer-1 -# -# Change history -# 20180610 - First release -# -# References -# http://digitalforensicsurvivalpodcast.com/2017/04/11/dfsp-060-browsing-on-the-edge/ -# https://forensenellanebbia.blogspot.com/2018/06/usrclassdat-stores-more-history-than.html -# -# copyright 2018 Gabriele Zambelli | Twitter: @gazambelli -#----------------------------------------------------------- - -package msedge_win10; -use strict; - -my %config = (hive => "USRCLASS\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20180610); - -sub getShortDescr { return "Get values from the user's Microsoft Edge Windows App key"; } - -sub getDescr {} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my (@ts,$d); - -my @arr; - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::rptMsg("msedge_win10 v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - # First, let's find out is Microsoft Edge was used to type any URL - my $version; - my $tag = 0; - my @globalitems = (); - my $key_path = "Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge\\TypedURLsVisitCount"; - my $key = $root_key->get_subkey($key_path); - if (defined($key)) { - $tag = 1; - } - else { - ::rptMsg($key_path." not found."); - } - - #TypedURLs - if ($tag) { - my $key_path = "Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge\\TypedURLs"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my %vals = getKeyValues($key); - foreach my $v (keys %vals) { - push @arr,($v." (TypedURLs) -> ".$vals{$v}); - } - } - else { - ::rptMsg(""); - ::rptMsg($key_path." has no subkeys."); - } - } - - #TypedURLsTime - if ($tag) { - my $key_path = "Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge\\TypedURLsTime"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my %vals = getKeyValues($key); - foreach my $v (keys %vals) { - @ts = unpack("VV",$key->get_value($v)->get_data()); - push @arr, ($v." (TypedURLsTime) -> ".gmtime(::getTime($ts[0],$ts[1]))." (UTC)"); - } - } - else { - ::rptMsg(""); - ::rptMsg($key_path." has no subkeys."); - } - } - - #TypedURLsVisitCount - if ($tag) { - my $key_path = "Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge\\TypedURLsVisitCount"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my %vals = getKeyValues($key); - foreach my $v (keys %vals) { - push @arr, ($v." (TypedURLsVisitCount) -> ".$vals{$v}."\r\n"); - } - } - else { - ::rptMsg(""); - ::rptMsg($key_path." has no subkeys."); - } - } - - if (scalar(@arr) > 0) { - #sort items in the array - ::rptMsg("|-- \\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\microsoft.microsoftedge_8wekyb3d8bbwe"); - ::rptMsg("|----- \\MicrosoftEdge\\TypedURLs"); - ::rptMsg("|----- \\MicrosoftEdge\\TypedURLsTime"); - ::rptMsg("|----- \\MicrosoftEdge\\TypedURLsVisitCount"); - ::rptMsg(""); - foreach my $i (sort @arr){ - ::rptMsg($i); - } - } -} - -sub getKeyValues { - my $key = shift; - my %vals; - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - } - return %vals; -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/msis.pl b/thirdparty/rr-full/plugins/msis.pl index 1be5db76175..83e6440cc7e 100644 --- a/thirdparty/rr-full/plugins/msis.pl +++ b/thirdparty/rr-full/plugins/msis.pl @@ -3,23 +3,27 @@ # Plugin to determine the MSI packages installed on the system # # Change history: +# 20200921 - MITRE update +# 20200517 - updated date output format # 20090911 - created # # References: # http://support.microsoft.com/kb/290134 # http://support.microsoft.com/kb/931401 # -# copyright 2009 H. Carvey, keydet89@yahoo.com +# copyright 2020 H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package msis; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20090911); + output => "report", + version => 20200921); sub getConfig{return %config} @@ -49,7 +53,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg(""); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @subkeys = $key->get_list_of_subkeys(); @@ -79,7 +83,7 @@ sub pluginmain { foreach my $t (reverse sort {$a <=> $b} keys %msi) { - ::rptMsg(gmtime($t)." (UTC)"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $item (@{$msi{$t}}) { ::rptMsg(" ".$item); } @@ -92,7 +96,6 @@ sub pluginmain { } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/msoffice.pl b/thirdparty/rr-full/plugins/msoffice.pl new file mode 100644 index 00000000000..e583bd11585 --- /dev/null +++ b/thirdparty/rr-full/plugins/msoffice.pl @@ -0,0 +1,341 @@ +#----------------------------------------------------------- +# msoffice.pl +# List Office documents for which the user explicitly opted to accept bypassing +# the default security settings for the application +# +# Change history +# 20210710 - Added AccessVBOM check +# 20210201 - Added AMSI integration check for macro-enabled documents +# 20200730 - MITRE ATT&CK updates +# 20200518 - updated date output format, minor updates +# 20200316 - minor update +# 20200102 - added check for UseRWHLinkNavigation value +# 20190902 - added check for OLE PackagerPrompt & AdditionalActionsDLL values +# 20190822 - created +# +# References +# 20190626 updates +# https://decentsecurity.com/block-office-macros +# https://gist.github.com/PSJoshi/749cf1733217d8791cf956574a3583a2 +# +# http://az4n6.blogspot.com/2016/02/more-on-trust-records-macros-and.html +# ForensicArtifacts.com posting by Andrew Case: +# http://forensicartifacts.com/2012/07/ntuser-trust-records/ +# http://archive.hack.lu/2010/Filiol-Office-Documents-New-Weapons-of-Cyberwarfare-slides.pdf +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package msoffice; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "user activity", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20210710); + +sub getConfig{return %config} +sub getShortDescr { + return "Get user's MSOffice content"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $office_version; +my %vba = (1 => "Enable all macros", + 2 => "Disable all macros w/ notification", + 3 => "Disalbe all macros except dig. signed macros", + 4 => "Disalbe all macros w/o notification"); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching msoffice v.".$VERSION); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg("msoffice v.".$VERSION); + ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + +# Check Identities + eval { + if (my $id = $key->get_subkey($office_version."\\Common\\Identity\\Identities")) { + my @subkeys = $id->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + ::rptMsg("Office Identities"); + foreach my $s (@subkeys) { + my $name = $s->get_value("FriendlyName")->get_data(); + my $email = $s->get_value("EmailAddress")->get_data(); + ::rptMsg($name." (".$email.")") if ($name ne ""); + } + } + } + ::rptMsg(""); + }; + +# Added 20190902 +# check for AdditionalActionsDlls +# https://attack.mitre.org/techniques/T1546/ + eval { + if (my $id = $key->get_subkey($office_version."\\Common")) { + my $aa = $id->get_value("AdditionalActionsDLL")->get_data(); + ::rptMsg("AdditionalActionsDLL value = ".$aa); + ::rptMsg(""); + } + }; + +# added 20200102 +# Check for AMSI support for Office 2016 +# https://malwaretips.com/threads/office-365-and-amsi-support-for-vba-macros.87281/ +# https://admx.help/?Category=Office2016&Policy=office16.Office.Microsoft.Policies.Windows::L_MacroRuntimeScanScope + eval { + if (my $id = $key->get_subkey($office_version."\\Common\\Security")) { + my $lw = $id->get_timestamp(); + my $rw = $id->get_value("MacroRuntimeScanScope")->get_data(); + ::rptMsg("Software\\Microsoft\\Office\\".$office_version."\\Common\\Security"); + ::rptMsg("LastWrite time: ".::format8601Date($lw)."Z"); + ::rptMsg("MacroRuntimeScanScope value = ".$rw); + ::rptMsg(""); + ::rptMsg("0 - AMSI integration disabled for all macro-enabled documents"); + ::rptMsg("1 - AMSI integration enabled only for low-trust documents"); + ::rptMsg("2 - AMSI integration enabled for all documents"); + } + }; + +# added 20210201 +# Check for UseRWHlinkNavigation value +# https://support.microsoft.com/en-us/help/4013793/specified-message-identity-is-invalid-error-when-you-open-delivery-rep +# https://attack.mitre.org/techniques/T1566/002/ + eval { + if (my $id = $key->get_subkey($office_version."\\Common\\Internet")) { + my $lw = $id->get_timestamp(); + my $rw = $id->get_value("UseRWHlinkNavigation")->get_data(); + ::rptMsg("Software\\Microsoft\\Office\\".$office_version."\\Common\\Internet"); + ::rptMsg("LastWrite time: ".::format8601Date($lw)."Z"); + ::rptMsg("UseRWHlinkNavigation value = ".$rw); + ::rptMsg("MITRE ATT&CK subtechnique T1566\.002 may apply here"); + ::rptMsg(""); + } + }; + + +# Now that we have the most recent version of Office installed, let's +# start looking at the various subkeys + my @apps = ("Word","PowerPoint","Excel","Access"); + + foreach my $app (@apps) { +# Check for DontUpdateLinks value + eval { + if (my $opt = $key->get_subkey($office_version."\\".$app."\\Options")) { + my $upd = $opt->get_value("DontUpdateLinks")->get_data(); + ::rptMsg("DontUpdateLinks value: ".$upd); + ::rptMsg(""); + } + }; +# Check values under "Security" key + eval { + if (my $sec = $key->get_subkey($office_version."\\".$app."\\Security")) { + my $vb = $sec->get_value("VBAWarnings")->get_data(); + ::rptMsg("VBAWarnings value: ".$vba{$vb}); + ::rptMsg(""); + } + }; + + eval { + if (my $sec = $key->get_subkey($office_version."\\".$app."\\Security")) { + my $b = $sec->get_value("blockcontentexecutionfrominternet")->get_data(); + ::rptMsg("blockcontentexecutionfrominternet value: ".$b); + } + }; + +# Added 20190902 +# https://www.microsoft.com/security/blog/2016/06/14/wheres-the-macro-malware-author-are-now-using-ole-embedding-to-deliver-malicious-files/ +# https://twitter.com/enigma0x3/status/889858819232337922 + eval { + if (my $sec = $key->get_subkey($office_version."\\".$app."\\Security")) { + my $pp = $sec->get_value("PackagerPrompt")->get_data(); + ::rptMsg("PackagerPrompt value: ".$b); + ::rptMsg("If PackagerPrompt value = 2, OLE is disabled."); + } + }; + +# Added 20210710 - AccessVBOM check +# https://www.secpod.com/blog/ms-office-default-function-bring-in-self-replicating-malware/ +# https://www.stigviewer.com/stig/microsoft_powerpoint_2007/2014-04-03/finding/V-17522 +# https://www.mcafee.com/blogs/other-blogs/mcafee-labs/zloader-with-a-new-infection-technique/ + eval { + if (my $sec = $key->get_subkey($office_version."\\".$app."\\Security")) { + my $pp = $sec->get_value("AccessVBOM")->get_data(); + ::rptMsg("AccessVBOM value: ".$b); + ::rptMsg("If AccessVBOM value = 0, programmatic access to VBA projects is disabled."); + ::rptMsg("This is the desired setting."); + } + }; +# TrustRecords and Trusted Locations +# TrustRecords may provide insight into User Execution:Malicious File +# https://attack.mitre.org/techniques/T1204/002/ + eval { + if (my $trs = $key->get_subkey($office_version."\\".$app."\\Security\\Trusted Documents\\TrustRecords")) { + my @vals = $trs->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg($app." - TrustRecords"); + foreach my $v (@vals) { + my $name = $v->get_name(); +# ::rptMsg($name); + my $data = $v->get_data(); + my ($t0,$t1) = (unpack("VV",substr($data,0,8))); + my $t = ::getTime($t0,$t1); + my $out_str = ::format8601Date($t)."Z: ".$v->get_name(); + my $e = unpack("V",substr($data, length($data) - 4, 4)); + $out_str .= " **[T1204\.002] Enable Content button clicked." if ($e == 2147483647); + ::rptMsg($out_str); + } + } + } + ::rptMsg(""); + }; + +# eval { +# if (my $tl = $key->get_subkey($office_version."\\".$app."\\Security\\Trusted Locations")) { +# my @subkeys = $tl->get_list_of_subkeys(); +# if (scalar @subkeys > 0) { +# ::rptMsg($app." - Trusted Locations"); +# foreach my $s (@subkeys) { +# ::rptMsg($s->get_value("Path")->get_data()); +# } +# } +# } +# }; +# File MRUs + eval { + if (my $fm = $key->get_subkey($office_version."\\".$app."\\File MRU")) { + my @vals = $fm->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg($app." - File MRU"); + foreach my $v (@vals) { + my $name = $v->get_name(); + next unless ($v->get_name() =~ m/^Item/); + my ($t,$file) = processMRUValue($v->get_data()); + ::rptMsg(::format8601Date($t)."Z: ".$file); + } + ::rptMsg(""); + } + } + }; + + eval { + if (my $um = $key->get_subkey($office_version."\\".$app."\\User MRU")) { + my @subkeys = $um->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my @vals = $s->get_subkey("File MRU")->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg($app."\\User MRU\\".$s->get_name()." - File MRU"); + foreach my $v (@vals) { + next unless ($v->get_name() =~ m/^Item/); + my ($t,$file) = processMRUValue($v->get_data()); + ::rptMsg(::format8601Date($t)."Z: ".$file); + } + ::rptMsg(""); + } + } + } + } + }; + +# Place MRU + eval { + if (my $fm = $key->get_subkey($office_version."\\".$app."\\Place MRU")) { + my @vals = $fm->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg($app." - Place MRU"); + foreach my $v (@vals) { + my $name = $v->get_name(); + next unless ($name =~ m/^Item/); + my ($t,$file) = processMRUValue($v->get_data()); + ::rptMsg(::format8601Date($t)."Z: ".$file); + } + ::rptMsg(""); + } + } + }; + + eval { + if (my $um = $key->get_subkey($office_version."\\".$app."\\User MRU")) { + my @subkeys = $um->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my @vals = $s->get_subkey("Place MRU")->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg($app."\\User MRU\\".$s->get_name()." - Place MRU"); + foreach my $v (@vals) { + next unless ($v->get_name() =~ m/^Item/); + my ($t,$file) = processMRUValue($v->get_data()); + ::rptMsg(::format8601Date($t)."Z: ".$file); + } + ::rptMsg(""); + } + } + } + } + }; + } + +# Word Reading Locations +# It appears that the DateTime value may be recorded as local system time, with minute +# resolution (vs. sec, or micro-sec) + eval { + if (my $rl = $key->get_subkey($office_version."\\Word\\Reading Locations")) { + my @subkeys = $rl->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + ::rptMsg("Word - Reading Locations"); + foreach my $s (@subkeys) { + my $path = $s->get_value("File Path")->get_data(); + my $dt = $s->get_value("Datetime")->get_data(); + ::rptMsg(::format8601Date($s->get_timestamp())."Z: ".$path." (".$dt.")"); + } + ::rptMsg(""); + } + } + }; +} + + +sub processMRUValue { + my $str = shift; + my ($stuff,$file) = split(/\*/,$str); + my $t_str = (split(/\]\[/,$stuff))[1]; + $t_str =~ s/^T//; + my $t = ::getFileTimeStr($t_str); + return ($t,$file); +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/msoffice_tln.pl b/thirdparty/rr-full/plugins/msoffice_tln.pl new file mode 100644 index 00000000000..113edf2711e --- /dev/null +++ b/thirdparty/rr-full/plugins/msoffice_tln.pl @@ -0,0 +1,231 @@ +#----------------------------------------------------------- +# msoffice_tln.pl +# List Office documents for which the user explicitly opted to accept bypassing +# the default security settings for the application +# +# Change history +# 20200730 - MITRE ATT&CK Updates +# 20200518 - minor updates +# 20190823 - updated to include AuthHistory LastLoginTime value +# 20190822 - created +# +# References +# 20190626 updates +# https://decentsecurity.com/block-office-macros +# https://gist.github.com/PSJoshi/749cf1733217d8791cf956574a3583a2 +# +# http://az4n6.blogspot.com/2016/02/more-on-trust-records-macros-and.html +# ForensicArtifacts.com posting by Andrew Case: +# http://forensicartifacts.com/2012/07/ntuser-trust-records/ +# http://archive.hack.lu/2010/Filiol-Office-Documents-New-Weapons-of-Cyberwarfare-slides.pdf +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package msoffice_tln; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "user activity", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "tln", + version => 20200730); + +sub getConfig{return %config} +sub getShortDescr { + return "Get user's MSOffice content"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $office_version; +my %vba = (1 => "Enable all macros", + 2 => "Disable all macros w/ notification", + 3 => "Disalbe all macros except dig. signed macros", + 4 => "Disalbe all macros w/o notification"); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; +# ::logMsg("Launching msoffice_tln v.".$VERSION); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + +# ::rptMsg("msoffice v.".$VERSION); +# ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + +# See if AuthHistory key includes a LastLoginTime value + eval { + if (my $id = $key->get_subkey($office_version."\\Common\\Identity\\Identities")) { + my @subkeys = $id->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_value("SigninName")->get_data(); + my ($t0,$t1) = unpack("VV",$s->get_subkey("AuthHistory")->get_value("LastLoginTime")->get_data()); + my $l = ::getTime($t0,$t1); + ::rptMsg($l."|REG|||".$name." MSOffice LastLoginTime"); + } + } + } + }; + +# Now that we have the most recent version of Office installed, let's +# start looking at the various subkeys + my @apps = ("Word","PowerPoint","Excel","Access"); + + foreach my $app (@apps) { +# TrustRecords and Trusted Locations +# TrustRecords may provide insight into User Execution:Malicious File +# https://attack.mitre.org/techniques/T1204/002/ + eval { + if (my $trs = $key->get_subkey($office_version."\\".$app."\\Security\\Trusted Documents\\TrustRecords")) { + my @vals = $trs->get_list_of_values(); + if (scalar @vals > 0) { +# ::rptMsg($app." - TrustRecords"); + foreach my $v (@vals) { + my $name = $v->get_name(); + my $data = $v->get_data(); + my ($t0,$t1) = (unpack("VV",substr($data,0,8))); + my $t = ::getTime($t0,$t1); +# my $out_str = gmtime($t)." UTC: ".$v->get_name(); + my $out_str = $t."|REG|||".$app." TrustRecords - ".$v->get_name(); + my $e = unpack("V",substr($data, length($data) - 4, 4)); + $out_str .= " **[T1204\.002] Enable Content button clicked." if ($e == 2147483647); + ::rptMsg($out_str); + } + } + } +# ::rptMsg(""); + }; + + +# File MRUs + eval { + if (my $fm = $key->get_subkey($office_version."\\".$app."\\File MRU")) { + my @vals = $fm->get_list_of_values(); + if (scalar @vals > 0) { +# ::rptMsg($app." - File MRU"); + foreach my $v (@vals) { + next unless ($v->get_name() =~ m/^Item/); + my ($t,$file) = processMRUValue($v->get_data()); +# ::rptMsg(gmtime($t)." UTC: ".$file); + ::rptMsg($t."|REG|||".$app." File MRU - ".$file); + } + } + } + }; + + eval { + if (my $um = $key->get_subkey($office_version."\\".$app."\\User MRU")) { + my @subkeys = $um->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my @vals = $s->get_subkey("File MRU")->get_list_of_values(); + if (scalar @vals > 0) { +# ::rptMsg($app." - File MRU"); + foreach my $v (@vals) { + next unless ($v->get_name() =~ m/^Item/); + my ($t,$file) = processMRUValue($v->get_data()); +# ::rptMsg(gmtime($t)." UTC: ".$file); + ::rptMsg($t."|REG|||".$app."\\User MRU\\".$s->get_name()." - File MRU - ".$file); + } + } + } + } + } + }; + +# Place MRU + eval { + if (my $fm = $key->get_subkey($office_version."\\".$app."\\Place MRU")) { + my @vals = $fm->get_list_of_values(); + if (scalar @vals > 0) { +# ::rptMsg($app." - Place MRU"); + foreach my $v (@vals) { + my $name = $v->get_name(); + next unless ($v->get_name() =~ m/^Item/); + my ($t,$file) = processMRUValue($v->get_data()); +# ::rptMsg(gmtime($t)." UTC: ".$file); + ::rptMsg($t."|REG|||".$app." Place MRU - ".$file); + } + } + } + }; + + eval { + if (my $um = $key->get_subkey($office_version."\\".$app."\\User MRU")) { + my @subkeys = $um->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my @vals = $s->get_subkey("Place MRU")->get_list_of_values(); + if (scalar @vals > 0) { +# ::rptMsg($app." - Place MRU"); + foreach my $v (@vals) { + next unless ($v->get_name() =~ m/^Item/); + my ($t,$file) = processMRUValue($v->get_data()); +# ::rptMsg(gmtime($t)." UTC: ".$file); + ::rptMsg($t."|REG|||".$app."\\User MRU\\".$s->get_name()." - Place MRU - ".$file); + } + } + } + } + } + }; + } + +# Word Reading Locations + eval { + if (my $rl = $key->get_subkey($office_version."\\Word\\Reading Locations")) { + my @subkeys = $rl->get_list_of_subkeys(); + if (scalar @subkeys > 0) { +# ::rptMsg("Word - Reading Locations"); + foreach my $s (@subkeys) { + my $path = $s->get_value("File Path")->get_data(); + my $dt = $s->get_value("Datetime")->get_data(); +# ::rptMsg(gmtime($s->get_timestamp())." UTC"); +# ::rptMsg($path." (".$dt.")"); + ::rptMsg($s->get_timestamp()."|REG|||MSWord Reading Locations - $path (".$dt.")"); + } + } + } + }; +} + + +sub processMRUValue { + my $str = shift; + my ($stuff,$file) = split(/\*/,$str); + my $t_str = (split(/\]\[/,$stuff))[1]; + $t_str =~ s/^T//; + my $t = ::getFileTimeStr($t_str); + return ($t,$file); +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/mspaper.pl b/thirdparty/rr-full/plugins/mspaper.pl deleted file mode 100644 index 325fc6474c7..00000000000 --- a/thirdparty/rr-full/plugins/mspaper.pl +++ /dev/null @@ -1,102 +0,0 @@ -#----------------------------------------------------------- -# mspaper.pl -# Plugin for Registry Ripper, NTUSER.DAT edition - gets the -# MSPaper Recent File List values -# -# Change history -# -# -# References -# -# -# copyright 2008 H. Carvey -#----------------------------------------------------------- -package mspaper; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080324); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets images listed in user's MSPaper key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching mspaper v.".$VERSION); - ::rptMsg("mspaper v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $tick = 0; - my $key_path = 'Software\\Microsoft'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my @subkeys = $key->get_list_of_subkeys(); - - if (scalar @subkeys > 0) { - foreach my $sk (@subkeys) { - if ($sk->get_name() =~ m/^mspaper/i) { - $tick = 1; - my $nkey = $sk->get_name()."\\Recent File List"; - my $msp; - if ($msp = $key->get_subkey($nkey)) { - ::rptMsg("MSPaper - Recent File List"); - ::rptMsg($key_path."\\".$nkey); - ::rptMsg("LastWrite Time ".gmtime($msp->get_timestamp())." (UTC)"); - my @vals = $msp->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - my $data = $v->get_data(); - my $tag = (split(/File/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg(" ".$val." -> ".$data); - } - } - else { - ::rptMsg($key_path."\\".$nkey." has no values."); - } - } - else { - ::rptMsg($key_path."\\".$nkey." not found."); - ::logMsg("Error: ".$key_path."\\".$nkey." not found."); - } - } - } - if ($tick == 0) { - ::rptMsg("SOFTWARE\\Microsoft\\MSPaper* not found."); - ::logMsg("SOFTWARE\\Microsoft\\MSPaper* not found."); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/muicache.pl b/thirdparty/rr-full/plugins/muicache.pl index 5dd21847bc8..4b433c22586 100644 --- a/thirdparty/rr-full/plugins/muicache.pl +++ b/thirdparty/rr-full/plugins/muicache.pl @@ -4,12 +4,19 @@ # Plugin for Registry Ripper, NTUSER.DAT edition - gets the # MUICache values # +# References +# https://www.youtube.com/watch?v=ea2nvxN878s&t=2s +# https://www.magnetforensics.com/blog/forensic-analysis-of-muicache-files-in-windows/ +# # Change history +# 20221121 - reference update, added check for hive +# 20200922 - MITRE update +# 20200525 - updated date output format, removed alertMsg() functionality # 20130425 - added alertMsg() functionality # 20120522 - updated to collect info from Win7 USRCLASS.DAT # # -# copyright 2012 Quantum Research Analytics, LLC +# copyright 2022 Quantum Research Analytics, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package muicache; @@ -19,8 +26,10 @@ package muicache; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20130425); + MITRE => "T1059", + category => "program execution", + output => "report", + version => 20221121); sub getConfig{return %config} sub getShortDescr { @@ -35,49 +44,44 @@ sub getShortDescr { sub pluginmain { my $class = shift; - my $ntuser = shift; + my $hive = shift; ::logMsg("Launching muicache v.".$VERSION); ::rptMsg("muicache v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($ntuser); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - my $key_path = 'Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache'; + my $key_path = (); + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my @paths = (); + if ($hive_guess eq "usrclass") { + $key_path = 'Local Settings\\Software\\Microsoft\\Windows\\Shell\\MUICache'; + } + elsif ($hive_guess eq "ntuser") { + $key_path = 'Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache'; + } + else {} + my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - ::alertMsg("ALERT: muicache: ".$key_path." ".$name." has \"Temp\" in path\.") if (grep(/[Tt]emp/,$name)); - next if ($name =~ m/^@/ || $name eq "LangID"); - my $data = $v->get_data(); - ::rptMsg(" ".$name." (".$data.")"); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - ::rptMsg(""); - } -# Added for access to USRCLASS.DAT - $key_path = 'Local Settings\\Software\\Microsoft\\Windows\\Shell\\MUICache'; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { foreach my $v (@vals) { my $name = $v->get_name(); - ::alertMsg("ALERT: muicache: ".$key_path." ".$name." has \"Temp\" in path\.") if (grep(/[Tt]emp/,$name)); next if ($name =~ m/^@/ || $name eq "LangID"); my $data = $v->get_data(); - ::rptMsg($name." (".$data.")"); + ::rptMsg(sprintf "%-80s %-30s",$name,$data); } } else { @@ -86,7 +90,14 @@ sub pluginmain { } else { ::rptMsg($key_path." not found."); - } + } + + ::rptMsg(""); + ::rptMsg("Analysis Tip: MUICache holds information from apps run by the user, incorporating metadata from the file's"); + ::rptMsg("\.rsrc section, or file version information. This artifact does NOT include time stamps."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.magnetforensics.com/blog/forensic-analysis-of-muicache-files-in-windows/"); + ::rptMsg("Ref: https://www.youtube.com/watch?v=ea2nvxN878s&t=2s"); } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/muicache_tln.pl b/thirdparty/rr-full/plugins/muicache_tln.pl deleted file mode 100644 index 2c3c4b60345..00000000000 --- a/thirdparty/rr-full/plugins/muicache_tln.pl +++ /dev/null @@ -1,91 +0,0 @@ -#! c:\perl\bin\perl.exe -#----------------------------------------------------------- -# muicache_tln.pl -# Plugin for Registry Ripper, NTUSER.DAT edition - gets the -# MUICache values -# -# Change history -# 20130425 - added alertMsg() functionality -# 20120522 - updated to collect info from Win7 USRCLASS.DAT -# -# -# copyright 2013 Quantum Research Analytics, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package muicache_tln; -use strict; - -my %config = (hive => "NTUSER\.DAT,USRCLASS\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20130425); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets EXEs from user's MUICache key (TLN)"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching muicache_tln v.".$VERSION); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - my $key_path = 'Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my $lw = $key->get_timestamp(); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - next if ($name =~ m/^@/ || $name eq "LangID"); - my $data = $v->get_data(); - ::alertMsg($lw."|ALERT|||HKCU\\".$key_path." ".$name." has \"Temp\" in path: ".$data) if (grep(/[Tt]emp/,$name)); -# ::rptMsg(" ".$name." (".$data.")"); - } - } - else { -# ::rptMsg($key_path." has no values."); - } - } - else { -# ::rptMsg($key_path." not found."); -# ::rptMsg(""); - } -# Added for access to USRCLASS.DAT - $key_path = 'Local Settings\\Software\\Microsoft\\Windows\\Shell\\MUICache'; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); -# ::rptMsg(""); - my $lw = $key->get_timestamp(); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - next if ($name =~ m/^@/ || $name eq "LangID"); - my $data = $v->get_data(); - ::alertMsg($lw."|ALERT|||HKCU\\".$key_path." ".$name." has \"Temp\" in path: ".$data) if (grep(/[Tt]emp/,$name)); - } - } - else { -# ::rptMsg($key_path." has no values."); - } - } - else { -# ::rptMsg($key_path." not found."); - } - -} -1; diff --git a/thirdparty/rr-full/plugins/mzthunderbird.pl b/thirdparty/rr-full/plugins/mzthunderbird.pl deleted file mode 100644 index d3952c11827..00000000000 --- a/thirdparty/rr-full/plugins/mzthunderbird.pl +++ /dev/null @@ -1,82 +0,0 @@ -#----------------------------------------------------------- -# mzthunderbird.pl -# Gets Thunderbird profile data -# -# Change history -# 20180712 - created -# -# References -# https://www.thunderbird.net/en-US/ -# -# Author: M. Jones, mictjon@gmail.com -#----------------------------------------------------------- -package mzthunderbird; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20180712); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets Thunderbird profile data"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching mzthunderbird v.".$VERSION); - ::rptMsg("mzthunderbird v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Thunderbird Email Addresses"); - ::rptMsg($key_path); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())." (UTC)]"); - my ($app,$msgct,$ts); - - eval { - $app = $s->get_value("Application")->get_data(); - ::rptMsg(" Application: ".$app); - }; - - eval { - $msgct = $s->get_value("MessageCount")->get_data(); - ::rptMsg(" MessageCount: ".$msgct); - }; - - eval { - my ($t0,$t1) = unpack("VV",$s->get_value("TimeStamp")->get_data()); - my $t = ::getTime($t0,$t1); - ::rptMsg(" TimeStamp: ".gmtime($t)); - }; - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/nation.pl b/thirdparty/rr-full/plugins/nation.pl index 7c96eb8bcee..461ed4df2b2 100644 --- a/thirdparty/rr-full/plugins/nation.pl +++ b/thirdparty/rr-full/plugins/nation.pl @@ -3,20 +3,30 @@ # Region Information # Get Geo Nation information from the NTUSER.DAT hive file # +# History: +# 20200921 - MITRE update +# 20200517 - updated date output format +# 20091116 - created +# +# # Written By: # Fahad Alzaabi # falzaab@masonlive.gmu.edu # George Mason University,CFRS 763 +# +# updated: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package nation; use strict; -my %config = (hive => "ntuser.dat", - osmask => 22, +my %config = (hive => "ntuser\.dat", + category => "config", + MITRE => "", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20091116); + output => "report", + version => 20200921); sub getConfig{return %config} @@ -43,7 +53,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("Nation Information Check"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my $nation = $key->get_value("Nation")->get_data(); ::rptMsg("The Region value is : ".$nation); @@ -349,17 +359,10 @@ sub pluginmain { ::rptMsg("The Country Is: Bonaire Saint Eustatius and Saba") if ($nation == 161832258); ::rptMsg("For more information please visit the link below:"); ::rptMsg("https://msdn.microsoft.com/en-us/library/aa723531.aspx"); - - - } - else { ::rptMsg($key_path." not found."); } - - ::rptMsg(""); - } 1; diff --git a/thirdparty/rr-full/plugins/nero.pl b/thirdparty/rr-full/plugins/nero.pl deleted file mode 100644 index 4e0930d46d1..00000000000 --- a/thirdparty/rr-full/plugins/nero.pl +++ /dev/null @@ -1,76 +0,0 @@ -#----------------------------------------------------------- -# nero.pl -# **Very Beta! Based on one sample hive file only! -# -# Change history -# 20100218 - created -# -# References -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package nero; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100218); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of Ahead\\Nero Recent File List subkeys"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my @nerosubkeys = ("Cover Designer","FlmgPlg","Nero PhotoSnap", - "NSPluginMgr","PhotoEffects","XlmgPlg"); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - my %hist; - ::logMsg("Launching nero v.".$VERSION); - ::rptMsg("nero v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Ahead'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - foreach my $nsk (@nerosubkeys) { - eval { - my $nk; - if ($nk = $key->get_subkey($nsk."\\Recent File List")) { - my @vals = $nk->get_list_of_values(); - if (scalar @vals > 0) { - ::rptMsg($nsk."\\Recent File List"); - ::rptMsg("LastWrite Time ".gmtime($nk->get_timestamp())." (UTC)"); - foreach my $v (@vals) { - ::rptMsg(" ".$v->get_name()." -> ".$v->get_data()); - } - ::rptMsg(""); - } - else { - ::rptMsg($nsk."\\Recent File List has no values."); - } - } - }; - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/netassist.pl b/thirdparty/rr-full/plugins/netassist.pl deleted file mode 100644 index 985da5eb481..00000000000 --- a/thirdparty/rr-full/plugins/netassist.pl +++ /dev/null @@ -1,123 +0,0 @@ -#----------------------------------------------------------- -# netassist.pl -# Plugin to determine if a system is infected with the BHO "My.Freeze.com". -# This is a BHO specifically for firefox and is installed as an addon using a -# third party installer. This is usually done when a user installs a product -# and is installed without the user reading all the information on the install. -# It usually requires the user to uncheck a box but as most users do not read -# everything it is installed unknowingly. -# If you look under the "addons" in firefox you will see an addon called -# "Freeze.com Net Assistant for Firefox", but you can only enable or disable -# it from there. To uninstall it completely from #the system you must -# uninstall from the system "add/remove" program under the control panel. -# -# Change history -# 20110427 [mmo] % created -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# Script written by Mark Morgan -#----------------------------------------------------------- -# Require # -package netassist; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110427); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Check for Firefox Extensions."; -} -sub getRefs { - my %refs = (""); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - my @interesting_paths = ( - 'Software\\Mozilla\\Firefox\\Extensions', - 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\NetAssistant', - 'Software\\Microsoft\\Installer\\Products\\D4676621F4CF7AF46BB388D4351B86F0', - 'Software\\Microsoft\\Installer\\Products\\D4676621F4CF7AF46BB388D4351B86F0\\SourceList', - - ); - my @interesting_keys = ( - "Values", - "ValueViewOnly" - ); - - # Initialize # - ::logMsg("Launching netassist v.".$VERSION); - ::rptMsg("netassist v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - # Extract # possible registry paths - foreach my $key_path (@interesting_paths) { - - # If # WinVNC path exists # - my $key; - if ($key = $root_key->get_subkey($key_path)) { - - ::rptMsg("netassist"); - ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); - ::rptMsg(""); - my %keys; - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - ::rptMsg(sprintf "%-12s %-20s",$v->get_name(),$v->get_data()); - } - - # Return # all key names+values for interesting keys # - foreach my $var (@interesting_keys) { - if (exists $keys{$var}) { - my $hstring = unpack ("H*",$keys{$var}); - ::rptMsg($var." -> ".$hstring); - } - } - - # Return # obligatory new-line # - ::rptMsg(""); - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # WinVNC isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/netlogon.pl b/thirdparty/rr-full/plugins/netlogon.pl index d10c5977762..95253962e44 100644 --- a/thirdparty/rr-full/plugins/netlogon.pl +++ b/thirdparty/rr-full/plugins/netlogon.pl @@ -3,26 +3,31 @@ # # # History: +# 20200921 - MITRE update +# 20200724 - minor updates +# 20200515 - minor updates # 20190223 - created # # References: # https://support.microsoft.com/en-us/help/154501/how-to-disable-automatic-machine-account-password-changes +# http://malwarejake.blogspot.com/2015/11/kerberos-silver-tickets-unique-attacker.html +# https://attack.mitre.org/techniques/T1558/002/ # -# copyright 2019 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package netlogon; use strict; -my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "System Config", +my %config = (hive => "system", + hivemask => 4, + output => "report", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, - version => 20190223); + MITRE => "T1558\.002", + version => 20200921); sub getConfig{return %config} sub getShortDescr { @@ -63,7 +68,7 @@ sub pluginmain { my $key_path = $set."\\services\\NetLogon\\Parameters"; my $key; if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg("LastWrite Time: ".gmtime($key->get_timestamp())." Z"); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); @vals = $key->get_list_of_values(); if (scalar @vals > 0) { foreach my $v (@vals) { @@ -87,6 +92,8 @@ sub pluginmain { } ::rptMsg(""); } + ::rptMsg("Analysis Note: If \"DisablePasswordChange\" is set to 0x1, this may indicate a silver ticket attack\."); + ::rptMsg("Also, searching for this value across the enterprise can be useful in threat hunting."); } 1; diff --git a/thirdparty/rr-full/plugins/netsh.pl b/thirdparty/rr-full/plugins/netsh.pl index 46c5e0ed615..79db1a3bd01 100644 --- a/thirdparty/rr-full/plugins/netsh.pl +++ b/thirdparty/rr-full/plugins/netsh.pl @@ -1,33 +1,33 @@ #----------------------------------------------------------- -# netsh.pl -# -# -# References -# http://www.adaptforward.com/2016/09/using-netshell-to-execute-evil-dlls-and-persist-on-a-host/ -# https://attack.mitre.org/techniques/T1128/ -# https://htmlpreview.github.io/?https://github.com/MatthewDemaske/blogbackup/blob/master/netshell.html +# netsh.pl # -# Change history -# 20190316 - updated references -# 20160926 - created +# Change history: +# 20200830 - MITRE updates +# 20200515 - updated date output format +# 20190316 - created +# +# Ref: +# https://attack.mitre.org/techniques/T1546/007/ +# https://github.com/MatthewDemaske/blogbackup/blob/master/netshell.html # -# Copyright 2019 QAR, LLC +# copyright 2020 QAR,LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package netsh; use strict; my %config = (hive => "Software", - osmask => 22, + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20190316); + output => "report", + MITRE => "T1546\.007", + version => 20200813); sub getConfig{return %config} - sub getShortDescr { - return "Get list of DLLs launched by NetSH"; + return "Gets list of NetSH helper DLLs"; } sub getDescr{} sub getRefs {} @@ -35,21 +35,25 @@ sub getShortDescr { sub getVersion {return $config{version};} my $VERSION = getVersion(); -my (@ts,$d); sub pluginmain { my $class = shift; my $hive = shift; - ::logMsg("Launching netsh v.".$VERSION); + ::rptMsg("Launching netsh v.".$VERSION); + ::rptMsg("netsh v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $key_path = 'Microsoft\\Netsh'; + + ::rptMsg("NetSH"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\NetSh"; my $key; - if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar @vals > 0) { ::rptMsg(""); @@ -57,9 +61,15 @@ sub pluginmain { foreach my $v (@vals) { ::rptMsg(sprintf "%-15s %-25s",$v->get_name(),$v->get_data()); } + ::rptMsg(""); + } + else { + } } - + else { + + } + ::rptMsg("Analysis Tip: Look for recently added (via key LastWrite time) values\\DLLs, with unusual paths."); } - 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/netsvcs.pl b/thirdparty/rr-full/plugins/netsvcs.pl deleted file mode 100644 index 2cb7c9eebbe..00000000000 --- a/thirdparty/rr-full/plugins/netsvcs.pl +++ /dev/null @@ -1,171 +0,0 @@ -#----------------------------------------------------------- -# netsvcs.pl -# Plugin that takes contents of netsvcs value in SvcHost key (from -# Software hive) and compares that to specific Windows services in the -# System hive. -# -# Steps: -# 1. From the names in @list, convert the names to all lower case, and create -# the %netsvcs hash. -# 2. Parse the Services subkey names, looking for Parameters\ServiceDLL values; -# if found, lower case the service name and see if it exists in the %netsvcs -# hash. If it does, add the ServiceDLL value and the Parameters key LastWrite -# time to the %netsvcs hash. -# 3. Determine if the service has an entry beneath the Enum\Root subkeys, with -# a name that begins with "LEGACY_"; if so, add that information to the hash. -# -# History: -# 20130905 - created -# -# References: -# -# -# -# copyright 2013 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package netsvcs; -use strict; - -my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "malware", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20130905); - -sub getConfig{return %config} -sub getShortDescr { - return "Checks services for netsvcs entries"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -# Caveat: this list of services is nominal, from a Windows 7 system. It is not -# all inclusive, nor is it complete for XP/2003 -my @list = qw/AeLookupSvc CertPropSvc SCPolicySvc lanmanserver gpsvc IKEEXT - AudioSrv FastUserSwitchingCompatibility Ias Irmon Nla Ntmssvc - NWCWorkstation Nwsapagent Rasauto Rasman Remoteaccess SENS Sharedaccess - SRService Tapisrv Wmi WmdmPmSp TermService wuauserv BITS ShellHWDetection - LogonHours PCAudit helpsvc uploadmgr iphlpsvc seclogon AppInfo msiscsi MMCSS - winmgmt SessionEnv browser EapHost schedule hkmsvc wercplsupport ProfSvc - Themes BDESVC AppMgmt/; - -my %svcdll; -my %netsvcs; - -#Ref: http://support.microsoft.com/kb/103000 -my %start_type = (0x00 => "Boot", - 0x01 => "System", - 0x02 => "Auto", - 0x03 => "On-Demand", - 0x04 => "Disabled"); - -my %types = (0x01 => "Kernel Driver", - 0x02 => "File Sys Driver", - 0x04 => "Adapter args", - 0x10 => "Own Process", - 0x20 => "Share Process"); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching netsvcs v.".$VERSION); - ::rptMsg("netsvcs v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my ($current,$ccs); - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; -# Set up our hash - foreach my $l (@list) { - my $d = $l; - $l =~ tr/[A-Z]/[a-z]/; - $netsvcs{$l}{DisplayName} = $d; - } - - my $path = $ccs."\\Services"; - my $svc; - if ($svc = $root_key->get_subkey($path)) { - my @subkeys = $svc->get_list_of_subkeys(); - if (scalar (@subkeys) > 0) { - foreach my $s (@subkeys) { - - eval { - my $dll = $s->get_subkey("Parameters")->get_value("ServiceDLL")->get_data(); - my $start = $s->get_value("Start")->get_data(); - my $type = $s->get_value("Type")->get_data(); - my $name = $s->get_name(); - my $display = $name; - $name =~ tr/[A-Z]/[a-z]/; - if (exists $netsvcs{$name}) { -# Note: the entry in the SvcHost key netsvcs value may be spelled differently - $netsvcs{$name}{Svc_DisplayName} = $display; - $netsvcs{$name}{ServiceDLL} = $dll; - $netsvcs{$name}{Start} = $start_type{$start}; - $netsvcs{$name}{Type} = $types{$type}; - $netsvcs{$name}{ServiceDLL_LastWrite} = $s->get_subkey("Parameters")->get_timestamp(); - } - }; - } - } - } -# check for enum\Root\LEGACY_* keys - $path = $ccs."\\Enum\\Root"; - my $enum; - if ($enum = $root_key->get_subkey($path)) { - my @subkeys = $enum->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - my $name = $s->get_name(); - next unless ($name =~ m/^LEGACY_/); - my $short = $name; - $short =~ s/^LEGACY_//; - $short =~ tr/[A-Z]/[a-z]/; - - if (exists $netsvcs{$short}) { - $netsvcs{$short}{Legacy_LastWrite} = $s->get_timestamp(); -# Try this next step...it may not work - eval { - my $o = $s->get_subkey("0000")->get_timestamp(); - $netsvcs{$short}{Legacy_0000_LastWrite} = $o; - }; - } - } - } - } - - foreach my $n (keys %netsvcs) { - if (exists $netsvcs{$n}{ServiceDLL}) { -# Output: Parameters key LastWrite time, DisplayName, ServiceDLL, Svc Start, Svc Type - my $out = gmtime($netsvcs{$n}{ServiceDLL_LastWrite})." Z,".$netsvcs{$n}{Svc_DisplayName}.",".$netsvcs{$n}{ServiceDLL}. - ",".$netsvcs{$n}{Start}.",".$netsvcs{$n}{Type}; -# Check to see if there's a LEGACY_* entry for the service - if (exists $netsvcs{$n}{Legacy_LastWrite}) { - $out .= ",".gmtime($netsvcs{$n}{Legacy_LastWrite})." Z"; - } - - ::rptMsg($out); - } - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/network.pl b/thirdparty/rr-full/plugins/network.pl deleted file mode 100644 index 79562a27515..00000000000 --- a/thirdparty/rr-full/plugins/network.pl +++ /dev/null @@ -1,97 +0,0 @@ -#----------------------------------------------------------- -# network.pl -# Plugin for Registry Ripper; Get information on network -# interfaces from the System hive file - from the -# Control\Network GUID subkeys... -# -# Change history -# -# -# References -# -# -# copyright 2008 H. Carvey -#----------------------------------------------------------- -package network; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080324); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets info from System\\Control\\Network GUIDs"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my %nics; - my $ccs; - ::logMsg("Launching network v.".$VERSION); - ::rptMsg("network v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - my $nw_path = $ccs."\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"; - my $nw; - if ($nw = $root_key->get_subkey($nw_path)) { - ::rptMsg("Network key"); - ::rptMsg($nw_path); -# Get all of the subkey names - my @sk = $nw->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - my $name = $s->get_name(); - next if ($name eq "Descriptions"); - if (my $conn = $nw->get_subkey($name."\\Connection")) { - ::rptMsg("Interface ".$name); - ::rptMsg("LastWrite time ".gmtime($conn->get_timestamp())." (UTC)"); - my %conn_vals; - my @vals = $conn->get_list_of_values(); - map{$conn_vals{$_->get_name()} = $_->get_data()}@vals; - ::rptMsg("\tName = ".$conn_vals{Name}); - ::rptMsg("\tPnpInstanceID = ".$conn_vals{PnpInstanceID}); - ::rptMsg("\tMediaSubType = ".$conn_vals{MediaSubType}); - ::rptMsg("\tIpCheckingEnabled = ".$conn_vals{IpCheckingEnabled}) - if (exists $conn_vals{IpCheckingEnabled}); - - } - ::rptMsg(""); - } - - } - else { - ::rptMsg($nw_path." has no subkeys."); - } - } - else { - ::rptMsg($nw_path." could not be found."); - ::logMsg($nw_path." could not be found."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/networkcards.pl b/thirdparty/rr-full/plugins/networkcards.pl index 23cf82d74b7..885dde3160e 100644 --- a/thirdparty/rr-full/plugins/networkcards.pl +++ b/thirdparty/rr-full/plugins/networkcards.pl @@ -1,7 +1,13 @@ #----------------------------------------------------------- # networkcards # -# copyright 2008 H. Carvey, keydet89@yahoo.com +# History +# 20200921 - MITRE update +# 20200518 - update date output format +# 20080325 - created +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package networkcards; use strict; @@ -10,12 +16,14 @@ package networkcards; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080325); + MITRE => "", + category => "config", + output => "report", + version => 20200921); sub getConfig{return %config} sub getShortDescr { - return "Get NetworkCards"; + return "Get NetworkCards Info"; } sub getDescr{} sub getRefs {} @@ -28,8 +36,8 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching networkcards v.".$VERSION); - ::rptMsg("networkcards v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("networkcards v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; @@ -40,25 +48,21 @@ sub pluginmain { ::rptMsg(""); my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { - my %nc; + ::rptMsg(sprintf "%-50s %-50s","Description","Key LastWrite time"); foreach my $s (@subkeys) { - my $service = $s->get_value("ServiceName")->get_data(); - $nc{$service}{descr} = $s->get_value("Description")->get_data(); - $nc{$service}{lastwrite} = $s->get_timestamp(); - } - - foreach my $n (keys %nc) { - ::rptMsg($nc{$n}{descr}." [".gmtime($nc{$n}{lastwrite})."]"); + eval { + my $desc = $s->get_value("Description")->get_data(); + ::rptMsg(sprintf "%-50s %-50s",$desc,::format8601Date($s->get_timestamp())."Z"); + }; + } } else { ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/networklist.pl b/thirdparty/rr-full/plugins/networklist.pl index a8260776444..3c43e03947d 100644 --- a/thirdparty/rr-full/plugins/networklist.pl +++ b/thirdparty/rr-full/plugins/networklist.pl @@ -5,6 +5,9 @@ # # # Change History: +# 20200921 - MITRE update +# 20200518 - additional updates +# 20200515 - minor updates # 20190128 - Added Nla\Wireless data # 20150812 - updated to include Nla\Cache data # 20120917 - updated to include NameType value @@ -14,23 +17,25 @@ # # References # -# copyright 2015 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package networklist; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20190128); + output => "report", + version => 20200921); sub getConfig{return %config} sub getShortDescr { - return "Collects network info from Vista+ NetworkList key"; + return "Collects network info from NetworkList key"; } sub getDescr{} sub getRefs {} @@ -126,7 +131,7 @@ sub pluginmain { foreach my $n (keys %nl) { my $str = sprintf "%-15s Gateway Mac: ".$nl{$n}{DefaultGatewayMac},$nl{$n}{ProfileName}; ::rptMsg($nl{$n}{ProfileName}); -# ::rptMsg(" Key LastWrite : ".gmtime($nl{$n}{LastWrite})." Z"); + ::rptMsg(" Key LastWrite : ".::format8601Date($nl{$n}{LastWrite})."Z"); ::rptMsg(" DateLastConnected: ".$nl{$n}{DateLastConnected}); ::rptMsg(" DateCreated : ".$nl{$n}{DateCreated}); ::rptMsg(" DefaultGatewayMac: ".$nl{$n}{DefaultGatewayMac}); @@ -185,10 +190,8 @@ sub parseDate128 { "Aug","Sep","Oct","Nov","Dec"); my @days = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat"); my ($yr,$mon,$dow,$dom,$hr,$min,$sec,$ms) = unpack("v*",$date); - $hr = "0".$hr if ($hr < 10); - $min = "0".$min if ($min < 10); - $sec = "0".$sec if ($sec < 10); - my $str = $days[$dow]." ".$months[$mon - 1]." ".$dom." ".$hr.":".$min.":".$sec." ".$yr; +# my $str = $days[$dow]." ".$months[$mon - 1]." ".$dom." ".$hr.":".$min.":".$sec." ".$yr; + my $str = sprintf("%04d-%02d-%02d %02d:%02d:%02d",$yr,$mon,$dom,$hr,$min,$sec); return $str; } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/networklist_tln.pl b/thirdparty/rr-full/plugins/networklist_tln.pl index 79d42e8eb2d..cd47197242d 100644 --- a/thirdparty/rr-full/plugins/networklist_tln.pl +++ b/thirdparty/rr-full/plugins/networklist_tln.pl @@ -5,6 +5,7 @@ # # # Change History: +# 20200921 - MITRE update # 20150812 - updated to include Nla\Cache data # 20120608 - updated from networklist.pl to add TLN output # 20090812 - updated code to parse DateCreated and DateLastConnected @@ -13,18 +14,20 @@ # # References # -# copyright 2015 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package networklist_tln; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20150812); + output => "tln", + version => 20200921); sub getConfig{return %config} diff --git a/thirdparty/rr-full/plugins/networkprotection.pl b/thirdparty/rr-full/plugins/networkprotection.pl new file mode 100644 index 00000000000..a39a17f9a10 --- /dev/null +++ b/thirdparty/rr-full/plugins/networkprotection.pl @@ -0,0 +1,99 @@ +#----------------------------------------------------------- +# networkprotection.pl +# Get Windows Defender NetworkProtection settings +# +# Change history: +# 20221114 - created +# +# References: +# https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/enable-network-protection?view=o365-worldwide +# https://www.stigviewer.com/stig/windows_defender_antivirus/2017-12-27/finding/V-77979 +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, 2013 +#----------------------------------------------------------- +package networkprotection; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + MITRE => "T1562\.001", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20221114); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get Windows Defender NetworkProtection settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my $key; + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $wd_count = 0; + ::logMsg("Launching networkprotection v.".$VERSION); + ::rptMsg("networkprotection v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path = "Policies\\Microsoft\\Windows Defender\\Policy Manager"; + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $n = $key->get_value("EnableNetworkProtection")->get_data(); + ::rptMsg("EnableNetworkProtection value: ".$n); + }; + ::rptMsg("EnableNetworkProtection value not found.") if ($@); + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + my $key_path = "Microsoft\\Windows Defender\\Windows Defender Exploit Guard\\NetworkProtection"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $n = $key->get_value("EnableNetworkProtection")->get_data(); + ::rptMsg("EnableNetworkProtection value: ".$n); + }; + ::rptMsg("EnableNetworkProtection value not found.") if ($@); + + } + else { + ::rptMsg($key_path." not found."); + } + + ::rptMsg(""); + + ::rptMsg("Analysis Tip: Windows Defender can be configured to prevent users/apps from accessing dangerous websites, via"); + ::rptMsg("the \"EnableNetworkProtection\" value."); + ::rptMsg(""); + ::rptMsg("0 - Off"); + ::rptMsg("1 - On "); + ::rptMsg("2 - Audit mode"); + ::rptMsg(""); + ::rptMsg("Ref: https://www.stigviewer.com/stig/windows_defender_antivirus/2017-12-27/finding/V-77979"); + ::rptMsg("Ref: https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/enable-network-protection?view=o365-worldwide "); +} +1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/networkproviders.pl b/thirdparty/rr-full/plugins/networkproviders.pl new file mode 100644 index 00000000000..23aa67d47df --- /dev/null +++ b/thirdparty/rr-full/plugins/networkproviders.pl @@ -0,0 +1,124 @@ +#----------------------------------------------------------- +# networkproviders.pl - +# +# History: +# 20220803 - updated Analysis Tip +# 20220217 - added reference, updated output +# 20210421 - created +# +# References: +# https://twitter.com/0gtweet/status/1283532806816137216 +# https://github.com/gtworek/PSBits/blob/master/PasswordStealing/NPPSpy/Get-NetworkProviders.ps1 +# https://www.scip.ch/en/?labs.20220217 <-- added 20220217 +# https://attack.mitre.org/techniques/T1556/003/ +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package networkproviders; +use strict; + +my %config = (hive => "system", + output => "report", + category => "credential access", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1556\.003", + version => 20220803); + +sub getConfig{return %config} +sub getShortDescr { + return "Get NetworkProviders info"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my @temps; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching networkproviders v.".$VERSION); + ::rptMsg("networkproviders v.".$VERSION); + ::rptMsg("Category: ".$config{category}." MITRE: ".$config{MITRE}); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\NetworkProvider\\Order"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + my @prov = (); + eval { + my $po = $key->get_value("ProviderOrder")->get_data(); + ::rptMsg("ProviderOrder value: ".$po); + ::rptMsg(""); + @prov = split(/,/,$po); + }; + + if (scalar @prov > 0) { + foreach my $p (@prov) { + my $key_path = $ccs."\\Services\\".$p."\\NetworkProvider"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $name = $key->get_value("Name")->get_data(); + ::rptMsg("Name : ".$name); + }; +# added 20220217 + eval { + my $t = $key->get_value("TriggerStartPrefix")->get_data(); + ::rptMsg("TriggerStartPrefix: ".$t); + }; + + eval { + my $disp = $key->get_value("DisplayName")->get_data(); + ::rptMsg("DisplayName : ".$disp); + }; + + eval { + my $dev = $key->get_value("DeviceName")->get_data(); + ::rptMsg("DeviceName : ".$dev); + }; + + eval { + my $path = $key->get_value("ProviderPath")->get_data(); + ::rptMsg("ProviderPath : ".$path); + + }; + ::rptMsg(""); + } + else { + ::rptMsg($key_path." not found."); + } + + } + + } +# ::rptMsg(""); + ::rptMsg("Analysis Tip: Network providers can be used to load NPLogonNotify API-based password theft tools. This plugin"); + ::rptMsg("enumerates installed Network Provider DLLs (ProviderPath) so that they can be checked and verified\. One provider"); + ::rptMsg("to specifically look for is \"logincontroll\", which may indicate NPPSpy was installed."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.scip.ch/en/?labs.20220217"); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/networkproviderservices.pl b/thirdparty/rr-full/plugins/networkproviderservices.pl new file mode 100644 index 00000000000..075c83e500f --- /dev/null +++ b/thirdparty/rr-full/plugins/networkproviderservices.pl @@ -0,0 +1,86 @@ +#----------------------------------------------------------- +# networkproviderservices.pl +# Plugin to check Windows services keys for a NetworkProvider subkey +# Based on the networkproviders.pl plugin, but checks Windows services keys for a NetworkProvider subkey +# +# History: +# 20230118 - created +# +# References: +# https://twitter.com/0gtweet/status/1283532806816137216 +# https://www.scip.ch/en/?labs.20220217 <-- added 20220217 +# https://attack.mitre.org/techniques/T1556/003/ +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package networkproviderservices; +use strict; + +my %config = (hive => "system", + output => "report", + category => "credential access", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1556\.003", + version => 20220803); + +sub getConfig{return %config} +sub getShortDescr { + return "Check Windows services keys for NetworkProvider subkey"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching networkproviderservices v.".$VERSION); + ::rptMsg("networkproviderservices v.".$VERSION); + ::rptMsg("Category: ".$config{category}." MITRE: ".$config{MITRE}); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\services"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + + if (my $n = $s->get_subkey("NetworkProvider")) { + ::rptMsg($key_path."\\".$s->get_name()."\\NetworkProvider subkey found"); + ::rptMsg("LastWrite time : ".::format8601Date($n->get_timestamp())."Z"); + eval { + my $dev = $n->get_value("DeviceName")->get_data(); + ::rptMsg("DeviceName : ".$dev); + }; + + eval { + my $path = $n->get_value("ProviderPath")->get_data(); + ::rptMsg("ProviderPath : ".$path); + + }; + ::rptMsg(""); + } + + } + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/networksetup2.pl b/thirdparty/rr-full/plugins/networksetup2.pl new file mode 100644 index 00000000000..449cc5bb832 --- /dev/null +++ b/thirdparty/rr-full/plugins/networksetup2.pl @@ -0,0 +1,122 @@ +#----------------------------------------------------------- +# networksetup2 +# Gets addresses from NetworkSetup2 subkeys +# +# +# History: +# 20200922 - MITRE update +# 20191004 - created +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package networksetup2; +use strict; + +my %config = (hive => "system", + MITRE => "", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20200922); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get NetworkSetup2 subkey info"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $reg; + +my %types = (0x47 => "wireless", + 0x06 => "wired", + 0x17 => "broadband (3g)"); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching networksetup2 v.".$VERSION); + ::rptMsg("networksetup2 v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + +# Code for System file, getting CurrentControlSet + my $current; + my $ccs; + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + $current = $key->get_value("Current")->get_data(); + $ccs = "ControlSet00".$current; + } + else { + ::rptMsg($key_path." not found."); + return; + } + + my $key_path = $ccs."\\Control\\NetworkSetup2\\Interfaces"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $k (@subkeys) { + my $alias = ""; + my $descr = ""; + my $type = ""; + my $iftype = ""; + + eval { + $alias = $k->get_subkey("Kernel")->get_value("IfAlias")->get_data(); + $descr = $k->get_subkey("Kernel")->get_value("IfDescr")->get_data(); + $type = $k->get_subkey("Kernel")->get_value("IfType")->get_data(); + + if (exists $types{$type}) { + $iftype = $types{$type}; + } + else { + $iftype = sprintf "0x%x",$type; + } +# ::rptMsg($alias." - ".$descr); + }; + + eval { + my $a = $k->get_subkey("Kernel")->get_value("CurrentAddress")->get_data(); + my @addr = unpack("C6",$a); + foreach my $i (0..5) { +# ::rptMsg(sprintf "%x",$ad); + $addr[$i] = sprintf "%x",$addr[$i]; + } + ::rptMsg($alias." - ".$descr." (".$iftype.")"); + ::rptMsg(" CurrentAddress : ".join(':',@addr)); + }; + + eval { + my $a = $k->get_subkey("Kernel")->get_value("PermanentAddress")->get_data(); + my @addr = unpack("C6",$a); + foreach my $i (0..5) { +# ::rptMsg(sprintf "%x",$ad); + $addr[$i] = sprintf "%x",$addr[$i]; + } + ::rptMsg(" PermanentAddress : ".join(':',@addr)); + }; + + } + } + else { + ::rptMsg($key_path." has no subkeys."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/networkuid.pl b/thirdparty/rr-full/plugins/networkuid.pl deleted file mode 100644 index d23c55cd499..00000000000 --- a/thirdparty/rr-full/plugins/networkuid.pl +++ /dev/null @@ -1,59 +0,0 @@ -#----------------------------------------------------------- -# networkuid.pl -# Gets UID value from Network key -# -# References -# http://blogs.technet.com/mmpc/archive/2010/03/11/got-zbot.aspx -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package networkuid; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20100312); - -sub getConfig{return %config} - -sub getShortDescr { - return "Gets Network key UID value"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching networkuid v.".$VERSION); - ::rptMsg("networkuid v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Network"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite time = ".gmtime($key->get_timestamp())); - ::rptMsg(""); - - eval { - my $uid = $key->get_value("UID")->get_data(); - ::rptMsg("UID value = ".$uid); - }; - ::rptMsg("UID value not found.") if ($@); - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/nic.pl b/thirdparty/rr-full/plugins/nic.pl deleted file mode 100644 index adbaa8143cd..00000000000 --- a/thirdparty/rr-full/plugins/nic.pl +++ /dev/null @@ -1,82 +0,0 @@ -#----------------------------------------------------------- -# nic.pl -# -# -# Change history -# 20100401 - created -# -# References -# LeaseObtainedTime - http://technet.microsoft.com/en-us/library/cc978465.aspx -# T1 - http://technet.microsoft.com/en-us/library/cc978470.aspx -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package nic; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100401); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets NIC info from System hive"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my %nics; - my $ccs; - ::logMsg("Launching nic v.".$VERSION); - ::rptMsg("nic v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - eval { - $current = $root_key->get_subkey("Select")->get_value("Current")->get_data(); - }; - my @nics; - my $key_path = "ControlSet00".$current."\\Services"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my @svcs = $key->get_list_of_subkeys(); - foreach my $s (@svcs) { - push(@nics,$s) if ($s->get_name() =~ m/^{/); - } - foreach my $n (@nics) { - eval { - my @vals = $n->get_subkey("Parameters\\Tcpip")->get_list_of_values(); - ::rptMsg("Adapter: ".$n->get_name()); - ::rptMsg("LastWrite Time: ".gmtime($n->get_timestamp())." Z"); - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); - $data = gmtime($data)." Z" if ($name eq "T1" || $name eq "T2"); - $data = gmtime($data)." Z" if ($name =~ m/Time$/); - - ::rptMsg(sprintf " %-20s %-20s",$name,$data); - - } - ::rptMsg(""); - }; - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/nic2.pl b/thirdparty/rr-full/plugins/nic2.pl index 946034b5845..e5f5e47c076 100644 --- a/thirdparty/rr-full/plugins/nic2.pl +++ b/thirdparty/rr-full/plugins/nic2.pl @@ -2,6 +2,8 @@ # nic2.pl # # Change history +# 20200922 - MITRE update +# 20200525 - updated date output format # 20150812 - included updates from Yogesh Khatri # 20100401 - created # @@ -9,7 +11,7 @@ # LeaseObtainedTime - http://technet.microsoft.com/en-us/library/cc978465.aspx # T1 - http://technet.microsoft.com/en-us/library/cc978470.aspx # -# copyright 2015 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC #----------------------------------------------------------- package nic2; use strict; @@ -18,8 +20,10 @@ package nic2; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20150812); + MITRE => "", + category => "config", + output => "report", + version => 20200922); sub getConfig{return %config} sub getShortDescr { @@ -57,14 +61,14 @@ sub pluginmain { if (scalar @guids > 0) { foreach my $g (@guids) { ::rptMsg("Adapter: ".$g->get_name()); - ::rptMsg("LastWrite Time: ".gmtime($g->get_timestamp())." Z"); + ::rptMsg("LastWrite Time: ".::format8601Date($g->get_timestamp())."Z"); eval { my @vals = $g->get_list_of_values(); foreach my $v (@vals) { my $name = $v->get_name(); my $data = $v->get_data(); - $data = gmtime($data)." Z" if ($name eq "T1" || $name eq "T2"); - $data = gmtime($data)." Z" if ($name =~ m/Time$/); + $data = ::format8601Date($data)."Z" if ($name eq "T1" || $name eq "T2"); + $data = ::format8601Date($data)."Z" if ($name =~ m/Time$/); $data = pack("h*",reverse $data) if (uc($name) eq uc("DhcpNetworkHint")); # SSID nibbles reversed //YK ::rptMsg(sprintf " %-28s %-20s",$name,$data); } @@ -77,14 +81,14 @@ sub pluginmain { ::rptMsg("Adapter: ".$g->get_name()."/".$ssid->get_name()); my $ssid_realname = pack("h*",reverse $ssid->get_name()); ::rptMsg("SSID Decoded: ".$ssid_realname); - ::rptMsg("LastWrite Time: ".gmtime($ssid->get_timestamp())." Z"); + ::rptMsg("LastWrite Time: ".::format8601Date($ssid->get_timestamp())."Z"); eval { my @vals = $ssid->get_list_of_values(); foreach my $v (@vals) { my $name = $v->get_name(); my $data = $v->get_data(); - $data = gmtime($data)." Z" if ($name eq "T1" || $name eq "T2"); - $data = gmtime($data)." Z" if ($name =~ m/Time$/); + $data = ::format8601Date($data)."Z" if ($name eq "T1" || $name eq "T2"); + $data = ::format8601Date($data)."Z" if ($name =~ m/Time$/); $data = pack("h*",reverse $data) if (uc($name) eq uc("DhcpNetworkHint")); ::rptMsg(sprintf " %-28s %-20s",$name,$data); } diff --git a/thirdparty/rr-full/plugins/nic_mst2.pl b/thirdparty/rr-full/plugins/nic_mst2.pl deleted file mode 100644 index 2fbb9c1bb62..00000000000 --- a/thirdparty/rr-full/plugins/nic_mst2.pl +++ /dev/null @@ -1,150 +0,0 @@ -#----------------------------------------------------------- -# nic_mst2.pl -# Plugin for Registry Ripper; Get information on network -# interfaces from the System hive file - start with the -# Control\Network GUID subkeys...within the Connection key, -# look for MediaSubType == 2, and maintain a list of GUIDs. -# Then go over to the Services\Tcpip\Parameters\Interfaces -# key and get the IP configurations for each of the interface -# GUIDs -# -# Change history -# -# -# References -# http://support.microsoft.com/kb/555382 -# http://support.microsoft.com/kb/894564 -# http://support.microsoft.com/kb/899868 -# -# copyright 2008 H. Carvey -#----------------------------------------------------------- -package nic_mst2; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080324); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets NICs from System hive; looks for MediaType = 2"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my %nics; - my $ccs; - ::logMsg("Launching nic_mst2 v.".$VERSION); - ::rptMsg("nic_mst2 v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - my $nw_path = $ccs."\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"; - my $nw; - if ($nw = $root_key->get_subkey($nw_path)) { - ::rptMsg("Network key"); - ::rptMsg($nw_path); -# Get all of the subkey names - my @sk = $nw->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - my $name = $s->get_name(); - next if ($name eq "Descriptions"); - if (my $conn = $nw->get_subkey($name."\\Connection")) { - my %conn_vals; - my @vals = $conn->get_list_of_values(); - map{$conn_vals{$_->get_name()} = $_->get_data()}@vals; -# See what the active NICs were on the system; "active" based on PnpInstanceID having -# a string value -# Get the GUID of the interface, the name, and the LastWrite time of the Connection -# key - if (exists $conn_vals{PnpInstanceID} && $conn_vals{PnpInstanceID} ne "") { - $nics{$name}{Name} = $conn_vals{Name}; - $nics{$name}{LastWrite} = $conn->get_timestamp(); - } - } - } - - } - else { - ::rptMsg($nw_path." has no subkeys."); - } - } - else { - ::rptMsg($nw_path." could not be found."); - } - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); -# access the Tcpip Services key to get the IP address information - if (scalar(keys %nics) > 0) { - my $key_path = $ccs."\\Services\\Tcpip\\Parameters\\Interfaces"; - if ($key = $root_key->get_subkey($key_path)) { - my %guids; - ::rptMsg($key_path); - ::rptMsg("LastWrite time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); -# Dump the names of the subkeys under Parameters\Interfaces into a hash - my @sk = $key->get_list_of_subkeys(); - map{$guids{$_->get_name()} = 1}(@sk); - - foreach my $n (keys %nics) { - if (exists $guids{$n}) { - my $if = $key->get_subkey($n); - ::rptMsg("Interface ".$n); - ::rptMsg("Name: ".$nics{$n}{Name}); - ::rptMsg("Control\\Network key LastWrite time ".gmtime($nics{$n}{LastWrite})." (UTC)"); - ::rptMsg("Services\\Tcpip key LastWrite time ".gmtime($if->get_timestamp())." (UTC)"); - - my @vals = $if->get_list_of_values; - my %ip; - map{$ip{$_->get_name()} = $_->get_data()}@vals; - - if (exists $ip{EnableDHCP} && $ip{EnableDHCP} == 1) { - ::rptMsg("\tDhcpDomain = ".$ip{DhcpDomain}); - ::rptMsg("\tDhcpIPAddress = ".$ip{DhcpIPAddress}); - ::rptMsg("\tDhcpSubnetMask = ".$ip{DhcpSubnetMask}); - ::rptMsg("\tDhcpNameServer = ".$ip{DhcpNameServer}); - ::rptMsg("\tDhcpServer = ".$ip{DhcpServer}); - } - else { - ::rptMsg("\tIPAddress = ".$ip{IPAddress}); - ::rptMsg("\tSubnetMask = ".$ip{SubnetMask}); - ::rptMsg("\tDefaultGateway = ".$ip{DefaultGateway}); - } - - } - else { - ::rptMsg("Interface ".$n." not found in the ".$key_path." key."); - } - ::rptMsg(""); - } - } - } - else { - ::rptMsg("No active network interface cards were found."); - ::logMsg("No active network interface cards were found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/nolmhash.pl b/thirdparty/rr-full/plugins/nolmhash.pl deleted file mode 100644 index e47e0bc67d6..00000000000 --- a/thirdparty/rr-full/plugins/nolmhash.pl +++ /dev/null @@ -1,76 +0,0 @@ -#----------------------------------------------------------- -# nolmhash.pl -# Gets NoLMHash value -# -# Change history -# 20100712 - created -# -# References -# http://support.microsoft.com/kb/299656 -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package nolmhash; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100712); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets NoLMHash value"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching nolmhash v.".$VERSION); - ::rptMsg("nolmhash v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my ($current,$ccs); - my $sel_path = 'Select'; - my $sel; - if ($sel = $root_key->get_subkey($sel_path)) { - $current = $sel->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - my $key_path = $ccs."\\Control\\Lsa"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("nolmhash v.".$VERSION); - ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); - ::rptMsg(""); - my $nolmhash; - eval { - $nolmhash = $key->get_value("NoLMHash")->get_data(); - ::rptMsg("NoLMHash value = ".$nolmhash); - ::rptMsg(""); - ::rptMsg("A value of 1 indicates that LMHashes are not stored in the SAM."); - }; - ::rptMsg("Error occurred getting NoLMHash value: $@") if ($@); - } - else { - ::rptMsg($key_path." not found."); - } - } - else { - ::rptMsg($sel_path." not found."); - ::logMsg($sel_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/notif.pl b/thirdparty/rr-full/plugins/notif.pl new file mode 100644 index 00000000000..f5a5f2f3e0c --- /dev/null +++ b/thirdparty/rr-full/plugins/notif.pl @@ -0,0 +1,70 @@ +#----------------------------------------------------------- +# notif.pl +# Get user's Notification settings +# +# Change history +# 20200926 - created +# +# References +# https://twitter.com/el_jasoon/status/854302900994101252 +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package notif; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + category => "config", + MITRE => "", + output => "report", + version => 20200926); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets user's Notification settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching notif v.".$VERSION); + ::rptMsg("notif v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $path = "Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings"; + if (my $key = $root_key->get_subkey($path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); + + eval { + my $e = $s->get_value("Enabled")->get_data(); + ::rptMsg("Enabled : ".$e); + }; + + eval { + my $e = $s->get_value("LastNotificationAddedTime")->get_data(); + my ($t0,$t1) = unpack("VV",$e); + ::rptMsg("LastNotificationAddedTime : ".::format8601Date(::getTime($t0,$t1)."Z")); + }; + + ::rptMsg(""); + } + } + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ntds.pl b/thirdparty/rr-full/plugins/ntds.pl new file mode 100644 index 00000000000..fc590a36b52 --- /dev/null +++ b/thirdparty/rr-full/plugins/ntds.pl @@ -0,0 +1,89 @@ +#----------------------------------------------------------- +# ntds.pl +# +# History: +# 20200921 - MITRE update +# 20200427 - updated output date format +# 20191016 - created +# +# References: +# https://blog.xpnsec.com/exploring-mimikatz-part-1/ +# http://redplait.blogspot.com/2015/02/lsasrvdlllsaploadlsadbextensiondll.html +# https://attack.mitre.org/techniques/T1547/008/ +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package ntds; +use strict; + +my %config = (hive => "System", + hivemask => 4, + output => "report", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1547\.008", + version => 20200921); + +sub getConfig{return %config} +sub getShortDescr { + return "Parse Services NTDS key for specific persistence values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching ntds v.".$VERSION); + ::rptMsg("ntds v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my ($current,$ccs); + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + $current = $key->get_value("Current")->get_data(); + $ccs = "ControlSet00".$current; + + my $ntds_path = $ccs."\\Services\\NTDS"; + + if (my $ntds = $key->get_subkey($ntds_path)) { + ::rptMsg("LastWrite Time: ".::format8601Date($ntds->get_timestamp())."Z"); + eval { + my $lsa = $ntds->get_value("LsaDbExtPt")->get_data(); + ::rptMsg("LsaDbExtPt value: ".$lsa); + }; + + eval { + my $dir = $ntds->get_value("DirectoryServiceExtPt")->get_data(); + ::rptMsg("DirectoryServiceExtPt value: ".$dir); + }; + } + else { + ::rptMsg($ntds_path." not found."); + } + + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ntuser b/thirdparty/rr-full/plugins/ntuser index 6c7f9c2410f..23df85fd200 100644 --- a/thirdparty/rr-full/plugins/ntuser +++ b/thirdparty/rr-full/plugins/ntuser @@ -1,149 +1,140 @@ -acmru -adoberdr -ahaha -aim -aports +activesetup +adobe +allowedenum +amsienable appassoc appcompatflags appkeys +appkeys_tln applets +applets_tln +apppaths +apppaths_tln appspecific -ares +appx +appx_tln arpcache attachmgr -autoendtasks +attachmgr_tln autorun -bitbucket_user -brisv +bitbucket +blm cached -cain -ccleaner -cdstaginginfo -clampi -clampitm +cached_tln +certs +clipbrd cmdproc +cmdproc_tln comdlg32 compdesc -controlpanel -cortana -cpldontload +consentstore +consentstore_tln ddo -decaf -dependency_walker +devicecache disablemru -domains +disabletools +doctoidmapping +elevatedinstall environment -eraser -fileexts -filehistory -foxitrdr -gthist -gtwhitelist -haven_and_hearth +featureusage +gpohist +gpohist_tln identities -iejava -ie_main -ie_settings -ie_zones -imgburn1 -internet_explorer_cu -internet_settings_cu +imagefile +improviders +injectdll64 +installelevated itempos -javafx jumplistdata -kankan knowndev -latentbot +link_click listsoft -liveContactsGUID load +locale +location logonstats -logonusername -menuorder +lxss +lxss_tln mixer +mixer_tln mmc +mmc_tln mmo mndmru +mndmru_tln mp2 -mp3 +mp2_tln mpmru -mspaper +msoffice +msoffice_tln muicache -mzthunderbird nation -nero -netassist -ntusernetwork -odysseus -officedocs -officedocs2010 -officedocs2010_tln +notif +office_test oisc -olsearch +onedrive +onedrive_tln +onenote osversion -outlook -outlook2 -policies_u -printermru -printers -privoxy +osversion_tln +outlookhomepage +outlookmacro +outlook_attach +pendinggpos +persistconn profiler -proxysettings +protectedview pslogging -publishingwizard +psscript putty -putty_sessions -rdphint -reading_locations -realplayer6 -realvnc recentapps +recentapps_tln recentdocs -recentdocs_timeline -reveton -rootkit_revealer +recentdocs_tln +resiliency +restartmanager +run +rundisabled runmru +runmru_tln +runvirtual +runvirtual_tln +run_json +run_yara +screensaver +screenshotindex searchscopes sevenzip shc -shellactivities shellbags_xp shellfolders -skype -snapshot_viewer -ssh_host_keys -startmenuinternetapps_cu -startpage -startup +speech +speech_tln +staginginfo +storagesense sysinternals -thunderbirdinstalled -trustrecords +sysinternals_tln +thostperms tsclient +tsclient_tln typedpaths +typedpaths_tln typedurls typedurlstime +typedurlstime_tln +typedurls_tln +ua_wiper uninstall -unreadmail +uninstall_tln userassist -userinfo -userlocsvc -user_run -user_win -utorrent -vawtrak -vista_bitbucket -vmplayer -vmware_vsphere_client -vnchooksapplicationprefs -vncviewer -wallpaper -warcraft3 -winlogon_u +userassist_tln +userextendedproperties +wc_shares +win11_edge winrar -winrar2 +winrar_tln winscp -winscp_sessions -winvnc winzip +wordstartup wordwheelquery -yahoo_cu +wordwheelquery_tln diff --git a/thirdparty/rr-full/plugins/ntusernetwork.pl b/thirdparty/rr-full/plugins/ntusernetwork.pl deleted file mode 100644 index 893ae6a9936..00000000000 --- a/thirdparty/rr-full/plugins/ntusernetwork.pl +++ /dev/null @@ -1,65 +0,0 @@ -#----------------------------------------------------------- -# ntusernetwork.pl -# Plugin for Registry Ripper, -# Network key parser -# -#----------------------------------------------------------- -package ntusernetwork; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20110601); - -sub getConfig{return %config} -sub getShortDescr { - return "Returns contents of user's Network subkeys"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching ntusernetwork v.".$VERSION); - ::rptMsg("ntusernetwork v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Network'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar @subkeys > 0) { - foreach my $s (@subkeys) { - ::rptMsg($key_path."\\".$s->get_name()); - ::rptMsg("LastWrite time: ".gmtime($s->get_timestamp())); - my @vals = $s->get_list_of_values(); - if (scalar @vals > 0) { - foreach my $v (@vals) { - ::rptMsg(sprintf " %-15s %-25s",$v->get_name(),$v->get_data()); - } - ::rptMsg(""); - } - } - } - else { - ::rptMsg($key_path." key has no subkeys."); - } - } - else { - ::rptMsg($key_path." key not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/null.pl b/thirdparty/rr-full/plugins/null.pl index 25928244b60..277e094c717 100644 --- a/thirdparty/rr-full/plugins/null.pl +++ b/thirdparty/rr-full/plugins/null.pl @@ -4,25 +4,27 @@ # Check key/value names in a hive for a leading null character # # Change history +# 20200921 - MITRE update # 20160119 - created # # References: # http://www.symantec.com/connect/blogs/kovter-malware-learns-poweliks-persistent-fileless-registry-update -# +# https://attack.mitre.org/techniques/T1036/ # -# copyright 2016 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey #----------------------------------------------------------- package null; use strict; -my %config = (hive => "All", +my %config = (hive => "all", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - category => "malware", - version => 20160119); + MITRE => "T1036", + category => "defense evasion", + output => "report", + version => 20200921); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/odysseus.pl b/thirdparty/rr-full/plugins/odysseus.pl deleted file mode 100644 index df0a1548fd6..00000000000 --- a/thirdparty/rr-full/plugins/odysseus.pl +++ /dev/null @@ -1,114 +0,0 @@ -#----------------------------------------------------------- -# odysseus.pl -# Extract registry keys for Odysseus by bindshell.net -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# http://blogs.technet.com/b/markrussinovich/archive/2011/03/08/3392087.aspx -# -# copyright (c) 2011-02-02 Brendan Coles -#----------------------------------------------------------- -# Require # -package odysseus; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 1, - hasRefs => 1, - osmask => 22, - version => 20110202); -my $VERSION = getVersion(); - -# Functions # -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extract registry keys for Odysseus by bindshell.net."; -} -sub getDescr { - return 'Extracts the following registry keys for Odysseus by'. - ' bindshell.net : "ProxyUpstreamHost","ProxyUpstreamPort",'. - '"ProxyPort","ServerCert","ServerCertPass"'; -} -sub getRefs { - my %refs = ("Odysseus Homepage:" => - "http://www.bindshell.net/tools/odysseus"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - my @interesting_keys = ( - "ProxyUpstreamHost", - "ProxyUpstreamPort", - "ProxyPort", - "ServerCert", - "ServerCertPass" - ); - - # Initialize # - ::logMsg("Launching odysseus v.".$VERSION); - ::rptMsg("odysseus v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\bindshell.net\\Odysseus"; - - # If # odysseus path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Odysseus"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Odysseus registry path # - my %keys; - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Odysseus registry path # - foreach my $v (@vals) { - $keys{$v->get_name()} = $v->get_data(); - } - - # Return # all key names+values for interesting keys # - foreach my $var (@interesting_keys) { - if (exists $keys{$var}) { - ::rptMsg($var." -> ".$keys{$var}); - } - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Odysseus isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/office_test.pl b/thirdparty/rr-full/plugins/office_test.pl new file mode 100644 index 00000000000..462833b4191 --- /dev/null +++ b/thirdparty/rr-full/plugins/office_test.pl @@ -0,0 +1,95 @@ +#----------------------------------------------------------- +# office_test.pl +# +# +# Change history: +# 20230403 - created +# +# References: +# https://attack.mitre.org/techniques/T1137/002/ +# https://www.cyberark.com/resources/threat-research-blog/persistence-techniques-that-persist +# 2014: https://www.hexacorn.com/blog/2014/04/16/beyond-good-ol-run-key-part-10/ +# 2016: https://unit42.paloaltonetworks.com/unit42-technical-walkthrough-office-test-persistence-method-used-in-recent-sofacy-attacks/ +# 2019: https://pentestlab.blog/2019/12/11/persistence-office-application-startup/ +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package office_test; +use strict; + +my %config = (hive => "software,ntuser\.dat", + category => "persistence", + MITRE => "T1137\.002", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20230403); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check for MS Office test/debug value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching office_test v.".$VERSION); + ::rptMsg("office_test v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my $key_path = (); + if ($hive_guess eq "software") { + $key_path = "Microsoft\\Office Test\\Special\\Perf"; + } + elsif ($hive_guess eq "ntuser") { + $key_path = "Software\\Microsoft\\Office Test\\Special\\Perf"; + } + else {} + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + eval { + my $d = $key->get_value("")->get_data(); + ::rptMsg("\"Default\" value: ".$d); + }; + ::rptMsg("\"Default\" value not found.") if ($@); + } + else { + ::rptMsg($key_path." key not found."); + } + + ::rptMsg(""); + ::rptMsg("Analysis Tip: When MS applications are opened, they check for the \"Default\" value beneath this key, and "); + ::rptMsg("load the DLL listed in the value."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.cyberark.com/resources/threat-research-blog/persistence-techniques-that-persist"); + ::rptMsg("Ref: https://unit42.paloaltonetworks.com/unit42-technical-walkthrough-office-test-persistence-method-used-in-recent-sofacy-attacks/"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/officedocs.pl b/thirdparty/rr-full/plugins/officedocs.pl deleted file mode 100644 index ba0d57b8fa0..00000000000 --- a/thirdparty/rr-full/plugins/officedocs.pl +++ /dev/null @@ -1,147 +0,0 @@ -#----------------------------------------------------------- -# officedocs.pl -# Plugin for Registry Ripper -# -# Change history -# -# -# References -# -# -# copyright 2008 H. Carvey -#----------------------------------------------------------- -package officedocs; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080324); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's Office doc MRU keys"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching officedocs v.".$VERSION); - ::rptMsg("officedocs v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - ::rptMsg("officedocs v.".$VERSION); -# First, let's find out which version of Office is installed - my $version; - my $tag = 0; - my @versions = ("7\.0","8\.0", "9\.0", "10\.0", "11\.0","12\.0"); - foreach my $ver (@versions) { - my $key_path = "Software\\Microsoft\\Office\\".$ver."\\Common\\Open Find"; - if (defined($root_key->get_subkey($key_path))) { - $version = $ver; - $tag = 1; - } - } - - if ($tag) { - ::rptMsg("MSOffice version ".$version." located."); - my $key_path = "Software\\Microsoft\\Office\\".$version; - my $of_key = $root_key->get_subkey($key_path); - if ($of_key) { -# Attempt to retrieve Word docs - my @funcs = ("Open","Save As","File Save"); - foreach my $func (@funcs) { - my $word = "Common\\Open Find\\Microsoft Office Word\\Settings\\".$func."\\File Name MRU"; - my $word_key = $of_key->get_subkey($word); - if ($word_key) { - ::rptMsg($word); - ::rptMsg("LastWrite Time ".gmtime($word_key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $value = $word_key->get_value("Value")->get_data(); - my @data = split(/\x00/,$value); - map{::rptMsg("$_");}@data; - } - else { -# ::rptMsg("Could not access ".$word); - } - ::rptMsg(""); - } -# Attempt to retrieve Excel docs - my $excel = 'Excel\\Recent Files'; - if (my $excel_key = $of_key->get_subkey($excel)) { - ::rptMsg($key_path."\\".$excel); - ::rptMsg("LastWrite Time ".gmtime($excel_key->get_timestamp())." (UTC)"); - my @vals = $excel_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - my $data = $v->get_data(); - my $tag = (split(/File/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg(" ".$val." -> ".$data); - } - } - else { - ::rptMsg($key_path.$excel." has no values."); - } - } - else { - ::rptMsg($key_path.$excel." not found."); - } - ::rptMsg(""); -# Attempt to retrieve PowerPoint docs - my $ppt = 'PowerPoint\\Recent File List'; - if (my $ppt_key = $of_key->get_subkey($ppt)) { - ::rptMsg($key_path."\\".$ppt); - ::rptMsg("LastWrite Time ".gmtime($ppt_key->get_timestamp())." (UTC)"); - my @vals = $ppt_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - my $data = $v->get_data(); - my $tag = (split(/File/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg(" ".$val." -> ".$data); - } - } - else { - ::rptMsg($key_path."\\".$ppt." has no values."); - } - } - else { - ::rptMsg($key_path."\\".$ppt." not found."); - } - } - else { - ::rptMsg("Could not access ".$key_path); - ::logMsg("Could not access ".$key_path); - } - } - else { - ::logMsg("MSOffice version not found."); - ::rptMsg("MSOffice version not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/officedocs2010.pl b/thirdparty/rr-full/plugins/officedocs2010.pl deleted file mode 100644 index 38a27890345..00000000000 --- a/thirdparty/rr-full/plugins/officedocs2010.pl +++ /dev/null @@ -1,205 +0,0 @@ -#----------------------------------------------------------- -# officedocs2010.pl -# Plugin to parse Office 2010 MRU entries (Word, Excel, Access, and PowerPoint) -# -# Change history -# 20110901 - updated to remove dependency on the DateTime module -# 20010415 [fpi] * added this banner and change the name from "officedocs" -# to "officedocs2010", since this plugins is little different -# from Harlan's one (merging suggested) -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright 2011 Cameron Howell -# modified 20110901, H. Carvey keydet89@yahoo.com -#----------------------------------------------------------- - -package officedocs2010; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 2011090); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets user's Office 2010 doc MRU values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getWinTS { - my $data = $_[0]; - my $winTS; - my $dateTime; - (my $prefix, my $suffix) = split(/\*/,$data); - if ($prefix =~ /\[.{9}\]\[T(.{16})\]/) { - $winTS = $1; - my @vals = split(//,$winTS); - my $t0 = join('',@vals[0..7]); - my $t1 = join('',@vals[8..15]); - $dateTime = ::getTime(hex($t1),hex($t0)); - } - return ($suffix ." ". gmtime($dateTime)); -} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching officedocs2010 v.".$VERSION); - ::rptMsg("officedocs2010 v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - # ::rptMsg("officedocs v.".$VERSION); # 20110830 [fpi] - redundant - my $tag = 0; - my $key_path = "Software\\Microsoft\\Office\\14.0"; - if (defined($root_key->get_subkey($key_path))) { - $tag = 1; - } - - if ($tag) { - ::rptMsg("MSOffice version 2010 located."); - my $key_path = "Software\\Microsoft\\Office\\14.0"; - my $of_key = $root_key->get_subkey($key_path); - if ($of_key) { -# Attempt to retrieve Word docs - my $word = 'Word\\File MRU'; - if (my $word_key = $of_key->get_subkey($word)) { - ::rptMsg($key_path."\\".$word); - ::rptMsg("LastWrite Time ".gmtime($word_key->get_timestamp())." (UTC)"); - my @vals = $word_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my $data = getWinTS($v->get_data()); - my $tag = (split(/Item/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg(" ".$val." -> ".$data); - } - } - else { - ::rptMsg($key_path.$word." has no values."); - } - } - else { - ::rptMsg($key_path.$word." not found."); - } - ::rptMsg(""); -# Attempt to retrieve Excel docs - my $excel = 'Excel\\File MRU'; - if (my $excel_key = $of_key->get_subkey($excel)) { - ::rptMsg($key_path."\\".$excel); - ::rptMsg("LastWrite Time ".gmtime($excel_key->get_timestamp())." (UTC)"); - my @vals = $excel_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my $data = getWinTS($v->get_data()); - my $tag = (split(/Item/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg(" ".$val." -> ".$data); - } - } - else { - ::rptMsg($key_path.$excel." has no values."); - } - } - else { - ::rptMsg($key_path.$excel." not found."); - } - ::rptMsg(""); -# Attempt to retrieve Access docs - my $access = 'Access\\File MRU'; - if (my $access_key = $of_key->get_subkey($access)) { - ::rptMsg($key_path."\\".$access); - ::rptMsg("LastWrite Time ".gmtime($access_key->get_timestamp())." (UTC)"); - my @vals = $access_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my $data = getWinTS($v->get_data()); - my $tag = (split(/Item/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg(" ".$val." -> ".$data); - } - } - else { - ::rptMsg($key_path."\\".$access." has no values."); - } - } - else { - ::rptMsg($key_path."\\".$access." not found."); - } - ::rptMsg(""); -# Attempt to retrieve PowerPoint docs - my $ppt = 'PowerPoint\\File MRU'; - if (my $ppt_key = $of_key->get_subkey($ppt)) { - ::rptMsg($key_path."\\".$ppt); - ::rptMsg("LastWrite Time ".gmtime($ppt_key->get_timestamp())." (UTC)"); - my @vals = $ppt_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my $data = getWinTS($v->get_data()); - my $tag = (split(/Item/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg(" ".$val." -> ".$data); - } - } - else { - ::rptMsg($key_path."\\".$ppt." has no values."); - } - } - else { - ::rptMsg($key_path."\\".$ppt." not found."); - } - } - else { - ::rptMsg("Could not access ".$key_path); - ::logMsg("Could not access ".$key_path); - } - } - else { - ::logMsg("MSOffice version not found."); - ::rptMsg("MSOffice version not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/officedocs2010_tln.pl b/thirdparty/rr-full/plugins/officedocs2010_tln.pl deleted file mode 100644 index 0a78a7bbab2..00000000000 --- a/thirdparty/rr-full/plugins/officedocs2010_tln.pl +++ /dev/null @@ -1,179 +0,0 @@ -#----------------------------------------------------------- -# officedocs2010_tln.pl -# Plugin to parse Office 2010 MRU entries (Word, Excel, Access, and PowerPoint) -# -# Change history -# 20120717 - created from officedocs2010.pl -# 20110901 - updated to remove dependency on the DateTime module -# 20010415 [fpi] * added this banner and change the name from "officedocs" -# to "officedocs2010", since this plugins is little different -# from Harlan's one (merging suggested) -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright 2011 Cameron Howell -# modified 20110901, H. Carvey keydet89@yahoo.com -#----------------------------------------------------------- - -package officedocs2010_tln; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20120717); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets user's Office 2010 doc MRU values; TLN output"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getWinTS { - my $data = $_[0]; - my $winTS; - my $dateTime; - (my $prefix, my $suffix) = split(/\*/,$data); - if ($prefix =~ /\[.{9}\]\[T(.{16})\]/) { - $winTS = $1; - my @vals = split(//,$winTS); - my $t0 = join('',@vals[0..7]); - my $t1 = join('',@vals[8..15]); - $dateTime = ::getTime(hex($t1),hex($t0)); - } -# return ($suffix ." ". gmtime($dateTime)); - return ($suffix,$dateTime); -} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching officedocs2010 v.".$VERSION); -# ::rptMsg("officedocs2010 v.".$VERSION); # 20110830 [fpi] + banner -# ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - # ::rptMsg("officedocs v.".$VERSION); # 20110830 [fpi] - redundant - my $tag = 0; - my $key_path = "Software\\Microsoft\\Office\\14.0"; - if (defined($root_key->get_subkey($key_path))) { - $tag = 1; - } - - if ($tag) { -# ::rptMsg("MSOffice version 2010 located."); - my $key_path = "Software\\Microsoft\\Office\\14.0"; - my $of_key = $root_key->get_subkey($key_path); - if ($of_key) { -# Attempt to retrieve Word docs - my $word = 'Word\\File MRU'; - if (my $word_key = $of_key->get_subkey($word)) { -# ::rptMsg($key_path."\\".$word); -# ::rptMsg("LastWrite Time ".gmtime($word_key->get_timestamp())." (UTC)"); - my @vals = $word_key->get_list_of_values(); - if (scalar(@vals) > 0) { -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my ($d0,$d1) = getWinTS($v->get_data()); - ::rptMsg($d1."|REG|||OfficeDocs2010 - ".$d0); - } - } - else { -# ::rptMsg($key_path.$word." has no values."); - } - } - else { -# ::rptMsg($key_path.$word." not found."); - } -# ::rptMsg(""); -# Attempt to retrieve Excel docs - my $excel = 'Excel\\File MRU'; - if (my $excel_key = $of_key->get_subkey($excel)) { -# ::rptMsg($key_path."\\".$excel); -# ::rptMsg("LastWrite Time ".gmtime($excel_key->get_timestamp())." (UTC)"); - my @vals = $excel_key->get_list_of_values(); - if (scalar(@vals) > 0) { -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my ($d0,$d1) = getWinTS($v->get_data()); - ::rptMsg($d1."|REG|||OfficeDocs2010 - ".$d0); - } - } - else { -# ::rptMsg($key_path.$excel." has no values."); - } - } - else { -# ::rptMsg($key_path.$excel." not found."); - } -# ::rptMsg(""); -# Attempt to retrieve Access docs - my $access = 'Access\\File MRU'; - if (my $access_key = $of_key->get_subkey($access)) { -# ::rptMsg($key_path."\\".$access); -# ::rptMsg("LastWrite Time ".gmtime($access_key->get_timestamp())." (UTC)"); - my @vals = $access_key->get_list_of_values(); - if (scalar(@vals) > 0) { -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my ($d0,$d1) = getWinTS($v->get_data()); - ::rptMsg($d1."|REG|||OfficeDocs2010 - ".$d0); - } - } - else { -# ::rptMsg($key_path."\\".$access." has no values."); - } - } - else { -# ::rptMsg($key_path."\\".$access." not found."); - } -# ::rptMsg(""); -# Attempt to retrieve PowerPoint docs - my $ppt = 'PowerPoint\\File MRU'; - if (my $ppt_key = $of_key->get_subkey($ppt)) { -# ::rptMsg($key_path."\\".$ppt); -# ::rptMsg("LastWrite Time ".gmtime($ppt_key->get_timestamp())." (UTC)"); - my @vals = $ppt_key->get_list_of_values(); - if (scalar(@vals) > 0) { -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my ($d0,$d1) = getWinTS($v->get_data()); - ::rptMsg($d1."|REG|||OfficeDocs2010 - ".$d0); - } - } - else { -# ::rptMsg($key_path."\\".$ppt." has no values."); - } - } - else { -# ::rptMsg($key_path."\\".$ppt." not found."); - } - } - else { -# ::rptMsg("Could not access ".$key_path); - ::logMsg("Could not access ".$key_path); - } - } - else { - ::logMsg("MSOffice version not found."); -# ::rptMsg("MSOffice version not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/oisc.pl b/thirdparty/rr-full/plugins/oisc.pl index c24bae49513..f6eafc25e4f 100644 --- a/thirdparty/rr-full/plugins/oisc.pl +++ b/thirdparty/rr-full/plugins/oisc.pl @@ -3,13 +3,18 @@ # Plugin for Registry Ripper # # Change history +# 20220530 - updated with references +# 20200922 - MITRE update # 20091125 - modified by H. Carvey # 20091110 - created # # References # http://support.microsoft.com/kb/838028 # http://support.microsoft.com/kb/916658 -# +# https://twitter.com/RonnyTNL/status/1435918945349931008 - CVE-2021-40444 +# https://twitter.com/keydet89/status/1531385090026221568 - msdt/Follina +# https://github.com/NVISOsecurity/nviso-cti/blob/master/advisories/29052022%20-%20msdt-0-day.md +# # Derived from the officeDocs plugin # copyright 2008-2009 H. Carvey, mangled 2009 M. Tarnawsky # @@ -23,8 +28,10 @@ package oisc; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20091125); + MITRE => "T1566\.001", + category => "initial access", + output => "report", + version => 20220530); my %prot = (0 => "Read-only HTTP", 1 => "WEC to FPSE-enabled web folder", @@ -52,74 +59,65 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching oisc v.".$VERSION); - ::rptMsg("oisc v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("oisc v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; # First, let's find out which version of Office is installed - my $version; - my $tag = 0; - my @versions = ("7\.0","8\.0", "9\.0", "10\.0", "11\.0","12\.0"); - foreach my $ver (@versions) { - my $key_path = "Software\\Microsoft\\Office\\".$ver."\\Common\\Internet\\Server Cache"; - if (defined($root_key->get_subkey($key_path))) { - $version = $ver; - $tag = 1; + my @version = (); + my $office_version = (); + my $key = (); + + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); } } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } +# ::rptMsg("Office Version: ".$office_version); - if ($tag) { - - my %isc; - - ::rptMsg("MSOffice version ".$version." located."); - my $key_path = "Software\\Microsoft\\Office\\".$version."\\Common\\Internet\\Server Cache"; - my $sc_key; - if ($sc_key = $root_key->get_subkey($key_path)) { + if ($key = $root_key->get_subkey($key_path."\\".$office_version."\\Common\\Internet\\Server Cache")) { + ::rptMsg($key_path."\\".$office_version."\\Common\\Internet\\Server Cache"); # Attempt to retrieve Servers Cache subkeys - my @sc = ($sc_key->get_list_of_subkeys()); - if (scalar(@sc) > 0) { - foreach my $s (@sc) { - my $name = $s->get_name(); - $isc{$name}{lastwrite} = $s->get_timestamp(); - - eval { - my $t = $s->get_value("Type")->get_data(); - (exists $types{$t}) ? ($isc{$name}{type} = $types{$t}) - : ($isc{$name}{type} = $t); - }; - - eval { - my $p = $s->get_value("Protocol")->get_data(); - (exists $prot{$p}) ? ($isc{$name}{protocol} = $prot{$p}) - : ($isc{$name}{protocol} = $p); - }; - - eval { - my @e = unpack("VV",$s->get_value("Expiration")->get_data()); - $isc{$name}{expiry} = ::getTime($e[0],$e[1]); - }; - } + my @subkeys = ($key->get_list_of_subkeys()); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + + eval { + my $expiry = $s->get_value("Expiration")->get_data(); + my ($t0,$t1) = unpack("VV",$expiry); + ::rptMsg("Expiration : ".::format8601Date(::getTime($t0,$t1))."Z"); + }; + + eval { + my $web = $s->get_value("WebURL")->get_data(); + ::rptMsg("WebURL: ".$web) if ($web ne ""); + }; + ::rptMsg(""); - foreach my $i (keys %isc) { - ::rptMsg($i); - ::rptMsg(" LastWrite : ".gmtime($isc{$i}{lastwrite})." UTC"); - ::rptMsg(" Expiry : ".gmtime($isc{$i}{expiry})." UTC"); - ::rptMsg(" Protocol : ".$isc{$i}{protocol}); - ::rptMsg(" Type : ".$isc{$i}{type}); - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); } } else { - ::rptMsg($key_path." not found."); + ::rptMsg($key_path."\\".$office_version."\\Common\\Internet\\Server Cache has no subkeys."); } } else { - ::rptMsg("MSOffice version not found."); + ::rptMsg($key_path."\\".$office_version."\\Common\\Internet\\Server Cache not found."); } } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/olsearch.pl b/thirdparty/rr-full/plugins/olsearch.pl deleted file mode 100644 index 93ff6b12ffd..00000000000 --- a/thirdparty/rr-full/plugins/olsearch.pl +++ /dev/null @@ -1,95 +0,0 @@ -#----------------------------------------------------------- -# olsearch.pl -# Get OutLook search MRU -# -# Change history -# 20130124 - created -# -# References -# -# -# copyright 2013 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package olsearch; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20130124); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's OutLook Searches"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching olsearch v.".$VERSION); - ::rptMsg("olsearch v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles\\Outlook\\0a0d020000000000c000000000000046'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg(""); - my $search; - eval { - $search = $key->get_value("101f0445")->get_data(); - my %items = parseSearchMRU($search); - ::rptMsg(sprintf "%-4s %-45s","No.","Search Term"); - foreach my $i (sort keys %items) { - ::rptMsg(sprintf "%-4s %-45s",$i,$items{$i}); - - } - }; - - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub parseSearchMRU { - my $data = shift; - my $len = length($data); - my %item; - my @ofs = (); - - my $num = unpack("V",substr($data,0,4)); - -# Ugly kludge to check for 64-bit OutLook; this is ugly b/c it -# won't work if the data is really, really huge...enough to require -# 8 bytes to store the offset to the string - if (unpack("V",substr($data,8,4)) == 0) { - my @o = unpack("V*",substr($data,4,4 * ($num * 2))); - foreach my $i (0..(scalar(@o) - 1)) { - push(@ofs,$o[$i]) if (($i % 2) == 0); - } - } - else { - @ofs = unpack("V*",substr($data,4,4 * $num)); - } - push(@ofs,$len); - - foreach my $i (0..($num - 1)) { - $item{$i} = substr($data,$ofs[$i], $ofs[$i + 1] - $ofs[$i]); - $item{$i} =~ s/\x00//g; - } - return %item; -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/onedrive.pl b/thirdparty/rr-full/plugins/onedrive.pl new file mode 100644 index 00000000000..d407c93caab --- /dev/null +++ b/thirdparty/rr-full/plugins/onedrive.pl @@ -0,0 +1,85 @@ +#----------------------------------------------------------- +# onedrive.pl +# +# +# Change history +# 20200922 - MITRE update +# 20200515 - updated date output format +# 20190823 - created +# +# References +# https://attack.mitre.org/techniques/T1567/002/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package onedrive; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1567\.002", + category => "user activity", + output => "report", + version => 20200922); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets contents of user's OneDrive key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching onedrive v.".$VERSION); + ::rptMsg("onedrive v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = "Software\\Microsoft\\OneDrive"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + + eval { + ::rptMsg("UserCID : ".$key->get_subkey("Accounts\\Personal")->get_value("UserCID")->get_data()); + }; + + eval { + ::rptMsg("UserFolder : ".$key->get_subkey("Accounts\\Personal")->get_value("UserFolder")->get_data()); + }; + + eval { + my $t = $key->get_subkey("Accounts\\Personal")->get_value("ClientFirstSignInTimestamp")->get_data(); + my $s = unpack("Vx4",$t); + ::rptMsg("ClientFirstSignInTimestamp: ".::format8601Date($s)."Z"); + }; + + eval { + my $t = $key->get_subkey("Accounts\\Personal")->get_value("NextOneRmUpdateTime")->get_data(); + my $s = unpack("Vx4",$t); + ::rptMsg("NextOneRmUpdateTime : ".::format8601Date($s)."Z"); + }; + + eval { + my $t = $key->get_subkey("Accounts\\Personal")->get_value("NextMigrationScan")->get_data(); + my $s = unpack("Vx4",$t); + ::rptMsg("NextMigrationScan : ".::format8601Date($s)."Z"); + }; + + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/onedrive_tln.pl b/thirdparty/rr-full/plugins/onedrive_tln.pl new file mode 100644 index 00000000000..c20efca0b1a --- /dev/null +++ b/thirdparty/rr-full/plugins/onedrive_tln.pl @@ -0,0 +1,80 @@ +#----------------------------------------------------------- +# onedrive_tln.pl +# +# +# Change history +# 20200922 - MITRE update +# 20190823 - created +# +# References +# https://attack.mitre.org/techniques/T1567/002/ +# +# +# copyright 2019 Quantum Analytics Research, LLC +#----------------------------------------------------------- +package onedrive_tln; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1567\.002", + category => "user activity", + output => "tln", + version => 20200922); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets contents of user's OneDrive key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; +# ::logMsg("Launching onedrivev.".$VERSION); +# ::rptMsg("onedrive v.".$VERSION); +# ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = "Software\\Microsoft\\OneDrive"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { +# ::rptMsg($key_path); + + my $user = (); + eval { + $user = $key->get_subkey("Accounts\\Personal")->get_value("UserCID")->get_data(); + }; + + eval { + my $t = $key->get_subkey("Accounts\\Personal")->get_value("ClientFirstSignInTimestamp")->get_data(); + my $s = unpack("Vx4",$t); + ::rptMsg($s."|REG|||".$user." OneDrive - ClientFirstSignInTimestamp"); + }; + + eval { + my $t = $key->get_subkey("Accounts\\Personal")->get_value("NextOneRmUpdateTime")->get_data(); + my $s = unpack("Vx4",$t); + ::rptMsg($s."|REG|||".$user." OneDrive - NextOneRmUpdateTime"); + }; + + eval { + my $t = $key->get_subkey("Accounts\\Personal")->get_value("NextMigrationScan")->get_data(); + my $s = unpack("Vx4",$t); + ::rptMsg($s."|REG|||".$user." OneDrive - NextMigrationScan"); + }; + } + else { +# ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/onenote.pl b/thirdparty/rr-full/plugins/onenote.pl new file mode 100644 index 00000000000..d3e42c2d690 --- /dev/null +++ b/thirdparty/rr-full/plugins/onenote.pl @@ -0,0 +1,120 @@ +#----------------------------------------------------------- +# onenote.pl +# +# +# Change history +# 20230306 - created +# +# References +# https://www.bleepingcomputer.com/news/security/how-to-prevent-microsoft-onenote-files-from-infecting-windows-with-malware/ +# https://labs.withsecure.com/publications/detecting-onenote-abuse +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package onenote; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "user activity", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20230306); + +sub getConfig{return %config} +sub getShortDescr { + return "Check OneNote settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $office_version; + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching onenote v.".$VERSION); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg("onenote v.".$VERSION); + ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + + if ($office_version ne "") { + + if ($key->get_subkey("SOFTWARE\\Policies\\Microsoft\\Office\\".$office_version."\\Onenote\\Options")) { +# https://admx.help/?Category=Office2007&Policy=onent12.Office.Microsoft.Policies.Windows::L_Disableembeddedfiles + eval { + my $e = $key->get_value("DisableEmbeddedFiles")->get_data(); + ::rptMsg("DisableEmbeddedFiles value: ".$e); + ::rptMsg(""); + ::rptMsg("Analysis Tip: This value disables the ability to embed files within a OneNote file."); + ::rptMsg("1 - Embedding files disabled"); + ::rptMsg("0 - Embedding files enabled (default)"); + ::rptMsg(""); + }; + + } + else { + ::rptMsg("SOFTWARE\\Policies\\Microsoft\\Office\\".$office_version."\\Onenote\\Options key not found"); + } + + if ($key->get_subkey("SOFTWARE\\Policies\\Microsoft\\Office\\".$office_version."\\Onenote\\Options\\EmbeddedFileOpenOptions")) { + +# https://labs.withsecure.com/publications/detecting-onenote-abuse + eval { + my $e = $key->get_value("EmbeddedFileOpenWarningDisabled")->get_data(); + ::rptMsg("EmbeddedFileOpenWarningDisabled value: ".$e); + ::rptMsg(""); + ::rptMsg("Analysis Tip: This value may be set to \"1\" if the user clicked the \"Don't show me this again\" checkbox in the"); + ::rptMsg("Warning dialog box when opening attachments\."); + ::rptMsg(""); + }; + +# https://admx.help/?Category=Office2007&Policy=onent12.Office.Microsoft.Policies.Windows::L_EmbeddedFilesBlockedExtensions + eval { + my $b = $key->get_value("BlockedExtensions")->get_data(); + ::rptMsg("BlockedExtensions value: ".$b); + ::rptMsg(""); + ::rptMsg("Analysis Tip: The BlockedExtensions value provides a list of file extensions that should be blocked"); + ::rptMsg("if they're embedded within the OneNote file."); + ::rptMsg(""); + }; + + } + else { + ::rptMsg("SOFTWARE\\Policies\\Microsoft\\Office\\".$office_version."\\Onenote\\Options\\EmbeddedFileOpenOptions key not found."); + } + } + else { + ::rptMsg("MS Office does not appear to be installed on this system; the Office version could not be determined."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/opencandy.pl b/thirdparty/rr-full/plugins/opencandy.pl deleted file mode 100644 index 628cfabde81..00000000000 --- a/thirdparty/rr-full/plugins/opencandy.pl +++ /dev/null @@ -1,77 +0,0 @@ -#----------------------------------------------------------- -# opencandy.pl - plugin to detect possible presence of OpenCandy adware -# -# Change history -# 20131008 - created -# -# References -# http://www.microsoft.com/security/portal/threat/encyclopedia/Entry.aspx?Name=Adware%3AWin32%2FOpenCandy#tab=2 -# -# Copyright (c) 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -# Require # -package opencandy; -use strict; - -# Declarations # -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 1, - hasRefs => 1, - osmask => 22, - category => "malware", - version => 20131008); -my $VERSION = getVersion(); - -# Functions # -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getDescr {} -sub getShortDescr { - return "Detect possible presence of OpenCandy adware"; -} -sub getRefs {} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching opencandy v.".$VERSION); - ::rptMsg("opencandy v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my @count = (); - - my @paths = ("ADatumCorporation\\OpenCandy", - "Wow6432Node\\ADatumCorporation\\OpenCandy"); - - foreach my $key_path (@paths) { - if ($key = $root_key->get_subkey($key_path)) { - push(@count,$key_path); - } - } - - if (scalar(@count) > 0) { - ::rptMsg("Possible OpenCandy infection detected\."); - foreach (@count) { - ::rptMsg(" Key: ".$_); - } - ::rptMsg(""); - ::rptMsg("See: http://www.microsoft.com/security/portal/threat/encyclopedia/Entry.aspx?Name=Adware%3AWin32%2FOpenCandy#tab=2"); - } - else { - ::rptMsg("Indicators not found\."); - } -} - -1; diff --git a/thirdparty/rr-full/plugins/osversion.pl b/thirdparty/rr-full/plugins/osversion.pl index 9861ccc1cc8..cdb7d0a3672 100644 --- a/thirdparty/rr-full/plugins/osversion.pl +++ b/thirdparty/rr-full/plugins/osversion.pl @@ -5,13 +5,15 @@ # not found" is a good thing. # # Change history +# 20200921 - MITRE update +# 20200511 - updated date output format # 20120601 - created # # References # Search Google for "Software\Microsoft\OSVersion" - you'll get several # hits that refer to various malware; # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package osversion; @@ -21,8 +23,10 @@ package osversion; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120601); + MITRE => "", + category => "config", + output => "report", + version => 20200921); sub getConfig{return %config} sub getShortDescr { @@ -47,7 +51,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("OSVersion"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my $os; eval { diff --git a/thirdparty/rr-full/plugins/osversion_tln.pl b/thirdparty/rr-full/plugins/osversion_tln.pl index 3e3ee39e178..dfdb4355f25 100644 --- a/thirdparty/rr-full/plugins/osversion_tln.pl +++ b/thirdparty/rr-full/plugins/osversion_tln.pl @@ -5,6 +5,7 @@ # not found" is a good thing. # # Change history +# 20200921 - MITRE update # 20120608 - created # # References @@ -21,8 +22,10 @@ package osversion_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120608); + MITRE => "", + output => "tln", + category => "config", + version => 20200921); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/outlook.pl b/thirdparty/rr-full/plugins/outlook.pl deleted file mode 100644 index fc613edd1f3..00000000000 --- a/thirdparty/rr-full/plugins/outlook.pl +++ /dev/null @@ -1,187 +0,0 @@ -#----------------------------------------------------------- -# outlook.pl -# **Very Beta! Based on one sample hive file only! -# -# Change history -# 20100218 - created -# -# References -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package outlook; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100218); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets user's Outlook settings"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - my %hist; - ::logMsg("Launching outlook v.".$VERSION); - ::rptMsg("outlook v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my @subkeys = $key->get_list_of_subkeys(); - if (scalar @subkeys > 0) { - ::rptMsg(""); - foreach my $s (@subkeys) { - - my $profile = $s->get_name(); - ::rptMsg($profile." Profile"); - -# AutoArchive settings -# http://support.microsoft.com/kb/198479 - eval { - my $data = $s->get_subkey("0a0d020000000000c000000000000046")->get_value("001f0324")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" Outlook 2007 AutoArchive path -> ".$data); - }; - - eval { - my $data = $s->get_subkey("0a0d020000000000c000000000000046")->get_value("001e0324")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" Outlook 2003 AutoArchive path -> ".$data); - }; - - eval { - my $data = $s->get_subkey("0a0d020000000000c000000000000046")->get_value("001e032c")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" Outlook 2003 AutoArchive path (alt) -> ".$data); - }; - -# http://support.microsoft.com/kb/288570 - eval { - my $data = $s->get_subkey("0a0d020000000000c000000000000046")->get_value("101e0384")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" Open Other Users MRU (Outlook 97) -> ".$data); - }; - - eval { - my $data = $s->get_subkey("0a0d020000000000c000000000000046")->get_value("101f0390")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" Open Other Users MRU (Outlook 2003) -> ".$data); - }; - - - - eval { - my $data = unpack("V",$s->get_subkey("13dbb0c8aa05101a9bb000aa002fc45a")->get_value("00036601")->get_data()); - my $str; - if ($data == 4) { - $str = " Cached Exchange Mode disabled."; - } - elsif ($data == 4484) { - $str = " Cached Exchange Mode enabled."; - } - else { - $str = sprintf " Cached Exchange Mode: 0x%x",$data; - } - ::rptMsg($str); - }; - - eval { - my $data = $s->get_subkey("13dbb0c8aa05101a9bb000aa002fc45a")->get_value("001f6610")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" Path to OST file: ".$data); - }; - - eval { - my $data = $s->get_subkey("13dbb0c8aa05101a9bb000aa002fc45a")->get_value("001f6607")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" Email: ".$data); - }; - - eval { - my $data = $s->get_subkey("13dbb0c8aa05101a9bb000aa002fc45a")->get_value("001f6620")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" Email: ".$data); - }; - -# http://support.microsoft.com/kb/959956 -# eval { -# my $data = $s->get_subkey("13dbb0c8aa05101a9bb000aa002fc45a")->get_value("01026687")->get_data(); -# $data =~ s/\x00/\./g; -# $data =~ s/\W//g; -# ::rptMsg(" Non-SMTP Email: ".$data); -# }; - - - - - - - - - - - - - - - eval { - my $data = $s->get_subkey("0a0d020000000000c000000000000046")->get_value("001e032c")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" Outlook 2003 AutoArchive path (alt) -> ".$data); - }; - - - - - - - eval { - my $data = $s->get_subkey("0a0d020000000000c000000000000046")->get_value("001f0418")->get_data(); - $data =~ s/\x00//g; - ::rptMsg(" 001f0418 -> ".$data); - }; -# ::rptMsg("Error : ".$@) if ($@); - - -# Account Names and signatures -# http://support.microsoft.com/kb/938360 - my @subkeys = $s->get_subkey("9375CFF0413111d3B88A00104B2A6676")->get_list_of_subkeys(); - if (scalar @subkeys > 0) { - - foreach my $s2 (@subkeys) { - eval { - - - }; - } - } - - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/outlook2.pl b/thirdparty/rr-full/plugins/outlook2.pl deleted file mode 100644 index ec7b6515162..00000000000 --- a/thirdparty/rr-full/plugins/outlook2.pl +++ /dev/null @@ -1,234 +0,0 @@ -#------------------------------------------------------------------------------ -# outlook2.pl -# A step in the swampy MAPI -# Plugin for RegRipper -# * BETA open to suggestions and corrections * -# -# Change history -# 20130308 created -# -# References -# [1] http://www.windowsitpro.com/article/registry2/inside-mapi-profiles-45347 -# [2] http://msdn.microsoft.com/en-us/library/ms526356(v=exchg.10).aspx -# -# Todo -# 001f6700 PST -# 001f6610 OST -# -# copyright 2013 Realitynet System Solutions snc -# author: francesco picasso -#------------------------------------------------------------------------------ -package outlook2; -use strict; - -use Parse::Win32Registry qw( unpack_windows_time - unpack_unicode_string - unpack_sid - unpack_ace - unpack_acl - unpack_security_descriptor ); - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20130308); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets MAPI (Outlook) settings *BETA*"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my %outlook_subkeys; - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching outlook2 v.".$VERSION); - ::rptMsg("outlook2 v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); - - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $tab; - my $key; - my $key_path; - my $outlook_key_path = 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles\\Outlook'; - my $accounts_key_name = '9375CFF0413111d3B88A00104B2A6676'; - ::rptMsg("Working path is '$key_path'"); - ::rptMsg(""); - - $key = $root_key->get_subkey($outlook_key_path); - if (!$key) { ::rptMsg("Outlook key not found"); return; } - my @subkeys = $key->get_list_of_subkeys(); - foreach my $s (@subkeys) { $outlook_subkeys{$s->get_name()} = $s; } - - # Accessing ACCOUNTS - # "Another well-known GUID is 9375CFF0413111d3B88A00104B2A6676, which is - # used to hold details about all the accounts that are in use within the - # profile. Under this subkey, you will find a subkey per account. - # For example, you'll typically find a subkey relating to the Outlook - # Address Book (OAB) account, the Exchange account, an account for each PST - # file that's been added to the profile, and any POP3/IMAP mail accounts - # that are defined within the profile." Ref[1] - $key_path = $outlook_key_path.'\\'.$accounts_key_name; - $key = $root_key->get_subkey($key_path); - if (!$key) { ::rptMsg("Accounts key '$accounts_key_name' not found"); return; } - ::rptMsg("__key_ $accounts_key_name"); - ::rptMsg("_time_ ".gmtime($key->get_timestamp())); - ::rptMsg("_desc_ accounts used within the profile"); - ::rptMsg(""); - - my @accounts_keys = $key->get_list_of_subkeys(); - foreach my $account_key (@accounts_keys) - { - $tab = ' '; - ::rptMsg($tab.'-----------------------------------'); - ::rptMsg($tab.$account_key->get_name()." [".gmtime($account_key->get_timestamp())."]"); - ::rptMsg($tab.'-----------------------------------'); - ::rptMsg($tab.get_unicode_string($account_key, 'Account Name')); - ::rptMsg($tab.get_dword_string_long($account_key, 'MAPI provider')); - ::rptMsg($tab.get_dword_string($account_key, 'Mini UID')); - ::rptMsg($tab.get_unicode_string($account_key, 'Service Name')); - ::rptMsg($tab.get_hex_string($account_key, 'Service UID')); - - my $service_id_key_name = $account_key->get_value('Service UID'); - if (!$service_id_key_name) { ::rptMsg(""); next; } - - ::rptMsg($tab.'\\'); - $tab = ' '; - parse_service($root_key, $outlook_key_path, $service_id_key_name, $tab); - $tab = ' '; - ::rptMsg($tab.'/'); - - ::rptMsg($tab.get_dword_string($account_key, 'XP Status')); - ::rptMsg($tab.get_hex_string($account_key, 'XP Provider UID')); - - my $xp_id_key_name = $account_key->get_value('XP Provider UID'); - if (!$xp_id_key_name) { ::rptMsg(""); next; } - ::rptMsg($tab.'\\'); - $tab = ' '; - parse_service($root_key, $outlook_key_path, $xp_id_key_name, $tab, 1); - $tab = ' '; - ::rptMsg($tab.'/'); - - ::rptMsg(""); - } - $tab = ''; - ::rptMsg(""); - ::rptMsg("Outlook subkeys not direclty linked to accounts"); - foreach my $okey_name (keys %outlook_subkeys) - { - ::rptMsg($tab."$okey_name"); - } -} - -sub parse_service -{ - my $root_key = shift; - my $outlook_key_path = shift; - my $ids = shift; - my $tab = shift; - my $xp_type = shift; - - $ids = $ids->get_raw_data(); - my $num_of_ids = length($ids) / 16; - for (my $i = 0; $i < $num_of_ids; $i += 1) - { - my $service_id_key_name = join('', unpack('(H2)16', $ids)); - $ids = substr($ids, 16); - my $service_id_key = $root_key->get_subkey($outlook_key_path.'\\'.$service_id_key_name); - if (!$service_id_key) - { - ::rptMsg($tab.'Service UID not found in Outlook path!'); - if (($i+1) != $num_of_ids) { ::rptMsg($tab.'+'); } - next; - } - ::rptMsg($tab.$service_id_key_name.' ['.gmtime($service_id_key->get_timestamp()).']'); - ::rptMsg($tab.'--------------------------------'); - - delete($outlook_subkeys{$service_id_key_name}); - - if ($xp_type) - { - ::rptMsg($tab.get_ascii_string($service_id_key, '001e660b', 'User')); - ::rptMsg($tab.get_ascii_string($service_id_key, '001e6614', 'Server')); - ::rptMsg($tab.get_ascii_string($service_id_key, '001e660c', 'Server Name')); - ::rptMsg($tab.get_unicode_string($service_id_key, '001f662b', 'Server Domain(?)')); - ::rptMsg($tab.get_unicode_string($service_id_key, '001f3001', 'Display Name')); - ::rptMsg($tab.get_unicode_string($service_id_key, '001f3006', 'Provider Display')); - ::rptMsg($tab.get_unicode_string($service_id_key, '001f300a', 'Provider DLL Name')); - } - else - { - ::rptMsg($tab.get_unicode_string($service_id_key, '001f3001', 'Display Name')); - ::rptMsg($tab.get_unicode_string($service_id_key, '001f3d0a', 'Service DLL Name')); - ::rptMsg($tab.get_unicode_string($service_id_key, '001f3d0b', 'Service Entry')); - } - - if (($i+1) != $num_of_ids) { ::rptMsg($tab.'+'); } - } -} - -sub get_hex_string -{ - my $key = shift; - my $value = shift; - my $data = $key->get_value($value); - if ($data) { $data = join('', unpack('(H2)*', $data->get_raw_data()));} - else { $data = ''; } - return sprintf("%-20s %s", $value.':', $data); -} - -sub get_dword_string -{ - my $key = shift; - my $value = shift; - my $data = $key->get_value($value); - if ($data) { $data = $data->get_data(); $data = sprintf('0x%08X', $data); } - else { $data = ''; } - return sprintf("%-20s %s", $value.':', $data); -} - -sub get_dword_string_long -{ - my $key = shift; - my $value = shift; - my $data = $key->get_value($value); - if ($data) { $data = $data->get_data(); $data = sprintf('%u [0x%08X]', $data, $data); } - else { $data = ''; } - return sprintf("%-20s %s", $value.':', $data); -} - -sub get_unicode_string -{ - my $key = shift; - my $value = shift; - my $value_desc = shift; - my $data = $key->get_value($value); - if ($data) { $data = unpack_unicode_string($data->get_data()); } - else { $data = ''; } - if (!$value_desc) { return sprintf("%-20s %s", $value.':', $data); } - return sprintf("%s %-20s %s", $value, '['.$value_desc.']:', $data); -} - -sub get_ascii_string -{ - my $key = shift; - my $value = shift; - my $value_desc = shift; - my $data = $key->get_value($value); - if ($data) { $data = $data->get_data(); } else { $data = ''; } - if (!$value_desc) { return sprintf("%-20s %s", $value.':', $data); } - return sprintf("%s %-20s %s", $value, '['.$value_desc.']:', $data); -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/outlook_attach.pl b/thirdparty/rr-full/plugins/outlook_attach.pl new file mode 100644 index 00000000000..3ba2895f396 --- /dev/null +++ b/thirdparty/rr-full/plugins/outlook_attach.pl @@ -0,0 +1,99 @@ +#----------------------------------------------------------- +# outlook_attach.pl +# List Office documents for which the user explicitly opted to accept bypassing +# the default security settings for the application +# +# Change history +# 20210504 - created +# +# References +# https://support.microsoft.com/en-us/topic/outlook-blocked-access-to-the-following-potentially-unsafe-attachments-c5c4a480-041e-2466-667f-e98d389ff822 +# https://www.slipstick.com/outlook/block-additional-attachment-types/ +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package outlook_attach; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "execution", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1204\.002", + output => "report", + version => 20210504); + +sub getConfig{return %config} +sub getShortDescr { + return "Get user's MSOffice content"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $office_version; + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching outlook_attach v.".$VERSION); + ::rptMsg("outlook_attach v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg("outlook_attach v.".$VERSION); + ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + + my $key_path = "Software\\Microsoft\\Office\\".$office_version."\\Outlook\\Security"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + eval { + my $l = $key->get_value("Level1Remove")->get_data(); + ::rptMsg("Level1Remove value : ".$l); + }; + + eval { + my $l = $key->get_value("Level1Add")->get_data(); + ::rptMsg("Level1Add value : ".$l); + }; + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Leve1Remove & Level1Add values control how Outlook attachments are treated, by extension."); + ::rptMsg("Level1Remove - Outlook issues a warning, allowing the user to save the file before launching"); + ::rptMsg("Level1Add - Completely block access to files with the extension"); + ::rptMsg(""); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/outlookhomepage.pl b/thirdparty/rr-full/plugins/outlookhomepage.pl new file mode 100644 index 00000000000..a86a15b4d97 --- /dev/null +++ b/thirdparty/rr-full/plugins/outlookhomepage.pl @@ -0,0 +1,151 @@ +#----------------------------------------------------------- +# outlookhomepage.pl +# +# Change history +# 20201103 - updated with analysis tips +# 20201102 - created +# +# References +# https://www.fireeye.com/blog/threat-research/2019/12/breaking-the-rules-tough-outlook-for-home-page-attacks.html +# https://docs.microsoft.com/en-us/microsoft-365/security/office-365-security/detect-and-remediate-outlook-rules-forms-attack?view=o365-worldwide +# https://attack.mitre.org/techniques/T1137/004/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +# *based on a plugin written and contributed by Mr. Hobbits +#----------------------------------------------------------- +package outlookhomepage; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1137\.004", + version => 20201103); + +sub getConfig{return %config} +sub getShortDescr { + return "Get Outlook WebView Homepage settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $office_version; + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching outlookhomepage v.".$VERSION); + ::rptMsg("outlookhomepage v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg("outlookhomepage v.".$VERSION); + ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + +# First, let's check the URL values for the various WebView subkeys + my $flag = 0; + my $key_path = "Software\\Microsoft\\Office\\".$office_version."\\Outlook\\WebView"; +# https://support.microsoft.com/en-us/office/outlook-home-page-feature-is-missing-in-folder-properties-d207edb7-aa02-46c5-b608-5d9dbed9bd04 + my @views = ("Inbox","Calendar","Contacts","Deleted Items","Drafts","Journal","Junk E-mail","Notes","Outbox", + "RSS","Sent Mail","Tasks"); + + foreach my $v (@views) { + if ($key = $root_key->get_subkey($key_path."\\".$v)) { + ::rptMsg($key_path."\\".$v); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + eval { + my $url = $key->get_value("URL")->get_data(); + ::rptMsg("URL value = ".$url); + $flag = 1; + }; + + } + else { +# ::rptMsg($key_path."\\".$v." not found."); + } +# ::rptMsg(""); + } + if ($flag) { + ::rptMsg("Analysis Tip: Outlook WebView homepages, particularly Inbox and Calendar, have been used to maintain persistence by"); + ::rptMsg("pointing to pages with malicious code embedded. Look for unusual or suspicious URLs. This technique rolls back the"); + ::rptMsg("CVE-2017-11774 patch."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.fireeye.com/blog/threat-research/2019/12/breaking-the-rules-tough-outlook-for-home-page-attacks.html"); + ::rptMsg(""); + } + +# check UserDefinedURL value + my $key_path = "Software\\Microsoft\\Office\\".$office_version."\\Outlook\\Today"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $u = $key->get_value("UserDefinedUrl")->get_data(); + ::rptMsg("UserDefinedUrl value = ".$u); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Pointing this value to a malicious web page has been used by actors to maintain persistence."); + ::rptMsg("Look for unusual values."); + ::rptMsg(""); + }; + } + else { +# ::rptMsg($key_path." not found."); + } + +# check Security values + my $key_path = "Software\\Microsoft\\Office\\".$office_version."\\Outlook\\Security"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-40s %-10s",$v->get_name(),$v->get_data()); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: When set to 1, several values serve to roll-back the CVE-2017-11774 patch and expose unsafe options."); + ::rptMsg(""); + ::rptMsg("EnableRoamingFolderHomepages = 1: Exposes unsafe options in Outlook, re-enabling the original home page tab and "); + ::rptMsg(" roaming home page behavior in the Outlook UI."); + ::rptMsg("NonDefaultStoreScript = 1: Allow for folders within non-default mailboxes to leverage a custom home page."); + ::rptMsg("EnableUnsafeClientMailRules = 1: Allows for \“Run as a Script\” and \“Start Application\” rules to be re-enabled"); + ::rptMsg(""); + } + } + else { +# ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/outlookmacro.pl b/thirdparty/rr-full/plugins/outlookmacro.pl new file mode 100644 index 00000000000..09b0b7500f4 --- /dev/null +++ b/thirdparty/rr-full/plugins/outlookmacro.pl @@ -0,0 +1,121 @@ +#----------------------------------------------------------- +# outlookmacro.pl +# Check +# +# Change history +# 20201212 - created +# +# References +# https://www.linkedin.com/pulse/outlook-backdoor-using-vba-samir-b-/ +# https://www.cybereason.com/hubfs/Cybereason%20Labs%20Analysis%20Operation%20Cobalt%20Kitty-Part2.pdf +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package outlookmacro; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + output => "report", + version => 20201212); + +sub getConfig{return %config} +sub getShortDescr { + return "Get LoadMacroProviderOnBoot value data"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getMitre {return $config{MITRE};} + +my $VERSION = getVersion(); +my $office_version; + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching outlookmacro v.".$VERSION); + ::rptMsg("outlookmacro v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg("outlookmacro v.".$VERSION); + ::rptMsg("MITRE ATT&CK subtechnique ".getMitre()); + ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + +# Check for LoadMacroProviderOnBoot value + eval { + if (my $id = $key->get_subkey($office_version."\\Outlook")) { + my $lw = $id->get_timestamp(); + my $rw = $id->get_value("LoadMacroProviderOnBoot")->get_data(); + ::rptMsg("Software\\Microsoft\\Office\\".$office_version."\\Outlook"); + ::rptMsg("LastWrite time: ".::format8601Date($lw)."Z"); + ::rptMsg("LoadMacroProviderOnBoot value = ".$rw); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the \"LoadMacroProviderOnBoot\" value is set to \"1\", any configured VBA project or module"); + ::rptMsg("will be loaded\. Check the contents of the VbaProject\.OTM file\. This technique was observed being used by "); + ::rptMsg("Cobalt Kitty\."); + } + }; + +# Check Security Level + eval { + if (my $id = $key->get_subkey($office_version."\\Outlook\\Security")) { + my $lw = $id->get_timestamp(); + my $rw = $id->get_value("Level")->get_data(); + ::rptMsg("Software\\Microsoft\\Office\\".$office_version."\\Outlook\\Security"); + ::rptMsg("LastWrite time: ".::format8601Date($lw)."Z"); + ::rptMsg("Level value = ".$rw); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the \"Level\" value is set to \"1\", execution of VBA projects is unrestricted."); + ::rptMsg("Ref: https://admx.help/?Category=Office2016&Policy=outlk16.Office.Microsoft.Policies.Windows::L_SecurityLevelOutlook"); + } + }; + +# Check Security Level, set via GPO +# https://getadmx.com/HKCU/software/policies/microsoft/office/16.0/outlook/security + my $gpo_path = "Software\\Policies\\Microsoft\\Office\\".$office_version."\\Outlook\\Security"; + eval { + if (my $id = $key->get_subkey($gpo_path)) { + my $lw = $id->get_timestamp(); + my $rw = $id->get_value("Level")->get_data(); + ::rptMsg("Software\\Policies\\Microsoft\\Office\\".$office_version."\\Outlook\\Security"); + ::rptMsg("LastWrite time: ".::format8601Date($lw)."Z"); + ::rptMsg("Level value = ".$rw); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the \"Level\" value is set to \"1\", execution of VBA projects is unrestricted, set via GPO."); + ::rptMsg("Ref: https://admx.help/?Category=Office2016&Policy=outlk16.Office.Microsoft.Policies.Windows::L_SecurityLevelOutlook"); + } + }; +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/pagefile.pl b/thirdparty/rr-full/plugins/pagefile.pl index c506974eb8d..611b2717cb7 100644 --- a/thirdparty/rr-full/plugins/pagefile.pl +++ b/thirdparty/rr-full/plugins/pagefile.pl @@ -4,24 +4,27 @@ # # # History: +# 20200921 - MITRE update # 20140505 - updated by Corey Harrell # 20081212 - created by H. Carvey, keydet89@yahoo.com # # Ref: # http://support.microsoft.com/kb/314834 - ClearPagefileAtShutdown # -# copyright 2014 Corey Harrell (jIIr) http://journeyintoir.blogspot.com/ -# Corey Harrell +# copyright 2020 QAR, LLC +# #----------------------------------------------------------- package pagefile; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20140505); + output => "report", + version => 20200921); sub getConfig{return %config} @@ -39,8 +42,8 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching pagefile v.".$VERSION); - ::rptMsg("pagefile v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("pagefile v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -49,26 +52,26 @@ sub pluginmain { my $key_path = 'Select'; my $key; if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); + $current = ::getCCS($root_key); - my $mm_path = "ControlSet00".$current."\\Control\\Session Manager\\Memory Management"; + my $mm_path = $current."\\Control\\Session Manager\\Memory Management"; my $mm; if ($mm = $root_key->get_subkey($mm_path)) { eval { my $files = $mm->get_value("PagingFiles")->get_data(); - ::rptMsg("PagingFiles = ".$files); + ::rptMsg("PagingFiles = ".$files); }; ::rptMsg($@) if ($@); eval { - my $cpf = $mm->get_value("ClearPageFileAtShutdown")->get_data(); - ::rptMsg("ClearPageFileAtShutdown = ".$cpf); + my $e = $mm->get_value("ExistingPageFiles")->get_data(); + ::rptMsg("ExistingPageFiles = ".$e); }; eval { - my $cpf = $mm->get_value("PagingFiles")->get_data(); - ::rptMsg("PagingFiles = ".$cpf); + my $cpf = $mm->get_value("ClearPageFileAtShutdown")->get_data(); + ::rptMsg("ClearPageFileAtShutdown = ".$cpf); }; } diff --git a/thirdparty/rr-full/plugins/pending.pl b/thirdparty/rr-full/plugins/pending.pl index 2253c045e16..716a9fcc861 100644 --- a/thirdparty/rr-full/plugins/pending.pl +++ b/thirdparty/rr-full/plugins/pending.pl @@ -2,28 +2,30 @@ # pending.pl # # History: +# 20230510 - added reference +# 20200922 - MITRE update # 20130711 - created # # References: # http://technet.microsoft.com/en-us/library/cc960241.aspx +# https://github.com/gtworek/PSBits/blob/master/Misc/PendingFileRenameOperations.cmd # # # -# copyright 2013 Quantum Analytics Research, LLC +# copyright 2023 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package pending; use strict; my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "System Activity", + output => "report", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20130711); + MITRE => "T1547", + version => 20230510); sub getConfig{return %config} sub getShortDescr { @@ -41,8 +43,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching pending v.".$VERSION); - ::rptMsg("pending v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("pending v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -60,8 +64,12 @@ sub pluginmain { eval { my $pend = $sm->get_value("PendingFileRenameOperations")->get_value(); - ::rptMsg($pend); + ::rptMsg(""); + ::rptMsg("Analysis Tip: While the Registry value is intended to record files to be renamed or deleted, it can also be "); + ::rptMsg("used as a persistence mechanism."); + ::rptMsg(""); + ::rptMsg("Ref: https://github.com/gtworek/PSBits/blob/master/Misc/PendingFileRenameOperations.cmd"); }; if ($@) { ::rptMsg("PendingFileRenameOperations value not found\."); diff --git a/thirdparty/rr-full/plugins/pendinggpos.pl b/thirdparty/rr-full/plugins/pendinggpos.pl new file mode 100644 index 00000000000..8c8a0d0bf20 --- /dev/null +++ b/thirdparty/rr-full/plugins/pendinggpos.pl @@ -0,0 +1,73 @@ +#----------------------------------------------------------- +# pendinggpos.pl +# +# +# Change history +# 20200922 - MITRE update +# 20200427 - updated output date format +# 20191020 - created +# +# References +# https://forums.juniper.net/t5/Threat-Research/New-Gootkit-Banking-Trojan-variant-pushes-the-limits-on-evasive/ba-p/319055 +# +# copyright 2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package pendinggpos; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + category => "persistence", + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1547", + version => 20200922); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets contents of user's PendingGPOs key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching pendinggpos v.".$VERSION); + ::rptMsg("pendinggpos v.".$VERSION); + ::rptMsg(getHive()." - ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Software\\Microsoft\\IEAK\\GroupPolicy\\PendingGPOs'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-30s %-10s",$v->get_name(),$v->get_data()); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: This is a persistence technique observed with Gootkit; look for suspicious values."); + ::rptMsg("https://blogs.juniper.net/en-us/threat-research/new-gootkit-banking-trojan-variant-pushes-the-limits-on-evasive-behavior"); + } + else { + ::rptMsg($key_path." has no values."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/perf.pl b/thirdparty/rr-full/plugins/perf.pl new file mode 100644 index 00000000000..6614d4b82c2 --- /dev/null +++ b/thirdparty/rr-full/plugins/perf.pl @@ -0,0 +1,84 @@ +#----------------------------------------------------------- +# perf.pl +# +# History: +# 20201130 - created +# +# References: +# https://itm4n.github.io/windows-registry-rpceptmapper-eop/ +# +# https://attack.mitre.org/techniques/T1543/003/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package perf; +use strict; + +my %config = (hive => "System", + category => "privilege escalation", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1543\.003", + version => 20201130); + +sub getConfig{return %config} +sub getShortDescr { + return "Get EnablePeriodicBackup value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching perf v.".$VERSION); + ::rptMsg("perf v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my @svcs = ("RpcEptMapper","Dnscache"); + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services"; + my $key = (); + if ($key = $root_key->get_subkey($key_path)) { + + foreach my $svc (@svcs) { + my $perf = (); + if ($perf = $key->get_subkey($svc."\\Performance")) { + ::rptMsg("LastWrite time: ".::format8601Date($perf->get_timestamp())."Z"); + + my @vals = ("Library", "Open", "Collect", "Close"); + foreach my $val (@vals) { + eval { + my $data = $perf->get_value($val)->get_data(); + ::rptMsg(sprintf "%-12s %-25s",$val,$data); + }; + } + ::rptMsg(""); + } + else { + ::rptMsg("Services\\".$svc."\\Performance subkey not found."); + } + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: This privilege escalation issue is specific to Win7 & Win2008R2\. Permissions on these two Service keys"); + ::rptMsg(" allow an actor to create a Performance subkey and auto-load a malicious DLL which will execute with System-level "); + ::rptMsg(" privileges."); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/persistconn.pl b/thirdparty/rr-full/plugins/persistconn.pl new file mode 100644 index 00000000000..7e7f0161ae5 --- /dev/null +++ b/thirdparty/rr-full/plugins/persistconn.pl @@ -0,0 +1,72 @@ +#----------------------------------------------------------- +# persistconn.pl +# +# +# Change history +# 20230109 - created +# +# References +# https://jeffpar.github.io/kbarchive/kb/168/Q168148/ +# +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package persistconn; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + category => "persistence", + MITRE => "T1547\.015", + version => 20230109); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets Persistent Connections values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching persistconn v.".$VERSION); + ::rptMsg("persistconn v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{category}." (".$config{MITRE}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + my $key_path = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Network\\Persistent Connections"; + + if (my $key = $root_key->get_subkey($key_path)) { + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-15s %-45s",$v->get_name(),$v->get_data()); + } + } + else { + ::rptMsg($key_path." has no values."); + } + } + else { + ::rptMsg($key_path." key not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Network connections can be persisted by choosing the \"net use /persistent:yes\" command, or by choosing "); + ::rptMsg("\"Reconnect at Logon\" in the Map Network Drive dialog; both allow mapped drives to be reconnected at logon. Look for "); + ::rptMsg("suspicious or unintended connections. Note that File and Printer Sharing needs to be enabled, as well."); + ::rptMsg(""); + ::rptMsg("Ref: https://gegeek.com/networking/mapped-drives/"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/phdet.pl b/thirdparty/rr-full/plugins/phdet.pl deleted file mode 100644 index 66ded86d253..00000000000 --- a/thirdparty/rr-full/plugins/phdet.pl +++ /dev/null @@ -1,82 +0,0 @@ -#----------------------------------------------------------- -# phdet.pl -# -# History: -# 20121213 - created -# -# References: -# http://www.microsoft.com/security/portal/Threat/Encyclopedia/Entry.aspx?Name=Win32/Phdet -# -# -# copyright 2012 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package phdet; -use strict; - -my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "Malware", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20120817); - -sub getConfig{return %config} -sub getShortDescr { - return "Check for a Phdet infection"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my %files; -my @temps; - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching phdet v.".$VERSION); - ::rptMsg("phdet v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my ($current,$ccs); - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - my $phdet_path = $ccs."\\Services\\msupdate"; - my $phdet; - if ($phdet = $root_key->get_subkey($phdet_path)) { - my @vals = $phdet->get_values(); - if (scalar(@vals) > 0) { - my %p_vals; - foreach my $v (@vals) { - $p_vals{$v->get_name()} = $v->get_data(); - } - ::rptMsg("DisplayName: ".$p_vals{"DisplayName"}); - ::rptMsg("Image Path : ".$p_vals{"ImagePath"}); - } - else { - ::rptMsg($phdet_path." key has no values."); - } - } - else { - ::rptMsg($phdet_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/photos.pl b/thirdparty/rr-full/plugins/photos.pl index 9f833d3a708..b3fda63044e 100644 --- a/thirdparty/rr-full/plugins/photos.pl +++ b/thirdparty/rr-full/plugins/photos.pl @@ -1,32 +1,34 @@ -package photos; + #------------------------------------------------------------ # photos.pl - read data on images opened via Win8 Photos app # # Change history +# 20200922 - MITRE update +# 20200525 - updated date output format # 20130308 - created # # Ref: # http://dfstream.blogspot.com/2013/03/windows-8-tracking-opened-photos.html # -# Copyright 2013 QAR, LLC +# Copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #------------------------------------------------------------ +package photos; use strict; my %config = (hive => "USRCLASS\.DAT", - hivemask => 32, - output => "report", - category => "User Activity", - osmask => 20, #not used at the moment + category => "user activity", + MITRE => "", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20130102); + output => "report", + version => 20200922); sub getConfig{return %config} sub getShortDescr { - return "Shell/BagMRU traversal in Win7 USRCLASS.DAT hives"; + return "Images opened via Win8 Photos App"; } sub getDescr{} sub getRefs {} @@ -61,7 +63,7 @@ sub pluginmain { my $name = $s->get_name(); my $lw = $s->get_timestamp(); ::rptMsg($name); - ::rptMsg("LastWrite: ".gmtime($lw)." UTC"); + ::rptMsg("LastWrite: ".::format8601Date($lw)."Z"); eval { my $fp = $s->get_value("FilePath")->get_data(); @@ -72,7 +74,7 @@ sub pluginmain { my $last = $s->get_value("LastUpdatedTime")->get_data(); my ($v0,$v1) = unpack("VV",$last); my $l = ::getTime($v0,$v1); - ::rptMsg("LastUpdatedTime: ".gmtime($l)." UTC"); + ::rptMsg("LastUpdatedTime: ".::format8601Date($l)."Z"); }; eval { diff --git a/thirdparty/rr-full/plugins/photos_win10.pl b/thirdparty/rr-full/plugins/photos_win10.pl deleted file mode 100644 index 87d062a2b73..00000000000 --- a/thirdparty/rr-full/plugins/photos_win10.pl +++ /dev/null @@ -1,191 +0,0 @@ -#----------------------------------------------------------- -# photos_win10.pl -# Plugin for RegRipper -# -# Parses Microsoft Photos (Windows App) key: -# - USRCLASS.DAT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\SystemAppData\Microsoft.Windows.Photos_8wekyb3d8bbwe -# -# On a live machine, the key path is: -# - HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\SystemAppData\Microsoft.Windows.Photos_8wekyb3d8bbwe -# -# The script was tested on Windows 10 against: -# - Microsoft.Windows.Photos_2017.37071.16410.0_x64__8wekyb3d8bbwe -# - Microsoft.Windows.Photos_2018.18022.15810.1000_x64__8wekyb3d8bbwe -# -# The script code is based on: -# - adoberdr.pl/landesk.pl/photos.pl by H. Carvey -# - iexplore.pl by E. Rye esten@ryezone.net -# http://www.ryezone.net/regripper-and-internet-explorer-1 -# -# Change history -# 20180610 - First release -# -# To Dos -# Extract value name "Link" -# -# References -# https://forensenellanebbia.blogspot.com/2018/06/usrclassdat-stores-more-history-than.html -# https://df-stream.com/2013/03/windows-8-tracking-opened-photos/ -# -# copyright 2018 Gabriele Zambelli | Twitter: @gazambelli -#----------------------------------------------------------- - -package photos_win10; -use strict; - -my %config = (hive => "USRCLASS\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20180610); - -sub getShortDescr { return "Get values from the user's Microsoft Photos Windows App key"; } - -sub getDescr {} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my (@ts,$d); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::rptMsg("photos_win10 v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - # First, let's find out which version of Microsoft Photos is installed - my $version; - my $tag = 0; - my @globalitems = (); - my $key_path = "Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppModel\\SystemAppData\\Microsoft.Windows.Photos_8wekyb3d8bbwe\\Schemas"; - my $key = $root_key->get_subkey($key_path); - if (defined($key)) { - my %vals = getKeyValues($key); - foreach my $v (keys %vals) { - if ($v =~ m/^PackageFullName/) { - #Version of Microsoft Photos App - ::rptMsg($key_path); - ::rptMsg(" PackageFullName => ".($vals{$v})); - $tag = 1; - } - } - } - else { - ::rptMsg($key_path." not found."); - } - - - #Print SubKey, Last Write Time, Viewed Picture - if ($tag) { - my $key_path = "Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppModel\\SystemAppData\\Microsoft.Windows.Photos_8wekyb3d8bbwe\\PersistedStorageItemTable\\ManagedByApp"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my %vals = getKeyValues($key); - if (scalar(keys %vals) > 0) { - foreach my $v (keys %vals) { - ::rptMsg("\t".$v." -> ".$vals{$v}); - } - } - my @sk = $key->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - ::rptMsg(""); - ::rptMsg($key_path); - foreach my $s (@sk) { - ::rptMsg(""); - ::rptMsg(" ".$s->get_name()); - ::rptMsg(" KeyLastWrite : ".gmtime($s->get_timestamp())." (UTC)"); - my %vals = getKeyValues($s); - foreach my $v (keys %vals) { - if ($v =~ m/^Metadata/) { - #Metadata contains the path to the viewed picture - ::rptMsg(" Metadata : ".$vals{$v}); - } - if ($v =~ m/^LastUpdatedTime/) { - #LastUpdatedTime - @ts = unpack("VV",$s->get_value($v)->get_data()); - ::rptMsg(" LastUpdatedTime: ".gmtime(::getTime($ts[0],$ts[1]))." (UTC)"); - } - } - } - } - else { - ::rptMsg(""); - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - } - - #Print Viewed Picture | Write Time - if ($tag) { - my $key_path = "Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppModel\\SystemAppData\\Microsoft.Windows.Photos_8wekyb3d8bbwe\\PersistedStorageItemTable\\ManagedByApp"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my %vals = getKeyValues($key); - my @sk = $key->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - ::rptMsg(""); - ::rptMsg(""); - ::rptMsg("## Microsoft Photos (Windows App): Recent Files ## (Tab-separated values)"); - ::rptMsg(""); - my @sitems; #create new array for sorted items - foreach my $s (@sk) { - my %vals = getKeyValues($s); - foreach my $v (keys %vals) { - if ($v =~ m/^Metadata/) { - if ($vals{$v} =~ m/^. /) { #find single character followed by a space at the beginning of the string - my $sd; #single digit - $sd = substr($vals{$v},0,1); - $vals{$v} =~ s/^. / $sd /g; #change from "^\. " to "^ \. ", Microsoft Photos 2018 prepends a number in front of the path - push @sitems, ($vals{$v}."\t".gmtime($s->get_timestamp())); - } - elsif ($vals{$v} =~ m/^.. /) { #find two characters followed by a space at the beginning of the string - push @sitems, ($vals{$v}."\t".gmtime($s->get_timestamp())); - } - else { - ::rptMsg($vals{$v}."\t KeyLastWrite: ".gmtime($s->get_timestamp())." (UTC)"); - } - } - } - } - if (scalar(@sitems) > 0) { - #sort alphabetically the items in the array - ::rptMsg("Metadata\tKeyLastWrite (UTC)"); #print header row - foreach my $item (sort @sitems){ - ::rptMsg($item); - } - } - } - ::rptMsg(""); - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - } -} - -sub getKeyValues { - my $key = shift; - my %vals; - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - } - return %vals; -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/pointandprint.pl b/thirdparty/rr-full/plugins/pointandprint.pl new file mode 100644 index 00000000000..a391d1a9c05 --- /dev/null +++ b/thirdparty/rr-full/plugins/pointandprint.pl @@ -0,0 +1,80 @@ +#----------------------------------------------------------- +# pointandprint.pl +# Check Software hive for various settings - Point & print restriction policies +# affect CVE-2021-1675 patch effectiveness +# +# Change history: +# 20210705 - created +# +# References: +# https://twitter.com/StanHacked/status/1410527329839980547 +# https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-34527 +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package pointandprint; +use strict; + +my %config = (hive => "software", + category => "privilege escalation", + MITRE => "T1068", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210705); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check Point & Print restrition values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching pointandprint v.".$VERSION); + ::rptMsg("pointandprint v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my @vals = ("NoWarningNoElevationOnInstall","NoWarningNoElevationOnUpdate","NoElevationOnInstall"); + my $root_key = $reg->get_root_key; + + my $key_path = "Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg(""); + foreach my $v (@vals) { + eval { + my $i = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-20s %-5s",$v,$i); + }; + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Even after applying the CVE-2021-1675 and -34527 patches, Point & Print restriction policies may"); + ::rptMsg("render the patches ineffective, even on non-DC systems. This may be the case if the NoElevationOnInstall and/or "); + ::rptMsg(""); + ::rptMsg("NoWarningNoElevationOnInstall values are set to \"1\"."); + ::rptMsg("Ref: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-34527"); +# Ref: https://www.miltonsecurity.com/company/blog/printnightmare-0-day-exploit-windows-dc +# If NoElevationOnInstall is set to "1", then the system is still vulnerable +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/polacdms.pl b/thirdparty/rr-full/plugins/polacdms.pl deleted file mode 100644 index 8147c53f0f1..00000000000 --- a/thirdparty/rr-full/plugins/polacdms.pl +++ /dev/null @@ -1,94 +0,0 @@ -#----------------------------------------------------------- -# polacdms -# Get the audit policy from the Security hive file; also, gets -# -# -# Change History: -# 20100531 - Created -# -# References: -# http://en.wikipedia.org/wiki/Security_Identifier -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package polacdms; -use strict; - -my %config = (hive => "Security", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100531); - -sub getConfig{return %config} -sub getShortDescr { - return "Get local machine SID from Security hive"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching polacdms v.".$VERSION); - ::rptMsg("polacdms v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Policy\\PolAcDmS"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("PolAcDmS"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $data; - eval { - $data = $key->get_value("")->get_data(); - }; - if ($@) { - ::rptMsg("Error occurred getting data from ".$key_path); - ::rptMsg(" - ".$@); - } - else { - my @d = unpack("V4",substr($data,8,16)); - ::rptMsg("Machine SID: S-1-5-".(join('-',@d))); - } - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); - $key_path = "Policy\\PolPrDmS"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("PolPrDmS"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $data; - eval { - $data = $key->get_value("")->get_data(); - }; - if ($@) { - ::rptMsg("Error occurred getting data from ".$key_path); - ::rptMsg(" - ".$@); - } - else { - my @d = unpack("V4",substr($data,8,16)); - ::rptMsg("Primary Domain SID: S-1-5-".(join('-',@d))); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/policies_u.pl b/thirdparty/rr-full/plugins/policies_u.pl deleted file mode 100644 index 57fcb5c873f..00000000000 --- a/thirdparty/rr-full/plugins/policies_u.pl +++ /dev/null @@ -1,75 +0,0 @@ -#----------------------------------------------------------- -# policies_u -# Get values from user's WinLogon key -# -# copyright 2009 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package policies_u; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20091021); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get values from the user's Policies key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching policies_u v.".$VERSION); - ::rptMsg("policies_u v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion"; - my $key; - if ($key = $root_key->get_subkey($key_path."\\policies")) { -# ::rptMsg("policies key found."); - - } - elsif ($key = $root_key->get_subkey($key_path."\\Policies")) { -# ::rptMsg("Policies key found."); - - } - else { - ::rptMsg("Neither policies nor Policies key found."); - return; - } - - eval { - my @vals = $key->get_subkey("Explorer")->get_list_of_values(); - if (scalar(@vals) > 0) { - ::rptMsg(""); - ::rptMsg("Explorer subkey values:"); - foreach my $v (@vals) { - my $str = sprintf "%-20s %-20s",$v->get_name(),$v->get_data(); - ::rptMsg(" ".$str); - } - } - }; - ::rptMsg(""); - eval { - my $quota = $key->get_subkey("System")->get_value("EnableProfileQuota")->get_data(); - ::rptMsg("EnableProfileQuota = ".$quota); - ::rptMsg(""); - ::rptMsg("The EnableProfileQuota = 1 setting causes the proquota\.exe to be run"); - ::rptMsg("automatically in order to limit the size of roaming profiles\. This"); - ::rptMsg("corresponds to the Limit Profile Size GPO setting\."); - }; - ::rptMsg("System\\EnableProfileQuota value not found\.") if ($@); -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/port_dev.pl b/thirdparty/rr-full/plugins/portdev.pl similarity index 52% rename from thirdparty/rr-full/plugins/port_dev.pl rename to thirdparty/rr-full/plugins/portdev.pl index 2a05f5eefa8..697f49dbc1b 100644 --- a/thirdparty/rr-full/plugins/port_dev.pl +++ b/thirdparty/rr-full/plugins/portdev.pl @@ -1,29 +1,34 @@ #----------------------------------------------------------- -# port_dev -# Parse Microsoft\Windows Portable Devices\Devices key on Vista +# portdev +# Parse Microsoft\Windows Portable Devices\Devices key # Get historical information about drive letter assigned to devices # -# NOTE: Credit for "discovery" goes to Rob Lee +# NOTE: Credit for original "discovery" of the key goes to Rob Lee # # Change History: +# 20220527 - updated to address different device types +# 20200921 - MITRE update # 20090118 - changed the name of the plugin from "removdev" # -# copyright 2008 H. Carvey, keydet89@yahoo.com +# copyright 2022 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- -package port_dev; +package portdev; use strict; my %config = (hive => "Software", - osmask => 192, + MITRE => "", + category => "devices", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20090118); + output => "report", + version => 20220527); sub getConfig{return %config} sub getShortDescr { - return "Parses Windows Portable Devices key (Vista)"; + return "Parses Windows Portable Devices info"; } sub getDescr{} sub getRefs {} @@ -35,49 +40,47 @@ sub getShortDescr { sub pluginmain { my $class = shift; my $hive = shift; - ::logMsg("Launching port_dev v.".$VERSION); - ::rptMsg("port_dev v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::logMsg("Launching portdev v.".$VERSION); + ::rptMsg("portdev v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key_path = "Microsoft\\Windows Portable Devices\\Devices"; my $key; if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("RemovDev"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); ::rptMsg(""); my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { my $name = $s->get_name(); - my $lastwrite = $s->get_timestamp(); + my $dev = ""; + my $sn = ""; + my @items = split(/\#/,$name); + if ($items[0] eq "SWD") { + $dev = $items[3]; + $sn = $items[4]; + } + elsif ($items[0] eq "USB") { + $dev = $items[1]; + $sn = $items[2]; + } + else { - my $letter; + } + + my $f = ""; eval { - $letter = $s->get_value("FriendlyName")->get_data(); + $f = $s->get_value("FriendlyName")->get_data(); }; - ::rptMsg($name." key error: $@") if ($@); - my $half; - if (grep(/##/,$name)) { - $half = (split(/##/,$name))[1]; - } - - if (grep(/\?\?/,$name)) { - $half = (split(/\?\?/,$name))[1]; - } - - my ($dev,$sn) = (split(/#/,$half))[1,2]; - - ::rptMsg("Device : ".$dev); - ::rptMsg("LastWrite : ".gmtime($lastwrite)." (UTC)"); - ::rptMsg("SN : ".$sn); - ::rptMsg("Drive : ".$letter); + ::rptMsg("Device : ".$dev); + ::rptMsg("LastWrite : ".::format8601Date($s->get_timestamp())."Z"); + ::rptMsg("SN : ".$sn); + ::rptMsg("FriendlyName : ".$f); ::rptMsg(""); - } } else { diff --git a/thirdparty/rr-full/plugins/portdev_tln.pl b/thirdparty/rr-full/plugins/portdev_tln.pl new file mode 100644 index 00000000000..3faea115843 --- /dev/null +++ b/thirdparty/rr-full/plugins/portdev_tln.pl @@ -0,0 +1,90 @@ +#----------------------------------------------------------- +# portdev +# Parse Microsoft\Windows Portable Devices\Devices key +# Get historical information about drive letter assigned to devices +# +# NOTE: Credit for original "discovery" of the key goes to Rob Lee +# +# Change History: +# 20220527 - created from portdev.pl +# +# copyright 2022 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package portdev_tln; +use strict; + +my %config = (hive => "Software", + MITRE => "", + category => "devices", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "tln", + version => 20220527); + +sub getConfig{return %config} + +sub getShortDescr { + return "Parses Windows Portable Devices info"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::logMsg("Launching portdev v.".$VERSION); +# ::rptMsg("portdev v.".$VERSION); +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key_path = "Microsoft\\Windows Portable Devices\\Devices"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { +# ::rptMsg($key_path); +# ::rptMsg(""); + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + + foreach my $s (@subkeys) { + my $name = $s->get_name(); + my $dev = ""; + my $sn = ""; + my @items = split(/\#/,$name); + if ($items[0] eq "SWD") { + $dev = $items[3]; + $sn = $items[4]; + } + elsif ($items[0] eq "USB") { + $dev = $items[1]; + $sn = $items[2]; + } + else { + + } + + my $f = ""; + eval { + $f = $s->get_value("FriendlyName")->get_data(); + }; + my $str = $dev; + $str .= "\\$sn" if ($sn ne ""); + $str .= " [".$f."]" if ($f ne ""); + ::rptMsg($s->get_timestamp()."|REG|||WinPortDev - $str"); + } + } + else { + ::rptMsg($key_path." has no subkeys."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/portproxy.pl b/thirdparty/rr-full/plugins/portproxy.pl new file mode 100644 index 00000000000..ed1bb8d7ab5 --- /dev/null +++ b/thirdparty/rr-full/plugins/portproxy.pl @@ -0,0 +1,74 @@ +#----------------------------------------------------------- +# portproxy.pl +# Check port proxy settings, set via netsh; look for potential tunneling activity +# +# History: +# 20200929 - minor updates +# 20200909 - created +# +# References: +# https://www.fireeye.com/blog/threat-research/2019/01/bypassing-network-restrictions-through-rdp-tunneling.html +# http://www.dfirnotes.net/portproxy_detection/ +# +# https://attack.mitre.org/techniques/T1572/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package portproxy; +use strict; + +my %config = (hive => "System", + output => "report", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1572", + version => 20200929); + +sub getConfig{return %config} +sub getShortDescr { + return "Check port proxy settings, set via netsh"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my @temps; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching portproxy v.".$VERSION); + ::rptMsg("portproxy v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + my $key; + my $key_path = $ccs."\\services\\PortProxy\\v4tov4\\tcp"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg(sprintf "%-25s %-25s","Listen IP/Port","Connect IP/Port"); + foreach my $v (@vals) { + ::rptMsg(sprintf "%-25s %-25s",$v->get_name(),$v->get_data()); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Entries may be an indication of the use of \"netsh\" to enable RDP tunneling."); + ::rptMsg("Ref: https://www.fireeye.com/blog/threat-research/2019/01/bypassing-network-restrictions-through-rdp-tunneling.html"); + } + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ports.pl b/thirdparty/rr-full/plugins/ports.pl new file mode 100644 index 00000000000..33a42bef37e --- /dev/null +++ b/thirdparty/rr-full/plugins/ports.pl @@ -0,0 +1,66 @@ +#----------------------------------------------------------- +# ports.pl +# +# History: +# 20210309 - created +# +# References: +# https://techcommunity.microsoft.com/t5/microsoft-defender-for-endpoint/investigating-the-print-spooler-eop-exploitation/ba-p/2166463 +# https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2020-1048 +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package ports; +use strict; + +my %config = (hive => "Software", + category => "privilege escalation", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1068", + output => "report", + version => 20210309); + +sub getConfig{return %config} +sub getShortDescr { + return "Check port assignments"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching ports v.".$VERSION); + ::rptMsg("ports v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg("");my $key; + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Ports"; + if ($key = $root_key->get_subkey($key_path)) { + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + foreach my $v (@vals) { + ::rptMsg(sprintf "%-15s %-20s",$v->get_name(),$v->get_data()); + } + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Printer ports can be exploited to elevate privileges; look for unusual/suspicious ports."); + ::rptMsg("Ref: https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2020-1048"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/powershellcore.pl b/thirdparty/rr-full/plugins/powershellcore.pl new file mode 100644 index 00000000000..71675da4fd6 --- /dev/null +++ b/thirdparty/rr-full/plugins/powershellcore.pl @@ -0,0 +1,194 @@ +#----------------------------------------------------------- +# powershellcore.pl +# +# +# Change history +# 20200922 - MITRE update +# 20200525 - updated date output format +# 20181005 - created +# +# References +# http://files.brucon.org/2018/03-Matt-Ryan-ReInvestigating-Powershell-Attacks.pdf +# +# Copyright (c) 2020 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package powershellcore; +use strict; + +my %config = (hive => "software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + category => "config", + version => 20200922); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Extracts PowerShellCore settings"; +} +sub getRefs {} + +sub pluginmain { + + # Declarations # + my $class = shift; + my $hive = shift; + + # Initialize # + ::logMsg("Launching powershellcore v.".$VERSION); + ::rptMsg("powershellcore v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + + my @paths = ("Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers", + "Wow6432Node\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers", + "Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers", + "Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers"); + + foreach my $key_path (@paths) { + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + + foreach my $v (@vals) { + ::rptMsg($v->get_name()." -> ".$v->get_data()); + } + } + else { + ::rptMsg($key_path." found, has no values."); + } + } + else { +# ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + +# Get all programs for which PCA "came up", for a user, even if no compatibility modes were +# selected +# Added 20130706 by H. Carvey + @paths = ("Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Persisted", + "Wow6432Node\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Persisted", + "Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Persisted", + "Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Persisted"); + + foreach my $key_path (@paths) { + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + ::rptMsg(" ".$v->get_name()); + } + } + else { + ::rptMsg($key_path." found, has no values\."); + } + } + else { +# As above, don't report on key paths not found +# ::rptMsg($key_path." not found\."); + } + } + +# Get Store key contents +# selected +# Added 20130930 by H. Carvey + @paths = ("Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Store", + "Wow6432Node\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Store", + "Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Store", + "Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Compatibility Assistant\\Store"); + + foreach my $key_path (@paths) { + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + + my ($t0,$t1) = unpack("VV",substr($v->get_data(),0x2C,8)); + my $t = ::getTime($t0,$t1); + + ::rptMsg(" ".::format8601Date($t)."Z - ".$v->get_name()); + } + } + else { + ::rptMsg($key_path." found, has no values\."); + } + } + else { +# As above, don't report on key paths not found +# ::rptMsg($key_path." not found\."); + } + } + +# Added check for use of AppCompat DB for persistence +# 21051021, H. Carvey + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Custom"; + if ($key = $root_key->get_subkey($key_path)){ + my @subkeys = $key->get_list_of_subkeys($key); + if (scalar @subkeys > 0) { + foreach my $sk (@subkeys) { + ::rptMsg("Key name: ".$sk->get_name()); + ::rptMsg("LastWrite time: ".::format8601Date($sk->get_timestamp())."Z"); + + my @vals = $sk->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + my $name = $v->get_name(); + my ($t0,$t1) = unpack("VV",$v->get_data()); + my $l = ::getTime($t0,$t1); + my $ts = ::format8601Date($l); + ::rptMsg(" ".$name." ".$ts."Z"); + } + } + ::rptMsg(""); + } + } + } + + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\InstalledSDB"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys($key); + if (scalar @subkeys > 0) { + foreach my $sk (@subkeys) { + my($path, $descr, $ts); + eval { + $descr = $sk->get_value("DatabaseDescription")->get_data(); + ::rptMsg("Description: ".$descr); + }; + + eval { + $path = $sk->get_value("DatabasePath")->get_data(); + ::rptMsg(" Path: ".$path); + }; + + eval { + my ($t0,$t1) = unpack("VV",$sk->get_value("DatabaseInstallTimeStamp")->get_data()); + my $l = ::getTime($t0,$t1); + $ts = ::format8601Date($l); + ::rptMsg(" Install TimeStamp: ".$ts."Z"); + }; + + ::rptMsg(""); + + } + } + } +} + +1; diff --git a/thirdparty/rr-full/plugins/prefetch.pl b/thirdparty/rr-full/plugins/prefetch.pl index 3fa5b08366c..6bd3083c1fc 100644 --- a/thirdparty/rr-full/plugins/prefetch.pl +++ b/thirdparty/rr-full/plugins/prefetch.pl @@ -3,28 +3,28 @@ # Access System hive file to get the Prefetch Parameters # # Change history -# 2016-05-06 Added check for SysMain service start method. James Habben +# 20200922 - MITRE update +# 20200515 - minor updates +# 20120914 - created # # References # http://msdn.microsoft.com/en-us/library/bb499146(v=winembedded.5).aspx # # copyright 2012 Corey Harrell (Journey Into Incident Response) +# updated copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package prefetch; use strict; -my %config = (hive => "SYSTEM", +my %config = (hive => "system", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20160506); - -my %starts = (0x00 => "Boot Start", - 0x01 => "System Start", - 0x02 => "Auto Start", - 0x03 => "Manual", - 0x04 => "Disabled"); + MITRE => "", + category => "config", + output => "report", + version => 20200922); sub getConfig{return %config} sub getShortDescr { @@ -49,12 +49,10 @@ sub pluginmain { # First thing to do is get the ControlSet00x marked current...this is # going to be used over and over again in plugins that access the system # file - my ($current,$ccs); my $key_path = 'Select'; my $key; if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; + my $ccs = ::getCCS($root_key); my $pp_path = $ccs."\\Control\\Session Manager\\Memory Management\\PrefetchParameters"; my $pp; if ($pp = $root_key->get_subkey($pp_path)) { @@ -65,36 +63,16 @@ sub pluginmain { ::rptMsg("1 = Application prefetching is enabled"); ::rptMsg("2 = Boot prefetching is enabled"); ::rptMsg("3 = Both boot and application prefetching is enabled"); - - } - else { - ::rptMsg($pp_path." not found."); - ::logMsg($pp_path." not found."); - } - - my $pfsvc_path = $ccs."\\services\\SysMain"; - my $pfsvc; - if ($pfsvc = $root_key->get_subkey($pfsvc_path)) { - my $svc_start = $pfsvc->get_value("Start")->get_data(); - if (exists $starts{$svc_start}) { - $svc_start = $starts{$svc_start}; - } ::rptMsg(""); - ::rptMsg("Superfetch service runs both Superfetch and Prefetch functions. Shortname is SysMain."); - ::rptMsg("SysMain Service = ".$svc_start); - - + ::rptMsg("Analysis Tip: Application Prefetching is disabled by default on Server platforms."); } else { - ::rptMsg($pfsvc_path." not found."); - ::logMsg($pfsvc_path." not found."); + ::rptMsg($pp_path." not found."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } - } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/printdemon.pl b/thirdparty/rr-full/plugins/printdemon.pl new file mode 100644 index 00000000000..2c526b60943 --- /dev/null +++ b/thirdparty/rr-full/plugins/printdemon.pl @@ -0,0 +1,104 @@ +#----------------------------------------------------------- +# printdemon.pl +# +# History +# 20200922 - MITRE update +# 20200514 - created +# +# Refs: +# https://windows-internals.com/printdemon-cve-2020-1048/ +# https://twitter.com/aionescu/status/1260466215299973121 +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package printdemon; +use strict; + +my %config = (hive => "software", + hasShortDescr => 1, + category => "persistence", + hasDescr => 0, + hasRefs => 1, + MITRE => "T1546", + output => "report", + version => 20200922); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets value assoc with printer ports and descriptions"; +} +sub getDescr{} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching printdemon v.".$VERSION); + ::rptMsg("printdemon v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key_path = 'Microsoft\Windows NT\CurrentVersion'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + +# First, get the Ports values + if (my $ports = $key->get_subkey("Ports")) { + ::rptMsg("Ports key"); + ::rptMsg("LastWrite time: ".::format8601Date($ports->get_timestamp())."Z"); + ::rptMsg(""); + my @vals = $ports->get_list_of_values(); + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-15s %-50s",$v->get_name(),$v->get_data()); + } + } + } + else { + ::rptMsg("Ports key not found."); + } + ::rptMsg(""); + ::rptMsg("Print\\Printers keys, Port values"); +# Now, get the Port value for each printer + if (my $pr = $key->get_subkey('Print\Printers')) { + my @printers = $pr->get_list_of_subkeys(); + if (scalar(@printers) > 0) { + foreach my $p (@printers) { + ::rptMsg("Printer : ".$p->get_name()); + ::rptMsg("LastWrite time : ".::format8601Date($p->get_timestamp())."Z"); + + eval { + my $p = $p->get_value("Print Processor")->get_data(); + ::rptMsg("Print Processor: ".$p); + + }; + + eval { + my $d = $p->get_value("Printer Driver")->get_data(); + ::rptMsg("Printer Driver : ".$d); + + }; + + eval { + my $pp = $p->get_value("Port")->get_data(); + ::rptMsg("Port : ".$pp); + }; + ::rptMsg(""); + ::rptMsg("Analysis Tip: Per CVE-2020-1048, an actor can add a printer port as a persistent backdoor."); + ::rptMsg("https://windows-internals.com/printdemon-cve-2020-1048/"); + } + } + } + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/printer_settings.pl b/thirdparty/rr-full/plugins/printer_settings.pl new file mode 100644 index 00000000000..4eae8446df0 --- /dev/null +++ b/thirdparty/rr-full/plugins/printer_settings.pl @@ -0,0 +1,120 @@ +#----------------------------------------------------------- +# printer_settings.pl +# +# History: +# 20200730 - Added MITRE ATT&CK technique +# 20200427 - updated output date format +# 20200119 - created +# +# References: +# +# https://securelist.com/project-tajmahal/90240/ +# 10 Apr 2019 +# Taj Mahal module modifies system to enable data theft, by setting attribute for +# printers: +# Key listed as: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers +# Value: Attributes +# +# https://www.undocprint.org/winspool/registry +# Lists key: SYSTEM\CurrentControlSet\Control\Print\Printers\ +# Appear to have the same values available as Software key. +# +# Testing indicates that the Attributes value in both keys is modified when setting +# the attribute via the UI. This is likely due to the fact that the System hive key +# is a link (value type REG_LINK) to the Software hive, as illustrated in the +# following: +# https://helgeklein.com/blog/2008/05/free-tool-list-registry-links-reg_link/ +# +# Note that the ability to query either hive is provided, in case the analyst only +# has access to one of the hives. +# +# https://attack.mitre.org/techniques/T1074/001/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package printer_settings; +use strict; + +my %config = (hive => "system, software", + hivemask => 4, + output => "report", + category => "collection", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1074\.001", + version => 20200730); + +sub getConfig{return %config} +sub getShortDescr { + return "Check printer attributes for KeepPrintedJobs setting"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching printer_settings v.".$VERSION); + ::rptMsg("printer_settings v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# hive + my ($current,$ccs); + my $sel = 'Select'; + my $key; + if ($key = $root_key->get_subkey($sel)) { + $current = $key->get_value("Current")->get_data(); + $ccs = "ControlSet00".$current; + my $key_path = $ccs."\\Control\\Print\\Printers"; + + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); + eval { + my $attr = $s->get_value("Attributes")->get_data(); + if ($attr & 0x100) { + ::rptMsg(" Printer: ".$s->get_name()." KeepPrintedJobs attribute set\."); + ::rptMsg(" Key LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + } + }; + } + } + } + } + + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); + eval { + my $attr = $s->get_value("Attributes")->get_data(); + if ($attr & 0x100) { + ::rptMsg(" Printer: ".$s->get_name()." KeepPrintedJobs attribute set\."); + ::rptMsg(" Key LastWrite time: ".::format8601Date($s->get_timestamp())); + } + }; + } + } + } + ::rptMsg("Analysis Tip: A printer attribute can be set to keep printed jobs after completion, which can lead to data theft."); + ::rptMsg("https://securelist.com/project-tajmahal/90240/"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/printermru.pl b/thirdparty/rr-full/plugins/printermru.pl deleted file mode 100644 index 82074a8221e..00000000000 --- a/thirdparty/rr-full/plugins/printermru.pl +++ /dev/null @@ -1,76 +0,0 @@ -#----------------------------------------------------------- -# printermru.pl -# Plugin to get RealVNC MRU listings from NTUSER.DAT -# -# Change history -# 20091125 - created -# -# References -# -# copyright 2009 H. Carvey -#----------------------------------------------------------- -package printermru; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20091125); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets user's Printer Wizard MRU listing"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching printermru v.".$VERSION); - ::rptMsg("printermru v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Printers\\Settings\\Wizard\\ConnectMRU'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %mru; - my @list; - foreach my $v (@vals) { - $mru{$v->get_name()} = $v->get_data(); - } - - if (exists $mru{MRUList}) { - @list = split(//,$mru{MRUList}); - } - - ::rptMsg("Printers listed in MRUList order."); - foreach my $i (0..scalar(@list) - 1) { - ::rptMsg(" ".$list[$i]." -> ".$mru{$list[$i]}); - } - - - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/printers.pl b/thirdparty/rr-full/plugins/printers.pl deleted file mode 100644 index 97a4a0f1aa2..00000000000 --- a/thirdparty/rr-full/plugins/printers.pl +++ /dev/null @@ -1,84 +0,0 @@ -#----------------------------------------------------------- -# printers.pl -# Get information about printers used by a user; System hive -# info is volatile -# -# Ref: -# http://support.microsoft.com/kb/102966 -# http://support.microsoft.com/kb/252388 -# http://support.microsoft.com/kb/102116 -# -# The following references contain information from the System -# hive that is volatile. -# http://www.undocprint.org/winspool/registry -# http://msdn.microsoft.com/en-us/library/aa394363(VS.85).aspx -# -# copyright 2008-2009 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package printers; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20090223); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get user's printers"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching printers v.".$VERSION); - ::rptMsg("printers v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows NT\\CurrentVersion\\PrinterPorts"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time: ".gmtime($key->get_timestamp())); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - ::rptMsg(" ".$v->get_name()." (".$v->get_data().")"); - } - } - else { - ::rptMsg($key_path." has no values."); - } - ::rptMsg(""); -# Get default printer - my $def_path = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows"; - my $def; - eval { - $def = $root_key->get_subkey($def_path)->get_value("Device")->get_data(); - ::rptMsg("Default Printer (via CurrentVersion\\Windows): ".$def); - }; -# another attempt to get the default printer - $def_path = "Printers"; - eval { - $def = $root_key->get_subkey($def_path)->get_value("DeviceOld")->get_data(); - ::rptMsg("Default Printer (via Printers->DeviceOld): ".$def); - }; - - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/printmon.pl b/thirdparty/rr-full/plugins/printmon.pl new file mode 100644 index 00000000000..c0b1124641d --- /dev/null +++ b/thirdparty/rr-full/plugins/printmon.pl @@ -0,0 +1,99 @@ +#----------------------------------------------------------- +# printmon.pl +# Access System hive file to get the printer monitors +# +# MITRE ATT&CK Technique: https://attack.mitre.org/techniques/T1013/ +# +# Change history +# 20200922 - MITRE update +# 20200427 - updated output date format +# 20191122 - created +# +# References +# https://www.bleepingcomputer.com/news/security/deprimon-malware-registers-itself-as-a-windows-print-monitor/ +# https://www.welivesecurity.com/2019/11/21/deprimon-default-print-monitor-malicious-downloader/ +# +# copyright 2020 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package printmon; + + +my %config = (hive => "System", + hasShortDescr => 1, + category => "persistence", + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + output => "report", + version => 20200922); + +sub getConfig{return %config} +sub getShortDescr { + return "Lists installed Print Monitors"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching printmon v.".$VERSION); + ::rptMsg("printmon v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $current; + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + $current = $key->get_value("Current")->get_data(); + my $ccs = "ControlSet00".$current; + my $path = $ccs."\\Control\\Print\\Monitors"; + + if ($pm = $root_key->get_subkey($path)) { + ::rptMsg($path); + ::rptMsg(getShortDescr()); + ::rptMsg(""); +# Get all subkeys and sort based on LastWrite times + my @subkeys = $pm->get_list_of_subkeys(); + if (scalar (@subkeys) > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_name(); + my $lw = $s->get_timestamp(); + my $driver = ""; + eval { + $driver = $s->get_value("Driver")->get_data(); + }; + + ::rptMsg($name." LastWrite: ".::format8601Date($lw)."Z"); + ::rptMsg(" Driver: ".$driver); + ::rptMsg(""); + + } + ::rptMsg("Analysis Tip: Malware has persisted as a print monitor; be sure to review suspicious DLLs."); + ::rptMsg("https://www.welivesecurity.com/2020/05/21/no-game-over-winnti-group/"); + } + else { + ::rptMsg($path." has no subkeys."); + } + } + else { + ::rptMsg($path." not found."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/printmon_tln.pl b/thirdparty/rr-full/plugins/printmon_tln.pl new file mode 100644 index 00000000000..f078631eb74 --- /dev/null +++ b/thirdparty/rr-full/plugins/printmon_tln.pl @@ -0,0 +1,93 @@ +#----------------------------------------------------------- +# printmon_tln.pl +# Plugin for Registry Ripper; Access System hive file to get the +# printer monitors +# +# MITRE ATT&CK Technique: https://attack.mitre.org/techniques/T1013/ +# +# Change history +# 20200922 - MITRE update +# 20191122 - created +# +# References +# https://www.bleepingcomputer.com/news/security/deprimon-malware-registers-itself-as-a-windows-print-monitor/ +# https://www.welivesecurity.com/2019/11/21/deprimon-default-print-monitor-malicious-downloader/ +# +# copyright 2019-2020 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package printmon_tln; + + +my %config = (hive => "System", + hasShortDescr => 1, + category => "persistence", + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + output => "tln", + version => 20200922); + +sub getConfig{return %config} +sub getShortDescr { + return "Lists installed Print Monitors"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::logMsg("Launching printmon v.".$VERSION); +# ::rptMsg("printmon v.".$VERSION); # banner +# ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $current; + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + $current = $key->get_value("Current")->get_data(); + my $ccs = "ControlSet00".$current; + my $path = $ccs."\\Control\\Print\\Monitors"; + + if ($pm = $root_key->get_subkey($path)) { + ::rptMsg($path); + ::rptMsg(getShortDescr()); + ::rptMsg(""); +# Get all subkeys and sort based on LastWrite times + my @subkeys = $pm->get_list_of_subkeys(); + if (scalar (@subkeys) > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_name(); + my $lw = $s->get_timestamp(); + my $driver = ""; + eval { + $driver = $s->get_value("Driver")->get_data(); + }; + + ::rptMsg($lw."|REG|||Printer Monitor ".$name.": Driver: ".$driver); + + } + } + else { + ::rptMsg($path." has no subkeys."); + } + } + else { + ::rptMsg($path." not found."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/printnightmare.pl b/thirdparty/rr-full/plugins/printnightmare.pl new file mode 100644 index 00000000000..bfe31a3647a --- /dev/null +++ b/thirdparty/rr-full/plugins/printnightmare.pl @@ -0,0 +1,123 @@ +#----------------------------------------------------------- +# printnightmare.pl +# +# History: +# 20230319 - added reference +# 20220306 - added ParaFlare documentation +# 20210705 - created +# +# References: +# 20230319: https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/a-print-nightmare-artifact-krbtgt-nt-authority/ba-p/3757962 +# https://vuldb.com/?id.177880 +# https://paraflare.com/luci-spools-the-fun-with-phobos-ransomware/ +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package printnightmare; +use strict; + +my %config = (hive => "system", + output => "report", + category => "privilege escalation", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1068", + version => 20230319); + +sub getConfig{return %config} +sub getShortDescr { + return "Get settings, re: PrintNightmare exploit, CVE-2021-34527"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $root_key = (); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching printnightmare v.".$VERSION); + ::rptMsg("printnightmare v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\Print\\Environments"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @sk1 = $key->get_list_of_subkeys(); + if (scalar @sk1 > 0) { + foreach my $s1 (@sk1) { + my $path = $key_path."\\".$s1->get_name()."\\Drivers"; + if ($root_key->get_subkey($path)) { + processDrivers($path); + } + } + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg("Analysis Tip: POCs for the PrintNightmare exploit have been shown to be missing value data for several values,"); + ::rptMsg("including InfPath and Manufacturer. However, these values missing data does not explicitly mean that you've been"); + ::rptMsg("compromised via the exploit."); + ::rptMsg(""); + ::rptMsg("Also be sure to review the Microsoft-Windows-PrintService/Admin Event Log for Event ID 808, with message \"The"); + ::rptMsg("print spooler failed to load a plug-in module\" for exploitation attempts. Be sure to check for Security-Auditing"); + ::rptMsg("event ID 4624 events, with type 3 logins, prior to the PrintServce/Admin event(s)."); + ::rptMsg(""); + ::rptMsg("Ref: https://paraflare.com/luci-spools-the-fun-with-phobos-ransomware/"); +} + +sub processDrivers { + my $path = shift; + my $key = (); + if ($key = $root_key->get_subkey($path)) { + my @sk = $key->get_list_of_subkeys(); + if (scalar @sk > 0) { + foreach my $s (@sk) { + processVersions($path."\\".$s->get_name()); + } + } + } +} + +sub processVersions { + my $path = shift; + my $key = (); + if ($key = $root_key->get_subkey($path)) { + my @sk = $key->get_list_of_subkeys(); + if (scalar @sk > 0) { + foreach my $s (@sk) { + processPrinter($path."\\".$s->get_name()); + } + } + } +} + +sub processPrinter { + my $path = shift; + my $key = (); + my @vals = ("Configuration File","Data File","Driver","InfPath","Manufacturer"); + + if ($key = $root_key->get_subkey($path)) { + ::rptMsg($path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + foreach my $v (@vals) { + eval { + my $i = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-20s %-40s",$v,$i); + }; + } + ::rptMsg(""); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/printprocessors.pl b/thirdparty/rr-full/plugins/printprocessors.pl new file mode 100644 index 00000000000..962dec96743 --- /dev/null +++ b/thirdparty/rr-full/plugins/printprocessors.pl @@ -0,0 +1,95 @@ +#----------------------------------------------------------- +# printprocessors.pl +# +# History: +# 20200922 - MITRE update +# 20200710 - created +# +# References: +# https://www.welivesecurity.com/2020/05/21/no-game-over-winnti-group/ +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package printprocessors; +use strict; + +my %config = (hive => "System", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + output => "report", + version => 20200922); + +sub getConfig{return %config} +sub getShortDescr { + return "Get entries from PrintProcessors subkeys"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my @temps; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching printprocessors v.".$VERSION); + ::rptMsg("printprocessors v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my $ccs = ::getCCS($root_key); + my $pp_path = $ccs."\\Control\\Print\\Environments"; + my $pp; + if ($pp = $root_key->get_subkey($pp_path)) { + my @subkeys1 = $pp->get_list_of_subkeys(); + if (scalar @subkeys1 > 0) { + foreach my $s1 (@subkeys1) { + + if (my $prt = $s1->get_subkey("Print Processors")) { + my @subkeys2 = $prt->get_list_of_subkeys(); + if (scalar @subkeys2 > 0) { + foreach my $s2 (@subkeys2) { + eval { + if (my $driver = $s2->get_value("Driver")->get_data()) { + ::rptMsg(""); + ::rptMsg($pp_path."\\".$s1->get_name()."\\Print Processors\\".$s2->get_name()); + ::rptMsg("LastWrite time: ".::format8601Date($s2->get_timestamp())."Z"); + ::rptMsg("Driver value = ".$driver); + } + }; + + } + } + } + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Alternative Print Processors have been used for persistence. Verify unusual DLLs listed and"); + ::rptMsg("suspicious print processor names."); + ::rptMsg("https://www.welivesecurity.com/2020/05/21/no-game-over-winnti-group/"); + } + else { + ::rptMsg($pp_path." not found."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/privoxy.pl b/thirdparty/rr-full/plugins/privoxy.pl deleted file mode 100644 index a5a7881320c..00000000000 --- a/thirdparty/rr-full/plugins/privoxy.pl +++ /dev/null @@ -1,94 +0,0 @@ -#----------------------------------------------------------- -# privoxy.pl -# Extracts the install path for Privoxy -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright (c) 2011-02-04 Brendan Coles -#----------------------------------------------------------- -# Require # -package privoxy; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110204); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts the install path for Privoxy."; -} -sub getRefs { - my %refs = ("Privoxy Homepage:" => - "http://www.privoxy.org/"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching privoxy v.".$VERSION); - ::rptMsg("privoxy v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\Privoxy"; - - # If # Privoxy path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Privoxy"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Privoxy registry path # - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Privoxy registry path # - foreach my $v (@vals) { - ::rptMsg($v->get_name()." -> ".$v->get_data()); - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Privoxy isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/processor_architecture.pl b/thirdparty/rr-full/plugins/processor_architecture.pl index 4532d8e3198..876aa267d01 100644 --- a/thirdparty/rr-full/plugins/processor_architecture.pl +++ b/thirdparty/rr-full/plugins/processor_architecture.pl @@ -3,26 +3,31 @@ # # Gets the processor_architecture registry values from the system hive # +# Change history: +# 20200922 - MITRE Update +# # Ref: # # -# copyright 2014 Corey Harrell (jIIr) http://journeyintoir.blogspot.com/ -# Corey Harrell +# copyright 2020 QAR, LLC +# H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package processor_architecture; use strict; -my %config = (hive => "System", - osmask => 22, +my %config = (hive => "system", + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20140505); + output => "report", + version => 20200922); sub getConfig{return %config} sub getShortDescr { - return "Get from the processor architecture from the System's environment key"; + return "Get from the processor architecture System hive"; } sub getDescr{} sub getRefs {} @@ -73,7 +78,6 @@ sub pluginmain { } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } 1; diff --git a/thirdparty/rr-full/plugins/product.pl b/thirdparty/rr-full/plugins/product.pl deleted file mode 100644 index 056db05048d..00000000000 --- a/thirdparty/rr-full/plugins/product.pl +++ /dev/null @@ -1,120 +0,0 @@ -#----------------------------------------------------------- -# product.pl -# Plugin to determine the MSI packages installed on the system -# -# Change history: -# 20100325 - created -# -# References: -# http://support.microsoft.com/kb/236590 -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package product; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20100325); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get installed product info"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my %msi; - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching product v.".$VERSION); - ::rptMsg("product v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Windows\\CurrentVersion\\Installer\\UserData"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { -# Each of these subkeys should be SIDs - foreach my $s (@subkeys) { - next unless ($s->get_name() =~ m/^S/); - ::rptMsg($s->get_name()); - if ($s->get_subkey("Products")) { - processSIDKey($s->get_subkey("Products")); - ::rptMsg(""); - } - else { - ::rptMsg($s->get_name()."\\Products subkey not found."); - } - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub processSIDKey { - my $key = shift; - my %prod; - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { -# ::rptMsg($key->get_name()); - foreach my $s (@subkeys) { - my ($displayname,$lastwrite); - eval { - $displayname = $s->get_subkey("InstallProperties")->get_value("DisplayName")->get_data(); - $lastwrite = $s->get_subkey("InstallProperties")->get_timestamp(); - }; - - my $displayversion; - eval { - $displayversion = $s->get_subkey("InstallProperties")->get_value("DisplayVersion")->get_data(); - }; - - my $installdate; - eval { - $installdate = $s->get_subkey("InstallProperties")->get_value("InstallDate")->get_data(); - }; - - my $str = $displayname." v.".$displayversion.", ".$installdate; - push(@{$prod{$lastwrite}},$str); - } - - foreach my $t (reverse sort {$a <=> $b} keys %prod) { - ::rptMsg(gmtime($t)." Z"); - foreach my $i (@{$prod{$t}}) { - ::rptMsg(" ".$i); - } - } - - - } - else { - ::rptMsg($key->get_name()." has no subkeys."); - return; - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/productpolicy.pl b/thirdparty/rr-full/plugins/productpolicy.pl index 0928db5fc61..1fd32390987 100644 --- a/thirdparty/rr-full/plugins/productpolicy.pl +++ b/thirdparty/rr-full/plugins/productpolicy.pl @@ -1,34 +1,33 @@ #----------------------------------------------------------- # productpolicy.pl -# Extract/parse the ControlSet00x\Control\ProductOptions\ProductPolicy value -# -# NOTE: For Vista and 2008 ONLY; the value structure changed with Windows 7 -# -# Change History: -# 20091116 - created # -# Ref: -# http://www.geoffchappell.com/viewer.htm?doc=studies/windows/km/ntoskrnl/ -# api/ex/slmem/productpolicy.htm&tx=19 -# http://www.geoffchappell.com/viewer.htm?doc=notes/windows/license/ -# install.htm&tx=3,5,6;4 +# History: +# 20230804 - created +# +# References: +# https://twitter.com/0gtweet/status/1687353033716273152 # -# copyright 2009 H. Carvey, keydet89@yahoo.com +# Note: all of the values from the ProductPolicy value, and their data, are parsed into a +# Perl hash; that way, if any new values are found at a later date, they can also be extracted +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package productpolicy; use strict; -my %config = (hive => "System", - osmask => 22, +my %config = (hive => "system", + output => "report", + category => "", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20091116); + MITRE => "", + version => 20230804); sub getConfig{return %config} - sub getShortDescr { - return "Parse ProductPolicy value (Vista & Win2008 ONLY)"; + return "Get entries from ProductPolicy value"; } sub getDescr{} sub getRefs {} @@ -36,112 +35,67 @@ sub getShortDescr { sub getVersion {return $config{version};} my $VERSION = getVersion(); -my %prodinfo = (1 => "Ultimate", - 2 => "Home Basic", - 3 => "Home Premium", - 5 => "Home Basic N", - 6 => "Business", - 7 => "Standard", - 8 => "Data Center", - 10 => "Enterprise", - 11 => "Starter", - 12 => "Data Center Core", - 13 => "Standard Core", - 14 => "Enterprise Core", - 15 => "Business N"); - +my %files; +my @temps; + sub pluginmain { my $class = shift; my $hive = shift; - ::logMsg("Launching productpolicy v.".$VERSION); - ::rptMsg("productpolicy v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - - my $curr; - eval { - $curr = $root_key->get_subkey("Select")->get_value("Current")->get_data(); - }; - $curr = 1 if ($@); - + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\ProductOptions"; my $key; - my $key_path = "ControlSet00".$curr."\\Control\\ProductOptions"; if ($key = $root_key->get_subkey($key_path)) { - my $prod; + eval { - $prod = $key->get_value("ProductPolicy")->get_data(); - }; - if ($@) { - ::rptMsg("Error getting ProductPolicy value: $@"); - } - else { - my %pol = parseData($prod); - ::rptMsg(""); - ::rptMsg("Note: This plugin applies to Vista and Windows 2008 ONLY."); - ::rptMsg("For a listing of names and values, see:"); - ::rptMsg("http://www.geoffchappell.com/viewer.htm?doc=notes/windows/license/install.htm&tx=3,5,6;4"); - ::rptMsg(""); - foreach my $p (sort keys %pol) { - ::rptMsg($p." - ".$pol{$p}); - } - - if (exists $prodinfo{$pol{"Kernel\-ProductInfo"}}) { + my $p = $key->get_value("ProductPolicy")->get_data(); +# ::probe($p); + my %policy = processData($p); + if (exists $policy{"Security-SPP-LastWindowsActivationTime"}) { + ::rptMsg(""); + my ($t0,$t1) = unpack("VV",$policy{"Security-SPP-LastWindowsActivationTime"}); + ::rptMsg("Security-SPP-LastWindowsActivationTime : ".::format8601Date(::getTime($t0,$t1))."Z"); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Grzegorz/\@0gtweet discovered this data embedded in the ProductPolicy value; it may be"); + ::rptMsg("useful in determining the lifetime of the endpoint."); ::rptMsg(""); - ::rptMsg("Kernel\-ProductInfo = ".$prodinfo{$pol{"Kernel\-ProductInfo"}}); + ::rptMsg("Ref: https://twitter.com/0gtweet/status/1687353033716273152 "); } - } + }; + } else { ::rptMsg($key_path." not found."); } } -sub parseHeader { -# Ref: http://www.geoffchappell.com/viewer.htm?doc=studies/windows/km/ntoskrnl/ -# api/ex/slmem/productpolicy.htm&tx=19,21 - my %h; - my @v = unpack("V*",shift); - $h{size} = $v[0]; - $h{array} = $v[1]; - $h{marker} = $v[2]; - $h{version} = $v[4]; - return %h; -} - -sub parseData { - my $pd = shift; - my %policy; - my $h = substr($pd,0,0x14); - my %hdr = parseHeader($h); - my $total_size = $hdr{size}; - my $cursor = 0x14; +sub processData { + my $data = shift; + my $totSz = unpack("V",substr($data,0,4)); + my $ofs = 0x14; + my %pol = (); - while ($cursor <= $total_size) { - my @vals = unpack("v4V2", substr($pd,$cursor,0x10)); - my $value = substr($pd,$cursor,$vals[0]); - my $name = substr($value,0x10,$vals[1]); - $name =~ s/\x00//g; + while ($ofs < $totSz) { + my $eSz = unpack("v",substr($data,$ofs,2)); + my $eNameSz = unpack("v",substr($data,$ofs + 2,2)); + my $eDataSz = unpack("v",substr($data,$ofs + 6,2)); + my $name = substr($data,$ofs + 0x10,$eNameSz); + $name =~ s/\00//g; - my $data = substr($value,0x10 + $vals[1],$vals[3]); - if ($vals[2] == 4) { -# $data = sprintf "0x%x",unpack("V",$data); - $data = unpack("V",$data); - } - elsif ($vals[2] == 1) { - $data =~ s/\x00//g; - } - elsif ($vals[2] == 3) { - $data = unpack("H*",$data); - } - else { - - } - $policy{$name} = $data; - $cursor += $vals[0]; + my $blob = substr($data,$ofs + 0x10 + $eNameSz,$eDataSz); +# ::rptMsg(sprintf "Section size : 0x%x",$eSz); +# ::rptMsg(sprintf "Name size : 0x%x",$eNameSz); +# ::rptMsg(sprintf "Data size : 0x%x",$eDataSz); +# ::rptMsg("Name : ".$name); +# ::rptMsg(""); +# ::probe($data); +# ::rptMsg(""); + $pol{$name} = $blob; + $ofs += $eSz; } - delete $policy{""}; - return %policy; + return %pol; } + 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/producttype.pl b/thirdparty/rr-full/plugins/producttype.pl deleted file mode 100644 index bd56ee69569..00000000000 --- a/thirdparty/rr-full/plugins/producttype.pl +++ /dev/null @@ -1,90 +0,0 @@ -#----------------------------------------------------------- -# producttype.pl -# Determine Windows product information -# -# History -# 20100713 - updated reference info, formatting -# 20100325 - renamed to producttype.pl -# -# References -# http://support.microsoft.com/kb/181412 -# http://support.microsoft.com/kb/152078 -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package producttype; -use strict; -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100325); - -sub getConfig{return %config} -sub getShortDescr { - return "Queries System hive for Windows Product info"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching producttype v.".$VERSION); - ::rptMsg("producttype v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - my $prod_key_path = $ccs."\\Control\\ProductOptions"; - if (my $prod_key = $root_key->get_subkey($prod_key_path)) { - ::rptMsg($prod_key_path); - ::rptMsg("LastWrite = ".gmtime($prod_key->get_timestamp())); - ::rptMsg(""); - ::rptMsg("Ref: http://support.microsoft.com/kb/152078"); - ::rptMsg(" http://support.microsoft.com/kb/181412"); - ::rptMsg(""); - my $type; - eval { - $type = $prod_key->get_value("ProductType")->get_data(); - ::rptMsg("ProductType = ".$type); - ::rptMsg("Ref: http://technet.microsoft.com/en-us/library/cc782360%28WS.10%29.aspx"); - ::rptMsg("WinNT indicates a workstation."); - ::rptMsg("ServerNT indicates a standalone server."); - ::rptMsg("LanmanNT indicates a domain controller (pri/backup)."); - }; - ::rptMsg(""); -#----------------------------------------------------------- -# http://technet.microsoft.com/en-us/library/cc784364(WS.10).aspx -# -# http://www.geoffchappell.com/viewer.htm?doc=studies/windows/ -# km/ntoskrnl/api/ex/exinit/productsuite.htm -# -#----------------------------------------------------------- - my $suite; - eval { - $suite = $prod_key->get_value("ProductSuite")->get_data(); - ::rptMsg("ProductSuite = ".$suite); - ::rptMsg("Ref: http://technet.microsoft.com/en-us/library/cc784364%28WS.10%29.aspx"); - }; - } - else { - ::rptMsg($prod_key_path." not found."); - } - } - else { - ::rptMsg("Select key not found."); - } -} -1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/profilelist.pl b/thirdparty/rr-full/plugins/profilelist.pl index d06e9f4deb0..f7d3ada2ba5 100644 --- a/thirdparty/rr-full/plugins/profilelist.pl +++ b/thirdparty/rr-full/plugins/profilelist.pl @@ -1,26 +1,28 @@ #----------------------------------------------------------- # profilelist.pl -# Gets ProfileList subkeys and ProfileImagePath value; also -# gets the ProfileLoadTimeHigh and Low values, and translates them -# into a readable time +# Gets ProfileList subkeys and ProfileImagePath value # # History: +# 20200922 - MITRE update +# 20200518 - updated date output format # 20100219 - updated to gather SpecialAccounts and domain # user info # 20080415 - created # # -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC #----------------------------------------------------------- package profilelist; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "software", + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20100219); + output => "report", + version => 20200922); sub getConfig{return %config} @@ -49,7 +51,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); +# ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @subkeys = $key->get_list_of_subkeys(); @@ -62,7 +64,7 @@ sub pluginmain { ::rptMsg("Path : ".$path); ::rptMsg("SID : ".$s->get_name()); - ::rptMsg("LastWrite : ".gmtime($s->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite : ".::format8601Date($s->get_timestamp())."Z"); my $user; if ($path) { @@ -71,31 +73,20 @@ sub pluginmain { $user = $a[$end]; $profiles{$s->get_name()} = $user; } - - my @load; - eval { - $load[0] = $s->get_value("ProfileLoadTimeLow")->get_data(); - $load[1] = $s->get_value("ProfileLoadTimeHigh")->get_data(); - }; - if (@load) { - my $loadtime = ::getTime($load[0],$load[1]); - ::rptMsg("LoadTime : ".gmtime($loadtime)." (UTC)"); - } + ::rptMsg(""); } } else { ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } # The following was added 20100219 - $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; if ($key = $root_key->get_subkey($key_path)) { my @subkeys = $key->get_list_of_subkeys(); if (scalar @subkeys > 0) { @@ -136,4 +127,4 @@ sub pluginmain { } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/profiler.pl b/thirdparty/rr-full/plugins/profiler.pl index 5f4daf6ff3d..f7925801e87 100644 --- a/thirdparty/rr-full/plugins/profiler.pl +++ b/thirdparty/rr-full/plugins/profiler.pl @@ -3,29 +3,31 @@ # # # Change history +# 20200922 - MITRE update +# 20200525 - updated date output format # 20140508 - created # # References # http://www.hexacorn.com/blog/2014/04/27/beyond-good-ol-run-key-part-11/ -# -# Copyright 2014 QAR, LLC +# https://attack.mitre.org/techniques/T1574/012/ +# +# Copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- -# Require # package profiler; use strict; -# Declarations # my %config = (hive => "NTUSER\.DAT, System", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - category => "autostart", - version => 20140510); + MITRE => "T1574\.012", + category => "persistence", + output => "report", + version => 20200922); + my $VERSION = getVersion(); -# Functions # sub getConfig {return %config} sub getHive {return $config{hive};} sub getVersion {return $config{version};} @@ -40,8 +42,10 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching profiler v.".$VERSION); - ::rptMsg("profiler v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + ::rptMsg("profiler v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key; @@ -51,9 +55,8 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { - # Return # plugin name, registry key and last modified date # ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); diff --git a/thirdparty/rr-full/plugins/protectedview.pl b/thirdparty/rr-full/plugins/protectedview.pl new file mode 100644 index 00000000000..4c982020a3c --- /dev/null +++ b/thirdparty/rr-full/plugins/protectedview.pl @@ -0,0 +1,108 @@ +#----------------------------------------------------------- +# protectedview.pl +# Get MSOffice settings for ProtectedView +# +# Change history +# 20220301 - created +# +# References +# https://www.huntress.com/blog/targeted-apt-activity-babyshark-is-out-for-blood +# https://admx.help/?Category=Office2016&Policy=excel16.Office.Microsoft.Policies.Windows::L_TurnOffProtectedViewForAttachmentsOpenedFromOutlook +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package protectedview; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562\.001", + output => "report", + version => 20220301); + +sub getConfig{return %config} +sub getShortDescr { + return "Get MSOffice ProtectedView settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $office_version; + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching protectedview v.".$VERSION); + ::rptMsg("protectedview v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg("protectedview v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE ATT&CK: ".$config{MITRE}." (".$config{category}.")\n"); + ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + +# System: Software\Microsoft\Office\$office_version\Word\Security\ProtectedView +# GPO : Software\Policies\Microsoft\Office\$office_version\Excel\Security\ProtectedView + + my @vals_to_query = ("DisableAttachmentsinPV", "DisableInternetFilesinPV", "DisableUnsafeLocationsinPV"); + my @apps = ("Word","Excel"); + my @paths = ("Software\\Microsoft\\Office","Software\\Policies\\Microsoft\\Office"); + + foreach my $p (@paths) { + foreach my $a (@apps) { + my $key_path = $p."\\".$office_version."\\".$a."\\Security\\ProtectedView"; + if (my $key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + foreach my $v (@vals_to_query) { + eval { + my $d = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-25s %-2s",$v,$d); + }; + ::rptMsg($v." value not found.") if ($@); + } + } + else { + ::rptMsg($key_path." not found."); + } + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Huntress analyst's write-up on BABYSHARK indicates that the threat actors modify these"); + ::rptMsg("Registry values."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.huntress.com/blog/targeted-apt-activity-babyshark-is-out-for-blood"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/proxysettings.pl b/thirdparty/rr-full/plugins/proxysettings.pl deleted file mode 100644 index 7864174ac51..00000000000 --- a/thirdparty/rr-full/plugins/proxysettings.pl +++ /dev/null @@ -1,72 +0,0 @@ -#----------------------------------------------------------- -# proxysettings.pl -# Plugin for Registry Ripper, -# Internet Explorer ProxySettings key parser -# -# Change history -# 20081224 - H. Carvey, updated sorting and printing routine -# -# -# copyright 2008 C. Bentley -#----------------------------------------------------------- -package proxysettings; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20081224); - -sub getConfig{return %config} -sub getShortDescr {return "Gets contents of user's Proxy Settings";} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching proxysettings v.".$VERSION); - ::rptMsg("proxysettings v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("ProxySettings"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %proxy; - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); - my $type = $v->get_type(); - $data = unpack("V",$data) if ($type == 3); - $proxy{$name} = $data; - } - foreach my $n (sort keys %proxy) { - my $str = sprintf " %-30s %-30s",$n,$proxy{$n}; - ::rptMsg($str); -# ::rptMsg(" ".$v->get_name()." ".$v->get_data()); - } - } - else { - ::rptMsg($key_path." key has no values."); - ::logMsg($key_path." key has no values."); - } - } - else { - ::rptMsg($key_path." hat key not found."); - ::logMsg($key_path." hat key not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/pslogging.pl b/thirdparty/rr-full/plugins/pslogging.pl index d44ba6ae9cf..9c51b74e18e 100644 --- a/thirdparty/rr-full/plugins/pslogging.pl +++ b/thirdparty/rr-full/plugins/pslogging.pl @@ -3,29 +3,31 @@ # # # Change history +# 20200922 - MITRE update +# 20200515 - minor updates # 20181209 - created # # References # https://getadmx.com/?Category=Windows_10_2016&Policy=Microsoft.Policies.PowerShell::EnableTranscripting # # -# Copyright (c) 2018 QAR, LLC +# Copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package pslogging; use strict; -# Declarations # my %config = (hive => "NTUSER\.DAT, Software", hasShortDescr => 0, hasDescr => 1, hasRefs => 0, - osmask => 22, - category => "config settings", - version => 20181209); + MITRE => "", + category => "config", + output => "report", + version => 20200922); + my $VERSION = getVersion(); -# Functions # sub getConfig {return %config} sub getHive {return $config{hive};} sub getVersion {return $config{version};} @@ -36,12 +38,9 @@ sub getShortDescr { sub getRefs {} sub pluginmain { - - # Declarations # my $class = shift; my $hive = shift; - # Initialize # ::logMsg("Launching pslogging v.".$VERSION); ::rptMsg("pslogging v.".$VERSION); ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); diff --git a/thirdparty/rr-full/plugins/psscript.pl b/thirdparty/rr-full/plugins/psscript.pl index f37eabe3094..1e2cde7c746 100644 --- a/thirdparty/rr-full/plugins/psscript.pl +++ b/thirdparty/rr-full/plugins/psscript.pl @@ -1,8 +1,6 @@ #----------------------------------------------------------- # psscript.pl # -# -# # http://www.hexacorn.com/blog/2017/01/07/beyond-good-ol-run-key-part-52/ # # Also, check folders: @@ -11,24 +9,29 @@ # # # Change history +# 20200922 - MITRE update +# 20200525 - updated date output format # 20170107 - created # -# Copyright 2017 QAR, LLC +# Copyright 2020 QAR, LLC +# H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package psscript; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "Software, NTUSER\.DAT", + MITRE => "T1546", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20170107); + output => "report", + version => 20200922); sub getConfig{return %config} sub getShortDescr { - return "Get PSScript\.ini values"; + return "Get values assoc with PSScript\.ini"; } sub getDescr{} sub getRefs {} @@ -42,23 +45,34 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching psscript v.".$VERSION); + ::rptMsg("psscript v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; -# updated added 20130326 my @paths = ("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Startup\\0\\0", - "Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup\\0\\0"); + "Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup\\0\\0", + "Microsoft\\Windows\\CurrentVersion\\Group Policy\\History\\{42B5FAAE-6536-11d2-AE5A-0000F87571E3}\\0"); foreach my $key_path (@paths) { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); if (scalar @vals > 0) { foreach my $v (@vals) { ::rptMsg($v->get_name()." - ".$v->get_data()); + + if ($v->get_name() eq "ExecTime") { + my $t = ::convertSystemTime($v->get_data()); + ::rptMsg("ExecTime: ".$t); + + } + } ::rptMsg(""); } @@ -78,7 +92,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg(""); ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); + ::rptMsg("LastWrite: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); diff --git a/thirdparty/rr-full/plugins/publishingwizard.pl b/thirdparty/rr-full/plugins/publishingwizard.pl deleted file mode 100644 index 19bb5f6a470..00000000000 --- a/thirdparty/rr-full/plugins/publishingwizard.pl +++ /dev/null @@ -1,100 +0,0 @@ -#----------------------------------------------------------- -# publishingwizard.pl -# Extract Extract AddNetPlace\\LocationMRU -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright (c) 2011-02-02 Brendan Coles -#----------------------------------------------------------- -# Require # -package publishingwizard; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110202); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extract AddNetPlace\\LocationMRU for Microsoft Publishing Wizard"; -} -sub getRefs { - my %refs = ("Microsoft Publishing Wizard Homepage:" => - "http://www.microsoft.com/downloads/details.aspx?FamilyId=56E5B1C5-BF17-42E0-A410-371A838E570A"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching publishingwizard v.".$VERSION); - ::rptMsg("publishingwizard v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\PublishingWizard\\AddNetworkPlace\\AddNetPlace\\LocationMRU"; - - # If # Publishing Wizard path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Publishing Wizard"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Publishing Wizard registry path # - my %keys; - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Publishing Wizard registry path # - foreach my $v (@vals) { - $keys{$v->get_name()} = $v->get_data(); - } - - # Return # all key names+values # - foreach (sort keys %keys) { - ::rptMsg($_." -> ".$keys{$_}); - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Publishing Wizard isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/putty.pl b/thirdparty/rr-full/plugins/putty.pl index 03fdb714f24..01c84fd286b 100644 --- a/thirdparty/rr-full/plugins/putty.pl +++ b/thirdparty/rr-full/plugins/putty.pl @@ -3,26 +3,29 @@ # Extracts the saved SshHostKeys for PuTTY # # Change history -# 20110830 [fpi] + banner, no change to the version number +# 20200924 - MITRE update +# 20200515 - date output format updated +# 20110830 - created # # References # -# copyright (c) 2011-02-04 Brendan Coles +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- -# Require # package putty; use strict; -# Declarations # my %config = (hive => "NTUSER\.DAT", hasShortDescr => 1, hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110204); + hasRefs => 0, + MITRE => "T1021", + category => "lateral movement", + output => "report", + version => 20200924); + my $VERSION = getVersion(); -# Functions # sub getDescr {} sub getConfig {return %config} sub getHive {return $config{hive};} @@ -30,67 +33,46 @@ package putty; sub getShortDescr { return "Extracts the saved SshHostKeys for PuTTY."; } -sub getRefs { - my %refs = ("PuTTY Homepage:" => - "http://www.chiark.greenend.org.uk/~sgtatham/putty/"); - return %refs; -} +sub getRefs {} -############################################################ -# pluginmain # -############################################################ sub pluginmain { - - # Declarations # my $class = shift; my $hive = shift; - # Initialize # ::logMsg("Launching putty v.".$VERSION); - ::rptMsg("putty v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner + ::rptMsg("putty v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key; my $key_path = "Software\\SimonTatham\\PuTTY\\SshHostKeys"; - # If # PuTTY path exists # if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # ::rptMsg("PuTTY"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); - # Extract # all keys from PuTTY registry path # my %keys; my @vals = $key->get_list_of_values(); - # If # registry keys exist in path # if (scalar(@vals) > 0) { - - # Extract # all key names+values for PuTTY registry path # foreach my $v (@vals) { $keys{$v->get_name()} = $v->get_data(); ::rptMsg($v->get_name()." -> ".$v->get_data()); } - - # Error # key value is null # - } else { + } + else { ::rptMsg($key_path." has no values."); } - - # Error # PuTTY isn't here, try another castle # - } else { + } + else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } - - # Return # obligatory new-line # ::rptMsg(""); } -# Error # oh snap! # 1; diff --git a/thirdparty/rr-full/plugins/putty_sessions.pl b/thirdparty/rr-full/plugins/putty_sessions.pl deleted file mode 100644 index a989b11a19c..00000000000 --- a/thirdparty/rr-full/plugins/putty_sessions.pl +++ /dev/null @@ -1,106 +0,0 @@ -#----------------------------------------------------------- -# putty_sessions.pl -# Extracts the sessions for PuTTY -# -# Change history -# 20170321 Created -# -# No copyright: Mark McCurdy -#----------------------------------------------------------- -# Require # -package putty_sessions; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20170321); -my $VERSION = getVersion(); - -my @ReturnValues = ("HostName", "LogFileName", "LogType", "LogFlush", "SSHLogOmitPasswords", \ - "SSHLogOmitData", "Protocol", "PortNumber", "TerminalType", "ProxyDNS", "ProxyLocalhost", \ - "ProxyMethod", "ProxyHost", "ProxyPort", "ProxyUsername", "ProxyPassword", "UserName", \ - "LocalUserName", "AgentFwd", "PublicKeyFile", "RemoteCommand", "PortForwardings"); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts the saved sessions for PuTTY."; -} -sub getRefs { - my %refs = ("PuTTY Homepage:" => - "http://www.chiark.greenend.org.uk/~sgtatham/putty/"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching putty_sessions v.".$VERSION); - ::rptMsg("putty_sessions v.".$VERSION); # 20170321 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\SimonTatham\\PuTTY\\Sessions"; - - # If # PuTTY path exists # - if ($key = $root_key->get_subkey($key_path)) { - - ::rptMsg("PuTTY"); - - my $session; - my @skarray; - @skarray = $key->get_list_of_subkeys(); - foreach my $session (@skarray) { - - # Return last modified date # - ::rptMsg("LastWrite Time ".gmtime($session->get_timestamp())." (UTC)"); - - # Extract # all keys from PuTTY registry path # - my %keys; - my @vals = $session->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for PuTTY registry path # - foreach my $v (@vals) { - if (grep { $v->get_name() eq $_ } @ReturnValues) { - $keys{$v->get_name()} = $v->get_data(); - ::rptMsg($v->get_name()." -> ".$v->get_data()); - } - } - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - ::rptMsg(""); - } - - # Error # PuTTY isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/railrunonce.pl b/thirdparty/rr-full/plugins/railrunonce.pl new file mode 100644 index 00000000000..deec00b4a4a --- /dev/null +++ b/thirdparty/rr-full/plugins/railrunonce.pl @@ -0,0 +1,70 @@ +#----------------------------------------------------------- +# railrunonce.pl +# The Run keys are only processed when the Explorer shell is started. +# With RemoteApp, Explorer is not the shell but rather the Remote Desktop +# service provides a shell for the application. +# +# References: +# https://blog.truesec.com/2020/07/10/onedrive-with-remote-desktop-services/ +# +# Change history: +# 20201020 - created +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package railrunonce; +use strict; + +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1547\.001", + category => "persistence", + output => "report", + version => 20201020); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks RemoteApp shell persistence"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching railrunonce v.".$VERSION); + ::rptMsg("railrunonce v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path; + my $key; + +# System Hive + my $ccs = ::getCCS($root_key); + + $key_path = $ccs."\\Control\\Terminal Server\\RailRunonce"; + if ($key = $root_key->get_subkey($key_path)){ + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-25s %-50s",$v->get_name(),$v->get_data()); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: The RailRunonce key serves the same purpose as the local Run keys, albeit for RemoteApp."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/rdphint.pl b/thirdparty/rr-full/plugins/rdphint.pl deleted file mode 100644 index 13a54581593..00000000000 --- a/thirdparty/rr-full/plugins/rdphint.pl +++ /dev/null @@ -1,63 +0,0 @@ -#----------------------------------------------------------- -# rdphint.pl - http://www.regripper.net/ -# Gathers servers logged onto via RDP and last successful username -# -# by Brandon Nesbit, Trustwave -#----------------------------------------------------------- -package rdphint; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20090715); - -sub getConfig{return %config} -sub getShortDescr { return "Gets hosts logged onto via RDP and the Domain\\Username";} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching rdphint v.".$VERSION); - ::rptMsg("rdphint v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = 'Software\\Microsoft\\Terminal Server Client\\Servers'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Terminal Server Client\\Servers"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - my $path; - eval { - $path = $s->get_value("UsernameHint")->get_data(); - }; - ::rptMsg(""); - ::rptMsg("Hostname: ".$s->get_name()); - ::rptMsg("Domain/Username: ".$path); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())." (UTC)"); - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/rdplockout.pl b/thirdparty/rr-full/plugins/rdplockout.pl new file mode 100644 index 00000000000..d747b1333e9 --- /dev/null +++ b/thirdparty/rr-full/plugins/rdplockout.pl @@ -0,0 +1,77 @@ +#----------------------------------------------------------- +# rdplockout.pl +# Determine the RDP Port used +# +# History +# 20220809 - created +# +# References +# https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-remote-access-client-account-lockout +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package rdplockout; +use strict; +my %config = (hive => "System", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1133", + category => "initial access", + output => "report", + version => 20220809); + +sub getConfig{return %config} +sub getShortDescr { + return "Queries System hive for RDP Lockout Settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $key; + + ::logMsg("Launching rdplockout v.".$VERSION); + ::rptMsg("rdplockout v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services\\RemoteAccess\\Parameters\\AccountLockout"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg("rdplockout v.".$VERSION); + ::rptMsg(""); + + eval { + my $max = $key->get_value("MaxDenials")->get_data(); + ::rptMsg("MaxDenials = ".$max); + }; + ::rptMsg("Error getting MaxDenials value: ".$@) if ($@); + + + eval { + my $res = $key->get_value("ResetTime (mins)")->get_data(); + ::rptMsg("ResetTime (mins) = ".$res); + }; + ::rptMsg("Error getting ResetTime (mins) value: ".$@) if ($@); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Values retrieved indicate account lockout settings for Remote Access."); + ::rptMsg("Also, look for a \"Domain Name:User Name\" value."); + ::rptMsg(""); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-remote-access-client-account-lockout"); + } + else { + ::rptMsg($key_path." not found."); + } +} +1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/rdpnla.pl b/thirdparty/rr-full/plugins/rdpnla.pl deleted file mode 100644 index 1dc90e7d278..00000000000 --- a/thirdparty/rr-full/plugins/rdpnla.pl +++ /dev/null @@ -1,56 +0,0 @@ -#----------------------------------------------------------- -# rdpnla.pl -# -# 20151203 - created -# -# Author: Chakib Gzenayi, chakib.gzenayi@gmail.com -#----------------------------------------------------------- -package rdpnla; -use strict; -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20151203); - -sub getConfig{return %config} -sub getShortDescr { - return "Queries System hive for RDP NLA Checking"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my $key; - - ::logMsg("Launching rdpnla v.".$VERSION); - ::rptMsg("rdpnla v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $chak = $root_key->get_subkey("Select")->get_value("Current")->get_data(); - my $key_path = "ControlSet00".$chak."\\Control\\Terminal Server\\WinStations\\RDP-Tcp"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my $sec; - eval { - $sec = $key->get_value("SecurityLayer")->get_data(); - ::rptMsg("SecurityLayer = ".$sec ); - }; - ::rptMsg("Error getting Value: ".$@) if ($@); - - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/rdpport.pl b/thirdparty/rr-full/plugins/rdpport.pl index 9213abd731c..ce41c69ed0f 100644 --- a/thirdparty/rr-full/plugins/rdpport.pl +++ b/thirdparty/rr-full/plugins/rdpport.pl @@ -3,12 +3,16 @@ # Determine the RDP Port used # # History +# 20220809 - updated MITRE ATT&CK +# 20200922 - MITRE update +# 20200526 - minor updates # 20100713 - created # # References # http://support.microsoft.com/kb/306759 # -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package rdpport; use strict; @@ -16,8 +20,10 @@ package rdpport; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20100713); + MITRE => "T1133", + category => "initial access", + output => "report", + version => 20220809); sub getConfig{return %config} sub getShortDescr { @@ -36,13 +42,15 @@ sub pluginmain { my $key; ::logMsg("Launching rdpport v.".$VERSION); - ::rptMsg("rdpport v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("rdpport v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - my $ccs = $root_key->get_subkey("Select")->get_value("Current")->get_data(); - my $key_path = "ControlSet00".$ccs."\\Control\\Terminal Server\\WinStations\\RDP-Tcp"; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\Terminal Server\\WinStations\\RDP-Tcp"; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("rdpport v.".$VERSION); ::rptMsg(""); @@ -50,6 +58,8 @@ sub pluginmain { eval { $port = $key->get_value("PortNumber")->get_data(); ::rptMsg("Remote Desktop Listening Port Number = ".$port); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Modifying the RDP port number can be considered a defense evasion/masquerading technique."); }; ::rptMsg("Error getting PortNumber: ".$@) if ($@); diff --git a/thirdparty/rr-full/plugins/reading_locations.pl b/thirdparty/rr-full/plugins/reading_locations.pl deleted file mode 100644 index 5344120c30f..00000000000 --- a/thirdparty/rr-full/plugins/reading_locations.pl +++ /dev/null @@ -1,86 +0,0 @@ -#----------------------------------------------------------- -# reading_locations.pl -# Plugin to get MS Office 2013 Reading Locations' subkey data from NTUSER.DAT -# -# Change history -# 20140130 - created -# 20190211 - added "paragraphID" int to hex conversion -# -# References -# http://dfstream.blogspot.com/2014/01/ms-word-2013-reading-locations.html -# -# Author: Jason Hale -#----------------------------------------------------------- -package reading_locations; -use strict; - -my %config = (hive => "NTUSER\.DAT", - #hivemask => 32, - output => "report", - category => "User Activity", - osmask => 60, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20140130); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets user's MS Word 2013 Reading Locations"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching reading_locations v.".$VERSION); - ::rptMsg("reading_locations v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Office\\15.0\\Word\\Reading Locations'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - my $name = $s->get_name(); - my $lw = $s->get_timestamp(); - ::rptMsg($name); - ::rptMsg("LastWrite: ".gmtime($lw)." UTC"); - - eval { - my $dt = $s->get_value("Datetime")->get_data(); - ::rptMsg("Datetime: ".$dt); - }; - - eval { - my $fp = $s->get_value("File Path")->get_data(); - ::rptMsg("File Path: ".$fp); - }; - - eval { - my $p = $s->get_value("Position")->get_data(); - my @ps = split(' ', $p); - my $paraid = sprintf("%X", $ps[0]); - ::rptMsg("Position: ".$p." (ParagraphID: ".$paraid.")"); - }; - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." key has no subkeys\."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/real_profilelist.pl b/thirdparty/rr-full/plugins/real_profilelist.pl deleted file mode 100644 index 6a5a7293f16..00000000000 --- a/thirdparty/rr-full/plugins/real_profilelist.pl +++ /dev/null @@ -1,139 +0,0 @@ -#----------------------------------------------------------- -# profilelist.pl -# Gets ProfileList subkeys and ProfileImagePath value; also -# gets the ProfileLoadTimeHigh and Low values, and translates them -# into a readable time -# -# History: -# 20100219 - updated to gather SpecialAccounts and domain -# user info -# 20080415 - created -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package profilelist; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20100219); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get content of ProfileList key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - - my %profiles; - - ::logMsg("Launching profilelist v.".$VERSION); - ::rptMsg("profilelist v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\ProfileList"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - my $path; - eval { - $path = $s->get_value("ProfileImagePath")->get_data(); - }; - - ::rptMsg("Path : ".$path); - ::rptMsg("SID : ".$s->get_name()); - ::rptMsg("LastWrite : ".gmtime($s->get_timestamp())." (UTC)"); - - my $user; - if ($path) { - my @a = split(/\\/,$path); - my $end = scalar @a - 1; - $user = $a[$end]; - $profiles{$s->get_name()} = $user; - } - - my @load; - eval { - $load[0] = $s->get_value("ProfileLoadTimeLow")->get_data(); - $load[1] = $s->get_value("ProfileLoadTimeHigh")->get_data(); - }; - if (@load) { - my $loadtime = ::getTime($load[0],$load[1]); - ::rptMsg("LoadTime : ".gmtime($loadtime)." (UTC)"); - } - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - -# The following was added 20100219 - my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; - if ($key = $root_key->get_subkey($key_path)) { - my @subkeys = $key->get_list_of_subkeys(); - if (scalar @subkeys > 0) { - ::rptMsg("Domain Accounts"); - foreach my $s (@subkeys) { - my $name = $s->get_name(); - next unless ($name =~ m/^S\-1/); - - (exists $profiles{$name}) ? (::rptMsg($name." [".$profiles{$name}."]")) - : (::rptMsg($name)); -# ::rptMsg("LastWrite time: ".gmtime($s->get_timestamp())); -# ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - -# Domain Cache? - eval { - my @cache = $key->get_subkey("DomainCache")->get_list_of_values(); - if (scalar @cache > 0) { - ::rptMsg(""); - ::rptMsg("DomainCache"); - foreach my $d (@cache) { - my $str = sprintf "%-15s %-20s",$d->get_name(),$d->get_data(); - ::rptMsg($str); - } - } - }; - - - } - else { - ::rptMsg($key_path." not found."); - } - - - -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/realplayer6.pl b/thirdparty/rr-full/plugins/realplayer6.pl deleted file mode 100644 index aaa70ef3258..00000000000 --- a/thirdparty/rr-full/plugins/realplayer6.pl +++ /dev/null @@ -1,78 +0,0 @@ -#----------------------------------------------------------- -# realplayer6.pl -# Plugin for Registry Ripper -# Get Real Player 6 MostRecentClipsx values -# -# Change history -# -# -# References -# -# Note: LastWrite times on c subkeys will all be the same, -# as each subkey is modified as when a new entry is added -# -# copyright 2008 H. Carvey -#----------------------------------------------------------- -package realplayer6; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080324); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets user's RealPlayer v6 MostRecentClips(Default) values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching realplayer6 v.".$VERSION); - ::rptMsg("realplayer6 v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\RealNetworks\\RealPlayer\\6.0\\Preferences"; - my $key = $root_key->get_subkey($key_path); - if ($key) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my %rpkeys; - my $tag = "MostRecentClips"; - my @subkeys = $key->get_list_of_subkeys(); - if (scalar @subkeys > 0) { - foreach my $s (@subkeys) { - my $name = $s->get_name(); - if ($name =~ m/^$tag/) { - my $num = $name; - $num =~ s/$tag//; - $rpkeys{$num}{name} = $name; - $rpkeys{$num}{data} = $s->get_value('')->get_data(); - $rpkeys{$num}{lastwrite} = $s->get_timestamp(); - } - } - foreach my $k (sort keys %rpkeys) { - ::rptMsg("\t".$rpkeys{$k}{name}." -> ".$rpkeys{$k}{data}); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; diff --git a/thirdparty/rr-full/plugins/realvnc.pl b/thirdparty/rr-full/plugins/realvnc.pl deleted file mode 100644 index 77a35aa36a2..00000000000 --- a/thirdparty/rr-full/plugins/realvnc.pl +++ /dev/null @@ -1,77 +0,0 @@ -#----------------------------------------------------------- -# realvnc.pl -# Plugin to get RealVNC MRU listings from NTUSER.DAT -# -# Change history -# 20091125 - created -# -# References -# -# copyright 2009 H. Carvey -#----------------------------------------------------------- -package realvnc; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20091125); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets user's RealVNC MRU listing"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching realvnc v.".$VERSION); - ::rptMsg("realvnc v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\RealVNC\\VNCViewer4\\MRU'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %mru; - my @order; - foreach my $v (@vals) { - $mru{$v->get_name()} = $v->get_data(); - } - - if (exists($mru{Order})) { - @order = unpack("C*",$mru{Order}); -# List systems connected to based on Order MRU value - ::rptMsg("*Systems output in \"Order\" sequence"); - foreach my $i (0..scalar(@order) - 1) { - $order[$i] = "0".$order[$i] if ($order[$i] < 10); - ::rptMsg(" ".$order[$i]." -> ".$mru{$order[$i]}); - } - } - else { - ::rptMsg("Could not find Order value."); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/recentapps.pl b/thirdparty/rr-full/plugins/recentapps.pl index 19e44e73f90..96ea199a2a8 100644 --- a/thirdparty/rr-full/plugins/recentapps.pl +++ b/thirdparty/rr-full/plugins/recentapps.pl @@ -2,12 +2,15 @@ # recentapps.pl # # Change history +# 20200922 - MITRE update +# 20200515 - updated date output format # 20171013 - created # # References -# https://twitter.com/EricRZimmerman/status/916422135987474433 +# https://df-stream.com/2017/10/recentapps/ # -# copyright 2017 H. Carvey, keydet89@yahoo.com +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package recentapps; use strict; @@ -16,8 +19,10 @@ package recentapps; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20171013); + MITRE => "", + category => "user activity", + output => "report", + version => 20200922); sub getConfig{return %config} sub getShortDescr { @@ -49,7 +54,7 @@ sub pluginmain { ::rptMsg("AppId : ".$s->get_value("AppId")->get_data()); my ($t1,$t2) = unpack("VV",$s->get_value("LastAccessedTime")->get_data()); my $lat = ::getTime($t1,$t2); - ::rptMsg("LastAccessedTime: ".gmtime($lat)." UTC"); + ::rptMsg("LastAccessedTime: ".::format8601Date($lat)."Z"); ::rptMsg("LaunchCount : ".$s->get_value("LaunchCount")->get_data()); }; @@ -62,7 +67,7 @@ sub pluginmain { ::rptMsg(" Path : ".$r->get_value("Path")->get_data()); my ($l1,$l2) = unpack("VV",$r->get_value("LastAccessedTime")->get_data()); my $l = ::getTime($l1,$l2); - ::rptMsg(" LastAccessedTime: ".gmtime($l)." UTC"); + ::rptMsg(" LastAccessedTime: ".::format8601Date($l)."Z"); ::rptMsg(""); }; } @@ -70,6 +75,8 @@ sub pluginmain { } ::rptMsg(""); } + ::rptMsg("Analysis Tip: Info about apps accessed by the user."); + ::rptMsg("https://df-stream.com/2017/10/recentapps/"); } else { ::rptMsg($key_path." has no subkeys."); diff --git a/thirdparty/rr-full/plugins/recentapps_tln.pl b/thirdparty/rr-full/plugins/recentapps_tln.pl index 3a6740ea2de..253f76ceb4f 100644 --- a/thirdparty/rr-full/plugins/recentapps_tln.pl +++ b/thirdparty/rr-full/plugins/recentapps_tln.pl @@ -2,13 +2,14 @@ # recentapps_tln.pl # # Change history +# 20200922 - MITRE update # 20190513 - updated timestamp issue # 20171013 - created # # References -# https://twitter.com/EricRZimmerman/status/916422135987474433 +# https://df-stream.com/2017/10/recentapps/ # -# copyright 2017 H. Carvey, keydet89@yahoo.com +# copyright 2020 H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package recentapps_tln; use strict; @@ -17,8 +18,10 @@ package recentapps_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20190513); + MITRE => "", + category => "user activity", + output => "tln", + version => 20200922); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/recentdocs.pl b/thirdparty/rr-full/plugins/recentdocs.pl index 4714db684a8..768c299e43b 100644 --- a/thirdparty/rr-full/plugins/recentdocs.pl +++ b/thirdparty/rr-full/plugins/recentdocs.pl @@ -4,6 +4,8 @@ # Parses RecentDocs keys/values in NTUSER.DAT # # Change history +# 20200924 - MITRE update +# 20200427 - updated output date format # 20100405 - Updated to use Encode::decode to translate strings # 20090115 - Minor update to keep plugin from printing terminating # MRUListEx value of 0xFFFFFFFF @@ -14,7 +16,7 @@ # References # # -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC #----------------------------------------------------------- package recentdocs; use strict; @@ -24,9 +26,12 @@ package recentdocs; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20100405); + MITRE => "", + category => "user activity", + output => "report", + version => 20200924); +sub getConfig {return %config} sub getShortDescr { return "Gets contents of user's RecentDocs key"; } @@ -42,7 +47,7 @@ sub pluginmain { my $ntuser = shift; ::logMsg("Launching recentdocs v.".$VERSION); ::rptMsg("recentdocs v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -52,7 +57,7 @@ sub pluginmain { ::rptMsg("RecentDocs"); ::rptMsg("**All values printed in MRUList\\MRUListEx order."); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); # Get RecentDocs values my %rdvals = getRDValues($key); if (%rdvals) { @@ -82,7 +87,7 @@ sub pluginmain { if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { ::rptMsg($key_path."\\".$s->get_name()); - ::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($s->get_timestamp())."Z"); my %rdvals = getRDValues($s); if (%rdvals) { @@ -147,9 +152,9 @@ sub getRDValues { else { # New code $data = decode("ucs-2le", $data); - my $file = (split(/\x00/,$data))[0]; -# my $file = (split(/\x00\x00/,$data))[0]; -# $file =~ s/\x00//g; + my $file = (split(/\00/,$data))[0]; +# my $file = (split(/\00\00/,$data))[0]; +# $file =~ s/\00//g; $rdvals{$name} = $file; } } diff --git a/thirdparty/rr-full/plugins/recentdocs_timeline.pl b/thirdparty/rr-full/plugins/recentdocs_timeline.pl deleted file mode 100644 index 91b9127fabd..00000000000 --- a/thirdparty/rr-full/plugins/recentdocs_timeline.pl +++ /dev/null @@ -1,215 +0,0 @@ -#----------------------------------------------------------- -# recentdocs_timeline.pl -# Plugin for Registry Ripper -# Parses RecentDocs keys/values in NTUSER.DAT into a timeline based on the MRUListEx -# The times are printed in UTC in Unix epoch format, line 98/99 are interchangeable to modify the output format of the date. -# -# This script is a modified version of Harlen Carvey's recentdocs plugin. -# This is an automated version of the process shown by Dan Pullega -# References: http://www.4n6k.com/2014/02/forensics-quickie-pinpointing-recent.html -# Note that these times should be used in conjunction with other artefacts as during testing I saw that not every item I accessed was stored in the ntuser.dat -# Also downloaded files appeared to be accessed, even though they werent. -# More testing is required - - -# Change history -# 20161112 - fixed name -# 20161116 - fixed presentation of data and added code to deal with entries with no values -# 20161115 - rename plugin and updated output to include human-readable date -# 20140224 - Fixed bug that took the lowest MRUList item, rather than the first -# 20140222 - Modified to combine last write times into MRUListEx -# 20100405 - Updated to use Encode::decode to translate strings -# 20090115 - Minor update to keep plugin from printing terminating -# MRUListEx value of 0xFFFFFFFF -# 20080418 - Minor update to address NTUSER.DAT files that have -# MRUList values in this key, rather than MRUListEx -# values -# -# References -# -# -# Original copyright 2010 Quantum Analytics Research, LLC -# Updated by Phill Moore - github.com/randomaccess3 -#----------------------------------------------------------- -package recentdocs_timeline; -use strict; -use Encode; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20161112); - -sub getShortDescr { - return "Gets contents of user's RecentDocs key and place last write times into timeline based on MRUListEx"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching recentdocs_timeline v.".$VERSION); - ::rptMsg("recentdocs_timeline v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my %hash = {}; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RecentDocs"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("RecentDocs"); - #::rptMsg("**All values printed in MRUList\\MRUListEx order."); - #::rptMsg($key_path); - #::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - -# Get RecentDocs subkeys' values - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - #::rptMsg($key_path."\\".$s->get_name()); - #::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); - - my %rdvals = getRDValues($s); - if (%rdvals) { - my $tag; - if (exists $rdvals{"MRUListEx"}) { - $tag = "MRUListEx"; - } - elsif (exists $rdvals{"MRUList"}) { - $tag = "MRUList"; - } - else { - - } - - my @list = split(/,/,$rdvals{$tag}); - my ($lastAccessed,@rest) = split(',', $rdvals{$tag}); - - my $d = $s->get_timestamp(); #unix time - #my $d = gmtime($s->get_timestamp()); #normalised time - - my $v = $rdvals{$lastAccessed}; - $hash{ $v } = $d; - - #This section was added after noticing that sometimes keys have no values in them. They still have names and dates/times - if ($v eq ""){ - ::rptMsg(gmtime($hash{$v})."\t\t:\tNO VALUES - CHECK KEY MANUALLY"); - } - else{ - ::rptMsg(gmtime($hash{$v})."\t\t:\t".$v); - } - - #::rptMsg($tag." = ".$rdvals{$tag}); - #foreach my $i (@list) { - # ::rptMsg(" ".$i." = ".$rdvals{$i}); - #} - } - else { - ::rptMsg($key_path." has no values."); - } - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - -::rptMsg(""); -::rptMsg("The last write times are now placed in line with the values in the MRUListEx value "); - - - - - - - - - - -# Get RecentDocs values - my %rdvals = getRDValues($key); - if (%rdvals) { - my $tag; - if (exists $rdvals{"MRUListEx"}) { - $tag = "MRUListEx"; - } - elsif (exists $rdvals{"MRUList"}) { - $tag = "MRUList"; - } - else { - - } - - my @list = split(/,/,$rdvals{$tag}); - foreach my $i (@list) { - if($hash{$rdvals{$i}}){ - ::rptMsg("\t".gmtime($hash{$rdvals{$i}})."\t\t".$i." = ".$rdvals{$i}); - } - else{ - ::rptMsg("\t\t\t\t\t".$i." = ".$rdvals{$i}) - } - } - ::rptMsg(""); - } - else { - ::rptMsg($key_path." has no values."); - ::logMsg("Error: ".$key_path." has no values."); - } - - } - else { - ::rptMsg($key_path." not found."); - } -} - - -sub getRDValues { - my $key = shift; - - my $mru = "MRUList"; - my %rdvals; - - my @vals = $key->get_list_of_values(); - if (scalar @vals > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); - if ($name =~ m/^$mru/) { - my @mru; - if ($name eq "MRUList") { - @mru = split(//,$data); - } - elsif ($name eq "MRUListEx") { - @mru = unpack("V*",$data); - } -# Horrible, ugly cludge; the last, terminating value in MRUListEx -# is 0xFFFFFFFF, so we remove it. - pop(@mru); - $rdvals{$name} = join(',',@mru); - } - else { -# New code - $data = decode("ucs-2le", $data); - my $file = (split(/\00/,$data))[0]; -# my $file = (split(/\00\00/,$data))[0]; -# $file =~ s/\00//g; - $rdvals{$name} = $file; - } - } - return %rdvals; - } - else { - return undef; - } -} - -1; diff --git a/thirdparty/rr-full/plugins/recentdocs_tln.pl b/thirdparty/rr-full/plugins/recentdocs_tln.pl index 1cdc7270aed..1f10dcd066f 100644 --- a/thirdparty/rr-full/plugins/recentdocs_tln.pl +++ b/thirdparty/rr-full/plugins/recentdocs_tln.pl @@ -3,6 +3,7 @@ # # # Change history +# 20200924 - MITRE update # 20140220 - updated # # References @@ -19,9 +20,12 @@ package recentdocs_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20140220); + MITRE => "", + category => "user activity", + output => "tln", + version => 20200924); +sub getConfig {return %config} sub getShortDescr { return "Gets contents of user's RecentDocs key (TLN)"; } @@ -50,8 +54,9 @@ sub pluginmain { # ::rptMsg("RecentDocs"); # ::rptMsg("**All values printed in MRUList\\MRUListEx order."); # ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); +# ::rptMsg("LastWrite Time: ".gmtime($key->get_timestamp())." (UTC)"); $lw = $key->get_timestamp(); + # Get RecentDocs values my %rdvals = getRDValues($key); if (%rdvals) { @@ -118,9 +123,9 @@ sub getRDValues { else { # New code $data = decode("ucs-2le", $data); - my $file = (split(/\x00/,$data))[0]; -# my $file = (split(/\x00\x00/,$data))[0]; -# $file =~ s/\x00//g; + my $file = (split(/\00/,$data))[0]; +# my $file = (split(/\00\00/,$data))[0]; +# $file =~ s/\00//g; $rdvals{$name} = $file; } } diff --git a/thirdparty/rr-full/plugins/recyclepersist.pl b/thirdparty/rr-full/plugins/recyclepersist.pl new file mode 100644 index 00000000000..7fffd66abef --- /dev/null +++ b/thirdparty/rr-full/plugins/recyclepersist.pl @@ -0,0 +1,100 @@ +#----------------------------------------------------------- +# recyclepersist.pl +# +# +# History +# 20230123 - created +# +# References +# https://github.com/D1rkMtr/RecyclePersist +# +# copyright 2023 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package recyclepersist; +use strict; + +my %config = (hive => "Software, USRCLASS\.DAT", + MITRE => "T1546", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20230123); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check for persistence via Recycle Bin"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching recyclepersist v.".$VERSION); + ::rptMsg("recyclepersist v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("Category: ".$config{category}." (MITRE ".$config{MITRE}.")"); + ::rptMsg(""); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + +#--------------------------------------------------------------- +# First, determine the hive + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my @paths = (); + if ($hive_guess eq "software") { + @paths = ("Classes\\CLSID","Classes\\Wow6432Node\\CLSID"); + } + elsif ($hive_guess eq "usrclass") { + @paths = ("CLSID"); + } + else {} + + foreach my $path (@paths) { + my $key; + my $key_path = $path."\\{645FF040-5081-101B-9F08-00AA002F954E}\\shell\\open\\command"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("Key LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $n = $key->get_value("")->get_data(); + ::rptMsg("(Default) value: ".$n); + }; + + eval { + my $d = $key->get_value("DelegateExecute")->get_data(); + ::rptMsg("DelegateExecute value: ".$d); + }; + + ::rptMsg("") if ($hive_guess eq "software"); + } + else { + ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Adding a \\shell\\open\\command value to the Recycle Bin will allow the program to be launched"); + ::rptMsg("when the Recycle Bin is opened. This key path does not exist by default; however, the \\shell\\empty\\command"); + ::rptMsg("key path does."); + ::rptMsg(""); + ::rptMsg("Ref: https://github.com/D1rkMtr/RecyclePersist"); +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/regback.pl b/thirdparty/rr-full/plugins/regback.pl index 04423406fe8..192d258aef0 100644 --- a/thirdparty/rr-full/plugins/regback.pl +++ b/thirdparty/rr-full/plugins/regback.pl @@ -1,108 +1,73 @@ #----------------------------------------------------------- # regback.pl -# Plugin to assist to determine if a registry backup was executed and -# provide the key name of the log file which is located at -# Windows/System32/logfiles/Scm/. -# It will then go out and list all tasks scheduled through the -# task scheduler along with the name of each log file associated -# with that task. It will then print out the last written time and date. -# This is for Windows NT systems ONLY (Vista, Win 7, 2008) blog post # -# Change History: -# 20110427 [mmo] % created -# 20110830 [fpi] + banner, no change to the version number +# History: +# 20201130 - created # -# References -# http://dfsforensics.blogspot.com/2011/03/interesting-regsitry-backup-feature-of.html -# -# Script written by Mark Morgan +# References: +# https://www.windowslatest.com/2019/07/01/enable-automatic-registry-backup-in-windows-10/ +# https://docs.microsoft.com/en-us/troubleshoot/windows-client/deployment/system-registry-no-backed-up-regback-folder +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package regback; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "system", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20100219); + MITRE => "", + output => "report", + version => 20201130); sub getConfig{return %config} - sub getShortDescr { - return "List all backup tasks along with logfile name and last written date/time"; + return "Get EnablePeriodicBackup value"; } - sub getDescr{} sub getRefs {} sub getHive {return $config{hive};} sub getVersion {return $config{version};} my $VERSION = getVersion(); +my %files; +my $str = ""; sub pluginmain { - - ::logMsg("Launching regback v.".$VERSION); - ::rptMsg("regback v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - my $class = shift; my $hive = shift; + ::logMsg("Launching regback v.".$VERSION); + ::rptMsg("regback v.".$VERSION); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tree\\Microsoft\\Windows\\Registry\\RegIdleBackup"; - my $key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\Session Manager\\Configuration Manager"; + my $key = (); if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("regidle"); ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - ::rptMsg(sprintf "%-12s %-20s",$v->get_name(),$v->get_data()); - } - } - else { - ::rptMsg($key_path." has no values."); - } + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $last = $key->get_value("EnablePeriodicBackup")->get_data(); + ::rptMsg("EnablePeriodicBackup value : ".$last); + }; + ::rptMsg("EnablePeriodicBackup value not found.") if ($@); + } else { ::rptMsg($key_path." not found."); } - - $class = shift; - $hive = shift; - - my %tasks; - - $root_key = $reg->get_root_key; - $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tasks"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - my $path; - eval { - $path = $s->get_value("Path")->get_data(); - ::rptMsg("Path : ".$path); - ::rptMsg("Dynamicinfo : ".$s->get_name()); - ::rptMsg("LastWrite : ".gmtime($s->get_timestamp())." (UTC)"); - ::rptMsg(""); - }; - - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } + ::rptMsg(""); + ::rptMsg("Analysis Tip: As of Win10 1803, copies of Reg hives were no longer maintained in the RegBack folder."); + ::rptMsg(" Adding and setting this value to \"1\" re-enables that."); } - -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/regin.pl b/thirdparty/rr-full/plugins/regin.pl deleted file mode 100644 index e3105929fb0..00000000000 --- a/thirdparty/rr-full/plugins/regin.pl +++ /dev/null @@ -1,74 +0,0 @@ -#----------------------------------------------------------- -# regin.pl -# -# History: -# 20141124 - created -# -# References: -# http://www.symantec.com/content/en/us/enterprise/media/security_response/whitepapers/regin-analysis.pdf -# https://securelist.com/files/2014/11/Kaspersky_Lab_whitepaper_Regin_platform_eng.pdf -# -# copyright 2014 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package regin; -use strict; - -my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "malware", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20141124); - -sub getConfig{return %config} -sub getShortDescr { - return "Detect Regin"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my %files; - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching regin v.".$VERSION); - ::rptMsg("regin v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my ($current,$ccs); - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - - my %stages = ("\\Control\\Class\\{4F20E605-9452-4787-B793-D0204917CA58}" => "Symantec stage 2/Kaspersky stage 1", - "\\Control\\Class\\{4F20E605-9452-4787-B793-D0204917CA5A}" => "Symantec stage 3", - "\\Control\\Class\\{39399744-44FC-AD65-474B-E4DDF-8C7FB97}" => "Kaspersky stage 2, 32-bit", - "\\Control\\Class\\{3F90B1B4-58E2-251E-6FFE-4D38C5631A04}" => "Kaspersky stage 2, 32-bit", - "\\Control\\Class\\{9B9A8ADB-8864-4BC4-8AD5-B17DFDBB9F58}" => "Kaspersky stage 3 (?)", - "\\Control\\RestoreList\\VideoBase" => "Symantec stage 2, v.2.0"); - - foreach my $i (keys %stages) { - $key_path = $ccs.$i; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Possible Regin infection found in ".$i); - ::rptMsg($stages{$i}); - } - } - } - -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/registerspooler.pl b/thirdparty/rr-full/plugins/registerspooler.pl new file mode 100644 index 00000000000..10444f02f46 --- /dev/null +++ b/thirdparty/rr-full/plugins/registerspooler.pl @@ -0,0 +1,74 @@ +#----------------------------------------------------------- +# registerspooler.pl +# Per \@onelin, setting the RegisterSpoolerRemoteRpcEndPoint value to "2" mitigates CVE-2021-34527 +# without having to disable the Spooler service. +# +# Change history: +# 20210705 - created +# +# References: +# https://twitter.com/onelin/status/1411085783545622531 +# https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.Printing.2::RegisterSpoolerRemoteRpcEndPoint +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package registerspooler; +use strict; + +my %config = (hive => "software", + category => "config", + MITRE => "N/A", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210705); + +sub getConfig{return %config} + +sub getShortDescr { + return "Look for BlackLivesMatter key assoc. w/ REvil ransomware"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching registerspooler v.".$VERSION); + ::rptMsg("registerspooler v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + my $key_path = "Policies\\Microsoft\\Windows NT\\Printers"; + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + eval { + my $i = $key->get_value("RegisterSpoolerRemoteRpcEndPoint")->get_data(); + ::rptMsg("RegisterSpoolerRemoteRpcEndPoint value: ".$i); + }; + } + else { + ::rptMsg($key_path." key not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Per \@onelin, setting the RegisterSpoolerRemoteRpcEndPoint value to \"2\" mitigates CVE-2021-34527"); + ::rptMsg("without having to disable the Spooler service."); + ::rptMsg("Ref: https://twitter.com/onelin/status/1411085783545622531"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/regtime.pl b/thirdparty/rr-full/plugins/regtime.pl deleted file mode 100644 index 9e60779534b..00000000000 --- a/thirdparty/rr-full/plugins/regtime.pl +++ /dev/null @@ -1,66 +0,0 @@ -#! c:\perl\bin\perl.exe -#----------------------------------------------------------- -# regtime.pl -# Plugin for Registry Ripper; traverses through a Registry -# hive file, pulling out keys and their LastWrite times, and -# then listing them in order, sorted by the most recent time -# first - works with any Registry hive file. -# -# Change history -# -# -# copyright 2008 H. Carvey -#----------------------------------------------------------- -package regtime; -use strict; - -my %config = (hive => "All", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080324); - -sub getConfig{return %config} -sub getShortDescr { - return "Dumps entire hive - all keys sorted by LastWrite time"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my %regkeys; - -sub pluginmain { - my $class = shift; - my $file = shift; - my $reg = Parse::Win32Registry->new($file); - my $root_key = $reg->get_root_key; - ::logMsg("Launching regtime v.".$VERSION); - ::rptMsg("regtime v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - traverse($root_key); - - foreach my $t (reverse sort {$a <=> $b} keys %regkeys) { - foreach my $item (@{$regkeys{$t}}) { - ::rptMsg(gmtime($t)."Z \t".$item); - } - } -} - -sub traverse { - my $key = shift; - my $ts = $key->get_timestamp(); - my $name = $key->as_string(); - $name =~ s/\$\$\$PROTO\.HIV//; - $name = (split(/\[/,$name))[0]; - push(@{$regkeys{$ts}},$name); - foreach my $subkey ($key->get_list_of_subkeys()) { - traverse($subkey); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/regtime_tln.pl b/thirdparty/rr-full/plugins/regtime_tln.pl deleted file mode 100644 index 558d7f0eebf..00000000000 --- a/thirdparty/rr-full/plugins/regtime_tln.pl +++ /dev/null @@ -1,66 +0,0 @@ -#! c:\perl\bin\perl.exe -#----------------------------------------------------------- -# regtime.pl -# Plugin for Registry Ripper; traverses through a Registry -# hive file, pulling out keys and their LastWrite times, and -# then listing them in order, sorted by the most recent time -# first - works with any Registry hive file. -# -# Change history -# -# -# copyright 2008 H. Carvey -#----------------------------------------------------------- -package regtime_tln; -use strict; - -my %config = (hive => "All", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20080324); - -sub getConfig{return %config} -sub getShortDescr { - return "Dumps entire hive - all keys sorted by LastWrite time"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my %regkeys; - -sub pluginmain { - my $class = shift; - my $file = shift; - my $reg = Parse::Win32Registry->new($file); - my $root_key = $reg->get_root_key; - ::logMsg("Launching regtime_tln v.".$VERSION); - - traverse($root_key); - - foreach my $t (reverse sort {$a <=> $b} keys %regkeys) { - foreach my $item (@{$regkeys{$t}}) { - #::rptMsg(gmtime($t)."Z \t".$item); - ::rptMsg($t."|REG|M... ".$item); - } - } -} - -sub traverse { - my $key = shift; - my $ts = $key->get_timestamp(); - my $name = $key->as_string(); - $name =~ s/\$\$\$PROTO\.HIV//; - $name = (split(/\[/,$name))[0]; - push(@{$regkeys{$ts}},$name); - foreach my $subkey ($key->get_list_of_subkeys()) { - traverse($subkey); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/remoteaccess.pl b/thirdparty/rr-full/plugins/remoteaccess.pl index 1565c19bcab..d5b794e827a 100644 --- a/thirdparty/rr-full/plugins/remoteaccess.pl +++ b/thirdparty/rr-full/plugins/remoteaccess.pl @@ -2,27 +2,28 @@ # remoteaccess.pl # # History: +# 20200924 - MITRE update +# 20200517 - minor updates # 20160906 - created # # References: # https://technet.microsoft.com/en-us/library/ff687746(v=ws.10).aspx # # -# copyright 2016 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package remoteaccess; use strict; my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "Config settings", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20160906); + MITRE => "", + output => "report", + version => 20200924); sub getConfig{return %config} sub getShortDescr { @@ -48,41 +49,43 @@ sub pluginmain { # First thing to do is get the ControlSet00x marked current...this is # going to be used over and over again in plugins that access the system # file - my ($current,$ccs); - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; + my $key; + my $ccs = ::getCCS($root_key); - $key_path = $ccs."\\services\\RemoteAccess\\Parameters\\AccountLockout"; + my $key_path = $ccs."\\services\\RemoteAccess\\Parameters\\AccountLockout"; - if ($key = $root_key->get_subkey($key_path)) { - - eval { - my $deny = $key->get_value("MaxDenials")->get_data(); - ::rptMsg("MaxDenials : ".$deny); - ::rptMsg("Remote Access Account Lockout Disabled.") if ($deny == 0); - ::rptMsg(""); - }; + if ($key = $root_key->get_subkey($key_path)) { - eval { - my $res = $key->get_value("ResetTime (mins)")->get_data(); - ::rptMsg("ResetTime (mins) : ".$res); - ::rptMsg("Default reset time is 2880 min, or 48 hrs"); - ::rptMsg(""); - }; + eval { + my $deny = $key->get_value("MaxDenials")->get_data(); + ::rptMsg("MaxDenials : ".$deny); + ::rptMsg("Remote Access Account Lockout Disabled.") if ($deny == 0); + ::rptMsg(""); + }; + eval { + my $res = $key->get_value("ResetTime (mins)")->get_data(); + ::rptMsg("ResetTime (mins) : ".$res); + ::rptMsg("Default reset time is 2880 min, or 48 hrs"); + ::rptMsg(""); + }; - } - else { - ::rptMsg($key_path." not found."); - } +# Check for locked out accounts + eval { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + ::rptMsg("Locked out accounts:"); + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()." LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + } + } + }; } else { ::rptMsg($key_path." not found."); } + } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/remoteuac.pl b/thirdparty/rr-full/plugins/remoteuac.pl new file mode 100644 index 00000000000..5a28492d67e --- /dev/null +++ b/thirdparty/rr-full/plugins/remoteuac.pl @@ -0,0 +1,76 @@ +#----------------------------------------------------------- +# remoteuac.pl +# Get setting for remote UAC +# +# Change history: +# 20220101 - created +# +# References: +# https://docs.microsoft.com/en-us/troubleshoot/windows-server/windows-security/user-account-control-and-remote-restriction +# https://redcanary.com/blog/blackbyte-ransomware/ <- Added 02142022 +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, 2013 +#----------------------------------------------------------- +package remoteuac; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + MITRE => "T1562", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220101); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get setting for remote UAC"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching remoteuac v.".$VERSION); + ::rptMsg("remoteuac v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = "Microsoft\\Windows\\CurrentVersion\\Policies\\System"; + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $uac = $key->get_value("LocalAccountTokenFilterPolicy")->get_data(); + ::rptMsg("LocalAccountTokenFilterPolicy value: ".$uac); + ::rptMsg(""); + ::rptMsg("0 - Filtered token created\. No Admin\. Default\."); + ::rptMsg("1 - Elevated token created\."); + }; + ::rptMsg("LocalAccountTokenFilterPolicy value not found.") if ($@); + } + else { +# ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: As of Vista, Windows implements UAC restrictions on the network\. Users logging in remotely to"); + ::rptMsg("target systems will not be provided an elevated token when logging in via a local Admin account. UAC "); + ::rptMsg("restrictions are enabled by default\."); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/troubleshoot/windows-server/windows-security/user-account-control-and-remote-restriction"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/removdev.pl b/thirdparty/rr-full/plugins/removdev.pl deleted file mode 100644 index f26846e7107..00000000000 --- a/thirdparty/rr-full/plugins/removdev.pl +++ /dev/null @@ -1,97 +0,0 @@ -#----------------------------------------------------------- -# removdev.pl -# Parse Microsoft\Windows Portable Devices\Devices key on Vista -# Get historical information about drive letter assigned to devices -# -# Change history -# 20090118 [hca] * changed the name of the plugin from "removdev" -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# NOTE: Credit for "discovery" goes to Rob Lee -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package removdev; -use strict; - -my %config = (hive => "Software", - osmask => 192, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 200800611); - -sub getConfig{return %config} - -sub getShortDescr { - return "Parses Windows Portable Devices key (Vista)"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching removdev v.".$VERSION); - ::rptMsg("removdev v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Windows Portable Devices\\Devices"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("RemovDev"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - - foreach my $s (@subkeys) { - my $name = $s->get_name(); - my $lastwrite = $s->get_timestamp(); - - my $letter; - eval { - $letter = $s->get_value("FriendlyName")->get_data(); - }; - ::rptMsg($name." key error: $@") if ($@); - - my $half; - if (grep(/##/,$name)) { - $half = (split(/##/,$name))[1]; - } - - if (grep(/\?\?/,$name)) { - $half = (split(/\?\?/,$name))[1]; - } - - my ($dev,$sn) = (split(/#/,$half))[1,2]; - - ::rptMsg("Device : ".$dev); - ::rptMsg("LastWrite : ".gmtime($lastwrite)." (UTC)"); - ::rptMsg("SN : ".$sn); - ::rptMsg("Drive : ".$letter); - ::rptMsg(""); - - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/renocide.pl b/thirdparty/rr-full/plugins/renocide.pl deleted file mode 100644 index eb9b2ff458f..00000000000 --- a/thirdparty/rr-full/plugins/renocide.pl +++ /dev/null @@ -1,69 +0,0 @@ -#----------------------------------------------------------- -# renocide.pl -# Plugin to assist in the detection of malware per MMPC -# blog post (References, below) -# -# Change History: -# 20130425 - added alertMsg() functionality -# 20110309 - created -# -# References -# http://www.microsoft.com/security/portal/Threat/Encyclopedia/Entry.aspx?Name=Win32/Renocide -# -# copyright 2013 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package renocide; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20130425); - -sub getConfig{return %config} - -sub getShortDescr { - return "Check for Renocide malware"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching renocide v.".$VERSION); - ::rptMsg("renocide v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\DRM\\amty"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("renocide"); - ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); - ::rptMsg(""); - ::rptMst($key_path." found; possible Win32\\Renocide infection\."); - ::alertMsg($key_path." found; possible Win32\\Renocide infection\."); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - ::rptMsg(sprintf "%-12s %-20s",$v->get_name(),$v->get_data()); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/resiliency.pl b/thirdparty/rr-full/plugins/resiliency.pl new file mode 100644 index 00000000000..fe47e531376 --- /dev/null +++ b/thirdparty/rr-full/plugins/resiliency.pl @@ -0,0 +1,119 @@ +#----------------------------------------------------------- +# resiliency.pl +# This plugin is in somewhat-testing mode; right now, it checks the StartupItems and DisabledItems +# subkeys only, as it seems that the DisabledItems subkey for Word (and possibly Excel) can contain +# references to files the user had open, or at least had knowledge of. +# +# Change history +# 20210325 - created +# +# To-Do: Add "DocumentRecovery" subkey, look for other subkeys to add +# +# +# References +# https://twitter.com/SBousseaden/status/1366025094779256838 +# https://www.sophos.com/en-us/threat-center/threat-analyses/viruses-and-spyware/Troj~Docker-Gen/detailed-analysis.aspx +# https://isc.sans.edu/diary/Interesting+VBA+Dropper/23016 +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package resiliency; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "user activity", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20210325); + +sub getConfig{return %config} +sub getShortDescr { + return "Get user's MSOffice Resiliency subkey content"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $office_version; +my @apps = ("Word","Excel","OneNote","OutLook"); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching resiliency v.".$VERSION); + ::rptMsg("resiliency v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg("resiliency v.".$VERSION); + ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + + foreach my $app (@apps) { + my $res_path = $office_version."\\".$app."\\Resiliency"; + if (my $id = $key->get_subkey($res_path)) { + my @subkeys = ("StartupItems","DisabledItems"); + foreach my $s (@subkeys) { + + if (my $i = $id->get_subkey($s)) { + my @vals = $i->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg($key_path."\\".$office_version."\\".$app."\\Resiliency\\".$s); + ::rptMsg("LastWrite time: ".::format8601Date($i->get_timestamp())."Z"); + foreach my $v (@vals) { + my $name = $v->get_name(); + my $data = $v->get_data(); + ::rptMsg("Value: ".$name); +# ::probe($data); + + if ($s eq "StartupItems") { + my ($t0,$t1) = unpack("VV",substr($data,16,8)); + ::rptMsg("Time: ".::format8601Date(::getTime($t0,$t1))."Z"); + } + + if ($s eq "DisabledItems") { + my $n = unpack("V",substr($data,4,4)); + my $i = ::getUnicodeStr(substr($data,12,$n)); + ::rptMsg("String: ".$i); + } + ::rptMsg(""); + } + } + } + } + } + else { +# ::rptMsg($res_path." not found."); + } + } + } + else { + ::rptMsg("MSOffice not found."); + } +} \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/restartmanager.pl b/thirdparty/rr-full/plugins/restartmanager.pl new file mode 100644 index 00000000000..a6ae5223b3d --- /dev/null +++ b/thirdparty/rr-full/plugins/restartmanager.pl @@ -0,0 +1,81 @@ +#----------------------------------------------------------- +# restartmanager.pl +# +# +# Change history +# 20210111 - created +# +# References +# https://docs.microsoft.com/en-us/windows/win32/rstmgr/about-restart-manager +# +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package restartmanager; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562\.001", + output => "report", + version => 20210111); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets RestartManager\\Session0000 values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching restartmanager v.".$VERSION); + ::rptMsg("restartmanager v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Software\\Microsoft\\RestartManager'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my $sess = (); + if ($sess = $key->get_subkey("Session0000")) { + ::rptMsg($key_path."\\Session0000"); + ::rptMsg("LastWrite Time ".::format8601Date($sess->get_timestamp())."Z"); + ::rptMsg(""); + my @vals = $sess->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-20s %-50s",$v->get_name(),$v->get_data()); + } + } + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: The Restart Manager determines apps/processes that need to be shutdown & restarted during an "); + ::rptMsg("install process\. Malware has been observed using this technique to keep files open during encryption, or to"); + ::rptMsg("to encrypt files that otherwise could not be accessed."); + ::rptMsg(""); + ::rptMsg("During an installation, the Session0000 key may be deleted after the FileInUse dialog is closed."); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/reveton.pl b/thirdparty/rr-full/plugins/reveton.pl deleted file mode 100644 index d849472e812..00000000000 --- a/thirdparty/rr-full/plugins/reveton.pl +++ /dev/null @@ -1,180 +0,0 @@ -#----------------------------------------------------------- -# reveton.pl -# -# -# Change history -# 20131010 - created -# -# References -# http://www.microsoft.com/security/portal/threat/encyclopedia/Entry.aspx?Name=Trojan%3AWin32%2FReveton#tab=2 -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package reveton; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - category => "malware", - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20131010); - -sub getConfig{return %config} -sub getShortDescr { - return "Checks for possible Reveton infection"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching reveton v.".$VERSION); - ::rptMsg("reveton v.".$VERSION); # banner - ::rptMsg(getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - my $count = 0; - my $key_path; - - my @paths = ('Software\\Microsoft\\Windows\\CurrentVersion\\Run', - 'Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run'); - my $key; -# Check #1 - foreach $key_path (@paths) { - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - my $lcname = $name; - $lcname =~ tr/[A-Z]/[a-z]/; - my $data = $v->get_data(); - my $lcdata = $data; - $lcdata =~ tr/[A-Z]/[a-z]/; - - if ($lcname =~ m/^task scheduler/ || $lcdata =~ m/task scheduler\.exe$/) { - ::rptMsg("Possible Reveton infection: ".$name." - ".$data); - $count++; - } - } - } - else { - ::rptMsg($key_path." has no values\."); - } - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); - } -# Check #2 - $key_path = 'Software\\Microsoft\\Internet Explorer\\Main'; - if ($key = $root_key->get_subkey($key_path)) { - - eval { - my $banner = $key->get_value("NoProtectedModeBanner")->get_data(); - ::rptMsg($key_path."\\NoProtectedModeBanner value = ".$banner); - ::rptMsg(""); - if ($banner == 1) { - ::rptMsg("Internet Explorer\\Main\\NoProtectedModeBanner set to 0x1: possible Reveton infection\."); - $count++; - ::rptMsg(""); - } - }; - } - else { - ::rptMsg($key_path." not found\."); - } - -# Check to see if IE toolbar is locked -# Check #3 - $key_path = 'Software\\Microsoft\\Internet Explorer\\Toolbar'; - if ($key = $root_key->get_subkey($key_path)) { - - eval { - my $tb = $key->get_value("Locked")->get_data(); - ::rptMsg($key_path."\\Locked value = ".$tb); - ::rptMsg(""); - if ($tb == 1) { - ::rptMsg("Internet Explorer Toolbar is locked: possible Reveton infection\."); - $count++; - ::rptMsg(""); - } - }; - } - else { - ::rptMsg($key_path." not found\."); - } - -# check Internet Zone Settings -# Check #4 - performs 5 identical checks - ::rptMsg("Checking Internet Zones Settings..."); - foreach my $z (0..4) { - $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\'.$z; - if ($key = $root_key->get_subkey($key_path)) { - eval { - my $val = $key->get_value("1609")->get_data(); -# ::rptMsg($key_path."\\1609 value = ".$val); -# ::rptMsg(""); - if ($val == 0x0) { - ::rptMsg("Internet Settings\\Zones\\".$z."\\1609 value is set to 0x0: possible Reveton infection\."); - ::rptMsg(""); - $count++; - } - }; - } - else { - ::rptMsg($key_path." not found\."); - } - } - -# Check #5 - see if Task Manager has been disalbed - ::rptMsg("Checking Task Manager Setting..."); - $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\'; - if ($key = $root_key->get_subkey($key_path)) { - eval { - my $val = $key->get_value("DisableTaskMgr")->get_data(); - if ($val == 0x1) { - ::rptMsg("Task Manager disabled: possible Reveton infection\."); - ::rptMsg(""); - $count++; - } - }; - } - else { - ::rptMsg($key_path." not found\."); - ::rptMsg(""); - } - -# Check #6 - ::rptMsg("Checking HideIcons Setting..."); - $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced\\'; - if ($key = $root_key->get_subkey($key_path)) { - eval { - my $val = $key->get_value("HideIcons")->get_data(); - if ($val == 0x1) { - ::rptMsg("HideIcons value set to 0x1: possible Reveton infection\."); - ::rptMsg(""); - $count++; - } - }; - } - else { - ::rptMsg($key_path." not found\."); - ::rptMsg(""); - } - - ::rptMsg("Final Score: ".$count."/6 checks succeeded\."); -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/revouninstaller.pl b/thirdparty/rr-full/plugins/revouninstaller.pl deleted file mode 100644 index 454fb5fce2f..00000000000 --- a/thirdparty/rr-full/plugins/revouninstaller.pl +++ /dev/null @@ -1,94 +0,0 @@ -#------------------------------------ -# revouninstall.pl -# Plugin for Registry Ripper, NTUSER.DAT - gets the information regarding the -# Revo Unistaller Pro application -# -# Change History: -# 20200329 - Initial Development -# -# References -# -# -# Copyright 2020 Tiago Sousa tsousahs@gmail.com -# ------------------------------------ -package revouninstaller; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20200329); - -sub getConfig { return %config } -sub getShortDescr { - return "Gets the information regarding revo unistaller execution"; -} - -sub getDescr {} -sub getRefs {} -sub getHive { return $config{ hive }; } -sub getVersion { return $config{ version }; } - -my $VERSION = getVersion(); - - -sub pluginmain { - - my $class = shift; - my $ntuser = shift; - - ::logMsg("Lauching revounistall v.".$VERSION); - ::rptMsg("revounistall v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n" ); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - # Browser Run - - my @key_paths = ( - "Software\\VS Revo Group\\Revo Uninstaller Pro\\TrackCleaner\\Browsers", - "Software\\VS Revo Group\\Revo Uninstaller Pro\\TrackCleaner\\Windows", - "Software\\VS Revo Group\\Revo Uninstaller Pro\\TrackCleaner\\MSOffice", - "Software\\VS Revo Group\\Revo Uninstaller Pro\\Uninstaller\\AppBar", - "Software\\VS Revo Group\\Revo Uninstaller Pro\\Uninstaller" - ); - - my $key; - my @vals; - - my @list_of_browsers; - - # Inside the browser key it may have separate sub keys for specific browsers - $key = $root_key->get_subkey( @key_paths[0] ); - @list_of_browsers = $key->get_list_of_subkeys(); - - - foreach $key (@list_of_browsers) { - push(@key_paths,$key_paths[0]."\\".$key->get_name()); - } - - # Remove the Browser key. it's not really needed anymore - shift(@key_paths); - - - foreach my $key_path (@key_paths) { - - $key = $root_key->get_subkey( $key_path ); - ::rptMsg("\n\nName:".$key->get_name()); - ::rptMsg("Last Write Time: ".gmtime($key->get_timestamp())." (UTC)\n"); - - my @vals = $key->get_list_of_values(); - - foreach my $v (@vals) { - if ($v->get_data() eq 1) { - ::rptMsg($v->get_name()." : Enabled"); - } elsif ($v->get_data() eq 0){ - ::rptMsg($v->get_name()." : Disabled"); - } else { - ::rptMsg($v->get_name()." : ".$v->get_data()); - } - } - } -} diff --git a/thirdparty/rr-full/plugins/rlo.pl b/thirdparty/rr-full/plugins/rlo.pl index 4a34afabec3..cd4bf2f6610 100644 --- a/thirdparty/rr-full/plugins/rlo.pl +++ b/thirdparty/rr-full/plugins/rlo.pl @@ -6,25 +6,28 @@ # which interpret the RLO control charater # # Change history +# 20200921 - MITRE updates +# 20200517 - minor updates # 20130904 - created # # References: # https://blog.commtouch.com/cafe/malware/exe-read-backwards-spells-malware/ -# +# https://attack.mitre.org/techniques/T1036/002/ # -# copyright 2013 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey #----------------------------------------------------------- package rlo; use strict; -my %config = (hive => "All", +my %config = (hive => "all", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - category => "malware", - version => 20130904); + MITRE => "T1036\.002", + category => "defense evasion", + output => "report", + version => 20200921); sub getConfig{return %config} sub getShortDescr { @@ -70,6 +73,7 @@ sub traverse { my ($n,$n2) = convertRLOName($name); $path =~ s/$name/$n/; ::rptMsg("RLO control char detected in key name: ".$path." [".$n2."]"); + ::rptMsg("Key LastWrite time: ".::format8601Date($ts)."Z"); } foreach my $val ($key->get_list_of_values()) { diff --git a/thirdparty/rr-full/plugins/rootkit_revealer.pl b/thirdparty/rr-full/plugins/rootkit_revealer.pl deleted file mode 100644 index ee1f0579ddd..00000000000 --- a/thirdparty/rr-full/plugins/rootkit_revealer.pl +++ /dev/null @@ -1,105 +0,0 @@ -#----------------------------------------------------------- -# rootkit_revealer.pl -# Extracts the EULA value for Sysinternals Rootkit Revealer -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright (c) 2011-02-04 Brendan Coles -#----------------------------------------------------------- -# Require # -package rootkit_revealer; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110204); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts the EULA value for Sysinternals Rootkit Revealer."; -} -sub getRefs { - my %refs = ("Sysinternals Rootkit Revealer Homepage:" => - "http://technet.microsoft.com/en-us/sysinternals/bb897445"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - my @interesting_keys = ( - "EulaAccepted" - ); - - # Initialize # - ::logMsg("Launching rootkit_revealer v.".$VERSION); - ::rptMsg("rootkit_revealer v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\Sysinternals\\RootkitRevealer"; - - # If # Rootkit Revealer path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Rootkit Revealer"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Rootkit Revealer registry path # - my %keys; - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Rootkit Revealer registry path # - foreach my $v (@vals) { - $keys{$v->get_name()} = $v->get_data(); - } - - # Return # all key names+values for interesting keys # - foreach my $var (@interesting_keys) { - if (exists $keys{$var}) { - ::rptMsg($var." -> ".$keys{$var}); - } - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Rootkit Revealer isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/routes.pl b/thirdparty/rr-full/plugins/routes.pl index c3a6ffa8f5d..a0cab531e13 100644 --- a/thirdparty/rr-full/plugins/routes.pl +++ b/thirdparty/rr-full/plugins/routes.pl @@ -4,6 +4,8 @@ # Some malware is known to create persistent routes # # Change History: +# 20200922 - MITRE updates +# 20200526 - updated date output format # 20100817 - created # # Ref: @@ -11,22 +13,25 @@ # http://www.symantec.com/security_response/writeup.jsp?docid= # 2010-041308-3301-99&tabid=2 # -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package routes; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "T1112", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20100817); + output => "report", + version => 20200922); sub getConfig{return %config} sub getShortDescr { - return "Get persistent routes"; + return "Get persistent routes from the Registry"; } sub getDescr{} sub getRefs {} @@ -45,19 +50,17 @@ sub pluginmain { my $root_key = $reg->get_root_key; # Code for System file, getting CurrentControlSet - my $current; my $key_path = 'Select'; my $key; if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; + my $ccs = ::getCCS($root_key); my $sb_path = $ccs."\\Services\\Tcpip\\Parameters\\PersistentRoutes"; my $sb; if ($sb = $root_key->get_subkey($sb_path)) { ::rptMsg($sb_path); - ::rptMsg("LastWrite: ".gmtime($sb->get_timestamp())); + ::rptMsg("LastWrite: ".::format8601Date($sb->get_timestamp())."Z"); ::rptMsg(""); my @vals = $sb->get_list_of_values(); @@ -67,6 +70,9 @@ sub pluginmain { my ($addr,$netmask,$gateway,$metric) = split(/,/,$v->get_name(),4); ::rptMsg(sprintf "%-15s %-15s %-15s %-5s",$addr,$netmask,$gateway,$metric); } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Persistent routes may provide alternative paths out of the infrastructure."); + ::rptMsg("To create a persistent route, use the \'route add\' command."); } else { ::rptMsg($sb_path." has no values."); diff --git a/thirdparty/rr-full/plugins/run.pl b/thirdparty/rr-full/plugins/run.pl new file mode 100644 index 00000000000..d3ff863b2d5 --- /dev/null +++ b/thirdparty/rr-full/plugins/run.pl @@ -0,0 +1,162 @@ +#----------------------------------------------------------- +# run +# Get contents of Run key from Software & NTUSER.DAT hives +# +# History: +# 20220722 - added check for value types (https://twitter.com/keydet89/status/1550473251062747137) +# 20220706 - removed StartupApproved entries +# 20220630 - updated output to address symbolic links +# 20200921 - MITRE update +# 20200511 - created +# +# Ref: +# https://attack.mitre.org/techniques/T1547/001/ +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package run; +use strict; + +my %config = (hive => "Software, NTUSER\.DAT", + MITRE => "T1547\.001", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220706); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get autostart key contents from Software/user hives"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching run v.".$VERSION); + ::rptMsg("run v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my @paths = (); + + if ($hive_guess eq "software") { + @paths = ("Microsoft\\Windows\\CurrentVersion\\Run", + "Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Microsoft\\Windows\\CurrentVersion\\RunServices", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", + "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"); + } + elsif ($hive_guess eq "ntuser") { + @paths = ("Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", + "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices", + "Software\\Microsoft\\Windows\\CurrentVersion\\RunServicesOnce", + "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". + "Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". + "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", + "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run"); + } + else {} + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + + my %vals = getKeyValues($key); + if (scalar(keys %vals) > 0) { + foreach my $v (keys %vals) { + ::rptMsg(" ".$v." - ".$vals{$v}); + } + ::rptMsg(""); + } + else { + ::rptMsg($key_path." has no values."); + } + + my @sk = $key->get_list_of_subkeys(); + if (scalar(@sk) > 0) { + foreach my $s (@sk) { + ::rptMsg(""); + ::rptMsg($key_path."\\".$s->get_name()); + ::rptMsg("LastWrite Time ".::format8601Date($s->get_timestamp())."Z"); + my %vals = getKeyValues($s); + foreach my $v (keys %vals) { + ::rptMsg(" ".$v." -> ".$vals{$v}); + } + ::rptMsg(""); + } + } + else { +# ::rptMsg($key_path." has no subkeys."); +# ::rptMsg(""); + } + } + else { +# ::rptMsg($key_path." not found."); +# ::rptMsg(""); + } + } +} + + +#------------------------------------------------------------------------------ +# +# +#------------------------------------------------------------------------------ +sub getKeyValues { + my $key = shift; + my %vals; + + my @vk = $key->get_list_of_values(); + if (scalar(@vk) > 0) { + foreach my $v (@vk) { + next if ($v->get_name() eq "" && $v->get_data() eq ""); + + my $data = $v->get_data(); + $data =~ s/\00//g if ($v->get_type() == 0x06); + $vals{$v->get_name()} = $data; + +# Added 20220722 +# https://twitter.com/keydet89/status/1550473251062747137 +# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/25cce700-7fcf-4bb6-a2f3-0f6d08430a55 + if ($v->get_type() != 0x01) { + ::rptMsg($v->get_name()." value is not type REG_SZ!"); + } + } + } + else { + + } + return %vals; +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/run_json.pl b/thirdparty/rr-full/plugins/run_json.pl new file mode 100644 index 00000000000..b1ddab4020e --- /dev/null +++ b/thirdparty/rr-full/plugins/run_json.pl @@ -0,0 +1,132 @@ +#----------------------------------------------------------- +# run +# Get contents of Run key from Software & NTUSER.DAT hives +# +# History: +# 20230102 - created from run.pl +# +# Ref: +# https://attack.mitre.org/techniques/T1547/001/ +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package run_json; +use strict; + +my %config = (hive => "Software, NTUSER\.DAT", + MITRE => "T1547\.001", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "json", + version => 20230102); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get autostart key contents from Software/NTUSER\.DAT hive"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +# https://en.wikipedia.org/wiki/Windows_Registry +my %types = (0 => "REG_NONE", + 1 => "REG_SZ", + 2 => "REG_EXPAND_SZ", + 3 => "REG_BINARY", + 4 => "REG_DWORD", + 5 => "REG_DWORD_BIG_ENDIAN", + 6 => "REG_LINK", + 7 => "REG_MULTI_SZ", + 8 => "REG_RESOURCE_LIST", + 9 => "REG_FULL_RESOURCE_DESCRIPTOR", + 10 => "REG_RESOURCE_REQUIREMENTS_LIST", + 11 => "REG_QWORD"); + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::logMsg("Launching run_json v.".$VERSION); +# ::rptMsg("run_json v.".$VERSION); # banner +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my @paths = (); + + if ($hive_guess eq "software") { + @paths = ("Microsoft\\Windows\\CurrentVersion\\Run", + "Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Microsoft\\Windows\\CurrentVersion\\RunServices", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", + "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"); + } + elsif ($hive_guess eq "ntuser") { + @paths = ("Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", + "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices", + "Software\\Microsoft\\Windows\\CurrentVersion\\RunServicesOnce", + "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". + "Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". + "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", + "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run"); + } + else {} + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + ::rptMsg("{"); + ::rptMsg(" \"pluginname\": \"run_json\""); + ::rptMsg(" \"hive\": \"".$reg->get_filename()."\""); + ::rptMsg(" \"hive_timestamp\": \"".::format8601Date($reg->get_timestamp())."Z\""); + ::rptMsg(" \"key\": \"".$key_path."\""); + ::rptMsg(" \"LastWrite Time\": \"".::format8601Date($key->get_timestamp())."Z\""); + + my @vals = $key->get_list_of_values(); + + ::rptMsg(" \"Num_values\": \"".(scalar @vals)."\""); + + if (scalar @vals > 0) { + ::rptMsg(" \"members\": ["); + foreach my $v (@vals) { + ::rptMsg(" {"); + ::rptMsg(" \"value\": \"".$v->get_name()."\""); + ::rptMsg(" \"type\": \"".$types{$v->get_type()}."\""); + ::rptMsg(" \"data\": \"".$v->get_data()."\""); + ::rptMsg(" },"); + } + ::rptMsg(" ]"); + } + ::rptMsg("}"); + ::rptMsg(""); + } + else { +# If $key_path is not found, no need to do anything + + } + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/run_yara.pl b/thirdparty/rr-full/plugins/run_yara.pl new file mode 100644 index 00000000000..0bbf2191434 --- /dev/null +++ b/thirdparty/rr-full/plugins/run_yara.pl @@ -0,0 +1,141 @@ +#----------------------------------------------------------- +# run_yara +# Get contents of Run key from Software & NTUSER.DAT hives +# +# History: +# 20230811 - created +# +# Ref: +# +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package run_yara; +use strict; + +my %config = (hive => "Software, NTUSER\.DAT", + MITRE => "T1547\.001", + category => "persistence", + output => "yara", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + version => 20230811); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get autostart key contents from Software hive"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $path_to_yara = ".\\yara64\.exe"; +my $path_to_rule_file = ".\\test\.yar"; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching run_yara v.".$VERSION); + ::rptMsg("run_yara v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my @paths = (); + + if ($hive_guess eq "software") { + @paths = ("Microsoft\\Windows\\CurrentVersion\\Run", + "Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Microsoft\\Windows\\CurrentVersion\\RunServices", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", + "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"); + } + elsif ($hive_guess eq "ntuser") { + @paths = ("Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", + "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices", + "Software\\Microsoft\\Windows\\CurrentVersion\\RunServicesOnce", + "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". + "Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". + "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", + "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run"); + } + else {} + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + foreach my $v (@vals) { + my $name = $v->get_name(); + my $data = $v->get_data(); + + ::rptMsg("Value name: ".$name); + my $temp_file = ".\\".$name; + open(FH,">",$temp_file); + print FH $data; + close(FH); + + eval { + my $output = qx/$path_to_yara -s -m $path_to_rule_file \"$temp_file\"/; + if ($output eq "" || $output eq "\n") { + + } + else { + ::rptMsg($output); + } + + }; + + + unlink($temp_file); + + } + ::rptMsg(""); + } + else { + ::rptMsg($key_path." has no values."); + } + + } + else { +# ::rptMsg($key_path." not found."); +# ::rptMsg(""); + } + } +} + + +#------------------------------------------------------------------------------ +# +# +#------------------------------------------------------------------------------ + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/rundisabled.pl b/thirdparty/rr-full/plugins/rundisabled.pl new file mode 100644 index 00000000000..28b730afdf0 --- /dev/null +++ b/thirdparty/rr-full/plugins/rundisabled.pl @@ -0,0 +1,155 @@ +#----------------------------------------------------------- +# rundisabled +# Get Startup items that were disabled via Task Manager or SysInternals Autoruns +# +# History: +# 20220706 - created +# +# Ref: +# https://renenyffenegger.ch/notes/Windows/registry/tree/HKEY_CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Explorer/StartupApproved/Run/ +# https://social.technet.microsoft.com/Forums/sharepoint/en-US/f2a2b59b-aa59-46de-922c-342fbdaf6d8c/registry-key-startupapproved-ignored?forum=autoruns +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package rundisabled; +use strict; + +my %config = (hive => "Software, NTUSER\.DAT", + MITRE => "T1562\.001", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220706); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get status of items in autostart locations"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching rundisabled v.".$VERSION); + ::rptMsg("rundisabled v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my @paths = (); + + if ($hive_guess eq "software") { + @paths = ("Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run", + "Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run32", + "Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\StartupFolder"); + } + elsif ($hive_guess eq "ntuser") { + @paths = ("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run", + "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run32", + "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\StartupFolder"); + } + else {} + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + + my %vals = getKeyValues($key); + if (scalar(keys %vals) > 0) { + foreach my $v (keys %vals) { + my @d = unpack("VVV",$vals{$v}); + if ($d[0] == 2 || $d[0] == 6) { +# ::rptMsg(" ".$v." - Enabled"); + ::rptMsg(sprintf "%-40s %-30s",$v,"Enabled"); + } + elsif ($d[0] == 3) { + my $t = ::getTime($d[1],$d[2]); +# ::rptMsg(" ".$v." - Disabled ".::format8601Date($t)."Z"); + ::rptMsg(sprintf "%-40s %-30s",$v,"Disabled ".::format8601Date($t)."Z"); + } + else {} + } + ::rptMsg(""); + } + else { +# ::rptMsg($key_path." has no values."); + } + + } + else { +# ::rptMsg($key_path." not found."); +# ::rptMsg(""); + } + } + +# Check for entries disabled via SysInternals Autoruns + my @paths = (); + if ($hive_guess eq "software") { + @paths = ("Microsoft\\Windows\\CurrentVersion\\Run\\AutorunsDisabled", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run\\AutorunsDisabled"); + } + elsif ($hive_guess eq "ntuser") { + @paths = ("Software\\Microsoft\\Windows\\CurrentVersion\\Run\\AutorunsDisabled", + "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run\\AutorunsDisabled"); + } + else {} + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg(sprintf "%-40s %-40s",$v->get_name(),$v->get_data()); + } + } + } + } +} + + +#------------------------------------------------------------------------------ +# +# +#------------------------------------------------------------------------------ +sub getKeyValues { + my $key = shift; + my %vals; + + my @vk = $key->get_list_of_values(); + if (scalar(@vk) > 0) { + foreach my $v (@vk) { + next if ($v->get_name() eq "" && $v->get_data() eq ""); + $vals{$v->get_name()} = $v->get_data(); + } + } + else { + + } + return %vals; +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/runmru.pl b/thirdparty/rr-full/plugins/runmru.pl index 3194050b032..25c4204be65 100644 --- a/thirdparty/rr-full/plugins/runmru.pl +++ b/thirdparty/rr-full/plugins/runmru.pl @@ -4,22 +4,27 @@ # RunMru values # # Change history +# 20201005 - MITRE update +# 20200525 - updated date output format # 20080324 - created # # References # # -# copyright 2008 H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package runmru; use strict; my %config = (hive => "NTUSER\.DAT", hasShortDescr => 1, + category => "execution", hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080324); + output => "report", + MITRE => "T1204", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -36,8 +41,10 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching runmru v.".$VERSION); - ::rptMsg("runmru v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("runmru v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -46,7 +53,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("RunMru"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); my %runvals; my $mru; diff --git a/thirdparty/rr-full/plugins/runmru_tln.pl b/thirdparty/rr-full/plugins/runmru_tln.pl index b36f1ebecc6..50cdd23b012 100644 --- a/thirdparty/rr-full/plugins/runmru_tln.pl +++ b/thirdparty/rr-full/plugins/runmru_tln.pl @@ -4,13 +4,14 @@ # RunMru values # # Change history +# 20201005 - MITRE update # 20120828 - updated to TLN format # 20080324 - created # # References # # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey #----------------------------------------------------------- package runmru_tln; @@ -20,8 +21,10 @@ package runmru_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120828); + MITRE => "T1204", + category => "program execution", + output => "tln", + version => 20201005); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/runonceex.pl b/thirdparty/rr-full/plugins/runonceex.pl index e7bfd48a72a..99d022bae66 100644 --- a/thirdparty/rr-full/plugins/runonceex.pl +++ b/thirdparty/rr-full/plugins/runonceex.pl @@ -2,6 +2,8 @@ # runonceex # # Change history: +# 20201005 - MITRE update +# 20200427 - updated output date format # 20190716 - created # # Ref: @@ -14,12 +16,13 @@ package runonceex; use strict; my %config = (hive => "Software", - category => "autostart", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20190716); + MITRE => "T1547", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -36,8 +39,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::rptMsg("Launching runonceex v.".$VERSION); - ::rptMsg("runonceex v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("runonceex v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $key_path = ('Microsoft\\Windows\\CurrentVersion\\RunOnceEx'); ::rptMsg("RunOnceEx"); @@ -52,7 +57,7 @@ sub pluginmain { if (scalar(@sk) > 0) { foreach my $s (@sk) { ::rptMsg($s->get_name()); - ::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($s->get_timestamp())."Z"); # Gets values and data my @vals = $s->get_list_of_values(); diff --git a/thirdparty/rr-full/plugins/runvirtual.pl b/thirdparty/rr-full/plugins/runvirtual.pl new file mode 100644 index 00000000000..7866478875b --- /dev/null +++ b/thirdparty/rr-full/plugins/runvirtual.pl @@ -0,0 +1,103 @@ +#----------------------------------------------------------- +# runvirtual.pl +# +# +# Change history +# 20220425 - updated code, added Analysis Tip +# 20201005 - MITRE update +# 20200427 - updated output date format +# 20191211 - created +# +# References +# https://docs.microsoft.com/en-us/microsoft-desktop-optimization-pack/appv-v5/running-a-locally-installed-application-inside-a-virtual-environment-with-virtualized-applications +# https://virtualvibes.algiz-technology.com/runvirtual-end-to-end/ +# +# Copyright 2022 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package runvirtual; +use strict; + +my %config = (hive => "NTUSER\.DAT, Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1610", + category => "execution", + output => "report", + version => 20220425); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Gets RunVirtual entries"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching runvirtual v.".$VERSION); + ::rptMsg("runvirtual v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my $key_path = (); + if ($hive_guess eq "software") { + $key_path = ("Microsoft\\AppV\\Client\\RunVirtual"); + } + elsif ($hive_guess eq "ntuser") { + $key_path = ("Software\\Microsoft\\AppV\\Client\\RunVirtual"); + } + else {} + + if ($key = $root_key->get_subkey($key_path)) { + + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_name(); + my $lw = $s->get_timestamp(); + ::rptMsg("RunVirtual subkey: ".$name." LastWrite: ".::format8601Date($lw)."Z"); + eval { + my $def = $s->get_value("")->get_data(); + ::rptMsg(" Default value = ".$def); + ::rptMsg(""); + }; + } + } + else { + ::rptMsg($key_path." has no subkeys\."); + } + } + else { + ::rptMsg($key_path." not found\."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Subkeys can be added to the RunVirtual key, allowing locally installed applications to be run in"); + ::rptMsg("virtual environments."); + ::rptMsg(""); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/windows/application-management/app-v/appv-running-locally-installed-applications-inside-a-virtual-environment"); +# ::rptMsg(""); +} + +1; diff --git a/thirdparty/rr-full/plugins/runvirtual_tln.pl b/thirdparty/rr-full/plugins/runvirtual_tln.pl new file mode 100644 index 00000000000..0791bcc9ac0 --- /dev/null +++ b/thirdparty/rr-full/plugins/runvirtual_tln.pl @@ -0,0 +1,92 @@ +#----------------------------------------------------------- +# runvirtual_tln.pl +# +# +# Change history +# 20220427 - updated code +# 20201005 - MITRE update +# 20191211 - created +# +# References +# https://docs.microsoft.com/en-us/microsoft-desktop-optimization-pack/appv-v5/running-a-locally-installed-application-inside-a-virtual-environment-with-virtualized-applications +# +# Copyright 2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package runvirtual_tln; +use strict; + +my %config = (hive => "NTUSER\.DAT, Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1610", + category => "execution", + output => "tln", + version => 20220427); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Gets RunVirtual entries"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::logMsg("Launching runvirtual v.".$VERSION); +# ::rptMsg("runvirtual v.".$VERSION); +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my $key_path = (); + if ($hive_guess eq "software") { + $key_path = ("Microsoft\\AppV\\Client\\RunVirtual"); + } + elsif ($hive_guess eq "ntuser") { + $key_path = ("Software\\Microsoft\\AppV\\Client\\RunVirtual"); + } + else {} + + if ($key = $root_key->get_subkey($key_path)) { +# ::rptMsg($key_path); +# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); +# ::rptMsg(""); + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_name(); + my $lw = $s->get_timestamp(); + my $str = "RunVirtual: ".$name." "; + eval { + my $def = $s->get_value("")->get_data(); + $str .= "Default value = ".$def; + }; + ::rptMsg($lw."|REG|||".$str); + } + } + else { +# ::rptMsg($key_path." has no subkeys\."); + } + } + else { +# ::rptMsg($key_path." not found\."); + } +} + +1; diff --git a/thirdparty/rr-full/plugins/ryuk_gpo.pl b/thirdparty/rr-full/plugins/ryuk_gpo.pl new file mode 100644 index 00000000000..c4549915ecc --- /dev/null +++ b/thirdparty/rr-full/plugins/ryuk_gpo.pl @@ -0,0 +1,132 @@ +#----------------------------------------------------------- +# ryuk_gpo.pl +# +# Get GPO policy settings from Software hive related to Ryuk +# +# Change history +# 20201005 - MITRE update +# 20200427 - updated output date format +# 20200312 - created +# +# References +# https://thebinaryhick.blog/2019/12/22/ryuk-and-gpos-and-powershell-oh-my/ +# https://attack.mitre.org/techniques/T1562/001/ +# +# Copyright 2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package ryuk_gpo; +use strict; + +my %config = (hive => "software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1562\.001", + category => "defense evasion", + version => 20201005); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Get GPO policy settings from Software hive related to Ryuk"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching ryuk_gpo v.".$VERSION); + ::rptMsg("ryuk_gpo v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = "Policies\\Microsoft"; + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + +# Powershell Policies + eval { + my $scripts = $key->get_subkey("Windows\\PowerShell")->get_value("EnableScripts")->get_data(); + ::rptMsg("PowerShell EnableScripts value = ".$scripts); + }; + + eval { + my $ep = $key->get_subkey("Windows\\PowerShell")->get_value("ExecutionPolicy")->get_data(); + ::rptMsg("PowerShell ExecutionPolicy value = ".$ep); + }; + + my @sys = ("EnableLogonScriptDelay","AsyncScriptDelay","GroupPolicyRefreshTime","GroupPolicyRefreshTimeOffset"); + foreach my $s (@sys) { + eval { + my $t = $key->get_subkey("Windows\\System")->get_value($s)->get_data(); + ::rptMsg("System ".$s." value = ".$t); + }; + } + +# WinRM + my @client = ("AllowBasic","AllowCredSSP","AllowUnencryptedTraffic","TrustedHosts","TrustedHostsList"); + foreach my $c (@client) { + eval { + my $t = $key->get_subkey("Windows\\WinRM\\Client")->get_value($c)->get_data(); + ::rptMsg("WinRM\\Client ".$c." value = ".$t); + }; + } + + my @service = ("AllowBasic","AllowCredSSP","AllowAutoConfig","IPv4Filter", "IPv6Filter","AllowUnencryptedTraffic","HttpCompatibilityListener","HttpsCompatibilityListener"); + foreach my $s (@service) { + eval { + my $t = $key->get_subkey("Windows\\WinRM\\Service")->get_value($s)->get_data(); + ::rptMsg("WinRM\\Service ".$s." value = ".$t); + }; + } + + eval { + my $t = $key->get_subkey("Windows\\WinRM\\Service\\WinRS")->get_value("AllowRemoteShellAccess")->get_data(); + ::rptMsg("WinRM\\Service\\WinRS AllowRemoteShellAccess value = ".$t); + }; + +# Defender, Security Services + eval { + my $t = $key->get_subkey("Windows Defender")->get_value("DisableAntiSpyware")->get_data(); + ::rptMsg("Windows Defender DisableAntiSpyware value = ".$t); + }; + + eval { + my $t = $key->get_subkey("Windows Defender\\Real-Time Protection")->get_value("DisableRealtimeMonitoring")->get_data(); + ::rptMsg("Windows Defender\\Real-Time Protection DisableRealtimeMonitoring value = ".$t); + }; + + eval { + my $t = $key->get_subkey("Windows NT\\Security Center")->get_value("SecurityCenterInDomain")->get_data(); + ::rptMsg("Windows NT\\Security Center SecurityCenterInDomain value = ".$t); + }; + + eval { + my $t = $key->get_subkey("Windows NT\\Terminal Services")->get_value("fAllowUnlistedRemotePrograms")->get_data(); + ::rptMsg("Windows NT\\Terminal Services fAllowUnlistedRemotePrograms value = ".$t); + }; + + eval { + my $t = $key->get_subkey("Windows NT\\Terminal Services")->get_value("fDenyTSConnections")->get_data(); + ::rptMsg("Windows NT\\Terminal Services fDenyTSConnections value = ".$t); + }; + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; diff --git a/thirdparty/rr-full/plugins/safeboot.pl b/thirdparty/rr-full/plugins/safeboot.pl deleted file mode 100644 index 2ec36fd3cc6..00000000000 --- a/thirdparty/rr-full/plugins/safeboot.pl +++ /dev/null @@ -1,106 +0,0 @@ -#----------------------------------------------------------- -# safeboot.pl -# -# Some malware is known to maintain persistence, even when the system -# is booted to SafeMode by writing entries to the SafeBoot subkeys -# ex: http://www.symantec.com/security_response/writeup.jsp? -# docid=2008-011507-0108-99&tabid=2 -# -# Ref: -# http://support.microsoft.com/kb/315222 -# http://support.microsoft.com/kb/202485/ -# -# copyright 2008-2009 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package safeboot; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20081216); - -sub getConfig{return %config} - -sub getShortDescr { - return "Check SafeBoot entries"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching safeboot v.".$VERSION); - ::rptMsg("safeboot v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# Code for System file, getting CurrentControlSet - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - - my $sb_path = $ccs."\\Control\\SafeBoot"; - my $sb; - if ($sb = $root_key->get_subkey($sb_path)) { - - my @sks = $sb->get_list_of_subkeys(); - - if (scalar(@sks) > 0) { - - foreach my $s (@sks) { - my $name = $s->get_name(); - my $ts = $s->get_timestamp(); - ::rptMsg($name." [".gmtime($ts)." Z]"); - my %sk; - my @subkeys = $s->get_list_of_subkeys(); - - if (scalar(@subkeys) > 0) { - foreach my $s2 (@subkeys) { - my $str; - my $default; - eval { - $default = $s2->get_value("")->get_data(); - }; - ($@)?($str = $s2->get_name()):($str = $s2->get_name()." (".$default.")"); - push(@{$sk{$s2->get_timestamp()}},$str); - } - - foreach my $t (sort keys %sk) { - ::rptMsg(gmtime($t)." Z"); - foreach my $i (@{$sk{$t}}) { - ::rptMsg(" ".$i); - } - } - ::rptMsg(""); - } - else { - ::rptMsg($name." has no subkeys."); - } - } - } - else { - ::rptMsg($sb_path." has no subkeys."); - } - } - else { - ::rptMsg($sb_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); -# ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/sam b/thirdparty/rr-full/plugins/sam index e64143aa20f..4baaed1f59a 100644 --- a/thirdparty/rr-full/plugins/sam +++ b/thirdparty/rr-full/plugins/sam @@ -1 +1,2 @@ samparse +samparse_tln diff --git a/thirdparty/rr-full/plugins/samparse.pl b/thirdparty/rr-full/plugins/samparse.pl index 18e7955c692..2440fe686b1 100644 --- a/thirdparty/rr-full/plugins/samparse.pl +++ b/thirdparty/rr-full/plugins/samparse.pl @@ -3,6 +3,10 @@ # Parse the SAM hive file for user/group membership info # # Change history: +# 20200825 - Unicode updates +# 20200730 - MITRE ATT&CK Updates +# 20200427 - updated output date format +# 20200216 - Added RID Hijacking check (https://pentestlab.blog/2020/02/12/persistence-rid-hijacking/) # 20160203 - updated to include add'l values (randomaccess/Phill Moore contribution) # 20120722 - updated %config hash # 20110303 - Fixed parsing of SID, added check for account type @@ -18,7 +22,9 @@ # Source available here: http://pogostick.net/~pnh/ntpasswd/ # http://accessdata.com/downloads/media/Forensic_Determination_Users_Logon_Status.pdf # -# copyright 2016 Quantum Analytics Research, LLC +# https://attack.mitre.org/techniques/T1136/001/ +# +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package samparse; @@ -29,12 +35,12 @@ package samparse; my %config = (hive => "SAM", hivemask => 2, output => "report", - category => "", - osmask => 63, #XP - Win8 + category => "user activity", + MITRE => "T1136\.001", hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - version => 20160203); + version => 20200825); sub getConfig{return %config} @@ -66,13 +72,15 @@ sub getRefs { my %types = (0xbc => "Default Admin User", 0xd4 => "Custom Limited Acct", 0xb0 => "Default Guest Acct"); - + sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching samparse v.".$VERSION); - ::rptMsg("samparse v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("samparse v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; ::rptMsg(""); @@ -81,24 +89,6 @@ sub pluginmain { ::rptMsg("-" x 25); my $key_path = 'SAM\\Domains\\Account\\Users'; my $key; - my $local_sid = ""; - my $account_key = $root_key->get_subkey("SAM\\Domains\\Account"); - if (defined $account_key) { - my $account_value = $account_key->get_value("V"); - if (defined $account_value) { - my $account_data = $account_value->get_data(); - if (defined $account_data) { - my $data_len = length($account_data); - if ($data_len >= 12) { - my @vArray = unpack("VVV",substr($account_data, $data_len-12, 12)); - my $vArray_len = @vArray; - if ($vArray_len == 3) { - $local_sid = "S-1-5-21-".$vArray[0]."-".$vArray[1]."-".$vArray[2]; - } - } - } - } - } if ($key = $root_key->get_subkey($key_path)) { my @user_list = $key->get_list_of_subkeys(); if (scalar(@user_list) > 0) { @@ -120,52 +110,36 @@ sub pluginmain { $c_date = $create->get_timestamp(); } }; - + ::rptMsg("Username : ".$v_val{name}." [".$rid."]"); - ::rptMsg("SID : ".$local_sid."-".$rid); ::rptMsg("Full Name : ".$v_val{fullname}); ::rptMsg("User Comment : ".$v_val{comment}); ::rptMsg("Account Type : ".$v_val{type}); - ::rptMsg("Account Created : ".gmtime($c_date)." Z") if ($c_date > 0); + ::rptMsg("Account Created : ".::format8601Date($c_date)."Z") if ($c_date > 0); my $f_value = $u->get_value("F"); my $f = $f_value->get_data(); my %f_val = parseF($f); - eval { - my $reset_data_value = $u->get_value("ResetData"); - my $reset_data = $reset_data_value->get_data(); - my $reset_data_hash = decode_json($reset_data); - my $reset_data_question_1 = $reset_data_hash->{'questions'}[0]; - my $reset_data_question_2 = $reset_data_hash->{'questions'}[1]; - my $reset_data_question_3 = $reset_data_hash->{'questions'}[2]; - my $question_1 = $reset_data_question_1->{'question'}; - ::rptMsg("Security Questions:"); - ::rptMsg(" Question 1 : ".$question_1); - ::rptMsg(" Answer 1 : ".$reset_data_question_1->{'answer'}); - ::rptMsg(" Question 2 : ".$reset_data_question_2->{'question'}); - ::rptMsg(" Answer 2 : ".$reset_data_question_2->{'answer'}); - ::rptMsg(" Question 3 : ".$reset_data_question_3->{'question'}); - ::rptMsg(" Answer 3 : ".$reset_data_question_3->{'answer'}); - }; - my $lastlogin; my $pwdreset; my $pwdfail; - ($f_val{last_login_date} == 0) ? ($lastlogin = "Never") : ($lastlogin = gmtime($f_val{last_login_date})." Z"); - ($f_val{pwd_reset_date} == 0) ? ($pwdreset = "Never") : ($pwdreset = gmtime($f_val{pwd_reset_date})." Z"); - ($f_val{pwd_fail_date} == 0) ? ($pwdfail = "Never") : ($pwdfail = gmtime($f_val{pwd_fail_date})." Z"); + ($f_val{last_login_date} == 0) ? ($lastlogin = "Never") : ($lastlogin = ::format8601Date($f_val{last_login_date})."Z"); + ($f_val{pwd_reset_date} == 0) ? ($pwdreset = "Never") : ($pwdreset = ::format8601Date($f_val{pwd_reset_date})."Z"); + ($f_val{pwd_fail_date} == 0) ? ($pwdfail = "Never") : ($pwdfail = ::format8601Date($f_val{pwd_fail_date})."Z"); my $given; my $surname; eval { $given = $u->get_value("GivenName")->get_data(); - $given =~ s/\x00//g; + $given = ::getUnicodeStr($given); +# $given =~ s/\00//g; }; eval { $surname = $u->get_value("SurName")->get_data(); - $surname =~ s/\x00//g; + $surname = ::getUnicodeStr($surname); +# $surname =~ s/\00//g; }; ::rptMsg("Name : ".$given." ".$surname); @@ -173,25 +147,70 @@ sub pluginmain { my $internet; eval { $internet = $u->get_value("InternetUserName")->get_data(); - $internet =~ s/\x00//g; + $internet = ::getUnicodeStr($internet); +# $internet =~ s/\00//g; ::rptMsg("InternetName : ".$internet); }; - - my $pw_hint; eval { $pw_hint = $u->get_value("UserPasswordHint")->get_data(); - $pw_hint =~ s/\x00//g; + $pw_hint = ::getUnicodeStr($pw_hint); +# $pw_hint =~ s/\00//g; }; ::rptMsg("Password Hint : ".$pw_hint) unless ($@); ::rptMsg("Last Login Date : ".$lastlogin); ::rptMsg("Pwd Reset Date : ".$pwdreset); ::rptMsg("Pwd Fail Date : ".$pwdfail); ::rptMsg("Login Count : ".$f_val{login_count}); + ::rptMsg("Embedded RID : ".$f_val{rid}); + + if ($rid != $f_val{rid}) { + ::rptMsg("ALERT [T1089]: Possible RID hijacking found!"); + } + foreach my $flag (keys %acb_flags) { ::rptMsg(" --> ".$acb_flags{$flag}) if ($f_val{acb_flags} & $flag); } + ::rptMsg(""); + + eval { + my $force = unpack("V",$u->get_value("ForcePasswordReset")->get_data()); + ::rptMsg("ForcePasswordReset : ".$force); + }; + + eval { + my $dont = unpack("V",$u->get_value("UserDontShowInLogonUI")->get_data()); + ::rptMsg("UserDontShowInLogonUI : ".$dont); + }; + +# ::rptMsg(""); +# eval { +# my $sup = $u->get_value("SupplementalCredentials")->get_data(); +# ::probe($sup); +# }; +# ::rptMsg(""); + + eval { + my $reset_data_value = $u->get_value("ResetData"); + my $reset_data = $reset_data_value->get_data(); + my $reset_data_hash = decode_json($reset_data); + my $reset_data_question_1 = $reset_data_hash->{'questions'}[0]; + my $reset_data_question_2 = $reset_data_hash->{'questions'}[1]; + my $reset_data_question_3 = $reset_data_hash->{'questions'}[2]; + my $question_1 = $reset_data_question_1->{'question'}; + ::rptMsg("Security Questions:"); + ::rptMsg(" Question 1 : ".$question_1); + ::rptMsg(" Answer : ".$reset_data_question_1->{'answer'}); + ::rptMsg(" Question 2 : ".$reset_data_question_2->{'question'}); + ::rptMsg(" Answer : ".$reset_data_question_2->{'answer'}); + ::rptMsg(" Question 3 : ".$reset_data_question_3->{'question'}); + ::rptMsg(" Answer : ".$reset_data_question_3->{'answer'}); + }; + + + + ::rptMsg(""); } } @@ -199,13 +218,12 @@ sub pluginmain { } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } ::rptMsg("-" x 25); ::rptMsg("Group Membership Information"); ::rptMsg("-" x 25); # Get Group membership information - $key_path = 'SAM\\Domains\\Builtin\\Aliases'; + my $key_path = 'SAM\\Domains\\Builtin\\Aliases'; if ($key = $root_key->get_subkey($key_path)) { my %grps; my @groups = $key->get_list_of_subkeys(); @@ -223,7 +241,7 @@ sub pluginmain { $name =~ s/^0000//; my %c_val = parseC($grps{$k}{C_value}); ::rptMsg("Group Name : ".$c_val{group_name}." [".$c_val{num_users}."]"); - ::rptMsg("LastWrite : ".gmtime($grps{$k}{LastWrite})." Z"); + ::rptMsg("LastWrite : ".::format8601Date($grps{$k}{LastWrite})."Z"); ::rptMsg("Group Comment : ".$c_val{comment}); if ($c_val{num_users} == 0) { ::rptMsg("Users : None"); @@ -383,9 +401,9 @@ sub _translateSID { #--------------------------------------------------------------------- sub _uniToAscii { my $str = $_[0]; - Encode::from_to($str,'UTF-16LE','utf8'); - $str = Encode::decode_utf8($str); + $str = ::getUnicodeStr($str); + $str =~ s/\00//g; return $str; } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/samparse_tln.pl b/thirdparty/rr-full/plugins/samparse_tln.pl index 39df56ce4ec..304351df822 100644 --- a/thirdparty/rr-full/plugins/samparse_tln.pl +++ b/thirdparty/rr-full/plugins/samparse_tln.pl @@ -3,6 +3,8 @@ # Parse the SAM hive file for user/group membership info # # Change history: +# 20200825 - Unicode updates +# 20200730 - MITRE ATT&CK updates # 20120827 - TLN version created from original samparse.pl # 20120722 - updated %config hash # 20110303 - Fixed parsing of SID, added check for account type @@ -18,7 +20,9 @@ # Source available here: http://pogostick.net/~pnh/ntpasswd/ # http://accessdata.com/downloads/media/Forensic_Determination_Users_Logon_Status.pdf # -# copyright 2012 Quantum Analytics Research, LLC +# https://attack.mitre.org/techniques/T1136/001/ +# +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package samparse_tln; @@ -26,15 +30,14 @@ package samparse_tln; my %config = (hive => "SAM", hivemask => 2, - output => "report", - category => "User Activity", - class => 0, # system - output => "TLN", - osmask => 63, #XP - Win8 + category => "user activity", + class => 0, + output => "tln", + MITRE => "T1136\.001", hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - version => 20120827); + version => 20200825); sub getConfig{return %config} @@ -100,28 +103,16 @@ sub pluginmain { } }; -# ::rptMsg("Username : ".$v_val{name}." [".$rid."]"); -# ::rptMsg("Full Name : ".$v_val{fullname}); -# ::rptMsg("User Comment : ".$v_val{comment}); -# ::rptMsg("Account Type : ".$v_val{type}); -# ::rptMsg("Account Created : ".gmtime($c_date)." Z") if ($c_date > 0); - my $f_value = $u->get_value("F"); my $f = $f_value->get_data(); my %f_val = parseF($f); - -# my $lastlogin; -# my $pwdreset; -# my $pwdfail; -# ($f_val{last_login_date} == 0) ? ($lastlogin = "Never") : ($lastlogin = gmtime($f_val{last_login_date})." Z"); -# ($f_val{pwd_reset_date} == 0) ? ($pwdreset = "Never") : ($pwdreset = gmtime($f_val{pwd_reset_date})." Z"); -# ($f_val{pwd_fail_date} == 0) ? ($pwdfail = "Never") : ($pwdfail = gmtime($f_val{pwd_fail_date})." Z"); - + my $pw_hint; my $c_descr = "Acct Created (".$v_val{type}.")"; eval { $pw_hint = $u->get_value("UserPasswordHint")->get_data(); - $pw_hint =~ s/\x00//g; + $pw_hint = ::getUnicodeStr($pw_hint); +# $pw_hint =~ s/\00//g; $c_descr .= " (Pwd Hint: ".$pw_hint.")"; }; @@ -140,8 +131,6 @@ sub pluginmain { if ($f_val{last_login_date} > 0) { ::rptMsg($f_val{last_login_date}."|SAM||".$v_val{name}."|Last Login (".$f_val{login_count}.")"); } - - } } } @@ -278,7 +267,8 @@ sub _translateSID { #--------------------------------------------------------------------- sub _uniToAscii { my $str = $_[0]; - $str =~ s/\x00//g; + $str = ::getUnicodeStr($str); +# $str =~ s/\00//g; return $str; } diff --git a/thirdparty/rr-full/plugins/sandbox.pl b/thirdparty/rr-full/plugins/sandbox.pl new file mode 100644 index 00000000000..ab575cfa26b --- /dev/null +++ b/thirdparty/rr-full/plugins/sandbox.pl @@ -0,0 +1,78 @@ +#----------------------------------------------------------- +# sandbox +# +# Change history: +# 20221024 - created +# +# Ref: +# https://learn.microsoft.com/en-us/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview +# https://admx.help/?Category=Windows_11_2022&Policy=Microsoft.Policies.WindowsSandbox::AllowClipboardRedirection +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package sandbox; +use strict; + +my %config = (hive => "software", + category => "", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20221024); + +sub getConfig{return %config} +sub getShortDescr { + return "Check Sandbox settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching sandbox v.".$VERSION); + ::rptMsg("sandbox v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + + my $key_path = ('Policies\\Microsoft\\Windows\\Sandbox'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + eval { + my $x = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-30s %-4s",$v->get_name(),$x); + }; + } + } + else { + ::rptMsg($key_path." has no values."); + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Windows Sandbox provides a lightweight desktop environment to safely run applications in isolation. "); + ::rptMsg("Software installed inside the Windows Sandbox environment remains \"sandboxed\" and runs separately from the host machine."); + ::rptMsg("This plugin retrieves Sandbox environment settings."); + ::rptMsg(""); + ::rptMsg("Ref: https://learn.microsoft.com/en-us/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/sbs.pl b/thirdparty/rr-full/plugins/sbs.pl deleted file mode 100644 index 2b91b2d731a..00000000000 --- a/thirdparty/rr-full/plugins/sbs.pl +++ /dev/null @@ -1,69 +0,0 @@ -#----------------------------------------------------------- -# sbs -# -# -# References -# http://www.hexacorn.com/blog/2017/12/29/beyond-good-ol-run-key-part-69/ -# -# History: -# 20180101 - created -# -# copyright 2018 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package sbs; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20180101); - -sub getConfig{return %config} - -sub getShortDescr { - return "Gets PreferExternalManifest value"; -} -sub getDescr{} -sub getRefs { - my %refs = (); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching sbs v.".$VERSION); - ::rptMsg("sbs v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# used a list of values to address the need for parsing the App Paths key -# in the Wow6432Node key, if it exists. - my @paths = ("Microsoft\\Windows\\CurrentVersion\\SideBySide", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\SideBySide"); - - foreach my $key_path (@paths) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("SBS"); - ::rptMsg($key_path); - ::rptMsg(""); - - my $sbs; - eval { - $sbs = $key->get_value("SideBySide")->get_data(); - ::rptMsg("SideBySide = ".$sbs); - }; - ::rptMsg("SideBySide value not found.") if ($@); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/schedagent.pl b/thirdparty/rr-full/plugins/schedagent.pl index 6b574e20cdb..ec011bc7a98 100644 --- a/thirdparty/rr-full/plugins/schedagent.pl +++ b/thirdparty/rr-full/plugins/schedagent.pl @@ -1,18 +1,25 @@ #----------------------------------------------------------- # schedagent -# Get contents of SchedulingAgent key from Software hive +# Get contents of SchedulingAgent key from Software hive # -# copyright 2010 Quantum Analytics Research, LLC +# History +# 20200925 - MITRE update +# 20200518 - updated date output format +# 20100817 - created +# +# copyright 2020 Quantum Analytics Research, LLC #----------------------------------------------------------- package schedagent; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - version => 20100817); + output => "report", + version => 20200925); sub getConfig{return %config} @@ -39,7 +46,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my ($oldname,$logpath,$folder,$lastrun,$size); @@ -65,7 +72,7 @@ sub pluginmain { # eval { $lastrun = $key->get_value("LastTaskRun")->get_data(); - ::rptMsg("LastTaskRun = ".parseSystemTime($lastrun)); + ::rptMsg("LastTaskRun = ".::convertSystemTime($lastrun)."Z"); ::rptMsg(""); ::rptMsg("Note: LastTaskRun time is written in local system time, not GMT"); }; @@ -76,14 +83,4 @@ sub pluginmain { } } -sub parseSystemTime { - my ($yr,$mon,$dow,$day,$hr,$min,$sec,$mil) = unpack("v8",$_[0]); - $mon = "0".$mon unless ($mon =~ /^\d\d$/); - $day = "0".$day unless ($day =~ /^\d\d$/); - $hr = "0".$hr unless ($hr =~ /^\d\d$/); - $min = "0".$min unless ($min =~ /^\d\d$/); - $sec = "0".$sec unless ($sec =~ /^\d\d$/); - return "$yr-$mon-$day $hr:$min:$sec"; -} - 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/screensaver.pl b/thirdparty/rr-full/plugins/screensaver.pl new file mode 100644 index 00000000000..ce8a9035957 --- /dev/null +++ b/thirdparty/rr-full/plugins/screensaver.pl @@ -0,0 +1,102 @@ +#----------------------------------------------------------- +# screensaver.pl +# +# Change history +# 20220427 - created +# +# References +# https://cocomelonc.github.io/tutorial/2022/04/26/malware-pers-2.html +# https://attack.mitre.org/techniques/T1546/002/ +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package screensaver; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + category => "persistence", + MITRE => "T1546\.002", + output => "report", + version => 20220427); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets user's screensaver settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching screensaver v.".$VERSION); + ::rptMsg("screensaver v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Control Panel\\Desktop'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + ::rptMsg($key_path); + ::rptMsg("LastWrite: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $s = $key->get_value("ScreenSaveActive")->get_data(); + if ($s == 1) { + ::rptMsg("Screensaver is active."); + } + elsif ($s == 0) { + ::rptMsg("Screensaver is not active."); + } + else { + ::rptMsg("ScreenSaveActive value: ".$s); + } + }; + ::rptMsg("ScreenSaveActive value not found.") if ($@); + + eval { + my $s = $key->get_value("ScreenSaverIsSecure")->get_data(); + ::rptMsg("ScreenSaverIsSecure value: ".$s); + }; + + eval { + my $s = $key->get_value("ScreenSaveTimeout")->get_data(); + ::rptMsg("ScreenSaveTimeout value : ".$s); + }; + + eval { + my $s = $key->get_value("SCRNSAVE\.exe")->get_data(); + ::rptMsg("SCRNSAVE\.exe value : ".$s); + }; + ::rptMsg("SCRNSAVE\.exe value not found.") if ($@); + + } + else { + ::rptMsg($key_path." has no values."); + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Threat actors have been observed using the screen saver as a persistent mechanism."); + ::rptMsg(""); + ::rptMsg("Ref: https://cocomelonc.github.io/tutorial/2022/04/26/malware-pers-2.html"); + ::rptMsg("Ref: https://attack.mitre.org/techniques/T1546/002/"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/screenshotindex.pl b/thirdparty/rr-full/plugins/screenshotindex.pl new file mode 100644 index 00000000000..bed83683e02 --- /dev/null +++ b/thirdparty/rr-full/plugins/screenshotindex.pl @@ -0,0 +1,69 @@ +#----------------------------------------------------------- +# screenshotindex.pl +# +# +# Change history +# 20230713 - created +# +# References +# https://twitter.com/keydet89/status/1679474166183936001 +# https://www.tenforums.com/tutorials/6108-reset-screenshot-index-counter-windows-10-a.html +# +# copyright 2023 QAR,LLC +# author: H. Carvey keydet89@yahoo.com +#----------------------------------------------------------- +package screenshotindex; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + category => "collection", + MITRE => "T1074\.001", # local data staging + version => 20230713); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks user's ScreenshotIndex value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching screenshotindex v.".$VERSION); + ::rptMsg("screenshotindex v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"; + + if ($key = $root_key->get_subkey($key_path)) { + eval { + my $start = $key->get_value("ScreenshotIndex")->get_data(); + ::rptMsg($key_path." ScreenshotIndex value = ".$start); + }; + ::rptMsg($key_path." ScreenshotIndex value not found.") if ($@); + } + else { + ::rptMsg($key_path." key not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: When a user takes a screenshot via Win + PrtScr, and automatically saves the file, the files is"); + ::rptMsg("saved to the user's \"\\Pictures\\Screenshots\" folder, and the ScreenshotIndex value is incremented. This is "); + ::rptMsg("a possible means of data collection for a threat actor, or an insider threat."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.tenforums.com/tutorials/6108-reset-screenshot-index-counter-windows-10-a.html"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/scriptleturl.pl b/thirdparty/rr-full/plugins/scriptleturl.pl new file mode 100644 index 00000000000..72d7c1052a1 --- /dev/null +++ b/thirdparty/rr-full/plugins/scriptleturl.pl @@ -0,0 +1,77 @@ +#----------------------------------------------------------- +# scriptleturl.pl +# +# +# History +# 20201005 - MITRE update +# 20200525 - minor updates +# 20160428 - created +# +# References +# https://www.carbonblack.com/2016/04/28/threat-advisory-squiblydoo-continues-trend-of-attackers-using-native-os-tools-to-live-off-the-land/ +# https://attack.mitre.org/techniques/T1218/010/ +# +# copyright 2020, Quantum Analytics Research, LLC +#----------------------------------------------------------- +package scriptleturl; +use strict; + +my %config = (hive => "Software, USRCLASS\.DAT", + MITRE => "T1218\.010", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20201005); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check CLSIDs for ScriptletURL subkeys"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + + my $scriptleturl; + + ::logMsg("Launching scriptleturl v.".$VERSION); + ::rptMsg("scriptleturl v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my @key_paths = ("Classes\\CLSID","CLSID","WOW6432Node\\CLSID"); + my $key; + foreach my $key_path (@key_paths) { + if ($key = $root_key->get_subkey($key_path)) { + my @sk = $key->get_list_of_subkeys(); + if (scalar(@sk) > 0) { + foreach my $s (@sk) { + + + eval { + $scriptleturl = $s->get_subkey("ScriptletURL")->get_value("(Default)")->get_data(); + ::rptMsg($s->get_name()."\\ScriptletURL key found: ".$scriptleturl); + ::rptMsg("Analysis Tip: Look for unusual entries that may be associated with SquiblyDoo."); + ::rptMsg("Ref: https://www.carbonblack.com/blog/threat-advisory-squiblydoo-continues-trend-of-attackers-using-native-os-tools-to-live-off-the-land/"); + }; + + } + } + } + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/scsi.pl b/thirdparty/rr-full/plugins/scsi.pl new file mode 100644 index 00000000000..e3f1d715947 --- /dev/null +++ b/thirdparty/rr-full/plugins/scsi.pl @@ -0,0 +1,128 @@ +#----------------------------------------------------------- +# scsi.pl +# Parses contents of Enum\SCSI +# +# History +# 20220802 - copied from usbstor.pl +# +# References: +# +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package scsi; +use strict; + +my %config = (hive => "System", + MITRE => "", + category => "devices", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220802); + +sub getConfig{return %config} + +sub getShortDescr { + return "Parses Enum\\SCSI key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $reg; + +sub pluginmain { + my $class = shift; + my $hive = shift; + $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + ::logMsg("Launching scsi v.".$VERSION); + ::rptMsg("scsi v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my $key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Enum\\SCSI"; + my $key; + + my @vals = ("DeviceDesc","Mfg","Service","FriendlyName"); + + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); + my @sk = $s->get_list_of_subkeys(); + if (scalar @sk > 0) { + foreach my $k (@sk) { + ::rptMsg(" ".$k->get_name()); + + foreach my $v (@vals) { + eval { + my $x = $k->get_value($v)->get_data(); + ::rptMsg(sprintf " %-15s: %-30s",$v,$x); + }; + } +# get Properties\{83da6326-97a6-4088-9453-a1923f573b29} + eval { + getProperties($k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}")); + }; + } + } + ::rptMsg(""); + } + } + else { + ::rptMsg($key_path." has no subkeys."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + + +sub getProperties { + my $key = shift; + + eval { + my $r = $key->get_subkey("0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Install",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Inserted",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Inserted",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Removal",::format8601Date($t)."Z"); + }; + + +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/scsi_tln.pl b/thirdparty/rr-full/plugins/scsi_tln.pl new file mode 100644 index 00000000000..e5c021004c7 --- /dev/null +++ b/thirdparty/rr-full/plugins/scsi_tln.pl @@ -0,0 +1,128 @@ +#----------------------------------------------------------- +# scsi_tln.pl +# Parses contents of Enum\USB key for USB devices (not only USB storage devices) +# +# History +# 20220802 - created, copied from usbstor_tln.pl +# +# References: +# +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package scsi_tln; +use strict; + +my %config = (hive => "System", + MITRE => "", + category => "devices", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "tln", + version => 20220802); + +sub getConfig{return %config} + +sub getShortDescr { + return "Parses Enum\\SCSI key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $reg; + +sub pluginmain { + my $class = shift; + my $hive = shift; + $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Enum\\SCSI"; + my $key; + + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { +# ::rptMsg($s->get_name()); + my @sk = $s->get_list_of_subkeys(); + if (scalar @sk > 0) { + foreach my $k (@sk) { +# my $serial = $k->get_name(); + my $f = ""; + my $x = ""; + + eval { + $f = $k->get_value("FriendlyName")->get_data(); + }; + + eval { + $x = $k->get_value("DeviceDesc")->get_data(); + }; + + my $name = $f; + if ($f eq "") { + $name = $x; + } + +# get Properties\{83da6326-97a6-4088-9453-a1923f573b29} + eval { + getProperties($name,$k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}")); + }; + } + } + } + } + else { +# ::rptMsg($key_path." has no subkeys."); + } + } + else { +# ::rptMsg($key_path." not found."); + } +} + + +sub getProperties { + my $name = shift; + my $key = shift; + + eval { + my $r = $key->get_subkey("0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||First Install - ".$name); + }; + + eval { + my $r = $key->get_subkey("0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||First Inserted - ".$name); + }; + + eval { + my $r = $key->get_subkey("0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||Last Inserted - ".$name); + }; + + eval { + my $r = $key->get_subkey("0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||Last Removal - ".$name); + + }; +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/searchscopes.pl b/thirdparty/rr-full/plugins/searchscopes.pl index 299293f94f2..7998c7b09fc 100644 --- a/thirdparty/rr-full/plugins/searchscopes.pl +++ b/thirdparty/rr-full/plugins/searchscopes.pl @@ -1,15 +1,16 @@ #----------------------------------------------------------- # searchscopes.pl -# Plugin for Registry Ripper, NTUSER.DAT edition - gets the -# ACMru values +# # # Change history +# 20201005 - MITRE update +# 20200517 - updated date output format # 20180406 - created (per request submitted by John McCash) # # References # https://www.online-tech-tips.com/internet-explorer-tips/change-default-search-engine-ie/ # -# copyright 2018 QAR, LLC +# copyright 2020 QAR, LLC # author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package searchscopes; @@ -19,8 +20,10 @@ package searchscopes; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180406); + MITRE => "", + category => "user activity", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -47,15 +50,13 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("SearchScopes"); ::rptMsg($key_path); - if (defined($key->get_value("DefaultScope"))) { - ::rptMsg("DefaultScope: ".$key->get_value("DefaultScope")->get_data()); - ::rptMsg(""); - } + ::rptMsg("DefaultScope: ".$key->get_value("DefaultScope")->get_data()); + ::rptMsg(""); # ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())." (UTC)]"); + ::rptMsg($s->get_name()." [".::format8601Date($s->get_timestamp())."Z]"); eval { ::rptMsg ("DisplayName: ".$s->get_value("DisplayName")->get_data()); }; diff --git a/thirdparty/rr-full/plugins/secctr.pl b/thirdparty/rr-full/plugins/secctr.pl index d4f0c8cb9a7..5d5a355b386 100644 --- a/thirdparty/rr-full/plugins/secctr.pl +++ b/thirdparty/rr-full/plugins/secctr.pl @@ -3,22 +3,27 @@ # Plugin to get data from Security Center keys # # Change History: +# 20201005 - MITRE update +# 20200517 - updated date output format # 20100310 - created # # References: # # -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package secctr; use strict; -my %config = (hive => "Software", +my %config = (hive => "software", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20100310); + MITRE => "", + category => "config", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -48,7 +53,7 @@ sub pluginmain { $infected++; ::rptMsg(""); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { diff --git a/thirdparty/rr-full/plugins/secrets.pl b/thirdparty/rr-full/plugins/secrets.pl index d5466cca016..b90954272ca 100644 --- a/thirdparty/rr-full/plugins/secrets.pl +++ b/thirdparty/rr-full/plugins/secrets.pl @@ -4,6 +4,9 @@ # # # History +# 20201005 - category update +# 20200831 - MITRE updates +# 20200517 - updated date output format # 20140730 - created # # Note: When gsecdump.exe is run with the "-a" switch, or the LSA @@ -13,7 +16,9 @@ # correlate to the time that gsecdump.exe was run. Insight for this # plugin was provided by Jamie Levy # -# copyright 2014 Quantum Analytics Research, LLC +# https://attack.mitre.org/techniques/T1555/ +# +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package secrets; @@ -23,8 +28,10 @@ package secrets; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20140730); + MITRE => "T1555", + category => "credential access", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -50,7 +57,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); } diff --git a/thirdparty/rr-full/plugins/secrets_tln.pl b/thirdparty/rr-full/plugins/secrets_tln.pl index 11e083bbe2d..a2573e0c814 100644 --- a/thirdparty/rr-full/plugins/secrets_tln.pl +++ b/thirdparty/rr-full/plugins/secrets_tln.pl @@ -4,6 +4,8 @@ # # # History +# 20201005 - category update +# 20200831 - MITRE updates # 20140730 - created # # Note: When gsecdump.exe is run with the "-a" switch, or the LSA @@ -13,7 +15,9 @@ # correlate to the time that gsecdump.exe was run. Insight for this # plugin was provided by Jamie Levy # -# copyright 2014 Quantum Analytics Research, LLC +# https://attack.mitre.org/techniques/T1555/ +# +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package secrets_tln; @@ -23,8 +27,10 @@ package secrets_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20140814); + MITRE => "T1555", + output => "tln", + category => "credential access", + version => 20201005); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/security b/thirdparty/rr-full/plugins/security index a2677d5903d..fa0e9d79e15 100644 --- a/thirdparty/rr-full/plugins/security +++ b/thirdparty/rr-full/plugins/security @@ -1,5 +1,4 @@ auditpol -auditpol_xp -lsasecrets -polacdms secrets +secrets_tln +securityproviders diff --git a/thirdparty/rr-full/plugins/securityproviders.pl b/thirdparty/rr-full/plugins/securityproviders.pl index 308a642ac0b..4818e625668 100644 --- a/thirdparty/rr-full/plugins/securityproviders.pl +++ b/thirdparty/rr-full/plugins/securityproviders.pl @@ -4,12 +4,14 @@ # that Win32/Hioles.C uses this key as a persistence mechanism # # Change history +# 20201005 - MITRE update +# 20200526 - updated date output format # 20120312 - added Hostname # # References -# +# https://attack.mitre.org/techniques/T1547/005/ # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package securityproviders; @@ -19,8 +21,10 @@ package securityproviders; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120312); + MITRE => "T1547\.005", + category => "persistence", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -38,7 +42,9 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching securityproviders v.".$VERSION); ::rptMsg("Launching securityproviders v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -53,7 +59,7 @@ sub pluginmain { my $key_path = $ccs."\\Control\\SecurityProviders"; my $key; if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("LastWrite: ".gmtime($key->get_timestamp())); + ::rptMsg("LastWrite: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my $providers = $key->get_value("SecurityProviders")->get_data(); ::rptMsg("SecurityPrividers = ".$providers); diff --git a/thirdparty/rr-full/plugins/services.pl b/thirdparty/rr-full/plugins/services.pl index b14fa166236..2494dd1a65d 100644 --- a/thirdparty/rr-full/plugins/services.pl +++ b/thirdparty/rr-full/plugins/services.pl @@ -4,6 +4,10 @@ # services # # Change history +# 20200831 - updated to include FailureCommand, MITRE updates +# 20200511 - updated date output format +# *Note: LastWrite time stamps not used, as they don't provide much value +# 20191024 - updated parsing of value data that includes ; # 20080507 - Added collection of Type and Start values; separated # data by Services vs. Drivers; created separate plugin # for Drivers @@ -12,17 +16,20 @@ # References # # -# copyright 2008 H. Carvey +# copyright 2020 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package services; #use strict; -my %config = (hive => "System", +my %config = (hive => "system", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080507); + category => "persistence", + MITRE => "T1547", + output => "report", + version => 20200831); sub getConfig{return %config} sub getShortDescr { @@ -53,8 +60,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching services v.".$VERSION); - ::rptMsg("services v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("services v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -114,20 +123,26 @@ sub pluginmain { $group = $s->get_value("Group")->get_data(); }; - my $str = $name.";".$display.";".$image.";".$type.";".$start.";".$group; + my $failcmd; + eval { + $failcmd = $s->get_value("FailureCommand")->get_data(); + }; + + my $str = $name."|".$display."|".$image."|".$type."|".$start."|".$group."|".$failcmd; push(@{$svcs{$s->get_timestamp()}},$str) unless ($str eq ""); } foreach my $t (reverse sort {$a <=> $b} keys %svcs) { - ::rptMsg(gmtime($t)."Z"); + ::rptMsg(gmtime($t)." Z"); foreach my $item (@{$svcs{$t}}) { - my ($n,$d,$i,$t,$s,$g) = split(/;/,$item,6); - ::rptMsg(" Name = ".$n); - ::rptMsg(" Display = ".$d); - ::rptMsg(" ImagePath = ".$i); - ::rptMsg(" Type = ".$t); - ::rptMsg(" Start = ".$s); - ::rptMsg(" Group = ".$g); + my ($n,$d,$i,$t,$s,$g) = split(/\|/,$item,7); + ::rptMsg(" Name = ".$n); + ::rptMsg(" Display = ".$d); + ::rptMsg(" ImagePath = ".$i); + ::rptMsg(" Type = ".$t); + ::rptMsg(" Start = ".$s); + ::rptMsg(" Group = ".$g); + ::rptMsg(" FailureCommand = ".$f); ::rptMsg(""); } } @@ -140,12 +155,10 @@ sub pluginmain { } else { ::rptMsg($s_path." not found."); - ::logMsg($s_path." not found."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } diff --git a/thirdparty/rr-full/plugins/sevenzip.pl b/thirdparty/rr-full/plugins/sevenzip.pl index 30abdaf299c..7fdf09fca56 100644 --- a/thirdparty/rr-full/plugins/sevenzip.pl +++ b/thirdparty/rr-full/plugins/sevenzip.pl @@ -1,17 +1,18 @@ #----------------------------------------------------------- # sevenzip.pl # -# -# # Change history +# 20220704 - updated to include MOTW prop. value +# 20200803 - updates +# 20200515 - minor updates # 20130315 - minor updates added # 20100218 - created # # References -# +# https://isc.sans.edu/forums/diary/7Zip+MoW/28810/ # # -# copyright 2013 Quantum Analytics Research, LLC +# copyright 2022 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package sevenzip; @@ -21,12 +22,14 @@ package sevenzip; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20130315); + category => "file access", + MITRE => "T1074", + output => "report", + version => 20220704); sub getConfig{return %config} sub getShortDescr { - return "Gets records of histories from 7-Zip keys"; + return "Gets 7-Zip histories & settings"; } sub getDescr{} sub getRefs {} @@ -39,8 +42,10 @@ sub pluginmain { my $class = shift; my $ntuser = shift; my %hist; - ::logMsg("Launching 7-zip v.".$VERSION); - + ::logMsg("Launching sevenzip v.".$VERSION); + ::rptMsg("sevenzip v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -52,55 +57,70 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { eval { - ::rptMsg("PanelPath0: ".$key->get_subkey("FM")->get_value("PanelPath0")->get_data()); + my $p = $key->get_subkey("FM")->get_value("PanelPath0")->get_data(); + ::rptMsg("PanelPath0: ".$p); ::rptMsg(""); }; eval { - ::rptMsg("ArcHistory:"); my $copy = $key->get_subkey("Compression")->get_value("ArcHistory")->get_data(); - my @c = split(/\x00\x00/,$copy); + my @c = split(/\00\00/,$copy); + ::rptMsg("ArcHistory:"); foreach my $hist (@c) { - $hist =~ s/\x00//g; + $hist =~ s/\00//g; ::rptMsg(" ".$hist); } }; eval { - ::rptMsg("PathHistory:"); my $copy = $key->get_subkey("Extraction")->get_value("PathHistory")->get_data(); - my @c = split(/\x00\x00/,$copy); + my @c = split(/\00\00/,$copy); + ::rptMsg("PathHistory:"); foreach my $hist (@c) { - $hist =~ s/\x00//g; + $hist =~ s/\00//g; ::rptMsg(" ".$hist); } - ::rptMsg(""); +# ::rptMsg(""); }; eval { - ::rptMsg("CopyHistory:"); my $copy = $key->get_subkey("FM")->get_value("CopyHistory")->get_data(); - my @c = split(/\x00\x00/,$copy); + my @c = split(/\00\00/,$copy); + ::rptMsg("CopyHistory:"); foreach my $hist (@c) { - $hist =~ s/\x00//g; + $hist =~ s/\00//g; ::rptMsg(" ".$hist); } - ::rptMsg(""); +# ::rptMsg(""); }; eval { - ::rptMsg("FolderHistory:"); my $copy = $key->get_subkey("FM")->get_value("FolderHistory")->get_data(); - my @c = split(/\x00\x00/,$copy); + my @c = split(/\00\00/,$copy); + ::rptMsg("FolderHistory:"); foreach my $hist (@c) { - $hist =~ s/\x00//g; + $hist =~ s/\00//g; ::rptMsg(" ".$hist); } }; - +# added 20220704 + if (my $o = $key->get_subkey("Options")) { + + eval { + my $m = $key->get_value("WriteZoneIdExtract")->get_data(); + ::rptMsg("WriteZoneIdExtract = ".$m); + }; + ::rptMsg("WriteZoneIdExtract value not found.") if ($@); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the WriteZoneIdExtract value doesn't exist, or is set to 0, MOTW is not propagated."); + ::rptMsg("If WriteZoneIdExtract = 1, MOTW is propagated."); + ::rptMsg("If WriteZoneIdExtract = 2, MOTW is propagated, for Office files only."); + ::rptMsg(""); + ::rptMsg("Ref: https://isc.sans.edu/forums/diary/7Zip+MoW/28810/"); + } } else { - ::rptMsg($key_path." not found."); +# ::rptMsg($key_path." not found."); } } } diff --git a/thirdparty/rr-full/plugins/sfc.pl b/thirdparty/rr-full/plugins/sfc.pl deleted file mode 100644 index 47482406961..00000000000 --- a/thirdparty/rr-full/plugins/sfc.pl +++ /dev/null @@ -1,108 +0,0 @@ -#----------------------------------------------------------- -# sfc.pl -# Check SFC settings in the Registry -# -# History -# 20100305 - updated -# -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package sfc; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20100305); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get SFC values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching sfc v.".$VERSION); - ::rptMsg("sfc v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("sfc v.".$VERSION); - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - next unless ($name =~ m/^sfc/i); - my $str; - if ($name =~ m/^sfcquota$/i || $name =~ m/^sfcdisable$/i) { - $str = sprintf " %-20s 0x%08x",$name,$v->get_data(); - } - else { - $str = sprintf " %-20s %-20s",$name,$v->get_data(); - } - ::rptMsg($str); - } - - } - else { - ::rptMsg($key_path." key has no values."); - } - } - else { - ::rptMsg($key_path." key not found."); - ::logMsg($key_path." key not found."); - } - ::rptMsg(""); -# According to http://support.microsoft.com/kb/222193, sfc* values in this key, if -# it exists, take precedence over and are copied into the values within the Winlogon -# key; see also http://support.microsoft.com/kb/222473/ - $key_path = "Policies\\Microsoft\\Windows NT\\Windows File Protection"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - next unless ($name =~ m/^sfc/i); - my $str; - if ($name =~ m/^sfcquota$/i || $name =~ m/^sfcdisable$/i) { - $str = sprintf " %-20s 0x%08x",$name,$v->get_data(); - } - else { - $str = sprintf " %-20s %-20s",$name,$v->get_data(); - } - ::rptMsg($str); - } - - } - else { - ::rptMsg($key_path." key has no values."); - } - } - else { - ::rptMsg($key_path." key not found."); -# ::logMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/shadow.pl b/thirdparty/rr-full/plugins/shadow.pl new file mode 100644 index 00000000000..4d74e78b550 --- /dev/null +++ b/thirdparty/rr-full/plugins/shadow.pl @@ -0,0 +1,81 @@ +#----------------------------------------------------------- +# shadow.pl +# The "Shadow" value allows for eavesdropping on RDP connections by admins; +# this could be used for insider threat issues. +# +# Change history: +# 20210425 - added "bitsamin.in" reference +# 20210217 - created +# +# References: +# https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.TerminalServer::TS_RemoteControl_2 +# http://woshub.com/rdp-session-shadow-to-windows-10-user/ +# https://bitsadm.in/blog/spying-on-users-using-rdp-shadowing +# https://twitter.com/SagieSec/status/1469001618863624194 #added 20220114 +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package shadow; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + MITRE => "T1112", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210425); + +sub getConfig{return %config} + +sub getShortDescr { + return "Shadow value allows for eavesdropping on RDP connections"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching shadow v.".$VERSION); + ::rptMsg("shadow v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key_path = "Policies\\Microsoft\\Windows NT\\Terminal Services"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $shadow = $key->get_value("Shadow")->get_data(); + ::rptMsg("Shadow value = ".$shadow); + }; + ::rptMsg("Shadow value not found.") if ($@); + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: The \"Shadow\" value allows admins to interact with a user's RDP session based on the option selected"); + ::rptMsg("0 - No remote control allowed"); + ::rptMsg("1 - Full control with user's permission"); + ::rptMsg("2 - Full control without user's permission"); + ::rptMsg("3 - View session with user's permission"); + ::rptMsg("4 - View session without user's permission"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shares.pl b/thirdparty/rr-full/plugins/shares.pl index 143c282b1ff..c053837d07e 100644 --- a/thirdparty/rr-full/plugins/shares.pl +++ b/thirdparty/rr-full/plugins/shares.pl @@ -4,6 +4,8 @@ # Retrieve information about shares from a System hive file # # History +# 20201005 - MITRE update +# 20200525 - minor updates # 20140730 - added collection of NullSessionShares # 20090112 - created # @@ -12,23 +14,25 @@ # For info about share types, see the Win32_Share WMI class: # http://msdn.microsoft.com/en-us/library/aa394435(VS.85).aspx # -# copyright 2014 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package shares; use strict; -my %config = (hive => "System", - osmask => 22, +my %config = (hive => "system", + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20140730); + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { - return "Get list of shares from System hive file"; + return "Lists available shares on the system"; } sub getDescr{} sub getRefs {} @@ -97,7 +101,7 @@ sub pluginmain { # Determine of the AutoShareServer/Wks values have been set my $path = $key_path."\\".$lanman; - $tag = "parameters"; + my $tag = "parameters"; my $para = getKeyPath($path,$tag); eval { if ($key = $root_key->get_subkey($path."\\".$para)) { @@ -139,4 +143,4 @@ sub getKeyPath { return $subkey; } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shc.pl b/thirdparty/rr-full/plugins/shc.pl index bb3fa0e5acd..ca9734f3aa3 100644 --- a/thirdparty/rr-full/plugins/shc.pl +++ b/thirdparty/rr-full/plugins/shc.pl @@ -3,36 +3,31 @@ # This key may have something to do with the Start Menu Cache - nothing # definitive yet. # -# In my tests *some* installers/applications populate this key on *some* systems -# and Windows shows *some* of these items as "Recently Installed" at the top of -# the start menu. More research is still needed. -Keith Twombley -# ktwombley@gmail.com -# # Change history +# 20201005 - MITRE update +# 20200427 - updated output date format +# 20200330 - updated # 20130412 - created - IN PROCESS; NOT COMPLETE -# 20190305 - updated - outputs entries from shc # -# # References # # https://chentiangemalc.wordpress.com/2011/11/02/customizing-default-start-menu-in-windows-developer-preview/ # http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/296cd88b-d806-4a81-a3d0-ea27de4c8b52 # -# Copyright 2013 QAR, LLC +# Copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package shc; use strict; my %config = (hive => "NTUSER\.DAT", - hivemask => 16, - output => "report", - category => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 32, #Windows 8 - version => 20190305); + MITRE => "", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -49,8 +44,8 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching shc v.".$VERSION); - ::rptMsg("shc v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("shc v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -58,21 +53,13 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { - my %shc; - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); - $shc{$name} = $data - } - - foreach my $u (sort {$a <=> $b} keys %shc) { - ::rptMsg(" ".$u." -> ".$shc{$u}); + ::rptMsg($v->get_name()." - ".$v->get_data()); } } else { diff --git a/thirdparty/rr-full/plugins/shellactivities.pl b/thirdparty/rr-full/plugins/shellactivities.pl deleted file mode 100644 index 5df9f5615f6..00000000000 --- a/thirdparty/rr-full/plugins/shellactivities.pl +++ /dev/null @@ -1,245 +0,0 @@ -#----------------------------------------------------------- -# shellactivities.pl -# -# -# Change history -# 20180709 - updated -# 20180611 - created (per request submitted by John McCash) -# -# References -# https://twitter.com/gazambelli/status/1005170301355864065 -# -# copyright 2018 QAR, LLC -# author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package shellactivities; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20180709); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's ShellActivities key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching shellactivities v.".$VERSION); - ::rptMsg("shellactivities v.".$VERSION); - ::rptMsg("- ".getShortDescr()."\n"); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\CloudStore\\Store\\Cache\\DefaultAccount\\$$windows.data.taskflow.shellactivities\\Current'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Key LastWrite: ".gmtime($key->get_timestamp())); - eval { - my $data = $key->get_value("Data")->get_data(); - processShellActivities($data); - }; - } - else { - ::rptMsg($key_path." not found."); - } -} - -#----------------------------------------------------------- -# -#----------------------------------------------------------- -sub processShellActivities { - my $data = shift; - my $sz = length($data); - my $count = 0; - my $offset = 4; - my ($l,$tag,$str); - my ($t0,$t1) = unpack("VV",substr($data,$offset,8)); - ::rptMsg("Time stamp: ".gmtime(::getTime($t0,$t1))." Z"); - ::rptMsg(""); - - while ($offset < ($sz - 10)) { - -# Code to locate the appropriate identifier - $tag = 1; - while ($tag) { - if (unpack("v",substr($data,$offset,2)) == 0x14d2) { - $tag = 0; - } - else { - $offset++; - # Check if at end of file and exit loop if it is - last if ($offset >= $sz ); - } - } - - # Check if at end of file and exit loop if it is - last if ($offset >= $sz ); - - - $offset += 2; - $l = unpack("C",substr($data,$offset,1)); -# ::rptMsg("String Length: ".sprintf "0x%x",$l); - $offset += 1; - $str = substr($data,$offset,$l * 2); - $str =~ s/\00//g; - ::rptMsg("Path: ".$str); - $offset += $l * 2; - - $tag = 1; - while ($tag) { - if (unpack("v",substr($data,$offset,2)) == 0x23d2) { - $tag = 0; - } - else { - $offset++; - } - } - - $offset += 2; - $l = unpack("C",substr($data,$offset,1)); - $offset += 1; - $str = substr($data,$offset,$l * 2); - $str =~ s/\00//g; -# ::rptMsg($str); - $offset += $l * 2; - - $tag = 1; - while ($tag) { - if (unpack("v",substr($data,$offset,2)) == 0x28d2) { - $tag = 0; - } - else { - $offset++; - } - } - - $offset += 2; - $l = unpack("C",substr($data,$offset,1)); - $offset += 1; - $str = substr($data,$offset,$l * 2); - $str =~ s/\00//g; - ::rptMsg("Window Title: ".$str); - $offset += $l * 2; - - $tag = 1; - while ($tag) { - if (unpack("v",substr($data,$offset,2)) == 0x32c6) { - $tag = 0; - } - else { - $offset++; - } - } - - $offset += 3; -# probe(substr($data,$offset,8)); - ($t0,$t1) = unpack("VV",substr($data,$offset,8)); -# ::rptMsg("Time 1: ".gmtime(::getTime($t0,$t1))." Z"); - - $tag = 1; - while ($tag) { - if (unpack("v",substr($data,$offset,2)) == 0x3cc6) { - $tag = 0; - } - else { - $offset++; - } - } - - $offset += 3; -# probe(substr($data,$offset,8)); - ($t0,$t1) = unpack("VV",substr($data,$offset,8)); -# ::rptMsg("Time 2: ".gmtime(::getTime($t0,$t1))." Z"); - $offset += 8; - - $count++; - ::rptMsg(""); - } - ::rptMsg("Total Count: ".$count); -} - -#----------------------------------------------------------- -# -#----------------------------------------------------------- - - -#----------------------------------------------------------- -# -#----------------------------------------------------------- - - -#----------------------------------------------------------- -# probe() -# -# Code the uses printData() to insert a 'probe' into a specific -# location and display the data -# -# Input: binary data of arbitrary length -# Output: Nothing, no return value. Displays data to the console -#----------------------------------------------------------- -sub probe { - my $data = shift; - my @d = printData($data); - ::rptMsg(""); - foreach (0..(scalar(@d) - 1)) { - ::rptMsg($d[$_]); - } - ::rptMsg(""); -} - -#----------------------------------------------------------- -# printData() -# subroutine used primarily for debugging; takes an arbitrary -# length of binary data, prints it out in hex editor-style -# format for easy debugging -# -# Usage: see probe() -#----------------------------------------------------------- -sub printData { - my $data = shift; - my $len = length($data); - - my @display = (); - - my $loop = $len/16; - $loop++ if ($len%16); - - foreach my $cnt (0..($loop - 1)) { -# How much is left? - my $left = $len - ($cnt * 16); - - my $n; - ($left < 16) ? ($n = $left) : ($n = 16); - - my $seg = substr($data,$cnt * 16,$n); - my $lhs = ""; - my $rhs = ""; - foreach my $i ($seg =~ m/./gs) { -# This loop is to process each character at a time. - $lhs .= sprintf(" %02X",ord($i)); - if ($i =~ m/[ -~]/) { - $rhs .= $i; - } - else { - $rhs .= "."; - } - } - $display[$cnt] = sprintf("0x%08X %-50s %s",$cnt,$lhs,$rhs); - } - return @display; -} - - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shellbags.pl b/thirdparty/rr-full/plugins/shellbags.pl index b0e71ec2995..07f15a2ecd8 100644 --- a/thirdparty/rr-full/plugins/shellbags.pl +++ b/thirdparty/rr-full/plugins/shellbags.pl @@ -3,6 +3,9 @@ # RR plugin to parse (Vista, Win7/Win2008R2) shell bags # # History: +# 20200831 - MITRE updates +# 20200824 - Unicode updates +# 20200428 - updated output date format # 20190715 - updated to parse WPD devices better # 20180702 - update to parseGUID function # 20180117 - modification thanks to input/data from Mike Godfrey @@ -35,23 +38,22 @@ # Moore for writing the shell bag parser for Registry Decoder, as well as # assistance with some parsing. # -# -# copyright 2015 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package shellbags; use strict; use Time::Local; -my %config = (hive => "USRCLASS\.DAT", - hivemask => 32, - output => "report", - category => "User Activity", - osmask => 20, #Vista, Win7/Win2008R2 +my %config = (hive => "USRCLASS\.DAT, NTUSER\.DAT", + hivemask => 32, + output => "report", + category => "user activity", + MITRE => "", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20190715); + version => 20200831); sub getConfig{return %config} @@ -146,6 +148,7 @@ sub getShortDescr { "{a8cdff1c-4878-43be-b5fd-f8091c1c60d0}" => "Documents", "{fdd39ad0-238f-46af-adb4-6c85480369c7}" => "Documents", "{374de290-123f-4565-9164-39c4925e467b}" => "Downloads", + "{088e3905-0323-4b02-9826-5d99428e115f}" => "Downloads", "{de61d971-5ebc-4f02-a3a9-6c82895e5c04}" => "Get Programs", "{a305ce99-f527-492b-8b1a-7e76fa98d6e4}" => "Installed Updates", "{871c5380-42a0-1069-a2ea-08002b30309d}" => "Internet Explorer (Homepage)", @@ -394,7 +397,6 @@ sub parseVariableEntry { while($t) { my $sz = unpack("V",substr($stuff,$cnt,4)); my $id = unpack("V",substr($stuff,$cnt + 4,4)); - return %item unless (defined $sz); #-------------------------------------------------------------- # sub-segment types # 0x0a - file name @@ -410,8 +412,8 @@ sub parseVariableEntry { my $num = unpack("V",substr($stuff,$cnt + 13,4)); my $str = substr($stuff,$cnt + 13 + 4,($num * 2)); - $str =~ s/\00//g; - $item{name} = $str; +# $str =~ s/\00//g; + $item{name} = ::getUnicodeStr($str); } $cnt += $sz; } @@ -427,10 +429,12 @@ sub parseVariableEntry { my ($n0, $n1, $n2) = unpack("VVV",substr($data,62,12)); my $n0_name = substr($data,0x4A,($n0 * 2)); - $n0_name =~ s/\00//g; + $n0_name = ::getUnicodeStr($n0_name); +# $n0_name =~ s/\00//g; my $n1_name = substr($data,(0x4A + ($n0 * 2)),($n1 * 2)); - $n1_name =~ s/\00//g; + $n1_name = ::getUnicodeStr($n1_name); +# $n1_name =~ s/\00//g; if ($n0_name eq "") { $item{name} = $n1_name; @@ -443,19 +447,20 @@ sub parseVariableEntry { elsif ($tag == 0x7b || $tag == 0xbb || $tag == 0xfb) { my ($sz1,$sz2,$sz3) = unpack("VVV",substr($data,0x3e,12)); $item{name} = substr($data,0x4a,$sz1 * 2); - $item{name} =~ s/\00//g; + $item{name} = ::getUnicodeStr($item{name}); +# $item{name} =~ s/\00//g; } elsif ($tag == 0x02 || $tag == 0x03) { my ($sz1,$sz2,$sz3,$sz4) = unpack("VVVV",substr($data,0x26,16)); $item{name} = substr($data,0x36,$sz1 * 2); - $item{name} =~ s/\00//g; + $item{name} = ::getUnicodeStr($item{name}); +# $item{name} =~ s/\00//g; } elsif (unpack("v",substr($data,6,2)) == 0x05) { my $o = 0x26; my $t = 1; while ($t) { my $i = substr($data,$o,1); - return %item unless (defined $i); if ($i =~ m/\00/) { $t = 0; } @@ -516,9 +521,11 @@ sub parseZipSubFolderItem { my $sz2 = unpack("V",substr($data,0x58,4)); my $str1 = substr($data,0x5C,$sz *2) if ($sz > 0); - $str1 =~ s/\00//g; + $str1 = ::getUnicodeStr($str1); +# $str1 =~ s/\00//g; my $str2 = substr($data,0x5C + ($sz * 2),$sz2 *2) if ($sz2 > 0); - $str2 =~ s/\00//g; + $str2 = ::getUnicodeStr($str2); +# $str2 =~ s/\00//g; if ($sz2 > 0) { $item{name} = $str1."\\".$str2; @@ -581,10 +588,12 @@ sub parseURIEntry { my $sz = unpack("V",substr($data,0x2a,4)); my $uri = substr($data,0x2e,$sz); - $uri =~ s/\00//g; + $uri = ::getUnicodeStr($uri); +# $uri =~ s/\00//g; my $proto = substr($data,length($data) - 6, 6); - $proto =~ s/\00//g; + $proto = ::getUnicodeStr($proto); +# $proto =~ s/\00//g; $item{name} = $proto."://".$uri." [".gmtime($item{uritime})."]"; @@ -645,7 +654,6 @@ sub parseGUID { else { return $guid; } - } #----------------------------------------------------------- @@ -669,7 +677,8 @@ sub parseDeviceEntry { } elsif ($tag == 2) { $item{name} = substr($data,0x0a,($ofs + 6) - 0x0a); - $item{name} =~ s/\00//g; + $item{name} = ::getUnicodeStr($item{name}); +# $item{name} =~ s/\00//g; } else { my $ver = unpack("C",substr($data,9,1)); @@ -690,9 +699,11 @@ sub parseDeviceEntry { my $userlen = unpack("V",substr($data,30,4)); my $devlen = unpack("V",substr($data,34,4)); my $user = substr($data,0x28,$userlen * 2); - $user =~ s/\00//g; + $user = ::getUnicodeStr($user); +# $user =~ s/\00//g; my $dev = substr($data,0x28 + ($userlen * 2),$devlen * 2); - $dev =~ s/\00//g; + $dev = ::getUnicodeStr($dev); +# $dev =~ s/\00//g; $item{name} = $user; } # Version unknown @@ -779,7 +790,7 @@ sub parseFolderEntry { $tag = 0; } else { - $str .= $s; + $str .= $s; $cnt++; } } @@ -794,12 +805,11 @@ sub parseFolderEntry { my $str = ""; while($tag) { my $s = substr($data,$ofs_shortname + $cnt,1); - return %item unless (defined $s); if ($s =~ m/\00/ && ((($cnt + 1) % 2) == 0)) { $tag = 0; } else { - $str .= $s; + $str .= $s; $cnt++; } } @@ -810,9 +820,7 @@ sub parseFolderEntry { my $tag = 1; my $cnt = 0; while ($tag) { - my $s = substr($data,$ofs + $cnt,2); - return %item unless (defined $s); - if (unpack("v",$s) == 0xbeef) { + if (unpack("v",substr($data,$ofs + $cnt,2)) == 0xbeef) { $tag = 0; } else { @@ -858,12 +866,14 @@ sub parseFolderEntry { my $str = substr($data,$ofs,length($data) - 30); my $longname = (split(/\00\00/,$str,2))[0]; +# $longname = ::getUnicodeStr($longname); + $longname =~ s/\00//g; if ($longname ne "") { - $item{name} = Utf16ToUtf8($longname); + $item{name} = $longname; } else { - $item{name} = UTF16ToUtf8($shortname); + $item{name} = $shortname; } } return %item; @@ -915,9 +925,7 @@ sub parseFolderEntry2 { my $tag = 1; while ($tag) { - my $s = substr($data,$ofs,2); - return %item unless (defined $s); - if (unpack("v",$s) == 0xbeef) { + if (unpack("v",substr($data,$ofs,2)) == 0xbeef) { $tag = 0; } else { @@ -956,7 +964,8 @@ sub parseFolderEntry2 { $item{name} = (split(/\00\00/,$str,2))[0]; $item{name} =~ s/\13\20/\2D\00/; - $item{name} = Utf16ToUtf8($item{name}); + $item{name} = ::getUnicodeStr($item{name}); +# $item{name} =~ s/\00//g; return %item; } @@ -1018,12 +1027,11 @@ sub shellItem0x52 { while ($tag) { $d = substr($data,0x32 + $cnt,2); - return %item unless (defined $d); if (unpack("v",$d) == 0) { $tag = 0; } else { - $item{name} .= $d; + $item{name} .= $d; $cnt += 2; } } @@ -1037,7 +1045,8 @@ sub shellItem0x52 { } $sz = unpack("V",substr($data,$ofs,4)); $item{str} = substr($data,$ofs + 4,$sz * 2); - $item{str} =~ s/\00//g; + $item{str} = ::getUnicodeStr($item{str}); +# $item{str} =~ s/\00//g; return %item; } @@ -1118,15 +1127,4 @@ sub getNum48 { } } -#--------------------------------------------------------------------- -# Utf16ToUtf8() -#--------------------------------------------------------------------- -sub Utf16ToUtf8 { - my $str = $_[0]; - Encode::from_to($str,'UTF-16LE','utf8'); - $str = Encode::decode_utf8($str); - return $str; -} - - 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shellbags_tln.pl b/thirdparty/rr-full/plugins/shellbags_tln.pl index f953b1afbf6..af0d33b7107 100644 --- a/thirdparty/rr-full/plugins/shellbags_tln.pl +++ b/thirdparty/rr-full/plugins/shellbags_tln.pl @@ -3,6 +3,7 @@ # RR plugin to parse (Vista, Win7/Win2008R2) shell bags # # History: +# 20200831 - MITRE, Unicode updates # 20180702 - code updates, including to parseGUID() function # 20120810 - added support for parsing Network types; added handling of # offsets for Folder types (ie, transition to long name offset), @@ -36,14 +37,14 @@ package shellbags_tln; use Time::Local; my %config = (hive => "USRCLASS\.DAT", - hivemask => 16, - output => "tln", - category => "User Activity", - osmask => 20, #Vista, Win7/Win2008R2 + hivemask => 16, + output => "tln", + category => "user activity", + MITRE => "", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20180702); + version => 20200831); sub getConfig{return %config} @@ -167,7 +168,7 @@ sub getShortDescr { sub pluginmain { my $class = shift; my $hive = shift; - ::logMsg("Launching shellbag2 v.".$VERSION); +# ::logMsg("Launching shellbags_tln v.".$VERSION); my %item = (); my $reg = Parse::Win32Registry->new($hive); @@ -358,7 +359,6 @@ sub parseVariableEntry { while($tag) { my $sz = unpack("V",substr($stuff,$cnt,4)); my $id = unpack("V",substr($stuff,$cnt + 4,4)); - return %item unless (defined $sz); #-------------------------------------------------------------- # sub-segment types # 0x0a - file name @@ -374,7 +374,7 @@ sub parseVariableEntry { my $num = unpack("V",substr($stuff,$cnt + 13,4)); my $str = substr($stuff,$cnt + 13 + 4,($num * 2)); - $str =~ s/\00//g; + $str = ::getUnicodeStr($str); $item{name} = $str; } $cnt += $sz; @@ -388,7 +388,6 @@ sub parseVariableEntry { # while($tag) { # my $sz = unpack("V",substr($stuff,$cnt,4)); # my $id = unpack("V",substr($stuff,$cnt + 4,4)); -# return %item unless (defined $sz); # # if ($sz == 0x00) { # $tag = 0; @@ -412,12 +411,12 @@ sub parseVariableEntry { elsif ($tag == 0x7b || $tag == 0xbb || $tag == 0xfb) { my ($sz1,$sz2,$sz3) = unpack("VVV",substr($data,0x3e,12)); $item{name} = substr($data,0x4a,$sz1 * 2); - $item{name} =~ s/\00//g; + $item{name} = ::getUnicodeStr($item{name}); } elsif ($tag == 0x02 || $tag == 0x03) { my ($sz1,$sz2,$sz3,$sz4) = unpack("VVVV",substr($data,0x26,16)); $item{name} = substr($data,0x36,$sz1 * 2); - $item{name} =~ s/\00//g; + $item{name} = ::getUnicodeStr($item{name}); } else { $item{name} = "Unknown Type"; @@ -470,9 +469,9 @@ sub parseZipSubFolderItem { my $sz2 = unpack("V",substr($data,0x58,4)); my $str1 = substr($data,0x5C,$sz *2) if ($sz > 0); - $str1 =~ s/\00//g; + $str1 = ::getUnicodeStr($str1); my $str2 = substr($data,0x5C + ($sz * 2),$sz2 *2) if ($sz2 > 0); - $str2 =~ s/\00//g; + $str2 = ::getUnicodeStr($str2); if ($sz2 > 0) { $item{name} = $str1."\\".$str2; @@ -511,10 +510,10 @@ sub parseURIEntry { my $sz = unpack("V",substr($data,0x2a,4)); my $uri = substr($data,0x2e,$sz); - $uri =~ s/\00//g; + $uri = ::getUnicodeStr($uri); my $proto = substr($data,length($data) - 6, 6); - $proto =~ s/\00//g; + $proto = ::getUnicodeStr($proto); $item{name} = $proto."://".$uri; @@ -608,7 +607,7 @@ sub parseDeviceEntry { } elsif ($tag == 2) { $item{name} = substr($data,0x0a,($ofs + 6) - 0x0a); - $item{name} =~ s/\00//g; + $item{name} = ::getUnicodeStr($item{name}); } else { my $ver = unpack("C",substr($data,9,1)); @@ -629,9 +628,9 @@ sub parseDeviceEntry { my $userlen = unpack("V",substr($data,30,4)); my $devlen = unpack("V",substr($data,34,4)); my $user = substr($data,0x28,$userlen * 2); - $user =~ s/\00//g; + $user = ::getUnicodeStr($user); my $dev = substr($data,0x28 + ($userlen * 2),$devlen * 2); - $dev =~ s/\00//g; + $dev = ::getUnicodeStr($dev); $item{name} = $user; } # Version unknown @@ -674,7 +673,7 @@ sub parseControlPanelEntry { # #----------------------------------------------------------- sub parseFolderEntry { - my $data = shift; + my $data = shift; my %item = (); $item{type} = unpack("C",substr($data,2,1)); @@ -703,71 +702,106 @@ sub parseFolderEntry { my @m = unpack("vv",substr($data,$ofs_mdate,4)); ($item{mtime_str},$item{mtime}) = convertDOSDate($m[0],$m[1]); +# DEBUG ------------------------------------------------ +# Added 20160706 based on sample data provided by J. Poling + + if (length($data) < 0x30) { +# start at offset 0xE, read in nul-term ASCII string (until "\00" is reached) + $ofs_shortname = 0xE; + my $tag = 1; + my $cnt = 0; + my $str = ""; + while($tag) { + my $s = substr($data,$ofs_shortname + $cnt,1); + if ($s =~ m/\00/) { + $tag = 0; + } + else { + $str .= $s; + $cnt++; + } + } + $item{name} = $str; + } + else { # Need to read in short name; nul-term ASCII # $item{shortname} = (split(/\00/,substr($data,12,length($data) - 12),2))[0]; - $ofs_shortname = $ofs_mdate + 6; - my $tag = 1; - my $cnt = 0; - my $str = ""; - while($tag) { - my $s = substr($data,$ofs_shortname + $cnt,1); - return %item unless (defined $s); - if ($s =~ m/\00/ && ((($cnt + 1) % 2) == 0)) { - $tag = 0; - } - else { - $str .= $s; - $cnt++; + $ofs_shortname = $ofs_mdate + 6; + my $tag = 1; + my $cnt = 0; + my $str = ""; + while($tag) { + my $s = substr($data,$ofs_shortname + $cnt,1); + if ($s =~ m/\00/ && ((($cnt + 1) % 2) == 0)) { + $tag = 0; + } + else { + $str .= $s; + $cnt++; + } } - } # $str =~ s/\00//g; - my $shortname = $str; - my $ofs = $ofs_shortname + $cnt + 1; + my $shortname = $str; + my $ofs = $ofs_shortname + $cnt + 1; # Read progressively, 1 byte at a time, looking for 0xbeef - my $tag = 1; - my $cnt = 0; - while ($tag) { - my $s = substr($data,$ofs + $cnt,2); - return %item unless (defined $s); - if (unpack("v",$s) == 0xbeef) { - $tag = 0; - } - else { - $cnt++; + my $tag = 1; + my $cnt = 0; + while ($tag) { + if (unpack("v",substr($data,$ofs + $cnt,2)) == 0xbeef) { + $tag = 0; + } + else { + $cnt++; + } } - } - $item{extver} = unpack("v",substr($data,$ofs + $cnt - 4,2)); - $ofs = $ofs + $cnt + 2; + $item{extver} = unpack("v",substr($data,$ofs + $cnt - 4,2)); +# printf "Version: 0x%x\n",$item{extver}; + $ofs = $ofs + $cnt + 2; - my @m = unpack("vv",substr($data,$ofs,4)); - ($item{ctime_str},$item{ctime}) = convertDOSDate($m[0],$m[1]); - $ofs += 4; - my @m = unpack("vv",substr($data,$ofs,4)); - ($item{atime_str},$item{atime}) = convertDOSDate($m[0],$m[1]); + my @m = unpack("vv",substr($data,$ofs,4)); + ($item{ctime_str},$item{ctime}) = convertDOSDate($m[0],$m[1]); + $ofs += 4; + my @m = unpack("vv",substr($data,$ofs,4)); + ($item{atime_str},$item{atime}) = convertDOSDate($m[0],$m[1]); - my $jmp; - if ($item{extver} == 0x07) { - $jmp = 26; - } - elsif ($item{extver} == 0x08) { - $jmp = 30; - } - elsif ($item{extver} == 0x09) { + my $jmp; + if ($item{extver} == 0x03) { + $jmp = 8; + } + elsif ($item{extver} == 0x07) { + $jmp = 26; + } + elsif ($item{extver} == 0x08) { + $jmp = 30; + } + elsif ($item{extver} == 0x09) { $jmp = 34; - } - else {} + } + else {} - $ofs += $jmp; + if ($item{type} == 0x31 && $item{extver} >= 0x07) { + my @n = unpack("Vvv",substr($data,$ofs + 8, 8)); + if ($n[2] != 0) { + $item{mft_rec_num} = getNum48($n[0],$n[1]); + $item{mft_seq_num} = $n[2]; +# ::rptMsg("MFT: ".$item{mft_rec_num}."/".$item{mft_seq_num}); +# probe($data); + } + } - my $str = substr($data,$ofs,length($data) - 30); - my $longname = (split(/\00\00/,$str,2))[0]; - $longname =~ s/\00//g; + $ofs += $jmp; - if ($longname ne "") { - $item{name} = $longname; - } - else { - $item{name} = $shortname; + my $str = substr($data,$ofs,length($data) - 30); + my $longname = (split(/\00\00/,$str,2))[0]; +# $longname = ::getUnicodeStr($longname); + $longname =~ s/\00//g; + + if ($longname ne "") { + $item{name} = $longname; + } + else { + $item{name} = $shortname; + } } return %item; } @@ -815,46 +849,20 @@ sub parseNetworkEntry { $item{name} = $names[0]; return %item; } + #----------------------------------------------------------- -# printData() -# subroutine used primarily for debugging; takes an arbitrary -# length of binary data, prints it out in hex editor-style -# format for easy debugging +# getNum48() +# borrowed from David Cowen's code #----------------------------------------------------------- -sub printData { - my $data = shift; - my $len = length($data); - my $tag = 1; - my $cnt = 0; - - my $loop = $len/16; - $loop++ if ($len%16); - - foreach my $cnt (0..($loop - 1)) { -# while ($tag) { - my $left = $len - ($cnt * 16); - - my $n; - ($left < 16) ? ($n = $left) : ($n = 16); - - my $seg = substr($data,$cnt * 16,$n); - my @str1 = split(//,unpack("H*",$seg)); - - my @s3; - my $str = ""; - - foreach my $i (0..($n - 1)) { - $s3[$i] = $str1[$i * 2].$str1[($i * 2) + 1]; - - if (hex($s3[$i]) > 0x1f && hex($s3[$i]) < 0x7f) { - $str .= chr(hex($s3[$i])); - } - else { - $str .= "\."; - } - } - my $h = join(' ',@s3); - ::rptMsg(sprintf "0x%08x: %-47s ".$str,($cnt * 16),$h); +sub getNum48 { + my $n1 = shift; + my $n2 = shift; + if ($n2 == 0) { + return $n1; + } + else { + $n2 = ($n2 *16777216); + return $n1 + $n2; } } diff --git a/thirdparty/rr-full/plugins/shellbags_xp.pl b/thirdparty/rr-full/plugins/shellbags_xp.pl index 25082ea89b8..22d5d8dd186 100644 --- a/thirdparty/rr-full/plugins/shellbags_xp.pl +++ b/thirdparty/rr-full/plugins/shellbags_xp.pl @@ -1,9 +1,18 @@ #----------------------------------------------------------- -# shellbags_xp.pl -# RR plugin to parse (Vista, Win7/Win2008R2) shell bags +# shellbags.pl +# RR plugin to parse XP shell bags # # History: -# 20130515 - created from shellbags.pl; many differences between XP and Win7 +# 20200831 - MITRE updates +# 20200824 - Unicode updates +# 20200428 - updated output date format +# 20190715 - updated to parse WPD devices better +# 20180702 - update to parseGUID function +# 20180117 - modification thanks to input/data from Mike Godfrey +# 20160706 - update +# 20150325 - updated parsing based on input from Eric Zimmerman +# 20140728 - updated shell item 0x01 parsing +# 20131216 - updated to support shell item type 0x52 # 20130102 - updated to include type 0x35 # 20120824 - updated parseFolderEntry() for XP (extver == 3) # 20120810 - added support for parsing Network types; added handling of @@ -29,28 +38,27 @@ # Moore for writing the shell bag parser for Registry Decoder, as well as # assistance with some parsing. # -# -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package shellbags_xp; use strict; use Time::Local; -my %config = (hive => "NTUSER\.DAT", - hivemask => 32, - output => "report", - category => "User Activity", - osmask => 20, #Vista, Win7/Win2008R2 +my %config = (hive => "USRCLASS\.DAT, NTUSER\.DAT", + hivemask => 32, + output => "report", + category => "user activity", + MITRE => "", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20130102); + version => 20200831); sub getConfig{return %config} sub getShortDescr { - return "Shell/BagMRU traversal in XP NTUSER.DAT hives"; + return "Shell/BagMRU traversal in XP USRCLASS\.DAT hives"; } sub getDescr{} sub getRefs {} @@ -71,6 +79,7 @@ sub getShortDescr { "{e2e7934b-dce5-43c4-9576-7fe4f75e7480}" => "Date and Time", "{00c6d95f-329c-409a-81d7-c46c66ea7f33}" => "Default Location", "{17cd9488-1228-4b2f-88ce-4298e93e0966}" => "Default Programs", + "{b4bfcc3a-db2c-424c-b029-7fe99a87c641}" => "Desktop", "{37efd44d-ef8d-41b1-940d-96973a50e9e0}" => "Desktop Gadgets", "{74246bfc-4c96-11d0-abef-0020af6b0b7a}" => "Device Manager", "{a8a91a66-3a7d-4424-8d24-04e180695c7a}" => "Devices and Printers", @@ -87,6 +96,7 @@ sub getShortDescr { "{a3dd4f92-658a-410f-84fd-6fbbbef2fffe}" => "Internet Options", "{a304259d-52b8-4526-8b1a-a1d6cecc8243}" => "iSCSI Initiator", "{725be8f7-668e-4c7b-8f90-46bdb0936430}" => "Keyboard", + "{bf782cc9-5a52-4a17-806c-2a894ffeeac5}" => "Language Settings", "{e9950154-c418-419e-a90a-20c5287ae24b}" => "Location and Other Sensors", "{1fa9085f-25a2-489b-85d4-86326eedcd87}" => "Manage Wireless Networks", "{6c8eec18-8d75-41b2-a177-8831d59d2d50}" => "Mouse", @@ -108,8 +118,6 @@ sub getShortDescr { "{62d8ed13-c9d0-4ce8-a914-47dd628fb1b0}" => "Regional and Language Options", "{241d7c96-f8bf-4f85-b01f-e2b043341a4b}" => "RemoteApp and Desktop Connections", "{00f2886f-cd64-4fc9-8ec5-30ef6cdbe8c3}" => "Scanners and Cameras", - "{e211b736-43fd-11d1-9efb-0000f8757fcd}" => "Scanners and Cameras", - "{d6277990-4c6a-11cf-8d87-00aa0060f5bf}" => "Scheduled Tasks", "{f2ddfc82-8f12-4cdd-b7dc-d4fe1425aa4d}" => "Sound", "{58e3c745-d971-4081-9034-86e34b30836a}" => "Speech Recognition Options", "{9c73f5e5-7ae7-4e32-a8e8-8d23b85255bf}" => "Sync Center", @@ -134,25 +142,26 @@ sub getShortDescr { "{de974d24-d9c6-4d3e-bf91-f4455120b917}" => "Common Files", "{c1bae2d0-10df-4334-bedd-7aa20b227a9d}" => "Common OEM Links", "{5399e694-6ce5-4d6c-8fce-1d8870fdcba0}" => "Control Panel", - "{21ec2020-3aea-1069-a2dd-08002b30309d}" => "Control Panel", "{1ac14e77-02e7-4e5d-b744-2eb1ae5198b7}" => "CSIDL_SYSTEM", "{b4bfcc3a-db2c-424c-b029-7fe99a87c641}" => "Desktop", "{7b0db17d-9cd2-4a93-9733-46cc89022e7c}" => "Documents Library", + "{a8cdff1c-4878-43be-b5fd-f8091c1c60d0}" => "Documents", "{fdd39ad0-238f-46af-adb4-6c85480369c7}" => "Documents", "{374de290-123f-4565-9164-39c4925e467b}" => "Downloads", + "{088e3905-0323-4b02-9826-5d99428e115f}" => "Downloads", "{de61d971-5ebc-4f02-a3a9-6c82895e5c04}" => "Get Programs", "{a305ce99-f527-492b-8b1a-7e76fa98d6e4}" => "Installed Updates", "{871c5380-42a0-1069-a2ea-08002b30309d}" => "Internet Explorer (Homepage)", "{031e4825-7b94-4dc3-b131-e946b44c8dd5}" => "Libraries", - "{49bf5420-fa7f-11cf-8011-00a0c90a8f78}" => "Mobile Device", #MS KB836152 + "{2112ab0a-c86a-4ffe-a368-0de96e47012e}" => "Music", + "{1cf1260c-4dd0-4ebb-811f-33c572699fde}" => "Music", "{4bd8d571-6d19-48d3-be97-422220080e43}" => "Music", "{20d04fe0-3aea-1069-a2d8-08002b30309d}" => "My Computer", "{450d8fba-ad25-11d0-98a8-0800361b1103}" => "My Documents", - "{fc9fb64a-1eb2-4ccf-af5e-1a497a9b5c2d}" => "My Shared Folders", -# "{5e591a74-df96-48d3-8d67-1733bcee28ba}" => "My Documents", "{ed228fdf-9ea8-4870-83b1-96b02cfe0d52}" => "My Games", "{208d2c60-3aea-1069-a2d7-08002b30309d}" => "My Network Places", "{f02c1a0d-be21-4350-88b0-7367fc96ef3c}" => "Network", + "{3add1653-eb32-4cb0-bbd7-dfa0abb5acca}" => "Pictures", "{33e28130-4e1e-4676-835a-98395c3bc3bb}" => "Pictures", "{a990ae9f-a03b-4e80-94bc-9912d7504104}" => "Pictures", "{7c5a40ef-a0fb-4bfc-874a-c0f2e0b9fa8e}" => "Program Files (x86)", @@ -168,16 +177,17 @@ sub getShortDescr { "{d65231b0-b2f1-4857-a4ce-a8e7c6ea7d27}" => "System32 (x86)", "{9e52ab10-f80d-49df-acb8-4330f5687855}" => "Temporary Burn Folder", "{f3ce0f7c-4901-4acc-8648-d5d44b04ef8f}" => "Users Files", - "{59031a47-3f72-44a7-89c5-5595fe6b30ee}" => "User Files", "{59031a47-3f72-44a7-89c5-5595fe6b30ee}" => "Users", + "{a0953c92-50dc-43bf-be83-3742fed03c9c}" => "Videos", + "{b5947d7f-b489-4fde-9e77-23780cc610d1}" => "Virtual Machines", "{f38bf404-1d43-42f2-9305-67de0b28fc23}" => "Windows"); sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching shellbags_xp v.".$VERSION); - ::rptMsg("shellbags_xp v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("shellbags_xp v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my %item = (); my $reg = Parse::Win32Registry->new($hive); @@ -190,8 +200,8 @@ sub pluginmain { $item{path} = "Desktop\\"; $item{name} = ""; # Print header info - ::rptMsg(sprintf "%-20s |%-20s | %-20s | %-20s | %-20s |Resource","MRU Time","Modified","Accessed","Created","Zip_Subfolder"); - ::rptMsg(sprintf "%-20s |%-20s | %-20s | %-20s | %-20s |"."-" x 12,"-" x 12,"-" x 12,"-" x 12,"-" x 12,"-" x 12); + ::rptMsg(sprintf "%-20s |%-20s | %-20s | %-20s | %-20s | %-12s |Resource","MRU Time","Modified","Accessed","Created","Zip_Subfolder", "MFT File Ref"); + ::rptMsg(sprintf "%-20s |%-20s | %-20s | %-20s | %-20s | %-12s |"."-" x 12,"-" x 12,"-" x 12,"-" x 12,"-" x 12,"-" x 12,"-" x 12); traverse($key,\%item); } else { @@ -224,22 +234,23 @@ sub traverse { my $type = unpack("C",substr($values{$v},2,1)); +# DEBUG ------------------------------------------------ +# ::rptMsg($key->get_path()."\\".$v); +# ::rptMsg(sprintf "Type = 0x%x",$type); +# probe($values{$v}); +# ::rptMsg(""); +# DEBUG ------------------------------------------------ + # Need to first check to see if the parent of the item was a zip folder # and if the 'zipsubfolder' value is set to 1 if (exists ${$parent}{zipsubfolder} && ${$parent}{zipsubfolder} == 1) { -# These data items are different on Win7; need to reparse for XP -# my @d = printData($values{$v}); -# ::rptMsg(""); -# foreach (0..(scalar(@d) - 1)) { -# ::rptMsg($d[$_]); -# } -# ::rptMsg(""); -# %item = parseZipSubFolderItem($values{$v}); -# $item{zipsubfolder} = 1; + %item = parseZipSubFolderItem($values{$v}); + $item{zipsubfolder} = 1; } elsif ($type == 0x00) { # Variable/Property Sheet %item = parseVariableEntry($values{$v}); +# probe($values{$v}); } elsif ($type == 0x01) { # @@ -249,16 +260,12 @@ sub traverse { # System Folder %item = parseSystemFolderEntry($values{$v}); } + elsif ($type == 0x2a) { + $item{name} = substr($values{$v},0x3,3); + } elsif ($type == 0x2e) { # Device - %item = parseDeviceEntry($values{$v}); - - my @d = printData($values{$v}); - ::rptMsg(""); - foreach (0..(scalar(@d) - 1)) { - ::rptMsg($d[$_]); - } - ::rptMsg(""); + %item = parseDeviceEntry($values{$v}); } elsif ($type == 0x2F) { # Volume (Drive Letter) @@ -278,6 +285,7 @@ sub traverse { elsif ($type == 0x31 || $type == 0x32 || $type == 0xb1 || $type == 0x74) { # Folder or Zip File %item = parseFolderEntry($values{$v}); +# probe($values{$v}); } elsif ($type == 0x35) { %item = parseFolderEntry2($values{$v}); @@ -290,20 +298,14 @@ sub traverse { # URI type %item = parseURIEntry($values{$v}); } - elsif ($type == 0xd7 || $type == 0x9 || $type == 0xe3 || $type == 0x45) { - %item = parseXPShellDeviceItem($values{$v}); + elsif ($type == 0x52) { + %item = shellItem0x52($values{$v}); } else { # Unknown type $item{name} = sprintf "Unknown Type (0x%x)",$type; - - my @d = printData($values{$v}); - ::rptMsg(""); - foreach (0..(scalar(@d) - 1)) { - ::rptMsg($d[$_]); - } - ::rptMsg(""); - } + probe($values{$v}); + } if ($item{name} =~ m/\.zip$/ && $type == 0x32) { $item{zipsubfolder} = 1; @@ -325,16 +327,21 @@ sub traverse { (exists $item{ctime_str} && $item{ctime_str} ne "0") ? ($c = $item{ctime_str}) : ($c = ""); (exists $item{datetime} && $item{datetime} ne "N/A") ? ($o = $item{datetime}) : ($o = ""); - my $resource = ${$parent}{path}.$item{name}; +# my $resource = ${$parent}{path}.$item{name}; + $item{name} = ${$parent}{name}.$item{name}; + $item{path} = ${$parent}{path}.$v."\\"; + my $resource = $item{name}; if (exists $item{filesize}) { $resource .= " [".$item{filesize}."]"; } - if (exists $item{timestamp} && $item{timestamp} > 0) { - $resource .= " [".gmtime($item{timestamp})." Z]"; + my $mft = ""; + if (exists $item{mft_rec_num}) { + $mft = $item{mft_rec_num}."/".$item{mft_seq_num}; } - my $str = sprintf "%-20s |%-20s | %-20s | %-20s | %-20s |".$resource,$item{mrutime_str},$m,$a,$c,$o; +# my $str = sprintf "%-20s |%-20s | %-20s | %-20s | %-20s |".$resource,$item{mrutime_str},$m,$a,$c,$o; + my $str = sprintf "%-20s |%-20s | %-20s | %-20s | %-20s | %-12s |".$resource." [".$item{path}."]",$item{mrutime_str},$m,$a,$c,$o,$mft; ::rptMsg($str); if ($item{name} eq "" || $item{name} =~ m/\\$/) { @@ -343,7 +350,7 @@ sub traverse { else { $item{name} = $item{name}."\\"; } - $item{path} = ${$parent}{path}.$item{name}; +# $item{path} = ${$parent}{path}.$item{name}; traverse($key->get_subkey($v),\%item); } } @@ -385,9 +392,9 @@ sub parseVariableEntry { # Ref: http://msdn.microsoft.com/en-us/library/aa965725(v=vs.85).aspx my $stuff = $segs{"{b725f130-47ef-101a-a5f1-02608c9eebac}"}; - my $tag = 1; + my $t = 1; my $cnt = 0x10; - while($tag) { + while($t) { my $sz = unpack("V",substr($stuff,$cnt,4)); my $id = unpack("V",substr($stuff,$cnt + 4,4)); #-------------------------------------------------------------- @@ -397,58 +404,71 @@ sub parseVariableEntry { # 0x0e, 0x0f, 0x10 - mod date, create date, access date(?) # 0x0c - size #-------------------------------------------------------------- - return %item unless (defined $sz); - if ($sz == 0x00) { $tag = 0; + if ($sz == 0x00) { + $t = 0; next; } elsif ($id == 0x0a) { my $num = unpack("V",substr($stuff,$cnt + 13,4)); my $str = substr($stuff,$cnt + 13 + 4,($num * 2)); - $str =~ s/\x00//g; - $item{name} = $str; +# $str =~ s/\00//g; + $item{name} = ::getUnicodeStr($str); } $cnt += $sz; } } - -# if (exists $segs{"{5cbf2787-48cf-4208-b90e-ee5e5d420294}"}) { -# my $stuff = $segs{"{5cbf2787-48cf-4208-b90e-ee5e5d420294}"}; -# my $tag = 1; -# my $cnt = 0x10; -# while($tag) { -# my $sz = unpack("V",substr($stuff,$cnt,4)); -# my $id = unpack("V",substr($stuff,$cnt + 4,4)); -# return %item unless (defined $sz); -# -# if ($sz == 0x00) { -# $tag = 0; -# next; -# } -# elsif ($id == 0x19) { -# -# my $num = unpack("V",substr($stuff,$cnt + 13,4)); -# my $str = substr($stuff,$cnt + 13 + 4,($num * 2)); -# $str =~ s/\x00//g; -# $item{name} = $str; -# } -# $cnt += $sz; -# } -# } + } elsif (substr($data,4,4) eq "AugM") { %item = parseFolderEntry($data); } +# Code for Windows Portable Devices +# Added 20190715 + elsif (parseGUID(substr($data,42,16)) eq "{27e2e392-a111-48e0-ab0c-e17705a05f85}") { + my ($n0, $n1, $n2) = unpack("VVV",substr($data,62,12)); + + my $n0_name = substr($data,0x4A,($n0 * 2)); + $n0_name = ::getUnicodeStr($n0_name); +# $n0_name =~ s/\00//g; + + my $n1_name = substr($data,(0x4A + ($n0 * 2)),($n1 * 2)); + $n1_name = ::getUnicodeStr($n1_name); +# $n1_name =~ s/\00//g; + + if ($n0_name eq "") { + $item{name} = $n1_name; + } + else { + $item{name} = $n0_name; + } + } # Following two entries are for Device Property data elsif ($tag == 0x7b || $tag == 0xbb || $tag == 0xfb) { my ($sz1,$sz2,$sz3) = unpack("VVV",substr($data,0x3e,12)); $item{name} = substr($data,0x4a,$sz1 * 2); - $item{name} =~ s/\x00//g; + $item{name} = ::getUnicodeStr($item{name}); +# $item{name} =~ s/\00//g; } elsif ($tag == 0x02 || $tag == 0x03) { my ($sz1,$sz2,$sz3,$sz4) = unpack("VVVV",substr($data,0x26,16)); $item{name} = substr($data,0x36,$sz1 * 2); - $item{name} =~ s/\x00//g; + $item{name} = ::getUnicodeStr($item{name}); +# $item{name} =~ s/\00//g; + } + elsif (unpack("v",substr($data,6,2)) == 0x05) { + my $o = 0x26; + my $t = 1; + while ($t) { + my $i = substr($data,$o,1); + if ($i =~ m/\00/) { + $t = 0; + } + else { + $item{name} .= $i; + $o++; + } + } } else { $item{name} = "Unknown Type"; @@ -465,7 +485,7 @@ sub parseNetworkEntry { my %item = (); $item{type} = unpack("C",substr($data,2,1)); - my @n = split(/\x00/,substr($data,4,length($data) - 4)); + my @n = split(/\00/,substr($data,4,length($data) - 4)); $item{name} = $n[0]; return %item; } @@ -482,17 +502,16 @@ sub parseZipSubFolderItem { # Get the opened/accessed date/time $item{datetime} = substr($data,0x24,6); - $item{datetime} =~ s/\x00//g; + $item{datetime} =~ s/\00//g; if ($item{datetime} eq "N/A") { } else { $item{datetime} = substr($data,0x24,40); - $item{datetime} =~ s/\x00//g; + $item{datetime} =~ s/\00//g; my ($date,$time) = split(/\s+/,$item{datetime},2); my ($mon,$day,$yr) = split(/\//,$date,3); my ($hr,$min,$sec) = split(/:/,$time,3); - my $gmtime = timegm($sec,$min,$hr,$day,($mon - 1),$yr); $item{datetime} = "$yr-$mon-$day $hr:$min:$sec"; # ::rptMsg("[Access_Time]: ".gmtime($gmtime)); @@ -502,9 +521,11 @@ sub parseZipSubFolderItem { my $sz2 = unpack("V",substr($data,0x58,4)); my $str1 = substr($data,0x5C,$sz *2) if ($sz > 0); - $str1 =~ s/\x00//g; + $str1 = ::getUnicodeStr($str1); +# $str1 =~ s/\00//g; my $str2 = substr($data,0x5C + ($sz * 2),$sz2 *2) if ($sz2 > 0); - $str2 =~ s/\x00//g; + $str2 = ::getUnicodeStr($str2); +# $str2 =~ s/\00//g; if ($sz2 > 0) { $item{name} = $str1."\\".$str2; @@ -517,35 +538,43 @@ sub parseZipSubFolderItem { #----------------------------------------------------------- # parse01ShellItem() -# I honestly have no idea what to do with this data; there's really -# no reference for or description of the format of this data. For -# now, this is just a place holder +# +# Updated 20140728 +# https://5c36fb3a2584d3bd2f8d3924d56b4d00d70e8000.googledrive.com/host/0B3fBvzttpiiSajVqblZQT3FYZzg/Windows%20Shell%20Item%20format.pdf +# http://msdn.microsoft.com/en-us/library/windows/desktop/cc144183(v=vs.85).aspx #----------------------------------------------------------- sub parse01ShellItem { my $data = shift; my %item = (); - $item{type} = unpack("C",substr($data,2,1));; - $item{name} = ""; + + my %cat = (0 => "All Control Panel Items", + 1 => "Appearance/Personalization", + 2 => "Hardware and Sound", + 3 => "Network and Internet", + 4 => "Sound/Audio", + 5 => "System and Security", + 6 => "Clock, Lang, Region", + 7 => "Ease of Access", + 8 => "Programs", + 9 => "User Accounts", + 10 => "Security Center", + 11 => "Mobile PC"); + + $item{size} = unpack("v",substr($data,0,2)); + $item{type} = unpack("C",substr($data,2,1)); + $item{sig} = unpack("V",substr($data,4,4)); + $item{cat} = unpack("V",substr($data,8,4)); + if (exists $cat{$item{cat}}) { + $item{name} = $cat{$item{cat}}; + } + else { + $item{name} = "Unknown Category (".$item{cat}.")"; + } + # ($item{val0},$item{val1}) = unpack("VV",substr($data,2,length($data) - 2)); return %item; } -#----------------------------------------------------------- -# parseXPShellDeviceItem() -# -#----------------------------------------------------------- -sub parseXPShellDeviceItem { - my $data = shift; - my %item = (); - my ($t0,$t1) = unpack("VV",substr($data,0x04,8)); - $item{timestamp} = ::getTime($t0,$t1); -# starting at offset 0x18, read the null-term. string as the name value - my $str = substr($data,0x18,length($data) - 0x18); - $item{name} = (split(/\x00/,$str))[0]; - - return %item; -} - #----------------------------------------------------------- # #----------------------------------------------------------- @@ -559,10 +588,12 @@ sub parseURIEntry { my $sz = unpack("V",substr($data,0x2a,4)); my $uri = substr($data,0x2e,$sz); - $uri =~ s/\x00//g; + $uri = ::getUnicodeStr($uri); +# $uri =~ s/\00//g; my $proto = substr($data,length($data) - 6, 6); - $proto =~ s/\x00//g; + $proto = ::getUnicodeStr($proto); +# $proto =~ s/\00//g; $item{name} = $proto."://".$uri." [".gmtime($item{uritime})."]"; @@ -612,13 +643,13 @@ sub parseGUID { my $d3 = unpack("v",substr($data,6,2)); my $d4 = unpack("H*",substr($data,8,2)); my $d5 = unpack("H*",substr($data,10,6)); - my $guid = sprintf "{%08x-%x-%x-$d4-$d5}",$d1,$d2,$d3; - + my $guid = sprintf "{%08x-%04x-%04x-$d4-$d5}",$d1,$d2,$d3; + if (exists $cp_guids{$guid}) { - return $cp_guids{$guid}; + return "CLSID_".$cp_guids{$guid}; } elsif (exists $folder_types{$guid}) { - return $folder_types{$guid}; + return "CLSID_".$folder_types{$guid}; } else { return $guid; @@ -631,29 +662,55 @@ sub parseGUID { sub parseDeviceEntry { my $data = shift; my %item = (); + + my $ofs = unpack("v",substr($data,4,2)); + my $tag = unpack("V",substr($data,6,4)); -# my $userlen = unpack("V",substr($data,30,4)); -# my $devlen = unpack("V",substr($data,34,4)); -# -# my $user = substr($data,0x28,$userlen * 2); -# $user =~ s/\x00//g; -# -# my $dev = substr($data,0x28 + ($userlen * 2),$devlen * 2); -# $dev =~ s/\x00//g; -# -# $item{name} = $user; - my $len = unpack("v",substr($data,0,2)); - if ($len == 0x14) { - $item{name} = parseGUID(substr($data,4,16)); +#----------------------------------------------------- +# DEBUG +# ::rptMsg("parseDeviceEntry, tag = ".$tag); +#----------------------------------------------------- + if ($tag == 0) { + my $guid1 = parseGUID(substr($data,$ofs + 6,16)); + my $guid2 = parseGUID(substr($data,$ofs + 6 + 16,16)); + $item{name} = $guid1."\\".$guid2 + } + elsif ($tag == 2) { + $item{name} = substr($data,0x0a,($ofs + 6) - 0x0a); + $item{name} = ::getUnicodeStr($item{name}); +# $item{name} =~ s/\00//g; } else { - my $len = unpack("v",substr($data,4,2)); - my $guid1 = parseGUID(substr($data,$len + 6,16)); - my $guid2 = parseGUID(substr($data,$len + 6 + 16,16)); - $item{name} = $guid1."\\".$guid2 + my $ver = unpack("C",substr($data,9,1)); + my $idx = unpack("C",substr($data,3,1)); + if ($idx == 0x80) { + $item{name} = parseGUID(substr($data,4,16)); + } +# Version 3 = XP + elsif ($ver == 3) { + my $guid1 = parseGUID(substr($data,$ofs + 6,16)); + my $guid2 = parseGUID(substr($data,$ofs + 6 + 16,16)); + $item{name} = $guid1."\\".$guid2 + + } +# Version 8 = Win7 + elsif ($ver == 8) { + my $userlen = unpack("V",substr($data,30,4)); + my $devlen = unpack("V",substr($data,34,4)); + my $user = substr($data,0x28,$userlen * 2); + $user = ::getUnicodeStr($user); +# $user =~ s/\00//g; + my $dev = substr($data,0x28 + ($userlen * 2),$devlen * 2); + $dev = ::getUnicodeStr($dev); +# $dev =~ s/\00//g; + $item{name} = $user; + } +# Version unknown + else { + $item{name} = "Device Entry - Unknown Version"; + } } - return %item; } @@ -689,7 +746,7 @@ sub parseControlPanelEntry { # #----------------------------------------------------------- sub parseFolderEntry { - my $data = shift; + my $data = shift; my %item = (); $item{type} = unpack("C",substr($data,2,1)); @@ -718,71 +775,106 @@ sub parseFolderEntry { my @m = unpack("vv",substr($data,$ofs_mdate,4)); ($item{mtime_str},$item{mtime}) = convertDOSDate($m[0],$m[1]); +# DEBUG ------------------------------------------------ +# Added 20160706 based on sample data provided by J. Poling + + if (length($data) < 0x30) { +# start at offset 0xE, read in nul-term ASCII string (until "\00" is reached) + $ofs_shortname = 0xE; + my $tag = 1; + my $cnt = 0; + my $str = ""; + while($tag) { + my $s = substr($data,$ofs_shortname + $cnt,1); + if ($s =~ m/\00/) { + $tag = 0; + } + else { + $str .= $s; + $cnt++; + } + } + $item{name} = $str; + } + else { # Need to read in short name; nul-term ASCII -# $item{shortname} = (split(/\x00/,substr($data,12,length($data) - 12),2))[0]; - $ofs_shortname = $ofs_mdate + 6; - my $tag = 1; - my $cnt = 0; - my $str = ""; - while($tag) { - my $s = substr($data,$ofs_shortname + $cnt,1); - return %item unless (defined $s); - if ($s =~ m/\x00/ && ((($cnt + 1) % 2) == 0)) { - $tag = 0; - } - else { - $str .= $s; - $cnt++; +# $item{shortname} = (split(/\00/,substr($data,12,length($data) - 12),2))[0]; + $ofs_shortname = $ofs_mdate + 6; + my $tag = 1; + my $cnt = 0; + my $str = ""; + while($tag) { + my $s = substr($data,$ofs_shortname + $cnt,1); + if ($s =~ m/\00/ && ((($cnt + 1) % 2) == 0)) { + $tag = 0; + } + else { + $str .= $s; + $cnt++; + } } - } -# $str =~ s/\x00//g; - my $shortname = $str; - my $ofs = $ofs_shortname + $cnt + 1; +# $str =~ s/\00//g; + my $shortname = $str; + my $ofs = $ofs_shortname + $cnt + 1; # Read progressively, 1 byte at a time, looking for 0xbeef - $tag = 1; - $cnt = 0; - while ($tag) { - my $s = substr($data,$ofs + $cnt,2); - return %item unless (defined $s); - if (unpack("v",$s) == 0xbeef) { - $tag = 0; + my $tag = 1; + my $cnt = 0; + while ($tag) { + if (unpack("v",substr($data,$ofs + $cnt,2)) == 0xbeef) { + $tag = 0; + } + else { + $cnt++; + } } - else { - $cnt++; + $item{extver} = unpack("v",substr($data,$ofs + $cnt - 4,2)); +# printf "Version: 0x%x\n",$item{extver}; + $ofs = $ofs + $cnt + 2; + + my @m = unpack("vv",substr($data,$ofs,4)); + ($item{ctime_str},$item{ctime}) = convertDOSDate($m[0],$m[1]); + $ofs += 4; + my @m = unpack("vv",substr($data,$ofs,4)); + ($item{atime_str},$item{atime}) = convertDOSDate($m[0],$m[1]); + + my $jmp; + if ($item{extver} == 0x03) { + $jmp = 8; } - } - $item{extver} = unpack("v",substr($data,$ofs + $cnt - 4,2)); - $ofs = $ofs + $cnt + 2; + elsif ($item{extver} == 0x07) { + $jmp = 26; + } + elsif ($item{extver} == 0x08) { + $jmp = 30; + } + elsif ($item{extver} == 0x09) { + $jmp = 34; + } + else {} - @m = unpack("vv",substr($data,$ofs,4)); - ($item{ctime_str},$item{ctime}) = convertDOSDate($m[0],$m[1]); - $ofs += 4; - @m = unpack("vv",substr($data,$ofs,4)); - ($item{atime_str},$item{atime}) = convertDOSDate($m[0],$m[1]); + if ($item{type} == 0x31 && $item{extver} >= 0x07) { + my @n = unpack("Vvv",substr($data,$ofs + 8, 8)); + if ($n[2] != 0) { + $item{mft_rec_num} = getNum48($n[0],$n[1]); + $item{mft_seq_num} = $n[2]; +# ::rptMsg("MFT: ".$item{mft_rec_num}."/".$item{mft_seq_num}); +# probe($data); + } + } - my $jmp; - if ($item{extver} == 0x03) { - $jmp = 8; - } - elsif ($item{extver} == 0x07) { - $jmp = 26; - } - elsif ($item{extver} == 0x08) { - $jmp = 30; - } - else {} + $ofs += $jmp; - $ofs += $jmp; + my $str = substr($data,$ofs,length($data) - 30); + my $longname = (split(/\00\00/,$str,2))[0]; +# $longname = ::getUnicodeStr($longname); + $longname =~ s/\00//g; - $str = substr($data,$ofs,length($data) - 30); - my $longname = (split(/\x00\x00/,$str,2))[0]; - $longname = $longname.chr 0x00; - - if ($longname ne "") { - $item{name} = Utf16ToUtf8($longname); - } - else { - $item{name} = Utf16ToUtf8($shortname); + if ($longname ne "") { + $item{name} = $longname; + } + else { + $item{name} = $shortname; + } } return %item; } @@ -833,9 +925,7 @@ sub parseFolderEntry2 { my $tag = 1; while ($tag) { - my $s = substr($data,$ofs,2); - return %item unless (defined $s); - if (unpack("v",$s) == 0xbeef) { + if (unpack("v",substr($data,$ofs,2)) == 0xbeef) { $tag = 0; } else { @@ -850,6 +940,9 @@ sub parseFolderEntry2 { if ($item{extver} == 0x03) { $jmp = 8; } + elsif ($item{extver} == 0x04) { + $jmp = 34; + } elsif ($item{extver} == 0x07) { $jmp = 26; } @@ -862,16 +955,17 @@ sub parseFolderEntry2 { my $str = substr($data,$ofs,length($data) - 30); - ::rptMsg(" --- parseFolderEntry2 --- "); - my @d = printData($str); - foreach (0..(scalar(@d) - 1)) { - ::rptMsg($d[$_]); - } - ::rptMsg(""); +# ::rptMsg(" --- parseFolderEntry2 --- "); +# my @d = printData($str); +# foreach (0..(scalar(@d) - 1)) { +# ::rptMsg($d[$_]); +# } +# ::rptMsg(""); - $item{name} = (split(/\x00\x00/,$str,2))[0]; - $item{name} =~ s/\x13\x20/\x2D\x00/; - $item{name} = Utf16ToUtf8($item{name}); + $item{name} = (split(/\00\00/,$str,2))[0]; + $item{name} =~ s/\13\20/\2D\00/; + $item{name} = ::getUnicodeStr($item{name}); +# $item{name} =~ s/\00//g; return %item; } @@ -882,63 +976,155 @@ sub parseNetworkEntry { my $data = shift; my %item = (); $item{type} = unpack("C",substr($data,2,1)); - my @names = split(/\x00/,substr($data,5,length($data) - 5)); + my @names = split(/\00/,substr($data,5,length($data) - 5)); $item{name} = $names[0]; return %item; } + +#----------------------------------------------------------- +# +#----------------------------------------------------------- +sub parseDatePathItem { + my $data = shift; + my %item = (); + $item{datestr} = substr($data,0x18,30); + my ($file,$dir) = split(/\00\00/,substr($data,0x44,length($data) - 0x44)); + $file =~ s/\00//g; + $dir =~ s/\00//g; + $item{name} = $dir.$file; + return %item; +} + +#----------------------------------------------------------- +# parseTypex53() +#----------------------------------------------------------- +sub parseTypex53 { + my $data = shift; + my %item = (); + + my $item1 = parseGUID(substr($data,0x14,16)); + my $item2 = parseGUID(substr($data,0x24,16)); + + $item{name} = $item1."\\".$item2; + + return %item; +} + +#----------------------------------------------------------- +# +#----------------------------------------------------------- +sub shellItem0x52 { + my $data = shift; + my %item = (); + my ($d, $ofs,$sz); + + $item{type} = unpack("C",substr($data,0x02,1)); + $item{subtype} = unpack("v",substr($data,0x06,2)); +# First, start at offset 0x32, read 2 bytes at a time until the null +# terminator is reached. + my $tag = 1; + my $cnt = 0; + + while ($tag) { + $d = substr($data,0x32 + $cnt,2); + if (unpack("v",$d) == 0) { + $tag = 0; + } + else { + $item{name} .= $d; + $cnt += 2; + } + } + $item{name} =~ s/\00//g; + + if ($item{subtype} < 3) { + $ofs = 0x32 + $cnt + 2; + } + else { + $ofs = 0x32 + $cnt + 8; + } + $sz = unpack("V",substr($data,$ofs,4)); + $item{str} = substr($data,$ofs + 4,$sz * 2); + $item{str} = ::getUnicodeStr($item{str}); +# $item{str} =~ s/\00//g; + return %item; +} + +#----------------------------------------------------------- +# probe() +# +# Code the uses printData() to insert a 'probe' into a specific +# location and display the data +# +# Input: binary data of arbitrary length +# Output: Nothing, no return value. Displays data to the console +#----------------------------------------------------------- +sub probe { + my $data = shift; + my @d = printData($data); + ::rptMsg(""); + foreach (0..(scalar(@d) - 1)) { + ::rptMsg($d[$_]); + } + ::rptMsg(""); +} + #----------------------------------------------------------- # printData() # subroutine used primarily for debugging; takes an arbitrary # length of binary data, prints it out in hex editor-style # format for easy debugging +# +# Usage: see probe() #----------------------------------------------------------- sub printData { my $data = shift; my $len = length($data); - my $tag = 1; + my @display = (); my $loop = $len/16; $loop++ if ($len%16); foreach my $cnt (0..($loop - 1)) { -# while ($tag) { +# How much is left? my $left = $len - ($cnt * 16); my $n; ($left < 16) ? ($n = $left) : ($n = 16); my $seg = substr($data,$cnt * 16,$n); - my @str1 = split(//,unpack("H*",$seg)); - - my @s3; - my $str = ""; - - foreach my $i (0..($n - 1)) { - $s3[$i] = $str1[$i * 2].$str1[($i * 2) + 1]; - - if (hex($s3[$i]) > 0x1f && hex($s3[$i]) < 0x7f) { - $str .= chr(hex($s3[$i])); - } - else { - $str .= "\."; - } + my $lhs = ""; + my $rhs = ""; + foreach my $i ($seg =~ m/./gs) { +# This loop is to process each character at a time. + $lhs .= sprintf(" %02X",ord($i)); + if ($i =~ m/[ -~]/) { + $rhs .= $i; + } + else { + $rhs .= "."; + } } - my $h = join(' ',@s3); -# ::rptMsg(sprintf "0x%08x: %-47s ".$str,($cnt * 16),$h); - $display[$cnt] = sprintf "0x%08x: %-47s ".$str,($cnt * 16),$h; + $display[$cnt] = sprintf("0x%08X %-50s %s",$cnt,$lhs,$rhs); } return @display; } -#--------------------------------------------------------------------- -# Utf16ToUtf8() -#--------------------------------------------------------------------- -sub Utf16ToUtf8 { - my $str = $_[0]; - Encode::from_to($str,'UTF-16LE','utf8'); - my $str2 = Encode::decode_utf8($str); - return $str; +#----------------------------------------------------------- +# getNum48() +# borrowed from David Cowen's code +#----------------------------------------------------------- +sub getNum48 { + my $n1 = shift; + my $n2 = shift; + if ($n2 == 0) { + return $n1; + } + else { + $n2 = ($n2 *16777216); + return $n1 + $n2; + } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shellbags_test.pl b/thirdparty/rr-full/plugins/shellbags_xp_old.pl similarity index 97% rename from thirdparty/rr-full/plugins/shellbags_test.pl rename to thirdparty/rr-full/plugins/shellbags_xp_old.pl index 3b068ea3acb..af848352029 100644 --- a/thirdparty/rr-full/plugins/shellbags_test.pl +++ b/thirdparty/rr-full/plugins/shellbags_xp_old.pl @@ -1,5 +1,5 @@ #----------------------------------------------------------- -# shellbags_test.pl +# shellbags_xp.pl # # # @@ -47,14 +47,10 @@ sub pluginmain { my $reg = Parse::Win32Registry->new($hive); $root_key = $reg->get_root_key; - my %paths = ("Win7" => "Local Settings\\Software\\Microsoft\\Windows\\Shell\\BagMRU", - "XP" => "Software\\Microsoft\\Windows\\ShellNoRoam\\BagMRU"); + my %paths = ("XP" => "Software\\Microsoft\\Windows\\ShellNoRoam\\BagMRU"); my $key; - if ($key = $root_key->get_subkey($paths{"Win7"})) { - setup($key); - } - elsif ($key = $root_key->get_subkey($paths{"XP"})) { + if ($key = $root_key->get_subkey($paths{"XP"})) { $XP = 1; setup($key); } @@ -420,4 +416,4 @@ sub parseFolderItem { } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shellexec.pl b/thirdparty/rr-full/plugins/shellexec.pl deleted file mode 100644 index c6ba0e56586..00000000000 --- a/thirdparty/rr-full/plugins/shellexec.pl +++ /dev/null @@ -1,125 +0,0 @@ -#----------------------------------------------------------- -# shellexec -# Get ShellExecuteHooks values from Software hive (based on BHO -# code) -# -# ShellExecuteHooks are DLLs that load as part of the Explorer.exe process, -# and can intercept commands. There are some legitimate applications that -# run as ShellExecuteHooks, but many times, malware (spy-, ad-ware) will -# install here. ShellExecuteHooks allow you to type a URL into the Start->Run -# box and have that URL opened in your browser. For example, in 2001, Michael -# Dunn wrote KBLaunch, a ShellExecuteHook that looked for "?q" in the Run box -# and would open the appropriate MS KB article. -# -# Refs: -# http://support.microsoft.com/kb/914922 -# http://support.microsoft.com/kb/170918 -# http://support.microsoft.com/kb/943460 -# -# History: -# 20130410 - added Wow6432Node support -# 20081229 - initial creation -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package shellexec; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20130410); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets ShellExecuteHooks from Software hive"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my %bhos; - ::logMsg("Launching shellexec v.".$VERSION); - ::rptMsg("shellexec v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my @paths = ("Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellExecuteHooks", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellExecuteHooks"); - - foreach my $key_path (@paths) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar (@vals) > 0) { - foreach my $s (@vals) { - my $name = $s->get_name(); - next if ($name =~ m/^-/ || $name eq ""); - my $clsid_path = "Classes\\CLSID\\".$name; - my $clsid; - if ($clsid = $root_key->get_subkey($clsid_path)) { - my $class; - my $mod; - my $lastwrite; - - eval { - $class = $clsid->get_value("")->get_data(); - $bhos{$name}{class} = $class; - }; - if ($@) { - ::logMsg(" Error getting Class name for CLSID\\".$name); - ::logMsg(" ".$@); - } - eval { - $mod = $clsid->get_subkey("InProcServer32")->get_value("")->get_data(); - $bhos{$name}{module} = $mod; - }; - if ($@) { - ::logMsg(" Error getting Module name for CLSID\\".$name); - ::logMsg(" ".$@); - } - eval{ - $lastwrite = $clsid->get_subkey("InProcServer32")->get_timestamp(); - $bhos{$name}{lastwrite} = $lastwrite; - }; - if ($@) { - ::logMsg(" Error getting LastWrite time for CLSID\\".$name); - ::logMsg(" ".$@); - } - - foreach my $b (keys %bhos) { - ::rptMsg($b); - ::rptMsg(" Class => ".$bhos{$b}{class}); - ::rptMsg(" Module => ".$bhos{$b}{module}); - ::rptMsg(" LastWrite => ".gmtime($bhos{$b}{lastwrite})); - ::rptMsg(""); - } - } - else { - ::rptMsg($clsid_path." not found."); - ::rptMsg(""); - } - } - } - else { - ::rptMsg($key_path." has no values. No ShellExecuteHooks installed."); - } - } - else { - ::rptMsg($key_path." not found."); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shellext.pl b/thirdparty/rr-full/plugins/shellext.pl deleted file mode 100644 index 7a6b4c456fe..00000000000 --- a/thirdparty/rr-full/plugins/shellext.pl +++ /dev/null @@ -1,98 +0,0 @@ -#----------------------------------------------------------- -# shellext -# Plugin to get approved shell extensions list from the -# Software hive -# -# This plugin retrieves the list of approved shell extensions from -# the Software hive; specifically, the "Shell Extensions\Approved" -# key. Once it has the names (GUID) and data (string) of each value, -# it then goes to the Classes\CLSID\{GUID} key to get the name of/path to -# the associated DLL, if available. It also gets the LastWrite time of the -# Classes\CLSID\{GUID} key. -# -# Analysis of an incident showed that the intruder placed their malware in -# the C:\Windows dir, using the same name as a known valid shell extension. -# When Explorer.exe launches, it reads the list of approved shell extensions, -# then goes to the Classes\CLSID key to get the path to the associated DLL. The -# intruder chose a shell extension that did not have an explicit path, so when -# explorer.exe looked for it, it started in the C:\Windows dir, and never got to -# the legit DLL in the C:\Windows\system32 dir. -# -# References: -# http://msdn.microsoft.com/en-us/library/ms682586%28VS.85%29.aspx -# -# -# Note: This plugin can take several minutes to run -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package shellext; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100515); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets Shell Extensions from Software hive"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my %bhos; - ::logMsg("Launching shellext v.".$VERSION); - ::rptMsg("shellext v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved";; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my %exts; - - my @vals = $key->get_list_of_values(); - if (scalar (@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - $exts{$name}{name} = $v->get_data(); - - my $clsid_path = "Classes\\CLSID\\".$name; - my $clsid; - if ($clsid = $root_key->get_subkey($clsid_path)) { - eval { - $exts{$v->get_name()}{lastwrite} = $clsid->get_timestamp(); - $exts{$v->get_name()}{dll} = $clsid->get_subkey("InProcServer32")->get_value("")->get_data(); - }; - } - } - foreach my $e (keys %exts) { - ::rptMsg($e." ".$exts{$e}{name}); - ::rptMsg(" DLL: ".$exts{$e}{dll}); - ::rptMsg(" Timestamp: ".gmtime($exts{$e}{lastwrite})." Z"); - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shellfolders.pl b/thirdparty/rr-full/plugins/shellfolders.pl index 4ec1889d4c4..98926ae5dc6 100644 --- a/thirdparty/rr-full/plugins/shellfolders.pl +++ b/thirdparty/rr-full/plugins/shellfolders.pl @@ -1,34 +1,38 @@ #----------------------------------------------------------- # shellfolders.pl +# A threat actor can maintain persistence by modifying the StartUp folder location, +# and using that new location for persistence # -# Retrieve the Shell Folders values from user's hive; while -# this may not be important in every instance, it may give the -# examiner indications as to where to look for certain items; -# for example, if the user's "My Documents" folder has been redirected -# as part of configuration changes (corporate policies, etc.). Also, -# this may be important as part of data leakage exams, as XP and Vista -# allow users to drop and drag files to the CD Burner. +# Change history +# 20201005 - MITRE update +# 20200515 - updated date output format +# 20190902 - removed alert() function +# 20131028 - updated to include User Shell Folders entry +# 20131025 - created # -# References: -# http://support.microsoft.com/kb/279157 -# http://support.microsoft.com/kb/326982 +# References +# http://www.fireeye.com/blog/technical/malware-research/2013/10/evasive-tactics-terminator-rat.html +# http://www.symantec.com/connect/articles/most-common-registry-key-check-while-dealing-virus-issue +# https://attack.mitre.org/techniques/T1547/001/ # -# copyright 2009 H. Carvey, keydet89@yahoo.com +# copyright 2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package shellfolders; use strict; my %config = (hive => "NTUSER\.DAT", - osmask => 22, hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20090115); + MITRE => "T1547\.001", + category => "persistence", + output => "report", + version => 20201005); sub getConfig{return %config} - sub getShortDescr { - return "Retrieve user Shell Folders values"; + return "Gets user's shell folders values"; } sub getDescr{} sub getRefs {} @@ -39,35 +43,49 @@ sub getShortDescr { sub pluginmain { my $class = shift; - my $hive = shift; + my $ntuser = shift; ::logMsg("Launching shellfolders v.".$VERSION); - ::rptMsg("shellfolders v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); + ::rptMsg("shellfolders v.".$VERSION); + ::rptMsg(getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'; my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); - my @vals = $key->get_list_of_values(); + eval { + my $start = $key->get_value("Startup")->get_data(); + ::rptMsg("StartUp folder : ".$start); + ::rptMsg(""); + ::rptMsg("Analysis Tip: A threat actor could modify the location of the user's StartUp folder."); + }; + } + else { + ::rptMsg($key_path." not found."); + } + +# added 20131028 + ::rptMsg(""); + $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders'; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $str = sprintf "%-20s %-40s",$v->get_name(),$v->get_data(); - ::rptMsg($str); - } + eval { + my $start = $key->get_value("Startup")->get_data(); + ::rptMsg("StartUp folder : ".$start); ::rptMsg(""); - } - else { - ::rptMsg($key_path." has no values."); - } + ::rptMsg("Analysis Tip: A threat actor could modify the location of the user's StartUp folder."); + }; } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } + 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shelloverlay.pl b/thirdparty/rr-full/plugins/shelloverlay.pl index 91088d63cdd..67a4ffd8b31 100644 --- a/thirdparty/rr-full/plugins/shelloverlay.pl +++ b/thirdparty/rr-full/plugins/shelloverlay.pl @@ -4,12 +4,13 @@ # based on LastWrite times of subkeys # # History +# 20201007 - MITRE update # 20100308 - created # # References # http://msdn.microsoft.com/en-us/library/cc144123%28VS.85%29.aspx -# Coreflood - http://vil.nai.com/vil/content/v_102053.htm -# http://www.secureworks.com/research/threats/coreflood/?threat=coreflood +# https://attack.mitre.org/techniques/T1546/015/ +# https://www.welivesecurity.com/wp-content/uploads/2016/10/eset-sednit-part-2.pdf, pg 69 # # Analysis Tip: Malware such as Coreflood uses a random subkey name and a # random CLSID GUID value @@ -24,8 +25,10 @@ package shelloverlay; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20100308); + MITRE => "T1546\.015", + category => "persistence", + output => "report", + version => 20201007); sub getConfig{return %config} sub getShortDescr { @@ -41,8 +44,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching shelloverlay v.".$VERSION); - ::rptMsg("shelloverlay v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("shelloverlay v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -53,7 +58,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("shelloverlay"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @subkeys = $key->get_list_of_subkeys(); @@ -69,11 +74,14 @@ sub pluginmain { } foreach my $t (reverse sort {$a <=> $b} keys %id) { - ::rptMsg(gmtime($t)." Z"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $item (@{$id{$t}}) { ::rptMsg(" ".$item); } ::rptMsg(""); + ::rptMsg("Analysis Tip: ShellIconOverlays can be used for persistence."); + ::rptMsg("See pg 69 of https://www.welivesecurity.com/wp-content/uploads/2016/10/eset-sednit-part-2.pdf"); +# ::rptMsg(""); } } diff --git a/thirdparty/rr-full/plugins/shimcache.pl b/thirdparty/rr-full/plugins/shimcache.pl index 6116e6391a6..34a997ea70d 100644 --- a/thirdparty/rr-full/plugins/shimcache.pl +++ b/thirdparty/rr-full/plugins/shimcache.pl @@ -5,6 +5,9 @@ # works within an analysis process. # # History: +# 20220920 - updated Win8.1 parsing +# 20201005 - MITRE update +# 20200428 - updated output date format # 20190112 - updated parsing for Win8.1 # 20180311 - updated for more recent version of Win10/Win2016 # 20160528 - updated @@ -20,21 +23,20 @@ # This plugin is based solely on the work and examples provided by Mandiant; # thanks to them for sharing this information, and making the plugin possible. # -# copyright 2016 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package shimcache; use strict; my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "Program Execution", + category => "file existence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, - version => 20190112); + MITRE => "", + output => "report", + version => 20220920); sub getConfig{return %config} sub getShortDescr { @@ -81,13 +83,13 @@ sub pluginmain { eval { $app_data = $appcompat->get_subkey("AppCompatibility")->get_value("AppCompatCache")->get_data(); ::rptMsg($appcompat_path."\\AppCompatibility"); - ::rptMsg("LastWrite Time: ".gmtime($appcompat->get_subkey("AppCompatibility")->get_timestamp())." Z"); + ::rptMsg("LastWrite Time: ".::format8601Date($appcompat->get_subkey("AppCompatibility")->get_timestamp())."Z"); }; eval { $app_data = $appcompat->get_subkey("AppCompatCache")->get_value("AppCompatCache")->get_data(); ::rptMsg($appcompat_path."\\AppCompatCache"); - ::rptMsg("LastWrite Time: ".gmtime($appcompat->get_subkey("AppCompatCache")->get_timestamp())." Z"); + ::rptMsg("LastWrite Time: ".::format8601Date($appcompat->get_subkey("AppCompatCache")->get_timestamp())."Z"); }; my $sig = unpack("V",substr($app_data,0,4)); @@ -130,11 +132,11 @@ sub pluginmain { $modtime = ""; } else { - $modtime = gmtime($modtime)." Z"; + $modtime = ::format8601Date($modtime); } $str = $files{$f}{filename}." ".$modtime; - $str .= " ".gmtime($files{$f}{updtime})." Z" if (exists $files{$f}{updtime}); + $str .= " ".::format8601Date($files{$f}{updtime}) if (exists $files{$f}{updtime}); $str .= " ".$files{$f}{size}." bytes" if (exists $files{$f}{size}); $str .= " Executed" if (exists $files{$f}{executed}); ::rptMsg($str); @@ -288,7 +290,6 @@ sub appWin8 { while($ofs < $len) { my $tag = unpack("V",substr($data,$ofs,4)); - last unless (defined $tag); # 32-bit if ($tag == 0x73746f72) { $jmp = unpack("V",substr($data,$ofs + 8,4)); @@ -334,8 +335,7 @@ sub appWin81 { while ($ofs < $len) { $tag = substr($data,$ofs,4); - last unless (defined $tag); - if ($tag eq "10ts") { + if ($tag eq "10ts" || $tag eq "00ts") { $sz = unpack("V",substr($data,$ofs + 0x08,4)); $name_len = unpack("v",substr($data,$ofs + 0x0c,2)); diff --git a/thirdparty/rr-full/plugins/shimcache_tln.pl b/thirdparty/rr-full/plugins/shimcache_tln.pl index cada7474b75..f8b24720241 100644 --- a/thirdparty/rr-full/plugins/shimcache_tln.pl +++ b/thirdparty/rr-full/plugins/shimcache_tln.pl @@ -5,6 +5,8 @@ # works within an analysis process. # # History: +# 20220920 - updated Win8.1 parsing +# 20201005 - MITRE update # 20190112 - updated parsing for Win8.1 # 20180311 - updated for more recent version of Win10/Win2016 # 20160528 - created @@ -26,14 +28,13 @@ package shimcache_tln; use strict; my %config = (hive => "System", - hivemask => 4, - output => "tln", - category => "Program Execution", + category => "file existence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, - version => 20190112); + MITRE => "", + output => "tln", + version => 20220920); sub getConfig{return %config} sub getShortDescr { @@ -284,7 +285,6 @@ sub appWin8 { while($ofs < $len) { my $tag = unpack("V",substr($data,$ofs,4)); - last unless (defined $tag); # 32-bit if ($tag == 0x73746f72) { $jmp = unpack("V",substr($data,$ofs + 8,4)); @@ -330,8 +330,7 @@ sub appWin81 { while ($ofs < $len) { $tag = substr($data,$ofs,4); - last unless (defined $tag); - if ($tag eq "10ts") { + if ($tag eq "10ts" || $tag eq "00ts") { $sz = unpack("V",substr($data,$ofs + 0x08,4)); $name_len = unpack("v",substr($data,$ofs + 0x0c,2)); diff --git a/thirdparty/rr-full/plugins/shutdown.pl b/thirdparty/rr-full/plugins/shutdown.pl index f2ff4634eae..71c0030d38c 100644 --- a/thirdparty/rr-full/plugins/shutdown.pl +++ b/thirdparty/rr-full/plugins/shutdown.pl @@ -4,12 +4,15 @@ # contents of the ShutdownTime value # # Change history -# +# 20201005 - MITRE update +# 20200518 - updated date output format +# 20080324 - created # # References # # -# copyright 2008 H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package shutdown; use strict; @@ -18,8 +21,10 @@ package shutdown; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080324); + MITRE => "", + category => "config", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -36,8 +41,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching shutdown v.".$VERSION); - ::rptMsg("shutdown v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("shutdown v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); +# ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -53,13 +60,12 @@ sub pluginmain { my $win; if ($win = $root_key->get_subkey($win_path)) { ::rptMsg($win_path." key, ShutdownTime value"); - ::rptMsg($win_path); - ::rptMsg("LastWrite Time ".gmtime($win->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite time: ".::format8601Date($win->get_timestamp())."Z"); my $sd; if ($sd = $win->get_value("ShutdownTime")->get_data()) { my @vals = unpack("VV",$sd); my $shutdown = ::getTime($vals[0],$vals[1]); - ::rptMsg(" ShutdownTime = ".gmtime($shutdown)." (UTC)"); + ::rptMsg("ShutdownTime : ".::format8601Date($shutdown)."Z"); } else { diff --git a/thirdparty/rr-full/plugins/shutdowncount.pl b/thirdparty/rr-full/plugins/shutdowncount.pl deleted file mode 100644 index fed7a565383..00000000000 --- a/thirdparty/rr-full/plugins/shutdowncount.pl +++ /dev/null @@ -1,82 +0,0 @@ -#----------------------------------------------------------- -# shutdowncount.pl -# -# *Value info first seen at: -# http://forensicsfromthesausagefactory.blogspot.com/2008/06/install-dates-and-shutdown-times-found.html -# thanks to DC1743@gmail.com -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package shutdowncount; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20080709); - -sub getConfig{return %config} - -sub getShortDescr { - return "Retrieves ShutDownCount value"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching shutdowncount v.".$VERSION); - ::rptMsg("shutdowncount v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# Code for System file, getting CurrentControlSet - my $current; - my $ccs; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - else { - ::logMsg("Could not find ".$key_path); - return - } - - $key_path = $ccs."\\Control\\Watchdog\\Display"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("ShutdownCount"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $count = 0; - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - if ($v->get_name() eq "ShutdownCount") { - $count = 1; - ::rptMsg("ShutdownCount = ".$v->get_data()); - } - } - ::rptMsg("ShutdownCount value not found.") if ($count == 0); - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/silentprocessexit.pl b/thirdparty/rr-full/plugins/silentprocessexit.pl index 61f3e7754c0..dbbf22c41d8 100644 --- a/thirdparty/rr-full/plugins/silentprocessexit.pl +++ b/thirdparty/rr-full/plugins/silentprocessexit.pl @@ -2,24 +2,30 @@ # silentprocessexit # # Change history: +# 20220501 - updated based on "malmoeb" tweet +# 20201005 - MITRE update +# 20200517 - updated date output format # 20180601 - created # # Ref: # https://oddvar.moe/2018/04/10/persistence-using-globalflags-in-image-file-execution-options-hidden-from-autoruns-exe/ +# https://twitter.com/malmoeb/status/1520458148749971458 +# https://attack.mitre.org/techniques/T1546/ # -# copyright 2018 QAR,LLC +# copyright 2022 QAR,LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package silentprocessexit; use strict; my %config = (hive => "Software", - category => "autostart", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180601); + MITRE => "T1546", + output => "report", + version => 20220501); sub getConfig{return %config} sub getShortDescr { @@ -36,8 +42,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::rptMsg("Launching silentProcessexit v.".$VERSION); - ::rptMsg("silentprocessexit v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("silentprocessexit v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $key_path = ('Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit'); my $reg = Parse::Win32Registry->new($hive); @@ -46,13 +54,25 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { my @sk = $key->get_list_of_subkeys(); - foreach my $s (@sk) { - ::rptMsg($s->get_name()); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())." UTC"); - eval { - ::rptMsg("MonitorProcess: ".$s->get_value("MonitorProcess")->get_data()); - }; + if (scalar @sk > 0) { + foreach my $s (@sk) { + ::rptMsg($s->get_name()); + ::rptMsg("LastWrite: ".::format8601Date($s->get_timestamp())."Z"); + eval { + ::rptMsg("MonitorProcess: ".$s->get_value("MonitorProcess")->get_data()); + }; + + eval { + ::rptMsg("ReportingMode : ".$s->get_value("ReportingMode")->get_data()); + }; + + ::rptMsg(""); + } + ::rptMsg("Analysis Tip: Application names listed indicate that when that process exits, another process may be launched."); + ::rptMsg("Review the below reference for other applicable settings. Also check \"Image File Execution Options\" key for a"); + ::rptMsg("GlobalFlag value that includes 0x200"); ::rptMsg(""); + ::rptMsg("Ref: https://oddvar.moe/2018/04/10/persistence-using-globalflags-in-image-file-execution-options-hidden-from-autoruns-exe/"); } } } diff --git a/thirdparty/rr-full/plugins/silentprocessexit_tln.pl b/thirdparty/rr-full/plugins/silentprocessexit_tln.pl index c6ae90f5791..82fbd0e60e9 100644 --- a/thirdparty/rr-full/plugins/silentprocessexit_tln.pl +++ b/thirdparty/rr-full/plugins/silentprocessexit_tln.pl @@ -2,6 +2,7 @@ # silentprocessexit_tln # # Change history: +# 20201005 - MITRE update # 20180601 - created # # Ref: @@ -14,12 +15,13 @@ package silentprocessexit_tln; use strict; my %config = (hive => "Software", - category => "autostart", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180601); + MITRE => "T1546", + output => "tln", + version => 20201005); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/sizes.pl b/thirdparty/rr-full/plugins/sizes.pl index 7b19a641643..25d6c557d61 100644 --- a/thirdparty/rr-full/plugins/sizes.pl +++ b/thirdparty/rr-full/plugins/sizes.pl @@ -6,29 +6,39 @@ # sizes; change $min_size value to suit your needs # # Change history +# 20230811 - added Regex to look for base64-encoded data +# 20201118 - updated to look for keys with a large num. of values +# - OSINT indicates that Cobalt Strike is written to a random key with 760 Reg_Sz values +# 20201012 - MITRE update +# 20200517 - minor updates # 20180817 - updated to include brief output, based on suggestion from J. Wood # 20180607 - modified based on Meterpreter input from Mari DeGrazia # 20150527 - Created +# +# https://attack.mitre.org/techniques/T1112/ # -# copyright 2015 QAR, LLC -# Author: H. Carvey +# copyright 2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package sizes; use strict; my $min_size = 5000; -my $output_size = 48; +my $min_vals = 100; +my $output_size = 64; -my %config = (hive => "All", +my %config = (hive => "all", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180817); + output => "report", + MITRE => "T1112", + category => "defense evasion", + version => 20230811); sub getConfig{return %config} sub getShortDescr { - return "Scans a hive file looking for binary value data of a min size (".$min_size.")"; + return "Scans hive for value data greater than ".$min_size." bytes, and keys with more than ".$min_vals." values"; } sub getDescr{} sub getRefs {} @@ -45,7 +55,7 @@ sub pluginmain { my $root_key = $reg->get_root_key; ::logMsg("Launching sizes v.".$VERSION); ::rptMsg("sizes v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $start = time; @@ -55,38 +65,62 @@ sub pluginmain { ::rptMsg("Scan completed: ".($finish - $start)." sec"); ::rptMsg("Total values : ".$count); + + ::rptMsg(""); + ::rptMsg("Analysis Tip: Threat actors my hide \"fileless\" commands in Registry values. This plugin sweeps through the Registry"); + ::rptMsg("to look for values with data greater than ".$min_size." bytes in size. It also looks for keys with more than ".$min_vals); + ::rptMsg("values; threat actors have been observed placing Cobalt Strike EXEs in up to 750 Registry values."); + ::rptMsg(""); + ::rptMsg("As of 20230811, a regex to look for base64-encoding in string value data was added."); } sub traverse { my $key = shift; # my $ts = $key->get_timestamp(); - foreach my $val ($key->get_list_of_values()) { - $count++; - my $type = $val->get_type(); - if ($type == 0 || $type == 3 || $type == 1 || $type == 2) { - my $data = $val->get_data(); - my $len = length($data); - if ($len > $min_size) { + my @vals = (); + if (@vals = $key->get_list_of_values()) { + + if (scalar @vals > $min_vals) { + my @name = split(/\\/,$key->get_path()); + $name[0] = ""; + $name[0] = "\\" if (scalar(@name) == 1); + my $path = join('\\',@name); + ::rptMsg("Key ".$path." [LastWrite time: ".::format8601Date($key->get_timestamp())."Z] has more than ".$min_vals." values [total values: ".(scalar @vals)."]"); + ::rptMsg(""); + } + + foreach my $val (@vals) { + $count++; + my $type = $val->get_type(); + if ($type == 0 || $type == 3 || $type == 1 || $type == 2) { + my $data = $val->get_data(); + my $len = length($data); + if ($len > $min_size) { - my @name = split(/\\/,$key->get_path()); - $name[0] = ""; - $name[0] = "\\" if (scalar(@name) == 1); - my $path = join('\\',@name); - ::rptMsg("Key : ".$path." Value: ".$val->get_name()." Size: ".$len." bytes"); + my @name = split(/\\/,$key->get_path()); + $name[0] = ""; + $name[0] = "\\" if (scalar(@name) == 1); + my $path = join('\\',@name); + ::rptMsg("Key : ".$path." Value: ".$val->get_name()." Size: ".$len." bytes"); # Data type "none", "Reg_SZ", "Reg_Expand_SZ" - if ($type == 0 || $type == 1 || $type == 2) { - ::rptMsg("Data Sample (first ".$output_size." bytes) : ".substr($data,0,$output_size)."..."); - } + if ($type == 0 || $type == 1 || $type == 2) { + ::rptMsg("Data Sample (first ".$output_size." bytes) : ".substr($data,0,$output_size)."..."); +# added 20230811 + if ($data =~ m/([A-Za-z0-9+\/]{4}){3,}([A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?/) { + ::rptMsg("Value may contain base64-encoded data"); + } +# -------------- + } # Binary data - if ($type == 3) { - my $out = substr($data,0,$output_size); - probe($out); + if ($type == 3) { + my $out = substr($data,0,$output_size); + ::probe($out); + } + ::rptMsg(""); } - - ::rptMsg(""); } } } @@ -96,65 +130,4 @@ sub traverse { } } -#----------------------------------------------------------- -# probe() -# -# Code the uses printData() to insert a 'probe' into a specific -# location and display the data -# -# Input: binary data of arbitrary length -# Output: Nothing, no return value. Displays data to the console -#----------------------------------------------------------- -sub probe { - my $data = shift; - my @d = printData($data); - ::rptMsg(""); - foreach (0..(scalar(@d) - 1)) { - ::rptMsg($d[$_]); - } - ::rptMsg(""); -} - -#----------------------------------------------------------- -# printData() -# subroutine used primarily for debugging; takes an arbitrary -# length of binary data, prints it out in hex editor-style -# format for easy debugging -# -# Usage: see probe() -#----------------------------------------------------------- -sub printData { - my $data = shift; - my $len = length($data); - - my @display = (); - - my $loop = $len/16; - $loop++ if ($len%16); - - foreach my $cnt (0..($loop - 1)) { -# How much is left? - my $left = $len - ($cnt * 16); - - my $n; - ($left < 16) ? ($n = $left) : ($n = 16); - - my $seg = substr($data,$cnt * 16,$n); - my $lhs = ""; - my $rhs = ""; - foreach my $i ($seg =~ m/./gs) { -# This loop is to process each character at a time. - $lhs .= sprintf(" %02X",ord($i)); - if ($i =~ m/[ -~]/) { - $rhs .= $i; - } - else { - $rhs .= "."; - } - } - $display[$cnt] = sprintf("0x%08X %-50s %s",$cnt,$lhs,$rhs); - } - return @display; -} - 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/skype.pl b/thirdparty/rr-full/plugins/skype.pl deleted file mode 100644 index 3c83bc65f15..00000000000 --- a/thirdparty/rr-full/plugins/skype.pl +++ /dev/null @@ -1,60 +0,0 @@ -#----------------------------------------------------------- -# skype.pl -# -# -# History -# 20100713 - created -# -# References -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package skype; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100713); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets data user's Skype key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching acmru v.".$VERSION); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Skype'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $install; - eval { - $install = $key->get_subkey("Installer")->get_value("DonwloadLastModified")->get_data(); - ::rptMsg("DonwloadLastModified = ".$install); - }; - ::rptMsg("DonwloadLastModified value not found: ".$@) if ($@); - - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/slack.pl b/thirdparty/rr-full/plugins/slack.pl index 9a0aa4e85f2..3bc392d21c8 100644 --- a/thirdparty/rr-full/plugins/slack.pl +++ b/thirdparty/rr-full/plugins/slack.pl @@ -1,28 +1,30 @@ #! c:\perl\bin\perl.exe #----------------------------------------------------------- # slack.pl -# +# Check contents of key/value slack space # # Change history +# 20201005 - MITRE update +# 20200517 - minor updates # 20180926 - created # # References: # # -# -# copyright 2018 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey #----------------------------------------------------------- package slack; use strict; -my %config = (hive => "All", +my %config = (hive => "all", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, + MITRE => "", category => "slack", - version => 20180926); + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/slack_tln.pl b/thirdparty/rr-full/plugins/slack_tln.pl index 6d25b697c6a..d1f5d2f33f6 100644 --- a/thirdparty/rr-full/plugins/slack_tln.pl +++ b/thirdparty/rr-full/plugins/slack_tln.pl @@ -4,6 +4,7 @@ # # # Change history +# 20201005 - MITRE update # 20190506 - slack_tln.pl created # 20180926 - original slack.pl created # @@ -21,9 +22,10 @@ package slack_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, + MITRE => "", category => "slack", - version => 20190506); + output => "tln", + version => 20201005); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/smartscreen.pl b/thirdparty/rr-full/plugins/smartscreen.pl new file mode 100644 index 00000000000..cdaf4ffbffc --- /dev/null +++ b/thirdparty/rr-full/plugins/smartscreen.pl @@ -0,0 +1,98 @@ +#----------------------------------------------------------- +# smartscreen.pl +# Windows Defender SmartScreen warns users before allowing them to run unrecognized programs +# downloaded from the Internet +# +# Change history: +# 20221108 - updated with Explorer\SmartScreenEnabled value check +# 20210806 - created +# +# References: +# https://www.stigviewer.com/stig/windows_10/2018-04-06/finding/V-63685 +# https://admx.help/?Category=Windows_8.1_2012R2&Policy=Microsoft.Policies.WindowsExplorer::EnableSmartScreen +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package smartscreen; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + MITRE => "T1562\.001", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20221108); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check Windows Defender SmartScreen settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching smartscreen v.".$VERSION); + ::rptMsg("smartscreen v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + my $key_path = "Policies\\Microsoft\\Windows\\System"; + if ($key = $root_key->get_subkey($key_path)) { + eval { + my $c = $key->get_value("EnableSmartScreen")->get_data(); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Windows Defender SmartScreen will warn users before running unrecognized programs downloaded from"); + ::rptMsg("the Internet."); + ::rptMsg("0 - Disabled"); + ::rptMsg("1 - Enabled"); + }; + ::rptMsg($key_path."\\EnableSmartScreen value not found.") if ($@); + + eval { + my $c = $key->get_value("ShellSmartScreenLevel")->get_data(); + ::rptMsg("ShellSmartScreenLevel value: ".$c); + ::rptMsg(""); + ::rptMsg("Analysis Tip: The ShellSmartScreenLevel value determines the actions taken when SmartScreen is enabled."); + ::rptMsg("Block - Will not present user with option to disregard warning and run the app."); + ::rptMsg("Warn - Warn user, but allow them to disregard the warning and run the app."); + }; + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); +# Added 20221108 +# https://twitter.com/wdormann/status/1588879659906711552 + my $key_path = "Microsoft\\Windows\\CurrentVersion\\Explorer"; + if ($key = $root_key->get_subkey($key_path)) { + + eval { + my $s = $key->get_value("SmartScreenEnabled")->get_data(); + ::rptMsg("SmartScreenEnabled value: ".$s); + }; + ::rptMsg($key_path."\\SmartScreenEnabled value not found.") if ($@); + + } + else { + ::rptMsg($key_path." not found."); + } + +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/smb.pl b/thirdparty/rr-full/plugins/smb.pl new file mode 100644 index 00000000000..11964ecc034 --- /dev/null +++ b/thirdparty/rr-full/plugins/smb.pl @@ -0,0 +1,90 @@ +#----------------------------------------------------------- +# smb.pl +# Checks status of SMBv1, v2, and V3 on the server +# +# History: +# 20220101 - created +# +# References: +# https://docs.microsoft.com/en-us/windows-server/storage/file-server/troubleshoot/detect-enable-and-disable-smbv1-v2-v3 +# https://docs.microsoft.com/en-us/security-updates/securitybulletins/2017/ms17-010 +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package smb; +use strict; + +my %config = (hive => "system", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562", + output => "report", + version => 20220101); + +sub getConfig{return %config} +sub getShortDescr { + return "Get SMB server settings (v1, v2, v3)"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching smb v.".$VERSION); + ::rptMsg("smb v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + + my $key_path = $ccs."\\Services\\LanmanServer\\Parameters"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Keypath: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); +# SMBv1 disabled on SMB Server + eval { + my $v1 = $key->get_value("SMB1")->get_data(); + ::rptMsg("SMB1 value: ".$v1); + ::rptMsg(""); + ::rptMsg("SMB1 value: ".$v1); + ::rptMsg("0 - disabled"); + ::rptMsg("1 - enabled (default)"); + }; + ::rptMsg("SMB1 value not found\. SMBv1 may be enabled\."); + +# SMBv2/v3 disabled on SMB Server + eval { + my $v2 = $key->get_value("SMB2")->get_data(); + ::rptMsg(""); + ::rptMsg("SMB2 value: ".$v2); + ::rptMsg("0 - disabled"); + ::rptMsg("1 - enabled (default)"); + }; + ::rptMsg("SMB2 value not found\. SMBv2/v3 may be enabled\."); + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: SMBv1 has significant vulnerabilities, and MS encourages adminst to disable it\. That said, threat "); + ::rptMsg("actors can enable it, exposing the server to those vulnerabilities, potentially as a means of persistence\."); + ::rptMsg("SMBv1 is vulnerable to the MS17-010 vulnerability, known as \"Eternal Blue\"\."); + ::rptMsg(""); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/windows-server/storage/file-server/troubleshoot/detect-enable-and-disable-smbv1-v2-v3"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/snapshot.pl b/thirdparty/rr-full/plugins/snapshot.pl deleted file mode 100644 index 95fc9b131de..00000000000 --- a/thirdparty/rr-full/plugins/snapshot.pl +++ /dev/null @@ -1,98 +0,0 @@ -#----------------------------------------------------------- -# snapshot.pl -# Plugin to check the ActiveX component for the MS Access Snapshot -# Viewer kill bit -# -# Ref: US-CERT Vuln Note #837785, http://www.kb.cert.org/vuls/id/837785 -# -# Note: Look for each GUID key, and check for the Compatibility Flags value; -# if the value is 0x400, the kill bit is set; a vulnerable system is -# indicated by having IE version 6.x, and the kill bits NOT set (IE 7 -# requires user interaction to download the ActiveX component -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package snapshot; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20080725); - -sub getConfig{return %config} - -sub getShortDescr { - return "Check ActiveX comp kill bit; Access Snapshot"; -} -sub getDescr{} -sub getRefs {"US-CERT Vuln Note 837785" => "http://www.kb.cert.org/vuls/id/837785"} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my @guids = ("{F0E42D50-368C-11D0-AD81-00A0C90DC8D9}", - "{F0E42D60-368C-11D0-AD81-00A0C90DC8D9}", - "{F2175210-368C-11D0-AD81-00A0C90DC8D9}"); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching snapshot v.".$VERSION); - ::rptMsg("snapshot v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Internet Explorer"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("ActiveX Snapshot Vuln"); - ::rptMsg($key_path); - ::rptMsg(""); - my $ver; - eval { - $ver = $key->get_value("Version")->get_data(); - }; - if ($@) { - ::rptMsg("IE Version not found."); - } - else { - ::rptMsg("IE Version = ".$ver) - } - - ::rptMsg(""); - foreach my $guid (@guids) { - my $g; - eval { - $g = $key->get_subkey("ActiveX Compatibility\\".$guid); - }; - if ($@) { - ::rptMsg("$guid not found."); - } - else { - ::rptMsg("GUID: $guid"); - my $flag; - eval { - $flag = $g->get_value("Compatibility Flags")->get_data(); - }; - if ($@) { - ::rptMsg("Compatibility Flags value not found."); - } - else { - my $str = sprintf "Compatibility Flags 0x%x",$flag; - ::rptMsg($str); - } - } - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/snapshot_viewer.pl b/thirdparty/rr-full/plugins/snapshot_viewer.pl deleted file mode 100644 index 03e29271b1d..00000000000 --- a/thirdparty/rr-full/plugins/snapshot_viewer.pl +++ /dev/null @@ -1,93 +0,0 @@ -#----------------------------------------------------------- -# snapshot_viewer.pl -# Extracts Recent File List for Microsoft Snapshot Viewer -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright (c) 2011-02-10 Brendan Coles -#----------------------------------------------------------- -# Require # -package snapshot_viewer; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110210); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts Recent File List for Microsoft Snapshot Viewer."; -} -sub getRefs { - my %refs = ("Microsoft Snapshot Viewer Homepage:" => - "http://support.microsoft.com/kb/175274"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching snapshot_viewer v.".$VERSION); - ::rptMsg("snapshot_viewer v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\Microsoft\\Snapshot Viewer\\Recent File List"; - - # If # Microsoft Snapshot Viewer path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Snapshot Viewer"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Microsoft Snapshot Viewer registry path # - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Microsoft Snapshot Viewer registry path # - foreach my $v (@vals) { - ::rptMsg($v->get_name()." -> ".$v->get_data()); - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Microsoft Snapshot Viewer isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/soft_run.pl b/thirdparty/rr-full/plugins/soft_run.pl deleted file mode 100644 index 2523fc3f289..00000000000 --- a/thirdparty/rr-full/plugins/soft_run.pl +++ /dev/null @@ -1,169 +0,0 @@ -#----------------------------------------------------------- -# soft_run -# Get contents of Run key from Software hive -# -# History: -# 20130603 - updated alert functionality -# 20130425 - added alertMsg() functionality -# 20130329 - added additional keys -# 20130314 - updated to include Policies keys -# 20120524 - updated to support newer OS's, and 64-bit -# 20080328 - created -# -# -# copyright 2013 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package soft_run; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20130603); - -sub getConfig{return %config} - -sub getShortDescr { - return "[Autostart] Get autostart key contents from Software hive"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Definition of the Run keys in the WinXP Registry" => - "http://support.microsoft.com/kb/314866"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching soft_run v.".$VERSION); - ::rptMsg("soft_run v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my @paths = ("Microsoft\\Windows\\CurrentVersion\\Run", - "Microsoft\\Windows\\CurrentVersion\\RunOnce", - "Microsoft\\Windows\\CurrentVersion\\RunServices", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - "Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", - "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\". - "Windows\\CurrentVersion\\Run", - "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\". - "Windows\\CurrentVersion\\RunOnce", - ); - - foreach my $key_path (@paths) { - - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my %vals = getKeyValues($key); - if (scalar(keys %vals) > 0) { - foreach my $v (keys %vals) { -# added 20130603 - alertCheckPath($vals{$v}); - alertCheckExt($vals{$v}); - alertCheckADS($vals{$v}); - - ::rptMsg(" ".$v." - ".$vals{$v}); - } - ::rptMsg(""); - } - else { - ::rptMsg($key_path." has no values."); - } - - my @sk = $key->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - ::rptMsg(""); - ::rptMsg($key_path."\\".$s->get_name()); - ::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); - my %vals = getKeyValues($s); - foreach my $v (keys %vals) { - ::rptMsg(" ".$v." -> ".$vals{$v}); - } - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." not found."); - ::rptMsg(""); - } - } -} - -sub getKeyValues { - my $key = shift; - my %vals; - - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - - } - return %vals; -} - -#----------------------------------------------------------- -# alertCheckPath() -#----------------------------------------------------------- -sub alertCheckPath { - my $path = shift; - $path = lc($path); - my @alerts = ("recycle","globalroot","temp","system volume information","appdata", - "application data"); - - foreach my $a (@alerts) { - if (grep(/$a/,$path)) { - ::alertMsg("ALERT: soft_run: ".$a." found in path: ".$path); - } - } -} - -#----------------------------------------------------------- -# alertCheckExt() -#----------------------------------------------------------- -sub alertCheckExt { - my $path = shift; - $path = lc($path); - my @exts = ("\.com","\.bat","\.pif"); - - foreach my $e (@exts) { - if ($path =~ m/$e$/) { - ::alertMsg("ALERT: soft_run: ".$path." ends in ".$e); - } - } -} -#----------------------------------------------------------- -# alertCheckADS() -#----------------------------------------------------------- -sub alertCheckADS { - my $path = shift; - my @list = split(/\\/,$path); - my $last = $list[scalar(@list) - 1]; - ::alertMsg("ALERT: soft_run: Poss. ADS found in path: ".$path) if grep(/:/,$last); -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/software b/thirdparty/rr-full/plugins/software index 2f996a673d1..3cfd5e73dfd 100644 --- a/thirdparty/rr-full/plugins/software +++ b/thirdparty/rr-full/plugins/software @@ -1,102 +1,153 @@ -ahaha +activesetup +allowedenum +amsiproviders appcompatflags appinitdlls appkeys +appkeys_tln +appmodel apppaths +apppaths_tln +appsetup assoc -at audiodev -banner -bho -bitbucket +auth +autoadminlogon +autorun +blm btconfig +calibrator +certpadding +certs +clipbrd clsid +clsid_tln cmd_shell -codeid -ctrlpnl +comautoapproval +consentstore +consentstore_tln +credentialsdelegation +datatracing dcom -defbrowser -dfrg +defender +denydeviceids +deviceguard +diagnostics direct +direct_tln +disablemru +disableonedrive +disableproxy disablesr -drivers32 -drwatson +disabletools +dnsclient +driverinstall +duo +elevatedinstall emdmgmt -esent -etos +enablelinkedconn +eventsasp +eventtranscript execpolicy -gauss +feature_block gpohist -handler -ie_version -ie_zones +gpohist_tln +heap +hello +hiddentasks imagefile -init_dlls +injectdll64 inprocserver installedcomp +installelevated installer -javasoft -kankan -kb950582 +installerlogging +installproperties +iso +killsuit +killsuit_tln landesk +landesk_tln lastloggedon -lazyshell licenses -logmein +localdumps +lsass_auditlevel macaddr -mrt +maint +mpnotify msis netsh networkcards networklist -networkuid -opencandy -port_dev -product +networklist_tln +networkprotection +office_test +pointandprint +portdev +portdev_tln +ports +powershellcore +printdemon +printer_settings profilelist pslogging psscript -regback -removdev -renocide +recyclepersist +registerspooler +remoteuac +run +rundisabled runonceex -sbs +runvirtual +runvirtual_tln +run_json +run_yara +ryuk_gpo +sandbox schedagent +scriptleturl secctr -sfc -shellexec -shellext +shadow shelloverlay silentprocessexit -snapshot -soft_run +silentprocessexit_tln +smartscreen spp_clients -sql_lastconnect +srum ssid -startmenuinternetapps_lm +storagesense susclient -svchost +symlink systemindex +tasks +tasks_tln teamviewer +telemetrycontroller termserv -thunderbirdinstalled +thispcpolicy +thumbnail_cleanup tracing -trappoll +tracing_tln uac +uacbypass uninstall +uninstall_tln updates -urlzone -virut +update_test volinfocache +volumecaches +wab +wab_tln watp wbem -webroot -winbackup +win11_edge +windowsupdate winevt -winlogon -winnt_cv +winevtchannels +winlogon_tln winver -win_cv wow64 +wrdata +wrdata_tln wsh_settings -yahoo_lm +xbox diff --git a/thirdparty/rr-full/plugins/source_os.pl b/thirdparty/rr-full/plugins/source_os.pl index 5cb218c306f..608be55f3bd 100644 --- a/thirdparty/rr-full/plugins/source_os.pl +++ b/thirdparty/rr-full/plugins/source_os.pl @@ -2,27 +2,30 @@ # source_os.pl # # History: +# 20220111 - updated with additional keys, etc. +# 20201005 - MITRE update +# 20200511 - update date output format +# 20190829 - added check for CmdLine value # 20180629 - created # # References: # http://az4n6.blogspot.com/2017/02/when-windows-lies.html # # -# copyright 2018 Quantum Analytics Research, LLC +# copyright 2022 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package source_os; use strict; my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "Program Execution", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 31, #XP - Win7 - version => 20180629); + MITRE => "", + output => "report", + version => 20220111); sub getConfig{return %config} sub getShortDescr { @@ -41,14 +44,25 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching source_os v.".$VERSION); - ::rptMsg("source_os v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("source_os v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; my $key_path = 'Setup'; my $key; if ($key = $root_key->get_subkey($key_path)) { +# https://eddiejackson.net/wp/?p=15847 + eval { + my $cmd = $key->get_value("CmdLine")->get_data(); + if ($cmd ne "") { + ::rptMsg("SetupType: ".$key->get_value("SetupType")->get_data()); + ::rptMsg($key_path."\\CmdLine value = ".$cmd); + } + }; + my @sk = $key->get_list_of_subkeys(); foreach my $s (@sk) { my $name = $s->get_name(); @@ -57,12 +71,13 @@ sub pluginmain { my $id = $s->get_value("InstallDate")->get_data(); ::rptMsg($name); - ::rptMsg(" InstallDate: ".gmtime($id)." Z"); + ::rptMsg("Last Write time: ".::format8601Date($s->get_timestamp())."Z"); + ::rptMsg(" InstallDate: ".::format8601Date($id)."Z"); eval { my ($t0,$t1) = unpack("VV",$s->get_value("InstallTime")->get_data()); my $t = ::getTime($t0,$t1); - ::rptMsg(" InstallTime: ".gmtime($t)." Z"); + ::rptMsg(" InstallTime: ".::format8601Date($t)." Z"); }; eval { @@ -88,6 +103,22 @@ sub pluginmain { ::rptMsg(""); } } +# BuildUpdate subkey (added 20220111) + if (my $s = $key->get_subkey("BuildUpdate")) { + ::rptMsg("BuildUpdate key"); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + ::rptMsg(""); + } +# Upgrade subkey (added 20220111) +# There may be devices of interest listed beneath +# Upgrade\PnP\CurrentControlSet\Control\DeviceMigration\Devices\USBStor, SWD\WPDBUSENUM, etc. +# Key LastWrite times may correspond to the Upgrade, but the devices will be listed + if (my $s = $key->get_subkey("Upgrade")) { + ::rptMsg("Upgrade key"); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + ::rptMsg(""); + } + } else { ::rptMsg($key_path." not found."); diff --git a/thirdparty/rr-full/plugins/sourcelist.pl b/thirdparty/rr-full/plugins/sourcelist.pl new file mode 100644 index 00000000000..e63909b4047 --- /dev/null +++ b/thirdparty/rr-full/plugins/sourcelist.pl @@ -0,0 +1,89 @@ +#----------------------------------------------------------- +# sourcelist +# +# Change history: +# 20221031 - created +# +# Ref: +# https://twitter.com/SBousseaden/status/1586862562624299010 +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package sourcelist; +use strict; + +my %config = (hive => "ntuser\.dat", + category => "execution", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1204\.002", + output => "report", + version => 20221031); + +sub getConfig{return %config} +sub getShortDescr { + return "Get media source for product installs"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching sourcelist v.".$config{version}); + ::rptMsg("sourcelist v.".$config{version}); + ::rptMsg("(".$config{hive}.") ".getShortDescr()) +# ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); +# ::rptMsg(""); + + my $key_path = ('Software\\Microsoft\\Installer\\Products'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + +# ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); +# ::rptMsg(""); + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $sk (@subkeys) { + + eval { + my $p = $sk->get_value("ProductName")->get_data(); + ::rptMsg("ProductName: ".$p); + }; + + eval { + my $p = $sk->get_subkey("SourceList")->get_value("PackageName")->get_data(); + ::rptMsg(" PackageName : ".$p); + }; + + eval { + my $m = $sk->get_subkey("SourceList\\Media")->get_value("1")->get_data(); + ::rptMsg(" SourceList\\Media\\1: ".$m); + + }; + + eval { + my $m = $sk->get_subkey("SourceList\\Net")->get_value("1")->get_data(); + ::rptMsg(" SourceList\\Net\\1 : ".$m); + + }; + + ::rptMsg(""); + } + } + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/sourcerouting.pl b/thirdparty/rr-full/plugins/sourcerouting.pl new file mode 100644 index 00000000000..31de08ab3d6 --- /dev/null +++ b/thirdparty/rr-full/plugins/sourcerouting.pl @@ -0,0 +1,79 @@ +#----------------------------------------------------------- +# sourcerouting.pl +# Check source routing setting; CVE-2021-24074 +# +# +# Change history +# 20210212 - created +# +# References +# https://meterpreter.org/cve-2021-24074-windows-tcp-ip-remote-code-execution-vulnerability-alert/ +# https://admx.help/?Category=SecurityBaseline&Policy=Microsoft.Policies.MSS::Pol_MSS_DisableIPSourceRouting +# +# copyright 2021 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package sourcerouting; +use strict; + +my %config = (hive => "System", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1203", + category => "execution", + output => "report", + version => 20210212); + +sub getConfig{return %config} +sub getShortDescr { + return "Get Source Routing setting"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my %nics; + my $ccs; + ::logMsg("Launching sourcerouting v.".$VERSION); + ::rptMsg("sourcerouting v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $current; + eval { + $current = ::getCCS($root_key); + }; + + my $key_path = $current."\\Services\\Tcpip\\Parameters"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + eval { + my $d = $key->get_value("DisableIPSourceRouting")->get_data(); + ::rptMsg("DisableIPSourceRouting value: ".$d); + }; + ::rptMsg("DisableIPSourceRouting value not found") if ($@); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Disabling Source Routing (set value to 2) can help protect against CVE-2021-24074"); + ::rptMsg("0 - No additional protection, source routed packets are allowed"); + ::rptMsg("1 - Medium, source routed packets ignored when IP forwarding is enabled"); + ::rptMsg("2 - Highest protection, source routing is completely disabled"); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/speech.pl b/thirdparty/rr-full/plugins/speech.pl new file mode 100644 index 00000000000..7130e492a2f --- /dev/null +++ b/thirdparty/rr-full/plugins/speech.pl @@ -0,0 +1,81 @@ +#----------------------------------------------------------- +# speech.pl +# The key and values in question are associated with the Windows text-to-speech +# functionality. It turns out that there are several malware variants, including +# ransomware (Cerber, MiliCry) that deliver an audio message. While not definitive, +# the results of this plugin provide a low fidelity indicator that may be useful. +# +# Change history +# 20201005 - MITRE update +# 20200427 - updated output date format +# 20191010 - created +# +# References +# https://www.trendmicro.com/vinfo/us/threat-encyclopedia/malware/ransom_cerber.vsafi +# https://www.trendmicro.com/vinfo/us/threat-encyclopedia/malware/ransom_milicry.gqs +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package speech; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1059", + category => "program execution", + output => "report", + version => 20201005); + +sub getConfig{return %config} +sub getShortDescr { + return "Get values from user's Speech key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching speech v.".$VERSION); + ::rptMsg("speech v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = "Software\\Microsoft\\Speech"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + + eval { + ::rptMsg("CurrentUserLexicon Datafile value : ".$key->get_subkey("CurrentUserLexicon\\{C9E37C15-DF92-4727-85D6-72E5EEB6995A}\\Files")->get_value("Datafile")->get_data()); + ::rptMsg(""); + }; + + eval { + ::rptMsg("Voices DefaultTokenId value : ".$key->get_subkey("Voices")->get_value("DefaultTokenId")->get_data()); + ::rptMsg(""); + }; + + eval { + ::rptMsg("PhoneConverters DefaultTokenId value : ".$key->get_subkey("PhoneConverters")->get_value("DefaultTokenId")->get_data()); + }; + ::rptMsg("Analysis Tip: A few ransomware variants have been observed providing indications of infection via the MS text-to-speech function."); + ::rptMsg("A such, this plugin may provide low fidelity indicators of malicious activity."); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/speech_tln.pl b/thirdparty/rr-full/plugins/speech_tln.pl new file mode 100644 index 00000000000..1f331787130 --- /dev/null +++ b/thirdparty/rr-full/plugins/speech_tln.pl @@ -0,0 +1,82 @@ +#----------------------------------------------------------- +# speech_tln.pl +# The key and values in question are associated with the Windows text-to-speech +# functionality. It turns out that there are several malware variants, including +# ransomware (Cerber, MiliCry) that deliver an audio message. While not definitive, +# the results of this plugin provide a low fidelity indicator that may be useful. +# +# Change history +# 20201005 - MITRE update +# 20191010 - created +# +# References +# https://www.trendmicro.com/vinfo/us/threat-encyclopedia/malware/ransom_cerber.vsafi +# https://www.trendmicro.com/vinfo/us/threat-encyclopedia/malware/ransom_milicry.gqs +# +# +# copyright 2019-2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package speech_tln; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1059", + category => "program execution", + output => "tln", + version => 20201005); + +sub getConfig{return %config} +sub getShortDescr { + return "Get values from user's Speech key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; +# ::logMsg("Launching speech v.".$VERSION); +# ::rptMsg("speech v.".$VERSION); +# ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = "Software\\Microsoft\\Speech"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + eval { + my $file = $key->get_subkey("CurrentUserLexicon\\{C9E37C15-DF92-4727-85D6-72E5EEB6995A}\\Files"); + my $lw = $file->get_timestamp(); + my $val = $file->get_value("Datafile")->get_data(); + ::rptMsg($lw."|REG|||Speech CurrentUserLexicon Datafile value : ".$val); + }; + + eval { + my $voices = $key->get_subkey("Voices"); + my $lw = $voices->get_timestamp(); + my $val = $voices->get_value("DefaultTokenId")->get_data(); + ::rptMsg($lw."|REG|||Speech Voices DefaultTokenId value : ".$val); + }; + + eval { + my $phone = $key->get_subkey("PhoneConverters"); + my $lw = $phone->get_timestamp(); + my $val = $phone->get_value("DefaultTokenId")->get_data(); + ::rptMsg($lw."|REG|||Speech PhoneConverters DefaultTokenId value : ".$val); + }; + } + else { +# ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/spooler.pl b/thirdparty/rr-full/plugins/spooler.pl new file mode 100644 index 00000000000..45b7e111b36 --- /dev/null +++ b/thirdparty/rr-full/plugins/spooler.pl @@ -0,0 +1,73 @@ +#----------------------------------------------------------- +# spooler.pl +# Check Spooler service RequiredPrivileges value +# +# History +# 20230715 - created +# +# References +# https://thedfirreport.com/2023/06/12/a-truly-graceful-wipe-out/ +# +# copyright 2023 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package spooler; +use strict; +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1547\.012", + category => "privilege escalation", + output => "report", + version => 20230715); + +sub getConfig{return %config} +sub getShortDescr { + return "Check Spooler service RequiredPrivileges value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $key; + + ::logMsg("Launching spooler v.".$VERSION); + ::rptMsg("spooler v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services\\Spooler"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $i = $key->get_value("RequiredPrivileges")->get_data(); + ::rptMsg("RequiredPrivileges value: ".$i); + }; + ::rptMsg("RequiredPrivileges value not found.") if ($@); + + ::rptMsg(""); + ::rptMsg("Analysis Tip: A threat actor was observed performing privilege escalation by stopping the Spooler service,"); + ::rptMsg("deleting the RequiredPrivileges value, restarting the Spooler service, and then injecting into the newly"); + ::rptMsg("created spoolsv.exe process."); + ::rptMsg(""); + ::rptMsg("Ref: https://thedfirreport.com/2023/06/12/a-truly-graceful-wipe-out/"); + } + else { + ::rptMsg($key_path." not found."); + } +} +1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/spp_clients.pl b/thirdparty/rr-full/plugins/spp_clients.pl index b230617e938..f80e783cd05 100644 --- a/thirdparty/rr-full/plugins/spp_clients.pl +++ b/thirdparty/rr-full/plugins/spp_clients.pl @@ -2,10 +2,11 @@ # spp_clients # # History +# 20201005 - MITRE update # 20130429 - added alertMsg() functionality # 20120914 - created # -# copyright 2013 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package spp_clients; @@ -15,8 +16,10 @@ package spp_clients; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 50, #Vista, Win7 - version => 20130429); + MITRE => "", + category => "config", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -34,7 +37,7 @@ sub pluginmain { my $hive = shift; ::logMsg("Launching spp_clients v.".$VERSION); ::rptMsg("spp_clients v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -43,16 +46,17 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("SPP_Clients"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); - + ::rptMsg("Monitored volumes: "); my $mon; eval { $mon = $key->get_value("{09F7EDC5-294E-4180-AF6A-FB0E6A0E9513}")->get_data(); - ::rptMsg("Monitored volumes: ".$mon); - ::alertMsg("ALERT: No volumes monitored by VSS\.") if ($mon eq ""); + ::rptMsg($mon); + ::rptMsg(""); + ::rptMsg("Analysis Tip: This value indicates volumes that are monitored for VSCs. A threat actor can read this value"); + ::rptMsg("and use volumes not monitored, or modify the value."); }; - } else { ::rptMsg($key_path." not found."); diff --git a/thirdparty/rr-full/plugins/sql_lastconnect.pl b/thirdparty/rr-full/plugins/sql_lastconnect.pl deleted file mode 100644 index 511ec4a7de9..00000000000 --- a/thirdparty/rr-full/plugins/sql_lastconnect.pl +++ /dev/null @@ -1,68 +0,0 @@ -#----------------------------------------------------------- -# sql_lastconnect.pl -# -# Per MS, Microsoft Data Access Components (MDAC) clients can attempt -# to use multiple protocols based on a protocol ordering, which is -# listed in the SuperSocketNetLib\ProtocolOrder value. Successful -# connection attempts (for SQL Server 2000) are cached in the LastConnect -# key. -# -# References: -# http://support.microsoft.com/kb/273673/ -# -# copyright 2009 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package sql_lastconnect; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20090112); - -sub getConfig{return %config} - -sub getShortDescr { - return "MDAC cache of successful connections"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching sql_lastconnect v.".$VERSION); - ::rptMsg("sql_lastconnect v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\MSSQLServer\\Client\\SuperSocketNetLib\\LastConnect"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("MDAC Cache of successful connections"); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $str = sprintf "%-15s %-25s",$v->get_name(),$v->get_data(); - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/srum.pl b/thirdparty/rr-full/plugins/srum.pl new file mode 100644 index 00000000000..66bc77780a2 --- /dev/null +++ b/thirdparty/rr-full/plugins/srum.pl @@ -0,0 +1,109 @@ +#----------------------------------------------------------- +# srum +# +# Change history: +# 20201005 - MITRE update +# 20200518 - minor updates +# 20150721 - created +# +# Ref: +# https://files.sans.org/summit/Digital_Forensics_and_Incident_Response_Summit_2015/PDFs/Windows8SRUMForensicsYogeshKhatri.pdf +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package srum; +use strict; + +my %config = (hive => "Software", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20201005); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets contents of SRUM subkeys"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching srum v.".$VERSION); + ::rptMsg("srum v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $key_path = ('Microsoft\\Windows NT\\CurrentVersion\\SRUM\\Extensions'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my $network; + if ($network = $key->get_subkey('{973F5D5C-1D90-4944-BE8E-24B94231A174}\\RecordSets\\0')) { + processNetworkData($network); + } + + ::rptMsg(""); + + my $app; + if ($app = $key->get_subkey('{d10ca2fe-6fcf-4f6d-848e-b2e99266fa89}\\RecordSets\\0')) { + processApplicationData($app); + } + + } + else { + ::rptMsg($key_path." not found."); + } +} + + +sub processNetworkData { + my $key = shift; + my @names; + my @sk = $key->get_list_of_subkeys(); + foreach my $s (sort @sk) { + push(@names,$s->get_name()); + } + + foreach my $n (sort @names) { + ::rptMsg("Name: ".$n); + my $data = $key->get_subkey($n)->get_value('AppId')->get_data(); + my $appid = substr($data,8,length($data)); + $appid =~ s/\00//g; + ::rptMsg(" AppID: ".$appid); + + } +} + +sub processApplicationData { + my $key = shift; + my @names; + my @sk = $key->get_list_of_subkeys(); + foreach my $s (sort @sk) { + push(@names,$s->get_name()); + } + + foreach my $n (sort {$a <=> $b} @names) { + ::rptMsg("Name: ".$n); + my $data = $key->get_subkey($n)->get_value('AppId')->get_data(); + my $appid = substr($data,8,length($data)); + $appid =~ s/\00//g; + ::rptMsg(" AppID: ".$appid); + + } + +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/srun_tln.pl b/thirdparty/rr-full/plugins/srun_tln.pl deleted file mode 100644 index 5651e8bb9ce..00000000000 --- a/thirdparty/rr-full/plugins/srun_tln.pl +++ /dev/null @@ -1,119 +0,0 @@ -#----------------------------------------------------------- -# srun_tln -# Get contents of Run key from Software hive -# -# History: -# 20130425 - created -# -# -# copyright 2013 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package srun_tln; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20130425); - -sub getConfig{return %config} - -sub getShortDescr { - return "[Autostart] Get autostart key contents from Software hive (TLN)"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Definition of the Run keys in the WinXP Registry" => - "http://support.microsoft.com/kb/314866"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching srun_tln v.".$VERSION); -# ::rptMsg("srun_tln v.".$VERSION); # banner -# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my @paths = ("Microsoft\\Windows\\CurrentVersion\\Run", - "Microsoft\\Windows\\CurrentVersion\\RunOnce", - "Microsoft\\Windows\\CurrentVersion\\RunServices", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - "Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", - "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\". - "Windows\\CurrentVersion\\Run", - "Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Software\\Microsoft\\". - "Windows\\CurrentVersion\\RunOnce", - ); - - my @alertpaths = ("recycle","globalroot","temp","system volume information","appdata", - "application data"); - - foreach my $key_path (@paths) { - - my $key; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my $lw = $key->get_timestamp(); - my %vals = getKeyValues($key); - if (scalar(keys %vals) > 0) { - foreach my $v (keys %vals) { - my $lc_path = lc($vals{$v}); - foreach my $a (@alertpaths) { - if (grep(/$a/,$lc_path)) { -# ::alertMsg("ALERT: soft_run: Temp Path found: ".$key_path." : ".$v." -> ".$vals{$v}); - ::alertMsg($lw."|ALERT|||srun_tln: Software\\".$key_path." Temp path: ".$v.": ".$vals{$v}); - } - } -# check to see if the data ends in .com - if ($vals{$v} =~ m/\.com$/ || $vals{$v} =~ m/\.bat$/ || $vals{$v} =~ m/\.pif$/) { -# ::alertMsg("ALERT: soft_run: Path ends in \.com/\.bat/\.pif: ".$key_path." : ".$v." -> ".$vals{$v}); - ::alertMsg($lw."|ALERT|||srun_tln: Software\\".$key_path." ends in \.com/\.bat/\.pif: ".$v.": ".$vals{$v}); - } - - my @list = split(/:/,$vals{$v}); - my $last = $list[scalar(@list) - 1]; - ::alertMsg($lw."|ALERT|||srun_tln: Poss. ADS found: ".$v.": ".$vals{$v}) if (grep(/:/,$last)); - -# ::rptMsg(" ".$v." - ".$vals{$v}); - } -# ::rptMsg(""); - } - else { -# ::rptMsg($key_path." has no values."); - } - } - - } -} - -sub getKeyValues { - my $key = shift; - my %vals; - - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - - } - return %vals; -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/ssh_host_keys.pl b/thirdparty/rr-full/plugins/ssh_host_keys.pl deleted file mode 100644 index e420d4f1e61..00000000000 --- a/thirdparty/rr-full/plugins/ssh_host_keys.pl +++ /dev/null @@ -1,109 +0,0 @@ -# ssh_host_keys.pl -# -# RegRipper module to extract stored Putty and WinSCP host keys. -# The keys are found in NTUSER.DAT under: -# -# Software\Martin Prikryl\WinSCP 2\SshHostKeys -# Software\SimonTatham\Putty\SshHostKeys -# -# Change History -# 04/02/2013 Added rptMsg for key not found errors by Corey Harrell -# -# Presence of a host key indicates a successful connection to a given host, -# but not necessarily a successful login. -# -# RegRipper module author Hal Pomeranz - -package ssh_host_keys; - -use strict; - -my %config = ('hive' => 'NTUSER.DAT', - 'hasShortDescr' => 1, - 'hasDescr' => 0, - 'hasRefs' => 0, - 'osmask' => 22, - 'version' => '20120809'); - -sub getConfig { return(%config); } -sub getShortDescr { return('Extracts Putty/WinSCP SSH Host Keys'); } -sub getDescr {} -sub getRefs {} -sub getHive { return($config{'hive'}); } -sub getVersion { return($config{'version'}); } - -my $VERSION = $config{'version'}; - -sub pluginmain { - my($class, $hive) = @_; - my($reg, $root, $key) = (); - - ::logMsg("Launching ssh_host_keys v.$VERSION\n"); - ::rptMsg("ssh_host_keys v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - unless ($reg = Parse::Win32Registry->new($hive)) { -# ::logMsg("Failed to open $hive: $!"); - return(); - } - unless ($root = $reg->get_root_key()) { -# ::logMsg("Failed to get root key from $hive: $!"); - return(); - } - - if ($key = $root->get_subkey('Software\SimonTatham\Putty\SshHostKeys')) { - display_key_data($key); - } - else { -# ::logMsg('"Software\SimonTatham\Putty\SshHostKeys" does not exist' . "\n"); - ::rptMsg('"Software\SimonTatham\Putty\SshHostKeys" does not exist' . "\n"); # line added on 04/02/2013 - } - - if ($key = $root->get_subkey('Software\Martin Prikryl\WinSCP 2\SshHostKeys')) { - display_key_data($key); - } - else { -# ::logMsg('"Software\Martin Prikryl\WinSCP 2\SshHostKeys" does not exist'); - ::rptMsg('"Software\Martin Prikryl\WinSCP 2\SshHostKeys" does not exist'); # line added on 04/02/2013 - } -} - - -sub display_key_data { - my($key) = @_; - - my $path = $key->get_path(); - $path =~ s/.*?\\//; - - ::rptMsg("$path\nLast Updated: " . scalar(gmtime($key->get_timestamp())) . " UTC\n"); - - my(%sort, %host_info) = (); - my @vals = $key->get_list_of_values(); - foreach my $val (@vals) { - my $name = $val->get_name(); - my($type, $port, $host) = $name =~ /^([^@]+)@(\d+):(.*)$/; - my $host_key = $val->get_data(); - - if ($host =~ /^[\d.]+$/) { - $sort{$name} = sprintf("%03d%03d%03d%03d", split(/\./, $host)); - } - else { $sort{$name} = $host; } - - $host_info{$name} = { - 'host' => $host, - 'port' => $port, - 'type' => $type, - 'key' => $host_key - }; - } - - foreach my $name ( - sort { $sort{$a} cmp $sort{$b} || - $host_info{$a}{'port'} <=> $host_info{$b}{'port'} || - $host_info{$a}{'type'} cmp $host_info{$b}{'type'} - } keys(%host_info)) { - ::rptMsg("$host_info{$name}{'host'}:$host_info{$name}{'port'} ($host_info{$name}{'type'})"); - ::rptMsg("$host_info{$name}{'key'}\n"); - } -} - -1; diff --git a/thirdparty/rr-full/plugins/ssid.pl b/thirdparty/rr-full/plugins/ssid.pl index 5a173056720..92560941392 100644 --- a/thirdparty/rr-full/plugins/ssid.pl +++ b/thirdparty/rr-full/plugins/ssid.pl @@ -4,6 +4,8 @@ # # # Change History: +# 20201005 - MITRE update +# 20200515 - updated date output format # 20100301 - Updated References; removed dwCtlFlags being # printed; minor adjustments to formatting # 20091102 - added code to parse EAPOL values for SSIDs @@ -13,7 +15,8 @@ # References # http://msdn.microsoft.com/en-us/library/aa448338.aspx # -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package ssid; use strict; @@ -22,12 +25,14 @@ package ssid; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20100301); + MITRE => "", + category => "config", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { - return "Get WZCSVC SSID Info"; + return "Get WZCSVC key SSID Info"; } sub getDescr{} sub getRefs {} @@ -67,7 +72,7 @@ sub pluginmain { my $name = $s->get_name(); if (exists($nc{$name})) { ::rptMsg("NIC: ".$nc{$name}{descr}); - ::rptMsg("Key LastWrite: ".gmtime($s->get_timestamp())." UTC"); + ::rptMsg("Key LastWrite: ".::format8601Date($s->get_timestamp())."Z"); ::rptMsg(""); my @vals = $s->get_list_of_values(); if (scalar(@vals) > 0) { @@ -87,7 +92,7 @@ sub pluginmain { my ($t1,$t2) = unpack("VV",substr($data,0x2B8,8)); my $t = ::getTime($t1,$t2); - my $str = sprintf gmtime($t)." MAC: %-18s %-8s",$mac,$ssid; + my $str = sprintf ::format8601Date($t)."Z MAC: %-18s %-8s",$mac,$ssid; ::rptMsg($str); } } @@ -109,7 +114,8 @@ sub pluginmain { # Now, go to the EAPOL key, locate the appropriate subkeys and parse out # any available SSIDs # EAPOL is Extensible Authentication Protocol over LAN - $key_path = "Microsoft\\EAPOL\\Parameters\\Interfaces"; + my $key_path = "Microsoft\\EAPOL\\Parameters\\Interfaces"; + my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg(""); ::rptMsg($key_path); @@ -124,7 +130,7 @@ sub pluginmain { else { ::rptMsg("NIC: ".$name); } - ::rptMsg("LastWrite time: ".gmtime($s->get_timestamp())." UTC"); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); my @vals = $s->get_list_of_values(); my %eapol; @@ -181,4 +187,4 @@ sub parseEAPOLData { return substr($data,0x14,$size); } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/staginginfo.pl b/thirdparty/rr-full/plugins/staginginfo.pl new file mode 100644 index 00000000000..49f8b81229c --- /dev/null +++ b/thirdparty/rr-full/plugins/staginginfo.pl @@ -0,0 +1,86 @@ +#----------------------------------------------------------- +# staginginfo.pl +# Plugin to get info regarding CD burning +# +# +# Change history +# 20210407 - created +# +# References +# https://secureartisan.wordpress.com/2012/06/04/windows-7-cddvd-burning/ +# https://attack.mitre.org/techniques/T1074/001/ +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package staginginfo; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "collection", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1074\.001", + output => "report", + version => 20210407); + +sub getConfig{return %config} +sub getShortDescr { + return "Get info regarding CD burning"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching staginginfo v.".$VERSION); + ::rptMsg("staginginfo v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CD Burning\\StagingInfo'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg("Drive : ".$s->get_name()); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + + eval { + ::rptMsg("StagingPath : ".$s->get_value("StagingPath")->get_data()); + }; + + eval { + ::rptMsg("Active : ".$s->get_value("Active")->get_data()); + }; + + eval { + ::rptMsg("DriveNumber : ".$s->get_value("DriveNumber")->get_data()); + }; + + ::rptMsg(""); + } + } + ::rptMsg("Analysis Tip: Information from this plugin provides insight into the use of Windows Explorer to burn CDs, and"); + ::rptMsg("should be correlated with other host-based data to develop greater context."); + } + else { + ::rptMsg($key_path." key not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/startmenuinternetapps_cu.pl b/thirdparty/rr-full/plugins/startmenuinternetapps_cu.pl deleted file mode 100644 index 65156ad5627..00000000000 --- a/thirdparty/rr-full/plugins/startmenuinternetapps_cu.pl +++ /dev/null @@ -1,98 +0,0 @@ -#----------------------------------------------------------- -# startmenuinternetapps_cu.pl -# Start Menu Internet Applications settings (HKCU) parser -# -# Change history -# 20100906 [fpi] % created -# 20101219 [fpi] % first version -# 20110830 [fpi] + banner, no change to the version number -# -# References -# http://msdn.microsoft.com/en-us/library/dd203067(VS.85).aspx -# -# copyright 2010 F. Picasso, francesco.picasso@gmail.com -#----------------------------------------------------------- -package startmenuinternetapps_cu; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20101219); - -sub getConfig{return %config} -sub getShortDescr { - return "Start Menu Internet Applications info current user"; -} -sub getDescr{} -sub getRefs { - my %refs = ("How to Register an Internet Browser or E-mail Client With the Windows Start Menu" => - "http://msdn.microsoft.com/en-us/library/dd203067(VS.85).aspx"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg( "Launching startmenuinternetapps_cu v.".$VERSION ); - ::rptMsg("startmenuinternetapps_cu v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new( $ntuser ); - my $root_key = $reg->get_root_key; - - my $path = 'Software\\Clients'; - my $key; - - if ( $key = $root_key->get_subkey( $path ) ) { - ::rptMsg( "Start Menu Internet Applications\n" ); - - my @subkeys = $key->get_list_of_subkeys(); - if ( ( scalar @subkeys ) > 0 ) { - - foreach my $sbk ( @subkeys ) { - my $tmp = $sbk->get_name(); - ::rptMsg( $tmp." [".gmtime( $sbk->get_timestamp() )." (UTC)]" ); - - if ( $tmp eq "StartMenuInternet" ) { - ::rptMsg( "NOTE: default Internet Browser client key" ); - } - elsif ( $tmp eq "Mail" ) { - ::rptMsg( "NOTE: default Mail client key" ); - } - - my @vals = $sbk->get_list_of_values(); - - if ( ( scalar @vals ) > 0 ) { - foreach my $val ( @vals ) { - $tmp = $val->get_name(); - if ( $tmp eq "" ) { - $tmp = "(default)"; - } - ::rptMsg( $tmp." -> ".$val->get_data()."\n" ); - } - } - else { - ::rptMsg( $sbk->get_name()." has no values." ); - ::logMsg( $sbk->get_name()." has no values." ); - } - } - } - else { - ::rptMsg( $key->get_name()." has no subkeys." ); - ::logMsg( $key->get_name()." has no subkeys." ); - } - } - else { - ::rptMsg( $path." not found. Check the same path in HKLM" ); - ::logMsg( $path." not found. Check the same path in HKLM" ); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/startmenuinternetapps_lm.pl b/thirdparty/rr-full/plugins/startmenuinternetapps_lm.pl deleted file mode 100644 index d0e7bab5ea3..00000000000 --- a/thirdparty/rr-full/plugins/startmenuinternetapps_lm.pl +++ /dev/null @@ -1,101 +0,0 @@ -#----------------------------------------------------------- -# startmenuinternetapps_lm.pl -# Start Menu Internet Applications settings (HKLM) parser -# -# Change history -# 20100906 [fpi] % created -# 20101219 [fpi] % first version -# 20110830 [fpi] + banner, no change to the version number -# -# References -# http://msdn.microsoft.com/en-us/library/dd203067(VS.85).aspx -# -# copyright 2010 F. Picasso, francesco.picasso@gmail.com -#----------------------------------------------------------- -package startmenuinternetapps_lm; -use strict; - -my %config = (hive => "SOFTWARE", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20101219); - -sub getConfig{return %config} -sub getShortDescr { - return "Start Menu Internet Applications info"; -} -sub getDescr{} -sub getRefs { - my %refs = ("How to Register an Internet Browser or E-mail Client With the Windows Start Menu" => - "http://msdn.microsoft.com/en-us/library/dd203067(VS.85).aspx"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg( "Launching startmenuinternetapps_lm.".$VERSION ); - ::rptMsg("startmenuinternetapps_lm v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new( $ntuser ); - my $root_key = $reg->get_root_key; - - my $path = 'Clients'; - my $key; - - if ( $key = $root_key->get_subkey( $path ) ) { - ::rptMsg( "Start Menu Internet Applications" ); - - my @subkeys = $key->get_list_of_subkeys(); - if ( ( scalar @subkeys ) > 0 ) { - - foreach my $sbk ( @subkeys ) { - ::rptMsg( "\n" ); - my $tmp = $sbk->get_name(); - ::rptMsg( " [".gmtime( $sbk->get_timestamp() )." (UTC)] ".$tmp ); - - my @vals = $sbk->get_list_of_values(); - - if ( ( scalar @vals ) > 0 ) { - foreach my $val ( @vals ) { - $tmp = $val->get_name(); - # print default only - if ( $tmp eq "" ) { - ::rptMsg( " VALUE: ".$tmp."(default) -> ".$val->get_data() ); - } - - } - } - else { - ::rptMsg( " VALUE: no values." ); - } - - # getting subkeys - my @subkeys2 = $sbk->get_list_of_subkeys(); - if ( ( scalar @subkeys2 ) > 0 ) { - foreach my $sbk2 ( @subkeys2 ) { - $tmp = $sbk2->get_name(); - ::rptMsg( " SUBKEY: "." [".gmtime( $sbk2->get_timestamp() )." (UTC)] ".$tmp ); - } - } - } - } - else { - ::rptMsg( $key->get_name()." has no subkeys." ); - ::logMsg( $key->get_name()." has no subkeys." ); - } - } - else { - ::rptMsg( $path." not found." ); - ::logMsg( $path." not found." ); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/startpage.pl b/thirdparty/rr-full/plugins/startpage.pl deleted file mode 100644 index 70592a87cb1..00000000000 --- a/thirdparty/rr-full/plugins/startpage.pl +++ /dev/null @@ -1,79 +0,0 @@ -#----------------------------------------------------------- -# startpage.pl -# For Windows 7 -# -# Change history -# 20100330 - created -# -# References -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package startpage; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100330); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of user's StartPage key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching startpage v.".$VERSION); - ::rptMsg("startpage v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartPage"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $menu; - my $balloon; - - eval { - my $val = $key->get_value("StartMenu_Start_Time")->get_data(); - my ($t0,$t1) = unpack("VV",$val); - $menu = ::getTime($t0,$t1); - ::rptMsg("StartMenu_Start_Time = ".gmtime($menu)." Z"); - }; - ::rptMsg("Error: ".@$) if (@$); - - eval { - my $val = $key->get_value("StartMenu_Balloon_Time")->get_data(); - my ($t0,$t1) = unpack("VV",$val); - $balloon = ::getTime($t0,$t1); - ::rptMsg("StartMenu_Balloon_Time = ".gmtime($balloon)." Z"); - }; - ::rptMsg("Error: ".@$) if (@$); - - - - - - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/startup.pl b/thirdparty/rr-full/plugins/startup.pl deleted file mode 100644 index 16577883e9b..00000000000 --- a/thirdparty/rr-full/plugins/startup.pl +++ /dev/null @@ -1,88 +0,0 @@ -#----------------------------------------------------------- -# startup.pl -# Plugin for Registry Ripper, NTUSER.DAT edition - gets the -# ACMru values -# -# Change history -# 20131028 - updated to include User Shell Folders entry -# 20131025 - created -# -# References -# http://www.fireeye.com/blog/technical/malware-research/2013/10/evasive-tactics-terminator-rat.html -# http://www.symantec.com/connect/articles/most-common-registry-key-check-while-dealing-virus-issue -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package startup; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20131028); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets user's Startup Folder location"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching startup v.".$VERSION); - ::rptMsg("startup v.".$VERSION); # banner - ::rptMsg(getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - eval { - my $start = $key->get_value("Startup")->get_data(); - ::rptMsg("StartUp folder : ".$start); - processPath($start); - }; - } - else { - ::rptMsg($key_path." not found."); - } - -# added 20131028 - ::rptMsg(""); - $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders'; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - eval { - my $start = $key->get_value("Startup")->get_data(); - ::rptMsg("StartUp folder : ".$start); - processPath($start); - }; - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub processPath { - my $path = shift; - my $lcpath = $path; - $lcpath =~ tr/[A-Z]/[a-z]/; - ::rptMsg("Alert: Possible incorrect path found") unless ($lcpath =~ m/start menu\\programs\\startup$/); -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/stillimage.pl b/thirdparty/rr-full/plugins/stillimage.pl deleted file mode 100644 index e1ed06788d9..00000000000 --- a/thirdparty/rr-full/plugins/stillimage.pl +++ /dev/null @@ -1,112 +0,0 @@ -#----------------------------------------------------------- -# stillimage.pl -# Parses contents of Enum\USB key for web cam -# -# History -# 20100222 - created -# -# References -# http://msdn.microsoft.com/en-us/library/ms791870.aspx -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package stillimage; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20100222); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get info on StillImage devices"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my $reg; - -sub pluginmain { - my $class = shift; - my $hive = shift; - $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - ::logMsg("Launching stillimage v.".$VERSION); - ::rptMsg("stillimage v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner -# Code for System file, getting CurrentControlSet - my $current; - my $ccs; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - else { - ::rptMsg($key_path." not found."); - return; - } - - $key_path = $ccs."\\Control\\Class\\{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}"; - if ($key = $root_key->get_subkey($key_path)) { - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar @subkeys > 0) { - ::rptMsg(""); - foreach my $s (@subkeys) { - my $name = $s->get_name(); - next unless ($name =~ m/\d\d/); - ::rptMsg($name); - - eval { - my $desc = $s->get_value("DriverDesc")->get_data(); - ::rptMsg(" ".$desc); - }; - - eval { - my $desc = $s->get_value("MatchingDeviceID")->get_data(); - ::rptMsg(" ".$desc); - }; - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } - -# http://msdn.microsoft.com/en-us/library/ms791870.aspx -# StillImage logging levels - $key_path = $ccs."\\Control\\StillImage\\Logging"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - ::rptMsg("StillImage Logging Level"); - eval { - my $level = $key->get_subkey("STICLI")->get_value("Level")->get_data(); - my $str = sprintf " STICLI Logging Level = 0x%x",$level; - ::rptMsg($str); - }; - ::rptMsg("STICLI Error: ".$@) if ($@); - - eval { - my $level = $key->get_subkey("STIMON")->get_value("Level")->get_data(); - my $str = sprintf " STIMON Logging Level = 0x%x",$level; - ::rptMsg($str); - }; - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/storagesense.pl b/thirdparty/rr-full/plugins/storagesense.pl new file mode 100644 index 00000000000..a681b6db5b4 --- /dev/null +++ b/thirdparty/rr-full/plugins/storagesense.pl @@ -0,0 +1,99 @@ +#----------------------------------------------------------- +# storagesense.pl +# Get StorageSense values +# +# Change history: +# 20201230 - created +# +# References: +# http://port139.hatenablog.com/entry/2018/12/24/122856 +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, 2013 +#----------------------------------------------------------- +package storagesense; +use strict; + +my %config = (hive => "software, ntuser\.dat", + category => "persistence", + MITRE => "T1547", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20201230); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get StorageSense values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching storagesense v.".$VERSION); + ::rptMsg("storagesense v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + + if ($hive_guess eq "software") { + + + } + elsif ($hive_guess eq "ntuser") { + + + } + else {} + + + my @paths = ("Software\\Microsoft\\Windows\\CurrentVersion\\StorageSense\\Parameters\\StoragePolicy", # HKCU + "Microsoft\\Windows\\CurrentVersion\\StorageSense\\Parameters", # HKLM + "Policies\\Microsoft\\Windows\\StorageSense"); # HKLM GPO + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg(""); + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg($v->get_name()." - ".$v->get_data()); + + } + + } + else { + ::rptMsg($key_path." has no values."); + } + } + else { +# ::rptMsg($key_path." not found."); + } + } + ::rptMsg("Analysis Tip: "); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/susclient.pl b/thirdparty/rr-full/plugins/susclient.pl index f7b8a01d141..13267ceea57 100644 --- a/thirdparty/rr-full/plugins/susclient.pl +++ b/thirdparty/rr-full/plugins/susclient.pl @@ -3,13 +3,15 @@ # Values within this key appear to include the hard drive serial number # # Change history +# 20201005 - MITRE update +# 20200518 - updated date output format # 20140326 - created # # References # Issues with WMI: http://www.techques.com/question/1-10989338/WMI-HDD-Serial-Number-Transposed # *command "wmic diskdrive get serialnumber" will return transposed info # -# Copyright 2014 QAR, LLC +# Copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package susclient; @@ -19,12 +21,13 @@ package susclient; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - category => "System Config", - version => 20140326); + MITRE => "", + category => "devices", + output => "report", + version => 20201005); + my $VERSION = getVersion(); -# Functions # sub getConfig {return %config} sub getHive {return $config{hive};} sub getVersion {return $config{version};} @@ -49,7 +52,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { @@ -61,21 +64,17 @@ sub pluginmain { ::rptMsg(sprintf "%-25s %-30s",$v->get_name(),$v->get_data()); } elsif ($v->get_name() eq "SusClientIdValidation") { - ::rptMsg("SusClientIdValidation"); -# probe($v->get_data()); -# ::rptMsg(""); my $sn = parseSN($v->get_data()); - ::rptMsg(" Serial Number: ".$sn); - + ::rptMsg("SusClientIdValidation - Serial Number: ".$sn); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If available, this value may be the HDD serial number."); } else {} - } } else { ::rptMsg($key_path." has no values\."); } - } else { ::rptMsg($key_path." not found."); @@ -90,67 +89,9 @@ sub parseSN { my $sz = unpack("C",substr($data,2,1)); $sn = substr($data,$offset,$sz); - $sn =~ s/\x00//g; - $sn =~ s/\x20//g; + $sn =~ s/\00//g; + $sn =~ s/\20//g; return $sn; } -#----------------------------------------------------------- -# probe() -# -# Code the uses printData() to insert a 'probe' into a specific -# location and display the data -# -# Input: binary data of arbitrary length -# Output: Nothing, no return value. Displays data to the console -#----------------------------------------------------------- -sub probe { - my $data = shift; - my @d = printData($data); - - foreach (0..(scalar(@d) - 1)) { - print $d[$_]."\n"; - } -} - -#----------------------------------------------------------- -# printData() -# subroutine used primarily for debugging; takes an arbitrary -# length of binary data, prints it out in hex editor-style -# format for easy debugging -#----------------------------------------------------------- -sub printData { - my $data = shift; - my $len = length($data); - - my @display = (); - - my $loop = $len/16; - $loop++ if ($len%16); - - foreach my $cnt (0..($loop - 1)) { -# How much is left? - my $left = $len - ($cnt * 16); - - my $n; - ($left < 16) ? ($n = $left) : ($n = 16); - - my $seg = substr($data,$cnt * 16,$n); - my $lhs = ""; - my $rhs = ""; - foreach my $i ($seg =~ m/./gs) { -# This loop is to process each character at a time. - $lhs .= sprintf(" %02X",ord($i)); - if ($i =~ m/[ -~]/) { - $rhs .= $i; - } - else { - $rhs .= "."; - } - } - $display[$cnt] = sprintf("0x%08X %-50s %s",$cnt,$lhs,$rhs); - } - return @display; -} - 1; diff --git a/thirdparty/rr-full/plugins/svc.pl b/thirdparty/rr-full/plugins/svc.pl deleted file mode 100644 index 909e1f6864a..00000000000 --- a/thirdparty/rr-full/plugins/svc.pl +++ /dev/null @@ -1,238 +0,0 @@ -#----------------------------------------------------------- -# svc.pl -# Plugin for Registry Ripper; Access System hive file to get the -# services, display short format (hence "svc", shortened version -# of service.pl plugin); outputs info in .csv format -# -# Change history -# 20131010 - added BackDoor.Kopdel checks -# 20130911 - rewrite; fixed issue with running in rip.exe, removed -# some of the more noisy alerts; added check for FailureActions -# 20130603 - added additional alert functionality -# 20130509 - added alertMsg() functionality, and several alerts -# 20081129 - created -# -# Ref: -# http://msdn.microsoft.com/en-us/library/aa394073(VS.85).aspx -# -# Analysis Tip: Several services keys have Parameters subkeys that point to -# the ServiceDll value; During intrusions, a service key may be added to -# the system's Registry; using this module, send the output to .csv format -# and sort on column B to get the names to line up -# -# Note: some checks/alerts borrowed from E. Schweinsberg's svc_plus.pl -# (bethlogic@gmail.com) -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package svc; -#use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20131010); - -sub getConfig{return %config} -sub getShortDescr { - return "Lists Services key contents by LastWrite time (CSV)"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my %obj = ("nt authority\\localservice" => 1, - "nt authority\\networkservice" => 1, - "localsystem" => 1); - -my %types = (0x001 => "Kernel driver", - 0x002 => "File system driver", - 0x004 => "Adapter", - 0x010 => "Own_Process", - 0x020 => "Share_Process", - 0x100 => "Interactive", - 0x110 => "Own_Process", - 0x120 => "Share_Process"); - -my %starts = (0x00 => "Boot Start", - 0x01 => "System Start", - 0x02 => "Auto Start", - 0x03 => "Manual", - 0x04 => "Disabled"); - -my $display = ""; -my $descr = ""; -my $start = ""; -my $image = ""; -my $dll = ""; -my $object = ""; -my $para = ""; - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching svc v.".$VERSION); - ::rptMsg("svc v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - my $s_path = $ccs."\\Services"; - my $svc; - my %svcs; - if ($svc = $root_key->get_subkey($s_path)) { -# Get all subkeys - my @subkeys = $svc->get_list_of_subkeys(); - if (scalar (@subkeys) > 0) { - foreach my $s (@subkeys) { - $name = $s->get_name(); - - eval { - $display = $s->get_value("DisplayName")->get_data(); -# take commas out of the display name, replace w/ semi-colons - $display =~ s/,/;/g; - }; - $display = "" if ($@); - - eval { - $type = $s->get_value("Type")->get_data(); - (exists $types{$type}) ? ($t = $types{$type}) : ($t = $type); - }; - if ($@) { - $type = ""; - $t = ""; - } - - eval { - $image = $s->get_value("ImagePath")->get_data(); -# if (($type == 0x01 || $type == 0x02) && ($image ne "")) { -# ::alertMsg("ALERT: svc: ".$name." Driver does not end in \.sys\.") unless ($image =~ m/\.sys$/); -# } -# alertCheckPath($image); -# alertCheckADS($image); - }; - $image = "" if ($@); - - eval { - $descr = $s->get_value("Description")->get_data(); - }; - -# added 20130911 -# ref: http://technet.microsoft.com/en-us/library/cc742019.aspx - eval { - my $fa = $s->get_value("FailureAction")->get_data(); - ::alertMsg("ALERT: Service ".$name." has FailureAction value: ".$fa); - }; - - my $st = ""; - eval { - $start = $s->get_value("Start")->get_data(); - (exists $starts{$start}) ? ($st = $starts{$start}) : ($st = $start); - }; - if ($@) { - $start = ""; - $st = ""; - } - -# added 20131010 - Backdoor.Kopdel check - eval { - my $ep = $s->get_value("ErrorPointer")->get_data(); - ::alertMsg("Alert: svc: ".$name." has ErrorPointer value: ".$ep); - }; -# added 20131010 - Backdoor.Kopdel check - eval { - my $eh = $s->get_value("ErrorHandle")->get_data(); - ::alertMsg("Alert: svc: ".$name." has ErrorHandle value: ".$eh); - }; -# WOW64 check added 20131108 - eval { - my $w = $s->get_value("WOW64")->get_data(); - ::alertMsg("Alert: svc: ".$name." has a WOW64 value: ".$w); - }; - - eval { - $object = $s->get_value("ObjectName")->get_data(); - }; - $object = "" if ($@); - - my $str = $name."\|".$display."\|".$image."\|".$t."\|".$st."\|".$object."\|".$descr; - push(@{$svcs{$s->get_timestamp()}},$str) unless ($str eq ""); -# Get ServiceDll value, if there is one - eval { - $para = $s->get_subkey("Parameters"); - $dll = $para->get_value("ServiceDll")->get_data(); - - ::alertMsg("ALERT: svc: ".$name." ServiceDll does not end in \.dll\.") unless ($dll =~ m/\.dll$/); - - alertCheckPath($dll); - alertCheckADS($dll); - - my $str = $name."\\Parameters\|\|".$dll."\|\|\|"; - push(@{$svcs{$para->get_timestamp()}},$str); - }; - - } - ::rptMsg("Time,Name,DisplayName,ImagePath/ServiceDll,Type,Start,ObjectName"); - foreach my $t (reverse sort {$a <=> $b} keys %svcs) { - foreach my $item (@{$svcs{$t}}) { - my ($n,$d,$i,$t2,$s,$o,$d2) = split(/\|/,$item,7); -# ::rptMsg($t.",".$n.",".$d.",".$i.",".$t2.",".$s.",".$o); - ::rptMsg(gmtime($t)." Z,".$n.",".$d.",".$i.",".$t2.",".$s.",".$o.",".$d2); - } - } - } - else { - ::rptMsg($s_path." has no subkeys."); - } - } - else { - ::rptMsg($s_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - - -#----------------------------------------------------------- -# alertCheckPath() -#----------------------------------------------------------- -sub alertCheckPath { - my $path = shift; - my $lcpath = $path; - $lcpath =~ tr/[A-Z]/[a-z]/; - my @alerts = ("recycle","globalroot","temp","system volume information","appdata", - "application data"); - - foreach my $a (@alerts) { - if (grep(/$a/,$lcpath)) { - ::alertMsg("ALERT: svc: ".$a." found in path: ".$path); - } - } -} - -#----------------------------------------------------------- -# alertCheckADS() -#----------------------------------------------------------- -sub alertCheckADS { - my $path = shift; - my @list = split(/\\/,$path); - my $last = $list[scalar(@list) - 1]; -# ::alertMsg("ALERT: svc: Poss. ADS found in path: ".$path) if grep(/:/,$last); -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/svc_plus.pl b/thirdparty/rr-full/plugins/svc_plus.pl deleted file mode 100644 index 5b68cc51648..00000000000 --- a/thirdparty/rr-full/plugins/svc_plus.pl +++ /dev/null @@ -1,182 +0,0 @@ -#----------------------------------------------------------- -# svc_plus.pl -# Plugin for Registry Ripper; Access System hive file to get the -# services, display short format (hence "svc", shortened version -# of service.pl plugin) -# -# Change history -# 20080610 [hca] % created -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# Author Elizabeth schweinsberg bethlogic@gmail.com -# based on svc2.pl copyright 2008 H. Carvey -#----------------------------------------------------------- -package svc_plus; -#use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20120625); - -sub getConfig{return %config} -sub getShortDescr { - return "Lists services/drivers in Services key by LastWrite times in a short format with warnings for type mismatches; ^^^^ Indicates non-standard Type, <<<< Indicates Start mismatch for Driver, **** Indicates ObjectName mismatch for Driver, >>>> Indicates Start mismatch for Service, ++++ Indicates nonstandard ObjectName for Service."; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -# Reference for types and start types: -# http://msdn.microsoft.com/en-us/library/aa394420(VS.85).aspx -my %types = (0x001 => "Kernel driver", - 0x002 => "File system driver", - 0x010 => "Own_Process", - 0x020 => "Share_Process", - 0x100 => "Interactive"); - -my %starts = (0x00 => "Boot Start", - 0x01 => "System Start", - 0x02 => "Auto Start", - 0x03 => "Manual", - 0x04 => "Disabled"); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching svc_plus v.".$VERSION); - ::rptMsg("svc_plus v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - my $s_path = $ccs."\\Services"; - my $svc; - my %svcs; - if ($svc = $root_key->get_subkey($s_path)) { - ::rptMsg($s_path); - ::rptMsg(getShortDescr()); - ::rptMsg(""); - # Get all subkeys and sort based on LastWrite times - my @subkeys = $svc->get_list_of_subkeys(); - if (scalar (@subkeys) > 0) { - foreach my $s (@subkeys) { - - my $type; - eval { - $type = $s->get_value("Type")->get_data(); - }; - - $name = $s->get_name(); - my $display; - eval { - $display = $s->get_value("DisplayName")->get_data(); - # take commas out of the display name, replace w/ semi-colons - $display =~ s/,/;/g; - }; - - my $image; - eval { - $image = $s->get_value("ImagePath")->get_data(); - }; - - my $start; - eval { - $start = $s->get_value("Start")->get_data(); - }; - - my $object; - eval { - $object = $s->get_value("ObjectName")->get_data(); - }; - # Check for the proper start for each type - if ($type == 0x001 || $type == 0x002) { - if ($start == 0x002) { - $start = "<<<<".$starts{$start}; - } - else { - if (exists $starts{$start}) { - $start = $starts{$start}; - } - } - # Drivers should not have an object - if ($object ne "") { - $object = "++++".$object; - } - } - if ($type == 0x010 || $type == 0x020 || $type == 0x100) { - if ($start == 0x000 || $start == 0x001) { - $start = ">>>>".$starts{$start} - } - else { - if (exists $starts{$start}) { - $start = $starts{$start}; - } - } - # Services MUST have an ObjectName, and if it's not one of these 3, check it out - @list = ("nt authority\\localservice", "nt authority\\networkservice", "localsystem"); - if (grep {"$_" eq lc($object)} @list ) { - } - else { - $object = "****".$object; - } - } - - if (exists $types{$type}) { - $type = $types{$types}; - } - else { - $type = "^^^^".$type; - } - my $str = $name."\|".$display."\|".$image."\|".$type."\|".$start."\|".$object; - push(@{$svcs{$s->get_timestamp()}},$str) unless ($str eq ""); - # Get ServiceDll value if there is one - eval { - my $para = $s->get_subkey("Parameters"); - my $dll = $para->get_value("ServiceDll")->get_data(); - my $str = $name."\\Parameters\|\|".$dll."\|\|\|"; - push(@{$svcs{$para->get_timestamp()}},$str); - }; - } - - foreach my $t (reverse sort {$a <=> $b} keys %svcs) { - foreach my $item (@{$svcs{$t}}) { - my ($n,$d,$i,$t2,$s,$o) = split(/\|/,$item,6); - ::rptMsg(gmtime($t)."Z".",".$n.",".$d.",".$i.",".$t2.",".$s.",".$o); - } - } - - } - else { - ::rptMsg($s_path." has no subkeys."); - ::logMsg("Error: ".$s_path." has no subkeys."); - } - } - else { - ::rptMsg($s_path." not found."); - ::logMsg($s_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} - -1; diff --git a/thirdparty/rr-full/plugins/svc_tln.pl b/thirdparty/rr-full/plugins/svc_tln.pl deleted file mode 100644 index bd678a932a0..00000000000 --- a/thirdparty/rr-full/plugins/svc_tln.pl +++ /dev/null @@ -1,204 +0,0 @@ -#----------------------------------------------------------- -# svc_tln.pl -# Gets services information, only outputs alerts/warnings in TLN format -# (regtime.pl gets the key LastWrite times; svc_tln.pl generates alerts -# or warnings based on the values) -# -# Change history -# 20130911 - updated IAW svc.pl -# 20130509 - created, based on svc.pl -# -# Ref: -# http://msdn.microsoft.com/en-us/library/aa394073(VS.85).aspx -# -# Analysis Tip: Several services keys have Parameters subkeys that point to -# the ServiceDll value; During intrusions, a service key may be added to -# the system's Registry; using this module, send the output to .csv format -# and sort on column B to get the names to line up -# -# Note: some checks/alerts borrowed from E. Schweinsberg's svc_plus.pl -# (bethlogic@gmail.com) -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package svc_tln; -#use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20130911); - -sub getConfig{return %config} -sub getShortDescr { - return "Lists Services key contents by LastWrite time (CSV)"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -my %obj = ("nt authority\\localservice" => 1, - "nt authority\\networkservice" => 1, - "localsystem" => 1); - -my %types = (0x001 => "Kernel driver", - 0x002 => "File system driver", - 0x004 => "Adapter", - 0x010 => "Own_Process", - 0x020 => "Share_Process", - 0x100 => "Interactive", - 0x110 => "Own_Process", - 0x120 => "Share_Process"); - -my %starts = (0x00 => "Boot Start", - 0x01 => "System Start", - 0x02 => "Auto Start", - 0x03 => "Manual", - 0x04 => "Disabled"); - -my @alerts = ("recycle","globalroot","temp","system volume information","appdata", - "application data"); - -my $display = ""; -my $start = ""; -my $image = ""; -my $dll = ""; -my $object = ""; -my $para = ""; - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching svc_tln v.".$VERSION); -# ::rptMsg("svc_tln v.".$VERSION); # banner -# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - my $s_path = $ccs."\\Services"; - my $svc; - my %svcs; - if ($svc = $root_key->get_subkey($s_path)) { -# ::rptMsg($s_path); -# ::rptMsg(getShortDescr()); -# ::rptMsg(""); -# Get all subkeys and sort based on LastWrite times - my @subkeys = $svc->get_list_of_subkeys(); - if (scalar (@subkeys) > 0) { - foreach my $s (@subkeys) { - $name = $s->get_name(); - my $lw = $s->get_timestamp(); - - eval { - $display = $s->get_value("DisplayName")->get_data(); -# take commas out of the display name, replace w/ semi-colons - $display =~ s/,/;/g; - }; - $display = "" if ($@); - - eval { - $t = $s->get_value("Type")->get_data(); - (exists $types{$t}) ? ($type = $types{$t}) : ($type = $t); - }; - $type = "" if ($@); - - eval { - $image = $s->get_value("ImagePath")->get_data(); - my $lcimage = $image; - $lcimage =~ tr/[A-Z]/[a-z]/; - if (($t == 0x01 || $t == 0x02) && ($lcimage ne "")) { - ::alertMsg($lw."|ALERT|||svc_tln: ".$name." Driver does not end in \.sys: ".$image) unless ($lcimage =~ m/\.sys$/); - } - $image = "" if ($@); - - foreach my $a (@alerts) { - ::alertMsg($lw."|ALERT|||svc_tln: ".$a." found in path: ".$image) if (grep(/$a/,$lcimage)); - } - - my @list = split(/\\/,$image); - my $last = scalar(@list) - 1; - ::alertMsg($lw."|ALERT|||svc_tln: Poss. ADS in path: ".$image) if (grep(/:/,$list[$last])); - - }; - -# if (($t == 0x01 || $t == 0x02) && ($image ne "")) { -# my $lcimage = $image; -# $lcimage =~ tr/[A-Z]/[a-z]/; -# ::alertMsg($lw."|ALERT|||svc_tln: ".$name." Driver not in system32\\drivers folder: ".$image) unless (grep(/system32\\drivers/,$lcimage)); -# } - -# added 20130911 -# ref: http://technet.microsoft.com/en-us/library/cc742019.aspx - eval { - my $fa = $s->get_value("FailureAction")->get_data(); - ::alertMsg($lw."|ALERT|||svc_tln: Service ".$name." has FailureAction value: ".$fa); - }; - - eval { - my $st = $s->get_value("Start")->get_data(); - (exists $starts{$st}) ? ($start = $starts{$st}) : ($start = $st); - }; - $start = "" if ($@); - - eval { - $object = $s->get_value("ObjectName")->get_data(); - my $lcobj = $object; - $lcobj =~ tr/[A-Z]/[a-z]/; - ::alertMsg($lw."|ALERT|||svc_tln: ".$name." Unknown ObjectName: ".$object) unless (exists $obj{$lcobj}); - ::alertMsg($lw."|ALERT|||svc_tln: ".$name." Driver with ObjectName: ".$object) if (($type == 0x01 || $type == 0x02) && ($object ne "")); - }; - - my $str = $name."\|".$display."\|".$image."\|".$type."\|".$start."\|".$object; - push(@{$svcs{$s->get_timestamp()}},$str) unless ($str eq ""); -# Get ServiceDll value if there is one - eval { - $para = $s->get_subkey("Parameters"); - $dll = $para->get_value("ServiceDll")->get_data(); - my $lcdll = $dll; - $lcdll =~ tr/[A-Z]/[a-z]/; - ::alertMsg($p_lw."|ALERT|||svc_tln: ".$name." ServiceDll does not end in \.dll\.") unless ($lcdll =~ m/\.dll$/); - - foreach my $a (@alerts) { - my $lcdll = $dll; - $lcdll =~ tr/[A-Z]/[a-z]/; - ::alertMsg($lw."|ALERT|||svc_tln: ".$a." found in path: ".$dll) if (grep(/$a/,$lcdll)); - } - - my @list = split(/\\/,$dll); - my $last = scalar(@list) - 1; - ::alertMsg($lw."|ALERT|||svc_tln: Poss. ADS in path: ".$dll) if (grep(/:/,$list[$last])); - - my $str = $name."\\Parameters\|\|".$dll."\|\|\|"; - push(@{$svcs{$para->get_timestamp()}},$str); - }; - - } - } - else { - ::rptMsg($s_path." has no subkeys."); - } - } - else { - ::rptMsg($s_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/svcdll.pl b/thirdparty/rr-full/plugins/svcdll.pl deleted file mode 100644 index 37a2386f5fc..00000000000 --- a/thirdparty/rr-full/plugins/svcdll.pl +++ /dev/null @@ -1,156 +0,0 @@ -#----------------------------------------------------------- -# svcdll.pl -# -# Change history -# 20131010 - added checks for Derusbi, hcdloader malware -# - ServiceDll value ends in .dat -# - ServiceDll with no path -# 20130603 - added alert functionality -# 20091104 - created -# -# Ref: -# http://msdn.microsoft.com/en-us/library/aa394073(VS.85).aspx -# -# Analysis Tip: Several services keys have Parameters subkeys that point to -# the ServiceDll value; During intrusions, a service key may be added to -# the system's Registry; this module provides a quick look, displaying the -# Service names (in malware, sometimes random) and the ServiceDll value, -# sorted based on the LastWrite time of the \Parameters subkey. -# -# copyright 2009 H. Carvey -#----------------------------------------------------------- -package svcdll; -use strict; - -my %config = (hive => "System", - hasShortDescr => 1, - category => "autostart", - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20130603); - -sub getConfig{return %config} -sub getShortDescr { - return "Lists Services keys with ServiceDll values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my ($dll, $name); -#my %types = (0x001 => "Kernel driver", -# 0x002 => "File system driver", -# 0x004 => "Adapter", -# 0x010 => "Own_Process", -# 0x020 => "Share_Process", -# 0x100 => "Interactive"); - -#my %starts = (0x00 => "Boot Start", -# 0x01 => "System Start", -# 0x02 => "Auto Start", -# 0x03 => "Manual", -# 0x04 => "Disabled"); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching svcdll v.".$VERSION); - ::rptMsg("svcdll v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; -# First thing to do is get the ControlSet00x marked current...this is -# going to be used over and over again in plugins that access the system -# file - my $current; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - my $ccs = "ControlSet00".$current; - my $s_path = $ccs."\\Services"; - my $svc; - my %svcs; - if ($svc = $root_key->get_subkey($s_path)) { - -# Get all subkeys and sort based on LastWrite times - my @subkeys = $svc->get_list_of_subkeys(); - if (scalar (@subkeys) > 0) { - foreach my $s (@subkeys) { - $name = $s->get_name(); - - eval { - $dll = $s->get_subkey("Parameters")->get_value("ServiceDll")->get_data(); - my $str = $name." -> ".$dll; - push(@{$svcs{$s->get_timestamp()}},$str) unless ($str eq ""); - }; - } - - foreach my $t (reverse sort {$a <=> $b} keys %svcs) { - ::rptMsg(gmtime($t)."Z"); - foreach my $item (@{$svcs{$t}}) { - ::rptMsg(" ".$item); - - alertCheckPath($item); - alertCheckADS($item); - } - ::rptMsg(""); - } - } - else { - ::rptMsg($s_path." has no subkeys."); - } - } - else { - ::rptMsg($s_path." not found."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -#----------------------------------------------------------- -# alertCheckPath() -#----------------------------------------------------------- -sub alertCheckPath { - my $path = shift; - my $lcpath = $path; - $lcpath =~ tr/[A-Z]/[a-z]/; - - my @alerts = ("recycle","globalroot","temp","system volume information","appdata", - "application data","wbem"); - - foreach my $a (@alerts) { - if (grep(/$a/,$path)) { - ::alertMsg("ALERT: svcdll: ".$a." found in path: ".$path); - } - } - - if ($lcpath =~ m/\.dat$/) { - ::alertMsg("ALERT: svcdll: Possible Derusbi infection: ".$path); - } - - if ($lcpath =~ m/\raswmi\.dll$/) { - ::alertMsg("ALERT: svcdll: Possible hcdloader infection: ".$path); - } - - my @list = split(/\\/,$path); - if (scalar(@list) < 3) { - ::alertMsg("ALERT: svcdll: Relative path detected: ".$path); - } -} - -#----------------------------------------------------------- -# alertCheckADS() -#----------------------------------------------------------- -sub alertCheckADS { - my $path = shift; - my @list = split(/\\/,$path); - my $last = $list[scalar(@list) - 1]; - ::alertMsg("ALERT: svcdll: Poss. ADS found in path: ".$path) if grep(/:/,$last); -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/svchost.pl b/thirdparty/rr-full/plugins/svchost.pl deleted file mode 100644 index 694205b36ea..00000000000 --- a/thirdparty/rr-full/plugins/svchost.pl +++ /dev/null @@ -1,76 +0,0 @@ -#----------------------------------------------------------- -# svchost -# Plugin to get data from Security Center keys -# -# Change History: -# 20100322 - created -# -# References: -# http://support.microsoft.com/kb/314056 -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package svchost; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100322); - -sub getConfig{return %config} -sub getShortDescr { - return "Get entries from SvcHost key"; -} -sub getDescr{} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my $infected = 0; - ::logMsg("Launching svchost v.".$VERSION); - ::rptMsg("svchost v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = 'Microsoft\Windows NT\CurrentVersion\SvcHost'; - my $key; - ::rptMsg("svchost"); - ::rptMsg(""); - - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my @data = $v->get_data(); - my $d; - if (scalar(@data) > 1) { - $d = join(',',@data); - } - else { - $d = $data[0]; - } - my $str = sprintf "%-15s %-55s",$v->get_name(),$d; - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - ::rptMsg(""); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/symlink.pl b/thirdparty/rr-full/plugins/symlink.pl new file mode 100644 index 00000000000..b31d2a401d1 --- /dev/null +++ b/thirdparty/rr-full/plugins/symlink.pl @@ -0,0 +1,99 @@ +#----------------------------------------------------------- +# symlink.pl +# +# +# Change history: +# 20220613 - created +# +# References: +# https://www.microsoft.com/security/blog/2022/06/13/the-many-lives-of-blackcat-ransomware/ +# https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.FileSys::SymlinkEvaluation +# +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package symlink; +use strict; + +my %config = (hive => "software,system", + category => "defense evasion", + MITRE => "T1562\.001", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220613); + +sub getConfig{return %config} + +sub getShortDescr { + return "Check NTFS Symlink settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching symlink v.".$VERSION); + ::rptMsg("symlink v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key = (); + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } +# Set paths + my $key_path = (); + if ($hive_guess eq "software") { + $key_path = "Policies\\Microsoft\\Windows\\Filesystems\\NTFS"; + } + elsif ($hive_guess eq "system") { + my $ccs = ::getCCS($root_key); + $key_path = $ccs."\\Control\\FileSystem"; + } + else {} + + if ($key = $root_key->get_subkey($key_path)) { +# ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my @values = ("SymlinkLocalToLocalEvaluation", + "SymlinkLocalToRemoteEvaluation", + "SymlinkRemoteToRemoteEvaluation", + "SymlinkRemoteToLocalEvaluation"); + + foreach my $v (@values) { + eval { + my $t = $key->get_value($v)->get_data(); + ::rptMsg(sprintf "%-35s %-2d",$v,$t); + }; + } + } + else { + ::rptMsg($key_path." key not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: A setting of \"1\" indicates that the evaluation is performed. The BlackCat ransomware was observed"); + ::rptMsg("setting the R2L and R2R evaluations to \"1\" via fsutil."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.microsoft.com/security/blog/2022/06/13/the-many-lives-of-blackcat-ransomware/"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/syscache b/thirdparty/rr-full/plugins/syscache index 004687da42e..f3c8a3cb6e3 100644 --- a/thirdparty/rr-full/plugins/syscache +++ b/thirdparty/rr-full/plugins/syscache @@ -1,2 +1,3 @@ syscache syscache_csv +syscache_tln diff --git a/thirdparty/rr-full/plugins/syscache.pl b/thirdparty/rr-full/plugins/syscache.pl index 08a5971bdc1..89123a786c9 100644 --- a/thirdparty/rr-full/plugins/syscache.pl +++ b/thirdparty/rr-full/plugins/syscache.pl @@ -2,12 +2,14 @@ # syscache.pl # # Change history +# 20201005 - MITRE update +# 20200515 - updated date output format # 20181209 - created # # References # https://github.com/libyal/winreg-kb/blob/master/documentation/SysCache.asciidoc # -# Copyright (c) 2018 QAR, LLC +# Copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package syscache; @@ -17,9 +19,11 @@ package syscache; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - category => "program execution", - version => 20181209); + MITRE => "", + category => "syscache", + output => "report", + version => 20201005); + my $VERSION = getVersion(); # Functions # @@ -62,7 +66,7 @@ sub processKey { my $key = shift; my $lw = $key->get_timestamp(); - ::rptMsg("LastWrite: ".gmtime($lw)." Z"); + ::rptMsg("LastWrite: ".::format8601Date($lw)."Z"); eval { my ($f1,$f2,$seq) = unpack("Vvv",$key->get_value("_FileId_")->get_data()); @@ -83,7 +87,7 @@ sub processKey { eval { my ($u1,$u2) = unpack("VV",$key->get_value("_UsnJournalId_")->get_data()); my $usn = ::getTime($u1,$u2); - ::rptMsg(" USN Journal ID = ".gmtime($usn)." Z"); + ::rptMsg(" USN Journal ID = ".::format8601Date($usn)."Z"); }; diff --git a/thirdparty/rr-full/plugins/syscache_csv.pl b/thirdparty/rr-full/plugins/syscache_csv.pl index b5534cc7850..028bc062603 100644 --- a/thirdparty/rr-full/plugins/syscache_csv.pl +++ b/thirdparty/rr-full/plugins/syscache_csv.pl @@ -2,13 +2,15 @@ # syscache_csv.pl # # Change history +# 20201005 - MITRE update +# 20200515 - updated date output format # 20190425 - csv output added # 20181209 - original plugin created # # References # https://github.com/libyal/winreg-kb/blob/master/documentation/SysCache.asciidoc # -# Copyright (c) 2018 QAR, LLC +# Copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package syscache_csv; @@ -18,9 +20,11 @@ package syscache_csv; hasShortDescr => 0, hasDescr => 0, hasRefs => 0, - osmask => 22, - category => "program execution", - version => 20190425); + MITRE => "", + category => "syscache", + output => "csv", + version => 20201005); + my $VERSION = getVersion(); # Functions # @@ -28,7 +32,9 @@ package syscache_csv; sub getHive {return $config{hive};} sub getVersion {return $config{version};} sub getDescr {} -sub getShortDescr {} +sub getShortDescr { + return "Parse SysCache\.hve file (CSV output)"; +} sub getRefs {} sub pluginmain { @@ -62,7 +68,7 @@ sub processKey { my @str = (); my $lw = $key->get_timestamp(); # ::rptMsg("LastWrite: ".gmtime($lw)." Z"); - push(@str,gmtime($lw)." UTC"); + push(@str,::format8601Date($lw)."Z"); eval { my ($f1,$f2,$seq) = unpack("Vvv",$key->get_value("_FileId_")->get_data()); diff --git a/thirdparty/rr-full/plugins/syscache_tln.pl b/thirdparty/rr-full/plugins/syscache_tln.pl index be03f97785f..d46bf2cf12a 100644 --- a/thirdparty/rr-full/plugins/syscache_tln.pl +++ b/thirdparty/rr-full/plugins/syscache_tln.pl @@ -2,13 +2,14 @@ # syscache_tln.pl # # Change history +# 20201005 - MITRE update # 20190516 - tln output added # 20181209 - original plugin created # # References # https://github.com/libyal/winreg-kb/blob/master/documentation/SysCache.asciidoc # -# Copyright (c) 2019 QAR, LLC +# Copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package syscache_tln; @@ -18,17 +19,20 @@ package syscache_tln; hasShortDescr => 0, hasDescr => 0, hasRefs => 0, - osmask => 22, - category => "program execution", - version => 20190516); + MITRE => "", + category => "syscache", + output => "tln", + version => 20201005); + my $VERSION = getVersion(); -# Functions # sub getConfig {return %config} sub getHive {return $config{hive};} sub getVersion {return $config{version};} sub getDescr {} -sub getShortDescr {} +sub getShortDescr { + return "Parse SysCache\.hve file (TLN output)"; +} sub getRefs {} sub pluginmain { diff --git a/thirdparty/rr-full/plugins/sysinternals.pl b/thirdparty/rr-full/plugins/sysinternals.pl index 1273b94dd94..b94904adde0 100644 --- a/thirdparty/rr-full/plugins/sysinternals.pl +++ b/thirdparty/rr-full/plugins/sysinternals.pl @@ -3,12 +3,15 @@ # # # Change history -# 20120608- created +# 20220824 - updated to check for global flag +# 20201005 - MITRE update +# 20200511 - updated date output format +# 20120608 - created # # References -# +# https://twitter.com/leonzandman/status/1561736801953382400 # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2022 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package sysinternals; @@ -18,8 +21,10 @@ package sysinternals; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080324); + MITRE => "T1204", + category => "program execution", + output => "report", + version => 20220824); sub getConfig{return %config} sub getShortDescr { @@ -36,6 +41,9 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching sysinternals v.".$VERSION); + ::rptMsg("sysinternals v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -44,11 +52,20 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("SysInternals"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + +# added 20220824 + ::rptMsg(""); + eval { + my $e = $key->get_value("EulaAccepted")->get_data(); + ::rptMsg("Global EulaAccepted value: ".$e); + ::rptMsg(""); + }; + my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())." (UTC)]"); + ::rptMsg($s->get_name()." [".::format8601Date($s->get_timestamp())."Z]"); my $eula; eval { diff --git a/thirdparty/rr-full/plugins/sysinternals_tln.pl b/thirdparty/rr-full/plugins/sysinternals_tln.pl index 7570ca854ed..27d5f0d181c 100644 --- a/thirdparty/rr-full/plugins/sysinternals_tln.pl +++ b/thirdparty/rr-full/plugins/sysinternals_tln.pl @@ -3,6 +3,7 @@ # # # Change history +# 20201005 - MITRE update # 20120608- created # # References @@ -18,8 +19,10 @@ package sysinternals_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20080324); + MITRE => "T1204", + category => "program execution", + output => "tln", + version => 20201005); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/system b/thirdparty/rr-full/plugins/system index 37c447961ad..966a6287787 100644 --- a/thirdparty/rr-full/plugins/system +++ b/thirdparty/rr-full/plugins/system @@ -1,73 +1,109 @@ -angelfire +allow_upgrade appcertdlls appcompatcache -auditfail +appcompatcache_json +appcompatcache_tln +appenvironment +autodialdll +automount backuprestore bam bam_tln -btconfig +bthenum bthport -comfoo +bthport_tln +codepage +coinstallers compname crashcontrol +cred +cred_tln dafupnp -ddm +databasepath +defenderautologger +defrag devclass -diag_sr +deviceguard +disable445 disablelastaccess +disableremotescm dllsearch -dnschanger -eventlog -eventlogs -fw_config -hibernate -ide +environment +fsdepends +fvestats +guestauth imagedev -kbdcrash -legacy -lsa_packages +ips +kdc +labconfig +locale +lsa macaddr +minint mountdev mountdev2 netlogon -netsvcs -network -nic +networkproviders +networkproviderservices +networksetup2 nic2 -nic_mst2 -nolmhash +ntds pagefile pending -phdet +perf +portproxy prefetch +printer_settings +printmon +printmon_tln +printnightmare +printprocessors processor_architecture productpolicy -producttype profiler -rdpnla +railrunonce +rdplockout rdpport -regin +regback remoteaccess routes -safeboot +scsi +scsi_tln securityproviders services shares shimcache +shimcache_tln shutdown -shutdowncount +smb +sourcerouting source_os -stillimage -svc -svcdll -svc_plus +spooler +symlink +systemindex termcert termserv +tgt +timeproviders timezone +tls +trailersupport +triggerinfo +tsutilities usb usbdevices +usbdevices_tln usbstor usbstor2 -usbstor3 +usbstor_tln +usn +utilities +volsnap +volsnap_tln +vss +wdfilter +wpbt wpdbusenum -xpedition +wpdbusenum_tln +wtg +zerologon diff --git a/thirdparty/rr-full/plugins/systemindex.pl b/thirdparty/rr-full/plugins/systemindex.pl index 38ca4e57389..dac0874e787 100644 --- a/thirdparty/rr-full/plugins/systemindex.pl +++ b/thirdparty/rr-full/plugins/systemindex.pl @@ -5,9 +5,11 @@ # and after seeing what was in it, I just wrote up a plugin # # History: +# 20201005 - MITRE update +# 20200518 - updated date output format # 20120716 - created # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package systemindex; @@ -17,8 +19,10 @@ package systemindex; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120716); + MITRE => "", + category => "user activity", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -48,7 +52,7 @@ sub pluginmain { foreach my $s (@subkeys) { my $name = $s->get_name(); my $ts = $s->get_timestamp(); - ::rptMsg($name." - LastWrite: ".gmtime($ts)); + ::rptMsg($name." - LastWrite time: ".::format8601Date($ts)."Z"); my $path; eval { diff --git a/thirdparty/rr-full/plugins/tasks.pl b/thirdparty/rr-full/plugins/tasks.pl new file mode 100644 index 00000000000..b304219c279 --- /dev/null +++ b/thirdparty/rr-full/plugins/tasks.pl @@ -0,0 +1,259 @@ +#----------------------------------------------------------- +# tasks.pl +# I wrote this plugin to assist with parsing and identifying Scheduled Tasks used by +# threat actors during engagements; in all of the observed cases, these tasks appear within +# the root of the TaskCache\Tree key +# +# Change history +# 20221222 - updated to map UUID in "Actions" to CLSID\InprocServer32 "(Default)" value +# 20200831 - added check for 0x03 at beginning of Actions +# 20200825 - Unicode updates +# 20200730 - MITRE ATT&CK updates +# 20200718 - parse Actions data +# 20200427 - updated output date format +# 20200416 - created +# +# Refs: +# https://github.com/libyal/winreg-kb/blob/master/documentation/Task%20Scheduler%20Keys.asciidoc +# http://port139.hatenablog.com/entry/2019/01/12/095429 +# https://blog.codsec.com/posts/malware/gracewire_adventure/ +# +# https://attack.mitre.org/techniques/T1053/005/ +# +# Copyright (c) 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package tasks; +use strict; + +my %config = (hive => "Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + MITRE => "T1053\.005", + category => "persistence", + version => 20221222); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr {return "Checks TaskCache\\Tasks subkeys";} +sub getRefs {} + +my $root_key = (); + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching tasks v.".$VERSION); + ::rptMsg("tasks v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + $root_key = $reg->get_root_key; + my $key; + my $key_path = 'Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tasks'; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + + eval { + my $path = $s->get_value("Path")->get_data(); + ::rptMsg("Path: ".$path); + }; + + eval { + my $uri = $s->get_value("URI")->get_data(); + ::rptMsg("URI : ".$uri); + }; + + eval { + my $data = $s->get_value("DynamicInfo")->get_data(); + if (length($data) == 0x1c) { + my ($t1,$t2) = processDynamicInfo28($data); +# Registration Time associated with TaskScheduler event IDs 106/140 + if ($t1 != 0) { + ::rptMsg("Task Reg Time : ".::format8601Date($t1)."Z"); + } +# In some cases, the second time stamp seems to be associated with the task +# failing to run for some reason; Last Launch/Last Launch Attempt Time? + if ($t2 != 0) { + ::rptMsg("Task Last Run : ".::format8601Date($t2)."Z"); + } + } + elsif (length($data) == 0x24) { + my ($t1,$t2,$t3) = processDynamicInfo36($data); + if ($t1 != 0) { + ::rptMsg("Task Reg Time : ".::format8601Date($t1)."Z"); + } + if ($t2 != 0) { + ::rptMsg("Task Last Run : ".::format8601Date($t2)."Z"); + } + if ($t3 != 0) { + ::rptMsg("Task Completed: ".::format8601Date($t3)."Z"); + } + } + else { + ::rptMsg("DynamicInfo data length = ".length($data)." bytes"); + } + }; + + eval { + my $actions = $s->get_value("Actions")->get_data(); + my $data = unpack("v",substr($actions,0,2)); + if ($data == 0x03) { + my ($user,$act) = parseActions($actions); + ::rptMsg("User : ".$user); + + my $a = (split(/\s/,$act))[0]; + + $a =~ tr/a-z/A-Z/; + + if ($a =~ m/^{/ && $a =~ m/}$/) { + $act .= " (".mapUUID($a).")"; + } + + ::rptMsg("Action : ".$act); + } + }; + ::rptMsg(""); + } + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: This plugin extracts information about Scheduled Tasks from the Software hive\. Where the task points to a UUID, "); + ::rptMsg("the plugin attempts to map to that CLSID subkey, and retrieve the \"(Default)\" value, which in many instances is a DLL. This is"); + ::rptMsg("done, because in Q4 2022, malware (i\.e\., \"FlawedGrace\") was observed modifying the UUID for the RegIdleBackup task, to point"); + ::rptMsg("to a malicious DLL. By default, the RegIdleBackup task UUID is {ca767aa8-9157-4604-b64b-40747123d5f2}, which points to regidle.dll."); + ::rptMsg(""); + ::rptMsg("It is possible that other, similar tasks could be similarly abused in the future."); + ::rptMsg(""); + ::rptMsg("Ref: https://blog.codsec.com/posts/malware/gracewire_adventure/"); +} + +sub processDynamicInfo28 { +#win7 + my $data = shift; + my ($t0,$t1) = unpack("VV",substr($data,4,8)); + my ($d0,$d1) = unpack("VV",substr($data,12,8)); + return(::getTime($t0,$t1),::getTime($d0,$d1)); +} + +sub processDynamicInfo36 { +#win10 + my $data = shift; + my ($t0,$t1) = unpack("VV",substr($data,4,8)); + my ($d0,$d1) = unpack("VV",substr($data,12,8)); + my ($r0,$r1) = unpack("VV",substr($data,0x1c,8)); + return(::getTime($t0,$t1),::getTime($d0,$d1),::getTime($r0,$r1)); +} + +#----------------------------------------------------------- +# parseActions() +# Parses Actions data +#----------------------------------------------------------- +sub parseActions { + my $data = shift; + my $len = length($data); + + my $cur = unpack("V",substr($data,2,4)); + my $user = substr($data,6,$cur); + $user = ::getUnicodeStr($user); +# $user =~ s/\00//g; + + my $action = ""; + my $tag = unpack("v",substr($data,6 + $cur,2)); + + if ($tag == 0x7777) { + my $g = substr($data,6 + $cur + 2 + 4,16); + $action = parseGUID($g); + + if ($len - (6 + $cur + 2 + 4 + 16) > 4) { + my $i = unpack("V", substr($data,6 + $cur + 2 + 4 + 16,4)); + my $r = substr($data,6 + $cur + 2 + 4 + 16 + 4,$i); + $r = ::getUnicodeStr($r); +# $r =~ s/\00//g; + $action .= " ".$r; + } + + } + elsif ($tag == 0x6666) { + my $l = unpack("V",substr($data,6 + $cur + 2 + 4,4)); + my $n = substr($data,6 + $cur + 2 + 4 + 4,$l); + $n = ::getUnicodeStr($n); +# $n =~ s/\00//g; + $action = $n; + + if ($len - (6 + $cur + 2 + 4 + 4 + $l) > 4) { + my $h = unpack("V",substr($data,6 + $cur + 2 + 4 + 4 + $l,4)); + my $j = substr($data,6 + $cur + 2 + 4 + 4 + $l + 4,$h); + $j = ::getUnicodeStr($j); +# $j =~ s/\00//g; + $action .= " ".$j; + } + + } + else {} + + return($user,$action); +} + +#----------------------------------------------------------- +# parseGUID() +# Takes 16 bytes of binary data, returns a string formatted +# as an MS GUID. +#----------------------------------------------------------- +sub parseGUID { + my $data = shift; + my $d1 = unpack("V",substr($data,0,4)); + my $d2 = unpack("v",substr($data,4,2)); + my $d3 = unpack("v",substr($data,6,2)); + my $d4 = unpack("H*",substr($data,8,2)); + my $d5 = unpack("H*",substr($data,10,6)); + my $guid = sprintf "{%08x-%04x-%04x-$d4-$d5}",$d1,$d2,$d3; + +# if (exists $cp_guids{$guid}) { +# return "CLSID_".$cp_guids{$guid}; +# } +# elsif (exists $folder_types{$guid}) { +# return "CLSID_".$folder_types{$guid}; +# } +# else { +# return $guid; +# } + return $guid; +} + +#----------------------------------------------------------- +# mapUUID() +# Map Action UUID to CLSID\InprocServer32 value +#----------------------------------------------------------- +sub mapUUID { + my $uuid = shift; + my $key = (); + my $rtn = (); + + if ($key = $root_key->get_subkey("Classes\\CLSID\\".$uuid."\\InprocServer32")) { + eval { + my $dll = $key->get_value("")->get_data(); + $rtn = $dll; + }; + $rtn = "UUID Default value not found" if ($@); + } + else { + $rtn = "UUID not found"; + } + return $rtn; +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/tasks_tln.pl b/thirdparty/rr-full/plugins/tasks_tln.pl new file mode 100644 index 00000000000..949981e7c5f --- /dev/null +++ b/thirdparty/rr-full/plugins/tasks_tln.pl @@ -0,0 +1,221 @@ +#----------------------------------------------------------- +# tasks_tln.pl +# +# Change history +# 20200831 - added check for 0x03 at beginning of Actions +# 20200825 - Unicode updates +# 20200730 - MITRE ATT&CK updates +# 20200718 - created from tasks.pl +# +# Refs: +# https://github.com/libyal/winreg-kb/blob/master/documentation/Task%20Scheduler%20Keys.asciidoc +# http://port139.hatenablog.com/entry/2019/01/12/095429 +# +# https://attack.mitre.org/techniques/T1053/005/ +# +# Copyright (c) 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package tasks_tln; +use strict; + +my %config = (hive => "Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1053\.005", + category => "persistence", + output => "tln", + version => 20200831); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr {return "Checks TaskCache\\Tasks subkeys";} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + +# ::logMsg("Launching tasks v.".$VERSION); +# ::rptMsg("tasks v.".$VERSION); +# ::rptMsg("(".$config{hive}.") ".getShortDescr()); +# ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = 'Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tasks'; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + + my $path = ""; + eval { + $path = $s->get_value("Path")->get_data(); + }; + + my $uri = ""; + eval { + $uri = $s->get_value("URI")->get_data(); + }; + + my $user = ""; + my $act = ""; + + eval { + my $actions = $s->get_value("Actions")->get_data(); + my $data = unpack("v",substr($actions,0,2)); + if ($data == 0x03) { + ($user,$act) = parseActions($actions); + } + }; + + if ($act ne "") { + $path .= " Actions: ".$act; + } + + my $reg_time = ""; + my $last_run = ""; + my $completed = ""; + + eval { + my $data = $s->get_value("DynamicInfo")->get_data(); + if (length($data) == 0x1c) { + my ($t1,$t2) = processDynamicInfo28($data); +# Registration Time associated with TaskScheduler event IDs 106/140 + if ($t1 != 0) { + $reg_time = $t1; + ::rptMsg($t1."|REG||".$user."|[T1053\.005] Task Reg Time ".$path); + } +# In some cases, the second time stamp seems to be associated with the task +# failing to run for some reason; Last Launch/Last Launch Attempt Time? + if ($t2 != 0) { + $last_run = $t2; + ::rptMsg($t2."|REG||".$user."|[T1053\.005] Task Last Run ".$path); + } + } + elsif (length($data) == 0x24) { + my ($t1,$t2,$t3) = processDynamicInfo36($data); + if ($t1 != 0) { + $reg_time = $t1; + ::rptMsg($t1."|REG||".$user."|[T1053\.005] Task Reg Time ".$path); + } + if ($t2 != 0) { + $last_run = $t2; + ::rptMsg($t2."|REG||".$user."|[T1053\.005] Task Last Run ".$path); + } + if ($t3 != 0) { + $completed = $t3; + ::rptMsg($t3."|REG||".$user."|[T1053\.005] Task Completed ".$path); + } + } + else { +# ::rptMsg("DynamicInfo data length = ".length($data)." bytes"); + } + }; + } + } + } + else { +# ::rptMsg($key_path." not found."); + } +} + +sub processDynamicInfo28 { +#win7 + my $data = shift; + my ($t0,$t1) = unpack("VV",substr($data,4,8)); + my ($d0,$d1) = unpack("VV",substr($data,12,8)); + return(::getTime($t0,$t1),::getTime($d0,$d1)); +} + +sub processDynamicInfo36 { +#win10 + my $data = shift; + my ($t0,$t1) = unpack("VV",substr($data,4,8)); + my ($d0,$d1) = unpack("VV",substr($data,12,8)); + my ($r0,$r1) = unpack("VV",substr($data,0x1c,8)); + return(::getTime($t0,$t1),::getTime($d0,$d1),::getTime($r0,$r1)); +} + +#----------------------------------------------------------- +# parseActions() +# Parses Actions data +#----------------------------------------------------------- +sub parseActions { + my $data = shift; + my $len = length($data); + + my $cur = unpack("V",substr($data,2,4)); + my $user = substr($data,6,$cur); + $user = ::getUnicodeStr($user); +# $user =~ s/\00//g; + + my $action = ""; + my $tag = unpack("v",substr($data,6 + $cur,2)); + + if ($tag == 0x7777) { + my $g = substr($data,6 + $cur + 2 + 4,16); + $action = parseGUID($g); + + if ($len - (6 + $cur + 2 + 4 + 16) > 4) { + my $i = unpack("V", substr($data,6 + $cur + 2 + 4 + 16,4)); + my $r = substr($data,6 + $cur + 2 + 4 + 16 + 4,$i); + $r = ::getUnicodeStr($r); +# $r =~ s/\00//g; + $action .= " ".$r; + } + + } + elsif ($tag == 0x6666) { + my $l = unpack("V",substr($data,6 + $cur + 2 + 4,4)); + my $n = substr($data,6 + $cur + 2 + 4 + 4,$l); + $n = ::getUnicodeStr($n); +# $n =~ s/\00//g; + $action = $n; + + if ($len - (6 + $cur + 2 + 4 + 4 + $l) > 4) { + my $h = unpack("V",substr($data,6 + $cur + 2 + 4 + 4 + $l,4)); + my $j = substr($data,6 + $cur + 2 + 4 + 4 + $l + 4,$h); + $j = ::getUnicodeStr($j); +# $j =~ s/\00//g; + $action .= " ".$j; + } + + } + else {} + + return($user,$action); +} + +#----------------------------------------------------------- +# parseGUID() +# Takes 16 bytes of binary data, returns a string formatted +# as an MS GUID. +#----------------------------------------------------------- +sub parseGUID { + my $data = shift; + my $d1 = unpack("V",substr($data,0,4)); + my $d2 = unpack("v",substr($data,4,2)); + my $d3 = unpack("v",substr($data,6,2)); + my $d4 = unpack("H*",substr($data,8,2)); + my $d5 = unpack("H*",substr($data,10,6)); + my $guid = sprintf "{%08x-%04x-%04x-$d4-$d5}",$d1,$d2,$d3; + +# if (exists $cp_guids{$guid}) { +# return "CLSID_".$cp_guids{$guid}; +# } +# elsif (exists $folder_types{$guid}) { +# return "CLSID_".$folder_types{$guid}; +# } +# else { +# return $guid; +# } + return $guid; +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/teamviewer.pl b/thirdparty/rr-full/plugins/teamviewer.pl index 0061253988f..2ed3d759af1 100644 --- a/thirdparty/rr-full/plugins/teamviewer.pl +++ b/thirdparty/rr-full/plugins/teamviewer.pl @@ -1,119 +1,73 @@ #----------------------------------------------------------- # teamviewer.pl -# Checks for installation/removal of TeamViewer -# -# Change history -# 20150627 +# Get TeamViewer Always_Online setting # -# References +# Change history: +# 20211025 - created # -# Copyright (c) Jimmy Tuong +# References: +# https://twitter.com/lkarlslund/status/1450413959106945030 +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey #----------------------------------------------------------- package teamviewer; use strict; -# Declarations # -my %config = (hive => "SOFTWARE", +my %config = (hive => "software", + category => "persistence", + MITRE => "", + osmask => 22, hasShortDescr => 1, hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20150627); -my $VERSION = getVersion(); + hasRefs => 0, + output => "report", + version => 20211025); + +sub getConfig{return %config} -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} sub getShortDescr { - return "Checks for installation/removal of TeamViewer"; + return "Get Teamviewer Always_Online setting"; } +sub getDescr{} sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +my $VERSION = getVersion(); sub pluginmain { - - # Declarations # my $class = shift; my $hive = shift; - - # Initialize # ::logMsg("Launching teamviewer v.".$VERSION); - ::rptMsg("teamviewer v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + ::rptMsg("teamviewer v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - my $key; - + my @paths = ("TeamViewer", "Wow6432Node\\TeamViewer"); - my @vals = ("InstallationDate","Version","InstallationDirectory","InstallationRev","LastUpdateCheck","LastKeepalivePerformance","LastMACUsed","ClientID"); - - my $key2; - my $installDir = "InstallationDirectory"; - my @paths2 = ("Microsoft\\Windows\\CurrentVersion\\Uninstall\\TeamViewer", - "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TeamViewer"); - - my $array_count = 0; - foreach my $key_path (@paths) { - # If TeamViewer path exists + my $key; if ($key = $root_key->get_subkey($key_path)) { - # Return # plugin name, registry key and last modified date # - ::rptMsg("[*] Found TeamViewer artifacts on the system:"); - ::rptMsg("Key Path : ".$key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); ::rptMsg(""); - - # Extract various installation value - if (scalar(@vals) > 0) { - ::rptMsg("\[VALUE : DATA\]"); - foreach my $v (@vals) { - if ($key->get_value($v)) { - if ($v eq "LastUpdateCheck"){ - ::rptMsg($key->get_value($v)->get_name()." : ".$key->get_value($v)->get_data()." -> ".gmtime($key->get_value($v)->get_data())." (UTC)"); - } else { - ::rptMsg($key->get_value($v)->get_name()." : ".$key->get_value($v)->get_data()); - } - } else { - ::rptMsg($v." not found."); - } - } - - # Error key value is null - } else { - ::rptMsg($key_path." has no values."); - } - - # Checks to see if TeamViewer is removed + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); eval { - - $root_key->get_subkey($paths2[$array_count])->get_name(); # 1st evaluation - $root_key->get_subkey($key_path)->get_value($installDir)->get_name(); # 2nd evaluation + my $a = $key->get_value("Always_Online")->get_data(); + ::rptMsg("Always_Online value : ".$a); + ::rptMsg("1 - TeamViewer is set to autostart"); }; - - if ($@) { - ::rptMsg(""); - ::rptMsg(""); - ::rptMsg("[*] Identified TeamViewer has been removed from the system. Hence, the below key and value do not exist means TeamViewer is not on the system:"); - ::rptMsg($paths2[$array_count]." key not found"); - ::rptMsg($installDir." value not found"); - } - - last; - - # Error key isn't there - } else { - ::rptMsg($key_path." not found."); } - - $array_count ++; - + else { +# ::rptMsg($key_path." not found."); + } } - ::rptMsg(""); + ::rptMsg("Analysis Tip: If the Always_Online value is set to 1, TeamViewer is set to autostart"); +# ::rptMsg(""); +# ::rptMsg(""); } - -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/telemetrycontroller.pl b/thirdparty/rr-full/plugins/telemetrycontroller.pl new file mode 100644 index 00000000000..3b50fba1c13 --- /dev/null +++ b/thirdparty/rr-full/plugins/telemetrycontroller.pl @@ -0,0 +1,118 @@ +#----------------------------------------------------------- +# telemetrycontroller.pl +# +# Change history +# 20220707 - added Scythe blog reference +# 20220328 - updated with values beneath TelemetryController key +# 20200609 - content created in appcompatflags.pl plugin +# +# References +# https://www.trustedsec.com/blog/abusing-windows-telemetry-for-persistence/ +# https://www.scythe.io/library/windows-telemetry-persistence +# +# https://attack.mitre.org/techniques/T1546/ +# +# Copyright 2022 Quantum Analytics Research, LLC +# H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package telemetrycontroller; +use strict; + +my %config = (hive => "Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + category => "persistence", + output => "report", + version => 20220707); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Checks for persistence beneath the TelemetryController subkey"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching telemetrycontroller v.".$VERSION); + ::rptMsg("telemetrycontroller v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + + my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\TelemetryController"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $t = $key->get_value("RunsBlocked")->get_data(); + ::rptMsg(sprintf "%-20s %-20s","RunsBlocked",$t); + }; + + eval { + my ($t0,$t1) = unpack("VV",$key->get_value("LastMaintenanceRun")->get_data()); + if ($t0 > 0 && $t1 > 0) { + ::rptMsg(sprintf "%-20s %-20s","LastMaintenanceRun",::format8601Date(::getTime($t0,$t1))."Z"); + } + else { + ::rptMsg(sprintf "%-20s %-20s","LastMaintenanceRun","0"); + } + }; + + eval { + my ($t0,$t1) = unpack("VV",$key->get_value("LastNormalRun")->get_data()); + if ($t0 > 0 && $t1 > 0) { + ::rptMsg(sprintf "%-20s %-20s","LastNormalRun",::format8601Date(::getTime($t0,$t1))."Z"); + } + else { + ::rptMsg(sprintf "%-20s %-20s","LastNormalRun","0"); + } + }; + + eval { + my ($t0,$t1) = unpack("VV",$key->get_value("LastOobeRun")->get_data()); + if ($t0 > 0 && $t1 > 0) { + ::rptMsg(sprintf "%-20s %-20s","LastOobeRun",::format8601Date(::getTime($t0,$t1))."Z"); + } + else { + ::rptMsg(sprintf "%-20s %-20s","LastOobeRun","0"); + } + }; + + ::rptMsg(""); + my @subkeys = $key->get_list_of_subkeys($key); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg($key_path."\\".$s->get_name()); + ::rptMsg(sprintf "%-15s %-20s","LastWrite time",::format8601Date($s->get_timestamp())."Z"); + + my @vals = $s->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + next if ($v->get_name() eq ""); + ::rptMsg(sprintf "%-15s %-20s",$v->get_name(),$v->get_data()); + } + } + ::rptMsg(""); + } + } + } + ::rptMsg("Analysis Tip: TelemetryController subkeys can be used for persistence."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.trustedsec.com/blog/abusing-windows-telemetry-for-persistence/"); + ::rptMsg("Ref: https://www.scythe.io/library/windows-telemetry-persistence"); +} + +1; diff --git a/thirdparty/rr-full/plugins/termcert.pl b/thirdparty/rr-full/plugins/termcert.pl index 2b97a188117..0704e0491f9 100644 --- a/thirdparty/rr-full/plugins/termcert.pl +++ b/thirdparty/rr-full/plugins/termcert.pl @@ -3,9 +3,12 @@ # Plugin for Registry Ripper; # # Change history +# 20201005 - MITRE update +# 20200526 - updated date output format # 20110316 - created # -# copyright 2011 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package termcert; use strict; @@ -14,8 +17,10 @@ package termcert; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20110316); + MITRE => "", + category => "config", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -32,8 +37,8 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching termcert v.".$VERSION); - ::rptMsg("termcert v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("termcert v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -49,7 +54,7 @@ sub pluginmain { my $ts; if ($ts = $root_key->get_subkey($ts_path)) { ::rptMsg($ts_path); - ::rptMsg("LastWrite Time ".gmtime($ts->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($ts->get_timestamp())."Z"); ::rptMsg(""); my $cert; diff --git a/thirdparty/rr-full/plugins/termserv.pl b/thirdparty/rr-full/plugins/termserv.pl index 5a92ab7decc..a5ea52305ab 100644 --- a/thirdparty/rr-full/plugins/termserv.pl +++ b/thirdparty/rr-full/plugins/termserv.pl @@ -1,8 +1,13 @@ #----------------------------------------------------------- # termserv.pl -# Plugin for Registry Ripper; +# Get values related to Terminal Server/Services, from System or Software hive # # Change history +# 20220908 - updated UserAuthentication info +# 20201005 - MITRE update +# 20200506 - updated date output format +# 20200318 - added check for port number +# 20190925 - added fSingleSessionPerUser check # 20190527 - Added checks in Software hive # 20160224 - added SysProcs info # 20131007 - updated with Sticky Keys info @@ -23,7 +28,8 @@ # TSEnabled value - http://support.microsoft.com/kb/222992 # TSUserEnabled value - http://support.microsoft.com/kb/238965 # -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package termserv; use strict; @@ -32,8 +38,10 @@ package termserv; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20190527); + MITRE => "T1133", + category => "persistence", + output => "report", + version => 20220908); sub getConfig{return %config} sub getShortDescr { @@ -50,6 +58,9 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching termserv v.".$VERSION); + ::rptMsg("termserv v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; # First thing to do is get the ControlSet00x marked current...this is @@ -65,7 +76,7 @@ sub pluginmain { my $ts; if ($ts = $root_key->get_subkey($ts_path)) { ::rptMsg($ts_path); - ::rptMsg("LastWrite Time ".gmtime($ts->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($ts->get_timestamp())."Z"); ::rptMsg(""); my $ver; @@ -124,12 +135,20 @@ sub pluginmain { my $help; eval { $help = $ts->get_value("fAllowToGetHelp")->get_data(); - ::rptMsg(" fAllowToGetHelp = ".$user); - ::rptMsg(" 1 = Users can request assistance from friend or a "); - ::rptMsg(" support professional."); + ::rptMsg(" fAllowToGetHelp = ".$help); + ::rptMsg(" 1 = Users can request assistance from friend or a support professional."); ::rptMsg(" Ref: http://www.pctools.com/guides/registry/detail/1213/"); }; - +# Added 20190925 +# fSingleSessionPerUser +# + my $single; + eval { + $single = $ts->get_value("fSingleSessionPerUser")->get_data(); + ::rptMsg(" fSingleSessionPerUser = ".$single); + ::rptMsg(""); + }; + ::rptMsg("AutoStart Locations"); eval { my $start = $ts->get_subkey("Wds\\rdpwd")->get_value("StartupPrograms")->get_data(); @@ -164,7 +183,7 @@ sub pluginmain { my @vals = $sys->get_list_of_values(); if ((scalar @vals) > 0) { ::rptMsg("SysProcs key values"); - ::rptMsg("LastWrite: ".gmtime($sys->get_timestamp())." Z"); + ::rptMsg("LastWrite: ".::format8601Date($sys->get_timestamp())."Z"); foreach my $v (@vals) { ::rptMsg(" ".$v->get_name()." - ".$v->get_data()); } @@ -172,7 +191,10 @@ sub pluginmain { }; # Sticky Keys info, added 20131007 -# ref: http://www.room362.com/blog/2012/5/25/sticky-keys-and-utilman-against-nla.html +# ref: http://www.room362.com/blog/2012/5/25/sticky-keys-and-utilman-against-nla.html +# +# added 20220908: +# https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-terminalservices-rdp-winstationextensions-userauthentication eval { ::rptMsg(""); my $ua = $ts->get_subkey("WinStations\\RDP-Tcp")->get_value("UserAuthentication")->get_data(); @@ -181,9 +203,21 @@ sub pluginmain { ::rptMsg("Analysis Tip: If the UserAuthentication value is 0, the system may be"); ::rptMsg("susceptible to a priv escalation exploitation via Sticky Keys. See:"); ::rptMsg("http://www.room362.com/blog/2012/5/25/sticky-keys-and-utilman-against-nla.html"); + ::rptMsg(""); + ::rptMsg("Also, if \"UserAuthentication\" = 1, then Network-Layer Auth (NLA) is enabled, and logins via"); + ::rptMsg("RDP may appear as type 3, rather than type 10."); }; ::rptMsg("UserAuthentication value not found\.") if ($@); - + +# Added 20200318 + eval { + ::rptMsg(""); + my $ua = $ts->get_subkey("WinStations\\RDP-Tcp")->get_value("PortNumber")->get_data(); + ::rptMsg("WinStations\\RDP-Tcp key"); + ::rptMsg(" PortNumber: ".$ua); + ::rptMsg("Analysis Tip: By default, the port number is 3389, but can be changed."); + }; + } else { ::rptMsg($ts_path." not found."); @@ -198,7 +232,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { my $lw = $key->get_timestamp(); ::rptMsg($key_path); - ::rptMsg("LastWrite: ".gmtime($lw)." Z"); + ::rptMsg("LastWrite: ".::format8601Date($lw)."Z"); ::rptMsg(""); # Note: fDenyTSConnections was added here because I've seen it used by bad actors, @@ -223,6 +257,9 @@ sub pluginmain { my $user = $key->get_value("UserAuthentication")->get_data(); ::rptMsg("UserAuthentication value = ".$user); }; +# Added: +# http://woshub.com/remote-desktop-session-time-limit/ + } else { diff --git a/thirdparty/rr-full/plugins/test.pl b/thirdparty/rr-full/plugins/test.pl new file mode 100644 index 00000000000..4ff0642d467 --- /dev/null +++ b/thirdparty/rr-full/plugins/test.pl @@ -0,0 +1,57 @@ +#----------------------------------------------------------- +# test +# +#----------------------------------------------------------- +package test; +use strict; + +my %config = (hive => "all", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + category => "config", + version => 20230811); + +sub getConfig{return %config} +sub getShortDescr { + return "Check for Yara EXE"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $path_to_yara = ".\\yara64\.exe"; +my $path_to_rule_file = ".\\test\.yar"; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching test v.".$VERSION); + ::rptMsg("test v.".$VERSION); +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); +# my $reg = Parse::Win32Registry->new($hive); +# my $root_key = $reg->get_root_key; + + if (-f $path_to_yara) { + + eval { + my $output = qx/$path_to_yara -s $path_to_rule_file $path_to_yara/; + if ($output eq "" || $output eq "\n") { + + } + else { + ::rptMsg($output); + } + + }; + + } + +} + + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/tgt.pl b/thirdparty/rr-full/plugins/tgt.pl new file mode 100644 index 00000000000..14a29d7478e --- /dev/null +++ b/thirdparty/rr-full/plugins/tgt.pl @@ -0,0 +1,81 @@ +#----------------------------------------------------------- +# tgt.pl +# +# Change history +# 20201116 - created +# +# Reference: +# https://twitter.com/CyberRaiju/status/1243536444309807105 +# +# https://attack.mitre.org/techniques/T1558/003/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package tgt; + +my %config = (hive => "System", + hasShortDescr => 1, + category => "credential access", + hasDescr => 0, + hasRefs => 0, + MITRE => "T1558\.003", + output => "report", + version => 20201116); + +sub getConfig{return %config} +sub getShortDescr { + return "Lists allowtgtsessionkey value data"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching tgt v.".$VERSION); + ::rptMsg("tgt v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key(); +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $current; + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + $current = $key->get_value("Current")->get_data(); + my $ccs = "ControlSet00".$current; + + $key_path = $ccs.'\\Control\\LSA\\Kerberos\\Parameters'; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $admin = $key->get_value("allowtgtsessionkey")->get_data(); + ::rptMsg("allowtgtsessionkey value = ".$admin); + }; + ::rptMsg("allowtgtsessionkey value not found.") if ($@); + + ::rptMsg(""); + ::rptMsg("Analysis Tip:"); + ::rptMsg("- 0: The KerbRetrieveEncodedTicket will not include a session key that that allows this TGT to be used for login."); + ::rptMsg("- 1: Indicates that a session key should be returned with the TGT according to current behavior."); + ::rptMsg("Note: This approach is disabled with Windows 10 and Credential Guard."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/thispcpolicy.pl b/thirdparty/rr-full/plugins/thispcpolicy.pl new file mode 100644 index 00000000000..89ec06a5d46 --- /dev/null +++ b/thirdparty/rr-full/plugins/thispcpolicy.pl @@ -0,0 +1,84 @@ +#----------------------------------------------------------- +# thispcpolicy +# +# This value, when set to "Hide", allows the 'extra' folders in Explorer to +# be hidden. +# +# MITRE ATT&CK: https://attack.mitre.org/techniques/T1564/001/ +# +# Change history: +# 20200916 - MITRE updates +# 20200511 - updated date output format +# 20191002 - created +# +# Ref: +# https://twitter.com/craiglandis/status/1178476402942676992 +# https://www.askvg.com/tip-remove-6-extra-folders-from-windows-10-explorer-this-pc/ +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package thispcpolicy; +use strict; + +my %config = (hive => "Software", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1564\.001", + output => "report", + version => 20200916); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets ThisPCPolicy values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching thispcpolicy v.".$VERSION); + ::rptMsg("thispcpolicy v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my %guids = ("3D Objects" => "{31C0DD25-9439-4F12-BF41-7FF4EDA38722}", + "Pictures" => "{0ddd015d-b06c-45d5-8c4c-f59713854639}", + "Videos" => "{35286a68-3c57-41a1-bbb1-0eae73d76c95}", + "Downloads" => "{7d83ee9b-2244-4e70-b1f5-5393042af1e4}", + "Music" => "{a0c69a99-21c8-4671-8703-7934162fcf1d}", + "Desktop" => "{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}", + "Documents" => "{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}"); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + foreach my $g (keys %guids) { + my $key; + ::rptMsg($g." Folder"); + my $key_path = 'Microsoft\\Windows\\CurrentVersion\\Explorer\\FolderDescriptions\\'.$guids{$g}.'\\PropertyBag'; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + + my $policy; + eval { + $policy = $key->get_value("ThisPCPolicy")->get_data(); + ::rptMsg("ThisPCPolicy value = ".$policy); + }; + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/thostperms.pl b/thirdparty/rr-full/plugins/thostperms.pl new file mode 100644 index 00000000000..eac3088f8f5 --- /dev/null +++ b/thirdparty/rr-full/plugins/thostperms.pl @@ -0,0 +1,88 @@ +#----------------------------------------------------------- +# thostperms.pl +# Plugin for Registry Ripper +# Parse Adobe Reader MRU keys +# +# Change history +# 20201015 - created +# +# References +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package thostperms; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + category => "user activity", + MITRE => "", + output => "report", + version => 20201015); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets user's THostPerms value from Acrobat Reader TrustManager"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching thostperms v.".$VERSION); + ::rptMsg("thostperms v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + +# First, determine app version + my $version; + my $path = "Software\\Adobe\\Acrobat Reader"; + if (my $key = $root_key->get_subkey($path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + my $name = $s->get_name(); + if (defined($root_key->get_subkey($path."\\".$name."\\TrustManager"))) { + $version = $name; + } + } + } + } + + my $key_path = "Software\\Adobe\\Acrobat Reader\\".$version."\\TrustManager\\cDefaultLaunchURLPerms"; + my $key = ""; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + my $thost = (); + eval { + $thost = $key->get_value("tHostPerms")->get_data(); +# ::rptMsg("tHostPerms value = ".$thost); + ::rptMsg("tHostPerms values"); + my @vals = split(/\|/,$thost); + foreach my $i (0..(scalar(@vals) - 1)) { + if (substr($vals[$i],0,4) eq "file") { + $vals[$i] = $vals[$i].":".$vals[$i + 1]; + splice @vals,$i + 1, 1; + } + } + + foreach my $v (@vals) { + ::rptMsg(" ".$v); + } + + }; + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/thumbnail_cleanup.pl b/thirdparty/rr-full/plugins/thumbnail_cleanup.pl new file mode 100644 index 00000000000..201124c8798 --- /dev/null +++ b/thirdparty/rr-full/plugins/thumbnail_cleanup.pl @@ -0,0 +1,83 @@ +#----------------------------------------------------------- +# thumbnail_cleanup.pl +# +# +# Change history: +# 20210315 - created +# +# References: +# https://www.ghacks.net/2019/03/04/how-to-block-the-automatic-cleaning-of-windows-10s-thumbnail-cache/ +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, 2013 +#----------------------------------------------------------- +package thumbnail_cleanup; +use strict; + +my %config = (hive => "software", + category => "collection", + MITRE => "T1005", + osmask => 22, + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210315); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get Thumbnail Cache Autorun value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +my %comp; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching thumbnail_cleanup v.".$VERSION); + ::rptMsg("thumbnail_cleanup v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my @paths = ("Microsoft\\Windows\\CurrentVersion\\Explorer\\VolumeCaches\\Thumbnail Cache", + "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VolumeCaches\\Thumbnail Cache"); + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); +# ::rptMsg(""); + + eval { + my $a = $key->get_value("Autorun")->get_data(); + ::rptMsg("Autorun value: ".$a); + }; + + } + else { +# ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: As of the Fall Creators update to Windows 10, the OS performs a number of automatic maintenance tasks,"); + ::rptMsg("one of which is to automatically clear the Thumbnail Cache. A Registry setting impacts this functionality."); + ::rptMsg(""); + ::rptMsg("0 - Blocks maintenance task from deleting thumbnail cache"); + ::rptMsg("1 - Enables maintenance task to delete thumbnail cache"); + ::rptMsg("A value of \"3\" may indicate a pre-1909 build of Windows 10"); + ::rptMsg("Ref: https://www.ghacks.net/2019/03/04/how-to-block-the-automatic-cleaning-of-windows-10s-thumbnail-cache/"); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/thunderbirdinstalled.pl b/thirdparty/rr-full/plugins/thunderbirdinstalled.pl deleted file mode 100644 index 7c729951407..00000000000 --- a/thirdparty/rr-full/plugins/thunderbirdinstalled.pl +++ /dev/null @@ -1,92 +0,0 @@ -#----------------------------------------------------------- -# thunderbirdinstalled -# Shows install current status for Mozilla Thunderbird -# -# References -# https://www.thunderbird.net/en-US/ -# -# History: -# 20180712 - created -# -# Author: -# M. Jones, mictjon@gmail.com -#----------------------------------------------------------- -package thunderbirdinstalled; -use strict; - -my %config = (hive => "Software,NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20120524); - -sub getConfig{return %config} - -sub getShortDescr { - return "Shows install status of Thunderbird"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Mozilla" => - "https://www.thunderbird.net/en-US/"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching thunderbirdinstalled v.".$VERSION); - ::rptMsg("thunderbirdinstalled v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# used a list of values to address the need for parsing the App Paths key -# in the Wow6432Node key, if it exists. - my @paths = ("Microsoft\\Windows\\CurrentVersion\\App Paths\\thunderbird.exe", - "WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths\\thunderbird.exe"); - - foreach my $key_path (@paths) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("Thunderbird installed"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my %apps; - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - - my $name = $s->get_name(); - my $lastwrite = $s->get_timestamp(); - my $path; - eval { - $path = $s->get_value("")->get_data(); - }; - push(@{$apps{$lastwrite}},$name." - ".$path); - } - - foreach my $t (reverse sort {$a <=> $b} keys %apps) { - ::rptMsg(gmtime($t)." (UTC)"); - foreach my $item (@{$apps{$t}}) { - ::rptMsg(" $item"); - } - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - ::rptMsg(" Thunderbird not installed."); - } - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/timeproviders.pl b/thirdparty/rr-full/plugins/timeproviders.pl new file mode 100644 index 00000000000..90be0d576f3 --- /dev/null +++ b/thirdparty/rr-full/plugins/timeproviders.pl @@ -0,0 +1,76 @@ +#----------------------------------------------------------- +# timeproviders.pl +# +# History: +# 20230125 - created +# +# References: +# https://github.com/blackc03r/OSCP-Cheatsheets/blob/master/offensive-security/persistence/t1209-hijacking-time-providers.md +# https://attack.mitre.org/techniques/T1547/003/ +# +# copyright 2023 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package timeproviders; +use strict; + +my %config = (hive => "System", + output => "report", + category => "program execution", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1547\.003", + version => 20200813); + +sub getConfig{return %config} +sub getShortDescr { + return "Check time providers for hijacking"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching timeproviders v.".$VERSION); + ::rptMsg("timeproviders v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("Category: ".$config{category}." - ".$config{MITRE}); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + my @providers = ("NtpClient", "NtpServer"); + my $key; + foreach my $p (@providers) { + my $key_path = $ccs."\\Services\\W32Time\\TimeProviders\\".$p; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $n = $key->get_value("DllName")->get_data(); + ::rptMsg("DllName value: ".$n); + }; + + } + else { + ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Threat actors can register a malicious time provider by changing the \"DllName\" value. The value should"); + ::rptMsg("point to %systemroot%\\system32\\w32time\.dll\."); + ::rptMsg(""); + ::rptMsg("Ref: https://github.com/blackc03r/OSCP-Cheatsheets/blob/master/offensive-security/persistence/t1209-hijacking-time-providers.md"); + ::rptMsg("Ref: https://attack.mitre.org/techniques/T1547/003/"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/timezone.pl b/thirdparty/rr-full/plugins/timezone.pl index e45be3ec85f..a9f85418c78 100644 --- a/thirdparty/rr-full/plugins/timezone.pl +++ b/thirdparty/rr-full/plugins/timezone.pl @@ -4,6 +4,8 @@ # contents of the TimeZoneInformation key # # Change history +# 20201005 - MITRE update +# 20200518 - updated date output format # 20160318 - added display of TimeZoneKeyName value # 20130830 - updated # 20080324 - created @@ -14,7 +16,7 @@ # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx # # -# copyright 2013 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package timezone; @@ -24,8 +26,10 @@ package timezone; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20160318); + MITRE => "", + category => "config", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -60,7 +64,7 @@ sub pluginmain { if ($tz = $root_key->get_subkey($tz_path)) { ::rptMsg("TimeZoneInformation key"); ::rptMsg($tz_path); - ::rptMsg("LastWrite Time ".gmtime($tz->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($tz->get_timestamp())."Z"); my %tz_vals; my @vals = $tz->get_list_of_values(); if (scalar(@vals) > 0) { diff --git a/thirdparty/rr-full/plugins/tls.pl b/thirdparty/rr-full/plugins/tls.pl new file mode 100644 index 00000000000..3b8b3b2bf13 --- /dev/null +++ b/thirdparty/rr-full/plugins/tls.pl @@ -0,0 +1,74 @@ +#----------------------------------------------------------- +# tls.pl +# +# History: +# 20210122 - created +# +# References: +# https://www.aon.com/cyber-solutions/aon_cyber_labs/cyber-labs-blog-see-ya-in-s3/ +# https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls#configuring-schannel-protocols-in-the-windows-registry +# +# https://attack.mitre.org/techniques/T1562/001/ +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package tls; +use strict; + +my %config = (hive => "System", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562\.001", + output => "report", + version => 20210122); + +sub getConfig{return %config} +sub getShortDescr { + return "Check TLS settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching tls v.".$VERSION); + ::rptMsg("tls v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my @versions = ("1.1", "1.2"); + foreach my $v (@versions) { + my $key_path = $ccs."\\Control\\SecurityProviders\\SCHANNEL\\Protocols\\TLS ".$v."\\Client"; + my $key = (); + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + eval { + my $dis = $key->get_value("DisabledByDefault")->get_data(); + ::rptMsg("DisabledByDefault value = ".$dis); + }; + } + else { + ::rptMsg($key_path." not found."); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Disabling the TLS client settings serves to remove security settings on the client side."); +# ::rptMsg(""); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/tracing.pl b/thirdparty/rr-full/plugins/tracing.pl index 842e2ad8675..2dbad5a64e8 100644 --- a/thirdparty/rr-full/plugins/tracing.pl +++ b/thirdparty/rr-full/plugins/tracing.pl @@ -3,6 +3,8 @@ # # # History: +# 20200924 - MITRE update +# 20200511 - updated date output format # 20120509 - created # # References: @@ -10,7 +12,7 @@ # http://answers.microsoft.com/en-us/windows/forum/windows_7-system/ms-removal # -tool-malware-and-proxycheckexe/d0d6dc68-1ab0-4148-9501-374d80f0a064 # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package tracing; @@ -20,8 +22,10 @@ package tracing; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120509); + MITRE => "", + category => "program execution", + output => "report", + version => 20200924); sub getConfig{return %config} sub getShortDescr { @@ -53,7 +57,7 @@ sub pluginmain { next if (scalar(@subkeys) == 1); foreach my $s (@subkeys) { my $lw = $s->get_timestamp(); - my $t = gmtime($lw); + my $t = ::format8601Date($lw)."Z"; my $name = $s->get_name(); ::rptMsg(sprintf "%-25s %-50s",$t,$name); } diff --git a/thirdparty/rr-full/plugins/tracing_tln.pl b/thirdparty/rr-full/plugins/tracing_tln.pl index 0dc3e953011..d1d36f8f15e 100644 --- a/thirdparty/rr-full/plugins/tracing_tln.pl +++ b/thirdparty/rr-full/plugins/tracing_tln.pl @@ -3,6 +3,7 @@ # # # History: +# 20200924 - MITRE update # 20120608 - created # # References: @@ -10,7 +11,7 @@ # http://answers.microsoft.com/en-us/windows/forum/windows_7-system/ms-removal # -tool-malware-and-proxycheckexe/d0d6dc68-1ab0-4148-9501-374d80f0a064 # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package tracing_tln; @@ -20,8 +21,10 @@ package tracing_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120608); + MITRE => "", + category => "program execution", + output => "tln", + version => 20200924); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/trailersupport.pl b/thirdparty/rr-full/plugins/trailersupport.pl new file mode 100644 index 00000000000..6962895b6c9 --- /dev/null +++ b/thirdparty/rr-full/plugins/trailersupport.pl @@ -0,0 +1,69 @@ +#----------------------------------------------------------- +# trailersupport.pl +# +# History: +# 20220111 - created +# +# References: +# https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-21907 +# +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package trailersupport; +use strict; + +my %config = (hive => "System", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20220111); + +sub getConfig{return %config} +sub getShortDescr { + return "Check EnableTrailerSupport value (CVE-2022-21907)"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching trailersupport v.".$VERSION); + ::rptMsg("trailersupport v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs.'\\Services\\HTTP\\Parameters'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + eval { + my $cmd = $key->get_value("EnableTrailerSupport")->get_data(); + ::rptMsg($key_path."\\EnableTrailerSupport value = ".$cmd); + ::rptMsg(""); + ::rptMsg("1 - Enabled (system vulnerable)"); + }; + ::rptMsg("EnableTrailerSupport value not found\.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: MS's patch for CVE-2022-21907 indicates that the vulnerable condition is not enabled by"); + ::rptMsg("default\. Setting the EnableTrailerSupport value to \"1\" enables the vulnerable condition."); +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/trappoll.pl b/thirdparty/rr-full/plugins/trappoll.pl deleted file mode 100644 index 8af55ee5de9..00000000000 --- a/thirdparty/rr-full/plugins/trappoll.pl +++ /dev/null @@ -1,64 +0,0 @@ -#----------------------------------------------------------- -# trappoll.pl -# There are indications that the contents of this value may be associated -# with a number of different malware variants. -# -# History -# 20120305 - created -# -# References -# http://home.mcafee.com/VirusInfo/VirusProfile.aspx?key=903224#none -# -# copyright 2012, Quantum Analytics Research, LLC -#----------------------------------------------------------- -package trappoll; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20120305); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get TrapPollTimeMilliSecs value, if found"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my %clsid; - ::logMsg("Launching trappoll v.".$VERSION); - ::rptMsg("Launching trappoll v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\RFC1156Agent\\CurrentVersion\\Parameters"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - if ($key->get_value("TrapPollTimeMilliSecs")) { - my $val = $key->get_value("TrapPollTimeMilliSecs")->get_data(); - ::rptMsg(sprintf "TrapPollTimeMilliSecs = 0x%x (".$val.")", $val); - } - else { - ::rptMsg("Value not found."); - } - } - else { - ::rptMsg($key_path." key not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/triggerinfo.pl b/thirdparty/rr-full/plugins/triggerinfo.pl new file mode 100644 index 00000000000..73188e2de9f --- /dev/null +++ b/thirdparty/rr-full/plugins/triggerinfo.pl @@ -0,0 +1,127 @@ +#----------------------------------------------------------- +# triggerinfo.pl +# +# +# References: +# https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_trigger +# https://docs.microsoft.com/en-us/windows/win32/services/service-trigger-events +# +# Change history: +# 20201020 - created +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package triggerinfo; +use strict; + +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + category => "persistence", + output => "report", + version => 20201020); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks Services TriggerInfo settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching triggerinfo v.".$VERSION); + ::rptMsg("triggerinfo v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path; + my $key; + +# System Hive + my $ccs = ::getCCS($root_key); + + $key_path = $ccs."\\Services"; + if ($key = $root_key->get_subkey($key_path)){ + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + if (my $trig = $s->get_subkey("TriggerInfo")) { + ::rptMsg($s->get_name()); + processTriggerInfo($trig); + } + else { +# Service key does not have a TriggerInfo subkey + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Services can be configured to perform actions based on trigger events."); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_trigger"); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/windows/win32/services/service-trigger-events"); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +sub processTriggerInfo { + my $key = shift; + + my @subkeys = (); + if (@subkeys = $key->get_list_of_subkeys()) { + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg(" ".$s->get_name()); + + eval { + my $g = $s->get_value("GUID")->get_data(); + my $guid = ::parseGUID($g); + ::rptMsg(sprintf " %-10s %-50s","GUID",$guid); + }; + + eval { + my $action = $s->get_value("Action")->get_data(); + ::rptMsg(sprintf " %-10s %-50s","Action",$action); + }; + + eval { + my $type = $s->get_value("Type")->get_data(); + ::rptMsg(sprintf " %-10s %-50s","Type",$type); + }; + + eval { + my $type = $s->get_value("DataType0")->get_data(); +# ::rptMsg(sprintf " %-10s %-50s","DataType0",$type); + my $d = $s->get_value("Data0")->get_data(); + if ($type == 2) { + my $data = ::getUnicodeStr($d); + $data =~ s/\00/ /g; + ::rptMsg(sprintf " %-10s %-50s","Data0",$data); + } + elsif ($type == 1) { + my $data = join ' ', unpack '(H2)*',$d; + ::rptMsg(sprintf " %-10s %-50s","Data0",$data); + } + else {} + + }; + + } + ::rptMsg(""); + } + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/trustrecords.pl b/thirdparty/rr-full/plugins/trustrecords.pl deleted file mode 100644 index d9e7a400315..00000000000 --- a/thirdparty/rr-full/plugins/trustrecords.pl +++ /dev/null @@ -1,129 +0,0 @@ -#----------------------------------------------------------- -# trustrecords.pl -# List Office documents for which the user explicitly opted to accept bypassing -# the default security settings for the application -# -# Change history -# 20190626 - updated to more recent versions of Office -# 20160224 - modified per Mari's blog post -# 20120716 - created -# -# References -# 20190626 updates -# https://decentsecurity.com/block-office-macros -# https://gist.github.com/PSJoshi/749cf1733217d8791cf956574a3583a2 -# -# http://az4n6.blogspot.com/2016/02/more-on-trust-records-macros-and.html -# ForensicArtifacts.com posting by Andrew Case: -# http://forensicartifacts.com/2012/07/ntuser-trust-records/ -# http://archive.hack.lu/2010/Filiol-Office-Documents-New-Weapons-of-Cyberwarfare-slides.pdf -# -# copyright 2012 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package trustrecords; -use strict; - -my %config = (hive => "NTUSER\.DAT", - category => "User Activity", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20190626); - -sub getConfig{return %config} -sub getShortDescr { - return "Get user's MSOffice TrustRecords values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my $office_version; -my %vba = (1 => "Enable all macros", - 2 => "Disable all macros w/ notification", - 3 => "Disalbe all macros except dig. signed macros", - 4 => "Disalbe all macros w/o notification"); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching trustrecords v.".$VERSION); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - ::rptMsg("trustrecords v.".$VERSION); - ::rptMsg(""); -# First, let's find out which version of Office is installed - my @version; - my $key; - my $key_path = "Software\\Microsoft\\Office"; - if ($key = $root_key->get_subkey($key_path)) { - my @subkeys = $key->get_list_of_subkeys(); - foreach my $s (@subkeys) { - my $name = $s->get_name(); - push(@version,$name) if ($name =~ m/^\d/); - } - } -# Determine MSOffice version in use - my @v = reverse sort {$a<=>$b} @version; - foreach my $i (@v) { - eval { - if (my $o = $key->get_subkey($i."\\User Settings")) { - $office_version = $i; - } - }; - } - -# Now that we have the most recent version of Office installed, let's -# start looking at the various subkeys - my @apps = ("Word","PowerPoint","Excel","Access"); - my $key_path = "Software\\Microsoft\\Office\\".$office_version; - - foreach my $app (@apps) { - ::rptMsg("**".$app."**"); - ::rptMsg("-" x 10); - my $app_path = $key_path."\\".$app."\\Security"; - eval { - if (my $sec = $root_key->get_subkey($app_path)) { - ::rptMsg("Security key LastWrite: ".gmtime($sec->get_timestamp())." Z"); - my $w = $sec->get_value("VBAWarnings")->get_data(); - ::rptMsg("VBAWarnings = ".$vba{$w}); - ::rptMsg(""); - } - }; - -# Added 20190626 - eval { - if (my $sec = $root_key->get_subkey($app_path)) { - my $blk = $sec->get_value("blockcontentexecutionfrominternet")->get_data(); - ::rptMsg("blockcontentexecutionfrominternet = ".$blk); - ::rptMsg(""); - } - }; - -# Trusted Documents/Trust Records - $app_path = $key_path."\\".$app."\\Security\\Trusted Documents"; - if (my $app_key = $root_key->get_subkey($app_path)) { - if (my $trust = $app_key->get_subkey("TrustRecords")) { - my @vals = $trust->get_list_of_values(); - ::rptMsg("TrustRecords"); - foreach my $v (@vals) { - my $data = $v->get_data(); - my ($t0,$t1) = (unpack("VV",substr($data,0,8))); - my $t = ::getTime($t0,$t1); - ::rptMsg(gmtime($t)." Z : ".$v->get_name()); - - my $e = unpack("V",substr($data, length($data) - 4, 4)); - ::rptMsg("**Enable Content button clicked.") if ($e == 2147483647); - } - } - } - ::rptMsg(""); - - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/trustrecords_tln.pl b/thirdparty/rr-full/plugins/trustrecords_tln.pl deleted file mode 100644 index 747bc011ecb..00000000000 --- a/thirdparty/rr-full/plugins/trustrecords_tln.pl +++ /dev/null @@ -1,99 +0,0 @@ -#----------------------------------------------------------- -# trustrecords_tln.pl -# List Office documents for which the user explicitly opted to accept bypassing -# the default security settings for the application -# -# Change history -# 20160224 - modified per Mari's blog post -# 20120717 - created; modified from trustrecords.pl plugin -# -# References -# http://az4n6.blogspot.com/2016/02/more-on-trust-records-macros-and.html -# ForensicArtifacts.com posting by Andrew Case: -# http://forensicartifacts.com/2012/07/ntuser-trust-records/ -# http://archive.hack.lu/2010/Filiol-Office-Documents-New-Weapons-of-Cyberwarfare-slides.pdf -# -# copyright 2012 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package trustrecords_tln; -use strict; - -my %config = (hive => "NTUSER\.DAT", - category => "User Activity", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20160224); - -sub getConfig{return %config} -sub getShortDescr { - return "Get user's MSOffice TrustRecords values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); -my $office_version; - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching trustrecords_tln v.".$VERSION); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - -# ::rptMsg("trustrecords v.".$VERSION); -# First, let's find out which version of Office is installed - my @version; - my $key; - my $key_path = "Software\\Microsoft\\Office"; - if ($key = $root_key->get_subkey($key_path)) { - my @subkeys = $key->get_list_of_subkeys(); - foreach my $s (@subkeys) { - my $name = $s->get_name(); - push(@version,$name) if ($name =~ m/^\d/); - } - } - -# Determine MSOffice version in use - my @v = reverse sort {$a<=>$b} @version; - foreach my $i (@v) { - eval { - if (my $o = $key->get_subkey($i."\\User Settings")) { - $office_version = $i; - } - }; - } - ::rptMsg("Version: ".$office_version); -# Now that we have the most recent version of Office installed, let's -# start looking at the various subkeys - my @apps = ("Word","PowerPoint","Excel","Access"); - $key_path = "Software\\Microsoft\\Office\\".$office_version; - - foreach my $app (@apps) { - my $app_path = $key_path."\\".$app."\\Security\\Trusted Documents"; -# ::rptMsg($app); - if (my $app_key = $root_key->get_subkey($app_path)) { - - if (my $trust = $app_key->get_subkey("TrustRecords")) { - my @vals = $trust->get_list_of_values(); - - foreach my $v (@vals) { - my $data = $v->get_data(); - my ($t0,$t1) = (unpack("VV",substr($data,0,8))); - my $t = ::getTime($t0,$t1); - my $descr = "TrustRecords - ".$v->get_name(); - my $e = unpack("V",substr($data, length($data) - 4, 4)); - $descr = $descr." [Enable Content button clicked]" if ($e == 2147483647); - ::rptMsg($t."|REG|||".$descr); - } - } - } -# ::rptMsg(""); - } -} -1; diff --git a/thirdparty/rr-full/plugins/tsclient.pl b/thirdparty/rr-full/plugins/tsclient.pl index 923bf50fe48..d947f6730a4 100644 --- a/thirdparty/rr-full/plugins/tsclient.pl +++ b/thirdparty/rr-full/plugins/tsclient.pl @@ -3,14 +3,16 @@ # Plugin for Registry Ripper # # Change history +# 20200924 - MITRE update +# 20200518 - updated date output format # 20120827 - updated # 20080324 - created # # References # http://support.microsoft.com/kb/312169 # -# copyright 2012 -# Author: H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package tsclient; use strict; @@ -19,8 +21,10 @@ package tsclient; hasShortDescr => 0, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120827); + MITRE => "T1021\.001", + category => "lateral movement", + output => "report", + version => 20200924); sub getConfig{return %config} sub getShortDescr { @@ -38,7 +42,9 @@ sub pluginmain { my $ntuser = shift; ::logMsg("Launching tsclient v.".$VERSION); ::rptMsg("Launching tsclient v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -47,7 +53,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("TSClient"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { my %mrus; @@ -71,17 +77,18 @@ sub pluginmain { } ::rptMsg(""); - $key_path = 'Software\\Microsoft\\Terminal Server Client\\Servers'; + my $key_path = 'Software\\Microsoft\\Terminal Server Client\\Servers'; + my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { my $name = $s->get_name(); my $lw = $s->get_timestamp(); - ::rptMsg($name." LastWrite: ".gmtime($lw)); + ::rptMsg($name." LastWrite time: ".::format8601Date($lw)."Z"); my $hint; eval { $hint = $s->get_value("UsernameHint")->get_data(); @@ -99,4 +106,4 @@ sub pluginmain { } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/tsclient_tln.pl b/thirdparty/rr-full/plugins/tsclient_tln.pl index 4ce08803173..2158ccbfb60 100644 --- a/thirdparty/rr-full/plugins/tsclient_tln.pl +++ b/thirdparty/rr-full/plugins/tsclient_tln.pl @@ -3,6 +3,7 @@ # Plugin for Registry Ripper # # Change history +# 20200924 - MITRE update # 20120827 - updated; added "Servers" key check, translated to TLN output # 20080324 - created # @@ -19,8 +20,10 @@ package tsclient_tln; hasShortDescr => 0, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120827); + MITRE => "T1021\.001", + category => "lateral movement", + output => "tln", + version => 20200924); sub getConfig{return %config} sub getShortDescr { @@ -64,7 +67,8 @@ sub pluginmain { } ::rptMsg(""); - $key_path = 'Software\\Microsoft\\Terminal Server Client\\Servers'; + my $key_path = 'Software\\Microsoft\\Terminal Server Client\\Servers'; + my $key; if ($key = $root_key->get_subkey($key_path)) { # ::rptMsg($key_path); # ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); @@ -93,4 +97,4 @@ sub pluginmain { } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/tsutilities.pl b/thirdparty/rr-full/plugins/tsutilities.pl new file mode 100644 index 00000000000..be2751d7aa1 --- /dev/null +++ b/thirdparty/rr-full/plugins/tsutilities.pl @@ -0,0 +1,81 @@ +#----------------------------------------------------------- +# tsutilities.pl +# +# +# References: +# https://www.hexacorn.com/blog/2020/07/30/beyond-good-ol-run-key-part-125/ +# https://twitter.com/0gtweet/status/1213745922942930945 +# +# Change history: +# 20200806 - created +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package tsutilities; +use strict; + +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1547", + category => "persistence", + output => "report", + version => 20200806); + +sub getConfig{return %config} +sub getShortDescr { + return "Checks TermServ Utilities"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching tsutilities v.".$VERSION); + ::rptMsg("tsutilities v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key_path; + my $key; + +# System Hive + my $ccs = ::getCCS($root_key); + + $key_path = $ccs."\\Control\\Terminal Server\\Utilities"; + if ($key = $root_key->get_subkey($key_path)){ + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg("Name : ".$s->get_name()); + ::rptMsg("LastWrite: ".::format8601Date($s->get_timestamp())."Z"); + + my @vals = $s->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + my $name = $v->get_name(); + my $data = $v->get_data(); + $data =~ s/\n/ /g; + ::rptMsg(sprintf " %-15s %-30s",$name,$data); + } + } + ::rptMsg(""); + } + ::rptMsg("Analysis Tips: Look for new values added to the various keys, or key LastWrite times that occur during the incident"); + ::rptMsg(" timeframe."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/typedpaths.pl b/thirdparty/rr-full/plugins/typedpaths.pl index 828eeff3996..66017ceb914 100644 --- a/thirdparty/rr-full/plugins/typedpaths.pl +++ b/thirdparty/rr-full/plugins/typedpaths.pl @@ -3,12 +3,14 @@ # For Windows 7, Desktop Address Bar History # # Change history +# 20201005 - MITRE update +# 20200526 - updated date output format # 20100330 - created # # References # -# -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package typedpaths; use strict; @@ -17,8 +19,10 @@ package typedpaths; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20100330); + MITRE => "", + category => "user activity", + output => "report", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -44,7 +48,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { diff --git a/thirdparty/rr-full/plugins/typedpaths_tln.pl b/thirdparty/rr-full/plugins/typedpaths_tln.pl index e576ccf252f..13c99dce26a 100644 --- a/thirdparty/rr-full/plugins/typedpaths_tln.pl +++ b/thirdparty/rr-full/plugins/typedpaths_tln.pl @@ -3,13 +3,14 @@ # For Windows 7, Desktop Address Bar History # # Change history +# 20201005 - MITRE update # 20120828 - updated to TLN format # 20100330 - created # # References # # -# copyright 2010 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC #----------------------------------------------------------- package typedpaths_tln; use strict; @@ -18,8 +19,10 @@ package typedpaths_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20120828); + MITRE => "", + category => "user activity", + output => "tln", + version => 20201005); sub getConfig{return %config} sub getShortDescr { @@ -35,7 +38,7 @@ sub getShortDescr { sub pluginmain { my $class = shift; my $ntuser = shift; - ::logMsg("Launching typedpaths v.".$VERSION); + ::logMsg("Launching typedpaths_tln v.".$VERSION); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -52,7 +55,6 @@ sub pluginmain { eval { $path = $key->get_value("url1")->get_data(); ::rptMsg($lw."|REG|||TypedPaths - ".$path); - }; } else { diff --git a/thirdparty/rr-full/plugins/typedurls.pl b/thirdparty/rr-full/plugins/typedurls.pl index fff1693ff86..699eec0911e 100644 --- a/thirdparty/rr-full/plugins/typedurls.pl +++ b/thirdparty/rr-full/plugins/typedurls.pl @@ -1,10 +1,11 @@ #! c:\perl\bin\perl.exe #----------------------------------------------------------- # typedurls.pl -# Plugin for Registry Ripper, NTUSER.DAT edition - gets the -# TypedURLs values +# Gets user's TypedURLs values # # Change history +# 20201012 - MITRE update +# 20200526 - updated date output format # 20120827 - TLN version created # 20080324 - created # @@ -16,7 +17,8 @@ # Also, new entries aren't added to the key until the current # instance of IE is terminated. # -# copyright 2008 H. Carvey +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package typedurls; use strict; @@ -25,8 +27,10 @@ package typedurls; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20080324); + MITRE => "", + category => "user activity", + output => "report", + version => 20201012); sub getConfig{return %config} sub getShortDescr { @@ -59,7 +63,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("TypedURLs"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { my %urls; @@ -78,12 +82,10 @@ sub pluginmain { } else { ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } diff --git a/thirdparty/rr-full/plugins/typedurls_tln.pl b/thirdparty/rr-full/plugins/typedurls_tln.pl index ca9139aa3f5..db86a38a20a 100644 --- a/thirdparty/rr-full/plugins/typedurls_tln.pl +++ b/thirdparty/rr-full/plugins/typedurls_tln.pl @@ -5,6 +5,7 @@ # TypedURLs values # # Change history +# 20201012 - MITRE update # 20120827 - TLN version created # 20080324 - created # @@ -26,8 +27,10 @@ package typedurls_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120827); + MITRE => "", + category => "user activity", + output => "tln", + version => 20201012); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/typedurlstime.pl b/thirdparty/rr-full/plugins/typedurlstime.pl index 5a9612eabff..50712f5374b 100644 --- a/thirdparty/rr-full/plugins/typedurlstime.pl +++ b/thirdparty/rr-full/plugins/typedurlstime.pl @@ -5,6 +5,8 @@ # TypedURLsTime values/data from Windows 8 systems # # Change history +# 20201012 - MITRE update +# 20200526 - updated date output format # 20120613 - created # # References @@ -13,7 +15,7 @@ # Notes: New entries aren't added to the key until the current # instance of IE is terminated. # -# copyright 2012 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package typedurlstime; @@ -23,8 +25,10 @@ package typedurlstime; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120613); + MITRE => "", + category => "user activity", + output => "report", + version => 20201012); sub getConfig{return %config} sub getShortDescr { @@ -41,8 +45,8 @@ sub pluginmain { my $class = shift; my $ntuser = shift; ::logMsg("Launching typedurlstime v.".$VERSION); - ::rptMsg("typedurlstime v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("typedurlstime v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $reg = Parse::Win32Registry->new($ntuser); my $root_key = $reg->get_root_key; @@ -51,7 +55,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("TypedURLsTime"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { my %urls; @@ -76,7 +80,7 @@ sub pluginmain { ::rptMsg(" ".$val." -> ".$data); } else { - ::rptMsg(" ".$val." -> ".gmtime($data)." Z (".$url.")"); + ::rptMsg(" ".$val." -> ".::format8601Date($data)."Z (".$url.")"); } } } diff --git a/thirdparty/rr-full/plugins/typedurlstime_tln.pl b/thirdparty/rr-full/plugins/typedurlstime_tln.pl index 37bf7829cc9..4f1cc508a35 100644 --- a/thirdparty/rr-full/plugins/typedurlstime_tln.pl +++ b/thirdparty/rr-full/plugins/typedurlstime_tln.pl @@ -5,6 +5,7 @@ # TypedURLsTime values/data from Windows 8 systems # # Change history +# 20201012 - MITRE update # 20120613 - created # # References @@ -23,8 +24,10 @@ package typedurlstime_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120613); + MITRE => "", + category => "user activity", + output => "tln", + version => 20201012); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/ua_wiper.pl b/thirdparty/rr-full/plugins/ua_wiper.pl new file mode 100644 index 00000000000..b2607f322dd --- /dev/null +++ b/thirdparty/rr-full/plugins/ua_wiper.pl @@ -0,0 +1,78 @@ +#----------------------------------------------------------- +# ua_wiper.pl +# Settings associated with wiper found targeting Ukraine +# +# Change history +# 20220301 - created +# +# References +# https://twitter.com/0xAmit/status/1496646517205221376 +# https://renenyffenegger.ch/notes/Windows/registry/tree/HKEY_CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Explorer/Advanced/index +# https://www.crowdstrike.com/blog/how-crowdstrike-falcon-protects-against-wiper-malware-used-in-ukraine-attacks/ +# https://blog.malwarebytes.com/threat-intelligence/2022/03/hermeticwiper-a-detailed-analysis-of-the-destructive-malware-that-targeted-ukraine/ +# +# copyright 2022 QAR,LLC +# author: H. Carvey keydet89@yahoo.com +#----------------------------------------------------------- +package ua_wiper; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + category => "defense evasion", + MITRE => "T1562\.001", + output => "report", + version => 20220301); + +sub getConfig{return %config} +sub getShortDescr { + return "Settings associated with wiper found in the Ukraine"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching ua_wiper v.".$VERSION); + ::rptMsg("ua_wiper v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced'; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + eval { + my $c = $key->get_value("ShowCompColor")->get_data(); + ::rptMsg($key_path." ShowCompColor value = ".$c); + }; + + eval { + my $c = $key->get_value("ShowInfoTip")->get_data(); + ::rptMsg($key_path." ShowInfoTip value = ".$c); + }; + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: Amit Serper found that, when analyzing a wiper deployed against Ukraine following the"); + ::rptMsg("Russian invasion in 2022, the malware set these values to \"0\". Crowdstrike analysis of the malware"); + ::rptMsg("indicates that there can be delays set when launching the EXE, so these settings may prevent the user "); + ::rptMsg("from seeing anything untoward had gone on."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.crowdstrike.com/blog/how-crowdstrike-falcon-protects-against-wiper-malware-used-in-ukraine-attacks/"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/uac.pl b/thirdparty/rr-full/plugins/uac.pl index 372a5943761..a03df8e0ad3 100644 --- a/thirdparty/rr-full/plugins/uac.pl +++ b/thirdparty/rr-full/plugins/uac.pl @@ -3,27 +3,32 @@ # Gets the User Account Configuration settings from the SOFTWARE hive file # # Change history +# 20220826 - added reference, updated MITRE ATT&CK +# 20200916 - MITRE updates +# 20200427 - updated output date format +# 20200409 - added reference # 20130213 Created # # References -# +# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpsb/341747f5-6b5d-4d30-85fc-fa1cc04038d4 # UAC Group Policy Settings and Registry Key Settings http://technet.microsoft.com/en-us/library/dd835564(v=ws.10).aspx +# 20220826: https://twitter.com/d4rksystem/status/1563226770962145280 # -# Plugin was created from the banner plugin authored by Special Agent Brook William Minnick -# Written By: # # Corey Harrell (Journey Into IR) -# Plugin was created from the banner plugin authored by Special Agent Brook William Minnick +# maintained by H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package uac; use strict; -my %config = (hive => "Software", - osmask => 22, +my %config = (hive => "Software", + MITRE => "T1562\.001", + category => "defense evasion", hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20130213); + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20220826); sub getConfig{return %config} @@ -41,8 +46,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching uac v.".$VERSION); - ::rptMsg("uac v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("uac v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -51,42 +58,42 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("UAC Information"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); # GET EnableLUA – my $enablelua; eval { - $enablelua = $key->get_value("EnableLUA")->get_data(); + $enablelua = $key->get_value("EnableLUA")->get_data(); }; if ($@) { - ::rptMsg("EnableLUA value not found."); + ::rptMsg("EnableLUA value not found."); } else { - ::rptMsg("EnableLUA value = ".$enablelua); - ::rptMsg(""); - ::rptMsg("User Account Control: Run all administrators in Admin Approval Mode"); - ::rptMsg("0 = Disabled"); - ::rptMsg("1 = Enabled (Default)"); + ::rptMsg("EnableLUA value = ".$enablelua); + ::rptMsg(""); + ::rptMsg("User Account Control: Run all administrators in Admin Approval Mode"); + ::rptMsg("0 = Disabled"); + ::rptMsg("1 = Enabled (Default)"); } ::rptMsg(""); - # GET EnableVirtualization – +# GET EnableVirtualization – my $enablevirtualization; eval { - $enablevirtualization = $key->get_value("EnableVirtualization")->get_data(); + $enablevirtualization = $key->get_value("EnableVirtualization")->get_data(); }; if ($@) { - ::rptMsg("EnableVirtualization value not found."); + ::rptMsg("EnableVirtualization value not found."); } else { - ::rptMsg("EnableVirtualization value = ".$enablevirtualization); - ::rptMsg(""); - ::rptMsg("User Account Control: Virtualize file and registry write failures to per-user locations"); - ::rptMsg("0 = Disabled"); - ::rptMsg("1 = Enabled (Default)"); + ::rptMsg("EnableVirtualization value = ".$enablevirtualization); + ::rptMsg(""); + ::rptMsg("User Account Control: Virtualize file and registry write failures to per-user locations"); + ::rptMsg("0 = Disabled"); + ::rptMsg("1 = Enabled (Default)"); } ::rptMsg(""); @@ -94,17 +101,17 @@ sub pluginmain { my $filteradministratortoken; eval { - $filteradministratortoken = $key->get_value("FilterAdministratorToken")->get_data(); + $filteradministratortoken = $key->get_value("FilterAdministratorToken")->get_data(); }; if ($@) { - ::rptMsg("FilterAdministratorToken value not found."); + ::rptMsg("FilterAdministratorToken value not found."); } else { - ::rptMsg("FilterAdministratorToken value = ".$filteradministratortoken); - ::rptMsg(""); - ::rptMsg("User Account Control: Admin Approval Mode for the built-in Administrator account"); - ::rptMsg("0 = Disabled (Default)"); - ::rptMsg("1 = Enabled"); + ::rptMsg("FilterAdministratorToken value = ".$filteradministratortoken); + ::rptMsg(""); + ::rptMsg("User Account Control: Admin Approval Mode for the built-in Administrator account"); + ::rptMsg("0 = Disabled (Default)"); + ::rptMsg("1 = Enabled"); } ::rptMsg(""); @@ -112,21 +119,21 @@ sub pluginmain { my $consentpromptbehavioradmin; eval { - $consentpromptbehavioradmin = $key->get_value("ConsentPromptBehaviorAdmin")->get_data(); + $consentpromptbehavioradmin = $key->get_value("ConsentPromptBehaviorAdmin")->get_data(); }; if ($@) { - ::rptMsg("ConsentPromptBehaviorAdmin value not found."); + ::rptMsg("ConsentPromptBehaviorAdmin value not found."); } else { - ::rptMsg("ConsentPromptBehaviorAdmin value = ".$consentpromptbehavioradmin); - ::rptMsg(""); - ::rptMsg("User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode"); - ::rptMsg("0 = Elevate without prompting"); - ::rptMsg("1 = Prompt for credentials on the secure desktop"); - ::rptMsg("2 = Prompt for consent on the secure desktop"); - ::rptMsg("3 = Prompt for credentials"); - ::rptMsg("4 = Prompt for consent"); - ::rptMsg("5 = Prompt for consent for non-Windows binaries (Default)"); + ::rptMsg("ConsentPromptBehaviorAdmin value = ".$consentpromptbehavioradmin); + ::rptMsg(""); + ::rptMsg("User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode"); + ::rptMsg("0 = Elevate without prompting"); + ::rptMsg("1 = Prompt for credentials on the secure desktop"); + ::rptMsg("2 = Prompt for consent on the secure desktop"); + ::rptMsg("3 = Prompt for credentials"); + ::rptMsg("4 = Prompt for consent"); + ::rptMsg("5 = Prompt for consent for non-Windows binaries (Default)"); } ::rptMsg(""); @@ -134,27 +141,25 @@ sub pluginmain { my $consentpromptbehavioruser; eval { - $consentpromptbehavioruser = $key->get_value("ConsentPromptBehaviorUser")->get_data(); + $consentpromptbehavioruser = $key->get_value("ConsentPromptBehaviorUser")->get_data(); }; if ($@) { - ::rptMsg("ConsentPromptBehaviorUser value not found."); + ::rptMsg("ConsentPromptBehaviorUser value not found."); } else { - ::rptMsg("ConsentPromptBehaviorUser value = ".$consentpromptbehavioruser); - ::rptMsg(""); - ::rptMsg("User Account Control: Behavior of the elevation prompt for standard users"); - ::rptMsg("0 = Automatically deny elevation requests"); - ::rptMsg("1 = Prompt for consent on the secure desktop"); - ::rptMsg("3 = Prompt for consent on the secure desktop (Default)"); + ::rptMsg("ConsentPromptBehaviorUser value = ".$consentpromptbehavioruser); + ::rptMsg(""); + ::rptMsg("User Account Control: Behavior of the elevation prompt for standard users"); + ::rptMsg("0 = Automatically deny elevation requests"); + ::rptMsg("1 = Prompt for consent on the secure desktop"); + ::rptMsg("3 = Prompt for consent on the secure desktop (Default)"); } ::rptMsg(""); } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); + ::rptMsg($key_path." not found."); } - } 1; diff --git a/thirdparty/rr-full/plugins/uacbypass.pl b/thirdparty/rr-full/plugins/uacbypass.pl new file mode 100644 index 00000000000..9b45418fe33 --- /dev/null +++ b/thirdparty/rr-full/plugins/uacbypass.pl @@ -0,0 +1,102 @@ +#----------------------------------------------------------- +# uacbypass.pl +# Checks for UAC bypasses +# +# Change history +# 20200924 - MITRE update +# 20200511 - updated date output format +# 20200504 - Added SLUI check +# 20200427 - updated output date format +# 20190911 - Created +# +# References +# SLUI: https://medium.com/@mattharr0ey/privilege-escalation-uac-bypass-in-changepk-c40b92818d1b +# https://enigma0x3.net/2017/03/17/fileless-uac-bypass-using-sdclt-exe/ +# http://technet.microsoft.com/en-us/library/dd835564(v=ws.10).aspx +# +# https://attack.mitre.org/techniques/T1548/002/ +# +# copyright 2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package uacbypass; +use strict; + +my %config = (hive => "USRCLASS\.DAT, Software", + MITRE => "T1548\.002", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20200924); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get possible UAC bypass settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching uacbypass v.".$VERSION); + ::rptMsg("uacbypass v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +#--------------------------------------------------------------------------- +# TrickBot uses Fodhelper/WReset bypass via "AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2" +# +# https://twitter.com/VK_Intel/status/1222929998618775553 +#--------------------------------------------------------------------------- + my @apps = ("exefile","Folder","mscfile","ms-settings","AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2","Launcher\.SystemSettings"); + foreach my $app (@apps) { +# USRCLASS.DAT + eval { + if (my $key = $root_key->get_subkey($app."\\shell\\open\\command")) { + my $def = $key->get_value("")->get_data(); + ::rptMsg($app."\\shell\\open\\command (Default) value: ".$def); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + } + }; + +# Software hive + eval { + if (my $key = $root_key->get_subkey("Classes\\".$app."\\shell\\open\\command")) { + my $def = $key->get_value("")->get_data(); + ::rptMsg("Classes\\".$app."\\shell\\open\\command (Default) value: ".$def); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + } + }; + } + + my $path = "exefile\\shell\\runas\\command"; + + foreach my $i ("","Classes\\") { + eval { + if (my $key = $root_key->get_subkey($i.$path)) { + my $def = $key->get_value("")->get_data(); + ::rptMsg($i.$path." (Default) value: ".$def); + } + }; + + eval { + if (my $key = $root_key->get_subkey($i.$path)) { + my $def = $key->get_value("IsolatedCommand")->get_data(); + ::rptMsg($i.$path." IsolatedCommand value: ".$def); + } + }; + } + +} +1; + diff --git a/thirdparty/rr-full/plugins/uninstall.pl b/thirdparty/rr-full/plugins/uninstall.pl index 2457e77470c..4137bd9c9c1 100644 --- a/thirdparty/rr-full/plugins/uninstall.pl +++ b/thirdparty/rr-full/plugins/uninstall.pl @@ -9,6 +9,8 @@ # http://msdn.microsoft.com/en-us/library/ms954376.aspx # # Change History: +# 20200916 - MITRE updates +# 20200525 - updated date output format # 20140512 - updated to include NTUSER.DAT (recommended by # Bartosz Inglot, bartosz.inglot@uk.pwc.com) # 20120523 - updated to include 64-bit systems @@ -16,23 +18,25 @@ # 20090413 - Extract DisplayVersion info # 20090128 - Added references # -# copyright 2014 Quantum Analytics Research, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package uninstall; use strict; my %config = (hive => "Software, NTUSER\.DAT", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20140512); + output => "report", + version => 20200916); sub getConfig{return %config} sub getShortDescr { - return "Gets contents of Uninstall keys from Software, NTUSER.DAT hives"; + return "Gets contents of Uninstall keys from Software, NTUSER\.DAT hives"; } sub getDescr{} sub getRefs {} @@ -82,7 +86,7 @@ sub pluginmain { push(@{$uninst{$lastwrite}},$display); } foreach my $t (reverse sort {$a <=> $b} keys %uninst) { - ::rptMsg(gmtime($t)." (UTC)"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $item (@{$uninst{$t}}) { ::rptMsg(" ".$item); } @@ -98,4 +102,4 @@ sub pluginmain { } } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/uninstall_tln.pl b/thirdparty/rr-full/plugins/uninstall_tln.pl index 2c349852f74..6013a6f04f9 100644 --- a/thirdparty/rr-full/plugins/uninstall_tln.pl +++ b/thirdparty/rr-full/plugins/uninstall_tln.pl @@ -10,6 +10,7 @@ # http://msdn.microsoft.com/en-us/library/ms954376.aspx # # Change History: +# 20200916 - MITRE updates # 20120523 - updated to include 64-bit systems # 20100116 - Minor updates # 20090413 - Extract DisplayVersion info @@ -21,16 +22,18 @@ package uninstall_tln; use strict; my %config = (hive => "Software, NTUSER\.DAT", - osmask => 22, + MITRE => "", + category => "config", #installed software hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20120523); + output => "tln", + version => 20200916); sub getConfig{return %config} sub getShortDescr { - return "Gets contents of Uninstall keys from Software, NTUSER.DAT hives(TLN format)"; + return "Gets contents of Uninstall keys from Software, NTUSER\.DAT hives(TLN format)"; } sub getDescr{} sub getRefs {} @@ -93,4 +96,4 @@ sub pluginmain { } } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/unreadmail.pl b/thirdparty/rr-full/plugins/unreadmail.pl deleted file mode 100644 index 479b5b63d7a..00000000000 --- a/thirdparty/rr-full/plugins/unreadmail.pl +++ /dev/null @@ -1,90 +0,0 @@ -#----------------------------------------------------------- -# unreadmail.pl -# -# -# Change history -# 20100218 - created -# -# References -# http://support.microsoft.com/kb/304148 -# http://support.microsoft.com/kb/831403 -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package unreadmail; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20100218); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of Unreadmail key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - my %hist; - ::logMsg("Launching unreadmail v.".$VERSION); - ::rptMsg("unreadmail v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - eval { - my $e = $key->get_value("MessageExpiryDays")->get_data(); - ::rptMsg("MessageExpiryDays : ".$e); - ::rptMsg(""); - }; - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar @subkeys > 0) { - ::rptMsg(""); - foreach my $s (@subkeys) { - ::rptMsg($s->get_name()); - ::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); - eval { - my $m = $s->get_value("MessageCount")->get_data(); - ::rptMsg(" MessageCount: ".$m); - }; - - eval { - my $a = $s->get_value("Application")->get_data(); - ::rptMsg(" Application : ".$a); - }; - - eval { - my @t = unpack("VV",$s->get_value("TimeStamp")->get_data()); - my $ts = ::getTime($t[0],$t[1]); - ::rptMsg(" TimeStamp : ".gmtime($ts)." (UTC)"); - }; - - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/update_test.pl b/thirdparty/rr-full/plugins/update_test.pl new file mode 100644 index 00000000000..e6ee1e8cfb4 --- /dev/null +++ b/thirdparty/rr-full/plugins/update_test.pl @@ -0,0 +1,73 @@ +#----------------------------------------------------------- +# update_test +# The WindowsUpdate\Test key reportedly provides persistence, as it is checked +# via Windows Update +# +# +# Change history: +# 20200907 - created +# +# Ref: +# https://www.hexacorn.com/blog/2020/09/06/beyond-good-ol-run-key-part-127-testhooks-bonus/ +# +# https://attack.mitre.org/techniques/T1546/010/ +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package update_test; +use strict; + +my %config = (hive => "Software", + category => "persistence", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546\.010", + output => "report", + version => 20200907); + +sub getConfig{return %config} +sub getShortDescr { + return "Get Windows Update\\Test values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching update_test v.".$VERSION); + ::rptMsg("update_test v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $key_path = ('Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Test'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + ::rptMsg($v->get_name()." - ".$v->get_data()); + } + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: The WindowsUpdate\\Test key is reportedly checked by Windows Updates, and may serve"); + ::rptMsg("as a persistence mechanism."); + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/updates.pl b/thirdparty/rr-full/plugins/updates.pl index b6a1dd97a13..77f9b60a6b0 100644 --- a/thirdparty/rr-full/plugins/updates.pl +++ b/thirdparty/rr-full/plugins/updates.pl @@ -4,21 +4,26 @@ # # References: # https://stackoverflow.com/questions/5102900/registry-key-location-for-security-update-and-hotfixes +# https://www.iblue.team/windows-forensics/security-patch-kb-install-date # # Change History: +# 20220724 - updated with new content # 20170715 - created # -# copyright 2017 Quantum Analytics Research, LLC +# copyright 2022 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package updates; use strict; my %config = (hive => "Software", + MITRE => "", + category => "", osmask => 22, hasShortDescr => 1, hasDescr => 0, hasRefs => 0, + output => "report", version => 20170715); sub getConfig{return %config} @@ -39,7 +44,7 @@ sub pluginmain { my %uninst; ::logMsg("Launching updates v.".$VERSION); - ::rptMsg("updates v.".$VERSION); # banner + ::rptMsg("updates v.".$VERSION); ::rptMsg("(".getHive().") ".getShortDescr()."\n"); my $key_path = 'Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\Packages'; @@ -52,34 +57,29 @@ sub pluginmain { ::rptMsg($key_path); ::rptMsg(""); - my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { + my $name = $s->get_name(); my $lastwrite = $s->get_timestamp(); - my $install; - eval { - $install = $s->get_value("InstallName")->get_data(); - }; - $install = $s->get_name() if ($install eq ""); - - my $client; - eval { - $client = $s->get_value("InstallClient")->get_data(); - }; - $install .= " InstallClient: ".$client unless ($@); - - push(@{$uninst{$lastwrite}},$install); + + ::rptMsg($name); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + + my @values = ("InstallClient","InstallLocation","InstallUser","SelfUpdate"); + foreach my $v (@values) { + + eval { + my $t = $s->get_value($v)->get_data(); + ::rptMsg(sprintf " %-18s %-40s",$v,$t); + }; + + } + + ::rptMsg(""); } } - foreach my $t (reverse sort {$a <=> $b} keys %uninst) { - ::rptMsg(gmtime($t)." (UTC)"); - foreach my $item (@{$uninst{$t}}) { - ::rptMsg(" ".$item); - } - ::rptMsg(""); - } } else { ::rptMsg($key_path." has no subkeys."); diff --git a/thirdparty/rr-full/plugins/urlzone.pl b/thirdparty/rr-full/plugins/urlzone.pl deleted file mode 100644 index e51d774342f..00000000000 --- a/thirdparty/rr-full/plugins/urlzone.pl +++ /dev/null @@ -1,98 +0,0 @@ -#----------------------------------------------------------- -# /root/bin/plugins/urlzone.pl -# Plugin to detect URLZONE infection -# -# copyright 2009 Stefan Kelm (skelm@bfk.de) -#----------------------------------------------------------- -package urlzone; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20090526); - -sub getConfig{return %config} - -sub getShortDescr {return "URLZONE detection";} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { -my $class = shift; -my $hive = shift; -::logMsg("Launching urlzone v.".$VERSION); -::rptMsg("urlzone v.".$VERSION); # banner -::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner -my $reg = Parse::Win32Registry->new($hive); -my $root_key = $reg->get_root_key; - -my $key_path = "Microsoft\\Windows\\CurrentVersion\\Internet Settings\\urlzone"; -my $key; -if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - ::rptMsg($key_path."\\".$s->get_name()); - ::rptMsg("LastWrite Time = ".gmtime($s->get_timestamp())." (UTC)"); - eval { - my @vals = $s->get_list_of_values(); - if (scalar(@vals) > 0) { - my %sns; - foreach my $v (@vals) { - $sns{$v->get_name()} = $v->get_data(); - } - foreach my $i (keys %sns) { - ::rptMsg("\t\t".$i." = ".$sns{$i}); - } - } - else { -# No values - } - }; - ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); -# ::logMsg($key_path." not found."); - } - - my $key_path2 = "Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\userinit.exe"; - my $key2; - if ($key2 = $root_key->get_subkey($key_path2)) { - ::rptMsg($key_path2); - ::rptMsg("LastWrite Time ".gmtime($key2->get_timestamp())." (UTC)"); - ::rptMsg(""); - my $dbg; - eval { - $dbg = $key2->get_value("Debugger")->get_data(); - }; - if ($@) { - ::rptMsg("Debugger value not found."); - } - else { - ::rptMsg("Debugger = ".$dbg); - } - ::rptMsg(""); - } - else { - ::rptMsg($key_path2." not found."); -# ::logMsg($key_path2." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/urun_tln.pl b/thirdparty/rr-full/plugins/urun_tln.pl deleted file mode 100644 index fbcc3b213a9..00000000000 --- a/thirdparty/rr-full/plugins/urun_tln.pl +++ /dev/null @@ -1,168 +0,0 @@ -#----------------------------------------------------------- -# urun_tln.pl -# Get contents of Run key from NTUSER.DAT hive -# -# Change History -# 20130425 - created -# -# References: -# http://msdn2.microsoft.com/en-us/library/aa376977.aspx -# http://support.microsoft.com/kb/170086 -# -# -# copyright 2013 Quantum Analytics Research, -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package urun_tln; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20130425); - -sub getConfig{return %config} - -sub getShortDescr { - return "[Autostart] Get autostart key contents from NTUSER.DAT hive"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Definition of the Run keys in the WinXP Registry" => - "http://support.microsoft.com/kb/314866"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching urun_tln v.".$VERSION); -# ::rptMsg("urun_tln v.".$VERSION); # banner -# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my @run = ("Software\\Microsoft\\Windows\\CurrentVersion\\Run", - "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", - "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices", - "Software\\Microsoft\\Windows\\CurrentVersion\\RunServicesOnce", - "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". - "Software\\Microsoft\\Windows\\CurrentVersion\\Run", - "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". - "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", - "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run"); - - my @alertpaths = ("recycle","globalroot","temp","system volume information","appdata", - "application data"); - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - foreach my $key_path (@run) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my $lw = $key->get_timestamp(); - my %vals = getKeyValues($key); - if (scalar(keys %vals) > 0) { - foreach my $v (keys %vals) { - my $lc_path = lc($vals{$v}); - foreach my $a (@alertpaths) { - if (grep(/$a/,$lc_path)) { -# ::alertMsg("ALERT: soft_run: Temp Path found: ".$key_path." : ".$v." -> ".$vals{$v}); - ::alertMsg($lw."|ALERT|||HKCU\\".$key_path." Temp path: ".$v.": ".$vals{$v}); - } - } -# check to see if the data ends in .com - if ($vals{$v} =~ m/\.com$/ || $vals{$v} =~ m/\.bat$/ || $vals{$v} =~ m/\.pif$/) { -# ::alertMsg("ALERT: user_run: Path ends in \.com/\.bat: ".$key_path." : ".$v." -> ".$vals{$v}); - ::alertMsg($lw."|ALERT|||HKCU\\".$key_path." \.com/\.bat/\.pif file found: ".$v.": ".$vals{$v}); - } - - my @list = split(/:/,$vals{$v}); - my $last = $list[scalar(@list) - 1]; - ::alertMsg($lw."|ALERT|||Poss. ADS found: ".$v.": ".$vals{$v}) if (grep(/:/,$last)); - -# ::rptMsg(" ".$v.": ".$vals{$v}); - } - } - else { -# ::rptMsg(""); -# ::rptMsg($key_path." has no values."); - } - } - else { -# ::rptMsg($key_path." not found."); - } -# ::rptMsg(""); - } - -# This section was added on 20130115 to address the 'run' and 'load' values that -# could be added to the key - my $key_path = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg(""); -# ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my $lw = $key->get_timestamp(); - my $run; - my $count = 0; - eval { - $run = $key->get_value("Run")->get_data(); -# ::rptMsg("Run value = ".$run); -# ::alertMsg("ALERT: user_run: ".$key_path." Run value found: ".$run); - ::alertMsg($lw."|ALERT|||urun_tln: HKCU\\".$key_path." Run value found: ".$run); - }; - if ($@) { -# ::rptMsg("Run value not found."); - } - - eval { - $run = $key->get_value("run")->get_data(); -# ::rptMsg("run value = ".$run); -# ::alertMsg("ALERT: user_run: ".$key_path." run value found: ".$run); - ::alertMsg($lw."|ALERT|||urun_tln: HKCU\\".$key_path." run value found: ".$run); - }; - if ($@) { -# ::rptMsg("run value not found."); - } - - my $load; - eval { - $load = $key->get_value("load")->get_data(); -# ::rptMsg("load value = ".$load); -# ::alertMsg("ALERT: user_run: ".$key_path." load value found: ".$load); - ::alertMsg($lw."|ALERT|||urun_tln: HKCU\\".$key_path." load value found: ".$load); - }; - if ($@) { -# ::rptMsg("load value not found."); - } - - } -} - -sub getKeyValues { - my $key = shift; - my %vals; - - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - - } - return %vals; -} - -1; diff --git a/thirdparty/rr-full/plugins/usb.pl b/thirdparty/rr-full/plugins/usb.pl index 33ba031b02d..cffe47842f7 100644 --- a/thirdparty/rr-full/plugins/usb.pl +++ b/thirdparty/rr-full/plugins/usb.pl @@ -2,24 +2,29 @@ # usb # # History: +# 20200916 - MITRE updates +# 20200515 - updated date output format +# 20190819 - updated to include time stamps # 20141111 - updated check for key LastWrite times # 20141015 - created # # Ref: # http://studioshorts.com/blog/2012/10/windows-8-device-property-ids-device-enumeration-pnpobject/ # -# copyright 2014 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package usb; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "", + category => "devices", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20141111); + output => "report", + version => 20200916); sub getConfig{return %config} @@ -56,7 +61,8 @@ sub pluginmain { return; } - $key_path = $ccs."\\Enum\\USB"; + my $key_path = $ccs."\\Enum\\USB"; + my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("USBStor"); ::rptMsg($key_path); @@ -65,22 +71,20 @@ sub pluginmain { my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())."]"); + ::rptMsg($s->get_name()." [".::format8601Date($s->get_timestamp())."Z]"); my @sk = $s->get_list_of_subkeys(); if (scalar(@sk) > 0) { foreach my $k (@sk) { my $serial = $k->get_name(); - ::rptMsg(" S/N: ".$serial." [".gmtime($k->get_timestamp())."]"); + ::rptMsg(" S/N: ".$serial." [".::format8601Date($k->get_timestamp())."Z]"); # added 20141015; updated 20141111 +# eval { +# ::rptMsg(" Device Parameters LastWrite: [".gmtime($k->get_subkey("Device Parameters")->get_timestamp())."]"); +# }; + eval { - ::rptMsg(" Device Parameters LastWrite: [".gmtime($k->get_subkey("Device Parameters")->get_timestamp())."]"); - }; - eval { - ::rptMsg(" LogConf LastWrite : [".gmtime($k->get_subkey("LogConf")->get_timestamp())."]"); - }; - eval { - ::rptMsg(" Properties LastWrite : [".gmtime($k->get_subkey("Properties")->get_timestamp())."]"); + ::rptMsg(" Properties Key LastWrite: ".::format8601Date($k->get_subkey("Properties")->get_timestamp())."Z"); }; my $friendly; eval { @@ -94,15 +98,29 @@ sub pluginmain { ::rptMsg(" ParentIdPrefix: ".$parent) if ($parent ne ""); # Attempt to retrieve InstallDate/FirstInstallDate from Properties subkeys # http://studioshorts.com/blog/2012/10/windows-8-device-property-ids-device-enumeration-pnpobject/ + my $t; + eval { + $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$t); + ::rptMsg(" First InstallDate : ".::format8601Date(::getTime($t0,$t1))."Z"); + }; + + eval { + $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$t); + ::rptMsg(" InstallDate : ".::format8601Date(::getTime($t0,$t1))."Z"); + }; + + eval { + $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$t); + ::rptMsg(" Last Arrival : ".::format8601Date(::getTime($t0,$t1))."Z"); + }; eval { - my $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\00000064\\00000000")->get_value("Data")->get_data(); + $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\0067")->get_value("")->get_data(); my ($t0,$t1) = unpack("VV",$t); - ::rptMsg(" InstallDate : ".gmtime(::getTime($t0,$t1))." UTC"); - - $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\00000065\\00000000")->get_value("Data")->get_data(); - ($t0,$t1) = unpack("VV",$t); - ::rptMsg(" FirstInstallDate: ".gmtime(::getTime($t0,$t1))." UTC"); + ::rptMsg(" Last Removal : ".::format8601Date(::getTime($t0,$t1))."Z"); }; } @@ -118,4 +136,4 @@ sub pluginmain { ::rptMsg($key_path." not found."); } } -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/usbdevices.pl b/thirdparty/rr-full/plugins/usbdevices.pl index 98f65732934..faca66c4f97 100644 --- a/thirdparty/rr-full/plugins/usbdevices.pl +++ b/thirdparty/rr-full/plugins/usbdevices.pl @@ -3,21 +3,31 @@ # Parses contents of Enum\USB key for USB devices (not only USB storage devices) # # History +# 20220524 - Updated +# 20200916 - MITRE updates +# 20200525 - updated date output format # 20140416 - updated to include WPD devices (Jasmine Chau) # 20120522 - updated to report only USBStor devices # 20100219 - created # -# copyright 2014 Quantum Analytics Research, LLC +# References: +# http://www.swiftforensics.com/2013/11/windows-8-new-registry-artifacts-part-1.html +# https://www.researchgate.net/publication/318514858_USB_Storage_Device_Forensics_for_Windows_10 +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package usbdevices; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "", + category => "devices", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20140416); + output => "report", + version => 20220524); sub getConfig{return %config} @@ -38,81 +48,40 @@ sub pluginmain { $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; ::logMsg("Launching usbdevices v.".$VERSION); - ::rptMsg("usbdevices v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner -# Code for System file, getting CurrentControlSet - my $current; - my $ccs; - my $key_path = 'Select'; + ::rptMsg("usbdevices v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + + my $key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Enum\\USB"; my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - else { - ::rptMsg($key_path." not found."); - return; - } - $key_path = $ccs."\\Enum\\USB"; + my @vals = ("DeviceDesc","Mfg","Service","FriendlyName"); + if ($key = $root_key->get_subkey($key_path)) { my @subkeys = $key->get_list_of_subkeys(); if (scalar @subkeys > 0) { foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); my @sk = $s->get_list_of_subkeys(); if (scalar @sk > 0) { - foreach my $s2 (@sk) { - - my ($desc,$class,$serv,$loc,$mfg,$fname); - - eval { - $desc = $s2->get_value("DeviceDesc")->get_data(); -# ::rptMsg($desc." [".$s->get_name()."\\".$s2->get_name()."]"); - }; - - eval { - $class = $s2->get_value("Class")->get_data(); - }; - - eval { - $serv = $s2->get_value("Service")->get_data(); - }; - - eval { - $loc = $s2->get_value("LocationInformation")->get_data(); - }; - - eval { - $mfg = $s2->get_value("Mfg")->get_data(); - }; + foreach my $k (@sk) { + ::rptMsg(" ".$k->get_name()); + foreach my $v (@vals) { + eval { + my $x = $k->get_value($v)->get_data(); + ::rptMsg(sprintf " %-15s: %-30s",$v,$x); + }; + } +# get Properties\{83da6326-97a6-4088-9453-a1923f573b29} eval { - $fname = $s2->get_value("FriendlyName")->get_data(); + getProperties($k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}")); }; - - if ($serv eq "USBSTOR") { - ::rptMsg($s->get_name()); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())); - ::rptMsg(" SN : ".$s2->get_name()); - ::rptMsg(" LastWrite: ".gmtime($s2->get_timestamp())); -# ::rptMsg("DeviceDesc: ".$desc); -# ::rptMsg("Class : ".$class); -# ::rptMsg("Location : ".$loc); -# ::rptMsg("MFG : ".$mfg); - ::rptMsg(""); - } - elsif (($class eq "WPD") && ($serv eq "WUDFRd")) { - ::rptMsg($s->get_name()); - ::rptMsg("LastWrite: ".gmtime($s->get_timestamp())); - ::rptMsg(" SN : ".$s2->get_name()); - ::rptMsg(" LastWrite: ".gmtime($s2->get_timestamp())); - ::rptMsg("MFG : ".$mfg); - ::rptMsg("FriendlyName: ".$fname); - ::rptMsg(""); - } } } + ::rptMsg(""); } } else { @@ -123,4 +92,41 @@ sub pluginmain { ::rptMsg($key_path." not found."); } } -1; + + +sub getProperties { + my $key = shift; + + eval { + my $r = $key->get_subkey("0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Install",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Inserted",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Inserted",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Removal",::format8601Date($t)."Z"); + }; + + +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/usbdevices_tln.pl b/thirdparty/rr-full/plugins/usbdevices_tln.pl new file mode 100644 index 00000000000..07544ab7317 --- /dev/null +++ b/thirdparty/rr-full/plugins/usbdevices_tln.pl @@ -0,0 +1,130 @@ +#----------------------------------------------------------- +# usbdevices_tln.pl +# Parses contents of Enum\USB key for USB devices (not only USB storage devices) +# +# History +# 20220524 - Updated +# 20200916 - MITRE updates +# 20200525 - updated date output format +# 20140416 - updated to include WPD devices (Jasmine Chau) +# 20120522 - updated to report only USBStor devices +# 20100219 - created +# +# References: +# http://www.swiftforensics.com/2013/11/windows-8-new-registry-artifacts-part-1.html +# https://www.researchgate.net/publication/318514858_USB_Storage_Device_Forensics_for_Windows_10 +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package usbdevices_tln; +use strict; + +my %config = (hive => "System", + MITRE => "", + category => "devices", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "tln", + version => 20220524); + +sub getConfig{return %config} + +sub getShortDescr { + return "Parses Enum\\USB key for USB & WPD devices"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $reg; + +sub pluginmain { + my $class = shift; + my $hive = shift; + $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# ::logMsg("Launching usbdevices v.".$VERSION); +# ::rptMsg("usbdevices v.".$VERSION); +# ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + + my $key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Enum\\USB"; + my $key; + + my @vals = ("DeviceDesc","Mfg","Service","FriendlyName"); + + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { +# ::rptMsg($s->get_name()); + my @sk = $s->get_list_of_subkeys(); + if (scalar @sk > 0) { + foreach my $k (@sk) { + my $serial = $k->get_name(); + my $x = ""; + eval { + $x = $k->get_value("DeviceDesc")->get_data(); + }; +# get Properties\{83da6326-97a6-4088-9453-a1923f573b29} + eval { + getProperties($x." [".$serial."]",$k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}")); + }; + } + } + } + } + else { +# ::rptMsg($key_path." has no subkeys."); + } + } + else { +# ::rptMsg($key_path." not found."); + } +} + + +sub getProperties { + my $name = shift; + my $key = shift; + + eval { + my $r = $key->get_subkey("0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||First Install - ".$name); + }; + + eval { + my $r = $key->get_subkey("0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||First Inserted - ".$name); + }; + + eval { + my $r = $key->get_subkey("0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||Last Inserted - ".$name); + }; + + eval { + my $r = $key->get_subkey("0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||Last Removal - ".$name); + + }; + + +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/usbstor.pl b/thirdparty/rr-full/plugins/usbstor.pl index f33f0b2ca76..04675b34b5d 100644 --- a/thirdparty/rr-full/plugins/usbstor.pl +++ b/thirdparty/rr-full/plugins/usbstor.pl @@ -1,32 +1,33 @@ #----------------------------------------------------------- -# usbstor +# usbstor.pl +# Parses contents of Enum\USBStor +# +# History +# 20220524 - copied from usbdevices.pl # -# History: -# 20141111 - updated check for key LastWrite times -# 20141015 - added subkey LastWrite times -# 20130630 - added FirstInstallDate, InstallDate query -# 20080418 - created +# References: +# http://www.swiftforensics.com/2013/11/windows-8-new-registry-artifacts-part-1.html +# https://www.researchgate.net/publication/318514858_USB_Storage_Device_Forensics_for_Windows_10 # -# Ref: -# http://studioshorts.com/blog/2012/10/windows-8-device-property-ids-device-enumeration-pnpobject/ -# -# copyright 2014 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package usbstor; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "", + category => "devices", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20141111); + output => "report", + version => 20220524); sub getConfig{return %config} sub getShortDescr { - return "Get USBStor key info"; + return "Parses Enum\\USBStor key"; } sub getDescr{} sub getRefs {} @@ -34,80 +35,46 @@ sub getShortDescr { sub getVersion {return $config{version};} my $VERSION = getVersion(); +my $reg; sub pluginmain { my $class = shift; my $hive = shift; - ::logMsg("Launching usbstor v.".$VERSION); - ::rptMsg("usbstor v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); + $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; + ::logMsg("Launching usbstor v.".$VERSION); + ::rptMsg("usbstor v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); -# Code for System file, getting CurrentControlSet - my $current; - my $ccs; - my $key_path = 'Select'; my $key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Enum\\USBStor"; + my $key; + + my @vals = ("DeviceDesc","Mfg","Service","FriendlyName"); + if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - else { - ::rptMsg($key_path." not found."); - return; - } - - $key_path = $ccs."\\Enum\\USBStor"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("USBStor"); - ::rptMsg($key_path); - ::rptMsg(""); my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { + if (scalar @subkeys > 0) { foreach my $s (@subkeys) { - ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())."]"); - + ::rptMsg($s->get_name()); my @sk = $s->get_list_of_subkeys(); - if (scalar(@sk) > 0) { + if (scalar @sk > 0) { foreach my $k (@sk) { - my $serial = $k->get_name(); - ::rptMsg(" S/N: ".$serial." [".gmtime($k->get_timestamp())."]"); -# added 20141015; updated 20141111 - eval { - ::rptMsg(" Device Parameters LastWrite: [".gmtime($k->get_subkey("Device Parameters")->get_timestamp())."]"); - }; - eval { - ::rptMsg(" LogConf LastWrite : [".gmtime($k->get_subkey("LogConf")->get_timestamp())."]"); - }; - eval { - ::rptMsg(" Properties LastWrite : [".gmtime($k->get_subkey("Properties")->get_timestamp())."]"); - }; - my $friendly; - eval { - $friendly = $k->get_value("FriendlyName")->get_data(); - }; - ::rptMsg(" FriendlyName : ".$friendly) if ($friendly ne ""); - my $parent; - eval { - $parent = $k->get_value("ParentIdPrefix")->get_data(); - }; - ::rptMsg(" ParentIdPrefix: ".$parent) if ($parent ne ""); -# Attempt to retrieve InstallDate/FirstInstallDate from Properties subkeys -# http://studioshorts.com/blog/2012/10/windows-8-device-property-ids-device-enumeration-pnpobject/ + ::rptMsg(" ".$k->get_name()); + foreach my $v (@vals) { + eval { + my $x = $k->get_value($v)->get_data(); + ::rptMsg(sprintf " %-15s: %-30s",$v,$x); + }; + } +# get Properties\{83da6326-97a6-4088-9453-a1923f573b29} eval { - my $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\00000064\\00000000")->get_value("Data")->get_data(); - my ($t0,$t1) = unpack("VV",$t); - ::rptMsg(" InstallDate : ".gmtime(::getTime($t0,$t1))." UTC"); - - $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\00000065\\00000000")->get_value("Data")->get_data(); - ($t0,$t1) = unpack("VV",$t); - ::rptMsg(" FirstInstallDate: ".gmtime(::getTime($t0,$t1))." UTC"); + getProperties($k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}")); }; - - } + } } ::rptMsg(""); } @@ -120,4 +87,41 @@ sub pluginmain { ::rptMsg($key_path." not found."); } } -1; + + +sub getProperties { + my $key = shift; + + eval { + my $r = $key->get_subkey("0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Install",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Inserted",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Inserted",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Removal",::format8601Date($t)."Z"); + }; + + +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/usbstor2.pl b/thirdparty/rr-full/plugins/usbstor2.pl index 7a1e0120dbc..c18b18d857a 100644 --- a/thirdparty/rr-full/plugins/usbstor2.pl +++ b/thirdparty/rr-full/plugins/usbstor2.pl @@ -1,25 +1,38 @@ #----------------------------------------------------------- -# usbstor2 -# Similar to usbstor plugin, but prints output in .csv format; -# also checks MountedDevices keys +# usbdevices.pl +# Parses contents of Enum\USB key for USB devices (not only USB storage devices) # +# History +# 20220524 - Updated +# 20200916 - MITRE updates +# 20200525 - updated date output format +# 20140416 - updated to include WPD devices (Jasmine Chau) +# 20120522 - updated to report only USBStor devices +# 20100219 - created # -# copyright 2008 H. Carvey, keydet89@yahoo.com +# References: +# http://www.swiftforensics.com/2013/11/windows-8-new-registry-artifacts-part-1.html +# https://www.researchgate.net/publication/318514858_USB_Storage_Device_Forensics_for_Windows_10 +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package usbstor2; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "", + category => "devices", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20080825); + output => "report", + version => 20220524); sub getConfig{return %config} sub getShortDescr { - return "Get USBStor key info; csv output"; + return "Parses Enum\\USB key for USB & WPD devices"; } sub getDescr{} sub getRefs {} @@ -30,106 +43,90 @@ sub getShortDescr { my $reg; sub pluginmain { - ::logMsg("Launching usbstor2 v.".$VERSION); - ::rptMsg("usbstor2 v.".$VERSION); # banner my $class = shift; my $hive = shift; $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; + ::logMsg("Launching usbdevices v.".$VERSION); + ::rptMsg("usbdevices v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); -# Code for System file, getting CurrentControlSet - my $current; - my $ccs; - my $key_path = 'Select'; my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - else { - ::rptMsg($key_path." not found."); - return; - } + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Enum\\USBStor"; + my $key; - my $name_path = $ccs."\\Control\\ComputerName\\ComputerName"; - my $comp_name; - eval { - $comp_name = $root_key->get_subkey($name_path)->get_value("ComputerName")->get_data(); - }; - $comp_name = "Test" if ($@); + my @vals = ("DeviceDesc","Mfg","Service","FriendlyName"); - $key_path = $ccs."\\Enum\\USBStor"; if ($key = $root_key->get_subkey($key_path)) { - + my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { + if (scalar @subkeys > 0) { foreach my $s (@subkeys) { - my $dev_class = $s->get_name(); + ::rptMsg($s->get_name()); my @sk = $s->get_list_of_subkeys(); - if (scalar(@sk) > 0) { + if (scalar @sk > 0) { foreach my $k (@sk) { - my $serial = $k->get_name(); - my $sn_lw = $k->get_timestamp(); - my $str = $comp_name.",".$dev_class.",".$serial.",".$sn_lw; + ::rptMsg(" ".$k->get_name()); - my $friendly; + foreach my $v (@vals) { + eval { + my $x = $k->get_value($v)->get_data(); + ::rptMsg(sprintf " %-15s: %-30s",$v,$x); + }; + } +# get Properties\{83da6326-97a6-4088-9453-a1923f573b29} eval { - $friendly = $k->get_value("FriendlyName")->get_data(); - $str .= ",".$friendly; + getProperties($k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}")); }; - $str .= ", " if ($@); - - my $parent; - eval { - $parent = $k->get_value("ParentIdPrefix")->get_data(); - $str .= ",".$parent; - - my $dev = checkMountedDevices($parent); - $str .= ",".$dev if ($dev); - - }; - - - ::rptMsg($str); } } + ::rptMsg(""); } } else { ::rptMsg($key_path." has no subkeys."); - ::logMsg($key_path." has no subkeys."); } } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } } -sub checkMountedDevices { - my $pip = shift; - my $root_key = $reg->get_root_key; - my $key_path = 'MountedDevices'; - my $key; - my %md; - if ($key = $root_key->get_subkey($key_path)) { - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - next unless ($name =~ m/^\\DosDevices/); - my $data = $v->get_data(); - if (length($data) > 12) { - $data =~ s/\x00//g; - return $name if (grep(/$pip/,$data)); - } - } - } - } - else { - return undef; - } - return undef; + +sub getProperties { + my $key = shift; + + eval { + my $r = $key->get_subkey("0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Install",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Inserted",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Inserted",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Removal",::format8601Date($t)."Z"); + }; + + } -1; + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/usbstor3.pl b/thirdparty/rr-full/plugins/usbstor3.pl deleted file mode 100644 index d8c1479341e..00000000000 --- a/thirdparty/rr-full/plugins/usbstor3.pl +++ /dev/null @@ -1,102 +0,0 @@ -#----------------------------------------------------------- -# usbstor3 -# Collects USBStor information, output in .csv -# -# History -# 20100312 - created -# -# -# copyright 2010 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package usbstor3; -use strict; - -my %config = (hive => "System", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20100312); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get USBStor key info"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching usbstor3 v.".$VERSION); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - -# Code for System file, getting CurrentControlSet - my $current; - my $ccs; - my $key_path = 'Select'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - else { - ::rptMsg($key_path." not found."); - return; - } - - $key_path = $ccs."\\Enum\\USBStor"; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg("USBStor"); -# ::rptMsg($key_path); -# ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { -# ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())."]"); - my $name1 = $s->get_name(); - my $time1 = gmtime($s->get_timestamp()); - - my @sk = $s->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $k (@sk) { - my $serial = $k->get_name(); -# ::rptMsg(" S/N: ".$serial." [".gmtime($k->get_timestamp())."]"); - my $str = $name1.",".$time1.",".$serial.",".gmtime($k->get_timestamp()); - - my $friendly; - eval { - $friendly = $k->get_value("FriendlyName")->get_data(); - $str .= ",".$friendly; - }; - $str .= "," if ($@); -# ::rptMsg(" FriendlyName : ".$friendly) if ($friendly ne ""); - my $parent; - eval { - $parent = $k->get_value("ParentIdPrefix")->get_data(); - $str .= ",".$parent; - }; - $str .= "," if ($@); -# ::rptMsg(" ParentIdPrefix: ".$parent) if ($parent ne ""); - ::rptMsg($str); - } - } -# ::rptMsg(""); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/usbstor_tln.pl b/thirdparty/rr-full/plugins/usbstor_tln.pl new file mode 100644 index 00000000000..3c52af3305c --- /dev/null +++ b/thirdparty/rr-full/plugins/usbstor_tln.pl @@ -0,0 +1,135 @@ +#----------------------------------------------------------- +# usbstor_tln.pl +# Parses contents of Enum\USB key for USB devices (not only USB storage devices) +# +# History +# 20220524 - created, copied from usbdevices_tln.pl +# +# References: +# http://www.swiftforensics.com/2013/11/windows-8-new-registry-artifacts-part-1.html +# https://www.researchgate.net/publication/318514858_USB_Storage_Device_Forensics_for_Windows_10 +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package usbstor_tln; +use strict; + +my %config = (hive => "System", + MITRE => "", + category => "devices", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "tln", + version => 20220524); + +sub getConfig{return %config} + +sub getShortDescr { + return "Parses Enum\\USBStor key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $reg; + +sub pluginmain { + my $class = shift; + my $hive = shift; + $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# ::logMsg("Launching usbdevices v.".$VERSION); +# ::rptMsg("usbdevices v.".$VERSION); +# ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + + my $key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Enum\\USBStor"; + my $key; + + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { +# ::rptMsg($s->get_name()); + my @sk = $s->get_list_of_subkeys(); + if (scalar @sk > 0) { + foreach my $k (@sk) { +# my $serial = $k->get_name(); + my $f = ""; + my $x = ""; + + eval { + $f = $k->get_value("FriendlyName")->get_data(); + }; + + eval { + $x = $k->get_value("DeviceDesc")->get_data(); + }; + + my $name = $f; + if ($f eq "") { + $name = $x; + } + +# get Properties\{83da6326-97a6-4088-9453-a1923f573b29} + eval { + getProperties($name,$k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}")); + }; + } + } + } + } + else { +# ::rptMsg($key_path." has no subkeys."); + } + } + else { +# ::rptMsg($key_path." not found."); + } +} + + +sub getProperties { + my $name = shift; + my $key = shift; + + eval { + my $r = $key->get_subkey("0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||First Install - ".$name); + }; + + eval { + my $r = $key->get_subkey("0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||First Inserted - ".$name); + }; + + eval { + my $r = $key->get_subkey("0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||Last Inserted - ".$name); + }; + + eval { + my $r = $key->get_subkey("0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||Last Removal - ".$name); + + }; + + +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/user_run.pl b/thirdparty/rr-full/plugins/user_run.pl deleted file mode 100644 index f0e6e5fbcb8..00000000000 --- a/thirdparty/rr-full/plugins/user_run.pl +++ /dev/null @@ -1,206 +0,0 @@ -#----------------------------------------------------------- -# user_run -# Get contents of Run key from NTUSER.DAT hive -# -# Change History -# 20140115 - added code to check for odd char in path -# 20130603 - updated alert functionality -# 20130425 - added alertMsg() functionality -# 20120329 - added additional keys -# 20130314 - updated to include Policies keys -# 20130313 - updated to include additional keys -# 20130115 - updated to include 64-bit, additional keys/values -# 20080328 - created -# -# References: -# http://msdn2.microsoft.com/en-us/library/aa376977.aspx -# http://support.microsoft.com/kb/170086 -# -# -# copyright 2013 Quantum Analytics Research, -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package user_run; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - version => 20140115); - -sub getConfig{return %config} - -sub getShortDescr { - return "[Autostart] Get autostart key contents from NTUSER.DAT hive"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Definition of the Run keys in the WinXP Registry" => - "http://support.microsoft.com/kb/314866"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching user_run v.".$VERSION); - ::rptMsg("user_run v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my @run = ("Software\\Microsoft\\Windows\\CurrentVersion\\Run", - "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", - "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices", - "Software\\Microsoft\\Windows\\CurrentVersion\\RunServicesOnce", - "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". - "Software\\Microsoft\\Windows\\CurrentVersion\\Run", - "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\". - "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", - "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run"); - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - foreach my $key_path (@run) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my %vals = getKeyValues($key); - if (scalar(keys %vals) > 0) { - foreach my $v (keys %vals) { -# added 20130603 - alertCheckPath($vals{$v}); - alertCheckExt($vals{$v}); - alertCheckADS($vals{$v}); - - ::rptMsg(" ".$v.": ".$vals{$v}); - } - } - else { - ::rptMsg(""); - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); - } - -# This section was added on 20130115 to address the 'run' and 'load' values that -# could be added to the key - my $key_path = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my $run; - my $count = 0; - eval { - $run = $key->get_value("Run")->get_data(); - ::rptMsg("Run value = ".$run); - ::alertMsg("ALERT: user_run: ".$key_path." Run value found: ".$run) unless ($run eq ""); - }; - if ($@) { - ::rptMsg("Run value not found."); - } - - eval { - $run = $key->get_value("run")->get_data(); - ::rptMsg("run value = ".$run); - ::alertMsg("ALERT: user_run: ".$key_path." run value found: ".$run) unless ($run eq ""); - }; - if ($@) { - ::rptMsg("run value not found."); - } - - my $load; - eval { - $load = $key->get_value("load")->get_data(); - ::rptMsg("load value = ".$load); - ::alertMsg("ALERT: user_run: ".$key_path." load value found: ".$load) unless ($load eq ""); - }; - if ($@) { - ::rptMsg("load value not found."); - } - - } -} - -sub getKeyValues { - my $key = shift; - my %vals; - - my @vk = $key->get_list_of_values(); - if (scalar(@vk) > 0) { - foreach my $v (@vk) { - next if ($v->get_name() eq "" && $v->get_data() eq ""); - $vals{$v->get_name()} = $v->get_data(); - } - } - else { - - } - return %vals; -} - -#----------------------------------------------------------- -# alertCheckPath() -#----------------------------------------------------------- -sub alertCheckPath { - my $path = shift; - $path = lc($path); - my @alerts = ("recycle","globalroot","temp","system volume information","appdata", - "application data"); - - foreach my $a (@alerts) { - if (grep(/$a/,$path)) { - ::alertMsg("ALERT: user_run: ".$a." found in path: ".$path); - } - } - - my $cnt = 0; - my @list = split(//,$path); - foreach my $n (@list) { - my $ch = ord($n); -# print $n." - ".$ch."\n"; - if ($ch < 0x20 || $ch > 0x7e) { - $cnt = 1; - } - } - ::alertMsg("ALERT: user_run: Odd char in path: ".$path) if ($cnt > 0); -} - -#----------------------------------------------------------- -# alertCheckExt() -#----------------------------------------------------------- -sub alertCheckExt { - my $path = shift; - $path = lc($path); - my @exts = ("\.com","\.bat","\.pif"); - - foreach my $e (@exts) { - if ($path =~ m/$e$/) { - ::alertMsg("ALERT: user_run: ".$path." ends in ".$e); - } - } -} -#----------------------------------------------------------- -# alertCheckADS() -#----------------------------------------------------------- -sub alertCheckADS { - my $path = shift; - my @list = split(/\\/,$path); - my $last = $list[scalar(@list) - 1]; - ::alertMsg("ALERT: user_run: Poss. ADS found in path: ".$path) if grep(/:/,$last); -} -1; diff --git a/thirdparty/rr-full/plugins/user_win.pl b/thirdparty/rr-full/plugins/user_win.pl deleted file mode 100644 index ee746e2b0e5..00000000000 --- a/thirdparty/rr-full/plugins/user_win.pl +++ /dev/null @@ -1,62 +0,0 @@ -#----------------------------------------------------------- -# user_win.pl -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package user_win; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20080415); - -sub getConfig{return %config} - -sub getShortDescr { - return " -- "; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching user_win v.".$VERSION); - ::rptMsg("user_win v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - eval { - my $load = $key->get_value("load")->get_data(); - ::rptMsg("load value = ".$load); - ::rptMsg("*Should be blank; anything listed gets run when the user logs in."); - }; - - eval { - my $run = $key->get_value("run")->get_data(); - ::rptMsg("run value = ".$run); - ::rptMsg("*Should be blank; anything listed gets run when the user logs in."); - }; - - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/userassist.pl b/thirdparty/rr-full/plugins/userassist.pl index 7d4813781ce..5b5e5cc4bad 100644 --- a/thirdparty/rr-full/plugins/userassist.pl +++ b/thirdparty/rr-full/plugins/userassist.pl @@ -5,6 +5,9 @@ # UserAssist values # # Change history +# 20230710 - added check of NoLog value +# 20200916 - MITRE updates +# 20200513 - updated date output format # 20170304 - removed alerts, added printing of values with no timestamps in the data # 20130603 - added alert functionality # 20100322 - Added CLSID list reference @@ -23,8 +26,10 @@ package userassist; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20170204); + MITRE => "T1204", + category => "program execution", + output => "report", + version => 20230710); sub getConfig{return %config} sub getShortDescr { @@ -50,8 +55,24 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("UserAssist"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); +#----------------------------------------------------------------------------- +# Added 20230710 +# Ref: https://blog.didierstevens.com/programs/userassist/ + eval { + my $n = $key->get_subkey("Settings")->get_value("NoLog")->get_data(); + if ($n == 1) { + ::rptMsg("Settings\\NoLog value set to \"1\", disabling creation of new entries on XP."); + } + }; + ::rptMsg("Settings\\NoLog value not found.") if ($@); + ::rptMsg(""); + ::rptMsg("Analysis Tip: The \"Settings\\NoLog\" value set to \"1\" disables the creation of new entries on XP."); + ::rptMsg(""); + ::rptMsg("Ref: https://blog.didierstevens.com/programs/userassist/"); + ::rptMsg(""); +#----------------------------------------------------------------------------- my @subkeys = $key->get_list_of_subkeys(); if (scalar(@subkeys) > 0) { foreach my $s (@subkeys) { @@ -128,7 +149,7 @@ sub processKey { } } foreach my $t (reverse sort {$a <=> $b} keys %ua) { - ::rptMsg(gmtime($t)." Z"); + ::rptMsg(::format8601Date($t)."Z"); foreach my $i (@{$ua{$t}}) { ::rptMsg(" ".$i); } diff --git a/thirdparty/rr-full/plugins/userassist_tln.pl b/thirdparty/rr-full/plugins/userassist_tln.pl index 3c6ca303f2e..7aa382794fc 100644 --- a/thirdparty/rr-full/plugins/userassist_tln.pl +++ b/thirdparty/rr-full/plugins/userassist_tln.pl @@ -5,6 +5,7 @@ # UserAssist values # # Change history +# 20200916 - MITRE updates # 20180710 - removed alert functionality # 20130603 - added alert functionality # 20110516 - created, modified from userassist2.pl @@ -24,8 +25,10 @@ package userassist_tln; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180710); + MITRE => "T1204", + category => "program execution", + output => "tln", + version => 20200916); sub getConfig{return %config} sub getShortDescr { diff --git a/thirdparty/rr-full/plugins/userextendedproperties.pl b/thirdparty/rr-full/plugins/userextendedproperties.pl new file mode 100644 index 00000000000..06a95d4ccb1 --- /dev/null +++ b/thirdparty/rr-full/plugins/userextendedproperties.pl @@ -0,0 +1,71 @@ +#----------------------------------------------------------- +# userextendedproperties.pl +# +# Change history +# 20220509 - created +# +# References +# +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package userextendedproperties; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + category => "identity", + MITRE => "", + output => "report", + version => 20220509); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets MS Live ID and account name mapping"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching userextendedproperties v.".$VERSION); + ::rptMsg("userextendedproperties v.".$VERSION); + ::rptMsg("- ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Software\\Microsoft\\IdentityCRL\\UserExtendedProperties'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + ::rptMsg("Name : ".$s->get_name()); + ::rptMsg("LastWrite : ".::format8601Date($s->get_timestamp())."Z"); + eval { + my $cid = $s->get_value("cid")->get_data(); + ::rptMsg("Microsoft ID: ".$cid); + }; + ::rptMsg(""); + } + } + else { + ::rptMsg($key_path." has no subkeys."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/userinfo.pl b/thirdparty/rr-full/plugins/userinfo.pl deleted file mode 100644 index fb1db594fec..00000000000 --- a/thirdparty/rr-full/plugins/userinfo.pl +++ /dev/null @@ -1,87 +0,0 @@ -#----------------------------------------------------------- -# userinfo.pl -# Plugin for Registry Ripper, NTUSER.DAT edition - gets the -# MS Office UserInfo values -# -# Change history -# 20130513 - added check for UserName in Common key -# 20110609 - created -# -# References -# Based on Joe G.'s post to ForensicArtifacts.com -# -# -# copyright 2011 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package userinfo; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20130513); - -sub getConfig{return %config} -sub getShortDescr { - return "Gets contents of MS Office UserInfo values"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching userinfo v.".$VERSION); - ::rptMsg("userinfo v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $key_path = 'Software\\Microsoft\\Office\\Common'; - if (my $key = $root_key->get_subkey($key_path)) { - my $username; - eval { - $username = $key->get_value("UserName")->get_data(); - ::rptMsg($key_path."\\UserName = ".$username); - }; - - } - else { - ::rptMsg($key_path." not found\."); - } - - ::rptMsg(""); - my %keys = (2003 => 'Software\\Microsoft\\Office\\11\.0\\Common\\UserInfo', - 2007 => 'Software\\Microsoft\\Office\\Common\\UserInfo'); - - foreach my $k (keys %keys) { - my $key_path = $keys{$k}; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my @vals = $key->get_list_of_values(); - if (scalar (@vals) > 0) { - foreach my $v (@vals) { - ::rptMsg(sprintf " %-15s %-20s",$v->get_name(),$v->get_data()); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); - } -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/userlocsvc.pl b/thirdparty/rr-full/plugins/userlocsvc.pl deleted file mode 100644 index 0cd4737c44c..00000000000 --- a/thirdparty/rr-full/plugins/userlocsvc.pl +++ /dev/null @@ -1,64 +0,0 @@ -#! c:\perl\bin\perl.exe -#----------------------------------------------------------- -# userlocsvc.pl -# Get the contents of the Microsoft\User Location Service\Clients key -# from the user's hive -# -# Ref: -# http://support.microsoft.com/kb/196301 -# -# copyright 2009 H. Carvey -#----------------------------------------------------------- -package userlocsvc; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20090411); - -sub getConfig{return %config} -sub getShortDescr { - return "Displays contents of User Location Service\\Client key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching userlocsvc v.".$VERSION); - ::rptMsg("userlocsvc v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - my $key_path = 'Software\\Microsoft\\User Location Service\\Client'; - my $key; - my %ua; - my $hrzr = "HRZR"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $str = sprintf "%-15s %-30s",$v->get_name(),$v->get_data(); - ::rptMsg($str) if ($v->get_type() == 1); - } - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/usn.pl b/thirdparty/rr-full/plugins/usn.pl new file mode 100644 index 00000000000..80cfbfcb664 --- /dev/null +++ b/thirdparty/rr-full/plugins/usn.pl @@ -0,0 +1,86 @@ +#----------------------------------------------------------- +# usn.pl +# +# +# History: +# 20220104 - created +# +# References: +# https://docs.microsoft.com/en-us/windows-server/storage/fsrm/fsrm-overview +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package usn; +use strict; + +my %config = (hive => "system", + output => "report", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562", + version => 20220101); + +sub getConfig{return %config} +sub getShortDescr { + return "Get USN change journal settings on Windows Server"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching usn v.".$VERSION); + ::rptMsg("usn v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + + my $key_path = $ccs."\\Services\\SrmSvc\\Settings"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Keypath: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $v1 = $key->get_value("SkipUSNCreationForSystem")->get_data(); + ::rptMsg("SkipUSNCreationForSystem value: ".$v1); + ::rptMsg(""); +# ::rptMsg("0 - disabled"); + ::rptMsg("1 - USN Change Journal disabled on the system"); + }; + ::rptMsg("SkipUSNCreationForSystem value not found\."); + + + eval { + my $v2 = $key->get_value("SkipUSNCreationForVolumes")->get_data(); + ::rptMsg(""); + ::rptMsg("SkipUSNCreationForVolumes value: ".$v2); + ::rptMsg("USN Change Journal disabled on the listed volumes"); + }; + ::rptMsg("SkipUSNCreationForVolumes value not found\."); + + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: USN Change Journal creation can be disabled on Windows Server\. The USN Change Journal is "); + ::rptMsg("recognized as a valuable investigative resource, and disabling it can significantly inhibit an investigation\."); + ::rptMsg(""); + ::rptMsg("Ref: https://docs.microsoft.com/en-us/windows-server/storage/fsrm/fsrm-overview"); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/usrclass b/thirdparty/rr-full/plugins/usrclass index d1bc5b2fbe7..bbbdd2e9855 100755 --- a/thirdparty/rr-full/plugins/usrclass +++ b/thirdparty/rr-full/plugins/usrclass @@ -1,8 +1,12 @@ -assoc -cmd_shell_u -msedge_win10 +appx +appx_tln +clsid +clsid_tln muicache photos -photos_win10 +recyclepersist +scriptleturl shellbags -shellbags_test +shellbags_tln +shellbags_xp +uacbypass diff --git a/thirdparty/rr-full/plugins/utilities.pl b/thirdparty/rr-full/plugins/utilities.pl new file mode 100644 index 00000000000..cd6f0bc8eae --- /dev/null +++ b/thirdparty/rr-full/plugins/utilities.pl @@ -0,0 +1,86 @@ +#----------------------------------------------------------- +# utilities.pl +# +# +# History +# 20221231 - created +# +# References +# https://twitter.com/0gtweet/status/1607690354068754433 +# +# copyright 2022-2023 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package utilities; +use strict; +my %config = (hive => "System", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + category => "persistence", + output => "report", + version => 20221231); + +sub getConfig{return %config} +sub getShortDescr { + return "Get TS Utilities subkey values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + my $key; + + ::logMsg("Launching utilities v.".$VERSION); + ::rptMsg("utilities v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("Category: ".$config{category}." - ".$config{MITRE}); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\Terminal Server\\Utilities"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg($key_path."\\".$s->get_name()); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + my @values = $s->get_list_of_values(); + if (scalar @values > 0) { + foreach my $v (@values) { + my $str = $v->get_data(); + $str =~ s/\00/\s/g; + ::rptMsg(sprintf "%-15s %-15s",$v->get_name(),$str); + } + ::rptMsg(""); + } + else { + ::rptMsg("Key ".$s->get_name()." has no values."); + } + } + } + else { + ::rptMsg($key_path." has no subkeys."); + } + +# ::rptMsg(""); + ::rptMsg("Analysis Tip: The \"query\" subkey beneath \"\\Terminal Server\\Utilities\" can be used for persistence. Look for "); + ::rptMsg("unusual value names."); + ::rptMsg(""); + ::rptMsg("Ref: https://twitter.com/0gtweet/status/1607690354068754433"); + } + else { + ::rptMsg($key_path." not found."); + } +} +1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/utorrent.pl b/thirdparty/rr-full/plugins/utorrent.pl deleted file mode 100644 index b38e27b6758..00000000000 --- a/thirdparty/rr-full/plugins/utorrent.pl +++ /dev/null @@ -1,149 +0,0 @@ -#------------------------------------------------------------------------------ -# uTorrent -# Shows path where uTorrent client installed (default is C:\Users\\AppData\Roaming\uTorrent) -# Version of uTorrent client installed -# Computer ID (should match 'cids' entry in settings.dat) -# -# Change history -# 20180615 - first release -# -# References -# n/a -# -# Copyright -# Michael Godfrey (c) 2018 -# mgodfrey [at] gmail.com -# -#------------------------------------------------------------------------------ - -package utorrent; -use strict; - -my %config = -( - hive => "NTUSER\.DAT", - hasShortDescr => 0, - hasDescr => 1, - hasRefs => 1, - osmask => 29, - version => 20180615 -); - -sub getConfig {return %config;} -sub getDescr {return "Shows uTorrent client install path, version and Unique ID of computer";} -sub getRefs {return "n/a";} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain -{ - my $class = shift; - my $hive = shift; - ::logMsg('Launching uTorrent v'.$VERSION); - ::rptMsg('utorrent v'.$VERSION.' ('.getDescr().")"); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - enum_recursively ($root_key, "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\uTorrent", 1,""); - enum_recursively ($root_key, "Software\\BitTorrent", 1,""); -} - -sub hexify -{ -my $data = shift; -my $l=''; -my $r=''; -my $n=0; -my $nd=''; -for (my $i=0; $i15) - - { - $nd.=sprintf("%-48s%s\n", $l,$r); - $l='';$r='';$n=0; - } -} -if ($n!=0) - { - $nd.=sprintf("%-48s%s\n", $l,$r); - - } -return $nd; -} - -sub enum_recursively -{ -my $root_key = shift; -my $key_path = shift; -my $rec_level = shift; -return if ($rec_level>3); -my $find = shift;$find = '.' if $find eq ''; -my $key; -my $key_printed=0; -my $sep = ' ' x 2; - -if ($key = $root_key->get_subkey($key_path)) -{ - - $sep = ' ' x 4; - my @vals = $key->get_list_of_values(); - my %ac_vals; - foreach my $v (sort {lc($a) <=> lc($b)} @vals) - { - my $vd = $v->get_data(); - my $vt = $v->get_type_as_string(); - if ($vt !~ /REG_(DWORD|SZ|EXPAND_SZ)/) - { - $vd = hexify($vd); - } - $ac_vals{$v->get_name()}{'VT'} = $vt; - $ac_vals{$v->get_name()}{'VD'} = $vd; - } - foreach my $a (sort {lc($a) <=> lc($b)} keys %ac_vals) - { - my $ax = $a; $ax = '(Default)' if $a eq ''; - my $vt = $ac_vals{$a}{'VT'}; - my $vd = $ac_vals{$a}{'VD'}; - if (($a.$vd) ne ''&& ($ax.$a.$vd) =~/$find/is) - { - if ($key_printed==0) - { - ::rptMsg("\n"); - ::rptMsg($sep.$key_path); - ::rptMsg($sep.'LastWrite Time '.gmtime($key->get_timestamp())." (UTC)\n"); - $key_printed=1; - } - $sep = ' ' x 4; - ::rptMsg($sep.$ax); - $sep = ' ' x 6; - ::rptMsg($sep.$vt); - $sep = ' ' x 8; - if ($vt !~ /REG_(DWORD|SZ|EXPAND_SZ)/) - { - $vd =~ s/[\n]+/\n$sep/sg; - } - ::rptMsg($sep.$vd); - } - - } - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) - { - foreach my $s (@subkeys) - { - enum_recursively ($root_key , $key_path."\\".$s->get_name(), $rec_level + 1,$find); - } - } -} -else -{ - ::rptMsg($sep.$key_path.' not found.'); -} -} diff --git a/thirdparty/rr-full/plugins/vawtrak.pl b/thirdparty/rr-full/plugins/vawtrak.pl deleted file mode 100644 index 08a21ddbfc5..00000000000 --- a/thirdparty/rr-full/plugins/vawtrak.pl +++ /dev/null @@ -1,127 +0,0 @@ -#----------------------------------------------------------- -# vawtrak.pl -# -# -# Change history -# 20131010 - created -# -# References -# http://www.microsoft.com/security/portal/threat/encyclopedia/entry.aspx?Name=Backdoor:Win32/Vawtrak.A#tab=2 -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package vawtrak; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - category => "malware", - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20131010); - -sub getConfig{return %config} -sub getShortDescr { - return "Checks for possible VawTrak infection"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg("Launching vawtrak v.".$VERSION); - ::rptMsg("vawtrak v.".$VERSION); # banner - ::rptMsg(getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - my $count = 0; - my $key_path; - - my @paths = ('Software\\Microsoft\\Windows\\CurrentVersion\\Run', - 'Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run'); - my $key; - - foreach $key_path (@paths) { - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); - my $lcdata = $data; - $lcdata =~ tr/[A-Z]/[a-z]/; - if ($lcdata =~ m/^regsvr32/ && $lcdata =~ m/\.dat$/) { - ::rptMsg("Possible Vawtrak infection: ".$name." - ".$data); - $count++; - } - } - } - else { - ::rptMsg($key_path." has no values\."); - } - } - else { - ::rptMsg($key_path." not found."); - } - ::rptMsg(""); - } - - $key_path = 'Software\\Microsoft\\Internet Explorer\\Main'; - if ($key = $root_key->get_subkey($key_path)) { - - eval { - my $banner = $key->get_value("NoProtectedModeBanner")->get_data(); - ::rptMsg($key_path."\\NoProtectedModeBanner value = ".$banner); - ::rptMsg(""); - if ($banner == 1) { - ::rptMsg("Internet Explorer\\Main\\NoProtectedModeBanner set to 0x1: possible Vawtrak infection\."); - $count++; - ::rptMsg(""); - } - }; - - eval { - my $tab = $key->get_value("TabProcGrowth")->get_data(); - ::rptMsg($key_path."\\TabProcGrowth value = ".$tab); - ::rptMsg(""); - if ($tab == 0) { - ::rptMsg("Internet Explorer\\Main\\TabProcGrowth value set to 0x0: possible VawTrak infection\.n"); - $count++; - ::rptMsg(""); - } - }; - - } - else { - ::rptMsg($key_path." not found\."); - } - - $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3'; - if ($key = $root_key->get_subkey($key_path)) { - eval { - my $val = $key->get_value("2500")->get_data(); - ::rptMsg($key_path."\\2500 value = ".$val); - ::rptMsg(""); - if ($val == 0x3) { - ::rptMsg("Internet Settings\\Zones\\3\\2500 value is set to 0x3: possible Vawtrak infection\."); - ::rptMsg(""); - $count++; - } - }; - } - else { - ::rptMsg($key_path." not found\."); - } - ::rptMsg("Final Score: ".$count."/4 checks succeeded\."); -} - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/virut.pl b/thirdparty/rr-full/plugins/virut.pl deleted file mode 100644 index 3188b3c514a..00000000000 --- a/thirdparty/rr-full/plugins/virut.pl +++ /dev/null @@ -1,72 +0,0 @@ -#----------------------------------------------------------- -# virut.pl -# Plugin to detect artifacts of a Virut infection -# -# References: -# Symantec: http://www.symantec.com/security_response/ -# writeup.jsp?docid=2009-020411-2802-99&tabid=2 -# -# Change History: -# 20130425 - added alertMsg() functionality -# 20090218 - created -# -# -# copyright 2013 QAR, LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package virut; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20130425); - -sub getConfig{return %config} - -sub getShortDescr { - return "Detect Virut artifacts"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching virut v.".$VERSION); - ::rptMsg("virut v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Windows\\CurrentVersion\\Explorer"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $update; - eval { - $update = $key->get_value("UpdateHost")->get_data(); - ::rptMsg("UpdateHost value detected! Possible Virut infection!"); - ::alertMsg("ALERT: virut: UpdateHost value detected! Possible Virut infection!"); - }; - ::rptMsg("UpdateHost value not found.") if ($@); - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - ::rptMsg(""); - ::rptMsg("Also be sure to check the SYSTEM\\ControlSet00n\\Services\\SharedAccess\\"); - ::rptMsg("Parameters\\FirewallPolicy\\DomainProfile\\AuthorizedApplications\\List key"); - ::rptMsg("for exceptions added to the firewall; use the fw_config\.pl plugin."); -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/vista_bitbucket.pl b/thirdparty/rr-full/plugins/vista_bitbucket.pl deleted file mode 100644 index 368c6c43c40..00000000000 --- a/thirdparty/rr-full/plugins/vista_bitbucket.pl +++ /dev/null @@ -1,96 +0,0 @@ -#----------------------------------------------------------- -# vista_bitbucket.pl -# BitBucket settings for Vista $Recylce.bin are maintained on a -# per-user, per-volume basis -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package vista_bitbucket; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 192, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20080420); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get BitBucket settings from Vista via NTUSER.DAT"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching vista_bitbucket v.".$VERSION); - ::rptMsg("vista_bitbucket v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - ::rptMsg($v->get_name()." : ".$v->get_data()); - } - - } - else { - ::rptMsg($key_path." has no values."); - } - ::rptMsg(""); - - my @vols; - eval { - @vols = $key->get_subkey("Volume")->get_list_of_subkeys(); - }; - if ($@) { - ::rptMsg("Could not access ".$key_path."\\Volume subkey."); - return; - } - - if (scalar(@vols) > 0) { - foreach my $v (@vols) { - ::rptMsg($v->get_name()." [".gmtime($v->get_timestamp())."] (UTC)"); - eval { - ::rptMsg(sprintf " %-15s %-3s","NukeOnDelete",$v->get_value("NukeOnDelete")->get_data()); - }; - - - } - - } - else { - ::rptMsg($key_path."\\Volume key has no subkeys."); - } - - - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - -} -1; diff --git a/thirdparty/rr-full/plugins/vmplayer.pl b/thirdparty/rr-full/plugins/vmplayer.pl deleted file mode 100644 index 8069fbc1b5b..00000000000 --- a/thirdparty/rr-full/plugins/vmplayer.pl +++ /dev/null @@ -1,94 +0,0 @@ -#----------------------------------------------------------- -# vmplayer.pl -# Extracts full filepath for recent VMware Player VM images -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright (c) 2011-02-04 Brendan Coles -#----------------------------------------------------------- -# Require # -package vmplayer; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110204); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts full filepath for recent VMware Player VM images."; -} -sub getRefs { - my %refs = ("VMware Player Homepage:" => - "http://www.vmware.com/products/player/"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - - # Initialize # - ::logMsg("Launching vmplayer v.".$VERSION); - ::rptMsg("vmplayer v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\VMware, Inc.\\VMware Player\\VMplayer\\Window position"; - - # If # VMware Player path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("VMware Player"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from VMware Player registry path # - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for VMware Player registry path # - foreach my $v (@vals) { - ::rptMsg($v->get_name()." -> ".$v->get_data()); - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # VMware Player isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/vmware_vsphere_client.pl b/thirdparty/rr-full/plugins/vmware_vsphere_client.pl deleted file mode 100644 index 9bf3529709c..00000000000 --- a/thirdparty/rr-full/plugins/vmware_vsphere_client.pl +++ /dev/null @@ -1,108 +0,0 @@ -#----------------------------------------------------------- -# vmware_vsphere_client.pl -# Extract recent connections list for VMware vSphere Client -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright (c) 2011-02-04 Brendan Coles -#----------------------------------------------------------- -# Require # -package vmware_vsphere_client; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110204); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extract recent connections list for VMware vSphere Client."; -} -sub getRefs { - my %refs = ("VMware vSphere Client Homepage:" => - "http://www.vmware.com/products/vsphere/"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - my @interesting_paths = ( - 'Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\ClientsXml', - 'Software\\VMware\\VMware Infrastructure Client\\Preferences' - ); - - # Initialize # - ::logMsg("Launching vmware_vsphere_client v.".$VERSION); - ::rptMsg("vmware_vsphere_client v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - # Extract # possible registry paths - foreach my $key_path (@interesting_paths) { - - # If # VMware vSphere Client path exists # - my $xml; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("VMware vSphere Client"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from VMware vSphere Client registry path # - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Return # all key names+values for VMware vSphere Client registry path # - foreach my $v (@vals) { - # Format # XML data with no new line characters - $xml = $v->get_data(); - $xml =~ s/>\s*\r*\n*/>/g; - ::rptMsg($v->get_name()." -> ".$xml); - } - # Return # obligatory new-line # - ::rptMsg(""); - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # VMware vSphere Client isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - - } - - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/vnchooksapplicationprefs.pl b/thirdparty/rr-full/plugins/vnchooksapplicationprefs.pl deleted file mode 100644 index 32b3163411b..00000000000 --- a/thirdparty/rr-full/plugins/vnchooksapplicationprefs.pl +++ /dev/null @@ -1,70 +0,0 @@ -#----------------------------------------------------------- -# vnchooksapplicationprefs.pl -# read application preference keys for apps launched in VNC session. -# Beta version. -# -# Change history -# 20110208 [sme] % created -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# Copyright 2011 SecurityMetrics, Inc. -#----------------------------------------------------------- -package vnchooksapplicationprefs; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20110208); - -sub getConfig{return %config} -sub getShortDescr { - return "Get VNCHooks Application Prefs list"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching vnchookapplicationprefs v.".$VERSION); - ::rptMsg("vnchookapplicationprefs v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Software\\ORL\\VNCHooks\\Application_Prefs"; - my $app_pref; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("VNCHooks\\Application_Prefs"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @apps = $key->get_list_of_subkeys(); - if (scalar(@apps) > 0) { - foreach my $a (@apps) { - ::rptMsg($a->get_name()); - ::rptMsg(" ".gmtime($a->get_timestamp())." Z"); - } - } - else { - ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/vncviewer.pl b/thirdparty/rr-full/plugins/vncviewer.pl deleted file mode 100644 index f6d57dad6b0..00000000000 --- a/thirdparty/rr-full/plugins/vncviewer.pl +++ /dev/null @@ -1,105 +0,0 @@ -#----------------------------------------------------------- -# vncviewer -# -# -# History: -# 20121231 - Updated to include VNCViewer4 -# 20080325 - created -# -# -# -#----------------------------------------------------------- -package vncviewer; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20121231); - -sub getConfig{return %config} -sub getShortDescr { - return "Get VNCViewer system list"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching vncviewer v.".$VERSION); - ::rptMsg("vncviewer v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Software\\ORL\\VNCviewer\\MRU"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("VNCViewer\\MRU"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %vnc; - foreach my $v (@vals) { - $vnc{$v->get_name()} = $v->get_data(); - } - my $ind; - if (exists $vnc{'index'}) { - $ind = $vnc{'index'}; - delete $vnc{'index'}; - } - - ::rptMsg("Index = ".$ind); - my @i = split(//,$ind); - foreach my $i (@i) { - ::rptMsg(" ".$i." -> ".$vnc{$i}); - } - ::rptMsg(""); - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } - - $key_path = "Software\\RealVNC\\VNCViewer4\\MRU"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - my $type = $v->get_type(); - my $data; - if ($type == 3) { - $data = $v->get_data_as_string(); - } - else { - $data = $v->get_data(); - } - - ::rptMsg(sprintf "%-8s %-25s",$name,$data); - } - - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/volinfocache.pl b/thirdparty/rr-full/plugins/volinfocache.pl index 08ec6027b63..64d60f472cc 100644 --- a/thirdparty/rr-full/plugins/volinfocache.pl +++ b/thirdparty/rr-full/plugins/volinfocache.pl @@ -5,6 +5,8 @@ # and after seeing what was in it, I just wrote up a plugin # # History: +# 20200916 - MITRE updates +# 20200518 - updated date output format # 20120822 - added drive types hash based on MS KB161300 # 20120716 - created # @@ -18,8 +20,10 @@ package volinfocache; hasShortDescr => 1, hasDescr => 0, hasRefs => 1, - osmask => 22, - version => 20120822); + MITRE => "", + category => "devices", + output => "report", + version => 20200916); sub getConfig{return %config} sub getShortDescr { @@ -43,7 +47,6 @@ sub pluginmain { 0x5 => "CDROM", 0x6 => "RAMDISK"); - ::logMsg("Launching volinfocache v.".$VERSION); ::rptMsg("Launching volinfocache v.".$VERSION); ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner my $reg = Parse::Win32Registry->new($hive); @@ -58,7 +61,7 @@ sub pluginmain { foreach my $s (@subkeys) { my $name = $s->get_name(); my $ts = $s->get_timestamp(); - ::rptMsg($name." - LastWrite: ".gmtime($ts)); + ::rptMsg($name." - LastWrite time: ".::format8601Date($ts)."Z"); my $type; eval { diff --git a/thirdparty/rr-full/plugins/volsnap.pl b/thirdparty/rr-full/plugins/volsnap.pl new file mode 100644 index 00000000000..3d453698600 --- /dev/null +++ b/thirdparty/rr-full/plugins/volsnap.pl @@ -0,0 +1,84 @@ +#----------------------------------------------------------- +# volsnap.pl +# Values beneath VSS\Diag subkeys (including VolSnap) have timestamps embedded in +# the data; wrote the plugin to extract the info, to be used in research to determine +# if there's value to the data +# +# History: +# 20210128 - created +# +# References: +# https://twitter.com/0gtweet/status/1354766164166115331 +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package volsnap; +use strict; + +my %config = (hive => "System", + category => "", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20210128); + +sub getConfig{return %config} +sub getShortDescr { + return "Check VSS\\Diag settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching volsnap v.".$VERSION); + ::rptMsg("volsnap v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my @subkeys = ("VolSnap","SPP","SystemRestore"); + my $key_path = $ccs."\\Services\\VSS\\Diag"; + my $key = (); + if ($key = $root_key->get_subkey($key_path)) { + foreach my $s (@subkeys) { + if (my $k = $key->get_subkey($s)) { + my @vals = $k->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + + my $name = $v->get_name(); + my $data = $v->get_data(); + my ($t0,$t1) = unpack("VV",substr($data,8,8)); + my $ts = ::format8601Date(::getTime($t0,$t1)); + + ::rptMsg($ts."Z ".$s."\\".$name); + + } + } + } + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: No tip; as of 20210128, this plugin is for testing purposes."); +# ::rptMsg(""); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/volsnap_tln.pl b/thirdparty/rr-full/plugins/volsnap_tln.pl new file mode 100644 index 00000000000..bec7784cc92 --- /dev/null +++ b/thirdparty/rr-full/plugins/volsnap_tln.pl @@ -0,0 +1,81 @@ +#----------------------------------------------------------- +# volsnap.pl +# Values beneath VSS\Diag subkeys (including VolSnap) have timestamps embedded in +# the data; wrote the plugin to extract the info, to be used in research to determine +# if there's value to the data +# +# History: +# 20210128 - created +# +# References: +# https://twitter.com/0gtweet/status/1354766164166115331 +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package volsnap_tln; +use strict; + +my %config = (hive => "System", + category => "", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "tln", + version => 20210128); + +sub getConfig{return %config} +sub getShortDescr { + return "Check VSS\\Diag settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::logMsg("Launching volsnap v.".$VERSION); +# ::rptMsg("volsnap v.".$VERSION); +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my @subkeys = ("VolSnap","SPP","SystemRestore"); + my $key_path = $ccs."\\Services\\VSS\\Diag"; + my $key = (); + if ($key = $root_key->get_subkey($key_path)) { + foreach my $s (@subkeys) { + if (my $k = $key->get_subkey($s)) { + my @vals = $k->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + + my $name = $v->get_name(); + my $data = $v->get_data(); + my ($t0,$t1) = unpack("VV",substr($data,8,8)); + my $ts = ::getTime($t0,$t1); + + ::rptMsg($ts."|REG|||VSS\\Diag\\".$s."\\".$name); + + } + } + } + } + } + else { +# ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/volumecaches.pl b/thirdparty/rr-full/plugins/volumecaches.pl new file mode 100644 index 00000000000..cd356d0e18b --- /dev/null +++ b/thirdparty/rr-full/plugins/volumecaches.pl @@ -0,0 +1,114 @@ +#----------------------------------------------------------- +# volumecaches +# +# Change history: +# 20221101 - created +# +# Ref: +# https://ss64.com/nt/cleanmgr-registry.html +# https://www.hexacorn.com/blog/2018/09/02/beyond-good-ol-run-key-part-86/ +# https://learn.microsoft.com/en-us/windows/win32/lwef/disk-cleanup?redirectedfrom=MSDN#registration +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package volumecaches; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1070\.004", + output => "report", + version => 20221101); + +sub getConfig{return %config} +sub getShortDescr { + return "Check VolumeCaches settings for use with cleanmgr"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching volumecaches v.".$VERSION); + ::rptMsg("volumecaches v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE ATT&CK: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $key_path = ('Microsoft\\Windows\\CurrentVersion\\Explorer\\VolumeCaches'); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $count = 0; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + if (checkForStateFlags($s)) { + + ::rptMsg($key_path."\\".$s->get_name()); + ::rptMsg("LastWrite Time ".::format8601Date($s->get_timestamp())."Z"); + ::rptMsg(""); + + getStateFlagsValue($s); + $count++; + } + } + ::rptMsg("No StateFlagsXXXX values found.") if ($count == 0); + } + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: StateFlagsXXXX values beneath the VolumeCaches subkeys can be used via cleanmgr\.exe to automate"); + ::rptMsg("cleanup operations by deleting files. Ex: \"cleanmgr /sagerun:64\" will clean all folders with \"StateFlags0064\""); + ::rptMsg("values set to \"2\", deleting the files in those folders; setting the value to \"0\" will disable this activity."); +# ::rptMsg(""); + ::rptMsg(""); + ::rptMsg("Ref: https://ss64.com/nt/cleanmgr-registry.html"); +} + +sub checkForStateFlags { + my $key = shift; + + my $flag = 0; + my $tag = "StateFlags"; + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + $flag = 1 if ($v->get_name() =~ m/^$tag/); + } + } + return $flag; +} + +sub getStateFlagsValue { + my $key = shift; + my $tag = "StateFlags"; + + my @vals = $key->get_list_of_values(); + if (scalar @vals > 0) { + foreach my $v (@vals) { + if ($v->get_name() =~ m/^$tag/) { + ::rptMsg(sprintf "%-16s 0x%04x",$v->get_name(),$v->get_data()); + } + } + } + +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/vss.pl b/thirdparty/rr-full/plugins/vss.pl new file mode 100644 index 00000000000..fa8e7671390 --- /dev/null +++ b/thirdparty/rr-full/plugins/vss.pl @@ -0,0 +1,74 @@ +#----------------------------------------------------------- +# vss.pl +# +# History: +# 20210128 - created +# +# References: +# https://twitter.com/0gtweet/status/1354766164166115331 +# https://support.hpe.com/hpesc/public/docDisplay?docLocale=en_US&docId=a00091959en_us +# +# https://attack.mitre.org/techniques/T1562/001/ +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package vss; +use strict; + +my %config = (hive => "System", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562\.001", + output => "report", + version => 20210128); + +sub getConfig{return %config} +sub getShortDescr { + return "Check VSS\\Diag settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching vss v.".$VERSION); + ::rptMsg("vss v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services\\VSS\\Diag"; + my $key = (); + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + eval { + my $dis = $key->get_value("")->get_data(); + ::rptMsg("(Default) value = ".$dis); + }; + ::rptMsg("(Default) value not found.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: A \"(Default)\" setting of \"Disabled\" disables VSS Legacy Tracing, and prevents"); + ::rptMsg("Windows Backup from running. If the value is set, no reboot is required."); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wab.pl b/thirdparty/rr-full/plugins/wab.pl new file mode 100644 index 00000000000..97c0add4ce8 --- /dev/null +++ b/thirdparty/rr-full/plugins/wab.pl @@ -0,0 +1,70 @@ +#----------------------------------------------------------- +# wab.pl +# +# Get WAB DLLPath value +# +# Change history +# 20200916 - MITRE updates +# 20200427 - updated output date format +# 20191122 - created +# +# References +# https://lolbas-project.github.io/lolbas/Binaries/Wab/ +# +# Copyright 2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wab; +use strict; + +my %config = (hive => "Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + category => "persistence", + output => "report", + version => 20200916); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Get WAB DLLPath settings"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + + ::logMsg("Launching wab v.".$VERSION); + ::rptMsg("wab v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = "Microsoft\\WAB\\DLLPath"; + + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my $def = ""; + eval { + $def = $key->get_value("")->get_data(); + ::rptMsg("(Default) value = ".$def); + }; + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; diff --git a/thirdparty/rr-full/plugins/wab_tln.pl b/thirdparty/rr-full/plugins/wab_tln.pl new file mode 100644 index 00000000000..b5d13747c57 --- /dev/null +++ b/thirdparty/rr-full/plugins/wab_tln.pl @@ -0,0 +1,69 @@ +#----------------------------------------------------------- +# wab_tln.pl +# +# Get WAB DLLPath value +# +# Change history +# 20200916 - MITRE updates +# 20191122 - created +# +# References +# https://lolbas-project.github.io/lolbas/Binaries/Wab/ +# +# Copyright 2019-2020 QAR, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wab_tln; +use strict; + +my %config = (hive => "Software", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + category => "persistence", + output => "tln", + version => 20200916); + +my $VERSION = getVersion(); + +sub getConfig {return %config} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} +sub getDescr {} +sub getShortDescr { + return "Get WAB DLLPath settings"; +} +sub getRefs {} + +sub pluginmain { + my $class = shift; + my $hive = shift; + +# ::logMsg("Launching wab v.".$VERSION); +# ::rptMsg("wab v.".$VERSION); +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $key; + my $key_path = "Microsoft\\WAB\\DLLPath"; + + if ($key = $root_key->get_subkey($key_path)) { +# ::rptMsg($key_path); +# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); +# ::rptMsg(""); + + my $lw = $key->get_timestamp(); + + my $def = ""; + eval { + $def = $key->get_value("")->get_data(); + ::rptMsg($lw."|REG|||HKLM\\SOFTWARE\\".$key_path." (Default) value = ".$def); + }; + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; diff --git a/thirdparty/rr-full/plugins/wallpaper.pl b/thirdparty/rr-full/plugins/wallpaper.pl deleted file mode 100644 index 94572b99dc9..00000000000 --- a/thirdparty/rr-full/plugins/wallpaper.pl +++ /dev/null @@ -1,92 +0,0 @@ -#----------------------------------------------------------- -# wallpaper.pl -# -# Wallpaper MRU -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package wallpaper; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 200800810); - -sub getConfig{return %config} - -sub getShortDescr { - return "Parses Wallpaper MRU Entries"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching wallpaper v.".$VERSION); - ::rptMsg("wallpaper v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Wallpaper\\MRU"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("wallpaper"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my %wp; - my @mrulist; - - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (sort @vals) { - my $name = $v->get_name(); - if ($name =~ m/^\d/) { - my $data = $v->get_data(); - my $str = getStringValue($data); - $wp{$name} = $str; - } - elsif ($name =~ m/^MRUList/) { - @mrulist = unpack("V*",$v->get_data()); - } - else { -# nothing to do - } - } - foreach my $m (@mrulist) { - next if ($m == 0xffffffff); - ::rptMsg($m." -> ".$wp{$m}); - } - } - else { - ::rptMsg($key_path." has no values"); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} - -#----------------------------------------------------------- -# getStringValue() - given a binary data type w/ a Unicode -# string at the beginning, delimited by \x00\x00, return an ASCII -# string -#----------------------------------------------------------- -sub getStringValue { - my $bin = shift; - my $str = (split(/\x00\x00/,$bin,2))[0]; - $str =~ s/\x00//g; - return $str; -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/warcraft3.pl b/thirdparty/rr-full/plugins/warcraft3.pl deleted file mode 100644 index f07ecd56eb0..00000000000 --- a/thirdparty/rr-full/plugins/warcraft3.pl +++ /dev/null @@ -1,106 +0,0 @@ -#----------------------------------------------------------- -# warcraft3.pl -# Extract usernames for Warcraft III -# -# Change history -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright (c) 2011-02-02 Brendan Coles -#----------------------------------------------------------- -# Require # -package warcraft3; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110202); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extract usernames for Warcraft 3."; -} -sub getRefs { - my %refs = ("Warcraft 3 Homepage:" => - "http://us.blizzard.com/games/war3/"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - my @interesting_keys = ( - "userbnet", - "userlocal" - ); - - # Initialize # - ::logMsg("Launching warcraft3 v.".$VERSION); - ::rptMsg("warcraft3 v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key; - my $key_path = "Software\\Blizzard Entertainment\\Warcraft III\\String"; - - # If # Warcraft III path exists # - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("Warcraft III"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from Warcraft III registry path # - my %keys; - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for Warcraft III registry path # - foreach my $v (@vals) { - $keys{$v->get_name()} = $v->get_data(); - } - - # Return # all key names+values for interesting keys # - foreach my $var (@interesting_keys) { - if (exists $keys{$var}) { - ::rptMsg($var." -> ".$keys{$var}); - } - } - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # Warcraft III isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/watp.pl b/thirdparty/rr-full/plugins/watp.pl index c22195ca243..accfeab3ca5 100644 --- a/thirdparty/rr-full/plugins/watp.pl +++ b/thirdparty/rr-full/plugins/watp.pl @@ -2,24 +2,27 @@ # watp # # Change history: +# 20200916 - MITRE updates +# 20200427 - updated output date format # 20190506 - created # # Ref: # # -# copyright 2019 QAR,LLC +# copyright 2020 QAR,LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package watp; use strict; my %config = (hive => "Software", - category => "config", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20190506); + MITRE => "", + output => "report", + version => 20200916); sub getConfig{return %config} sub getShortDescr { @@ -47,7 +50,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); foreach my $v (@vals) { diff --git a/thirdparty/rr-full/plugins/wbem.pl b/thirdparty/rr-full/plugins/wbem.pl index b9c82b9ecbf..cfe23f97cc2 100644 --- a/thirdparty/rr-full/plugins/wbem.pl +++ b/thirdparty/rr-full/plugins/wbem.pl @@ -5,25 +5,33 @@ # Keylogger. # # History +# 20200916 - MITRE updates +# 20200511 - updated date output format +# 20190729 - Updated with 'autorecover mofs' info # 20120306 - created -# +# +# Ref: +# https://twitter.com/king5in/status/1022024264910815232 # -# copyright 2012, Quantum Analytics Research, LLC +# copyright 2020, Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package wbem; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20120306); + output => "report", + version => 20200916); sub getConfig{return %config} sub getShortDescr { - return "Get contents of WBEM\\WDM key"; + return "Get some contents from WBEM key"; } sub getDescr{} sub getRefs {} @@ -46,7 +54,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my @vals = $key->get_list_of_values(); @@ -63,5 +71,30 @@ sub pluginmain { else { ::rptMsg($key_path." not found."); } + +# Added 20190729 +# Ref: https://docs.microsoft.com/en-us/windows/win32/wmisdk/pragma-autorecover +# Ref: https://twitter.com/mattifestation/status/1021879005815816192 + $key_path = "Microsoft\\WBEM\\CIMOM"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + my $mofs; + my $moftime; + eval { + $moftime = $key->get_value("Autorecover MOFs Timestamp")->get_data(); + ::rptMsg(""); + }; + + eval { + $mofs = $key->get_value("Autorecover MOFs")->get_data(); + ::rptMsg("Autorecover MOFs: ".$mofs); + }; + } + else { + ::rptMsg($key_path." not found."); + } } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wc_shares.pl b/thirdparty/rr-full/plugins/wc_shares.pl new file mode 100644 index 00000000000..1f8a19e8943 --- /dev/null +++ b/thirdparty/rr-full/plugins/wc_shares.pl @@ -0,0 +1,82 @@ +#----------------------------------------------------------- +# wc_shares.pl +# +# +# Change history +# 20200916 - MITRE updates +# 20200515 - updated date output format +# 20171016 - created +# +# References +# +# +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wc_shares; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1021\.002", + category => "lateral movement", + output => "report", + version => 20200916); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets contents of user's WorkgroupCrawler/Shares subkeys"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching wc_shares v.".$VERSION); + ::rptMsg("wc_shares v.".$VERSION); + ::rptMsg("- ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\WorkgroupCrawler\\Shares'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()." [".::format8601Date($s->get_timestamp())."Z]"); + + eval { + my $filename = $s->get_value("Filename")->get_data(); + ::rptMsg(" Filename = ".$filename); + + }; + + eval { + my ($t0,$t1) = unpack("VV",$s->get_value("DateLastVisited")->get_data()); + my $last = ::getTime($t0,$t1); + ::rptMsg(" DateLastVisited = ".::format8601Date($last)."Z"); + + }; + ::rptMsg(""); + } + } + else { + ::rptMsg($key_path." has no subkeys."); + } + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wdfilter.pl b/thirdparty/rr-full/plugins/wdfilter.pl new file mode 100644 index 00000000000..869d9fb56ef --- /dev/null +++ b/thirdparty/rr-full/plugins/wdfilter.pl @@ -0,0 +1,77 @@ +#----------------------------------------------------------- +# wdfilter.pl - WdFilter is in group "FSFilter Anti-Virus" +# +# History: +# 20201229 - created +# +# References: +# https://twitter.com/jonasLyk/status/1339437249528795136 +# https://twitter.com/jonasLyk/status/1343909320178741250 +# https://www.n4r1b.com/posts/2020/01/dissecting-the-windows-defender-driver-wdfilter-part-1/ +# https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/load-order-groups-and-altitudes-for-minifilter-drivers +# +# https://attack.mitre.org/techniques/T1562/001/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wdfilter; +use strict; + +my %config = (hive => "system", + output => "report", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562\.001", + version => 20201229); + +sub getConfig{return %config} +sub getShortDescr { + return "Get WDFilter Altitude value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my @temps; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching wdfilter v.".$VERSION); + ::rptMsg("wdfilter v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Services\\WdFilter\\Instances\\WdFilter Instance"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my $alt = (); + eval { + ::rptMsg(""); + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + $alt = $key->get_value("Altitude")->get_data(); + ::rptMsg("Altitude value = ".$alt); + ::rptMsg(""); + ::rptMsg("Analysis Tip: \"Altitude\" values determine where a driver attaches to the stack. The default value for WdFilter is"); + ::rptMsg("\"328010\". A value of -1 indicates an attempt to prevent the filter from attaching to any volumes, disabling WinDefend."); + }; + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/webroot.pl b/thirdparty/rr-full/plugins/webroot.pl deleted file mode 100644 index 5a4162713e2..00000000000 --- a/thirdparty/rr-full/plugins/webroot.pl +++ /dev/null @@ -1,301 +0,0 @@ -#----------------------------------------------------------- -# webroot.pl -# Plugin to parse webroot antivirus registry data -# I have only extracted some of the data from the root key "WOW6432Node\\WRData", manual review is recommended -# I also do not know what a number of fields mean, so further work may be required to fully exploit the data in this key. -# -# Change history -# 20191230 - initial commit -# -# References -# -# copyright 2019 Phill Moore -#----------------------------------------------------------- - -package webroot; -use strict; - - -my %config = (hive => "SOFTWARE", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20191230); - -sub getConfig{return %config} -sub getShortDescr { - return "Provides *some* of the webroot data in the registry, manual review is still recommended. Particularly surrounding the root key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - - -sub displayActions { - ::rptMsg("---------------------------------------------------------------"); - my $root_key = shift; - my $key_path = "WOW6432Node\\WRData\\Actions"; - my $key; - if ($key = $root_key->get_subkey($key_path)){ - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @vals = $key->get_list_of_values(); - - foreach my $val (@vals) { - my $d = $val->get_data(); - my $v = $val->get_name(); - my $str = $v.":\t".$d; - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub displayJournal { - ::rptMsg("---------------------------------------------------------------"); - my $root_key = shift; - my $key_path = "WOW6432Node\\WRData\\Journal"; - my $key; - if ($key = $root_key->get_subkey($key_path)){ - ::rptMsg(""); - ::rptMsg($key_path . " - ". gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @vals = $key->get_list_of_values(); - - ::rptMsg("filename,md5,timestamp"); - foreach my $val (@vals) { - - #format = "filename=$filename,md5=$md5,timestamp=$timestamp" - my @d = split (/,/, $val->get_data()); - my $fn=(split(/\=/,$d[0]))[1]; - my $md5= (split(/\=/,$d[1]))[1]; - my $ts=(split(/\=/,$d[2]))[1]; - my $timestamp=gmtime($ts); - my $str = $fn.",".$md5.",".$ts.",".$timestamp; - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub displayStatus { - ::rptMsg("---------------------------------------------------------------"); - my $root_key = shift; - my $key_path = "WOW6432Node\\WRData\\Status"; - my $key; - if ($key = $root_key->get_subkey($key_path)){ - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @vals = $key->get_list_of_values(); - - foreach my $val (@vals) { - my $d = $val->get_data(); - my $v = $val->get_name(); - - #if $v is in the following list then convert timestamp - my @timestamp_fields = ["AgentStartupTime", "ExpirationDate", "LastDeepScan", "LastScan", "LastThreatSeen", "SystemStateUpdated", "UpdateTime", "UpdateTime"]; - $d = $d." (".gmtime($d).")" if ($v ~~ @timestamp_fields); - - my $str = $v.":\t".$d; - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub displayFileFlags { - ::rptMsg("---------------------------------------------------------------"); - my $root_key = shift; - my $key_path = "WOW6432Node\\WRData\\FileFlags"; - my $key; - if ($key = $root_key->get_subkey($key_path)){ - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - ::rptMsg("MD5 hash:\t\t\t\taction, last changed"); - my @vals = $key->get_list_of_values(); - foreach my $val (@vals) { - my $d = $val->get_data(); - my $v = $val->get_name(); - - my @split_d = split (/\,/, $d); - my @changetime = split (/\=/, $split_d[1]); - my $str = $v.":\t".$d."(".gmtime($changetime[1]).")"; - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub displayIPM { - ::rptMsg("---------------------------------------------------------------"); - my $root_key = shift; - my $key_path = "WOW6432Node\\WRData\\IPM";; - my $key; - if ($key = $root_key->get_subkey($key_path)){ - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @vals = $key->get_list_of_values(); - foreach my $val (@vals) { - my $d = $val->get_data(); - my $v = $val->get_name(); - my $d = $d." (".gmtime($d).")"if ($v eq "ILU"); - my $str = $v.":\t".$d; - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - - - - -sub dumpAllVals { - ::rptMsg("---------------------------------------------------------------"); - my $root_key = shift; - my $key_path = shift; - my $key; - if ($key = $root_key->get_subkey($key_path)){ - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @vals = $key->get_list_of_values(); - foreach my $val (@vals) { - my $d = $val->get_data(); - my $v = $val->get_name(); - my $str = $v.":\t".$d; - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." not found."); - } -} - -sub dumpThreatsVals { - ::rptMsg("---------------------------------------------------------------"); - my $root_key = shift; - my $key_path = shift; - my $key; - my $v; - my $str; - - if ($key = $root_key->get_subkey($key_path)){ - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @vals = sort ($key->get_list_of_values()); - - foreach my $val (@vals) { - - my $v = $val->get_name(); - my $d = $val->get_data(); - if ($v eq "Count"){ - $str = $v.":\t".$d; - } - else { - my @split_d = split (/\|/, $d); - my $path = $split_d[0]; - my $detection = $split_d[1]; - my $ts = $split_d[2]; - my $timestamp = gmtime(hex($ts)); - $str = $v.":\t".$path."|".$detection."|".$ts." (".$timestamp.")"; - } - ::rptMsg($str); - } - } - else { - ::rptMsg($key_path." not found."); - } - - -} - -sub displayThreats { - my $root_key = shift; - my $key_path = "WOW6432Node\\WRData\\Threats"; - - - dumpAllVals($root_key, $key_path); - my @threats = ($key_path."\\Active", $key_path."\\History"); - - foreach my $k (@threats){ - #::rptMsg($k); - dumpThreatsVals($root_key, $k); - } -} - - - -my $VERSION = getVersion(); -my $PLUGIN = "webroot"; - -sub pluginmain { - my $class = shift; - my $hive = shift; - my $infected = 0; - ::logMsg("Launching ".$PLUGIN." v.".$VERSION); - ::rptMsg($PLUGIN." v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "WOW6432Node\\WRData"; - my $key; - - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - #my @vals = $key->get_list_of_values(); - my @vals = ("AVP", "BMV", "GWord", "HPL", "InstallDir", "InstalledVersion", "InstallTime", "LastInfection", "OIT"); - - foreach my $v (@vals) { - my $d = $key->get_value($v)->get_data(); - my $str = $v.":\t".$d; - ::rptMsg($str); - } - - - displayActions($root_key); - displayFileFlags($root_key); - displayIPM($root_key); - displayJournal($root_key); - displayStatus($root_key); - displayThreats($root_key); - dumpAllVals($root_key, "WOW6432Node\\WRData\\wrURL"); - } - else { - ::rptMsg($key_path." not found."); - ::rptMsg(""); - } -}1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/win11_edge.pl b/thirdparty/rr-full/plugins/win11_edge.pl new file mode 100644 index 00000000000..b9354b93b32 --- /dev/null +++ b/thirdparty/rr-full/plugins/win11_edge.pl @@ -0,0 +1,107 @@ +#----------------------------------------------------------- +# win11_edge.pl +# MS Edge values from Windows 11 +# +# Change history: +# 20210927 - created +# +# References: +# +# +# +# copyright 2021 Quantum Analytics Research, LLC +# Author: H. Carvey +#----------------------------------------------------------- +package win11_edge; +use strict; + +my %config = (hive => "software, ntuser\.dat", + category => "", + MITRE => "", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + version => 20210927); + +sub getConfig{return %config} + +sub getShortDescr { + return "Get Win11 MSEdge values"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching win11_edge v.".$VERSION); + ::rptMsg("win11_edge v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my %guess = (); + my $hive_guess = ""; + my %guess = ::guessHive($hive); + foreach my $g (keys %guess) { + $hive_guess = $g if ($guess{$g} == 1); + } + my $key; + my $key_path = (); + + if ($hive_guess eq "software") { + $key_path = ("Policies\\Microsoft\\Edge"); + } + elsif ($hive_guess eq "ntuser") { + $key_path = ("Software\\Policies\\Microsoft\\Edge"); + } + else {} + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("Key path: ".$key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + + eval { + my $d = $key->get_value("DeveloperToolsAvailability")->get_data(); + ::rptMsg("DeveloperToolsAvailability value : ".$d); + ::rptMsg("0 - Block dev tools by enterprise policy, allow in other contexts"); + ::rptMsg("1 - Allow using dev tools"); + ::rptMsg("2 - Block using dev tools"); + }; + + eval { + my $d = $key->get_value("DefaultJavaScriptJitSetting")->get_data(); + ::rptMsg("DefaultJavaScriptJitSetting value : ".$d); + ::rptMsg("0 = Default"); + ::rptMsg("1 = AllowJavaScriptJit"); + ::rptMsg("2 = BlockJavaScriptJit which means do not allow any site to run JavaScript JIT"); + }; + + eval { + my $d = $key->get_value("ShowPDFDefaultRecommendationsEnabled")->get_data(); + ::rptMsg("ShowPDFDefaultRecommendationsEnabled value: ".$d); + ::rptMsg("0 = Disabled"); + ::rptMsg("1 = Enabled (default)"); + }; + + eval { + my $d = $key->get_value("RemoteDebuggingAllowed")->get_data(); + ::rptMsg("RemoteDebuggingAllowed value : ".$d); + ::rptMsg("0 = Disabled"); + ::rptMsg("1 = Enabled (default)"); + }; + } + else { +# ::rptMsg($key_path." not found."); + } +# ::rptMsg("Analysis Tip: "); +# ::rptMsg(""); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/win_cv.pl b/thirdparty/rr-full/plugins/win_cv.pl deleted file mode 100644 index 6669347c75b..00000000000 --- a/thirdparty/rr-full/plugins/win_cv.pl +++ /dev/null @@ -1,87 +0,0 @@ -#----------------------------------------------------------- -# win_cv.pl -# Get and display the contents of the Windows\CurrentVersion key -# Output sorted based on length of data -# -# Change History: -# 20080609: added translation of InstallDate time -# -# copyright 2009 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package win_cv; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20090312); - -sub getConfig{return %config} -sub getShortDescr { - return "Get & display the contents of the Windows\\CurrentVersion key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching win_cv v.".$VERSION); - ::rptMsg("win_cv v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\Windows\\CurrentVersion"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my %cv; - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); - my $len = length($data); - next if ($name eq ""); - if ($v->get_type() == 3) { - $data = _translateBinary($data); - } - push(@{$cv{$len}},$name." : ".$data); - } - foreach my $t (sort {$a <=> $b} keys %cv) { - foreach my $item (@{$cv{$t}}) { - ::rptMsg(" $item"); - } - } - } - else { - ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values"); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} - - -sub _translateBinary { - my $str = unpack("H*",$_[0]); - my $len = length($str); - my @nstr = split(//,$str,$len); - my @list = (); - foreach (0..($len/2)) { - push(@list,$nstr[$_*2].$nstr[($_*2)+1]); - } - return join(' ',@list); -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winbackup.pl b/thirdparty/rr-full/plugins/winbackup.pl deleted file mode 100644 index 252c0abc18f..00000000000 --- a/thirdparty/rr-full/plugins/winbackup.pl +++ /dev/null @@ -1,210 +0,0 @@ -#----------------------------------------------------------- -# winbackup.pl -# -# Change History -# 20120812 [fpi] % created from winver.pl -# -# References -# -# copyright 2012 M. DeGrazia, arizona4n6@gmail.com -#----------------------------------------------------------- -package winbackup; -use strict; - -my %config = (hive => "Software", - osmask => 16, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20120812); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get Windows Backup"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching winbackup v.".$VERSION); - ::rptMsg("winbackup v.".$VERSION); - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key_path = "Microsoft\\Windows\\CurrentVersion\\WindowsBackup\\ScheduleParams\\TargetDevice"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - - my $name; - eval { - $name = $key->get_value("PresentableName")->get_data(); - }; - if ($@) { -# ::rptMsg("PresentableName value not found."); - } - else { - ::rptMsg(" PresentableName = ".$name); - } - - my $uniquename; - eval { - $uniquename = $key->get_value("UniqueName")->get_data(); - }; - if ($@) { -# ::rptMsg("UniqueName value not found."); - } - else { - ::rptMsg(" UniqueName = ".$uniquename); - } - - - my $devlabel; - eval { - $devlabel = $key->get_value("Label")->get_data(); - }; - if ($@) { -# ::rptMsg("Label value not found."); - } - else { - ::rptMsg(" Label = ".$devlabel); - } - - - my $vendor; - eval { - $vendor = $key->get_value("DeviceVendor")->get_data(); - }; - if ($@) { -# ::rptMsg("DeviceVendor value not found."); - } - else { - ::rptMsg(" DeviceVendor = ".$vendor); - } - - my $deviceproduct; - eval { - $deviceproduct = $key->get_value("DeviceProduct")->get_data(); - }; - if ($@) { -# ::rptMsg("DeviceVendor value not found."); - } - else { - ::rptMsg(" DeviceProduct = ".$deviceproduct); - } - - my $deviceversion; - eval { - $deviceversion = $key->get_value("DeviceVersion")->get_data(); - }; - if ($@) { -# ::rptMsg("DeviceVendor value not found."); - } - else { - ::rptMsg(" DeviceVersion = ".$deviceversion); - } - - - my $devserial; - eval { - $devserial = $key->get_value("DeviceSerial")->get_data(); - }; - if ($@) { -# ::rptMsg("DeviceSerial value not found."); - } - else { - ::rptMsg(" DeviceSerial = ".$devserial); - } - } - else { - ::rptMsg($key_path." not found."); - } - -#status - - ::rptMsg(""); - $key_path = "Microsoft\\Windows\\CurrentVersion\\WindowsBackup\\Status"; - if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg("{name}"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my $lastresulttime; - eval { - $lastresulttime = $key->get_value("LastResultTime")->get_data(); - }; - if ($@) { -# ::rptMsg("LastSuccess value not found."); - } - else { - my @vals = unpack("VV",$lastresulttime); - my $lrt = ::getTime($vals[0],$vals[1]); - ::rptMsg(" LastResultTime = ".gmtime($lrt)." (UTC)"); - } - - my $lastsuccess; - eval { - $lastsuccess = $key->get_value("LastSuccess")->get_data(); - }; - if ($@) { -# ::rptMsg("LastSuccess value not found."); - } - else { - my @vals = unpack("VV",$lastsuccess); - my $ls = ::getTime($vals[0],$vals[1]); - ::rptMsg(" LastSuccess = ".gmtime($ls)." (UTC)"); - } - - my $lasttarget; - eval { - $lasttarget = $key->get_value("LastResultTarget")->get_data(); - }; - if ($@) { -# ::rptMsg("LastResultTarget value not found."); - } - else { - ::rptMsg(" LastResultTarget = ".$lasttarget); - } - - my $LRTPrestName; - eval { - $LRTPrestName = $key->get_value("LastResultTargetPresentableName")->get_data(); - }; - if ($@) { -# ::rptMsg("LastResultTargetPresentableName value not found."); - } - else { - ::rptMsg(" LastResultTargetPresentableName = ".$LRTPrestName); - } - - - my $LRTTargetLabel; - eval { - $LRTTargetLabel = $key->get_value("LastResultTargetLabel")->get_data(); - }; - if ($@) { -# ::rptMsg("LastResultTargetLabel value not found."); - } - else { - ::rptMsg(" LastResultTargetLabel = ".$LRTTargetLabel); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; diff --git a/thirdparty/rr-full/plugins/windowsupdate.pl b/thirdparty/rr-full/plugins/windowsupdate.pl new file mode 100644 index 00000000000..b7b2175d6bf --- /dev/null +++ b/thirdparty/rr-full/plugins/windowsupdate.pl @@ -0,0 +1,89 @@ +#----------------------------------------------------------- +# windowsupdate +# +# Change history: +# 20221024 - created +# +# Ref: +# https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.WindowsUpdate::DoNotConnectToWindowsUpdateInternetLocations +# https://gist.github.com/powershellshocked/2aa2cceb102e84d4d328e0412202c228 +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package windowsupdate; +use strict; + +my %config = (hive => "software", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562\.001", + output => "report", + version => 20221024); + +sub getConfig{return %config} +sub getShortDescr { + return "Check settings that may disable Windows Updates"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching windowsupdate v.".$VERSION); + ::rptMsg("windowsupdate v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my $key_path = ('Policies\\Microsoft\\Windows\\WindowsUpdate'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + +# https://gist.github.com/powershellshocked/2aa2cceb102e84d4d328e0412202c228 + eval { + my $x = $key->get_value("ElevateNonAdmins")->get_data(); + ::rptMsg("ElevateNonAdmins value: ".$x); + ::rptMsg("1 - Users in the Users security group are allowed to approve/disapprove updates"); + ::rptMsg("0 - Only users in the Administrators group can approve/disapprove updates"); + ::rptMsg(""); + ::rptMsg("Analysis Tip: A setting of \"0\" may inhibit Windows Updates."); +# ::rptMsg(""); + }; + ::rptMsg("ElevateNonAdmins value not found.") if ($@); + +# https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.WindowsUpdate::DoNotConnectToWindowsUpdateInternetLocations + eval { + my $x = $key->get_value("DoNotConnectToWindowsUpdateInternetLocations")->get_data(); + ::rptMsg("DoNotConnectToWindowsUpdateInternetLocations value: ".$x); + ::rptMsg("1 - Enabled"); + ::rptMsg("0 - Disabled"); + ::rptMsg(""); + ::rptMsg("Analysis Tip: Even if Windows systems are configured to retrieve updates from an internal server, it may "); + ::rptMsg("periodically contact the public services to enable future connections. Enabling the policy (setting to 1)"); + ::rptMsg("will disable the functionality, and may cause connections to other public services (i.e., Windows Store) to"); + ::rptMsg("stop working, as well."); + + }; + ::rptMsg("DoNotConnectToWindowsUpdateInternetLocations value not found.") if ($@); + + } + else { + ::rptMsg($key_path." not found."); + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winevt.pl b/thirdparty/rr-full/plugins/winevt.pl index 1d59ba01f5c..72e72712113 100644 --- a/thirdparty/rr-full/plugins/winevt.pl +++ b/thirdparty/rr-full/plugins/winevt.pl @@ -1,33 +1,31 @@ #----------------------------------------------------------- -# winevt.pl -# Extracts the event log settings stored in the software hive -# to show what logging is enabled and disabled +# winevt # +# Change history: +# 20201012 - created +# +# Ref: +# # -# Change History: -# 20140402 % created -# -# References -# http://publib.boulder.ibm.com/infocenter/tivihelp/v61r1/index.jsp?topic=%2Fcom.ibm.itm.doc_6.3%2Ftrouble%2Ftema_oswinevents_trouble.htm -# -# Script written by Corey Harrell (Journey Into IR) +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package winevt; use strict; my %config = (hive => "Software", - osmask => 22, + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20140402); + MITRE => "", + output => "report", + version => 20201012); sub getConfig{return %config} - sub getShortDescr { - return "Get the Windows event log policy from the Winevt\\Channels key"; + return "Gets Enabled values for WINEVT Channels"; } - sub getDescr{} sub getRefs {} sub getHive {return $config{hive};} @@ -36,47 +34,39 @@ sub getShortDescr { my $VERSION = getVersion(); sub pluginmain { - - ::logMsg("Launching winevt v.".$VERSION); - ::rptMsg("winevt v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - my $class = shift; my $hive = shift; - my $reg = Parse::Win32Registry->new($hive); + ::rptMsg("Launching winevt v.".$VERSION); + ::rptMsg("winevt v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + my @paths = ('Microsoft\\Windows\\CurrentVersion\\WINEVT\\Channels'); + ::rptMsg("WINEVT"); + my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\Windows\\CurrentVersion\\WINEVT\\Channels"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - my $enabled; - eval { - $enabled = $s->get_value("Enabled")->get_data(); - }; - - ::rptMsg("Event Log Registry Key : ".$s->get_name()); - ::rptMsg("LastWrite : ".gmtime($s->get_timestamp())." (UTC)"); - ::rptMsg("Enabled Value : ".$enabled); - ::rptMsg(""); - + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + ::rptMsg(sprintf "%-22s %-87s %-2s","LastWrite","Channel","Enabled"); + foreach my $s (@subkeys) { + my $enabled = (); + eval { + $enabled = $s->get_value("Enabled")->get_data(); + }; + $enabled = $@ if ($@); + my $lw = ::format8601Date($key->get_timestamp())."Z"; + ::rptMsg(sprintf "%-22s %-87s %-2s",$lw,$s->get_name(),$enabled); + } } - - } - else { - ::rptMsg($key_path." has no subkeys."); + } } - else { - ::rptMsg($key_path." not found."); - } + ::rptMsg(""); + ::rptMsg("Analysis Tip: This plugin retrieves the \"Enabled\" value from each available WINEVT Channel, indicating"); + ::rptMsg("if it's enabled. This can help obviate attempts at anti- or counter-forensics, by identifying when the"); + ::rptMsg("setting may have been changed."); } - -1; +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winevtchannels.pl b/thirdparty/rr-full/plugins/winevtchannels.pl new file mode 100644 index 00000000000..16b947902a0 --- /dev/null +++ b/thirdparty/rr-full/plugins/winevtchannels.pl @@ -0,0 +1,84 @@ +#----------------------------------------------------------- +# winevtchannels +# +# Change history: +# 20220516 - created +# +# Ref: +# +# +# copyright 2022 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package winevtchannels; +use strict; + +my %config = (hive => "Software", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1562\.002", + output => "report", + version => 20220516); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets WINEVT\\Channels info"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching winevtchannels v.".$VERSION); + ::rptMsg("winevtchannels v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + + my $key_path = ('Microsoft\\Windows\\CurrentVersion\\WINEVT\\Channels'); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + ::rptMsg($s->get_name()); + ::rptMsg("LastWrite time: ".::format8601Date($s->get_timestamp())."Z"); + + eval { + my $e = $s->get_value("Enabled")->get_data(); + ::rptMsg(" Enabled : ".$e); + }; + + eval { + my $o = $s->get_value("OwningPublisher")->get_data(); + ::rptMsg(" OwningPublisher: ".$o); + + }; + ::rptMsg(""); + } + + } + else { + ::rptMsg($key_path." has no subkeys."); + } + } + else { + ::rptMsg($key_path." not found."); + } + + ::rptMsg("Analysis Tip: A number of Windows Event Logs can be disabled simply by changing the \"Enabled\" value in the"); + ::rptMsg("Channels subkey."); +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winlogon.pl b/thirdparty/rr-full/plugins/winlogon.pl deleted file mode 100644 index 9f356d36283..00000000000 --- a/thirdparty/rr-full/plugins/winlogon.pl +++ /dev/null @@ -1,198 +0,0 @@ -#----------------------------------------------------------- -# WinLogon -# Get values from WinLogon key -# -# History -# 20130910 - added check for GinaDLL value, updated checks -# 20130425 - added alertMsg() functionality -# 20130411 - added specaccts.pl & notify.pl functionality -# 20130410 - updated; added Wow6432Node support, merged TaskMan -# 20100219 - Updated output to better present some data -# 20080415 - created -# -# References -# http://technet.microsoft.com/en-us/library/cc738733(v=ws.10).aspx -# -# TaskMan: http://technet.microsoft.com/en-us/library/cc957402.aspx -# http://www.geoffchappell.com/viewer.htm?doc=notes/windows/shell/explorer/ -# taskman.htm&tx=3,5-7,12;4&ts=0,19 -# System: http://technet.microsoft.com/en-us/library/cc784246(v=ws.10).aspx -# -# copyright 2013 Quantum Analytics Research, LLC -#----------------------------------------------------------- -package winlogon; -use strict; - -my %config = (hive => "Software", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20130425); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get values from the WinLogon key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching winlogon v.".$VERSION); - ::rptMsg("winlogon v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my @paths = ("Microsoft\\Windows NT\\CurrentVersion\\Winlogon", - "Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"); - - foreach my $key_path (@paths) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %wl; - foreach my $v (@vals) { - my $lcname = $v->get_name(); - my $name = $lcname; - $lcname =~ tr/[A-Z]/[a-z]/; - my $data = $v->get_data(); -# checks added 20130425 - if ($name eq "Userinit") { - my @ui = split(/,/,$data); - if (scalar(@ui) > 1 && $ui[1] ne "") { - ::alertMsg("ALERT: winlogon: ".$key_path." Userinit value has multiple entries: ".$data); - } -# alert if the Userinit value does not end in "userinit.exe" (after taking commas into account) -# ::alertMsg("ALERT: winlogon: ".$key_path." Userinit value: ".$ui[0]) unless ($ui[0] =~ m/userinit\.exe$/); - } -# added 20130910 -# ref: http://support.microsoft.com/kb/302346 - if ($lcname eq "ginadll") { - ::alertMsg("WARNING: winlogon: ".$key_path." GinaDLL value found: ".$data); - } - - if ($lcname eq "shell") { - my $lcdata = $data; - $lcdata =~ tr/[A-Z]/[a-z]/; - ::alertMsg("ALERT: winlogon: ".$key_path." Shell value not explorer\.exe: ".$data) unless ($lcdata =~ m/^explorer\.exe$/); - } - ::alertMsg("ALERT: winlogon: ".$key_path." TaskMan value found: ".$data) if ($lcname eq "taskman"); - ::alertMsg("ALERT: winlogon: ".$key_path." System value found: ".$data) if ($lcname eq "system"); -# /end 20130425 additions - - my $len = length($data); - next if ($name eq ""); - if ($v->get_type() == 3 && $name ne "DCacheUpdate") { - $data = _translateBinary($data); - } - - $data = sprintf "0x%x",$data if ($name eq "SfcQuota"); - if ($name eq "DCacheUpdate") { - my @v = unpack("VV",$data); - $data = gmtime(::getTime($v[0],$v[1])); - } - - push(@{$wl{$len}},$name." = ".$data); - } - - foreach my $t (sort {$a <=> $b} keys %wl) { - foreach my $item (@{$wl{$t}}) { - ::rptMsg(" $item"); - } - } - ::rptMsg(""); - \checkNotifySubkey($key); - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - ::rptMsg(""); - } - - } - ::rptMsg("Analysis Tips: The UserInit and Shell values are executed when a user logs on\."); - ::rptMsg("The UserInit value should contain a reference to userinit.exe; the Shell value"); - ::rptMsg("should contain just 'explorer.exe'\. Check TaskMan & System values, if found\."); - ::rptMsg(""); - -# SpecialAccounts/UserList functionality added 20130411 - my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my %apps; - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - ::rptMsg(sprintf "%-20s 0x%x",$v->get_name(),$v->get_data()); - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } - -} - -sub checkNotifySubkey { - my $key = shift; - my $notify; - if ($notify = $key->get_subkey("Notify")) { - ::rptMsg("Notify subkey contents:"); - my @sk = $notify->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $s (@sk) { - my $name = $s->get_name(); -# added 20130425 - ::alertMsg("ALERT: winlogon: Notify subkey: possible Troj_Tracor infection\.") if ($name =~ m/^f0bd/); - my $lw = $s->get_timestamp(); - ::rptMsg(" ".$name." - ".gmtime($lw)); - my $dllname; - eval { - $dllname = $s->get_value("DLLName")->get_data(); - ::rptMsg(" DLLName: ".$dllname); - }; - ::rptMsg(""); - } - } - else { - ::rptMsg("Notify subkey has no subkeys."); - } - } - else { - ::rptMsg("Notify subkey not found\."); - } - ::rptMsg(""); -} - -sub _translateBinary { - my $str = unpack("H*",$_[0]); - my $len = length($str); - my @nstr = split(//,$str,$len); - my @list = (); - foreach (0..($len/2)) { - push(@list,$nstr[$_*2].$nstr[($_*2)+1]); - } - return join(' ',@list); -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winlogon_tln.pl b/thirdparty/rr-full/plugins/winlogon_tln.pl index 62f965596fa..0d3d54c6a44 100644 --- a/thirdparty/rr-full/plugins/winlogon_tln.pl +++ b/thirdparty/rr-full/plugins/winlogon_tln.pl @@ -3,6 +3,7 @@ # Get values from WinLogon key # # History +# 20200916 - MITRE updates # 20130429 - created, from winlogon.pl # # References @@ -17,11 +18,13 @@ package winlogon_tln; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20130429); + output => "tln", + version => 20200916); sub getConfig{return %config} diff --git a/thirdparty/rr-full/plugins/winlogon_u.pl b/thirdparty/rr-full/plugins/winlogon_u.pl deleted file mode 100644 index 31c7ebd105d..00000000000 --- a/thirdparty/rr-full/plugins/winlogon_u.pl +++ /dev/null @@ -1,108 +0,0 @@ -#----------------------------------------------------------- -# winlogon_u -# Get values from user's WinLogon key -# -# Change History: -# 20130425 - added alertMsg() functionality -# 20130410 - added Wow6432Node support -# 20130328 - updated with ThreatExpert info -# 20091021 - created -# -# References: -# http://support.microsoft.com/kb/119941 -# http://www.threatexpert.com/report.aspx?md5=c463f9829bc79e0bb7296e1396ce4e01 -# -# copyright 2013 QAR,LLC -# Author: H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package winlogon_u; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20130425); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get values from the user's WinLogon key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching winlogon_u v.".$VERSION); - ::rptMsg("winlogon_u v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my @paths = ("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", - "Software\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"); - - foreach my $key_path (@paths) { - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %wl; - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); -# checks added 20130425 - ::alertMsg("ALERT: winlogon_u: ".$key_path." RunGrpConv value found: ".$data) if ($name eq "RunGrpConv"); - if ($name =~ m/^[Ss]hell/) { - ::alertMsg("ALERT: winlogon_u: ".$key_path." Shell value not explorer\.exe: ".$data) unless ($data eq "explorer\.exe"); - } - my $len = length($data); - next if ($name eq ""); - if ($v->get_type() == 3) { - $data = _translateBinary($data); - } - push(@{$wl{$len}},$name." = ".$data); - } - - foreach my $t (sort {$a <=> $b} keys %wl) { - foreach my $item (@{$wl{$t}}) { - ::rptMsg(" $item"); - } - } - - ::rptMsg(""); - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } - } - ::rptMsg("Analysis Tip: Existence of RunGrpConv = 1 value may indicate that the"); - ::rptMsg(" system had been infected with Bredolab (Symantec)\. Also, check the"); - ::rptMsg(" contents of a \"shell\" value - should only include Explorer\.exe, if"); - ::rptMsg(" it exists\."); -} - -sub _translateBinary { - my $str = unpack("H*",$_[0]); - my $len = length($str); - my @nstr = split(//,$str,$len); - my @list = (); - foreach (0..($len/2)) { - push(@list,$nstr[$_*2].$nstr[($_*2)+1]); - } - return join(' ',@list); -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winnt_cv.pl b/thirdparty/rr-full/plugins/winnt_cv.pl deleted file mode 100644 index da2f316b9a4..00000000000 --- a/thirdparty/rr-full/plugins/winnt_cv.pl +++ /dev/null @@ -1,97 +0,0 @@ -#----------------------------------------------------------- -# winnt_cv.pl -# Get and display the contents of the Windows\CurrentVersion key -# Output sorted based on length of data -# -# Change History: -# 20161123: added translation of InstallTime time (found in Win10) - Phill Moore, randomaccess3@gmail.com -# InstallTime should match InstallDate -# 20080609: added translation of InstallDate time -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package winnt_cv; -use strict; - -my %config = (hive => "Software", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 22, - version => 20161123); - -sub getConfig{return %config} -sub getShortDescr { - return "Get & display the contents of the Windows NT\\CurrentVersion key"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - ::logMsg("Launching winnt_cv v.".$VERSION); - ::rptMsg("winnt_cv v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\Windows NT\\CurrentVersion"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg("WinNT_CV"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - my %cv; - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $name = $v->get_name(); - my $data = $v->get_data(); - $data = gmtime($data)." (UTC)" if ($name eq "InstallDate"); - - if ($name eq "InstallTime"){ - my @t = unpack("VV",$data); - $data = gmtime(::getTime($t[0],$t[1]))." (UTC)"; - } - - my $len = length($data); - next if ($name eq ""); - if ($v->get_type() == 3) { - $data = _translateBinary($data); - } - push(@{$cv{$len}},$name." : ".$data); - } - foreach my $t (sort {$a <=> $b} keys %cv) { - foreach my $item (@{$cv{$t}}) { - ::rptMsg(" $item"); - } - } - } - else { - ::rptMsg($key_path." has no values."); - ::logMsg($key_path." has no values"); - } - } - else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } -} - - -sub _translateBinary { - my $str = unpack("H*",$_[0]); - my $len = length($str); - my @nstr = split(//,$str,$len); - my @list = (); - foreach (0..($len/2)) { - push(@list,$nstr[$_*2].$nstr[($_*2)+1]); - } - return join(' ',@list); -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winrar.pl b/thirdparty/rr-full/plugins/winrar.pl index 0d62e6d944e..eb6bd790b3e 100644 --- a/thirdparty/rr-full/plugins/winrar.pl +++ b/thirdparty/rr-full/plugins/winrar.pl @@ -3,20 +3,27 @@ # Get WinRAR\ArcHistory entries # # History +# 20200916 - MITRE updates +# 20200526 - updated date output format # 20080819 - created # +# Ref: +# https://attack.mitre.org/techniques/T1074/001/ # -# copyright 2008 H. Carvey, keydet89@yahoo.com +# copyright 2020 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package winrar; use strict; my %config = (hive => "NTUSER\.DAT", - osmask => 22, + MITRE => "T1074\.001", + category => "data staged", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20080819); + output => "report", + version => 20200916); sub getConfig{return %config} @@ -34,8 +41,10 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching winrar v.".$VERSION); - ::rptMsg("winrar v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("winrar v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; @@ -44,7 +53,7 @@ sub pluginmain { if ($key = $root_key->get_subkey($key_path)) { ::rptMsg("WinRAR"); ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); my %arc; @@ -65,8 +74,6 @@ sub pluginmain { } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } - } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winrar2.pl b/thirdparty/rr-full/plugins/winrar2.pl deleted file mode 100644 index b2707a0dc69..00000000000 --- a/thirdparty/rr-full/plugins/winrar2.pl +++ /dev/null @@ -1,87 +0,0 @@ -#----------------------------------------------------------- -# winrar2.pl -# Get WinRAR\ArcHistory entries -# -# History -# 20150820 - updated by Phillip Moore to include additional artefacts relating to the use of the edit dialog box -# 20080819 - created -# -# -# copyright 2008 H. Carvey, keydet89@yahoo.com -#----------------------------------------------------------- -package winrar2; -use strict; - -my %config = (hive => "NTUSER\.DAT", - osmask => 22, - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - version => 20150820); - -sub getConfig{return %config} - -sub getShortDescr { - return "Get WinRAR\\ArcHistory, WinRAR\\DialogEditHistory\\ArcName, WinRAR\\DialogEditHistory\\ExtrPath entries"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain{ - ::logMsg("Launching winrar2 v.".$VERSION); - ::rptMsg("winrar2 v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - my $class = shift; - my $hive = shift; - - ::rptMsg("---------------------------------------------------------------------------------"); - parsesubkey($class, $hive, "Software\\WinRAR\\ArcHistory"); - ::rptMsg("Analysis Tip: The values relate to the recently accessed files using the WinRAR program."); - ::rptMsg("---------------------------------------------------------------------------------"); - ::rptMsg(""); - parsesubkey($class, $hive, "Software\\WinRAR\\DialogEditHistory\\ArcName"); - ::rptMsg("Analysis Tip: The values relate to the dropdown list in the \"Add\" menu. As a result this can used to determine the file name (and sometimes path) of a file that has been appended or created."); - ::rptMsg("---------------------------------------------------------------------------------"); - ::rptMsg(""); - parsesubkey($class, $hive, "Software\\WinRAR\\DialogEditHistory\\ExtrPath"); - ::rptMsg("Analysis Tip: These values relate to the dropdown list in the \"Extract\" menu. They show where a compressed file was extracted to.") -} - -sub parsesubkey { - my $class = shift; - my $hive = shift; - my $key_path = shift; - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my %arc; - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - $arc{$v->get_name()} = $v->get_data(); - } - - foreach (sort keys %arc) { - ::rptMsg($_." -> ".$arc{$_}); - } - - } - else { - ::rptMsg($key_path." has no values."); - } - } - else { - ::rptMsg($key_path." not found."); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winrar_tln.pl b/thirdparty/rr-full/plugins/winrar_tln.pl index 6f14c377e96..d8fd5404959 100644 --- a/thirdparty/rr-full/plugins/winrar_tln.pl +++ b/thirdparty/rr-full/plugins/winrar_tln.pl @@ -3,21 +3,27 @@ # Get WinRAR\ArcHistory entries # # History +# 20200916 - MITRE updates # 20120829 - updated to TLN # 20080819 - created (winrar.pl) # +# Ref: +# https://attack.mitre.org/techniques/T1074/001/ # -# copyright 2008 H. Carvey, keydet89@yahoo.com +# copyright 2020 QAR, LLC +# H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package winrar_tln; use strict; my %config = (hive => "NTUSER\.DAT", - osmask => 22, + MITRE => "T1074\.001", + category => "data staged", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20120829); + output => "tln", + version => 20200916); sub getConfig{return %config} diff --git a/thirdparty/rr-full/plugins/winscp.pl b/thirdparty/rr-full/plugins/winscp.pl index bc0e174cf5a..5a8dfb6960a 100644 --- a/thirdparty/rr-full/plugins/winscp.pl +++ b/thirdparty/rr-full/plugins/winscp.pl @@ -3,24 +3,27 @@ # # # Change history +# 20200916 - MITRE updates +# 20200525 - updated date output format # 20140203 - created # # References # # -# copyright 2014 QAR, LLC +# copyright 2020 QAR, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package winscp; use strict; my %config = (hive => "NTUSER\.DAT", - category => "program execution", + category => "lateral movement", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20140203); + MITRE => "T1021", + output => "report", + version => 20200916); sub getConfig{return %config} sub getShortDescr { @@ -46,7 +49,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); ::rptMsg(""); # CDCache eval { diff --git a/thirdparty/rr-full/plugins/winscp_sessions.pl b/thirdparty/rr-full/plugins/winscp_sessions.pl deleted file mode 100644 index e923bed4d1d..00000000000 --- a/thirdparty/rr-full/plugins/winscp_sessions.pl +++ /dev/null @@ -1,125 +0,0 @@ -# winscp_sessions.pl -# -# RegRipper module to extract saved session data from NTUSER.DAT -# Software\Martin Prikryl\WinSCP 2\Sessions key. Password decoding -# algorithm adapted from Metasploit's winscp.rb module, originally -# written by TheLightCosine (http://cosine-security.blogspot.com) -# -# Change History -# 04/02/2013 Added rptMsg for key not found errors by Corey Harrell -# -# RegRipper module author Hal Pomeranz - -package winscp_sessions; - -use strict; - -my %config = ('hive' => 'NTUSER.DAT', - 'hasShortDescr' => 1, - 'hasDescr' => 0, - 'hasRefs' => 0, - 'osmask' => 22, - 'version' => '20120809'); - -sub getConfig { return(%config); } -sub getShortDescr { return('Extracts WinSCP stored session data'); } -sub getDescr {} -sub getRefs {} -sub getHive { return($config{'hive'}); } -sub getVersion { return($config{'version'}); } - -my $VERSION = $config{'version'}; - -sub pluginmain { - my($class, $hive) = @_; - my($reg, $root, $key) = (); - - ::logMsg("Launching winscp_sessions v.$VERSION\n"); - ::rptMsg("winscp_sessions v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner - unless ($reg = Parse::Win32Registry->new($hive)) { - ::logMsg("Failed to open $hive: $!"); - return(); - } - unless ($root = $reg->get_root_key()) { - ::logMsg("Failed to get root key from $hive: $!"); - ::rptMsg("Failed to get root key from $hive: $!"); # line added on 04/02/2013 - return(); - } - - unless ($key = $root->get_subkey('Software\Martin Prikryl\WinSCP 2\Sessions')) { - ::logMsg('"Software\Martin Prikryl\WinSCP 2\Sessions" does not exist'); - ::rptMsg('"Software\Martin Prikryl\WinSCP 2\Sessions" does not exist'); # line added on 04/02/2013 - return(); - } - - my %sessions = (); - my @subkeys = $key->get_list_of_subkeys(); - foreach my $sk (@subkeys) { - my $session_name = $sk->get_name(); - my $epoch = $sk->get_timestamp(); - - my $host = $sk->get_value_data('HostName'); - my $user = $sk->get_value_data('Username'); - my $enc_pass = $sk->get_value_data('PASSWORD'); - my $dec_pass = undef; - if (length($enc_pass)) { - $dec_pass = decrypt_password($enc_pass, $user . $host); - } - - $sessions{$session_name} = { - 'last_update' => $epoch, - 'host' => $host, - 'user' => $user, - 'password' => $dec_pass - }; - } - - foreach my $session_name ( - sort { $sessions{$a}{'last_update'} <=> $sessions{$b}{'last_update'} || - $a cmp $b } keys(%sessions)) { - - my $header = sprintf("%-35s Last Updated: %s UTC", $session_name, scalar(gmtime($sessions{$session_name}{'last_update'}))); - - ::rptMsg("$header"); - ::rptMsg(" Host: $sessions{$session_name}{'host'}"); - ::rptMsg(" User: $sessions{$session_name}{'user'}"); - ::rptMsg(" Password: $sessions{$session_name}{'password'}\n"); - } -} - - -# This code adapted from TheLightCosine's winscp.rb Metasploit module -# -sub decrypt_password { - my($enc, $prefix) = @_; - - my $user_host_encoded = 0; - - my $length = decode_chars(substr($enc, 0, 2, undef)); - if ($length == 0xFF) { - $user_host_encoded = 1; - $enc = substr($enc, 2); - $length = decode_chars(substr($enc, 0, 2, undef)); - } - - my $skip_len = decode_chars(substr($enc, 0, 2, undef)) * 2; - $enc = substr($enc, $skip_len); - - my $dec = ''; - for (my $i = 0; $i < $length; $i++) { - last if (length($enc) < 2); - $dec .= chr(decode_chars(substr($enc, 0, 2, undef))); - } - - $dec = substr($dec, length($prefix)) if ($user_host_encoded); - return($dec); -} - -sub decode_chars { - my($hex) = @_; - - return((hex($hex) ^ 0xA3) ^ 0xFF); -} - -1; diff --git a/thirdparty/rr-full/plugins/winver.pl b/thirdparty/rr-full/plugins/winver.pl index 1dd3720ba9c..ba6a1010b85 100644 --- a/thirdparty/rr-full/plugins/winver.pl +++ b/thirdparty/rr-full/plugins/winver.pl @@ -1,22 +1,31 @@ #----------------------------------------------------------- # winver.pl # -# copyright 2008-2009 H. Carvey, keydet89@yahoo.com +# +# Change History: +# 20200916 - MITRE updates +# 20200525 - updated date output format, other updates +# 20081210 - created +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package winver; use strict; my %config = (hive => "Software", - osmask => 22, + MITRE => "", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20081210); + output => "report", + version => 20200916); sub getConfig{return %config} sub getShortDescr { - return "Get Windows version"; + return "Get Windows version & build info"; } sub getDescr{} sub getRefs {} @@ -29,81 +38,48 @@ sub pluginmain { my $class = shift; my $hive = shift; ::logMsg("Launching winver v.".$VERSION); - ::rptMsg("winver v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + ::rptMsg("winver v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + + + my %vals = (1 => "ProductName", + 2 => "ReleaseID", + 3 => "CSDVersion", + 4 => "BuildLab", + 5 => "BuildLabEx", + 6 => "CompositionEditionID", + 7 => "RegisteredOrganization", + 8 => "RegisteredOwner"); + my $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; - my $key_path = "Microsoft\\Windows NT\\CurrentVersion"; my $key; if ($key = $root_key->get_subkey($key_path)) { -# ::rptMsg("{name}"); -# ::rptMsg($key_path); -# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - - my $prod; - eval { - $prod = $key->get_value("ProductName")->get_data(); - }; - if ($@) { -# ::rptMsg("ProductName value not found."); - } - else { - ::rptMsg("ProductName = ".$prod); - } - - my $csd; - eval { - $csd = $key->get_value("CSDVersion")->get_data(); - }; - if ($@) { -# ::rptMsg("CSDVersion value not found."); - } - else { - ::rptMsg("CSDVersion = ".$csd); - } - - my $build; - eval { - $build = $key->get_value("BuildName")->get_data(); - }; - if ($@) { -# ::rptMsg("BuildName value not found."); - } - else { - ::rptMsg("BuildName = ".$build); + foreach my $v (sort {$a <=> $b} keys %vals) { + + eval { + my $i = $key->get_value($vals{$v})->get_data(); + ::rptMsg(sprintf "%-25s %-20s",$vals{$v},$i); + }; } - my $buildex; eval { - $buildex = $key->get_value("BuildNameEx")->get_data(); + my $install = $key->get_value("InstallDate")->get_data(); + ::rptMsg(sprintf "%-25s %-20s","InstallDate",::format8601Date($install)."Z"); }; - if ($@) { -# ::rptMsg("BuildName value not found."); - } - else { - ::rptMsg("BuildNameEx = ".$buildex); - } - - - my $install; + eval { - $install = $key->get_value("InstallDate")->get_data(); + my $it = $key->get_value("InstallTime")->get_data(); + my ($t0,$t1) = unpack("VV",$it); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf "%-25s %-20s","InstallTime",::format8601Date($t)."Z"); }; - if ($@) { -# ::rptMsg("InstallDate value not found."); - } - else { - ::rptMsg("InstallDate = ".gmtime($install)); - } - } else { ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); } - } 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/winvnc.pl b/thirdparty/rr-full/plugins/winvnc.pl deleted file mode 100644 index 85b0b2842d7..00000000000 --- a/thirdparty/rr-full/plugins/winvnc.pl +++ /dev/null @@ -1,122 +0,0 @@ -#----------------------------------------------------------- -# winvnc.pl -# Extracts the encrypted password for WinVNC -# -# Change History -# 20110205 [bco] * bug fix, password output now in hex format -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright (c) 2011-02-02 Brendan Coles -#----------------------------------------------------------- -# Require # -package winvnc; -use strict; - -# Declarations # -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20110202); -my $VERSION = getVersion(); - -# Functions # -sub getDescr {} -sub getConfig {return %config} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} -sub getShortDescr { - return "Extracts the encrypted password for WinVNC."; -} -sub getRefs { - my %refs = ("WinVNC Homepage:" => - "http://www.realvnc.com/"); - return %refs; -} - -############################################################ -# pluginmain # -############################################################ -sub pluginmain { - - # Declarations # - my $class = shift; - my $hive = shift; - my @interesting_paths = ( - 'Software\\ORL\\WinVNC3', - 'Software\\ORL\\WinVNC3\\Default', - 'Software\\ORL\\WinVNC\\Default', - 'Software\\RealVNC\\WinVNC4', - 'Software\\RealVNC\\Default' - ); - my @interesting_keys = ( - "Password", - "PasswordViewOnly" - ); - - # Initialize # - ::logMsg("Launching winvnc v.".$VERSION); - ::rptMsg("winvnc v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - - # Extract # possible registry paths - foreach my $key_path (@interesting_paths) { - - # If # WinVNC path exists # - my $key; - if ($key = $root_key->get_subkey($key_path)) { - - # Return # plugin name, registry key and last modified date # - ::rptMsg("WinVNC"); - ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); - ::rptMsg(""); - - # Extract # all keys from winvnc registry path # - my %keys; - my @vals = $key->get_list_of_values(); - - # If # registry keys exist in path # - if (scalar(@vals) > 0) { - - # Extract # all key names+values for winvnc registry path # - foreach my $v (@vals) { - $keys{$v->get_name()} = $v->get_data(); - } - - # Return # all key names+values for interesting keys # - foreach my $var (@interesting_keys) { - if (exists $keys{$var}) { - my $hstring = unpack ("H*",$keys{$var}); - ::rptMsg($var." -> ".$hstring); - } - } - - # Return # obligatory new-line # - ::rptMsg(""); - - # Error # key value is null # - } else { - ::rptMsg($key_path." has no values."); - } - - # Error # WinVNC isn't here, try another castle # - } else { - ::rptMsg($key_path." not found."); - ::logMsg($key_path." not found."); - } - - } - - # Return # obligatory new-line # - ::rptMsg(""); -} - -# Error # oh snap! # -1; diff --git a/thirdparty/rr-full/plugins/winzip.pl b/thirdparty/rr-full/plugins/winzip.pl index 99cd3744b9a..be0476fec91 100644 --- a/thirdparty/rr-full/plugins/winzip.pl +++ b/thirdparty/rr-full/plugins/winzip.pl @@ -2,10 +2,12 @@ # WinZip # # History +# 20200803 - updates +# 20200526 - updated date output format # 20140730 - updated to include mru/archives info # 20080325 - created # -# copyright 2014 QAR, LLC +# copyright 2020 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package winzip; @@ -15,8 +17,10 @@ package winzip; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20140730); + category => "user activity", + MITRE => "T1074", + output => "report", + version => 20200803); sub getConfig{return %config} sub getShortDescr { @@ -51,7 +55,7 @@ sub pluginmain { if (exists $sk{'extract'}) { my $tag = "extract"; - ::rptMsg($key_path."\\extract [".gmtime($sk{'extract'}->get_timestamp)."]"); + ::rptMsg($key_path."\\extract [".::format8601Date($sk{'extract'}->get_timestamp)."Z]"); my @vals = $sk{'extract'}->get_list_of_values(); my %ext; foreach my $v (@vals) { @@ -71,7 +75,7 @@ sub pluginmain { if (exists $sk{'filemenu'}) { my $tag = "filemenu"; - ::rptMsg($key_path."\\filemenu [".gmtime($sk{'extract'}->get_timestamp)."]"); + ::rptMsg($key_path."\\filemenu [".::format8601Date($sk{'filemenu'}->get_timestamp)."Z]"); my @vals = $sk{'filemenu'}->get_list_of_values(); my %ext; foreach my $v (@vals) { @@ -91,7 +95,7 @@ sub pluginmain { # added 20140730 my $archives; if ($archives = $key->get_subkey("mru\\archives")) { - ::rptMsg("mru\\archives subkey [".gmtime($archives->get_timestamp())."]"); + ::rptMsg("mru\\archives subkey [".::format8601Date($archives->get_timestamp())."Z]"); my @vals = $archives->get_list_of_values(); if (scalar @vals > 0) { diff --git a/thirdparty/rr-full/plugins/wordstartup.pl b/thirdparty/rr-full/plugins/wordstartup.pl new file mode 100644 index 00000000000..ea20721bbb9 --- /dev/null +++ b/thirdparty/rr-full/plugins/wordstartup.pl @@ -0,0 +1,96 @@ +#----------------------------------------------------------- +# wordstartup.pl +# Display location of MSWord startup folder, if changed +# +# Change history +# 20220529 - created +# +# References +# https://twitter.com/malmoeb/status/1530862908871163905 +# https://www.thewindowsclub.com/how-to-change-the-startup-folder-of-word#:~:text=Where%20is%20Word%20Startup%20folder,%5CMicrosoft%5CWord%5CSTARTUP. +# https://insight-jp.nttsecurity.com/post/102hojk/operation-restylink-apt-campaign-targeting-japanese-companies +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wordstartup; +use strict; + +my %config = (hive => "NTUSER\.DAT", + category => "defense evasion", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1112", + output => "report", + version => 20220529); + +sub getConfig{return %config} +sub getShortDescr { + return "Display MSWord StartUp folder, if changed"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $office_version; + +sub pluginmain { + my $class = shift; + my $ntuser = shift; + ::logMsg("Launching wordstartup v.".$VERSION); + ::rptMsg("wordstartup v.".$VERSION); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg("wordstartup v.".$VERSION); + ::rptMsg("MITRE ATT&CK: ".$config{category}." (".$config{MITRE}.")"); + ::rptMsg(""); +# First, let's find out which version of Office is installed + my @version; + my $key; + my $key_path = "Software\\Microsoft\\Office"; + if ($key = $root_key->get_subkey($key_path)) { + my @subkeys = $key->get_list_of_subkeys(); + foreach my $s (@subkeys) { + my $name = $s->get_name(); + push(@version,$name) if ($name =~ m/^\d/); + } + } +# Determine MSOffice version in use + my @v = reverse sort {$a<=>$b} @version; + foreach my $i (@v) { + eval { + if (my $o = $key->get_subkey($i."\\User Settings")) { + $office_version = $i; + } + }; + } + + my $key = ""; + my $key_path = "Software\\Policies\\Microsoft\\office\\".$office_version."\\word\\options"; + if ($key = $root_key->get_subkey($key_path)) { + eval { + my $start = $key->get_value("startup-path")->get_data(); + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg("MSWord STARTUP folder: ".$start); + }; + ::rptMsg("startup-path value not found.") if ($@); + } + else { + ::rptMsg($key_path." not found."); + } + ::rptMsg(""); + ::rptMsg("Analysis Tip: By default, the MSWord STARTUP folder is located at \"%AppData%\\Roaming\\Microsoft\\Word\\STARTUP\""); + ::rptMsg("\.dot files in this folder may contain macros that are run each time MSWord is launched, and this folder can be"); + ::rptMsg("changed via GPO or the Registry. Use of the MSWord STARTUP folder was observed in the RestyLink APT campaign:"); + ::rptMsg("https://insight-jp.nttsecurity.com/post/102hojk/operation-restylink-apt-campaign-targeting-japanese-companies"); +# ::rptMsg(""); +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wordwheelquery.pl b/thirdparty/rr-full/plugins/wordwheelquery.pl index 1a453698149..17d7344e1c6 100644 --- a/thirdparty/rr-full/plugins/wordwheelquery.pl +++ b/thirdparty/rr-full/plugins/wordwheelquery.pl @@ -3,6 +3,9 @@ # For Windows 7 # # Change history +# 20200916 - MITRE updates +# 20200824 - Unicode update +# 20200526 - updated date output format # 20100330 - created # # References @@ -17,8 +20,10 @@ package wordwheelquery; hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20100330); + MITRE => "", + category => "user activity", + output => "report", + version => 20200916); sub getConfig{return %config} sub getShortDescr { @@ -44,7 +49,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar(@vals) > 0) { my @list; @@ -57,8 +62,7 @@ sub pluginmain { } else { my $data = $v->get_data(); - $data =~ s/\x00//g; - $wwq{$name} = $data; + $wwq{$name} = ::getUnicodeStr($data); } } # list searches in MRUListEx order diff --git a/thirdparty/rr-full/plugins/wordwheelquery_tln.pl b/thirdparty/rr-full/plugins/wordwheelquery_tln.pl new file mode 100644 index 00000000000..7a716123cd7 --- /dev/null +++ b/thirdparty/rr-full/plugins/wordwheelquery_tln.pl @@ -0,0 +1,72 @@ +#----------------------------------------------------------- +# wordwheelquery_tln.pl +# For Windows 7+ +# +# Change history +# 20200916 - MITRE updates +# 20200824 - Unicode update +# 20200325 - created, copied from wordwheelquery.pl +# 20100330 - original plugin created +# +# References +# http://www.winhelponline.com/blog/clear-file-search-mru-history-windows-7/ +# +# copyright 2020 Quantum Analytics Research, LLC +#----------------------------------------------------------- +package wordwheelquery_tln; +use strict; + +my %config = (hive => "NTUSER\.DAT", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + category => "user activity", + output => "tln", + version => 20200916); + +sub getConfig{return %config} +sub getShortDescr { + return "Gets contents of user's WordWheelQuery key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $ntuser = shift; +# ::logMsg("Launching wordwheelquery v.".$VERSION); +# ::rptMsg("wordwheelquery v.".$VERSION); # banner +# ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\WordWheelQuery"; + my $key; + my $search = ""; + if ($key = $root_key->get_subkey($key_path)) { + my $lw = $key->get_timestamp(); + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 1) { + my $data = $key->get_value("MRUListEx")->get_data(); + my @list = unpack("V*",$data); + if ($list[0] != 0xffffffff) { + $search = $key->get_value($list[0])->get_data(); + $search = ::getUnicodeStr($search); + } + ::rptMsg($lw."|REG|||WordWheelQuery most recent search: ".$search); + } + else { +# ::rptMsg($key_path." has no values."); + } + } + else { +# ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wow64.pl b/thirdparty/rr-full/plugins/wow64.pl index fff65ad3774..7fe0c85b0cc 100644 --- a/thirdparty/rr-full/plugins/wow64.pl +++ b/thirdparty/rr-full/plugins/wow64.pl @@ -2,25 +2,30 @@ # wow64 # # Change history: +# 20200916 - MITRE udpates +# 20200515 - updated date output format # 20190712 - created # # Ref: # http://www.hexacorn.com/blog/2019/07/11/beyond-good-ol-run-key-part-108-2/ # https://wbenny.github.io/2018/11/04/wow64-internals.html # -# copyright 2019 QAR,LLC +# https://attack.mitre.org/techniques/T1546/ +# +# copyright 2020 QAR,LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package wow64; use strict; my %config = (hive => "Software", - category => "persistence", + category => "persistence", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20190712); + MITRE => "T1546", + output => "report", + version => 20200916); sub getConfig{return %config} sub getShortDescr { @@ -38,7 +43,9 @@ sub pluginmain { my $hive = shift; ::rptMsg("Launching wow64 v.".$VERSION); ::rptMsg("wow64 v.".$VERSION); - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + ::rptMsg("(".$config{hive}.") ".getShortDescr()); + ::rptMsg("MITRE: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); my @paths = ('Microsoft\\WOW64\\x86','Microsoft\\WOW64\\arm'); ::rptMsg("WOW64"); @@ -49,7 +56,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); my @vals; if (@vals = $key->get_list_of_values()) { diff --git a/thirdparty/rr-full/plugins/wpbt.pl b/thirdparty/rr-full/plugins/wpbt.pl new file mode 100644 index 00000000000..9ac48f18a31 --- /dev/null +++ b/thirdparty/rr-full/plugins/wpbt.pl @@ -0,0 +1,71 @@ +#----------------------------------------------------------- +# wpbt.pl +# Get Windows Platform Binary Table Settings +# +# Change history +# 20220718 - created +# +# References +# https://persistence-info.github.io/Data/wpbbin.html +# https://github.com/Jamesits/dropWPBT +# +# +# copyright 2022 QAR, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wpbt; + +my %config = (hive => "system", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "report", + category => "persistence", + MITRE => "T1542\.001", + version => 20220718); + +sub getConfig{return %config} +sub getShortDescr { + return "Get Windows Platform Binary Table Settings"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching wpbt v.".$VERSION); + ::rptMsg("wpbt v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()); + ::rptMsg("MITRE ATT&CK technique: ".$config{MITRE}." (".$config{category}.")"); + ::rptMsg(""); + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Control\\Session Manager"; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $a = $key->get_value("DisableWpbtExecution")->get_data(); + ::rptMsg("DisableWpbtExecution value: ".$a); + }; + + ::rptMsg(""); + ::rptMsg("Analysis Tip: Setting the DisableWpbtExecution to \"1\" disables reading of the platform binary table."); + ::rptMsg(""); + ::rptMsg("Ref: https://persistence-info.github.io/Data/wpbbin.html"); + ::rptMsg("Ref: https://grzegorztworek.medium.com/using-uefi-to-inject-executable-files-into-bitlocker-protected-drives-8ff4ca59c94c"); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wpdbusenum.pl b/thirdparty/rr-full/plugins/wpdbusenum.pl index 7d88ee546d8..146f936fd08 100644 --- a/thirdparty/rr-full/plugins/wpdbusenum.pl +++ b/thirdparty/rr-full/plugins/wpdbusenum.pl @@ -1,32 +1,33 @@ #----------------------------------------------------------- -# wpdbusenum -# Gets contents of Enum\WpdBusEnumRoot keys +# wpdbusenum.pl +# Parses contents of Enum\USBStor # +# History +# 20220524 - copied from usbdevices.pl # -# History: -# 20141111 - updated check for key LastWrite times -# 20141015 - added additional checks -# 20120523 - Added support for a DeviceClasses subkey that includes -# "WpdBusEnum" in the names; from MarkW and ColinC -# 20120410 - created +# References: +# http://www.swiftforensics.com/2013/11/windows-8-new-registry-artifacts-part-1.html +# https://www.researchgate.net/publication/318514858_USB_Storage_Device_Forensics_for_Windows_10 # -# copyright 2012 Quantum Analytics Research, LLC -# Author: H. Carvey, keydet89@yahoo.com +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package wpdbusenum; use strict; my %config = (hive => "System", - osmask => 22, + MITRE => "", + category => "devices", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - version => 20141111); + output => "report", + version => 20220524); sub getConfig{return %config} sub getShortDescr { - return "Get WpdBusEnumRoot subkey info"; + return "Parses Enum\\SWD\\WPDBUSENUM key"; } sub getDescr{} sub getRefs {} @@ -39,80 +40,37 @@ sub getShortDescr { sub pluginmain { my $class = shift; my $hive = shift; - ::logMsg("Launching wpdbusenum v.".$VERSION); - ::rptMsg("wpdbusenum v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner $reg = Parse::Win32Registry->new($hive); my $root_key = $reg->get_root_key; + ::logMsg("Launching wpdbusenum v.".$VERSION); + ::rptMsg("wpdbusenum v.".$VERSION); + ::rptMsg("(".getHive().") ".getShortDescr()."\n"); -# Code for System file, getting CurrentControlSet - my $current; - my $ccs; - my $key_path = 'Select'; my $key; - if ($key = $root_key->get_subkey($key_path)) { - $current = $key->get_value("Current")->get_data(); - $ccs = "ControlSet00".$current; - } - else { - ::rptMsg($key_path." not found."); - return; - } + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Enum\\SWD\\WPDBUSENUM"; + my $key; + + my @vals = ("DeviceDesc","FriendlyName"); - $key_path = $ccs."\\Enum\\WpdBusEnumRoot"; if ($key = $root_key->get_subkey($key_path)) { - + my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { + if (scalar @subkeys > 0) { foreach my $s (@subkeys) { - my $dev_class = $s->get_name(); - my @sk = $s->get_list_of_subkeys(); - if (scalar(@sk) > 0) { - foreach my $k (@sk) { - my $serial = $k->get_name(); - my ($dev,$sn) = (split(/#/,$k->get_name(),5))[3,4]; - $sn =~ s/#$//; - ::rptMsg($dev." (".$sn.")"); + ::rptMsg($s->get_name()); - my $sn_lw = $k->get_timestamp(); - ::rptMsg(" LastWrite: ".gmtime($sn_lw)); - - eval { - ::rptMsg(" DeviceDesc: ".$k->get_value("DeviceDesc")->get_data()); - }; - - eval { - ::rptMsg(" Friendly: ".$k->get_value("FriendlyName")->get_data()); - }; - - eval { - my $mfg = $k->get_value("Mfg")->get_data(); - ::rptMsg(" Mfg: ".$mfg) unless ($mfg eq ""); - }; -# added 20141015; updated 20141111 - eval { - ::rptMsg(" Device Parameters LastWrite: [".gmtime($k->get_subkey("Device Parameters")->get_timestamp())."]"); - }; - eval { - ::rptMsg(" LogConf LastWrite : [".gmtime($k->get_subkey("LogConf")->get_timestamp())."]"); - }; - eval { - ::rptMsg(" Properties LastWrite : [".gmtime($k->get_subkey("Properties")->get_timestamp())."]"); - }; - eval { - my $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\00000064\\00000000")->get_value("Data")->get_data(); - my ($t0,$t1) = unpack("VV",$t); - ::rptMsg(" InstallDate : ".gmtime(::getTime($t0,$t1))." UTC"); - - $t = $k->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}\\00000065\\00000000")->get_value("Data")->get_data(); - ($t0,$t1) = unpack("VV",$t); - ::rptMsg(" FirstInstallDate: ".gmtime(::getTime($t0,$t1))." UTC"); - }; - - - ::rptMsg(""); - } + foreach my $v (@vals) { + eval { + my $x = $s->get_value($v)->get_data(); + ::rptMsg(sprintf " %-15s: %-30s",$v,$x); + }; } +# get Properties\{83da6326-97a6-4088-9453-a1923f573b29} + eval { + getProperties($s->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}")); + }; + ::rptMsg(""); } } else { @@ -122,36 +80,42 @@ sub pluginmain { else { ::rptMsg($key_path." not found."); } +} + + +sub getProperties { + my $key = shift; + + eval { + my $r = $key->get_subkey("0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Install",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","First Inserted",::format8601Date($t)."Z"); + }; -# Added on 20120523, based on a tweet from Mark Woan while he was attending -# CEIC2012; he attributes this to ColinC. Googling for this key, I found a -# number of references to USBOblivion, a tool described as being able to wipe -# out (all) indications of USB removable storage devices being connected to -# the system. - $key_path = $ccs."\\Control\\DeviceClasses\\{10497b1b-ba51-44e5-8318-a65c837b6661}"; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg($key_path); - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - my $name = $s->get_name(); - my $lw = $s->get_timestamp(); - - my (@n) = split(/#/,$name); - - if ($n[3] eq "WpdBusEnumRoot") { - ::rptMsg($n[8]."\\".$n[9]); - ::rptMsg("LastWrite: ".gmtime($lw)); - ::rptMsg(""); - } - } - } - else { - ::rptMsg($key_path." has no subkeys."); - } - } - else { - ::rptMsg($key_path." not found."); - } + eval { + my $r = $key->get_subkey("0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Inserted",::format8601Date($t)."Z"); + }; + + eval { + my $r = $key->get_subkey("0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg(sprintf " %-15s: %-25s","Last Removal",::format8601Date($t)."Z"); + }; + + } -1; + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wpdbusenum_tln.pl b/thirdparty/rr-full/plugins/wpdbusenum_tln.pl new file mode 100644 index 00000000000..716190e5aee --- /dev/null +++ b/thirdparty/rr-full/plugins/wpdbusenum_tln.pl @@ -0,0 +1,129 @@ +#----------------------------------------------------------- +# wpdbusenum_tln.pl +# Parses contents of Enum\SWD\WPDBUSENUM key +# +# History +# 20220524 - created, copied from usbdevices_tln.pl +# +# References: +# http://www.swiftforensics.com/2013/11/windows-8-new-registry-artifacts-part-1.html +# https://www.researchgate.net/publication/318514858_USB_Storage_Device_Forensics_for_Windows_10 +# +# copyright 2022 Quantum Analytics Research, LLC +# author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wpdbusenum_tln; +use strict; + +my %config = (hive => "System", + MITRE => "", + category => "devices", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + output => "tln", + version => 20220524); + +sub getConfig{return %config} + +sub getShortDescr { + return "Parses Enum\\USBStor key"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my $reg; + +sub pluginmain { + my $class = shift; + my $hive = shift; + $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# ::logMsg("Launching usbdevices v.".$VERSION); +# ::rptMsg("usbdevices v.".$VERSION); +# ::rptMsg("(".getHive().") ".getShortDescr()."\n"); + + my $key; + my $ccs = ::getCCS($root_key); + my $key_path = $ccs."\\Enum\\SWD\\WPDBUSENUM"; + my $key; + + if ($key = $root_key->get_subkey($key_path)) { + + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + + my $f = ""; + my $x = ""; + + eval { + $f = $s->get_value("FriendlyName")->get_data(); + }; + + eval { + $x = $s->get_value("DeviceDesc")->get_data(); + }; + + my $name = $x; + if ($f ne "") { + $name .= " [".$f."]"; + } + +# get Properties\{83da6326-97a6-4088-9453-a1923f573b29} + eval { + getProperties($name,$s->get_subkey("Properties\\{83da6326-97a6-4088-9453-a1923f573b29}")); + }; + } + } + else { +# ::rptMsg($key_path." has no subkeys."); + } + } + else { +# ::rptMsg($key_path." not found."); + } +} + + +sub getProperties { + my $name = shift; + my $key = shift; + + eval { + my $r = $key->get_subkey("0064")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||First Install - ".$name); + }; + + eval { + my $r = $key->get_subkey("0065")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||First Inserted - ".$name); + }; + + eval { + my $r = $key->get_subkey("0066")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||Last Inserted - ".$name); + }; + + eval { + my $r = $key->get_subkey("0067")->get_value("")->get_data(); + my ($t0,$t1) = unpack("VV",$r); + my $t = ::getTime($t0,$t1); + ::rptMsg($t."|REG|||Last Removal - ".$name); + + }; + + +} + + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wrdata.pl b/thirdparty/rr-full/plugins/wrdata.pl new file mode 100644 index 00000000000..6d4e399d52a --- /dev/null +++ b/thirdparty/rr-full/plugins/wrdata.pl @@ -0,0 +1,152 @@ +#----------------------------------------------------------- +# wrdata.pl +# +# Change history: +# 20200916 - MITRE updates +# 20200427 - updated output date format +# 20200413 - created +# +# Ref: +# +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wrdata; +use strict; + +my %config = (hive => "Software", + category => "antivirus", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20200916); + +sub getConfig{return %config} +sub getShortDescr { + return "Collects WebRoot AV Data"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching wrdata v.".$VERSION); + ::rptMsg("wrdata v.".$VERSION); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + my @paths = ('WRData','Wow6432Node\\WRData'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time ".::format8601Date($key->get_timestamp())."Z"); + + eval{ + my $li = $key->get_value("LastInfection")->get_data(); + ::rptMsg(""); + ::rptMsg("LastInfection: ".$li); + ::rptMsg(""); + }; + +# Status subkey + if (my $s = $key->get_subkey("Status")) { + ::rptMsg($key_path."\\Status"); + + my @vals = ("CurrentUser","DNSServer","Email","HostName","InternalIP","IPV4","MACAddress","OS","PrimaryBrowser"); + foreach my $v (@vals) { + eval { + ::rptMsg(sprintf "%-15s %-30s",$v,$s->get_value($v)->get_data()); + }; + } + ::rptMsg(""); + + eval { + my $lb = $s->get_value("LastBlockedURL")->get_data(); + my $ls = $s->get_value("LastBlockedURLSeen")->get_data(); + ::rptMsg("Last Blocked URL: ".$lb." [".::format8601Date($ls)."Z]"); + }; + + eval { + my $lt = $s->get_value("LatestThreat")->get_data(); + my $l = $s->get_value("LastThreatSeen")->get_data(); + ::rptMsg("Lastest Threat: ".$lt." [".::format8601Date($l)."Z]"); + ::rptMsg(""); + }; + + } + else { + ::rptMsg("Key ".$key_path."\\Status not found."); + } + +# Journal subkey + if (my $j = $key->get_subkey("Journal")) { + my @vals = $j->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg($key_path."\\Journal"); + foreach my $v (@vals) { + my ($file,$hash,$ts) = split(/,/,$v->get_data(),3); + my $f = (split(/=/,$file,2))[1]; + my $h = (split(/=/,$hash,2))[1]; + my $t = (split(/=/,$ts,2))[1]; + ::rptMsg("Filename : ".$f); + ::rptMsg("MD5 Hash : ".$h); + ::rptMsg("Time stamp: ".::format8601Date($t)."Z"); + ::rptMsg(""); + } + } + } + else { + ::rptMsg("Key ".$key_path."\\Journal not found."); + } + +# Threats\Active subkey + if (my $a = $key->get_subkey("Threats\\Active")) { + my @vals = $a->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg($key_path."\\Threats\\Active"); + foreach my $v (@vals) { + next if ($v->get_name() eq "Count"); + my ($file,$id,$t) = split(/\|/,$v->get_data(),3); + ::rptMsg($id." = ".$file." [".::format8601Date(hex($t))."Z]"); + } + ::rptMsg(""); + } + } + else { + ::rptMsg("Key ".$key_path."\\Threats\\Active not found."); + } + +# Threats\History subkey + if (my $h = $key->get_subkey("Threats\\History")) { + my @vals = $h->get_list_of_values(); + if (scalar @vals > 0) { + ::rptMsg($key_path."\\Threats\\History"); + foreach my $v (@vals) { + next if ($v->get_name() eq "Count"); + my ($file,$id,$t) = split(/\|/,$v->get_data(),3); + ::rptMsg($id." = ".$file." [".::format8601Date(hex($t))."Z]"); + } + ::rptMsg(""); + } + } + else { + ::rptMsg("Key ".$key_path."\\Threats\\History not found."); + } + } + else { + ::rptMsg($key_path." not found."); + } + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wrdata_tln.pl b/thirdparty/rr-full/plugins/wrdata_tln.pl new file mode 100644 index 00000000000..5c58b425ab2 --- /dev/null +++ b/thirdparty/rr-full/plugins/wrdata_tln.pl @@ -0,0 +1,131 @@ +#----------------------------------------------------------- +# wrdata_tln.pl +# +# Change history: +# 20200916 - MITRE Updates +# 20200413 - created +# +# Ref: +# +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wrdata_tln; +use strict; + +my %config = (hive => "Software", + category => "antivirus", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "tln", + version => 20200916); + +sub getConfig{return %config} +sub getShortDescr { + return "Collects WebRoot AV Data"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; +# ::rptMsg("Launching wrdata v.".$VERSION); +# ::rptMsg("wrdata v.".$VERSION); # banner +# ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + my @paths = ('WRData','Wow6432Node\\WRData'); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + foreach my $key_path (@paths) { + my $key; + if ($key = $root_key->get_subkey($key_path)) { +# ::rptMsg($key_path); +# ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); + +# Status subkey + if (my $s = $key->get_subkey("Status")) { + + eval { + my $lb = $s->get_value("LastBlockedURL")->get_data(); + my $ls = $s->get_value("LastBlockedURLSeen")->get_data(); + ::rptMsg($ls."|REG|||WebRoot Last Blocked URL: ".$lb); + }; + + eval { + my $lt = $s->get_value("LatestThreat")->get_data(); + my $l = $s->get_value("LastThreatSeen")->get_data(); + ::rptMsg($l."|REG|||WebRoot LatestThreat: ".$lt); + }; + + } + else { +# ::rptMsg("Key ".$key_path."\\Status not found."); + } + +# Journal subkey + if (my $j = $key->get_subkey("Journal")) { + my @vals = $j->get_list_of_values(); + if (scalar @vals > 0) { +# ::rptMsg($key_path."\\Journal"); + foreach my $v (@vals) { + my ($file,$hash,$ts) = split(/,/,$v->get_data(),3); + my $f = (split(/=/,$file,2))[1]; + my $h = (split(/=/,$hash,2))[1]; + my $t = (split(/=/,$ts,2))[1]; + ::rptMsg($t."|REG|||WebRoot Journal value: $f Hash: $h"); + } + } + } + else { +# ::rptMsg("Key ".$key_path."\\Journal not found."); + } + +# Threats\Active subkey + if (my $a = $key->get_subkey("Threats\\Active")) { + my @vals = $a->get_list_of_values(); + if (scalar @vals > 0) { +# ::rptMsg($key_path."\\Threats\\Active"); + foreach my $v (@vals) { + next if ($v->get_name() eq "Count"); + my ($file,$id,$t) = split(/\|/,$v->get_data(),3); + ::rptMsg(hex($t)."|REG|||WebRoot Threats\\Active $id $file"); + } +# ::rptMsg(""); + } + } + else { +# ::rptMsg("Key ".$key_path."\\Threats\\Active not found."); + } + +# Threats\History subkey + if (my $h = $key->get_subkey("Threats\\History")) { + my @vals = $h->get_list_of_values(); + if (scalar @vals > 0) { +# ::rptMsg($key_path."\\Threats\\History"); + foreach my $v (@vals) { + next if ($v->get_name() eq "Count"); + my ($file,$id,$t) = split(/\|/,$v->get_data(),3); + ::rptMsg(hex($t)."|REG|||WebRoot Threats\\History $id $file"); + } +# ::rptMsg(""); + } + } + else { +# ::rptMsg("Key ".$key_path."\\Threats\\History not found."); + } + } + else { +# ::rptMsg($key_path." not found."); + } + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/wsh_settings.pl b/thirdparty/rr-full/plugins/wsh_settings.pl index e7c8b2dc577..d097b2bcd60 100644 --- a/thirdparty/rr-full/plugins/wsh_settings.pl +++ b/thirdparty/rr-full/plugins/wsh_settings.pl @@ -2,24 +2,30 @@ # wsh_settings # # Change history: +# 20230201 - updated references, analysis tips +# 20200916 - MITRE updates +# 20200517 - updated date output format # 20180819 - created # # Ref: # http://www.hexacorn.com/blog/2018/08/18/lateral-movement-using-wshcontroller-wshremote-objects-iwshcontroller-and-iwshremote-interfaces/ +# https://www.trustedsec.com/blog/new-attacks-old-tricks-how-onenote-malware-is-evolving +# https://www.thewindowsclub.com/windows-script-host-access-is-disabled-on-this-machine # -# copyright 2018 QAR,LLC +# copyright 2023 QAR,LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- package wsh_settings; use strict; my %config = (hive => "Software", - category => "config", + category => "config", hasShortDescr => 1, hasDescr => 0, hasRefs => 0, - osmask => 22, - version => 20180819); + MITRE => "T1210", + output => "report", + version => 20230201); sub getConfig{return %config} sub getShortDescr { @@ -37,8 +43,8 @@ sub pluginmain { my $hive = shift; my ($name,$data); ::rptMsg("Launching wsh_settings v.".$VERSION); - ::rptMsg("wsh_settings v.".$VERSION); # banner - ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + ::rptMsg("wsh_settings v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); my $key_path = ('Microsoft\\Windows Script Host\\Settings'); my $reg = Parse::Win32Registry->new($hive); @@ -47,7 +53,7 @@ sub pluginmain { my $key; if ($key = $root_key->get_subkey($key_path)) { ::rptMsg($key_path); - ::rptMsg("Key LastWrite: ".gmtime($key->get_timestamp())." Z"); + ::rptMsg("Key LastWrite: ".::format8601Date($key->get_timestamp())."Z"); my @vals = $key->get_list_of_values(); if (scalar @vals > 1) { foreach my $v (@vals) { @@ -56,7 +62,11 @@ sub pluginmain { ::rptMsg(sprintf "%-20s %d",$name,$data); } ::rptMsg(""); - ::rptMsg("Analysis Tip: If Remote value is set to 1, system may be WSH Remoting target"); + ::rptMsg("Analysis Tip: If Remote value is set to 1, system may be WSH Remoting target."); + ::rptMsg("If Enable value is set to \"1\", WSH is enabled on the system; setting it to \"0\""); + ::rptMsg("to disable WSH can inhibit attacks that use WSH."); + ::rptMsg(""); + ::rptMsg("Ref: https://www.trustedsec.com/blog/new-attacks-old-tricks-how-onenote-malware-is-evolving"); } else { ::rptMsg($key_path." has no values."); diff --git a/thirdparty/rr-full/plugins/wtg.pl b/thirdparty/rr-full/plugins/wtg.pl new file mode 100644 index 00000000000..e54db2c8de6 --- /dev/null +++ b/thirdparty/rr-full/plugins/wtg.pl @@ -0,0 +1,68 @@ +#----------------------------------------------------------- +# wtg.pl +# If the Windows installation is set as "Windows To Go", some operations +# have been reported as failing. +# +# History: +# 20200909 - created +# +# References: +# https://support.microsoft.com/en-us/help/2778881/multiple-operations-fail-if-windows-8-is-improperly-identified-as-a-wi +# +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package wtg; +use strict; + +my %config = (hive => "system", + output => "report", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + version => 20200909); + +sub getConfig{return %config} +sub getShortDescr { + return "Check for Windows To Go setting"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my @temps; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching wtg v.".$VERSION); + ::rptMsg("wtg v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $ccs = ::getCCS($root_key); + + my $key_path = $ccs."\\Control"; + if (my $key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + eval { + my $p = $key->get_value("PortableOperatingSystem")->get_data(); + ::rptMsg("PortableOperatingSystem value = ".$p); + ::rptMsg(""); + ::rptMsg("Analysis Tip: If the value is set to \"1\", the system believes it is Windows To Go"); + }; + ::rptMsg("PortableOperatingSystem value not found.") if ($@); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/xbox.pl b/thirdparty/rr-full/plugins/xbox.pl new file mode 100644 index 00000000000..e83338f68ed --- /dev/null +++ b/thirdparty/rr-full/plugins/xbox.pl @@ -0,0 +1,71 @@ +#----------------------------------------------------------- +# xbox.pl +# Check for existence of TreatDeviceAsXbox value +# +# +# Change history: +# 20200909 - created +# +# Ref: +# https://twitter.com/Hexacorn/status/1303293650835828736 +# +# copyright 2020 QAR,LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package xbox; +use strict; + +my %config = (hive => "Software", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "T1546", + output => "report", + version => 20200909); + +sub getConfig{return %config} +sub getShortDescr { + return "Check for existence of TreatDeviceAsXbox value"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::rptMsg("Launching xbox v.".$VERSION); + ::rptMsg("xbox v.".$VERSION); + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); + + my @paths = ('Microsoft\\Windows\\CurrentVersion\\Diagnostics\\DiagTrack\\TestHooks', + 'Microsoft\\Windows\\CurrentVersion\\Diagnostics\\DiagTrack\\TestHooks\\Volatile'); + my $key_path; + foreach $key_path (@paths) { + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; + + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg($key_path); + ::rptMsg("LastWrite Time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + eval { + my $x = $key->get_value("TreatDeviceAsXbox")->get_data(); + ::rptMsg("TreatDeviceAsXbox value = ".$x); + ::rptMsg(""); + ::rptMsg("Analysis Tip: This value is queried via svchost.exe when several DLLs are loaded; if it exists,"); + ::rptMsg("the behavior of the operating system or applications may be impacted."); + }; + ::rptMsg($key_path."\\TreatDeviceAsXbox value not found.") if ($@); + } + else { + ::rptMsg($key_path." key not found."); + } + } +} +1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/xpedition.pl b/thirdparty/rr-full/plugins/xpedition.pl deleted file mode 100644 index df9d5744776..00000000000 --- a/thirdparty/rr-full/plugins/xpedition.pl +++ /dev/null @@ -1,67 +0,0 @@ -#----------------------------------------------------------- -# xpedition.pl -# Determine the edition of XP (MediaCenter, TabletPC) -# -# History -# 20120722 - updated the %config hash -# 20090727 - created -# -# References -# http://windowsitpro.com/article/articleid/94531/ -# how-can-a-script-determine-if-windows-xp-tablet-pc-edition-is-installed.html -# http://unasked.com/question/view/id/119610 -# -# copyright 2009 H. Carvey -#----------------------------------------------------------- -package xpedition; -use strict; -my %config = (hive => "System", - hivemask => 4, - output => "report", - category => "", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 0, - osmask => 1, - version => 20120722); - -sub getConfig{return %config} -sub getShortDescr { - return "Queries System hive for XP Edition info"; -} -sub getDescr{} -sub getRefs {} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $hive = shift; - my $key; - my $edition = 0; - - ::logMsg("Launching xpedition v.".$VERSION); - ::rptMsg("xpedition v.".$VERSION); # banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; - ::rptMsg("xpedition v.".$VERSION); - eval { - $key = $root_key->get_subkey("WPA\\MediaCenter")->get_value("Installed")->get_data(); - if ($key == 1) { - ::rptMsg("MediaCenter Edition"); - $edition = 1; - } - }; - - eval { - $key = $root_key->get_subkey("WPA\\TabletPC")->get_value("Installed")->get_data(); - if ($key == 1) { - ::rptMsg("TabletPC Edition"); - $edition = 1; - } - }; -} -1 \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/yahoo_cu.pl b/thirdparty/rr-full/plugins/yahoo_cu.pl deleted file mode 100644 index ae331080db2..00000000000 --- a/thirdparty/rr-full/plugins/yahoo_cu.pl +++ /dev/null @@ -1,349 +0,0 @@ -#----------------------------------------------------------- -# yahoo_cu.pl -# Yahoo Messenger parser (HKCU) -# -# Change history -# 20101219 [fpi] % created -# 20101219 [fpi] % first version -# 20101221 [fpi] * added refences, minor changes -# 20110830 [fpi] + banner, no change to the version number -# -# References -# Registry Quick Find Chart - AccessData -# Bruce Long Internet Forensics - Yahoo Instant Messenger -# http://www.xssed.com/article/14/Paper_In-Depth_Analysis_of_Yahoo_Authentication_Schemes/ -# -# -# NOTE: missing to manage the following -# - IMVironments (global and user) -# - user\Cache (missing informations about it) -# - user\Chat -# -# copyright 2011 F. Picasso -#----------------------------------------------------------- -package yahoo_cu; -use strict; - -my %config = (hive => "NTUSER\.DAT", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20101219); - -sub getConfig{return %config} -sub getShortDescr { - return "Yahoo Messenger parser"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Registry Quick Find Chart - AccessData" => - "http://www.accessdata.com/media/en_us/print/papers/wp.Registry_Quick_Find_Chart.en_us.pdf", - "In-Depth Analysis of Yahoo! Authentication Schemes" => - "http://www.xssed.com/article/14/Paper_In-Depth_Analysis_of_Yahoo_Authentication_Schemes/", - "Bruce Long" => - "Internet Forensics - Yahoo Instant Messenger"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg( "Launching yahoo_cu v.".$VERSION ); - ::rptMsg("yahoo_cu v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new( $ntuser ); - my $root_key = $reg->get_root_key; - - my $path = 'Software\\Yahoo\\pager'; - my $key; - - if ( $key = $root_key->get_subkey( $path ) ) { - - ::rptMsg( "LastWrite Time ".gmtime($key->get_timestamp())." (UTC) ".$key->get_name() ); - - my %summary = ( 'Version' => '', - 'Launch on Startup' => '', - 'Connection Server' => '', - 'Last Login UserName' => '', - 'Last Local IP' => '', - 'AutoLogin' => '', - 'Save Password' => '', - 'Encrypted Password' => '', - 'Yahoo Token' => '' - ); - - my @vals = $key->get_list_of_values(); - if ( ( scalar @vals ) > 0 ) { - foreach my $val ( @vals ) { - _fillSummary( $val, \%summary ); - } - _printSummary( \%summary ); - } - else { - ::rptMsg( $key->get_name()." has no values." ); - ::logMsg( $key->get_name()." has no values." ); - } - - if ( $key = $key->get_subkey( 'profiles' ) ) { - ::rptMsg( "\n LastWrite Time ".gmtime($key->get_timestamp())." (UTC) ".$key->get_name() ); - my $tmp; - my $cu; - my $sbk; - my @badusers; - my @users; - my @subkeys = $key->get_list_of_subkeys(); - if ( ( scalar @subkeys ) > 0 ) { - # finding users and bad users (bad logins) - # 1- if subkey has no subkeys, is not a user - # 2- if subkey has 3 or less subkeys, probably it's a bad user - # 3- if subkey has >3 subkeys, probably it's a good user - foreach $sbk ( @subkeys ) { - my @subkeys2 = $sbk->get_list_of_subkeys(); - $tmp = scalar @subkeys2; - if ( $tmp > 0 && $tmp < 4 ) { - push( @badusers, $sbk ); - } - elsif ( $tmp >= 4 ) { - push( @users, $sbk ); - } - } - } - - # got users and badusers - ::rptMsg( " Found ".scalar @users." users." ); - ::rptMsg( " Found ".scalar @badusers." bad users logins." ); - ::rptMsg( "" ); - - # let's parse users - my $spaces = ' '; - if ( scalar @users ) { - foreach $cu ( @users ) { - ::rptMsg( $spaces."USER: ".$cu->get_name() ); - ::rptMsg( $spaces."LastWrite Time ".gmtime($cu->get_timestamp())." (UTC) ".$cu->get_name() ); - _parseUserValues( \$cu, $spaces ); - $spaces = ' '; - _parseAlerts( \$cu, $spaces ); - _parseArchives( \$cu, $spaces ); - _parseFriendIcons( \$cu, $spaces ); - _parseFT( \$cu, $spaces ); - } - } - - # let's parse badusers - ::rptMsg( "" ); - if ( scalar @badusers ) { - foreach $cu ( @badusers ) { - ::rptMsg( " BAD LOGIN USER: ".$cu->get_name() ); - ::rptMsg( " LastWrite Time ".gmtime($cu->get_timestamp())." (UTC) ".$cu->get_name() ); - if ( $sbk = $cu->get_subkey( 'Alerts' ) ) { - ::rptMsg( " LastWrite Time ".gmtime($sbk->get_timestamp())." (UTC) ".$sbk->get_name() ); - _printExpectedValue( \$sbk, 'Total Login Tries', ' ' ); - } - else { - ::rptMsg( " Missing expected 'Alerts' subkey" ); - } - ::rptMsg( "" ); - } - } - } - else { - ::rptMsg( "No profiles found." ); - ::logMsg( "No profiles found." ); - } - } - else { - ::rptMsg( $path." not found." ); - ::logMsg( $path." not found." ); - } -} - -#------------------------------------------------------------------------------ - -sub _parseUserValues() { - my @vals = ${$_[0]}->get_list_of_values(); - foreach my $v (@vals) { - my $val = $v->get_name(); - my $data = $v->get_data(); - if ( $val eq 'All Identities' ) { - ::rptMsg( $_[1].$val." = ".$data ); - } - elsif ( $val eq 'Selected Identities' ) { - ::rptMsg( $_[1].$val." = ".$data ); - } - elsif ( $val eq 'pref' ) { - ::rptMsg( $_[1].$val." = ".$data ); - } - elsif ( $val eq 'yinsider date' ) { - ::rptMsg( $_[1].$val." = ".gmtime($data)." (UTC)" ); - } - } -} - -#------------------------------------------------------------------------------ - -sub _parseAlerts() { - if ( my $local = ${$_[0]}->get_subkey( 'Alerts' ) ) { - ::rptMsg( $_[1]."LastWrite Time ".gmtime( $local->get_timestamp())." (UTC) ".$local->get_name() ); - _printExpectedValue( \$local, 'Total Login Tries', $_[1] ); - _printExpectedValue( \$local, 'Total Disconnects', $_[1] ); - } - else { - ::rptMsg( $_[1]."Missing expected 'Alerts' subkey." ); - } -} - -#------------------------------------------------------------------------------ - -sub _parseArchives() { - my $got1; - my $got2; - my $val1; - my $val2; - my $str; - if ( my $local = ${$_[0]}->get_subkey( 'Archive' ) ) { - ::rptMsg( $_[1]."LastWrite Time ".gmtime( $local->get_timestamp())." (UTC) ".$local->get_name() ); - # messages archive policies - ( $got1, $val1 ) = _printExpectedValue( \$local, 'Enabled', $_[1] ); - ( $got2, $val2 ) = _printExpectedValue( \$local, 'Autodelete', $_[1] ); - - if ( $got1 && $got2 ) { - if ( $val1 != 0 ) { - $str = "Messages archiving is ENABLED. " - } - else { - $str = "Messages archiving is NOT enabled. " - } - if ( $val2 != 0 ) { - $str .= "Archived messages are DELETED automatically on user sign-off."; - } - else { - $str .= "Archived messages are NOT automatically deleted on user sign-off."; - } - ::rptMsg( $_[1]."NOTE: ".$str ); - } - else { - ::rptMsg( $_[1]."NOTE: cannot determine archived messages policy due to missing values." ); - } - # voice call archive policies - ( $got1, $val1 ) = _printExpectedValue( \$local, 'CallHistoryEnabled', $_[1] ); - ( $got2, $val2 ) = _printExpectedValue( \$local, 'CallHistoryAutodelete', $_[1] ); - - if ( $got1 && $got2 ) { - if ( $val1 != 0 ) { - $str = "Call history archiving is ENABLED. " - } - else { - $str = "Call history archiving is NOT enabled. " - } - if ( $val2 != 0 ) { - $str .= "Call history is DELETED automatically on user sign-off."; - } - else { - $str .= "Call history is NOT automatically deleted on user sign-off."; - } - ::rptMsg( $_[1]."NOTE: ".$str ); - } - else { - ::rptMsg( $_[1]."NOTE: cannot determine call history policy due to missing values." ); - } - - } - else { - ::rptMsg( $_[1]."Missing expected 'Archive' subkey." ); - } -} - -#------------------------------------------------------------------------------ - -sub _parseFriendIcons() { - if ( my $local = ${$_[0]}->get_subkey( 'FriendIcons' ) ) { - ::rptMsg( $_[1]."LastWrite Time ".gmtime( $local->get_timestamp())." (UTC) ".$local->get_name() ); - _printExpectedValue( \$local, 'Checksum', $_[1] ); - _printExpectedValue( \$local, 'LastDir', $_[1] ); - _printExpectedValue( \$local, 'Path', $_[1] ); - } - else { - ::rptMsg( $_[1]."Missing expected 'FriendIcons' subkey." ); - } -} - -#------------------------------------------------------------------------------ - -sub _parseFT() { - if ( my $local = ${$_[0]}->get_subkey( 'FT' ) ) { - ::rptMsg( $_[1]."LastWrite Time ".gmtime( $local->get_timestamp())." (UTC) ".$local->get_name() ); - _printExpectedValue( \$local, 'LastSaveLocation', $_[1] ); - _printExpectedValue( \$local, 'LastSendLocation', $_[1] ); - } - else { - ::rptMsg( $_[1]."Missing expected 'FT' subkey." ); - } -} - -#------------------------------------------------------------------------------ - -sub _printExpectedValue() { - my $got; - my $val; - my $tmp; - if ( $tmp = ${$_[0]}->get_value( $_[1] ) ) { - $val = $tmp->get_data(); - ::rptMsg( $_[2].$_[1]." = ".$val ); - $got = 1; - } - else { - ::rptMsg( $_[2]."Missing expected value '".$_[1]."'" ); - $got = 0; - } - return ( $got, $val ); -} - -#------------------------------------------------------------------------------ - -sub _fillSummary() { - my $tmp = $_[0]->get_name(); - if ( $tmp eq 'Version' ) { ${$_[1]}{'Version'} = $_[0]->get_data(); } - elsif ( $tmp eq 'Launch on Startup' ) { ${$_[1]}{'Launch on Startup'} = $_[0]->get_data(); } - elsif ( $tmp eq 'ConnServer' ) { ${$_[1]}{'Connection Server'} = $_[0]->get_data(); } - elsif ( $tmp eq 'Yahoo! User ID' ) { ${$_[1]}{'Last Login UserName'} = $_[0]->get_data(); } - elsif ( $tmp eq 'CurrentUserLocalIP' ) { ${$_[1]}{'Last Local IP'} = $_[0]->get_data(); } - elsif ( $tmp eq 'Auto Login' ) { ${$_[1]}{'AutoLogin'} = $_[0]->get_data(); } - elsif ( $tmp eq 'Save Password' ) { ${$_[1]}{'Save Password'} = $_[0]->get_data(); } - elsif ( $tmp eq 'EOptions string' ) { ${$_[1]}{'Encrypted Password'} = $_[0]->get_data(); } - elsif ( $tmp eq 'ETS' ) { ${$_[1]}{'Yahoo Token'} = $_[0]->get_data(); } -} - -#------------------------------------------------------------------------------ - -sub _printSummary() { - ::rptMsg( ' Version = '.${$_[0]}{'Version'} ); - ::rptMsg( ' Launch on Startup = '.${$_[0]}{'Launch on Startup'} ); - ::rptMsg( ' Connection Server = '.${$_[0]}{'Connection Server'} ); - ::rptMsg( ' Last Login UserName = '.${$_[0]}{'Last Login UserName'} ); - ::rptMsg( ' Last Local IP = '.${$_[0]}{'Last Local IP'} ); - ::rptMsg( ' AutoLogin = '.${$_[0]}{'AutoLogin'} ); - ::rptMsg( ' Save Password = '.${$_[0]}{'Save Password'} ); - ::rptMsg( ' Encrypted Password = '.${$_[0]}{'Encrypted Password'} ); - ::rptMsg( ' Yahoo Token = '.${$_[0]}{'Yahoo Token'} ); - - if ( ${$_[0]}{'Encrypted Password'} ne '' ) { - ::rptMsg( " NOTE: detected encrypted password.\nYou should be able to decrypt the password." ); - } - elsif ( ${$_[0]}{'Yahoo Token'} ne '' ) { - ::rptMsg( " NOTE: detected Yahoo ETS Token. You should be able to impersonificate the user "); - ::rptMsg( " using the Yahoo Token but you cannot obtain the cleartext password." ); - } - else { - ::rptMsg( " NOTE: you should not be able to obtain the password." ); - } -} - -#------------------------------------------------------------------------------ - -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/yahoo_lm.pl b/thirdparty/rr-full/plugins/yahoo_lm.pl deleted file mode 100644 index 6496c838744..00000000000 --- a/thirdparty/rr-full/plugins/yahoo_lm.pl +++ /dev/null @@ -1,97 +0,0 @@ -#----------------------------------------------------------- -# yahoo_lm.pl -# Yahoo Messenger parser (HKLM) -# -# Change history -# 20101219 [fpi] % created -# 20101219 [fpi] % first version -# 20110830 [fpi] + banner, no change to the version number -# -# References -# -# copyright 2011 F. Picasso -#----------------------------------------------------------- -package yahoo_lm; -use strict; - -my %config = (hive => "SOFTWARE", - hasShortDescr => 1, - hasDescr => 0, - hasRefs => 1, - osmask => 22, - version => 20101219); - -sub getConfig{return %config} -sub getShortDescr { - return "Yahoo Messenger parser"; -} -sub getDescr{} -sub getRefs { - my %refs = ("Access Data Registry Quick Reference" => - "google it!"); - return %refs; -} -sub getHive {return $config{hive};} -sub getVersion {return $config{version};} - -my $VERSION = getVersion(); - -sub pluginmain { - my $class = shift; - my $ntuser = shift; - ::logMsg( "Launching yahoo_lm v.".$VERSION ); - ::rptMsg("yahoo_lm v.".$VERSION); # 20110830 [fpi] + banner - ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new( $ntuser ); - my $root_key = $reg->get_root_key; - - my $path = 'Yahoo'; - my $key; - - if ( $key = $root_key->get_subkey( $path ) ) { - ::rptMsg( "Searching for Yahoo Messenger installation..." ); - - my $found = 0; - my @subkeys = $key->get_list_of_subkeys(); - if ( ( scalar @subkeys ) > 0 ) { - - foreach my $sbk ( @subkeys ) { - my $tmp = $sbk->get_name(); - - if ( $tmp eq "pager" ) { - $found++; - ::rptMsg( "... 'pager' key is present." ); - ::rptMsg( "[".gmtime( $sbk->get_timestamp() )." (UTC)] ".$tmp ); - - my @vals = $sbk->get_list_of_values(); - - if ( ( scalar @vals ) > 0 ) { - foreach my $val ( @vals ) { - $tmp = $val->get_name(); - if ( $tmp eq "ProductVersion" ) { - $found++; - ::rptMsg( $tmp." -> ".$val->get_data() ); - } - } - if ( $found == 1 ) { - ::rptMsg( "unable to get 'ProductVersion' value." ); - } - } - } - } - if ( $found == 0 ) { - ::rptMsg( "No Yahoo Messenger installation detected." ); - } - } - else { - ::rptMsg( $key->get_name()." has no subkeys." ); - ::logMsg( $key->get_name()." has no subkeys." ); - } - } - else { - ::rptMsg( $path." not found." ); - ::logMsg( $path." not found." ); - } -} -1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/zerologon.pl b/thirdparty/rr-full/plugins/zerologon.pl new file mode 100644 index 00000000000..71179e61973 --- /dev/null +++ b/thirdparty/rr-full/plugins/zerologon.pl @@ -0,0 +1,84 @@ +#----------------------------------------------------------- +# zerologon.pl +# +# History: +# 20200922 - created +# +# References: +# https://twitter.com/h0tz3npl0tz/status/1308154057794744325 +# https://www.cynet.com/zerologon/ +# https://blog.zsec.uk/zerologon-attacking-defending/ +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#----------------------------------------------------------- +package zerologon; +use strict; + +my %config = (hive => "System", + category => "config", + hasShortDescr => 1, + hasDescr => 0, + hasRefs => 0, + MITRE => "", + output => "report", + version => 20200922); + +sub getConfig{return %config} +sub getShortDescr { + return "Check Registry setting to protect against ZeroLogon exploit"; +} +sub getDescr{} +sub getRefs {} +sub getHive {return $config{hive};} +sub getVersion {return $config{version};} + +my $VERSION = getVersion(); +my %files; +my $str = ""; + +sub pluginmain { + my $class = shift; + my $hive = shift; + ::logMsg("Launching zerologon v.".$VERSION); + ::rptMsg("zerologon v.".$VERSION); # banner + ::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + my $reg = Parse::Win32Registry->new($hive); + my $root_key = $reg->get_root_key; +# First thing to do is get the ControlSet00x marked current...this is +# going to be used over and over again in plugins that access the system +# file + my $key; + my $ccs = ::getCCS($root_key); + + my $key_path = $ccs."\\Services\\NetLogon\\Parameters"; + + if ($key = $root_key->get_subkey($key_path)) { + + ::rptMsg($key_path); + ::rptMsg("LastWrite time: ".::format8601Date($key->get_timestamp())."Z"); + ::rptMsg(""); + + eval { + my $f = $key->get_value("FullSecureChannelProtection")->get_data(); + ::rptMsg("FullSecureChannelProtection = ".$f); + ::rptMsg(""); + }; + if ($@) { + ::rptMsg("FullSecureChannelProtection value not found."); + ::rptMsg(""); + } + + ::rptMsg("Analysis Tip: The ".$key_path."\\FullSecureChannelProtection value needs to set to "); + ::rptMsg("\"1\" in order to fully protect a patched system (CVE-2020-1472)."); + ::rptMsg(""); + ::rptMsg("Ref: https://twitter.com/h0tz3npl0tz/status/1308154057794744325"); + ::rptMsg(" https://www.cynet.com/zerologon/"); + ::rptMsg(" https://blog.zsec.uk/zerologon-attacking-defending/"); + } + else { + ::rptMsg($key_path." not found."); + } +} + +1; \ No newline at end of file diff --git a/thirdparty/rr-full/q.ico b/thirdparty/rr-full/q.ico new file mode 100644 index 00000000000..8737cc16e32 Binary files /dev/null and b/thirdparty/rr-full/q.ico differ diff --git a/thirdparty/rr-full/registry.yar b/thirdparty/rr-full/registry.yar new file mode 100644 index 00000000000..a2b9595dc97 --- /dev/null +++ b/thirdparty/rr-full/registry.yar @@ -0,0 +1,43 @@ +import "pe" + +rule Encoding +{ + meta: + author : "H. Carvey" + date : "2023-08-14" + reference : "https://www.elastic.co/guide/en/security/current/encoded-executable-stored-in-the-registry.html" + + strings: + $str1 = "TVqQAAMAAAAEAAAA*" + + condition: + $str1 + +} + +rule Executable +{ + meta: + author : "H. Carvey" + date : "2023-08-14" + reference : "https://dmfrsecurity.com/2021/12/21/100-days-of-yara-day-2-identifying-pe-files-and-measuring-speed-of-rules/" + + strings: + $str1 = "MZ" + $str2 = { 4D 5A } + + condition: + ($str1 or $str2) at 0 or uint16(0) == 0x5a4d +} + + + + + + + + + + + + diff --git a/thirdparty/rr-full/regripper.pdf b/thirdparty/rr-full/regripper.pdf deleted file mode 100644 index 49c45abe779..00000000000 Binary files a/thirdparty/rr-full/regripper.pdf and /dev/null differ diff --git a/thirdparty/rr-full/rip.exe b/thirdparty/rr-full/rip.exe index 575299ad2ac..f3b2c07856e 100755 Binary files a/thirdparty/rr-full/rip.exe and b/thirdparty/rr-full/rip.exe differ diff --git a/thirdparty/rr-full/rip.pl b/thirdparty/rr-full/rip.pl index ef8815a86bd..c4c16a56734 100644 --- a/thirdparty/rr-full/rip.pl +++ b/thirdparty/rr-full/rip.pl @@ -8,6 +8,16 @@ # Usage: see "_syntax()" function # # Change History +# 20250429 - removed reference to defunct function +# 20230822 - minor tweak in plugin processing +# 20220714 - added JSON::PP based on input from Mark McKinnon +# 20210302 - added Digest::MD5 +# 20200824 - Unicode parsing updates +# 20200803 - updated to version 4.0 Pro +# 20200427 - added getDateFromEpoch(), output date format in RFC 3339 profile of ISO 8601 +# 20200331 - added "auto" capability...point rip at a hive, it determines the hive type and runs +# hive-specific plugins automatically, obviating the need for profiles +# 20200324 - multiple updates # 20190318 - modified code to allow the .exe to be run from anywhere within the file system # 20190128 - added Time::Local, modifications to module Key.pm # 20180406 - added "-uP" switch to update profiles @@ -21,15 +31,19 @@ # 20080419 - added '-g' switch (experimental) # 20080412 - added '-c' switch # -# copyright 2013-2019 Quantum Analytics Research, LLC +# copyright 2023 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com -# #------------------------------------------------------------------------- use strict; use Parse::Win32Registry qw(:REG_); use Getopt::Long; use Time::Local; use File::Spec; +use Encode::Unicode; +use Digest::MD5; +use JSON::PP; +require 'time.pl'; +require 'rr_helper.pl'; # Included to permit compiling via Perl2Exe #perl2exe_include "Parse/Win32Registry.pm"; @@ -48,7 +62,7 @@ my %config; Getopt::Long::Configure("prefix_pattern=(-|\/)"); -GetOptions(\%config,qw(reg|r=s file|f=s csv|c guess|g user|u=s sys|s=s plugin|p=s update|uP list|l help|?|h)); +GetOptions(\%config,qw(reg|r=s file|f=s csv|c dirty|d auto|a autoTLN|aT guess|g user|u=s sys|s=s plugin|p=s update|uP list|l help|?|h)); # Code updated 20090102 my @path; @@ -67,8 +81,7 @@ #my $plugindir = File::Spec->catfile("plugins"); #print "Plugins Dir = ".$plugindir."\n"; # End code update -my $VERSION = "2\.8_20190318"; -my @alerts = (); +my $VERSION = "4\.0"; if ($config{help} || !%config) { _syntax(); @@ -85,7 +98,7 @@ closedir(DIR); my $count = 1; - print "Plugin,Version,Hive,Description\n" if ($config{csv}); + print "Plugin,Version,Hive,MITRE ATT&CK,Category,Description\n" if ($config{csv}); foreach my $p (@plugins) { next unless ($p =~ m/\.pl$/); my $pkg = (split(/\./,$p,2))[0]; @@ -93,11 +106,17 @@ $p = File::Spec->catfile($plugindir,$p); eval { require $p; - my $hive = $pkg->getHive(); - my $version = $pkg->getVersion(); - my $descr = $pkg->getShortDescr(); + my %plugin = $pkg->getConfig(); + my $hive = $plugin{hive}; + $hive =~ s/\,/ /g; + my $version = $plugin{version}; + my $mitre = $plugin{MITRE}; + my $category = $plugin{category}; + my $descr = $pkg->getShortDescr(); + $descr =~ s/\,/;/g; + if ($config{csv}) { - print $pkg.",".$version.",".$hive.",".$descr."\n"; + print $pkg.",".$version.",".$hive.",".$mitre.",".$category.",".$descr."\n"; } else { print $count.". ".$pkg." v.".$version." [".$hive."]\n"; @@ -132,8 +151,8 @@ require $p; my $hive = $pkg->getHive(); my @hives = split(/,/,$hive); - foreach my $h (@hives) { - my $lch = lc($h); + foreach my $lch (@hives) { + $lch =~ tr/A-Z/a-z/; $lch =~ s/\.dat$//; $lch =~ s/^\s+//; @@ -159,6 +178,12 @@ } exit; } +#------------------------------------------------------------- +# +#------------------------------------------------------------- +if ($config{dirty}) { + checkHive($config{reg}); +} #------------------------------------------------------------- # @@ -169,7 +194,6 @@ my $hive = $config{reg}; die "You must enter a hive file path/name.\n" if ($hive eq ""); # die $hive." not found.\n" unless (-e $hive); - my %plugins = parsePluginsFile($config{file}); if (%plugins) { logMsg("Parsed Plugins file."); @@ -191,7 +215,6 @@ logMsg($plugins{$i}." complete."); rptMsg("-" x 40); } - printAlerts(); } #------------------------------------------------------------- @@ -208,7 +231,83 @@ my %guess = guessHive($hive); foreach my $g (keys %guess) { - ::rptMsg(sprintf "%-8s = %-2s",$g,$guess{$g}); +# ::rptMsg(sprintf "%-8s = %-2s",$g,$guess{$g}); + ::rptMsg($g) if ($guess{$g} == 1); + } +} + +#------------------------------------------------------------- +# +#------------------------------------------------------------- +if ($config{reg} && ($config{auto} || $config{autoTLN})) { +# Attempt to guess which kind of hive we have + my $hive = $config{reg}; + die "You must enter a hive file path/name.\n" if ($hive eq ""); +# die $hive." not found.\n" unless (-e $hive); + + my $reg; + my $root_key; + my %guess = guessHive($hive); + my $type = ""; + foreach my $g (keys %guess) { +# ::rptMsg(sprintf "%-8s = %-2s",$g,$guess{$g}); + $type = $g if ($guess{$g} == 1); + } + + my @plugins; + opendir(DIR,$plugindir) || die "Could not open $plugindir: $!\n"; + @plugins = readdir(DIR); + closedir(DIR); +# hash of lists to hold plugin names + my %files = (); + + foreach my $p (@plugins) { + next unless ($p =~ m/\.pl$/); +# $pkg = name of plugin + my $pkg = (split(/\./,$p,2))[0]; + + if ($config{auto}) { + next if ($pkg =~ m/tln$/ || $pkg =~ m/json$/ || $pkg =~ m/yara$/ || $pkg =~ m/csv$/); + } + elsif ($config{autoTLN}) { + next unless ($pkg =~ m/tln$/); + } + else {} + +# $p = $plugindir.$p; + $p = File::Spec->catfile($plugindir,$p); + eval { + require $p; + my $hive = $pkg->getHive(); + my @hives = split(/,/,$hive); + foreach my $lch (@hives) { + $lch =~ tr/A-Z/a-z/; + $lch =~ s/\.dat$//; + $lch =~ s/^\s+//; + $type =~ tr/A-Z/a-z/; + $files{$pkg} = 1 if ($lch eq $type); + } + }; + print "Error: $@\n" if ($@); + } + +# ::rptMsg("Plugins to run against ".$type." hive..."); +# foreach my $f (sort keys %files) { +# ::rptMsg(" ".$f); +# } + + foreach my $f (sort keys %files) { + eval { +# require "plugins/".$plugins{$i}."\.pl"; + my $plugin_file = File::Spec->catfile($plugindir,$f.".pl"); + require $plugin_file; + $f->pluginmain($hive); + }; + if ($@) { + logMsg("Error in ".$f.": ".$@); + } +# logMsg($plugins{$i}." complete."); + rptMsg("-" x 40) unless ($config{autoTLN}); } } @@ -221,7 +320,6 @@ my $hive = $config{reg}; die "You must enter a hive file path/name.\n" if ($hive eq ""); # die $hive." not found.\n" unless (-e $hive); - # check to see if the plugin exists my $plugin = $config{plugin}; # my $pluginfile = $plugindir.$config{plugin}."\.pl"; @@ -235,33 +333,44 @@ if ($@) { logMsg("Error in ".$pluginfile.": ".$@); } - printAlerts(); } +#------------------------------------------------------------- +# +#------------------------------------------------------------- sub _syntax { print<< "EOT"; Rip v.$VERSION - CLI RegRipper tool -Rip [-r Reg hive file] [-f plugin file] [-p plugin module] [-l] [-h] -Parse Windows Registry files, using either a single module, or a plugins file. +Rip [-r Reg hive file] [-f profile] [-p plugin] [options] +Parse Windows Registry files, using either a single module, or a profile. + +NOTE: This tool does NOT automatically process Registry transaction logs! The tool +does check to see if the hive is dirty, but does not automatically process the +transaction logs. If you need to incorporate transaction logs, please consider +using yarp + registryFlush.py, or rla.exe from Eric Zimmerman. - -r Reg hive file...Registry hive file to parse - -g ................Guess the hive file (experimental) - -f [profile].......use the plugin file (default: plugins\\plugins) - -p plugin module...use only this module + -r [hive] .........Registry hive file to parse + -d ................Check to see if the hive is dirty + -g ................Guess the hive file type + -a ................Automatically run hive-specific plugins + -aT ...............Automatically run hive-specific TLN plugins + -f [profile].......use the profile + -p [plugin]........use the plugin -l ................list all plugins - -c ................Output list in CSV format (use with -l) - -s system name.....Server name (TLN support) + -c ................Output plugin list in CSV format (use with -l) + -s systemname......system name (TLN support) -u username........User name (TLN support) - -uP ...............Update profiles + -uP ...............Update default profiles -h.................Help (print this information) Ex: C:\\>rip -r c:\\case\\system -f system C:\\>rip -r c:\\case\\ntuser.dat -p userassist + C:\\>rip -r c:\\case\\ntuser.dat -a C:\\>rip -l -c All output goes to STDOUT; use redirection (ie, > or >>) to output to a file\. -copyright 2019 Quantum Analytics Research, LLC +copyright 2025 Quantum Analytics Research, LLC EOT } @@ -287,24 +396,6 @@ sub rptMsg { } } -#------------------------------------------------------------- -# -#------------------------------------------------------------- -sub alertMsg { - push(@alerts,$_[0]); -} - -sub printAlerts { - if (scalar(@alerts) > 0) { -# print "\n"; -# print "Alerts\n"; -# print "-" x 40,"\n"; - foreach (@alerts) { - print $_."\n"; - } - } -} - #------------------------------------------------------------- # parsePluginsFile() # Parse the plugins file and get a list of plugins @@ -351,7 +442,17 @@ sub guessHive { $root_key = $reg->get_root_key; }; $guess{unknown} = 1 if ($@); - +#------------------------------------------------------------- +# updated 20200324 +# see if we can get the name from the hive file + my $embed = $reg->get_embedded_filename(); + my @n = split(/\\/,$embed); + my $r = $n[scalar(@n) - 1]; + $r =~ tr/A-Z/a-z/; + my $name = (split(/\./,$r,2))[0]; + $guess{$name} = 1; +#------------------------------------------------------------- + # Check for SAM eval { $guess{sam} = 1 if (my $key = $root_key->get_subkey("SAM\\Domains\\Account\\Users")); @@ -375,30 +476,40 @@ sub guessHive { }; # Check for NTUSER.DAT eval { - $guess{ntuser} = 1 if ($root_key->get_subkey("Software\\Microsoft\\Windows\\CurrentVersion")); + $guess{ntuser} = 1 if ($root_key->get_subkey("Software\\Microsoft\\Windows\\CurrentVersion")&& + $root_key->get_subkey("Software\\Microsoft\\Windows NT\\CurrentVersion")); }; + eval { + $guess{usrclass} = 1 if ($root_key->get_subkey("Local Settings\\Software") && + $root_key->get_subkey("lnkfile")); + }; + return %guess; } #------------------------------------------------------------- -# getTime() -# Translate FILETIME object (2 DWORDS) to Unix time, to be passed -# to gmtime() or localtime() +# checkHive() +# check to see if hive is "dirty" +# Added 20200220 #------------------------------------------------------------- -sub getTime($$) { - my $lo = shift; - my $hi = shift; - my $t; - - if ($lo == 0 && $hi == 0) { - $t = 0; - } else { - $lo -= 0xd53e8000; - $hi -= 0x019db1de; - $t = int($hi*429.4967296 + $lo/1e7); - }; - $t = 0 if ($t < 0); - return $t; -} \ No newline at end of file +sub checkHive { + my $hive = shift; + my $reg = Parse::Win32Registry->new($hive); + my $dirty; + ::rptMsg("***Hive Check***"); + if ($reg->is_dirty() == 1) { + ::rptMsg("The hive (".$hive.") is dirty."); + ::rptMsg(""); + ::rptMsg("Please consider processing hive transaction logs via either Maxim's yarp + registryFlush.py"); + ::rptMsg("or via Eric Zimmerman's rla.exe."); + } + elsif ($reg->is_dirty() == 0) { + ::rptMsg("Hive is not dirty."); + } + else { + ::rptMsg("Unknown if hive is dirty."); + } + ::rptMsg(""); +} diff --git a/thirdparty/rr-full/rip_bulk.zip b/thirdparty/rr-full/rip_bulk.zip deleted file mode 100644 index 689b38d9ec1..00000000000 Binary files a/thirdparty/rr-full/rip_bulk.zip and /dev/null differ diff --git a/thirdparty/rr-full/rr.exe b/thirdparty/rr-full/rr.exe index de4898478dc..d30974669f4 100755 Binary files a/thirdparty/rr-full/rr.exe and b/thirdparty/rr-full/rr.exe differ diff --git a/thirdparty/rr-full/rr.pl b/thirdparty/rr-full/rr.pl index fdd78c2fc60..6c9cec59dcf 100644 --- a/thirdparty/rr-full/rr.pl +++ b/thirdparty/rr-full/rr.pl @@ -8,6 +8,17 @@ # version # # Change History: +# 20230822 - minor tweak in plugin processing +# 20220714 - added JSON::PP based on input from Mark McKinnon +# 20210302 - added Digest::MD5 +# 20201026 - added SelectAll(), Clear() functions for Textfield; fixed issue with ID'ing UsrClass.dat hives +# 20200824 - Unicode parsing updates +# 20200803 - updated to version 4.0 Pro +# 20200511 - added code to provide date format in ISO 8601/RFC 3339 format +# 20200401 - Added code to check hive type, collect plugins, and automatically run those +# plugins against the hive +# 20200322 - multiple updates +# 20190318 - modified code to allow the .exe to be run from anywhere within the file system # 20190128 - added Time::Local, modifications to module Key.pm # 20130429 - minor updates, including not adding .txt files to Profile list # 20130425 - added alertMsg() functionality, updated to v2.8 @@ -35,14 +46,20 @@ # Functionality: # - plugins file is selectable # -# copyright 2013-2019 Quantum Research Analytics, LLC +# copyright 2022 Quantum Research Analytics, LLC # Author: H. Carvey, keydet89@yahoo.com -# #----------------------------------------------------------- #use strict; use Win32::GUI(); +#use Win32::GUI::Constants qw(CW_USEDEFAULT); use Time::Local; use Parse::Win32Registry qw(:REG_); +use File::Spec; +use Encode::Unicode; +use Digest::MD5; +use JSON::PP; +require 'time.pl'; +require 'rr_helper.pl'; # Included to permit compiling via Perl2Exe #perl2exe_include "Parse/Win32Registry.pm"; @@ -61,9 +78,11 @@ #----------------------------------------------------------- # Global variables #----------------------------------------------------------- -my $VERSION = "2\.8_20190128"; +my $VERSION = "4\.0"; my %env; -my @alerts = (); +my $plugindir; +($^O eq "MSWin32") ? ($plugindir = $str."plugins/") + : ($plugindir = File::Spec->catfile("plugins")); #----------------------------------------------------------- # GUI @@ -82,6 +101,7 @@ my $main = new Win32::GUI::Window ( -name => "Main", -title => "RegRipper, v.".$VERSION, + -left => CW_USEDEFAULT, -pos => [200, 200], # Format: [width, height] -maxsize => [500, 420], @@ -144,36 +164,19 @@ -tabstop => 1, -text => "Browse"); -$main->AddLabel( - -text => "Profile:", - -left => 20, - -top => 90); - -# http://perl-win32-gui.sourceforge.net/cgi-bin/docs.cgi?doc=combobox -my $combo = $main->AddCombobox( - -name => "Combobox", -# -dropdown => 1, - -dropdownlist => 1, - -top => 90, - -left => 100, - -width => 120, - -height => 110, - -tabstop=> 1, - ); - my $testlabel = $main->AddLabel( -text => "", -name => "TestLabel", - -pos => [10,140], - -size => [445,160], + -pos => [10,90], + -size => [445,210], -frame => etched, -sunken => 1 ); my $report = $main->AddTextfield( -name => "Report", - -pos => [20,150], - -size => [425,140], + -pos => [20,100], + -size => [425,190], -multiline => 1, -vscroll => 1, -autohscroll => 1, @@ -189,7 +192,7 @@ -width => 50, -height => 25, -tabstop => 1, - -text => "Rip It"); + -text => "Rip!"); $main->AddButton( -name => 'close', @@ -204,9 +207,17 @@ -text => "RegRipper v.".$VERSION." opened\.", ); -populatePluginsList(); -$combo->Text(""); -$status->Text("Profile List Populated."); +$status->Text("Ready."); + +#----------------------------------------------------------- +# Added 20200322 +$report->Append("NOTE: This tool does NOT automatically process and incorporate Registry hive\r\n"); +$report->Append("transaction logs. The tool will check to see if the hive is dirty.\r\n"); +$report->Append("\r\n"); +$report->Append("If you need to process/incorporate transaction logs, please consider using\r\n"); +$report->Append("yarp + registryFlush.py (Maxim Suhanov) or rla.exe (Eric Zimmerman).\r\n"); +$report->Append("\r\n"); +#----------------------------------------------------------- $main->Show(); Win32::GUI::Dialog(); @@ -251,52 +262,102 @@ sub go_Click { "Doh!!",16); return; } -# Get the selected item from the Plugins file listbox -# only allows for single selections at this time; defaults to ntuser -# if none selected - my $pluginfile = $combo->GetLBText($combo->GetCurSel()); - $pluginfile = "ntuser" if ($pluginfile eq ""); + +# added 20201026 + $report->SelectAll(); + $report->Clear(); + +# Guess the hive type, then run through all of the available plugins to get a list +# to run against that hive. +#---------------------------------------------------------------------------------------- +# added 20200322 + my $dirty = checkHive($env{ntuser}); + if ($dirty == 1) { + $status->Text("Hive is dirty."); + $report->Append("Hive is dirty. If you need to process hive transaction logs, please consider\r\n"); + $report->Append("doing so via yarp + registryFlush.py (Maxim Suhanov) or rla.exe (Eric Zimmerman).\r\n"); + logMsg("Hive (".$env{ntuser}.") is dirty.\n"); + rptMsg("Hive (".$env{ntuser}.") is dirty."); + rptMsg("If you need to process hive transasction logs, please consider using yarp + registryFlush.py"); + rptMsg("(Maxim Suhanov) or rla.exe (Eric Zimmerman).\n"); + } + elsif ($dirty == 0) { + $status->Text("Hive is not dirty."); + $report->Append("Hive is not dirty.\r\n"); + logMsg("Hive (".$env{ntuser}.") is not dirty.\n"); + rptMsg("Hive (".$env{ntuser}.") is not dirty.\n"); + } + else {} +#---------------------------------------------------------------------------------------- + $report->Append("Logging to ".$env{logfile}."\r\n"); - $report->Append("Using plugins file ".$pluginfile."\r\n"); + logMsg("Log opened."); logMsg("File: ".$env{ntuser}); logMsg("Environment set up."); - my %plugins = parsePluginsFile($pluginfile); - logMsg("Parsed Plugins file ".$pluginfile); - if (scalar(keys %plugins) == 0) { - Win32::GUI::MessageBox($main,$ENV{USERNAME}.", the plugins file has no plugins!!.\r\n", - "Doh!!",16); - return; + +#---------------------------------------------------------------------------------------- +# determine the type of hive file + + my %guess = guessHive($env{ntuser}); + my $type = ""; + foreach my $g (keys %guess) { +# ::rptMsg(sprintf "%-8s = %-2s",$g,$guess{$g}); + $type = $g if ($guess{$g} == 1); + } + $report->Append("Hive type: ".$type."\r\n"); +#---------------------------------------------------------------------------------------- +# get a list of plugins based on the hive type + $report->Append("Getting list of plugins based on hive type...\r\n"); + my @plugins; + opendir(DIR,$plugindir) || die "Could not open $plugindir: $!\n"; + @plugins = readdir(DIR); + closedir(DIR); +# hash of lists to hold plugin names + my %files = (); + + foreach my $p (@plugins) { + next unless ($p =~ m/\.pl$/); +# $pkg = name of plugin + my $pkg = (split(/\./,$p,2))[0]; +# skip over plugins that end in _tln, _json, or _yara + next if ($pkg =~ m/tln$/ || $pkg =~ m/json$/ || $pkg =~ m/yara$/ || $pkg =~ /csv$/); +# $p = $plugindir.$p; + $p = File::Spec->catfile($plugindir,$p); + eval { + require $p; + my $hive = $pkg->getHive(); + my @hives = split(/,/,$hive); + foreach my $lch (@hives) { + $lch =~ tr/A-Z/a-z/; + $lch =~ s/\.dat$//; + $lch =~ s/^\s+//; + $type =~ tr/A-Z/a-z/; + $files{$pkg} = 1 if ($lch eq $type); + } + }; + print "Error: $@\n" if ($@); } + $report->Append("...Done.\r\n"); + $report->Append("Start ripping...\r\n"); my $err_cnt = 0; - foreach my $i (sort {$a <=> $b} keys %plugins) { + foreach my $f (sort keys %files) { eval { - require "plugins\\".$plugins{$i}."\.pl"; - $plugins{$i}->pluginmain($env{ntuser}); +# require "plugins/".$plugins{$i}."\.pl"; + my $plugin_file = File::Spec->catfile($plugindir,$f.".pl"); + require $plugin_file; + $f->pluginmain($env{ntuser}); }; if ($@) { $err_cnt++; - logMsg("Error in ".$plugins{$i}.": ".$@); + logMsg("Error in ".$f.": ".$@); } - - $report->Append($plugins{$i}."...Done.\r\n"); - $status->Text($plugins{$i}." completed."); - - Win32::GUI::DoEvents(); - logMsg($err_cnt." plugins completed with errors."); - logMsg($plugins{$i}." complete."); + $report->Append($f."...Done.\r\n"); + $status->Text($f." complete."); rptMsg("-" x 40); + Win32::GUI::DoEvents(); } -# add output of alerts to the report file here - if (scalar(@alerts) > 0) { -# rptMsg(""); -# rptMsg("Alerts"); -# rptMsg("-" x 40); - foreach my $a (@alerts) { - rptMsg($a); - } - } - + $report->Append($err_cnt." plugins completed with errors.\r\n"); $status->Text("Done."); } @@ -306,10 +367,6 @@ sub close_Click { exit -1; } -sub Combobox_CloseUp { - $status->Text("Profile = ".$combo->GetLBText($combo->GetCurSel())); -} - # About box sub RR_OnAbout { my $self = shift; @@ -318,7 +375,7 @@ sub RR_OnAbout { "Parses Registry hive (NTUSER\.DAT, System, etc.) files, placing pertinent info in a report ". "file in a readable manner.\r\n". "\r\n". - "Copyright 2013 Quantum Analytics Research, LLC.\r\n". + "Copyright 2023 Quantum Analytics Research, LLC.\r\n". "H\. Carvey, keydet89\@yahoo\.com", "About...", MB_ICONINFORMATION | MB_OK, @@ -343,76 +400,8 @@ sub setUpEnv { # Assemble path to log file $f[scalar(@f) - 1] = "log"; $path[$last] = join('.',@f); - print join('\\',@path)."\n"; - $env{logfile} = join('\\',@path); - -# Use the above code to set up the path to the Timeline -# (.tln) file -# Assemble path to log file -# $f[scalar(@f) - 1] = "tln"; -# $path[$last] = join('.',@f); # print join('\\',@path)."\n"; -# $env{tlnfile} = join('\\',@path); - -} - -#----------------------------------------------------------- -# get a list of plugins files from the plugins dir -#----------------------------------------------------------- -sub getProfiles { - my @pluginfiles; - opendir(DIR,"plugins"); - my @files = readdir(DIR); - close(DIR); - - foreach my $f (@files) { - next if ($f =~ m/^\.$/ || $f =~ m/^\.\.$/); - next if ($f =~ m/\.pl$/ || $f =~ m/\.txt$/); - push(@pluginfiles,$f); - } - return @pluginfiles; -} - -#----------------------------------------------------------- -# populate the list of plugins files -#----------------------------------------------------------- -sub populatePluginsList { - my @files = getProfiles(); - foreach my $f (@files) { - $combo->InsertItem($f); - } -} - -#----------------------------------------------------------- -# -#----------------------------------------------------------- -sub parsePluginsFile { - my $file = $_[0]; - my %plugins; -# Parse a file containing a list of plugins -# Future versions of this tool may allow for the analyst to -# choose different plugins files - my $pluginfile = "plugins\\".$file; - if (-e $pluginfile) { - open(FH,"<",$pluginfile); - my $count = 1; - while() { - chomp; - next if ($_ =~ m/^#/ || $_ =~ m/^\s+$/); -# next unless ($_ =~ m/\.pl$/); - next if ($_ eq ""); - $_ =~ s/^\s+//; - $_ =~ s/\s+$//; - $plugins{$count++} = $_; - } - close(FH); - $status->Text("Plugin file parsed and loaded."); - return %plugins; - } - else { - $report->Append($pluginfile." not found.\r\n"); - return undef; - } + $env{logfile} = join('\\',@path); } sub logMsg { @@ -428,27 +417,73 @@ sub rptMsg { close(FH); } -sub alertMsg { - push(@alerts,$_[0]); +#------------------------------------------------------------- +# guessHive() +# updated 20200322 +#------------------------------------------------------------- +sub guessHive { + my $hive = shift; + my $reg; + my $root_key; + my %guess; + eval { + $reg = Parse::Win32Registry->new($hive); + $root_key = $reg->get_root_key; + }; + $guess{unknown} = 1 if ($@); +#------------------------------------------------------------- +# updated 20200322 +# see if we can get the name from the hive file + my $embed = $reg->get_embedded_filename(); + my @n = split(/\\/,$embed); + my $r = $n[scalar(@n) - 1]; + $r =~ tr/A-Z/a-z/; + my $name = (split(/\./,$r,2))[0]; + $guess{$name} = 1; +#------------------------------------------------------------- + +# Check for SAM + eval { + $guess{sam} = 1 if (my $key = $root_key->get_subkey("SAM\\Domains\\Account\\Users")); + }; +# Check for Software + eval { + $guess{software} = 1 if ($root_key->get_subkey("Microsoft\\Windows\\CurrentVersion") && + $root_key->get_subkey("Microsoft\\Windows NT\\CurrentVersion")); + }; + +# Check for System + eval { + $guess{system} = 1 if ($root_key->get_subkey("MountedDevices") && + $root_key->get_subkey("Select")); + }; + +# Check for Security + eval { + $guess{security} = 1 if ($root_key->get_subkey("Policy\\Accounts") && + $root_key->get_subkey("Policy\\PolAdtEv")); + }; +# Check for NTUSER.DAT + eval { + $guess{ntuser} = 1 if ($root_key->get_subkey("Software\\Microsoft\\Windows\\CurrentVersion")&& + $root_key->get_subkey("Software\\Microsoft\\Windows NT\\CurrentVersion")); + }; + + eval { + $guess{usrclass} = 1 if ($root_key->get_subkey("Local Settings\\Software") && + $root_key->get_subkey("lnkfile")); + }; + + return %guess; } #------------------------------------------------------------- -# getTime() -# Translate FILETIME object (2 DWORDS) to Unix time, to be passed -# to gmtime() or localtime() +# checkHive() +# check to see if hive is "dirty" +# Added 20200322 #------------------------------------------------------------- -sub getTime($$) { - my $lo = shift; - my $hi = shift; - my $t; - - if ($lo == 0 && $hi == 0) { - $t = 0; - } else { - $lo -= 0xd53e8000; - $hi -= 0x019db1de; - $t = int($hi*429.4967296 + $lo/1e7); - }; - $t = 0 if ($t < 0); - return $t; -} \ No newline at end of file +sub checkHive { + my $hive = shift; + my $reg = Parse::Win32Registry->new($hive); + return $reg->is_dirty(); +} diff --git a/thirdparty/rr/rr_helper.pl b/thirdparty/rr/rr_helper.pl new file mode 100644 index 00000000000..10bd7e54893 --- /dev/null +++ b/thirdparty/rr/rr_helper.pl @@ -0,0 +1,133 @@ +#------------------------------------------------------------- +# rr_helper.pl +# This file contains helper functions for RegRipper +# +# Note: The main UI code (GUI or CLI) must 'use' the Time::Local +# module. +# +# Change history: +# 20200730 - created +# +# copyright 2020 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#------------------------------------------------------------- + +#------------------------------------------------------------- +# getCCS() +# +# Given a key object for the System hive, return the ControlSet +# marked "Current"; pass $root_key to function +# +# my $root_key = $reg->get_root_key; +#------------------------------------------------------------- +sub getCCS { + my $root_key = shift; + my $current; + my $ccs; + my $key_path = 'Select'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + $current = $key->get_value("Current")->get_data(); + $ccs = "ControlSet".sprintf "%03d",$current; + return $ccs; + } + else { +# ::rptMsg($key_path." not found."); + return undef; + } +} + +#----------------------------------------------------------- +# probe() +# +# Code the uses printData() to insert a 'probe' into a specific +# location and display the data +# +# Input: binary data of arbitrary length +# Output: Nothing, no return value. Displays data to the console +#----------------------------------------------------------- +sub probe { + my $data = shift; + my @d = printData($data); + + foreach (0..(scalar(@d) - 1)) { + print $d[$_]."\n"; + } +} + +#----------------------------------------------------------- +# printData() +# subroutine used primarily for debugging; takes an arbitrary +# length of binary data, prints it out in hex editor-style +# format for easy debugging +#----------------------------------------------------------- +sub printData { + my $data = shift; + my $len = length($data); + + my @display = (); + + my $loop = $len/16; + $loop++ if ($len%16); + + foreach my $cnt (0..($loop - 1)) { +# How much is left? + my $left = $len - ($cnt * 16); + + my $n; + ($left < 16) ? ($n = $left) : ($n = 16); + + my $seg = substr($data,$cnt * 16,$n); + my $lhs = ""; + my $rhs = ""; + foreach my $i ($seg =~ m/./gs) { +# This loop is to process each character at a time. + $lhs .= sprintf(" %02X",ord($i)); + if ($i =~ m/[ -~]/) { + $rhs .= $i; + } + else { + $rhs .= "."; + } + } + $display[$cnt] = sprintf("0x%08X %-50s %s",$cnt,$lhs,$rhs); + + } + return @display; +} + +#------------------------------------------------------------- +# getUnicodeStr() +# +#------------------------------------------------------------- +sub getUnicodeStr { + my $data = shift; + Encode::from_to($data,'UTF-16LE','utf8'); + $data = Encode::decode_utf8($data); + return $data; +} + +#----------------------------------------------------------- +# parseGUID() +# Takes 16 bytes of binary data, returns a string formatted +# as an MS GUID. +#----------------------------------------------------------- +sub parseGUID { + my $data = shift; + my $d1 = unpack("V",substr($data,0,4)); + my $d2 = unpack("v",substr($data,4,2)); + my $d3 = unpack("v",substr($data,6,2)); + my $d4 = unpack("H*",substr($data,8,2)); + my $d5 = unpack("H*",substr($data,10,6)); + my $guid = sprintf "{%08x-%04x-%04x-$d4-$d5}",$d1,$d2,$d3; + + return $guid; + +} + +#------------------------------------------------------------- +# function() +# +#------------------------------------------------------------- + +1; \ No newline at end of file diff --git a/thirdparty/rr/test.yar b/thirdparty/rr/test.yar new file mode 100644 index 00000000000..747fe338452 --- /dev/null +++ b/thirdparty/rr/test.yar @@ -0,0 +1,34 @@ +rule Test1 +{ + strings: + $defend_1116 = "Microsoft-Windows-Windows Defender/1116" nocase + $defend_1117 = "Microsoft-Windows-Windows Defender/1117" nocase + + condition: + $defend_1116 or $defend_1117 +} + +rule Test2 +{ + strings: + $str = "NUMBER" nocase + + condition: + $str +} + +rule Test3 +{ + meta: + description = "boink" + author = "Yo Mama" + + strings: + $str1 = "onedrive" nocase + $str2 = "vmware" nocase + + condition: + $str1 or $str2 +} + + diff --git a/thirdparty/rr/time.pl b/thirdparty/rr/time.pl new file mode 100644 index 00000000000..6dbd1adbe4b --- /dev/null +++ b/thirdparty/rr/time.pl @@ -0,0 +1,123 @@ +#------------------------------------------------------------- +# time.pl +# This file contains helper functions for translating time values +# into something readable. This file is accessed by the main UI +# code via the 'require' pragma. +# +# Note: The main UI code (GUI or CLI) must 'use' the Time::Local +# module. +# +# Change history: +# 20220523 - added references +# 20200728 - minor updates +# 20120925 - created +# +# copyright 2022 Quantum Analytics Research, LLC +# Author: H. Carvey, keydet89@yahoo.com +#------------------------------------------------------------- + +#------------------------------------------------------------- +# References +# About Time: https://docs.microsoft.com/en-us/windows/win32/sysinfo/about-time +# Shell Items: https://github.com/libyal/libfwsi/blob/main/documentation/Windows%20Shell%20Item%20format.asciidoc +# FAT time stamp resolution: https://stackoverflow.com/questions/31524478/fat-date-resolution-timestamps-on-windows +# FAT date/time values: https://forensicswiki.xyz/page/FAT#FAT_date_and_time_values +# +# +#------------------------------------------------------------- + +#------------------------------------------------------------- +# getTime() +# Translate FILETIME object (2 DWORDS) to Unix time, to be passed +# to gmtime() or localtime() +# +# The code was borrowed from Andreas Schuster's excellent work +#------------------------------------------------------------- +sub getTime($$) { + my $lo = $_[0]; + my $hi = $_[1]; + my $t; + + if ($lo == 0 && $hi == 0) { + $t = 0; + } else { + $lo -= 0xd53e8000; + $hi -= 0x019db1de; + $t = int($hi*429.4967296 + $lo/1e7); + }; + $t = 0 if ($t < 0); + return $t; +} + +#----------------------------------------------------------- +# convertDOSDate() +# subroutine to convert 4 bytes of binary data into a human- +# readable format. Returns both a string and a Unix-epoch +# time. +#----------------------------------------------------------- +sub convertDOSDate { + my $date = shift; + my $time = shift; + + if ($date == 0x00 || $time == 0x00){ + return (0,0); + } + else { + my $sec = ($time & 0x1f) * 2; + $sec = "0".$sec if (length($sec) == 1); + if ($sec == 60) {$sec = 59}; + my $min = ($time & 0x7e0) >> 5; + $min = "0".$min if (length($min) == 1); + my $hr = ($time & 0xF800) >> 11; + $hr = "0".$hr if (length($hr) == 1); + my $day = ($date & 0x1f); + $day = "0".$day if (length($day) == 1); + my $mon = ($date & 0x1e0) >> 5; + $mon = "0".$mon if (length($mon) == 1); + my $yr = (($date & 0xfe00) >> 9) + 1980; + my $gmtime = timegm($sec,$min,$hr,$day,($mon - 1),$yr); + return ("$yr-$mon-$day $hr:$min:$sec",$gmtime); +# return gmtime(timegm($sec,$min,$hr,$day,($mon - 1),$yr)); + } +} + +#----------------------------------------------------------- +# convertSystemTime() +# Converts 128-bit SYSTEMTIME object to readable format +#----------------------------------------------------------- +sub convertSystemTime { + my $date = $_[0]; + my @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul", + "Aug","Sep","Oct","Nov","Dec"); + my @days = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat"); + my ($yr,$mon,$dow,$dom,$hr,$min,$sec,$ms) = unpack("v*",$date); + $hr = "0".$hr if ($hr < 10); + $min = "0".$min if ($min < 10); + $sec = "0".$sec if ($sec < 10); + my $str = sprintf("%04d-%02d-%02d %02d:%02d:%02d",$yr,$mon,$dom,$hr,$min,$sec); + return $str; +} + +#----------------------------------------------------------- +# getFileTimeStr() +# Converts FILETIME string (i.e., "01D3C4A7328ED3C0") to *nix epoch +# time +#----------------------------------------------------------- +sub getFileTimeStr { + my $str = shift; + my @ints = split(//,$str); + return getTime(hex(join('',@ints[8..15])),hex(join('',@ints[0..7]))); +} + +#----------------------------------------------------------- +# format8601Date() +# Convert Unix epoch time to ISO8601-like format +# output date format in RFC 3339 profile of ISO 8601 +#----------------------------------------------------------- +sub format8601Date { + my $epoch = shift; + my ($sec,$min,$hour,$mday,$mon,$year) = gmtime($epoch); + return sprintf("%04d-%02d-%02d %02d:%02d:%02d",(1900 + $year),($mon + 1),$mday,$hour,$min,$sec); +} + +1; \ No newline at end of file