Closed gvb84 closed 1 year ago
Hi @gvb84 Thanks for reaching out. As far as I know, there should be no difference between SSFS and rsecsffs decryption. Can you name any difference of the systems where it worked and where it didn't work regarding the CommonCryptoLib version and provide the version details if so? Is there any chance you'd be able to do some known plaintext testing such as adding a new entry with rsecssfx and then checking if the plaintext value is the same after decryption?
Hi all,
There are two flavors of .KEY files.
The one of hdbuserstore
vhcala4hci:a4hadm 38> hdbuserstore list
DATA FILE : /home/a4hadm/.hdb/vhcala4hci/SSFS_HDB.DAT
KEY FILE : /home/a4hadm/.hdb/vhcala4hci/SSFS_HDB.KEY
which is 92 bytes long
vhcala4hci:a4hadm 40> ls -l /home/a4hadm/.hdb/vhcala4hci/SSFS_HDB.KEY
-rw-r----- 1 a4hadm sapsys 92 Jul 21 08:40 /home/a4hadm/.hdb/vhcala4hci/SSFS_HDB.KEY
and the other of rsecssfx
vhcala4hci:a4hadm 35> rsecssfx info
Secure Storage in the File System Management Tool
=================================================
File Locations
--------------
Data File : /usr/sap/A4H/SYS/global/security/rsecssfs/data/SSFS_A4H.DAT (Exists)
Global Key File : /usr/sap/A4H/SYS/global/security/rsecssfs/key/SSFS_A4H.KEY (Exists)
Local Key File : /usr/sap/A4H/SYS/global/security/rsecssfs/key/SSFS_A4H.LKY (Does Not Exist)
which is 187 bytes
vhcala4hci:a4hadm 41> ls -l /usr/sap/A4H/SYS/global/security/rsecssfs/key/SSFS_A4H.KEY
-rw-r--r-- 1 a4hadm sapsys 187 Aug 30 09:35 /usr/sap/A4H/SYS/global/security/rsecssfs/key/SSFS_A4H.KEY
So, for the hdbuserstore
SAP didn't bother to encrypt the key and the class SAPSSFSKey
just reads it as it is stored unencrypted.
The rsecssfx
implementation for the key is different. Here the key is stored encrypted.
The .DAT files itself are the same format, one just needs the right key and below is a way how to get it. It was done as Proof of Concept. Propoer implementation in pysap is still needed .
Add this to __init__.py
def rsec_decrypt_key(key_enc):
kek = "\x9F\x60\xA6\xDD\x7E\x15\x7D\x07\x0C\xC3\x57\x90\x9A\xA2\x90\xE9\x36\x0E\xEE\x47\x2F\xDA\x47\x72"
kek = [ord(i) for i in kek]
kek1 = kek[0:8]
kek2 = kek[8:16]
kek3 = kek[16:24]
""" Default Key Encryption Key embedded in rsecssfx/kernel binaries """
blob = [ord(i) for i in key_enc[:56]]
last_key_byte = bytearray(key_enc[56:])
""" Last key byte is computed outside DES decryption """
cipher = RSECCipher()
round_1 = cipher.crypt(RSECCipher.MODE_DECODE, blob, kek3, len(blob))
round_2 = cipher.crypt(RSECCipher.MODE_ENCODE, round_1, kek2, len(round_1))
round_3 = cipher.crypt(RSECCipher.MODE_DECODE, round_2, kek1, len(round_2))
t1 = [ord(i) for i in key_enc[48:56]]
tmp = cipher.crypt(RSECCipher.MODE_ENCODE, t1, kek3, 8)
last_key_byte = last_key_byte[0] ^ tmp[0]
tmp = cipher.crypt(RSECCipher.MODE_ENCODE, round_2[48:56], kek2, 8)
last_key_byte = last_key_byte ^ tmp[0]
tmp = cipher.crypt(RSECCipher.MODE_ENCODE, round_2[48:56], kek1, 8)
last_key_byte = last_key_byte ^ tmp[0]
return round_3[33:] + [last_key_byte]
in SAPSSFS.py
modify
# Custom imports
from pysap.utils.crypto import rsec_decrypt, rsec_decrypt_key
and add
class SAPSSFSKeyE(Packet):
"""SAP SSFS Key (encrypted) file format packet.
Key file length is 0xbb
"""
name = "SAP SSFS Encrypted Key"
fields_desc = [
StrFixedLenField("preamble", "RSecSSFsKey", 11),
ByteField("type", None),
TimestampField("timestamp", None),
StrFixedLenPaddedField("user", None, 24, padd=" "),
StrFixedLenPaddedField("host", None, 24, padd=" "),
# probably kind of check sum or just noise
StrFixedLenField("unknown", None, 62),
StrFixedLenField("key_enc", None, 57),
]
@property
def get_plain_key(self):
return rsec_decrypt_key(self.key_enc)
Here how to use it.
from pysap.SAPSSFS import *
import logging
logging.basicConfig()
sid = "A4H"
with open("/usr/sap/"+sid+"/SYS/global/security/rsecssfs/key/SSFS_"+sid+".KEY", "rb") as fd:
key = fd.read()
ssfs_key_e = SAPSSFSKeyE(key)
# create empty ssfs_key object
ssfs_key = SAPSSFSKey()
ssfs_key.key = ''
for x in ssfs_key_e.get_plain_key:
ssfs_key.key += chr(x)
#ssfs_key.show() is not going to work as other data is missing
with open("/usr/sap/"+sid+"/SYS/global/security/rsecssfs/data/SSFS_"+sid+".DAT", "rb") as fd:
data = fd.read()
ssfs_data = SAPSSFSData(data)
keys = set()
for k in ssfs_data.records:
keys.add(k.key_name.strip())
for key in keys:
data = ssfs_data.get_value(key, ssfs_key)
open(key.replace("/", "_")+"2", "wb").write(data)
I just tested @rstenet's code and it works like a charm. So it was indeed an additional encryption/obfuscation layer happening for apparently rsecssfs
only. Thank you so much @rstenet!
Edit: I've reworked the code a bit and put up a PR introducing the SAPSSFSKeyE
class. It works transparently so one does not have to do the copying anymore as in the proof of concept. Simply load the key data in the class via ssfs_key = SAPSSFSKeyE(fdata)
and then pass that to ssfs_data.get_value(key, ssfs_key)
and off you go.
I know it is working and with this decryption begins the real fun.
For example in /usr/sap/<SID>/SYS/global/security/rsecssfs/key/SSFS_<SID>.KEY
you find the key for the entries in RSECTAB
table.
In /hana/shared/<SID>/global/hdb/security/ssfs/SSFS_<SID>.DAT
are the HANA root encryption keys.
There is a chance SAP changes the encryption from 3DEES to AES in the future as they did it for AS JAVA recently, but till then enjoy.
Thanks @ret5et and @gvb84 for the collaboration to sort this out. Indeed the initial implementation was only focused on the hdbuserstore
client keys, but it's great to see that adding support for the additional file types was not that hard. This will enable the evaluation of additional post-exploitation scenarios if the keys then are exposed.
I'll take a look at the pull request and merge accordingly!
Thanks @ret5et and @gvb84 for the collaboration to sort this out. Indeed the initial implementation was only focused on the
hdbuserstore
client keys, but it's great to see that adding support for the additional file types was not that hard. This will enable the evaluation of additional post-exploitation scenarios if the keys then are exposed.I'll take a look at the pull request and merge accordingly!
it was not Dmitry Yudin (ret5et) even if the names look similar
Oh totally my bad! (I think the GitHub tab completer didn't help!)
Kudos go totally to @rstenet and @gvb84 for this one 😄
I've had success decrypting leaked SSFS files just fine based on the documentation. Think data managed by
hdbuserstore
. However I never managed to do this properly forclassic
rsecssfs files. Nothing I find online makes me think there should be a difference but maybe there is? I've been looking into getting entries with keys likeSYSTEM_PKI/PIN
andSYSTEM_PKI/PSE
decrypted but I cannot make sense of the output I get; just garbled data. For example forSYSTEM_PKI/PIN
I get 96 bytes that are seemingly random which does not square with the length of the random PIN that is generated by the SAP HANA binaries upon first use of these entries.It looks like either one of the following three option:
Just posting this here wondering if someone has a clue on this?
The code I use looks as follows with as input
SSFS_SID.DAT
andSSFS_SID.KEY
grabbed from a system from under/usr/sap/<SID>/SYS/global/security/rsecssfs/
:I then get output that the decrypted payload integrity is False. The outputted If I use
rsecssfx
I get:My main system output for the storage here looks as follows. It is those files that I have been attempting to decrypt: