Attacking Password Managers: KeePass

In this two-part blog post, Ill be taking a look into attacks password managers to improve my knowledge on techniques which can be used against them. To start with, I will take a look at local password managers by looking at the sort of techniques which can be used against KeePass. The second post covers some attacks against LastPass.

Ill start by covering some generic attacks against password managers (Both local and cloud-based), before moving onto KeePass-specific attacks.

  1. Generic Password Manager Attacks
    1. Monitoring/Grabbing the Clipboard contents
    2. Keylogging
  2. KeePass Attacks
  3. KeeThief
    1. Finding KeePass files
    2. Stealing the master password from memory
    3. Backdooring the trigger system
    4. Cracking A KeePass file
    5. Additional Persistence Methods

Generic Password Manager Attacks

Monitoring/Grabbing the Clipboard contents

We can do this within Cobalt Strike using the clipboard command. This is only a point-in-time grab of the data, and will not actively monitor it!

We can also use Mimikatz to do this with misc::clip, but this is way overkill for most situations

Keylogging

If we are feeling patient (or want to coerce the target into opening their password manager), we can grab the master password by keylogging their system to capture the main password.

KeePass Attacks

Now lets look into some KeePass-specific attacks. This was all performed against v2.52 which was the latest at the time! This was installed with all the default settings, and the TopSecretStuff.kdbx file is using a password of ‘infected‘.

And lets add a password or 2 to our KeePass database

KeeThief

KeeThief is another great tool from GhostPack. It was originally released 7 years ago and targets .NET 3.5, which meant I had to install the framework on my test system. For real-world usage, make sure you adapt this to your target environment!

I then had to combine the Microsoft.Diagnostics.Runtime.dll DLL into the KeeThief executable and make the entry point public to avoid getting an “Invoke_3 on EntryPoint failed” error in Cobalt Strike. To do this, I used ILMerge which was downloaded as part of building KeeThief. Using the csproj file from KeeThief for guidance, the final command was:

PATH_TO_KEETHIEF\KeeTheft\packages\ILMerge.2.14.1208\tools>ILMerge.exe /target:winexe /targetplatform:"v2,C:\Windows\Microsoft.NET\Framework\v2.0.50727" /target:exe /out:test.exe "PATH_TO_KEETHIEF\bin\Release\KeeTheft.exe" "PATH_TO_KEETHIEF\bin\Release\Microsoft.Diagnostics.Runtime.dll"

I also did the same for the associated PowerShell file which this should produce, using the command:

PATH_TO_KEETHIEF\PowerShell>powershell -exec bypass -File Out-CompressedDll.ps1 KeeThief.exe KeeThief.ps1

The commands are covered in the README of KeeThief

Finding KeePass files

Using the pre-built KeePassConfig.ps1 file, we can hunt in a system for KeePass XML files. These XML files contain the configuration data for KeePass which we will modify later on as part of an attack!

Stealing the master password from memory

We can now simply run the KeeThief tool against our system, and so long as KeePass is running, we can steal the plaintext master password. Notably KeePass just needs to be running on the system and be unlocked with the main password – a pretty typical arrangement for most users who use KeePass!

We can also do this using the KeeThief.ps1 script if we want to throw it back to 2017, with the Find-KeePassDatabaseKey cmdlet.

From this point, we could steal the TopSecretStuff.kdbx file, and interact with it away from the host.

Backdooring the trigger system

As mentioned in an older Mandiant report, KeePass includes a ‘trigger’ system which can be exploited. In short, this trigger system allows specific actions or commands to be run when specific criteria are met in KeePass. Mandiant’s example is to dump out the KeePass DB whenever KeePass is opened. On the 23rd January 2023, CVE-2023-24055 was released for this, but is disputed by KeePass.

As covered in the comments of KeePassConfig.ps1, we need to pipe the output of Find-KeePassConfig into Get-KeePassConfigTrigger for it to work.

This didn’t work with me, after a bit of debugging I believe the structure of the KeePass config has likely changed since the tool was created, or the tool had revealed a false positive file. Below is an example of the error within the Add-KeePassConfigTrigger function

After manually creating a Trigger on the target system, the C:\Program Files\KeePass Password Safe 2\KeePass.config.xml file had not changed and was essentially an empty XML file

<?xml version="1.0" encoding="utf-8"?>
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Meta>
   <PreferUserConfiguration>true</PreferUserConfiguration>
  </Meta>
