kudelskisecurity / volatility-gpg

Volatility3 plugin to extract secrets from gpg-agent
GNU General Public License v3.0
8 stars 0 forks source link

No Matches from LiME or AVML Captures #2

Open sempervictus opened 1 year ago

sempervictus commented 1 year ago

I am attempting to recover a long-cached passphrase for a signing key from a GPG agent which was initialized back in 2020. Built a 5.15.93 debug kernel, got dwarf2json from it, and verified that banners on captures works correctly. I shut down the VM, start it cold, execute the signing command which unwraps the GPG key and verify that it runs correctly, then perform the capture (insmod lime... or avml). Running /vol.py -vvvv -s ~/tmp/gpg-dump/sym/ -f ~/tmp/gpg-dump/out.lime -p . linux.gpg_full --epoch <time of VM boot> doesn't find anything, neither does partial. I expanded the search window to cover the keychain's existence:

diff --git i/linux/gpg_full.py w/linux/gpg_full.py
index f58d58b..5614478 100644
--- i/linux/gpg_full.py
+++ w/linux/gpg_full.py
@@ -119,7 +119,7 @@ class GPGItem(plugins.PluginInterface):
             if epoch is None:
                 epoch = int(time())
             epoch_start = strftime("%d %b %Y %H:%M:%S", localtime(epoch))
-            epoch_end = strftime("%d %b %Y %H:%M:%S", localtime(epoch + 0xffffff))
+            epoch_end = strftime("%d %b %Y %H:%M:%S", localtime(epoch + 0xfffffff))
             print(f"Searching from {epoch_start} to {epoch_end}")
             byt = (epoch >> 24).to_bytes(5, "little")
             regex_pattern = b'.{3}' + byt + b'.{3}' + byt + b'\x58\x02\x00\x00'

and fed it --epoch 1423666015 still to no avail. Libgcrypt iis:

$ pacman -Qi libgcrypt
Name            : libgcrypt
Version         : 1.10.1-2
sempervictus commented 1 year ago

I disabled RANDSTRUCT, ASLR, STACKLEAK, and built off of upstream with no patches. Running a sequence of date +%s ; uname -a ; key_use_script.sh ; insmod lime-5.15.93 "path=/srv/out.comp.lime format=raw dio=1 compress=1" in order to get the epoch, throw a string identifying the kernel into memory, and then using the cached passphrase for the key right before capture yields the same result :confused:. System is running pretty bare when this executes on a fresh boot - ~260MB of used (out of 24G). I made sure to pkill gpg-agent prior to execution to try and keep everything reasonably adjacent.

amietn commented 1 year ago

Hello @sempervictus . Thanks for testing and using our plugin! We have used this command for the memory dump:

insmod lime-$(uname-r).ko "path=/memdump.mem format=lime"

Note that we did not use compress=1, nor dio=1. We dumped to lime format, not raw format. Maybe the fact that the dump is compressed is the problem? Does the linux.pslist module work with your dump and does it list the gpg-agent process?

The epoch_end is only used for printing purposes but does not alter the search window. By the way, we spotted an error in the computation of epoch_end which was printed out and we pushed a fix https://github.com/kudelskisecurity/volatility-gpg/commit/10a50f0b9c08dfd62c72d9dc5794b1926691564b With the previous version of the code and using the timestamp you mentioned (1423666015), the plugin would print the following:

Searching from 11 Feb 2015 15:46:55 to 24 Aug 2015 21:07:10

However it would only search from Wed Feb 11 2015 14:46:55 GMT+0000 to Wed Mar 11 2015 08:42:39 GMT+0000. This should be now fixed.

You mentioned gpg-agent was initialized in 2020. Is this really the search period you wanted to search in?

If it's not sensitive, could you please share the memory dump with us so we can have a look at it?

sempervictus commented 1 year ago

Thanks for pinging back @amietn - i tried uncompressed lime format dumps w/ the LiME module and using AVML, same effect (they are also decompressed prior to processing and dio just avoids writing to the intermediate caches attempting to get data to persistent storage ASAP). It looks like Volatility3 has some issue parsing AVML's Snappy-compressed lime format, and it can't actually deal with the LiME module's compression format at all (so that's not the problem here). Unfortunately the data is is real, for an actual signing key - so i cannot share the collected buffers, but can go through how to make a reproducer:

  1. Create a VM with a clock a few years in the past - in this case, a ~3y-old Arch Linux base image with the relevant GPG pieces (pretty sure an old archiso will work).
  2. Create a root key with a passphrase, create a child signing key with a passphrase.
  3. Inform the agent of the passhprases and use it to sign something with the child key to ensure that the agent is aware.
  4. Shut down the vm and bring the clock forward to current, update to current packages, install properly instrumented and defanged debug kernel - pre v6 for now while they sort out the whole maple tree thing (mm_struct changed, no more easily iterable linear list of VMAs)
  5. Attempt to collect the phassphrase(s) provided to the agent in step 3 having full root access to the system volume and agent state storing the keychain.
sylvainpelissier commented 1 year ago

Hi,

It seems you shut down the VM after step 3 thus you would lose the cached items of GPG. When does you dump the memory exactly ? What is the output of linux.pslist module in step 5 ?