Restoring Windows CMD sessions from pagefile.sys

Posted on December 2, 2013 | Category :DFIR, Memory Forensics | 13 Comments

Why care about pagefile?

Sometimes you find yourself lost with only a few places to look during IR investigations. This is usually the time when you want to have a memory dump and volatility. Especially with more advanced lateral movements of the adversary only using close to windows tools or minimal helper scripts executed via command line. But often images take a long way until they arrive on your workstation. And memory images are still not a default TODO on imaging guides of all IT departments. Soooooooo:

During one of these Incidents I found interesting command line related (unicode!) strings in the X-Ways Forensics Index pointing to the C:\pagefile.sys.There is nothing completely new about CMD artifacts in pagefile but I wanted to know if there are any patterns that will make the search for these information scriptable.

The usual approach for reviewing pagefiles so far has been a good old strings search:

% strings -a -td -el pagefile.sys > pagefile.sys.strings

And the some grep magic to get the interesting stuff. But this approach is very time consuming as grep does not give you the whole related data but only the hit line for your keywords (yes I know about “-A” and “-B”). Wouldn’t it be better to have a chunk oriented view on interesting parts of the pagefile?

After some search I found an interesting looking tool called page_brute on GitHub.

page_brute divides the pagefile.sys in 4096 Byte chunks (default – can be changed) and runs user defined yara rules against them to find interesting stuff. This is a notably better approach as now every contextual related data in the pagefile can be parsed as written from the system. Below you see a sample run:

% python page_brute-BETA.py -r default_signatures.yar -f pagefile.sys
[+] - PAGE_BRUTE processing file: pagefile.sys
[+] - YARA rule of File type provided for compilation: default_signatures.yar
..... Ruleset Compilation Successful.
[+] - PAGE_BRUTE running with the following options:
	[-] - FILE: pagefile.sys
	[-] - PAGE_SIZE: 4096
	[-] - RULES TYPE: FILE
	[-] - RULE LOCATION: default_signatures.yar
	[-] - INVERSION SCAN: False
	[-] - WORKING DIR: PAGE_BRUTE-2013-12-02-17:43:58-RESULTS
	=================

        [!] FLAGGED BLOCK 5234: webartifact_html
        [!] FLAGGED BLOCK 17582: webartifact_html
        [!] FLAGGED BLOCK 18178: webartifact_html
----- SNIP -----
        [!] FLAGGED BLOCK 24590: webartifact_html
        [!] FLAGGED BLOCK 28126: webartifact_javascript
        [!] FLAGGED BLOCK 28527: webartifact_html
Done!
Ending page_id is: 196608

As the tool is currently in beta state you should have a look at the code and the default rules yourself. I did not encounter any errors with the main script so far.

The dive

To search for chunks that are clearly related to cmd usage we need to view an exmaple in a hex editor:

dd if=pagefile.sys bs=1 skip=38995798 | xxd | less

0000000: 4f00 6f00 7300 6f00 6600 7400 2000 5700  O.o.s.o.f.t. .W.
0000010: 6900 6e00 6400 6f00 7700 7300 2000 5800  i.n.d.o.w.s. .X.
0000020: 5000 2000 5b00 5600 6500 7200 7300 6900  P. .[.V.e.r.s.i.
0000030: 6f00 6e00 2000 3500 2e00 3100 2e00 3200  o.n. .5...1...2.
0000040: 3600 3000 3000 5d00 2000 2000 2000 2000  6.0.0.]. . . . .
0000050: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000060: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000070: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000080: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000090: 2000 2000 2000 2000 2000 2800 4300 2900   . . . . .(.C.).
00000a0: 2000 4300 6f00 7000 7900 7200 6900 6700   .C.o.p.y.r.i.g.
00000b0: 6800 7400 2000 3100 3900 3800 3500 2d00  h.t. .1.9.8.5.-.
00000c0: 3200 3000 3000 3100 2000 4d00 6900 6300  2.0.0.1. .M.i.c.
00000d0: 7200 6f00 7300 6f00 6600 7400 2000 4300  r.o.s.o.f.t. .C.
00000e0: 6f00 7200 7000 2e00 2000 2000 2000 2000  o.r.p... . . . .
00000f0: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000100: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000110: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000120: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000130: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000140: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000150: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000160: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000170: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000180: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
0000190: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
00001a0: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
00001b0: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
00001c0: 2000 2000 2000 2000 2000 2000 2000 2000   . . . . . . . .
00001d0: 2000 2000 2000 2000 2000 4300 3a00 5c00   . . . . .C.:.\.
00001e0: 4400 6f00 6b00 7500 6d00 6500 6e00 7400  D.o.k.u.m.e.n.t.
00001f0: 6500 2000 7500 6e00 6400 2000 4500 6900  e. .u.n.d. .E.i.
0000200: 6e00 7300 7400 6500 6c00 6c00 7500 6e00  n.s.t.e.l.l.u.n.
0000210: 6700 6500 6e00 5c00 4a00 4b00 6900 7200  g.e.n.\.J.K.i.r.
0000220: 6b00 3e00 6300 6400 2000 4400 6500 7300  k.>.c.d. .D.e.s.
0000230: 6b00 7400 6f00 7000 2000 2000 2000 2000  k.t.o.p. . . . .

