Magnet CTF Week 9: ReMEMORYing How To Do This

 · 12 mins read

TL;DR: Week 9 of the #MagnetWeeklyCTF began a new case, this one involving a memory image.


Check out the week 1 blog post for how to get started on the Magnet Weekly CTF.

Get the first challenge

The weekly challenge for week 9 was split into seven parts! The first was:

The user had a conversation with themselves about changing their password. What was the password they were contemplating changing too. Provide the answer as a text string.

The absolute scariest thing to me about memory forensics is that you realize just how much of what we think of as “secure” is absolutely not. Passwords are no exception, especially when you are “chatting with yourself”. Since I did not yet have Volatility installed, I resorted to the good old standby: strings.

[notta@cuppa case3]$ strings memdump.mem | grep -i password | grep -i change

"Hmmm mmaybe I should change my password to:"

[notta@cuppa case3]$ strings memdump.mem | grep -i mmaybe -A 3

Hmmm mmaybe I should change my password to: 
Great idea warren
Thank you warren

The first command runs strings on our memory dump, then pipes that to grep looking for a case insensitive (-i) instance of the word “password”, and finally pipes that output to grep looking for a case insensitive instance of the word “change”. Turns out, we have a hit, which thrilled me as I didn’t want to have to sift through all of the strings output. Normally, I would expect strings to return the next line in the file immediately after this line, but since I used grep twice, I ripped the line from its context and had to go back to get it. Thankfully, the string that returned has a typo in it (“mmaybe”), which should make for a quick grep.

The second command is running strings on the same file, then piping the result to grep looking for a case insensitive instance of the word “mmaybe” and returning the next three lines (-A 3) to get it in context. The result shows our answer: “wow_this_is_an_uncrackable_password”

Get the second challenge

The second challenge was:

What is the md5 hash of the file which you recovered the password from?

I have a bit of a complaint about this question because the right answer should be “224f93209cbea29e862890f30dfa762d” which is the md5sum of memdump.mem. Since that is the file which contains all of this, it should be correct.

Assuming Magnet was being coy in the question, I decided to refresh my memory of volatility. This is my go to memory tool, since it works nicely from the command line and is free/open source. After cloning the repository from Github, I started exploring what the right starting point was. Initially, I remembered I needed to have the right profile to get volatility to play nice, so I started with imageinfo.

[notta@cuppa case3]$ volatility -f memdump.mem imageinfo

Volatility Foundation Volatility Framework 2.6.1
INFO    : volatility.debug    : Determining profile based on KDBG search...
          Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_24000, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_24000, Win7SP1x64_23418
                     AS Layer1 : WindowsAMD64PagedMemory (Kernel AS)
                     AS Layer2 : FileAddressSpace (/home/notta/2020_magnet/case3/memdump.mem)
                      PAE type : No PAE
                           DTB : 0x187000L
                          KDBG : 0xf80002c2a120L
          Number of Processors : 2
     Image Type (Service Pack) : 1
                KPCR for CPU 0 : 0xfffff80002c2c000L
                KPCR for CPU 1 : 0xfffff88002f00000L
             KUSER_SHARED_DATA : 0xfffff78000000000L
           Image date and time : 2020-04-20 23:23:26 UTC+0000
     Image local date and time : 2020-04-20 19:23:26 -0400

Volatility’s first guess as to what to use for the profile was Win7SP1x64 so I used that for the rest of the challenge.

Since we are looking for a file, I decided to do this the hard way and just dumped all the files out using dumpfiles, making sure to include the file name in the exported file name (--name), then grepped for the answer to see which had it.

[notta@cuppa case3]$ volatility -f memdump.mem --profile=Win7SP1x64 \
dumpfiles --dump-dir=dumpfiles --name

[notta@cuppa dumpfiles]$ grep -r mmaybe

grep: file.3180.0xfffffa803316f710.AutoRecovery save of Document1.asd.dat: binary file matches

[notta@cuppa dumpfiles]$ file \
file.3180.0xfffffa803316f710.AutoRecovery save of Document1.asd.dat

