Skip links
Picture of Vahagn Vardanian

Vahagn Vardanian

Co-founder and CTO of RedRays

SAP Penetration Testing: What Gets Tested

SAP security Penetration testing Scoping SAP BTP

Most quotes for SAP penetration testing arrive as a page of methodology names and a day count. That tells you nothing about where the hours go. So here are the layers in the order a tester works them, which is also how to read the scope section of a statement of work. Assume an ABAP system with part of the business extended into SAP BTP. For one production system plus one BTP subaccount, five to ten working days is realistic. Below five, someone is running a scanner and reformatting the output.

At a glance

Written forTechnical buyers about to commission an SAP penetration test
What it coversExposed network services, the ICF service tree, SAP GUI and transactions, the authorization model, custom ABAP, and the BTP layer
Target reader time7 minute read
AuthorVahagn Vardanian, Co-founder and CTO, RedRays

Where SAP penetration testing starts: the exposed network services

Day one goes to what answers on the network, and from where. An ABAP application server publishes far more than the dispatcher port SAP GUI uses. The message server, the gateway and the ICM all listen on predictable ports, and what matters is which of them answer from an ordinary user subnet rather than only the server network.

The message server gets checked for two things: whether the internal port is exposed outside the server segment, and whether ms/acl_info points at a real ACL file. One that accepts registrations from anywhere lets an attacker register a rogue application server into the group. SMMS shows the state from inside. The outside view is the part that counts.

The gateway is where a lot of engagements find their first foothold. It lets external programs register as RFC servers, controlled by gw/reg_info and gw/sec_info and backed by gw/acl_mode. If those are missing or carry a wildcard host, anyone who reaches the gateway port can register a program and receive RFC calls meant for a legitimate server. RFC callback belongs to the same pass, governed by rfc/callback_security_method.

The ICM and the ICF service tree

The ICM handles HTTP and its ports come from icm/server_port_<n>. What gets checked there is ordinary web work: plain HTTP still enabled next to HTTPS, an admin handler reachable, a certificate nobody has looked at since it was issued.

Behind it sits the ICF tree in SICF, where the SAP-specific part begins. Every active node is a URL. /sap/public/info hands out system data to anyone who asks, /sap/bc/soap/rfc turns HTTP into RFC calls, /sap/bc/gui/sap/its/webgui puts a full GUI in the browser, and /sap/bc/adt exposes the development tools API. Each gets checked for whether it should be active, whether it carries a stored logon user, and where from. Stored service users are the quiet problem: a fixed user hands an unauthenticated visitor that user’s authorizations. icf/set_HTTPonly_flag_on_cookies and login/ticket_only_by_https belong here too.

SAP GUI, transactions, and how privilege escalation actually happens

Once the tester holds a user, the engagement is about how far that user stretches. The transaction pass is fast and mechanical. Can the account start SE16, SE16N or SE17 and read tables directly. SE38 or SA38 to run a report. SM59 for RFC destinations, SM49 and SM69 for external commands, then SM30, SU01 and SCC4. A transaction code is only half the check, because S_TCODE is not the object that protects the data. A user who cannot start SE16 but holds S_TABU_DIS on a wide authorization group reads the table anyway.

Privilege escalation here is rarely a memory bug. It is a chain of legitimate authorizations. S_RFC with * for the function group, plus a remote-enabled module that reads tables or runs code, gives a weak account a general-purpose remote interface. S_DEVELOP with activity 02 lets a user change a program, which means running any ABAP as the application server user, which ends the ABAP layer. S_LOG_COM and the SXPG_COMMAND_EXECUTE path move it to the operating system. S_BTCH_NAM schedules a job under another step user, so a weak account inherits a strong one.

RSUSR002 and RSUSR008_009_NEW give the population view of who holds what. SU53 and STAUTHTRACE give the per-attempt view of which object stopped you. Standard accounts get their own pass: SAP*, DDIC, SAPCPIC, EARLYWATCH, and whether login/no_automatic_user_sapstar still allows the hardcoded SAP* fallback if the user record is deleted.

On a fixed budget I would cut the external scan to one day and put those hours into the authorization model. It is an argument I lose regularly, usually to someone who bought the test to close a perimeter item on an audit plan. I hold it anyway. Authorization findings explain how a purchasing clerk ends up able to release a payment run, and they exist only as combinations, so nobody finds them without sitting down with the role extract.

Custom ABAP is the part of SAP penetration testing that does not scale by hand

Every SAP system carries Z and Y code nobody has read since the author changed jobs. It has database access and, in the wrong places, operating system access, and in most systems it runs to several hundred thousand lines.