</Configuration>

This would explain why PowerShell was throwing an error, as there were no XML elements for it to add nodes onto! With a bit of hunting, I found the file at %APPDATA%/Roaming/KeePass/KeePass.config.xml

We can now pipe this file path into Find-KeePassConfig and then into Add-KeePassConfigTrigger like so:

"C:\Users\vagrant\AppData\Roaming\KeePass\KeePass.config.xml" | Find-KeePassConfig | Add-KeePassConfigTrigger -Verbose

We can confirm this by looking at the KeePass.config.xml file after running the command

Before we restart KeePass, note that there are no files in the %APPDATA%/Roaming/KeePass folder

After signing into KeePass, a file named TopSecretStuff.csv is dropped into the folder

Which we can open to reveal our passwords

We can then remove our configuration change by running Remove-KeePassConfigTrigger

Cracking A KeePass file

The above attacks have relied on us compromising a system where the user is either actively using KeePass, or will routinely use it. What if we come across a user which only infrequently uses it, or find a *.kdbx file on a share?

For this, we can use the keepass2john executable from John The Ripper – which is built into Kali. If you are using a different OS, then Harmj0y has made a Python script to do this!

We now have our hash:

Looking at the example hashes from Hashcat (Mode 13400), we can see that we will need to remove the database name ‘TopSecretStuff‘ from the beginning of this string

We can then run Hashcat using mode 13400 to try and crack this

hashcat.exe -a 0 pass.txt rockyou.txt -m 13400

Additional Persistence Methods

Something which I read about, but didn’t attempt was obtaining persistence via the KeePass plugins, which is covered by @two06 on Medium, they also have another post which covers another way of abusing the trigger system to execute arbitrary code, in a similar way to KeeThief’s persistence method.

In my second post in this series, I will take a look at LastPass as an example of a cloud/browser-based password manager!

Shodan 201: Rummaging Around The Internet

Shodan is a well known resource which is one of my go to tools for OSINT and technical enumeration. This post will cover how we can use Shodan filters in a slightly more advanced way to hunt for resources.

I will start by covering some of the basics, before diving into filters and then ending by covering some more complex queries.

Overview

At a very basic level, Shodan routinely scans every port on every host on the internet and then ingests this into their platform. This means we can query _the whole internet_ to find details on hosts. This has a few advantages:

  • As an attacker, as we can passively enumerate targets without having to actively interact with them
  • This data is gathered continuously by Shodan, so it is always quite up to date
  • All of this can be queried through the website, or API
    • With the API, we can automate this.. 😉

Without an account we can still do some limited searching but the real value comes from having a paid account so we can use filters. In the past, they have had a sale on Black Friday.

This post will assume we have a paid account, but as a free user, we can still search for phrases, such as those which contain the word hacked in their response.

And we can also look up specific IP addresses, such as Google’s DNS server at 8.8.8.8:

Filters

Shodan has a page which lists all of the filters available. I wont delve into all of them in this post as some are fairly straightforward, and Shodan itself has a great reference page for this purpose. Instead I will look at some of the more interesting ones.

Screenshots

There are several ways of querying for screenshots in Shodan. We can check if a host has an associated screenshot with the has_screenshot parameter:

has_screenshot:true

We can also search for text within screenshots using screenshot.label. For example screenshot.label:desktop

Searching for labels using this filter doesn’t always return a complete set of results from my experience, luckily we can also query for them from images.shodan.io.

SSL Certificates

From my experience, searching for SSL certificate hostnames in Shodan is a great way of identifying hosts based on SSL information. We can do this with the hostname filter in Shodan. For example, we will look for hosts using an SSL certificate containing contoso.com:

We can also combine multiple hostnames together which can help to reduce the number of queries made to Shodan! For example we can hunt for contoso.com and example.com with the following query:

hostname:contoso.com,example.com

 We can also look within an SSL Certificate for part of the issuer CN or subject CN. For example, hosts with a certificate issued by test.local

And searching by subject name

HTTP Content

As expected, we can look within the HTML content of pages to find results. This has previously been done with the R1Soft vulnerability found by Huntress in early November 2022. Using their query of http.html:"Server Backup Manager SE" we can see that at the time of writing this post, 3,192 backup appliances remain online – showing that their vulnerability has led to nearly 2,000 backup devices being removed from the internet.

