Sunday, 5 February 2012

Writing a CCleaner RegRipper Plugin Part 2


Welcome Back Viewers!
We now continue with our scheduled programming ... heh-heh...

About RegRipper (on SIFT V2.12)

RegRipper is written in Perl and is included with the SIFT VM. There are 3 main components to it:
  • the rip.pl Perl script located in /usr/local/bin
  • the various plugin scripts located in /usr/local/src/regripper/plugins
  • the Parse::Win32Registry library which is already installed/run (from source code at /usr/local/share/perl/5.10.0/Parse/Win32Registry)
Harlan Carvey has written a "how to write a plugin" section in Windows Registry Forensics (pp.73-78).

For more information on the methods/functions available for the Parse::Win32Registry, you can type:
"man parse::win32registry" at a SIFT VM terminal window.

RegRipper can be called at the SIFT command line, using something like:
"rip.pl -r /cases/NTUSER.DAT -p ccleaner"

*assuming "ccleaner.pl" exists in the plugin directory

Typing "rip -h" gives the full syntax/explanation.

Most plugins follow a similar pattern:
  1. Create a new Parse::Win32Registry object (passing the hive name as a parameter eg "NTUSER.DAT")
  2. Call the Parse::Win32Registry's "get_root_key" method
  3. Use the root key to call the "get_subkey" method (passing the key name as a parameter eg "Software\Piriform\CCleaner")
  4. Use "get_list_of_values" method to obtain the values under the subkey.
Some Perls of Wisdom?

A few Perls of note before we get stuck into coding:
  • "#" denotes comments. Anything after a # sign on the line is ignored by the Perl interpreter.
  • Each statement line should be terminated with a ";". A statement can be a declaration / assignment / function call.
  • "{ }" can be used to logically group multiple lines.
  • "sub" denotes subroutines (aka methods/functions).
  • A method / function can be passed a parameter using "( )" eg "function(x)" or it can be called without and use a default input argument eg in subroutines, "shift" defaults to use "@_".
  • "my" denotes local variables (they do not exist outside the containing "{ }" braces).
  • "$" denote scalar values (can be strings, numbers) and its the most basic / common variable Perl type.
  • "@" denotes an array / list of scalars.
  • "( )" can also be used to denote arrays/lists (see examples below).
  • "[i]" can be used to index items in arrays/lists (see examples below).
  • "%" denotes an associated array / hashmap (where each "value" in a list has a corresponding "key id").
  • "::rptMsg" is an existing RegRipper function to print out messages to the command line.
  • "." can be used to join strings and variables together.

Some array examples:
@x = (10, 20, 30); # sets up an array of numbers and stores it in @x
$y = $x[1]; # sets scalar y to equal the second scalar item in x (array index starts at 0). $x[1] is used instead of @x because we are assigning / using a scalar value contained in x. ie y = 20

%fruits("apple" => 9,
        "banana" => 23,
        "cherry" =>11); # sets up an associated array (%) with "apple" being the "key id" and 9 being the "key value". Similarly, "banana" has a value of 23 etc.

$fruits{"orange"} = 1; # will add the variable "orange" and its value of "1" to the "fruits" associated array

delete($fruits{"orange"}); # will remove "orange" and its value from the "fruits" array

@fruit_keys = keys(%fruits); # copies the key ids to the "fruit_keys" array using the perl "keys" function. "keys" returns an array list so that's why we use "@fruit_keys" instead of $fruit_keys or %fruitkeys.

@fruit_values = values(%fruits); # copies the key values to the "fruit_values" array using the perl "values" function

Some helpful Perl references are:
  • the official documentation here
  • good old wikipedia
  • and Chapter 16, Unix Unleashed 3ed. by Robin Burk (SAMS 1998) - which has a good summary of Perl. That's where I got the array examples from.
Writing the RegRipper Plugin

