Hi SAP security enthusiasts,
the Security Audit Log [SAL] contains -obviously- information about important system events and should therefore better not get lost unintentionally!
Let's see, how the Audit Log can be erased and what we can do to prevent this and maximize its protection.
The Audit Log is stored in log-files that are located in the file system (see parameter DIR_AUDIT) and is either rotated daily or when the current file is full (see parameter rsau/max_diskspace/per_file).
Access to these files is possible via kernel functionality (from within the SAP system) or on operating system level (e.g. via an external OS command). We’ll focus on access via the SAP application server and won’t take a deeper look at manipulations directly from OS level (i.e. from the command line).
Ways to delete the Security Audit Log
From inside the SAP system, three variants of deleting the SAL files exist.
Below we'll check them and see, which measures exist to protect the logs.
Variant 1: SM18 (or function module RSAU_CLEAR_AUDIT_LOG or system function C_REMOVE)
SM18 is the SAP standard way of removing old SAL files.
The transaction is protected by an authorization check for S_ADMI_FCD with value AUDA (AUDit log Administration). The minimum age of files to be erased is 3 days − a nice feature, because an attacker cannot remove fresh logs and hide what he/she did moments ago.
SM18 is not really problematic… S_ADMI_FCD with value AUDA is a critical authorization, which nobody should be assigned to on production.
Diving a bit deeper…
A clever developer might try to copy the code that is responsible for the deletion to a customer report − fortunately this does not work... see below:
REPORT. WRITE 'SM18 (C_REMOVE):'. DATA: errno TYPE rsautypes-errno , errmsg TYPE rsautypes-errmsg. CALL 'C_REMOVE' ID 'DIR' FIELD '/usr/sap/<SID>/DVEBMGS00/log' ID 'FILE' FIELD 'audit_20160425' ID 'GROUP' FIELD 'AUDIT' ID 'ERRNO' FIELD errno ID 'ERRMSG' FIELD errmsg. IF sy-subrc = 0. WRITE: 'successful'. ELSE. WRITE: 'failed;', errmsg. " caller not registered ENDIF.
⇒ This report always runs into an error, because the SAP kernel checks the program name that calls the C_REMOVE function against an internal whitelist; we'll always get a "failed; caller not registered" message…
Variant 2: DELETE DATASET
A more sophisticated way of getting rid of audit log files is to simply delete them via the DELETE DATASET statement from within an ABAP report.
A simple example:
REPORT. WRITE 'DELETE DATASET:'. TRY. DELETE DATASET '/usr/sap/<SID>/DVEBMGS00/log/audit_20160425'. IF sy-subrc = 0. WRITE 'successful'. ELSE. WRITE 'failed / not found'. ENDIF. CATCH cx_sy_file_authority. WRITE 'missing authorization'. ENDTRY.
The kernel checks for S_DATASET authorizations each time a DELETE DATASET statement is executed (this cannot be prevented, since the check takes place in the kernel).
The field ACTVT is checked for value 06 (delete) and the FILENAME field for the path of the file to be deleted.
A check for authorization object S_PATH can be activated to implement an additional protection for file system accesses. It is then evaluated after a successful S_DATASET check.
Alternatively, we can also prevent unauthorized log file deletion completely… even if a user has S_DATASET with all field values set to "*"!
For the additional check, the logic is as follows (see SAP Note 177702):
- first, the system checks whether the SPTH table contains a PATH, which matches the start of the path name of the log file that we want to delete
- if an authorization group is set in the FS_BRGRU field, the system then performs an authorization check on S_PATH with ACTVT = 02 (write or delete)
Additionally, the table SPTH has two flags, which can further limit file access and overwrite the authorization checks on S_PATH and S_DATASET:
- if FS_NOREAD has the value "X", neither read nor write access is possible
- and FS_NOWRITE = "X" prevents write access
To effectively prevent Security Audit Log deletion, you can add the following entry to SPTH:
PATH = /usr/sap/<SID>/DVEBMGS00/log/audit_
FS_NOREAD = X
FS_NOWRITE = X
Afterwards, no user will be able to read or modify the SAL log files – disregarding their authorizations!
Anyway, SM20 will continue to work, as the access takes place in the kernel.
Variant 3: External operating system command
The third variant does not use the SAP kernel to delete the file, but rather an OS command (in the following example we’ll use the Unix/Linux rm command).
The following report creates a new external command, executes it to delete the same audit log file as above and finally deletes the command.
The creation and deletion of the command is logged via table logging for table SXPGCOSTAB… but at least at a first glance, we can hide what we did by deleting the command after execution…
REPORT. WRITE 'External command (SM69):'. DATA command TYPE sxpgcolist. command-NAME = 'Z_DEL_AUDITLOG'. command-opsystem = sy-opsys. command-opcommand = 'rm'. command-addpar = 'X'. CALL FUNCTION 'SXPG_COMMAND_INSERT' " --- Create command EXPORTING command = command EXCEPTIONS OTHERS = 1. IF sy-subrc = 0. WRITE 'creation successful;'. ELSE. WRITE 'creation failed.'. LEAVE PROGRAM. ENDIF. DATA exitcode TYPE extcmdexex-exitcode. CALL FUNCTION 'SXPG_COMMAND_EXECUTE' " --- Execute command EXPORTING commandname = command-NAME additional_parameters = '/usr/sap/<SID>/DVEBMGS00/log/audit_20160425' IMPORTING exitcode = exitcode EXCEPTIONS OTHERS = 1. IF sy-subrc = 0 AND exitcode = 0. WRITE 'execution successful;'. ELSE. WRITE 'execution failed;'. ENDIF. DATA: BEGIN OF command_del. INCLUDE STRUCTURE sxpgcostab. DATA: sapcommand TYPE sxpgcolist-sapcommand , TYPE(8) TYPE c , END OF command_del , rc TYPE i. command_del-NAME = command-NAME. command_del-opsystem = command-opsystem. PERFORM command_delete(saplsxpt) " --- Delete command USING SPACE CHANGING command_del rc. IF sy-subrc = 0 AND rc = 0. WRITE 'deletion successful.'. ELSE. WRITE 'deletion failed.'. ENDIF.
Of course, you could also create, execute and delete the command via transaction SM69.
The above method works fine, if you have S_RZL_ADM (ACTVT = 01) for the creation of the command and S_LOG_COM authorizations for execution.
Unfortunately, the SPTH-protection from variant 2 does not work here.
If table logging is enabled (profile parameter rec/client), changes are recorded in table DBTABLOG and can be evaluated via transaction SCU3 (for table SXPGCOSTAB).
All three variants can be protected quite well via authorizations, so log deletion can be prevented by strictly controlling these authorizations.
Anyway high-privileged users – e.g. emergency users – still have access, so I suggest also implementing the SPTH-protection described in variant 2. 💡 It provides a good additional line of defense around the log files.
Please keep in mind that you should also take care of protecting table SPTH itself… 💡
Apart from the access perspective, organizational measures should be implemented to make sure that malicious code like to one above is checked (and refused) before a transport to production takes place!
Currently a lack of time prevents me from finishing a post, which is in the pipeline for weeks now. Nevertheless I'd like to point out a behavior of Security Policies, which might lead to an unexpected situation, if you're not aware of it (fortunately you will be).
Let us assume that you have hardened your system and set the password-related profile parameters login/min_password_lowercase = 1 and login/min_password_uppercase = 1. This forces users to include at least one lower- and one uppercase character in new passwords. The kernel default for both parameters is 0.
Additionally you set the parameter login/min_password_lng = 10 to enforce a minimum password length of 10 characters.
Unfortunately, there is an RFC user on your system, which must – for whatever technical reason – not have a password longer than 8 characters. As you don't want to lower the system setting, you choose to create a new SECPOL with just the attribute MIN_PASSWORD_LENGTH = 8 and leave the profile parameter as it is (= 10). The desired result is obviously to override the minimum password length with a lower value for just the one RFC user and stay with the more secure value for all other users.
The SAP Help Portal says:
… you create security policies with attributes, for which you explicitly do not want to use the default value.
So my initial expectation was, that a SECPOL attribute overrides the corresponding profile parameter (in our example MIN_PASSWORD_LENGTH overrides login/min_password_lng)… and all other profile parameters, for which SECPOL attributes exist, stay as they are set in the profile.
So login/min_password_lowercase could potentially be overridden by MIN_PASSWORD_LOWERCASE – but since we did not add it to our SECPOL, one could assume that the profile value stays intact.
Security Policies do not override profile parameters - rather they replace them! All of them.
All profile parameters, for which a SECPOL attribute exists, are replaced – so if an attribute is not explicitly set in a SECPOL, the kernel default value is used! Not the profile parameter value.
The bottom line
If you've set any of the profile parameter values, which can be influenced by a Security Policy, you have to make sure that all those profile parameters are explicitly set again in all of your SECPOLs!
So once you choose to make use of SECPOLs, you have to maintain all values at least twice: in RZ10 and in your Security Policy/ies… otherwise you end up using default values by mistake.
Yep... unspectacular... but still interesting 🙂
Some time ago, I posted a small C++ program that decompresses the source code stored in table REPOSRC
(⇒ check this article).
It was never intended to be more than a proof of concept, but since many people showed interest in it (and found bugs), I decided to create a GitHub repository:
It contains the history of my ancient CVS repository (converted thanks to this great how-to).
So… if you find any bugs or want to help improve it — please go there and either create an issue or simply fork it and unleash the developer in you).
In this article, we'll take a look at a potential weakness that is often considered minor and thus underrated – but might still be used to lever out important security measures: parameter transactions.
They basically execute another tcode… along with pre-defined screen input – the "parameters".
For example, the transaction code SM30_PRGN_CUST is a shortcut to SM30 for the maintenance view PRGN_CUST:
When you call it, SM30 is executed and table PRGN_CUST is opened in maintenance mode. Since the option "Skip initial screen" is enabled, it jumps directly into the table maintenance view itself – if it was disabled, one would be able to override the given screen options in the SM30 dynpro… including the table name! That's the point where the weakness starts.
Now let's select some more parameter transactions from table TSTCP.
They start with either "/*" or "/N" — the first one skips the called tcode's initial screen, the latter one doesn't.
Below you can find the selection options for all SE38 parameter transactions:
Now let's have a look at the lines marked red and green in our result:
The first one – SE38L – is unsafe, because the "/N" indicates that the SE38 selection screen is just filled out – but the report name can be overridden easily.
This means that SE38L is equivalent to SE38 ❗
Well… for SE38L, -M, -N this might not be a big surprise, but what about RBDCPCLR? … I never would have guessed!
The second sample – SE38N – calls SE38 and executes report RDELALOG immediately: nothing to see here, move along.
- When you limit access to a transaction, always keep in mind that insecure parameter transactions might represent a backdoor.
- Customer parameter transactions should always use the "Skip initial screen" option.
See you soon!
User and authorization administration in the Java stack is a pain in the neck – that's a fact! The identity management tools are inferior as compared to the ABAP stack, but nevertheless there are ways to make your life a bit easier…
Mass user maintenance in the UME
When it comes to mass user creation/modification in the Java UME (database only, no ABAP- or LDAP-data source), no tool like SU10 exists and many admins choose the hard way of creating users one by one... but wait... the "Identity Management" screen has an "Import" button:
Standard Format for UME imports
The screen behind that "Import" button provides not much more than a text field, which needs to be populated with user master data in the correct format (btw.: the amount of importable data is limited to 1 MiB).
The import format is documented here, but SAP provides no easy solution to create data in that format.
This is – you probably guessed it – the point, where my solution comes in.
Generally speaking, the "Standard Format" – as SAP calls it – is similar to the format of many .ini files and thus quite simple.
It can be used to create and modify users, groups, and roles — for users, a typical import record looks like this:
Squeeze mass user data into the Standard Format
For this task, I've prepared a very simple Excel file for you… download it here:
You can insert the user name, first and last name, password and up to three roles for up to 100 users into column A-G.
The formula in column H generates the expected format from the input data.
When finished, simply mark the cells in column H starting from line 2 (i.e. without the header).
Unfortunately, Excel is a very smart tool 😕 and automagically inserts quotation marks around the copied cells.
You need to remove these quote signs manually from the copied data…
Alternatively, you can also copy the clipboard's contents into an empty Word document, then copy everything again – that way the quotes are removed, too.
Afterwards, paste the data into the text area on the Import screen of the Identity Management; then click "Upload".
The protocol on the next screen contains information about the import result.
See you next time!