file.3180.0xfffffa803316f710.AutoRecovery save of Document1.asd.dat: CDFV2 Microsoft Word

[notta@cuppa dumpfiles]$ md5sum \
file.3180.0xfffffa803316f710.AutoRecovery save of Document1.asd.dat


The dumped file in question (file.3180.0xfffffa803316f710.AutoRecovery save of Document1.asd.dat) is a Microsoft Word document, and had an MD5 hash of af1c3038dca8c7387e47226b88ea6e23. That MD5 hash is the right answer.

Get the third challenge

The third challenge was:

What is the birth object ID for the file which contained the password?

To find the birth object ID, you need to dig into the NTFS information for the file. To get that information, we’ll look at the Master File Table (MFT), which is serviced by volatility’s mftparser.

[notta@cuppa case3]$ volatility -f memdump.mem --profile=Win7SP1x64 \
mftparser --output-file=mft2.txt

[notta@cuppa case3]$ cat mft2.txt \
| grep "MFT entry found\|\.asd\|Birth Object ID" \
| grep -A 1 -B 1 "\.asd"

MFT entry found at offset 0x7dfd000
2020-04-20 23:22:36 UTC+0000 2020-04-20 23:22:36 UTC+0000   2020-04-20 23:22:36 UTC+0000   2020-04-20 23:22:36 UTC+0000   Users\Warren\AppData\Roaming\MICROS~1\Word\AutoRecovery save of Document1.asd
Birth Object ID: 31013058-7f31-01c8-6b08-210191061101

In this case, I saved the mftparser output as a text file, then used cat to send the output to grep, looking for three different lines. I matched on anything that said “MFT entry found”, the extension “.asd”, or “Birth Object ID”. I then piped that further into grep to give me one line before (-B 1) and after (-A 1), but only for the line with “.asd”.

The first grep would hit for every file and groom volatilty’s output down from a lot of extra things, to just the line giving the MFT Entry offset, and the Birth object ID, or a filename with “.asd” in it. The second grep then goes through that groomed output and finds the one file with “.asd”, so we know the line before and after is its information. In this case, the birth object listed (“31013058-7f31-01c8-6b08-210191061101”) is the correct answer.

Get the fourth challenge

The fourth challenge was:

What is the name of the user and their unique identifier which you can attribute the creation of the file document to? Format: #### (Name)

The volatility plugin to find the security IDs of the users for each process is getsids. As I had previously identified process 3180 as the process for WINWORD, this was a quick one.

[notta@cuppa case3]$ volatility -f memdump.mem --profile=Win7SP1x64 getsids -p 3180

WINWORD.EXE (3180): S-1-5-21-4288132831-552422005-3632184702-1000 (Warren)
WINWORD.EXE (3180): S-1-5-21-4288132831-552422005-3632184702-513 (Domain Users)
WINWORD.EXE (3180): S-1-1-0 (Everyone)
WINWORD.EXE (3180): S-1-5-114 (Local Account (Member of Administrators))
WINWORD.EXE (3180): S-1-5-32-544 (Administrators)
WINWORD.EXE (3180): S-1-5-32-545 (Users)
WINWORD.EXE (3180): S-1-5-4 (Interactive)
WINWORD.EXE (3180): S-1-2-1 (Console Logon (Users who are logged onto the physical console))
WINWORD.EXE (3180): S-1-5-11 (Authenticated Users)
WINWORD.EXE (3180): S-1-5-15 (This Organization)
WINWORD.EXE (3180): S-1-5-113 (Local Account)
WINWORD.EXE (3180): S-1-5-5-0-691206 (Logon Session)
WINWORD.EXE (3180): S-1-2-0 (Local (Users with the ability to log in locally))
WINWORD.EXE (3180): S-1-5-64-10 (NTLM Authentication)
WINWORD.EXE (3180): S-1-16-12288 (High Mandatory Level)