So after all that, I can tell you're just rearing to go ... and for those of you that stay - here's the code I came up with. Its based on the "warcraft3" plugin already included with SIFT V2.12. Note: the formatting might be a little off because of the blog column width causing long lines (typically comments) to wrap.

# Start of Code

 #-----------------------------------------------------------
# ccleaner.pl
#   Gets CCleaner User Settings
#
# Change history
#   20120128 Initial Version based on warcraft3.pl plugin
#
# References
#
#
#-----------------------------------------------------------
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;


# End of Code

I have also included some screenshots of the code with line numbers so we can walk through it. Fun, fun, fun eh?
Code Screenshot #1

I used the SIFT's Ubuntu gedit file editor (by typing "gedit" at the command line) which has automatic Perl syntax highlighting, line numbers and multiple tabs. You can see the file path "/usr/local/src/regripper/plugins/" of the current file being edited ("ccleaner.pl") in the title bar.
Comments are in blue, variable names are green, Perl keywords are brownish-orange, Perl functions are aqua. You can probably customize it further.
As mentioned previously, I followed the same structure as the warcraft3.pl plugin so the first few lines are kinda standard / common to all plugins.

Lines 1 - 11 are just comments
Line 12 declares the name of this package/module.
Line 13 is a directive to ensure we follow the Perl syntax rules
Lines 15-20 declares a "config" associative array e.g. "hive" key having a value of "NTUSER\.DAT" etc.
The "\" is required so Perl doesn't interpret the "." as something else.
These are standard RegRipper variables that can later be accessed using the subroutines declared on lines 22 - 31.


Code Screenshot #2

Every RegRipper plugin must declare a "pluginmain" subroutine. This particular one will be called whenever you specify the "-p ccleaner" option.
Lines 34-35: We see two local (ie "my") variables declared. These variables don't exist outside of our "pluginmain". The "pluginmain" is called in / by "rip.pl" which also passes in the hive name parameter (eg NTUSER.DAT).
The parameter is available via the inbuilt Perl array "@_". To get to the hive name we need to pop/shift the first item off the "@_" array (ie the "ccleaner" package name). I'm not really sure why the package name was added to the "@_" array when "rip.pl" only passes in the hive name. Anyway, we call "shift" again to get our hive value (NTUSER.DAT).
Lines 36-38: Prints out "Launching ccleaner" and the version info. This seems to be the standard thing to do.
Lines 39-40: Creates a new "Parse::Win32Registry" object using the hive we specified at the command line (via "-r /cases/NTUSER.DAT"). Then we get a root key object (storing it in "root_key") so we can then use it later to get to our CCleaner key.
Lines 41-42: We declare a local variable "key" (to be used later) and also declare a "key_path" scalar containing the actual Software registry key we are interested in (ie Software\\Piriform\\CCleaner).
Line 44: If our "root_key" get_subkey method returns a result (stored in "key") we can then parse through / print the values otherwise execution skips to line 73 and we print out "Software\Piriform\CCleaner does not exist"
Lines 46-48: Prints out the "key_path" ("Software\Piriform\CCleaner") and the last write time.
Lines 49 and 54: We create a new local associative array called "cckeys" and a new local array called "ccvals". We then use the returned "key" variable to call "get_list_of_values" and store the result in "ccvals". "ccvals" should now contain multiple variables of the "Parse:Win32Registry::WinNT::Value" type. We can't access the registry key names / values directly - we have to use the library supplied "get_name" and "get_data" subroutines.
Lines 58-70: We check to see if we have any "Value" items in the "ccvals" array and if we don't, execution skips to line 69 and we print out "Software\Piriform\CCleaner has no values". If we do have items in the "ccvals" array, we iterate through each item and store the name and value (eg "UpdateCheck" and the value "1") in the "cckeys" associative array using the name as the index / id. Then on lines 64-65, we get a list of these key ids (eg "UpdateCheck") and sort the list using the inbuilt Perl "sort" function. We iterate through this sorted list for each key id and we print out the key id (eg "UpdateCheck") and then a " -> " followed by the key value (eg "1"). Execution then goes to line 76 where we print out an empty line and the package returns a value of "1" by convention.

