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]:
../_images/fileformats_SAPSSFS_10_0.png

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 Key content

[6]:
ssfs_key.canvas_dump()
[6]:
../_images/fileformats_SAPSSFS_14_0.png

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]:
../_images/fileformats_SAPSSFS_22_0.png

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