The classes that come back over and over:

  • Missing or incomplete AUTHORITY-CHECK, including the check whose sy-subrc is never evaluated.
  • Injection into dynamic SQL, usually a WHERE clause concatenated out of a screen parameter.
  • Injection into dynamic ABAP, where a value reaches GENERATE SUBROUTINE POOL or an INSERT REPORT.
  • OPEN DATASET on a user-supplied path, which is directory traversal against the application server filesystem.
  • CALL 'SYSTEM' and the external command wrappers around it.
  • Hardcoded credentials, usually a technical user sitting in a constant.
  • Remote-enabled function modules doing sensitive work with no check at all, the ABAP version of an unauthenticated API endpoint.

Hand review does not survive that volume. Ten days of reading covers what looks interesting from outside and samples the rest, missing the boring one in a rarely used report that happens to be RFC-enabled. So we pair it with a static scan of the whole custom codebase, which changes what the manual days buy. The scanner reads objects over ADT and tracks how a value moves from an entry point to a sink, so the tester opens the engagement holding a ranked list of candidate paths instead of a package tree. The reading hours then go where no tool can follow: deciding which of those paths a real user, holding real roles, can actually walk.

The limitation is triage. A missing AUTHORITY-CHECK in a report that two people in one plant can start is a finding, not a risk, and no tool tells you which one you are holding. Someone who knows the business has to read the list.

The BTP side: approuter routes, role collections, and destinations

If the business has extended into BTP, the cloud side is a separate test, and it falls out of scope constantly because the ABAP and BTP teams report to different people.

The approuter is the front door and xs-app.json decides who gets through it. Routes with authenticationType set to none, routes with no scope property where one belongs, CSRF protection switched off during development and never switched back: each one a route a browser reaches without the check the designer assumed. Then xs-security.json, where scopes and role templates compose into role collections. The recurring problem there is a collection built for testing and assigned to a group nobody has pruned since.

Destinations get their own pass. Read the authentication type and the proxy type. Basic authentication with a stored technical user means anyone who can invoke that route gets the user’s rights on the far end, with no trail back to a human. On-premise destinations run through the Cloud Connector, so its access control list is part of the test. Space roles matter too: a SpaceDeveloper reads the environment of a running application, and those environments hold service credentials.

Integration flows are code, and they get skipped constantly. The Groovy inside them handles payloads and credentials, and an API Management proxy missing a policy someone assumed was there is an open route to a backend. We cover that side with a separate product that also reads Cloud Foundry application configuration.

What a non-production system can and cannot prove

Most clients want the test run against QA. Reasonable, as long as you know what you are buying. QA proves things about code and roles: if transports flow the usual way, the custom ABAP there is close to what production runs and the PFCG role definitions are the same objects, so exploiting a missing AUTHORITY-CHECK there is real evidence about production.

QA proves nothing about configuration or exposure. Profile parameters are per instance, so gw/reg_info, ms/acl_info and the login parameters can differ. Network placement differs by design: an exposed gateway in QA says nothing about production, and a locked one clears nothing either. The workable compromise is exploitation in QA plus a read-only configuration review of production: RSPARAM output, the SMGW and SMMS ACL screens, the SICF service list, role extracts. That is still not a full production test, and the report should say so.

What is normally out of scope, and why

Denial of service is excluded, and should be. Proving you can halt an ERP system is not worth the outage. A tester who reaches a payment transaction shows the reach and stops there. Read access to production data is agreed up front, usually with a rule that findings quote field names, not values.

SAP-managed infrastructure is out. Under RISE, or on SAP-hosted BTP services, the platform belongs to SAP, which publishes its own rules for testing its cloud services. Your side of the line is your configuration, applications and identities. Kernel and standard-code research is out too, though patch state stays in: which SAP Security Notes are missing, which components are behind. Social engineering and physical access get scoped separately; folded into a technical SAP assessment, neither gets done properly.

A penetration test is a photograph of one system on a few specific days. The next transport changes the code, the next role request changes the model, and neither waits for your test window. Read the report as a description of the kinds of problem your organization produces, then put the repeatable parts, the code scan and the parameter review, on a schedule you run yourself.

Scoping a test on your landscape

Send us the object counts and the layer list from this post and we will come back with the days, not with a brochure.

Contact RedRays

RedRays is an independent SAP security vendor and is not affiliated with SAP SE. SAP and the product names referenced here are trademarks of SAP SE. Transaction codes, authorization objects and parameters named in this article are given for orientation; verify them against your own release before acting on them.

Explore More

Three identical server cabinets carrying stacks of code of very different heights beside a measuring rule

ABAP Code Security Scan Cost Drivers

What moves the cost of an ABAP code security scan: custom object counts, effective lines, systems in scope, transport gating, triage and retest.