The only user account using this program is “Warren”. His security ID is “S-1-5-21-4288132831-552422005-3632184702-1000”, but they specify the format to be “XXXX”, hence the right answer is just “1000”.

Get the fifth challenge

The fifth challenge was:

What is the version of software used to create the file containing the password?

In this case, I only used volatility’s procdump plugin to drop the executable to disk, then used pe-tree to read the information.

[notta@cuppa case3]$ volatility -f memdump.mem --profile=Win7SP1x64 \
procdump --dump-dir=procdump -p 3180

Volatility Foundation Volatility Framework 2.6.1
Process(V)         ImageBase          Name                 Result
------------------ ------------------ -------------------- ------
0xfffffa803177bb00 0x000000013f9f0000 WINWORD.EXE          OK: executable.3180.exe

[notta@cuppa case3]$ pe-tree procdump/executable.3180.exe

pe-tree will bring up a GUI and in the VS_VERSION section, you’ll see that the version is “15.0.5233.1000”. After submitting that, and having it not be right, I played around to discover Magnet only wanted the major version (“15”). That is the correct answer.

Get the sixth challenge

The sixth challenge was:

What is the virtual memory address offset where the password string is located in the memory image?

This question is where things went a bit sideways. The question hinges on the mapping between physical offsets in the actual file and the virtual offset that it represents. Or to reword it, where to find each bit of the memory we imaged in the file we saved it in. Volatility’s memmap plugin will show you that mapping between the virtual and physical nicely, as long as you know where it is you want to look.

[notta@cuppa case3]$ strings -tx memdump.mem | grep wow_this_is_an_uncrackable_password

af12a2d wow_this_is_an_uncrackable_password

[notta@cuppa case3]$ volatility -f memdump.mem --profile=Win7SP1x64 memmap -p 3180 \
| grep "000af12"

Volatility Foundation Volatility Framework 2.6.1
WINWORD.EXE pid:   3180
Virtual            Physical                         Size     DumpFileOffset
------------------ ------------------ ------------------ ------------------
0x0000000002180000 0x000000000af12000             0x1000           0x41e000
0x000000000af12000 0x00000000ac6d9000             0x1000          0x2643000

To find out where to look, we’re using strings and telling it to display the output of where it finds the string in hex (-tx). This says that in the dump file, our target string starts at 0xaf12a2d. Once we know the rough physical address, we can look at the memmap for the WINWORD process (-p 3180) and grep for the beginning of the address. We don’t want to look for the entire address because memory is allocated in blocks and I’d looked earlier to see it was probably a block of 1000 (in hex).

We have two decisions from this output, in the first line you can see that virtual address 0x2180000 is mapped to physical address 0xaf12000. In the second line, virtual address 0xaf12000 is mapped to physical address 0xac6d9000. Because we saw the string at physical address 0xaf12a2d, we want the first answer. We can then take the last three digits of the string address and add it to our base address to get the answer: 0x2180a2d. We can do this because we’re just comparing the offset from the start of the block, whether physical or virtual. Since we are another 0xa2d into the physical block, we’ll be another 0xa2d into the virtual block.

The problem is…. that wasn’t the right answer. I tried it in hex, I tried it in octal, I tried it in decimal. I tried the physical address, I tried all of the above with all the 0’s on front as well, and with and without the “0x”. None of it worked. Finally, I hit up Jessica Hyde to ask if, maybe, the answer was wrong. Thankfully, after she dug in, she realized there was a single leading 0 on the answer, making the “correct” answer actually “0x02180a2d”.

I was very happy after the answer was fixed to allow the version without a leading 0 and greatly appreciate her digging into the issue as quickly as she did.

Get the seventh challenge

The seventh challenge was:

What is the physical memory address offset where the password string is located in the memory image?

We had to answer this question to answer the previous question. The answer was “0xaf12a2d”.


Volatility is an excellent free, open source tool for memory analysis, if you haven’t checked it out, you should. This week was a really good reminder of some memory analysis basics and a Windows refresher after a month of Android and a month of Linux, two OS’s I am much more comfortable with.