Very interesting here is the gap between cmd lines. As the default (horizontal) buffer sizes of Windows command lines equals 80 Bytes we see these \x20\x00 blank identifiers.

After some testing the fun fact is -> there seems to be no CMD artifact in single pagefile chunks without at least one blank line \0/

Below the simple yara file that searches for chunks with 80 blanks (EDIT: please find a newer version at the end of this post):

rule CMDscan_Optimistic_Blanklines
{
meta:
	author="Robert Haist | @SleuthKid"
	description="Searching for blank lines that occur with cmd usage"

strings:
	$blanks = /([\x20][\x00])\1{80}/
condition:
	$blanks
}

If we use this yara rule with page_brute we get something like:

% python page_brute-BETA.py -r cmd_optimistic_blanks.yar -f pagefile.sys
[+] - PAGE_BRUTE processing file: pagefile.sys
[+] - YARA rule of File type provided for compilation: cmd_optimistic_blanks.yar
..... Ruleset Compilation Successful.
[+] - PAGE_BRUTE running with the following options:
	[-] - FILE: pagefile.sys
	[-] - PAGE_SIZE: 4096
	[-] - RULES TYPE: FILE
	[-] - RULE LOCATION: cmd_optimistic_blanks.yar
	[-] - INVERSION SCAN: False
	[-] - WORKING DIR: PAGE_BRUTE-2013-12-02-19:12:19-RESULTS
	=================

        [!] FLAGGED BLOCK 5025: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 5026: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 9520: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 9521: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 9522: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 9574: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 21196: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 21197: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 21198: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 23092: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 26882: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 28663: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 29033: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 29035: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 29036: CMDscan_Optimistic_Blanklines
        [!] FLAGGED BLOCK 29320: CMDscan_Optimistic_Blanklines
Done!
Ending page_id is: 196608

The ouput files are stored in:

 <$progdir>/PAGE_BRUTE-<$fooo>-RESULTS/CMDscan_Optimistic_Blanklines/<$pageblock_number>.page

Now we are able to parse the relevant logical chunks as one related object and identify found CMD sessions:

% strings -el 9520.page | sed -e "s/\s\{4,\}/\\n/g"
Oosoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\Dokumente und Einstellungen\JKirk>cd Desktop
C:\Dokumente und Einstellungen\JKirk\Desktop>python agent.py
[+] Starting agent on 0.0.0.0:8000 ...  
O
^C

Did you notice the “^C” on the bottom? Seems like we really see the whole cmd session including special chars. You want to make sure to parse unicode strings here.

Not every dumped page chunk will include a CMD session. I am not completely sure why and did not find much useful information about the internal mechanisms (Please feel free to enhance my knowledge :) ).But it does seem like programs invoking a cmd.exe subprocess (like installers, update etc.) are all paging with the 80-blank behavior.

This example is not impressive dude!

I don’t have a interesting example prepared at the time of writing this post. If there is enough interest I can prepare a more detailed article about real life IR related artifacts to expect here. So please try this with fresh and hot pagefiles yourself. You may find some REALLY interesting stuff there. Just search for the usual suspects. I had fun with ( psexec | net | 7z | rar | zip ).

During making of this article no real computers where harmed.

Special THX to research support from:

KlausTichmann & Florian Roth (Regex RULEZ!)

 

[UPDATE: 05.12.13]

As there was some serious interest in this article I will try to update it regularly to include all new findings. Check my Twitter feed @SleuthKid for update announcements.
Some further research showed two magic byte constructs that seem to be related to further CMD paging. Please test the updated yara rule below:

 rule CMDscan_Optimistic_Blanklines
 {
 meta:
     author="Robert Haist | @SleuthKid"
     description="Searching for blank lines and magic bytes that occur with paged cmd usage"

 strings:
     $m1 = { 63 3A 79 26 7B } // Mysterious Magic Byte -> c:y&{
     $m2 = { 77 00 27 00 57 00 27 00 77 00 } // Mysterious Magic Byte -> w'W'w
     $blanks = /([\x20])\1{80}/ wide // Checks for 80 Blanks

 condition:
     $blanks or $m1 or $m2
 }

I know that there are good chances to catch CMD artifacts with simple keyword searches but I try to find more low level pattern here to really get ALL CMD related pages. Sometimes these only include parts of memory sections without any guessable keywords. Think of passwords and user names or custom executable names that get logged here.

