Challenge info:
We are certain that our internal network has been breached and the attacker tries to move laterally. We managed to capture some suspicious traffic and create a memory dump from a compromised server. I hope you are skilled enough to bring this incident to its end.
We are given two files — a PCAP and a memory dump file.
PCAP
Wireshark shows us that the PCAP contains SSH traffic between several hosts with one of the streams involving OpenSSH 7.6.
We can see that the SSH communication was established using a SSH-2.0-OpenSSH_7.6
protocol and software version.
We also see the encryption and key exchange algorithm used were aes256-gcm
and curve25519-sha256
respectively.
Other than the initial key exchange information, there’s nothing much else to gather from the PCAP. Let’s move over to the memory dump file for now.
Volatility Profile
Volatility is a well known open-source memory forensics framework, and would be an excellent tool to analyze our memory dump.
Before we can perform the analysis, we need to provide Volatility a profile of the target system to ensure its tools know how to locate and parse the system-specific kernel data structures of the dump.
Lucky for us, the Volatility wiki provides a handy guide that covers the necessary procedure.
We’ll first have to determine the OS and kernel version of the dumped system, and if we already have a pre-built profile for that.
Unfortunately, we don’t see any pre-built profile matching an Ubuntu 18.04.5 version with a 4.15.0–142-generic
kernel.
As pointed out by the wiki page, the alternative way is to replicate the target environment and build our own profile from it.
Download the Ubuntu 18.04.5 ISO image from the releases page and complete the installation.
Once installed, check its system information. We’ll have to upgrade the kernel version from -122
to -142
to match our target environment.
The pkgs site reveals to us the package name to install our desired kernel version — linux-image-4.15.0–142-generic
.
Install the package and reboot the system, and you’ll see that our Ubuntu machine matches our target system version.
Finally, follow Andrea Fortuna’s excellent guide to generate a Volatility profile from our Ubuntu machine, and transfer over to our analysis machine.
Verify that Volatility can locate our target profile, and we are game!
Memory dump analysis
First things first, let’s gather some basic information about our target system.
So this dump belonged to a host on 192.168.1.11
and the host was acting as a SSH server for a 192.168.1.10
host. This matches one of the SSH conversations captured in our earlier PCAP file.
Process-wise, the suppoie
process of PID 1728 spawned from sshd
stands out. Based on its name, the supplied config file, and the IP the process calls out to, the process could potentially be attributed to a Monero miner malware.
These are all interesting, but none of these findings seem to point us anywhere closer to our flag.
Given the fact that we are given a memory dump of a SSH server serving one of the SSH conversations captured in the PCAP file, let’s see if we can extract anything useful from this dump to decrypt that traffic.
SSH Key Extraction
Google search revealed a blog post from NCC Group pointing to a Volatility community plugin that extracts SSH session keys from a memory dump. Excellent!
The plugin successfully extracts two session keys from the dump.
With the keys in our hands, let’s now decrypt the SSH traffic in our PCAP file. Conveniently, the author of the earlier blog post also provides a tool to do exactly this.
SSH Decryption
Unfortunately, the tool required a non-trivial amount of effort to set it up. Here are the steps I took to finally get it to working.
Environment Setup
Let’s create a virtual environment before installing any packages this tool may require:
python2 -m virtualenv venv
source venv/bin/activate
Dependency Install
From the root of the tool repository, set up the tool and install any dependency the tool requires:
The install fails with the message that it can’t download the pynids@
module.
I’m not too well-versed with Python’s setuptools
, but the pynids
line may not be written in the way setuptools
expects to be.
I followed the python-packaging documentation to move the pynids
URL into a dependency_links
key, but that led me to another error:
From the cryptography documentation, this occurs because building the module requires having a Rust toolchain, and that upgrading pip to the latest version should resolve it. Unfortunately, this problem consisted even after my pip upgrade on my python 2.7 environment. I could potentially have dug this path deeper, but I decided to cheat and install the required modules manually.
pip install dissect.cstruct==1.0.0 psutil tabulate gevent libnacl cryptography
As for pynids
, clone its repository, untar the libnids-1.25
archive in the root folder, and follow the instructions to build it.
If everything goes well, you should see a nidsmodule.so
under the build/
folder of the pynids
root folder. Copy the library file to your venv
packages folder. For me, it was venv/lib/python2.7/site-packages/
.
Finally, run the tool’s setup.py install
one last time. It still errors out but should work for our purposes.
Python patching
With all the dependencies ironed out, let’s format the earlier extracted keys into the expected keys.json
file, and run the tool.
Interestingly, we see from the traceback a syntax error on the usage of af-strings
, a Python3 construct, from thedissect.cstruct
module. This error makes sense because we are running the tool from a Python2 environment and the Python interpreter would not be able to understand any Python3 feature used in the tool’s dependency module.
I ended up patching all instances of the f-strings
into a Python2 compatible formatting construct, like format()
.
# Python3
code_object = compile(
source,
f'<compiled {structure_name}>',
'exec',
)# Python2
code_object = compile(
source,
'<compiled {}>'.format(structure_name),
'exec',
)
dissect.cstruct
module uses another Python3 construct — the “super()
method with no arguments”.
Fix all instances of this feature in the following manner too:
# Python3class PackedType(RawType):
"""Implements a packed type that uses Python struct packing characters."""def __init__(self, cstruct, name, size, packchar):
super().__init__(cstruct, name, size)
self.packchar = packchar# Python 2class PackedType(RawType):
"""Implements a packed type that uses Python struct packing characters."""def __init__(self, cstruct, name, size, packchar):
super(PackedType, self).__init__(cstruct, name, size)
self.packchar = packchar
Once they are all fixed, let’s run the tool again.
Ok, the tool still errors out, but it doesn’t look like a dependency issue anymore. We are making progress here.
Traceback tells us cstruct.py
encountered an unexpected token type string
, which was invoked from structdef.py
at line 243.
It turns out the tools’ structdef.py
had a typo in the C snippet.
The tool also had issues parsing the keys.json
file. I fixed the way the tool parses the json lines in the following manner:
Giving the tool another run, we finally begin to see it returning the key exchange metadata, but it fails to parse the session keys from the json file.
It looks like the tool expects to parse the keys.json
line as a dictionary, but it currently is not (but rather as a string, probably). I added the following line to allow the key
value to be interpreted as a dictionary.
Running the tool 🙏
We have finally tamed the tool to return us the decrypted SSH traffic!
We see the first half of the flag in the Password
field. The second half is base64 encoded in the command history.
Combining the two, we have the flag:
HTB{w3ll_1_th0ught_n0_0n3_w0uld_f1nd_m3!!!}