Hack In Paris Challenge 2

The challenge began on Wednesday February, 3rd 2016, with a tweet:

#HIPChall Number 2 : http://bit.ly/1QbHueV  #FrenchTouch
A #BLESnifferp and Two #HIPGoldenTickets to win!
Be the first, Be Fair play, Enjoy

This tweet indicates a web site again, The link to https://hackinparis.com/challenge-2.html. It contains this message:

CHALLENGE #HIPCHALL - STEP 2
Hello,

You are investigating on behalf of a subsidiary of the ANFN group based
on a suspicion of industrial espionage ; an employee has refused to
report to the internal interview and did not cooperate with the analysis
of his post. You got a memory image by a roundabout way.
The image is available at the following address:

 https://hackinparis.com/data/chall2016/step-2/dump.7z

The fingerprints for memdump_0x0-0x100000000_20160127-125924.bin :

 md5 e60e726b0bd6f3ed02c063ea4df43add
 sha256 6081121b669bbd6e16da754799ba1f366953e7ab159149b1943b436a5d11bbef

Once you have completed the analysis, please send your conclusions
(and the validation flag) to merenwen@hackinparis.com.

The given 7zip file contains memdump_0x0-0x100000000_20160127-125924.bin, which is 4GB-large (4294967295 bytes). Its MD5 and SHA256 sums match the ones which are given in the message.

Running volatility (https://github.com/volatilityfoundation/volatility) on the file to get information about this captured image leads (after 30 minutes on my laptop) to the following result:

$ volatility -f memdump_0x0-0x100000000_20160127-125924.bin imageinfo
Volatility Foundation Volatility Framework 2.5
INFO    : volatility.debug : Determining profile based on KDBG search...
          Suggested Profile(s) : Win7SP0x64, Win7SP1x64, Win2008R2SP0x64, Win2008R2SP1x64
                     AS Layer1 : AMD64PagedMemory (Kernel AS)
                     AS Layer2 : FileAddressSpace (/tmp/memdump_0x0-0x100000000_20160127-125924.bin)
                      PAE type : No PAE
                           DTB : 0x187000
                          KDBG : 0xf80003a4d0a0
          Number of Processors : 1
     Image Type (Service Pack) : 1
                KPCR for CPU 0 : 0xfffff80003a4ed00
             KUSER_SHARED_DATA : 0xfffff78000000000
           Image date and time : 2016-01-27 12:06:45 UTC+0000
     Image local date and time : 2016-01-27 13:06:45 +0100

$ volatility -f memdump_0x0-0x100000000_20160127-125924.bin kdbgscan
Volatility Foundation Volatility Framework 2.5
**************************************************
Instantiating KDBG using: memdump_0x0-0x100000000_20160127-125924.bin WinXPSP2x86 (5.1.0 32bit)
Offset (P)                    : 0x3a4d0a0
KDBG owner tag check          : True
Profile suggestion (KDBGHeader): Win7SP1x64
PsActiveProcessHead           : 0x3a83590
PsLoadedModuleList            : 0x3aa1890
KernelBase                    : 0xfffff8000385e000

$ volatility -f memdump_0x0-0x100000000_20160127-125924.bin kdbgscan --profile=Win7SP1x64
Volatility Foundation Volatility Framework 2.5
**************************************************
Instantiating KDBG using: Kernel AS Win7SP1x64 (6.1.7601 64bit)
Offset (V)                    : 0xf80003a4d0a0
Offset (P)                    : 0x3a4d0a0
KDBG owner tag check          : True
Profile suggestion (KDBGHeader): Win7SP1x64
Version64                     : 0xf80003a4d068 (Major: 15, Minor: 7601)
Service Pack (CmNtCSDVersion) : 1
Build string (NtBuildLab)     : P7?
PsActiveProcessHead           : 0xfffff80003a83590 (2 processes)
PsLoadedModuleList            : 0xfffff80003aa1890 (1 modules)
KernelBase                    : 0xfffff8000385e000 (Matches MZ: True)
Major (OptionalHeader)        : 6
Minor (OptionalHeader)        : 1
KPCR                          : 0xfffff80003a4ed00 (CPU 0)

The version field “Major: 15, Minor: 7601” states that the image has been taken from a system running Windows 7 SP1. The volatility profile to use is therefore Win7SP1x64.

However with this profile, psscan does not show good results:

$ volatility -f memdump_0x0-0x100000000_20160127-125924.bin --profile=Win7SP1x64 --dtb=0x187000 --kdbg=0x3a4d0a0 psscan
Volatility Foundation Volatility Framework 2.5
Offset(P)          Name                PID   PPID PDB                Time created                   Time exited
------------------ ---------------- ------ ------ ------------------ ------------------------------ ------------------------------
0x00000000433d2040 System                4      0 0x0000000000187000 2016-01-27 11:24:51 UTC+0000

The system might not have been running when the image has been taken...

A quick keyword analysis of the strings which can be found in the memory dump (using strings -tx and strings -tx -e l to print sequences of printable characters with their hexadecimal offsets) shows:

a0b03db @\malwhere\malwhere.exe
e12b688 "C:\malware.exe"
33dc8648 C:\malware.exe
af219078 C:\malware.exe

1daf6a80 .+:\\HACKING\\VYMPIRE\\CRYPT\\child\\.+.vbp

# These are strings from Metasploit framework
50c702e0 http://www.amazon.com/Oracle-Hackers-Handbook-Hacking-Defending/dp/0470080221
7045f3aa ent feedback. Some people just seem to enjoy hacking SAP :)
91183fc8 lent feedback. Some people just seem to enjoy hacking SAP :)
b2d5ec88 http://sh0dan.org/oldfiles/hackingcitrix.html