As an example, we can look for other queries which can identify other R1Soft devices. As an example, we could search for the favicon to find other systems which might not include the string “Server Backup Manager SE“.  To do this, we can click on this host and click on the Raw Data tab at the top of the page

 After digging through the data, we find the hash is -450254253, which we can then hunt for using http.favicon.hash:-450254253, revealing a further 1,000 devices which could well be additional backup servers (Though we would want to further validate these results!)

With this query, we can use the ‘Historical Trend’ tab to show historical data on this query. This shows that the number of devices exposed to the internet has been gradually decreasing since 2018, with a sharp decline since the vulnerability was released

Continuing with the R1Soft theme, the core vulnerability was due to an authentication bypass (CVE-2022-36537) within the ZK framework. We can also search Shodan for sites using ZK with the http.component:zk query. We can see this returns over 6,000 results and includes some R1Soft systems

More Complex Queries

With Shodan we can combine several queries together to perform some complex searches. For this I will start with the R1Soft systems from above. We might be interested in finding which systems could be vulnerable to CVE-2022-36537 within the ZK library, but ignore any R1Soft systems (As they will be obviously patched by now..).

To do this, I will combine the http.component and then ignore any results from the http.favicon.hash query results, which we performed earlier, giving us the following query: http.component:zk -http.favicon.hash:-450254253

Finding Interesting Hosts For A Target

This is where Shodan shows its power – we can search for hosts for a target which have interesting ‘stuff’ related to them. For example, we can combine the hostname filter (To find hosts based on their SSL certificate) and the port filter (To find those with SSH exposed).

Assuming our the internal domain name of our target was ‘domain.local‘, we can find 12 potential hosts with SSH exposed with the query hostname:domain.local port:22.

In a similar vein, I also quite like to perform that search but ignore both port 80 and 443 with -port:80 -port:443. We can also shrink this query by doing -port:80,443 instead!

Using our fictitious target of ‘domain.local‘, this search reveals 53 hosts which have an SSL certificate for domain.local and are exposing what I would consider as non-standard port

Find potentially unmanaged devices

Another useful one is to ignore a ‘range’ of IP addresses (Whether this is via CIDR or an ASN) and then search for your targets name via some means (HTML content, SSL Certificates and so on).

For example, looking for a Microsoft term within the HTML content (http.html:contoso) and ignoring any hosts which are within an ASN which has Microsoft in the name (-org:microsoft). This search is shown below:

Or in a similar vein, we can search for hosts with a certificate subject name containing ‘contoso‘, and ignore any SSL certificates with a hostname containing ‘microsoft.com‘, making a query of: ssl.cert.subject.cn:contoso -hostname:microsoft.com

Tracking Assets Over Time

Another way of using the ‘Historical Trend’ tab is to track individual assets over time by effectively performing a basic historical search. For example, lets assume we want to find all the hosts which have exposed their CLR version via a header. To do this, we will take one of the examples from above and append a query for ‘X-AspNet-Version‘ headers, giving us a current set of 15 devices:

ssl.cert.subject.cn:contoso -hostname:microsoft.com X-AspNet-Version

Now what if we want to see if any other hosts would have met that criteria in the past? We can do that by going to the Historical Trend tab. As we can see below, the number of devices has varied significantly over the previous 5 years:

From here we can click on ‘Facet By’ and select IP to get a list of all the IP addresses which constitute the data in the graph above

Unfortunately we cannot access the hostname value, but we can also access the ssl.cert.subject.cn parameter which might reveal some hostname information

This data might provide a starting point for us to discover which assets have revealed that header.

Summary

Hopefully this post has been of use, below are some other blog posts which I found helpful:

Hack The Boo

Hack The Boo was a Halloween themed CTF from Hack The Box. I could only dedicate a few hours to this, but still managed to solve 3 machines. Below is a quick writeup on the machines I did:

Evaluation Deck

First off, I downloaded the supplied files from the CTF site

/conf/supervisord.conf shows that we will be running /app/run.py using Flask, a web framework.

In the run.py file, we import and run the app function within application/main

From this file, we now import 2 functions (web and api) from application/blueprints/routes, which are hosted at / and /api respectively.

Finally, we find the vulnerable function. We can POST JSON data to the /get_health URL, which ultimately is passed through exec to perform a calculation on line 28.