We can now copy / save this script into the plugin directory, type "chmod a+x /usr/local/src/regripper/plugins/ccleaner.pl" to make it executable and then launch RegRipper with our new ccleaner plugin using:
"rip.pl -p ccleaner -r /cases/NTUSER.DAT". *Assuming the relevant NTUSER.DAT is in /cases/.

Here is the output of "ccleaner.pl" when run against a NTUSER.DAT containing CCleaner keys:

Launching ccleaner v.20120128
ccleaner v.20120128
(NTUSER.DAT) Gets User's CCleaner Settings


Software\Piriform\CCleaner
LastWrite Time Fri Jan  6 05:52:48 2012 (UTC)


(App)Autocomplete Form History -> True
(App)Custom Folders -> False
(App)DNS Cache -> True
(App)Desktop Shortcuts -> False
(App)FTP Accounts -> True
(App)Google Chrome - Saved Form Information -> True
(App)Google Chrome - Saved Passwords -> True
(App)Hotfix Uninstallers -> False
(App)IIS Log Files -> False
(App)Last Download Location -> True
(App)MS Photo Editor -> True
(App)Mozilla - Saved Form Information -> True
(App)Mozilla - Saved Passwords -> True
(App)Mozilla - Site Preferences -> True
(App)Notepad++ -> False
(App)Old Prefetch data -> True
(App)Saved Passwords -> True
(App)Start Menu Shortcuts -> False
(App)Sun Java -> True
(App)Tray Notifications Cache -> False
(App)Wipe Free Space -> False
BackupPrompt -> 0
DefaultDetailedView -> 1
DelayTemp -> 1
FFDetailed -> 1
IEDetailed -> 1
Language -> 1033
MSG_CONFIRMCLEAN -> False
MSG_WARNMOZCACHE -> False
SecureDeleteMethod -> 0
SecureDeleteType -> 1
UpdateCheck -> 1
UpdateKey -> 01/06/2012 04:52:48 PM
WINDOW_HEIGHT -> 679
WINDOW_LEFT -> 329
WINDOW_MAX -> 0
WINDOW_TOP -> 23
WINDOW_WIDTH -> 1202
WipeFreeSpaceDrives -> C:\|F:\

Here's the output when we run the ccleaner plugin against an NTUSER.DAT which doesn't have any CCleaner artifacts (eg "rip.pl -p ccleaner -r /mnt/m57jean/Documents\ and\ Settings/Jean/NTUSER.DAT"):

Launching ccleaner v.20120128
ccleaner v.20120128
(NTUSER.DAT) Gets User's CCleaner Settings


Software\Piriform\CCleaner does not exist.

And so that concludes our little exercise ...  it wasn't too complicated in the end eh? My main goal was to write a simple RegRipper plugin and I think I did that OK. However, I think further ProcMon analysis of CCleaner could prove interesting. Anyway that's all we have time for today folks. As always, let me know what you think in the Comments section. I won't hold my breath lol ...