There are also two interesting email addresses, xavuddyhe-7642@yopmail.com (used as LastPass account, according to an XML file at offset b400420c) and fla@ndh.com (used as Google account, according to structures at offset 1b481b5c).

The memory dump also contains information about files which may have been loaded into RAM (which explains the Metasploit strings). To dump such files, mftparser command can be used in Volatility:

volatility -f memdump_0x0-0x100000000_20160127-125924.bin --profile=Win7SP1x64 \
--dtb=0x187000 --kdbg=0xf80003a4d0a0 --kpcr=0xfffff80003a4ed00 mftparser

Analyzing the output of this command (with file paths) shows the following things:

So, there are two downloaded files, conf.txt.zip (downloaded on 2016-01-27 09:59:04 UTC) and conf.txt (1).zip (downloaded on 2016-01-27 10:09:09 UTC). Both these ZIP files contain a file named conf.txt, which is encrypted. John The Ripper could be used to crack the password, with zip2john output:

$ zip2john conf.txt*.zip | tee zip.hashes
conf.txt (1).zip->conf.txt is using AES encryption, extrafield_length is 11
conf.txt.zip->conf.txt is using AES encryption, extrafield_length is 11

$ cat zip.hashes
conf.txt (1).zip:$zip$*0*1*4cedf3e0f29e38f8*efb7
conf.txt.zip:$zip$*0*1*cbb9a1dc906a255b*bfc3

$ john zip.hashes
Loaded 2 password hashes with 2 different salts (WinZip PBKDF2-HMAC-SHA-1 [32/64])

However John does not find anything in a reasonable time.

At that point, a hint was given to solve the challenge, https://twitter.com/hackinparis/status/695638145396772864:

#HIPChall n°2 FIRST HINT
Have fun, Be fair play, enjoy :)

« A quick analysis of the corporate proxy logs revealed the top 5 domains requested by the suspect in the past 7 days:
- accounts.google.com
- www.amazon.fr
- s000.tinyupload.com
- www.adobe.com
- www.youtube.com »
                            Sysdream's team

Using strings -tx | grep -C5 to match the domains with some contextual information reveal several things.

Even if the password is encrypted here, it may be in unencrypted elsewhere. Let’s grep on “Passwd-hidden” input name to be sure:

$ strings -tx memdump_0x0-0x100000000_20160127-125924.bin |grep -i Passwd-hidden |grep -i value
12dd784c 53896395004,"uniqid":23476135},"docid":1,"sharedsite":0,"automaticallyFill":1,
"is_launch":false,"manualfill":false,"name":"Passwd-hidden","value":"totoXXX69!","type":"password",

Bingo :) Even John is happy now:

$ echo 'totoXXX69!' |john --stdin zip.hashes
Loaded 2 password hashes with 2 different salts (WinZip PBKDF2-HMAC-SHA-1 [32/64])
totoXXX69!       (conf.txt.zip)
totoXXX69!       (conf.txt (1).zip)
guesses: 2  time: 0:00:00:00  c/s: 10.00  trying: totoXXX69!

The conf.txt file of conf.txt.zip archive contains:

Connexion VPN
User : d.fla
Password :
NDH{33a8c7e18f2be80243bd13b697bf101c7a53f3f30165ffd35d321e8376840caa}

while the one from conf.txt (1).zip contains:

Connexion VPN
User : d.fla
Password :
HIP{33a8c7e18f2be80243bd13b697bf101c7a53f3f30165ffd35d321e8376840caa}

(NDH has been replaced with HIP in the password)

To conclude the flag of the second Hack In Paris challenge is HIP{33a8c7e18f2be80243bd13b697bf101c7a53f3f30165ffd35d321e8376840caa}.

Appendix - dump page table entries

With volatility, it is possible to dump the page tables once the base address (CR3 value on an X86 CPU) is known. volatility imageinfo gave DTB : 0x187000, which is the base address. Here is a Python script to dump the page table:

#!/usr/bin/env python2
import volatility.addrspace as addrspace
import volatility.conf as conf
import volatility.registry as registry
import volatility.utils as utils

registry.PluginImporter()
config = conf.ConfObject()
registry.register_global_options(config, addrspace.BaseAddressSpace)
config.parse_options()
config.update('PROFILE', "Win7SP1x64")
config.update('LOCATION', "file:///tmp/memdump_0x0-0x100000000_20160127-125924.bin")
config.update('dtb', 0x187000)
config.update('kdbg', 0xfffff80003a4d0a0)
config.update('kpcr', 0xfffff80003a4ed00)
addr_space = utils.load_as(config)

# Try to read the page table from DTB
for vaddr, size in addr_space.get_available_pages():
    # Expand kernel virtual addresses
    if vaddr & (1 << 47):
        vaddr |= 0xffff << 48
    phy = addr_space.vtop(vaddr)
    # only output something when the physical address is within 4GB
    if not (phy >> 32):
        print('%16x (%6x) -> %x' % (vaddr, size, phy))

In the result, there are some interesting things for people who enjoy learning low-level operating-system-related materials.

First there is an identity mapping at 0xfffff800 00000000, which maps parts of the physical memory. For example:

fffff80003800000 (200000) -> 3800000
fffff80003a00000 (200000) -> 3a00000 <- kdbdg = 0xf80003a4d0a0
fffff80003c00000 (200000) -> 3c00000
fffff80003e00000 (200000) -> 3e00000

Next there is a recursive mapping at 0xfffff6fb7dbed000. It can be found because it maps to the DTB:

fffff6fb7dbed000 (  1000) -> 187000

Moreover, this address is special regarding the page directory decomposition, where each entry is 9-bit long (an address has a 16-bit extension-bit prefix, 4 9-bit indexes and a 12-bit offset in a 4KB page, 16+4*9+12=64 bits):

fffff6fb7dbed000
    f68          PGD index = 0x1ed
      7b4        PUD index = 0x1ed
        3da      PMD index = 0x1ed
          1ed    PTE index = 0x1ed

This is due to the fact that on Windows, entry 0x1ed is self-referential. This also means that addresses beginning with fffff6 in the page table dump are associated to page directory structures. For example, the leaf containing the Page Table Entry (PTE) for virtual page 0xfffffffffffe0000 is:

$ python -c 'print(hex((0xfffffffffffe0000 & 0xfffffffff000) >> 9 | 0xfffff68000000000))'
0xfffff6ffffffff00

The dump contains:

fffff6fffffff000 (  1000) -> 1f4000
fffffffffffe0000 (  1000) -> fee00000

As fffff6ffffffff00 is offset f00 of page fffff6fffffff000, which is mapped to 1f4000, the entry for fffffffffffe0000 is the 8 bytes at 1f4f00:

0001f4f00: 7b01 e0fe 0000 0000

So the PTE is in fee0017b, which means “physical page fee00000 with flags 17b” (bits: 0=present, 1=writable, !2=non-userspace, 3=page write through, 4=page cache disabled, 5=accessed, 6=dirty, 8=global).