Santa received ransomware from an evil elf. But his magical security reindeer team made the right decision and properly secured evidence. Can you help Santa recover the list of naughty and nice kids?
Files can be downloaded here: https://we.tl/t-nBPKrElpa0
Santa in his time machine was a little bit dizzy and left one fake flag ;)
This challenge is about memory forensics. Initially its creators made a mistake by leaving the flag in plaintext in the memory dump:
$ strings dump.mem | grep 'ctf{'
cnctf{N0t_V3ry_S3cur3}
Due to that, the challenge was replaced with the hardened version on the next day. However, later this day I solved this challenge as intended.
We have a memory dump, and Volatility is a typical tool to use in such forensics challenges. Let's get some info about the dump:
$ volatility -f dump.mem imageinfo
Volatility Foundation Volatility Framework 2.6.1
INFO : volatility.debug : Determining profile based on KDBG search...
Suggested Profile(s) : Win7SP1x86_23418, Win7SP0x86, Win7SP1x86_24000, Win7SP1x86
AS Layer1 : IA32PagedMemoryPae (Kernel AS)
AS Layer2 : FileAddressSpace (/home/oioki/xmas/09/dump.mem)
PAE type : PAE
DTB : 0x185000L
KDBG : 0x82933be8L
Number of Processors : 1
Image Type (Service Pack) : 0
KPCR for CPU 0 : 0x82934c00L
KUSER_SHARED_DATA : 0xffdf0000L
Image date and time : 2021-10-02 08:56:35 UTC+0000
Image local date and time : 2021-10-02 10:56:35 +0200
The most important is profile (Win7SP1x86_23418
) which we have to specify in the subsequent volatility runs. The challenge called True secret
and contains the file with the same name, so likely we are dealing with TrueCrypt encrypted file. We can get the TrueCrypt binary here.
Let's try to find the password in the memory, using the truecryptsummary
volatility plugin:
$ volatility -f dump.mem --profile=Win7SP1x86_23418 truecryptsummary
Volatility Foundation Volatility Framework 2.6.1
Registry Version TrueCrypt Version 7.1a
Password geslo123 at offset 0x8e701064
Process TrueCrypt.exe at 0x83f48030 pid 524
Service truecrypt state SERVICE_RUNNING
Kernel Module truecrypt.sys at 0x8e6cd000 - 0x8e704000
Symbolic Link E: -> \Device\TrueCryptVolumeE mounted 2021-10-02 08:56:11 UTC+0000
Symbolic Link Volume{f55120a6-235d-11ec-adde-20791829acf1} -> \Device\TrueCryptVolumeE mounted 2021-10-02 08:56:11 UTC+0000
Symbolic Link E: -> \Device\TrueCryptVolumeE mounted 2021-10-02 08:56:11 UTC+0000
File Object \Device\TrueCryptVolumeE\ at 0x172f0f80
File Object \Device\TrueCryptVolumeE\ at 0x1e03f340
Driver \Driver\truecrypt at 0x1eb337a8 range 0x8e6cd000 - 0x8e703b80
Device TrueCryptVolumeE at 0x83ef7a38 type FILE_DEVICE_DISK
Container Path: \??\C:\Users\Tasha\Documents\MySecrets
Device TrueCrypt at 0x85133ca8 type FILE_DEVICE_UNKNOWN
The password is geslo123
("geslo" is "password" in Slovenian).
Indeed, it is our password for the TrueCrypt disk image (which was also provided):
$ ./truecrypt TrueSecret mount
The mounted disk contains only a single file with the link to pastebin. Its contents were 2PN4XQGdPo31Fb5MCAmECmRUGqPz
. It is not Base64, but something else. When I get tired of this guessing, I just use CyberChef Magic method. Just to double check:
$ base58 -d
2PN4XQGdPo31Fb5MCAmECmRUGqPz
ctf{N0t_V3ry_S3cur3}
I could not identify the correct profile of the new image with my volatility version, so I used volatility Docker image with the recent profiles available:
$ function volatility() { docker run --rm --user=$(id -u):$(id -g) -v "$(pwd)":/dumps:rw,Z -ti phocean/volatility $@; }
$ volatility -f /dumps/dump.mem imageinfo
Volatility Foundation Volatility Framework 2.6.1
INFO : volatility.debug : Determining profile based on KDBG search...
Suggested Profile(s) : Win10x64_19041
AS Layer1 : SkipDuplicatesAMD64PagedMemory (Kernel AS)
AS Layer2 : FileAddressSpace (/dumps/dump.mem)
PAE type : No PAE
DTB : 0x1ad002L
KDBG : 0xf8016fe1db20L
Number of Processors : 2
Image Type (Service Pack) : 0
KPCR for CPU 0 : 0xfffff8016e0eb000L
KPCR for CPU 1 : 0xffffdb005e9c0000L
KUSER_SHARED_DATA : 0xfffff78000000000L
Image date and time : 2021-12-10 06:45:00 UTC+0000
Image local date and time : 2021-12-10 07:45:00 +0100
We can do some other stuff, like dumping process list (see pslist.txt):
$ volatility --profile Win10x64_19041 -f /dumps/dump.mem pslist > pslist.txt
However, doing some other stuff resulted in a weird error. This is probably due to my setup, but whatever:
$ volatility --profile Win10x64_19041 -f /dumps/dump.mem clipboard
Volatility Foundation Volatility Framework 2.6.1
Session WindowStation Format Handle Object Data
---------- ------------- ------------------ ------------------ ------------------ --------------------------------------------------
WARNING : volatility.debug : Cannot find nt!ObGetObjectType
WARNING : volatility.debug : Cannot find nt!ObGetObjectType
Traceback (most recent call last):
File "vol.py", line 192, in <module>
main()
File "vol.py", line 183, in main
command.execute()
File "/volatility/volatility/commands.py", line 147, in execute
func(outfd, data)
File "/volatility/volatility/plugins/gui/clipboard.py", line 155, in render_text
for session, wndsta, clip, handle in data:
File "/volatility/volatility/plugins/gui/clipboard.py", line 66, in calculate
for wndsta in windowstations.WndScan(self._config).calculate():
File "/volatility/volatility/plugins/gui/windowstations.py", line 57, in calculate
for wind in self.scan_results(addr_space):
File "/volatility/volatility/poolscan.py", line 252, in scan
skip_type_check = skip_type_check)
File "/volatility/volatility/plugins/overlays/windows/windows.py", line 1258, in get_object
return self.get_object_top_down(struct_name, object_type, skip_type_check)
File "/volatility/volatility/plugins/overlays/windows/windows.py", line 1231, in get_object_top_down
header.get_object_type() == object_type):
File "/volatility/volatility/plugins/overlays/windows/win7.py", line 155, in get_object_type
return self.type_map.get(int(self.TypeIndex), '')
File "/volatility/volatility/plugins/overlays/windows/win10.py", line 334, in TypeIndex
return ((addr >> 8) ^ cook ^ indx) & 0xFF
TypeError: unsupported operand type(s) for ^: 'int' and 'NoneType'
I noticed that one of the processes were notepad.exe
, and volatility challenges creators love to put some sensitive into Notepad documents. Let's extract some strings from there:
$ volatility --profile Win10x64_19041 -f /dumps/dump.mem memdump -p 6148 -D /dumps/memdump/
Volatility Foundation Volatility Framework 2.6.1
************************************************************************
Writing notepad.exe [ 6148] to 6148.dmp
$ strings 6148.dmp > 6148.dmp.str
and expect the password to be in some readable form (like geslo123
but add a few special characters just to make sure):
$ grep -E '^[a-z0-9!@#$%^&*()_]{1,39}$' 6148.dmp.str | sort | uniq > 6148.dmp.str2
This is where I am not proud of myself, because I had to use a tool to bruteforce the TrueCrypt password:
$ git clone https://github.com/lvaccaro/truecrack
$ cd truecrack
$ ./configure --enable-cpu
$ make
$ bin/truecrack -v -t gg.tc -w memdump/6148.dmp.str2
TrueCrack v3.6
Website: http://code.google.com/p/truecrack
Contact us: [email protected]
Memory initialization...
COUNT PASSWORD RESULT
0 !!!! NO
1 !!!% NO
...
956 0948d NO
957 !095 NO
958 )098) NO
959 098f6bcd4621d373cade4e832627b4f6 YES
Found password: "098f6bcd4621d373cade4e832627b4f6"
Password length: "33"
Total computations: "960"
The correct password is 098f6bcd4621d373cade4e832627b4f6
(by the way, this is an MD5 hash for test
string).
Let's do the same as in Solution 2:
$ ./truecrypt gg.tc mount
The image contains a single file:
$ base58 -d mount/secretdata.txt
https://pastebin.com/N9kHEfZW
And we get the flag by visiting https://pastebin.com/N9kHEfZW:
ctf{All1Want4XmassIsY0u}
Do you know the proper solution? Please let me know.