10 comments:

  1. Thanks for your great job.
    I always read your posts with great pleasure cause they're interesting and very detailed.

    ReplyDelete
  2. Hi Thierry13,

    Thanks for commenting. It nice to know someone out there is reading this blog. Do you have any suggestions for future posts? I'm kinda running out of ideas ...

    ReplyDelete
  3. Hi, I've a lot of ideas !

    - RAM analysis with volatility
    - Malware analysis ( exe analysis and live analysis)
    - Windows.edb analysis
    - how to carve for a specific file (identify header, configure tools...)

    These are themes of interest for me :)

    ReplyDelete
  4. Hi Thierry13,

    I've done a brief / basic post on Volatility in: http://cheeky4n6monkey.blogspot.com.au/2011/12/using-sift-to-crack-windows-xp-password.html

    Are you interested in a specific area of Volatility? Its pretty straight forward to use it to get a list of processes/network connections etc. from a memory dump.

    At this stage, I'm not that confident researching/analysing malware (even on a virtual machine). I don't have a spare PC to infect. So that one might have to wait.

    File carving was briefly covered when I used SIFT's "foremost" for one of my M57 posts. If you are after a list of file header signatures, this site might prove useful: http://www.garykessler.net/library/file_sigs.html

    I am unfamilar with Windows.edb analysis. I just found this article on the forensicfocus forums though: http://www.forensicfocus.com/windows-search-forensics
    Is that what you are referring to? It does sound like a promising area of research eh?

    ReplyDelete
  5. Thanks for your answer.

    - Concerning RAM analysis I was wondering how to find Internet browsing datas...Volatility can be use to find connexions to web servers, process/browsers running and files handles. Other tools can be used to find URL (strings with "http"...), data carving to find web pages, python scripts (gmail datas)...Maybe it could be interesting to see all these tools, or others, working all together (to be honest i've never try them on a real case)

    - Concerning data carving : I was interested in the total process, how do you identify a file header, how do you add a specific header to foremost or scalpel, How do you configure these tools to carve these specific datas (if there is no footer for example...)

    - Concerning Windows.edb...sure it is a real gold mine ;) but for now there isnt so much free tools to exploit it...I've tryed esedbviewer from woanware but without success cause my databases are "dirty"...I know there is the libesedb command line tools but have not tested yet...

    I know it's easy to ask and hard to do....but you ask for ideas :)

    Thanks for your work.

    Thierry

    ReplyDelete
  6. Hi thanks for your answer,


    - Concerning RAM analysis I wanted to know how to extract browsing artefacts. Volatility can be used but there is other tools (strings, python scripts, scalpel...) and it could be useful to see them work all together.

    - concerning data carving I was interested in the process to carve a specific file : How to identify the file header, How to configure a specific tool (foremost, scalpel...).
    In fact it's quite easy to use these tools to carve well known files but how to carve "unknown" ones...

    - "windows.edb" seems to be a real gold mine but i didn't find any free tool to exploit it.
    Tests i've done with "esedbviewer" wasn't effective cause my databases always seems "dirty". I know there is the libesedb command line tools but I have not try it yet...

    I know it's easy to ask and hard to do....but you ask for ideas ;)

    Thanks for your work.

    Thierry

    ReplyDelete
  7. Thanks for the cool suggestions. I'll see what I can come up with but it might take some time. I'll probably look at Data carving with SIFT's foremost first.

    ReplyDelete
  8. Great post, and thank you for memorializing this information into a RegRipper plugin!

    In my experience, the CCleaner values found in the NTUSER\Software\Piriform\CCleaner hive only exist once the user has changed or updated them. Is this also what you found during your testing? For instance, in the ccleaner.pl output provided in your post, I would expect that the user at one time set/unset each one of those options found.

    ReplyDelete
    Replies
    1. Hi Chad,

      Thanks for writing and nice catch!

      For my example output I did set/unset some values to check the plugin was working but I must admit that I was focusing more on getting the plugin working.

      After reading your comment, I went back and uninstalled/re-installed CCleaner v3.18 and played around with it a bit more. The behaviour is as you described - key values only appear in the Registry if they have been explicitly set by the user.

      I also observed the following behaviour:
      After installation, there is only a "Language" value under the CCleaner hive entry.
      After the first launch, several more values are added "UpdateKey", "CookiesToSave", "WINDOW_LEFT", "WINDOW_TOP", "WINDOW_WIDTH", "WINDOW_HEIGHT", "WINDOW_MAX".
      After checking the "Old Prefetch Data" option, the value "(App)Old Prefetch data" subsequently appears in the CCleaner hive.

      At the very least, we can see if a user has launched CCleaner after installation by the presence of the "WINDOW_" and "UpdateKey" values.

      Thanks

      Delete