Comments 9

  1. PB Reply
    13/12/02

    Nice work.

  2. H. Carvey Reply
    13/12/03

    Quick question…I’m running Windows 7 64-bit, and 32-bit Python 2.7.1. I tried installing Yara via pip, but when I run “yara-ctypes -h”, but I’m getting a “Failed to import ‘C:\Python27\DLLs\libyara.dll’” error.

    Any thoughts?

    Thanks.

    • 13/12/03

      You should be good with using the fresh builds available at the new yara releases page: https://github.com/plusvic/yara/releases

      First install Python 2.7 then this:

      https://googledrive.com/host/0BznOMqZ9f3VUek8yN3VvSGdhRFU/yara-python-1.7.1.win-amd64-py2.7.exe

      I did not test any of this on windows but you might be happy with this.
      I am happy to help out more via email or jabber :)

    • 13/12/04

      The issue is the pip version of Yara. Unfortunately, there’s the standard yara release (that Haist posted below), and there’s another one that someone created by themselves and put into pip. The one that pip gives you is somewhat the same but isn’t really compatible. I hit this when working with Volatility, and it’s an issue that isn’t documented anywhere.

      You’ll want to get the official release and build from scratch.

  3. H. Carvey Reply
    13/12/04

    Robert,

    I downloaded the file you linked to and ran the installer. I then went to the command prompt, and typed “page_brute.py” (I’d dropped “BETA” off of the name), and got:

    Failed to import ‘C:\Python27\DLLs\libyara.dll’

  4. H. Carvey Reply
    13/12/04

    Thanks, I got the yara-python stuff installed…I had run the pip command, so I grabbed the package, and copied the 32-bit copy of the DLL into the Python27\DLLs folder, so the “yara-cytpes -h” command finally worked.

    However, here’s where I am now…I’m running the command:
    page_brute.py -r default_signatures.yar -f h:\test\pagefile.sys

    Here’s the output:
    [+] – PAGE_BRUTE processing file: h:\test\pagefile.sys
    [+] – YARA rule of File type provided for compilation: default_signatures.yar
    ….. Ruleset Compilation Successful.
    Traceback (most recent call last):
    File “C:\Python27\page_brute.py”, line 229, in
    main()
    File “C:\Python27\page_brute.py”, line 183, in main
    os.makedirs(WORKING_DIR)
    File “C:\Python27\lib\os.py”, line 157, in makedirs
    mkdir(name, mode)
    WindowsError: [Error 123] The filename, directory name, or volume label syntax is incorrect: ‘PAGE_BRUTE-2013-12-04-09:01:05-RESULTS’

    So, I try running this command:
    C:\Python27>page_brute.py -r default_signatures.yar -f h:\test\pagefile.sys -o h:\test\output

    And I get this:
    [+] – PAGE_BRUTE processing file: h:\test\pagefile.sys
    [+] – YARA rule of File type provided for compilation: default_signatures.yar
    ….. Ruleset Compilation Successful.
    [+] – PAGE_BRUTE running with the following options:
    [-] – FILE: h:\test\pagefile.sys
    [-] – PAGE_SIZE: 4096
    [-] – RULES TYPE: FILE
    [-] – RULE LOCATION: default_signatures.yar
    [-] – INVERSION SCAN: False
    [-] – WORKING DIR: h:\test\output
    =================

    Traceback (most recent call last):
    File “C:\Python27\page_brute.py”, line 229, in
    main()
    File “C:\Python27\page_brute.py”, line 202, in main
    CHUNK_OUTPUT_DIR=WORKING_DIR + “/” + matches.rule
    AttributeError: ‘str’ object has no attribute ‘rule’

    Thoughts?

  5. H. Carvey Reply
    13/12/04

    Based on Brian Baskin’s input, I uninstalled yara and yara-python via pip (and verified via ‘pip list’), and then re-installed per Michael Hale’s input here:

    https://code.google.com/p/volatility/issues/detail?id=446

    Running ‘yara-ctypes -h’ completes with no errors, and the version of yara is 1.7.6; however, I still get the same error as above.

  6. Matonis Reply
    13/12/04

    Issue should be fixed. The error stems the default output directory having colons from the datetime function which not allowed in directory names on Windows systems. Another issue are the directory delimiters being a default *NIX “/” – I’ve since made the fix to use os.path.join and the code should be A-OK for both *NIX and M$.

    For future reference, you can debug yara installs by opening up the python terminal: python >> import yara. If that give you no errors, then yara should be installed successfully.

  7. D. Meier Reply
    13/12/10

    Have you tried this Yara rule against a volatile ram image yet? It seems directly translatable; I will do some poking around tonight to see if it finds anything additional. I know Volatility has a few plugins for CMD sessions, but this may dig out more.

    Thanks for the work!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <p> <q cite=""> <strike> <strong>

 

Pingbacks 4

pingback from Daily Blog #166: Forensic Lunch 12/6/13 : Learn DFIR December 6, 2013
pingback from Daily Blog #167: Saturday Reading 12/7/13 : Learn DFIR December 7, 2013
pingback from Daily Blog #169: Sunday Funday 12/8/13 Winner! : Learn DFIR December 10, 2013
pingback from Live Streaming Κάλυψη Συνεδρείων December 16, 2013