SAP SSFS¶
The following subsections show a representation of the file format portions and how to generate them.
First we need to perform some setup to import the packet classes:
[1]:
from pysap.SAPSSFS import *
from pysap.utils.crypto import rsec_decrypt
from IPython.display import display
SSFS files¶
We’ll read the key and data files used in the test case suite and use them as example:
[2]:
with open("../../tests/data/ssfs_hdb_dat", "rb") as fd:
data = fd.read()
ssfs_data = SAPSSFSData(data)
with open("../../tests/data/ssfs_hdb_key", "rb") as fd:
key = fd.read()
ssfs_key = SAPSSFSKey(key)
SSFS files are comprised of the following main structures:
SSFS Data¶
[3]:
ssfs_data.show()
###[ SAP SSFS Data File ]###
\records \
|###[ SAP SSFS Data Record ]###
| preamble = 'RSecSSFsData'
| length = 179
| type = 1
| filler1 = ''
| key_name = 'HDB/KEYNAME/DB_CON_ENV '
| timestamp = '2019-11-26 16:15:40 UTC'
| user = 'SomeUser '
| host = 'ubuntu '
| is_deleted= no
| is_stored_as_plaintext= yes
| is_binary_data= no
| filler2 = ''
| hmac = '\x91\x9c\xbd&>U\xa3\x13\xdb\x11VG\xc0\xbb\x86\x9a:#\x07\x13'
| data = 'Env'
|###[ SAP SSFS Data Record ]###
| preamble = 'RSecSSFsData'
| length = 184
| type = 1
| filler1 = ''
| key_name = 'HDB/KEYNAME/DB_DATABASE_NAME '
| timestamp = '2019-11-26 16:15:40 UTC'
| user = 'SomeUser '
| host = 'ubuntu '
| is_deleted= no
| is_stored_as_plaintext= yes
| is_binary_data= no
| filler2 = ''
| hmac = '\xf4\x95\xb6(\xca\xb0\xa8t"V;T\xa5\xc8\xf3\xa7\xc5b\xf5\x08'
| data = 'Database'
|###[ SAP SSFS Data Record ]###
| preamble = 'RSecSSFsData'
| length = 184
| type = 1
| filler1 = ''
| key_name = 'HDB/KEYNAME/DB_USER '
| timestamp = '2019-11-26 16:15:40 UTC'
| user = 'SomeUser '
| host = 'ubuntu '
| is_deleted= no
| is_stored_as_plaintext= yes
| is_binary_data= no
| filler2 = ''
| hmac = ':\x85N\xf4\xe9\x8a\xbe\x93\xfc\x8f\xb5\x92\x91\x85\x9b\x8d!\xbd_r'
| data = 'SomeUser'
|###[ SAP SSFS Data Record ]###
| preamble = 'RSecSSFsData'
| length = 304
| type = 1
| filler1 = ''
| key_name = 'HDB/KEYNAME/DB_PASSWORD '
| timestamp = '2019-11-26 16:15:40 UTC'
| user = 'SomeUser '
| host = 'ubuntu '
| is_deleted= no
| is_stored_as_plaintext= no
| is_binary_data= no
| filler2 = ''
| hmac = 'pQ\xd7\x8e\xa4\x80\xc5\xaa\xca\xc1\xdc\xec\xcb\x0b\x9f\x0b;Jg\xfa'
| data = '\xdf\xc5ER=\xa0\xec\xe1\xcb\x8e39[A\x8f\xdc\xca\x14t/<\xa7d\x9c\xb9\x8b\x05\x05\x9dm\x9e\xdd\xe1\xb5+7\x9d\r\x006-\x90\xaa\x04\x1c\x12\xde\x8e\xb4\xf5\xcei\xb7.\xba0\xc6\xca\xe4\xbe\xb8p\xb6\x12r6\xd2\x12\xce\x9b\xb1-}\xd9Z\x96\xffFx\xd5T\xdah\xf7\xbf\xaf\xd0l\x8b\xffV\x0ba\x1e^\x11\x9b\xadyoP\xfdvV\xdf\x08\xa6\xbdc\xda\xfaU\xb5\xc0NC\\+\x03\x1c\xc2\xb0\x87{vi\x1f\xf9'
As can be observed, a SSFS Data file contains multiple records with different key/value pairs, as well as associated meta data.
Some records contain values stored in plaintext, while others are stored in an encrypted fashion. We’ll see a password record, which is stored encrypted:
[4]:
ssfs_data.records[-1].canvas_dump()
[4]:

Additionally, each SSFS record contains an HMAC-SHA1 value calculated using a fixed key. The intent of this value is to provide integrity validation as well as ensure that an authentic tool was used to generate the files:
[5]:
ssfs_data.records[-1].valid
[5]:
True
SSFS Value access¶
The values contained in SSFS Data records can be accessed by providing the key name:
[7]:
ssfs_data.get_value('HDB/KEYNAME/DB_USER')
[7]:
'SomeUser'
SSFS Data content decryption¶
For those records that are stored encrypted, it’s possible to access the right value by providing the key name and the proper SSFS decryption key structure:
[8]:
ssfs_data.get_value('HDB/KEYNAME/DB_PASSWORD', ssfs_key)
No handlers could be found for logger "pysap.ssfs"
[8]:
'SomePassword'
SSFS Decrypted Payload structure¶
The decryption mechanism can be user to obtain the raw data stored encrypted:
[9]:
decrypted_blob = rsec_decrypt(ssfs_data.get_record('HDB/KEYNAME/DB_PASSWORD').data, ssfs_key.key)
decrypted_blob
[9]:
"lWF\x96`c\xacc\x00\x00\x00\x0c\xfd\x9d<\xde2G\xe0\xf2\x8cE\xddj\xff\x8e\x12\\O\xea6\xc7SomePassword!\x19\xd3\x8d]\xab\xda\xb6\xd3\xc4\xbeK\x0e\x86\x00\xad'\xe6\xff%C.a\xd1vde\x1a\x88,N\xd3\x9e\x87\xab\xc3\xf2'U\xcd\xe2<\xeb65se4j\x0f\xbc\xa5V\xee\xe9M\xbc\xddx>\\\x18\xa9\x87\xdae\x12\xb4\x0c\x86\x89\x0fdG\xde%\xed4\xd7\x02\xf3Z\xed\xb5"
It’s possible also to parse that raw data and obtain the underlying strucutures and meta data associated:
[10]:
payload = SAPSSFSDecryptedPayload(decrypted_blob)
payload.canvas_dump()
[10]:

The decrypted payload contains a hash calculated using the SHA-1 algorithm, and that can be used to validate integrity of the entire payload:
[11]:
payload.valid
[11]:
True