Daniel Berlin on Security Insight on SAP security, development stuff… and all the rest

24Jun 12

Decompress ABAP source code from table REPOSRC

If you ever wondered, where SAP stores your report source code, you probably came across table REPOSRC – but the source code is stored in a compressed format, so there's no way to get hold of it directly.
I've been trying to figure out, how this compression works for some time now… fortunately, several people dealt with the SAP DIAG protocol recently – which got me off the ground.


The DIAG protocol uses a form of the Lempel-Ziv (LZ) compression algorithm and a bold attempt confirmed that this is also true for the source code compression.

The code stored in REPOSRC-DATA is actually compressed using the LZH algorithm (Lempel-Ziv plus Huffman coding), which is used by the SAP DB MaxDB database too (thanks to Dennis Yurichev for the idea).

Knowing this, I wrote a decompression tool around a small portion of the MaxDB code, which also takes care of some SAP specialties:

  • The 1st byte of the compressed data seems to be junk (or might have a special meaning !?)
  • The first 2 bytes of the decompressed source are junk, too ?!
  • Lines are terminated with character code 0xFF, which has to be adjusted
  • The 1st, 3rd, 5th … decompressed byte contains NUL (not sure, why !)

The decompressed source code has a fixed line length of 255 characters (blank-padded).


  1. Download this archive (it contains the decompressor tool compiled for Win32 and an ABAP report to dump the binary source code). The source code is available here; you can easily compile it on Linux/BSD/Unix/Windows using the enclosed build script.
  2. To extract the compressed source code from SAP, use the report "ZS_REPOSRC_DOWNLOAD". It reads the DATA field from table REPOSRC for a given report and stores it in a binary file on your workstation.
  3. Decompress the file on the command line.

PS: Works for Kernel 7.x, no guarantee for older releases.
PS 2: The functionality on non-Unicode systems is unknown… I'll check this later.

Have a nice day!

16Jun 12

Authorization ranges for S_TCODE

If you ever wondered about the physics of S_TCODE authorization ranges, this post is for you.
We'll take a look at the order of characters (which is essential for correct ranges) and the pitfalls to avoid.


To keep things easy and since most of us (?) are probably using a keyboard with Latin-1 characters on it, I'll focus on the ISO-8859-1 character set… though this is not exhaustive, as your system might use UTF-8 or some other charset.

Basically, the character code in the "From" value of a range serves as the start value and is incremented until the "To" code is reached. All characters between the low and high end of the range are covered; so AZ* includes AA, AAA, BBBB … ZZZZZ …

Concerning the order of characters, a range from / to Z is treated basically like this:

#!/usr/bin/env perl
$from = ord("/");    # Character code 47
$to   = ord("Z");    # Character code 90
for ( $from .. $to ) {
    print $_ . " == " . chr($_) . "\n";

Jump to Ideone.com to execute the code online or to Wikipedia for a table of ISO-8859-1 characters.

Btw.: some characters – e.g. backspace – are not printable and thus not usable.


Usually, SAP allows us to use 2 wildcard characters: * (match any number of characters) and + (match a single character), but for authorization values only the asterisk is valid; the plus sign is treated as a normal character (see SAP Note 136647).

At this point, I'd like to stress:

An asterisk at the end of a "From" value is never useful, but might be terribly wrong !

The reason for this is that the start value is the first character in the range and all "higher" character (combinations) have a "higher" value (i.e. A < AA < AAA).

Bad example:
The range SU* … SU21 includes SU24 as well, because the "From" value already covers all SU… transactions.

Transaction codes

To be able to create valid (and sensible) ranges for S_TCODE, we'll next look at the characters allowed in tcodes, because all others are of no interest (at least until we take a look at the tcodes SAP created itself).

In SE93 you'll get this error, when trying to use a disallowed character:

The function module responsible for this safety-belt is "RS_CHARACTER_CHECK" (called in the include LSEUKFWM), which gives us an idea of the set of allowed characters:

            allowed_special_characters    " Set to '+' in LSEUKFWM
            INTO allowed_characters.

This should be the full range one has to take into consideration when doing S_TCODE… which is only almost true… because SAP is not bound to the above restriction! When you look into table TSTC you'll find tcodes which contain characters not allowed by the above check. At least on a number of systems, I found quite some.

Here's a summary of the order of ASCII characters and corresponding notes (explanation below the table):

ISO-8859-1CharacterUsable in customer
tcodes ?
Used in any tcode ?First character of any tcode ?Note on general usage in authorization values
ISO-8859-1CharacterUsable in customer
tcodes ?
Used in any tcode ?First character of any tcode ?Note on general usage in authorization values
0 … 31( control characters )NoNoNoNot usable.
32( space )Spaces at the end of authorization values are automatically cut off.
33!YesCannot be used at the beginning of authorization values.
40(YesCannot be used at the beginning of any authorization value.
42*NoWildcard character.
If used in the middle of a value, all subsequent characters are ignored (see SAP Note 136647).
43+YesYesYesNot a wildcard, but treated as a normal character in authorization values
(see SAP Note 136647).
(namespace delimiter)
(namespace delimiter)
48 … 570 … 9YesYes
65 … 90A … ZYesYesYes
95_YesYesYesCannot be used as the only character in a value.
97 … 122a … zNo
(only uppercase letters)
(only uppercase letters)
127( delete )Not usable.
128 … 166€ … ¦No
(extended ASCII)
NoUsable, but won't make sense in the majority of cases.
168 … 255¨ … ÿNoUsable, but won't make sense in the majority of cases.


  • ISO-8859-1 … the character code
  • Character … self-explanatory
  • Usable in customer tcodes ? – see above (RS_CHARACTER_CHECK)
  • Used in any tcode ?red, if not consistent with 3rd column; green if so
  • First character of any tcode ?red, if not consistent with 3rd column; green otherwise
  • Note on general usage in authorization values … self-explanatory


If you want to obfuscate an asterisk in S_TCODE, just replace it with: + … _* — hardly anybody will ever notice 😉

27May 12

Find step users in SAP batch jobs

Hello all,
using personal users to execute batch job steps is not a good idea!
Of course it's convenient, but those users might vanish or their authorizations might change – but how to clean this mess up? Since SE37 does not allow you to select jobs by step user, one might try to use SE16 (→ table TBTCP) to find the affected jobs… which will drive you crazy, believe me!


This time, I'll provide you with another nice report to clean up your system and prevent any batch-related headache!


  • Create a new report in SE38 and paste this source code (don't forget to set a program authorization group).
  • In the selection texts:
    • Tick "dictionary reference" for R_JOBNAM, R_SDLUSR and R_USRNAM
    • Leave all other texts empty ("?...")

  • Activate & execute.


On the selection screen you can choose the:

  • job name, -scheduler and -status (like in SE37) and
  • step user name (initially set to your user name, but try DDIC or SAP*).

Submit your selection and you'll get a result similar to this:

The columns at your disposal are:

  • Job name — … self-explanatory.
  • Job no. — the internal ID of the job.
  • Scheduler — the user, who scheduled the job.
  • Job status — … self-explanatory.
  • Step no. — the step ID that matches your selection (the job might consist of more steps).
  • Rept. name — the report or command.
  • Name — the step user (green, if the user still exists; red otherwise)

As always in my reports, you can double-click on almost everything:

  • Job name — goto SE37 and show all matching jobs by name.
  • Job no. — show only the particular job in SE37.
  • Scheduler — open SU01 for the user, who scheduled the job.
  • Step no. — display the step list of the selected job.
  • Rept. name — jump to SE38.
  • Name — show the step user in SU01.


Red is dead, green is clean … unless it's a personal user ! 😎

28Apr 12

Find SAP tables without table authorization group

Hi people,
another common finding of audits is, that not all (of your customer-) tables are assigned to a table authorization group — allowing everyone with S_TABU_DIS and DICBERCLS = "&NC&" to access them. Therefore, you might want to take care of this…

(Certainly, this is not always a sensible check 🙄 – it depends on the data contained in the respective tables! … but anyway many auditors check for it.)

The challenge

The assignment of tables to authorization groups is defined in table TDDAT; but it only contains registered assignments. If a developer never assigned his/her table to an authorization group, it won't appear in it. So the problem is to find all tables without an entry in TDDAT plus all tables with an empty entry there. Furthermore, some "tables" are uninteresting, e.g. structures, help views and temporary or local tables, as they don't contain any sensible data.

Please, do not start Excel at this point… there is a better way!

The idea

Obviously we need a way to list all unprotected tables (no matter whether they're in TDDAT or not) and automatically exclude all table- (and view-) types containing uncritical or no data.

Therefore, we need to find DDIC objects, which…

  • have no entry in TDDAT or
  • are assigned to the authorization group "&NC&" (which has the same effect as no entry) or
  • are assigned to an empty authorization group (same effect) and
  • aren't internal tables or append structures (as they cannot contain data) and
  • aren't help views, structure views or append views (which do not contain real data as well) and
  • aren't tables for temporary data and
  • aren't local (i.e. not transportable) ← YMMV.

The solution

Fortunately, I was bored some time ago and programmed a report for this…

  • Create a new program in SE38:

  • Fill in the attributes:

… and choose a suitable authorization group (P_GROUP, marked red)!

  • Maintain the selection texts:

… and leave P_COUNT and P_EMPTY empty, tick "Dictionary reference" for the other ones.

  • Activate and execute it.


The report shows the following selection screen on execution:

On the first tab you can:

  • Select the range of Table names to inspect, e.g. "Z*".
  • Choose whether or not to Check tables for data (i.e. determine whether the number of rows is > 0)
    … which might heavily increase the runtime!
  • Decide whether to Show empty tables (if you've selected the previous option).

The second tab is pre-filled with sensible values (YMMV):

  • The Authorization group field is set to "&NC&" and empty values (only for tables that exist in TDDAT).
  • The Table category is set to exclude structures and append structures.
  • The View type field excludes help-, structure- and append-views as well as views without any type,
  • The Delivery class option excludes tables for temporary data.
  • The Package field is dynamically ❗ filled with all local packages on the system, the report runs on.

Now execute it and you'll get a result like this:

In the ALV header, you can see the number of tables matching your selection and the number without an authorization group.


  • Table name — … self-explanatory.
  • Short description — table description from the data dictionary.
  • AuGr — assigned authorization group (usually "&NC&" or empty).
  • Description — the authorization group's description from TBRGT.
  • Data — whether or not the table contains any data (only available if you ticked "Check table for data").
    If it contains a flash symbol, the check failed.
  • Cl.-spec. — whether or not the table is client specific.
  • Log — whether or not the table has "Log data changes" enabled in its technical settings.
  • Del. class — the delivery class, which might give you an indication to classify the table contents.
  • Package — the package the table belongs to (might point you to the "area" the table belongs to).
  • Tab. cat. — the type of the table (e.g. transparent -, cluster -, pool table or view).

Furthermore, you can double-click on the following fields:

  • Table name — jump to SE11.
  • Short description — inspect the selected table via SE16.
  • AuGr — open SE54.
  • Data — show the number of entries in the table.
  • Cl.-spec. — show description.
  • Log — show technical table settings.
  • Del. class — show all available delivery classes.
  • Package — open the package explorer.
  • Tab. cat. — show a list of table types.


Have fun, play around with the options and clean up your system! 😀

14Apr 12

Remove developer keys from productive systems

Dear reader,
a check, many auditors perform on your productive SAP systems is whether any developer keys exist (in table DEVACCESS). If there are any, this might become a finding you can easily avoid (… although your system is properly protected against changes in SCC4 and SE03 :roll:).

To delete all entries from DEVACCESS on production without touching the development system (and make your developers as well as the auditors happy)…

  • Launch SE14
  • Enter object name "DEVACCESS"

  • Select "Delete data", then click "Activate and adjust database"

  • Confirm

  • Mission accomplished

Note: This changes the activation log (go to SE11, choose DEVACCESS and click Utilities » Activation Log).

Page 5 of 6« First...23456