We can see that we need to provide 3 values: current_health, attack_power and operator. On line 27, current_health and attack_power are cast to ints, so we will abuse the operator value to inject code.

I will spin up a VM and intercept a request using Burp Suite & FoxyProxy. We can see that when we click on a card in the UI, it will make a POST request to get_health.

I will send this request to Repeater, so we can modify the request. My initial thought here was that we could insert a command into the middle of the request and concatenate our current_health and attack_power variables to the end. For example, using the values above it would result in an output along the lines of 100CommandOutput22.

After running the code locally, it became apparent that this wouldn’t be possible due to us attempting to concatenate a string to an integer. Therefore I looked a little deeper, and saw the response function was taking the result value of the output and returning it as a response. We can abuse this by setting our own value to the response variable. We will do this by supplying an operator value of ‘; result = ‘. We can see below that we are effectively generating a JSON response of “result = 123; result = 456″. The server then returns the 456 value.

Now we can control the output, we will import os.popen to run system commands and leverage this vulnerability into an RCE. Using os.system here will run the command, but the returned value is the exit code of the process – not the output of the command!

We now need to import and run a function in one line. Typically in Python we would import our libraries such as ‘os‘ at the top of the file and call the later, but we don’t have that luxury here!

To do this, we will use the __import__ function. I always forget the structure of this, but SethSec had it on their blog:

__import__('os').popen(r'COMMAND').read()

Putting this all together, we have the following value for ‘operator’:

; result = __import__('os').popen('whoami').read(); a =

We will run this against the box:

By running some basic commands, we can see that the flag is in the parent directory to where we are being executed from.

And we have our flag, using the operator parameter of:

; result = __import__('os').popen('cat ../flag.txt').read(); a =

Wrong Spooky Season

This was the first forensics challenge to be released, with only a single PCAP file provided. I loaded this into WireShark and could see that the file contained HTTP traffic which immidiately grabbed my attention due to it being in plaintext.

Scrolling through the results, we can see there is some seriously suspicious looking requests!

This final GET request appears to be the point at which the ‘attackers’ established a form of reverse shell using Socat. If we sort by frame number, we can see a session is created and then the subsequent frames contain the communication between the attacker and the server.

If we right click on frame 466, we can then select Follow -> Follow TCP Stream to investigate the traffic further. This confirms my suspicion that this is a reverse shell, as we see common enumeration commands being run:

At the end of this output we can see a string which caught my attention. The double equals sign makes me think it is likely base64 encoded. This value is piped into ‘rev‘, so it is likely to be reversed as well.

Using CyberChef to reverse and decode this, we can reveal the flag!

Pumpkin Stand

This challenge was very frustrating for me, as I do not normally do the pwn or reversing challenges! I very much went down the wrong route here, but I figured it would be worth including it anyway.

First off, we have some files we can download locally, and a docker instance. The docker instance shows the command line output of a program.

Inspecting the files, we have an ELF executable

Which when we run it, we are prompted with the same output as the website

I tried running ‘strings pumpkin_stand‘, which suggests that the binary is reading from the flag.txt file.

At this point, I figured the solution was likely to revolve around debugging the program and manipulating it into revealing the flag. My initial ideas revolved around altering the pumpcoins variable which dictates the number of coins we have available to spend.

To do this, I loaded the binary in GDB and dug into it. Some commands of use were:

CommandUsage
b *function_nameSet a breakpoint on the ‘function_name’ function
disassShow current call stack
info break (i b)Show all break points
del [breakpoint_number]Remove a breakpoint
set {int} 0x12345678 = 10000Set a variable

Continuing with my idea of modifying the number of coins we have, I set the pumpcoin value to 10000 using set {int} 0x555555602018 = 10000

After removing my breakpoint and resuming, we can see we now have 10000 coins!

I purchased the laser, but it did not reveal the flag for me. I then attempted all manner of different combinations but couldn’t make any progress.

After stepping away from the challenge and speaking with a colleague, I realised that I should have focused more on the Docker container! We could connect to the docker container over a plaintext socket. When we had connected to this, we could simply enter in incorrect values and the binary would not catch the error and instead show the flag.

I believe a successful combination was to buy item ‘3’, which would not be handled correctly.

Summary

Overall, Hack The Boo was another excellent CTF organised by HackTheBox. Hopefully next time I will be able to dedicate more time to it and try to get a bit higher up the leaderboard!