Michael Jordon - 14th August 2012
This blog post details a vulnerability that was found in SAP’s Host Control service. The vulnerability allows for 100% reliable full code execution as the SAP administrator from an unauthenticated perspective. This vulnerability was patched in May 2012 (SAP Security Note 1341333 ); however at the request of SAP we have delayed the publication of the details by 3 months. As I believe the vulnerability and the technique used to exploit it are technically interesting, I thought I would go into more depth than a typical advisory normally would..
The management console has a SOAP web service listening on port 50013, in order to use the service, authentication is required. It is within this authentication process that the vulnerability lies, through insufficient validation of its input. An authentication request takes four parameters: database type, username, password and database name. An example can be seen in figure 1.
The SAP server protocol uses these variables to call a command line application dbmsrv.exe which in turn calls dbmcli.exe (command line SQL tool). When these applications are called, it uses the following command lines:
Highlighted in red, the parameters from the web service request are placed into the command line. When using CAT  to change the request you can see these values change. There is no validation on these inputs, so when sending a request with the following arguments:
Database name: NSP test1 Username: admin test2 Database Type: ada test3
The following command is run:
The second command is never executed because the first command fails. But we can see that the arguments are being passed in, but also that they are being surrounded with double quotes which in theory should prevent us from breaking out. However, a subsequent test shows that we can inject quotes into the parameters:
Database name: NSP" "test1 Username: admin" "test2 Database Type: ada" "test3
So we have full argument injection. The standard way of trying to exploit this type of vulnerability is to add a second command to be run at the same time, for example:
dbname & calc.exe
However that only works if the command is being passed through a command interpreter (i.e. cmd.exe). Since we can change the parameters being passed to the application; the trick is to find some arguments that will do something interesting.
By examining the arguments accepted by both applications we see nothing of obvious help in sapdbctrl.exe but when looking dbmcli.exe we see the following:
First the DBMServer-Command means we can run any database command that we want to. Examining the features of the SAP database there is a syntax that allows for system commands to be run by prefixing them with an exclamation mark. So:
will run the calculator on the server. However, this syntax cannot be passed in at the command line; it needs to be entered when dbmcli is running, through user interaction. So we know that it can run system commands; we just need to provide it with the command via stdin.
In the help output the next part we need is the –i option. This allows for a script to be provided. If we can give it a script then we can have the ‘!calc’ line in it. However, we need to find a way to create a file on the server in which we can control at least a single line. The exclamation mark also has to be the first character on the line for the command to execute.
This leads us to the output file option; which means that it will run the SQL specified and write the output to a file. This is useful because we can then start to write a file to the disk if we can provide this argument. Further experimentation showed that the database type has to be correct in order for dbmcli.exe to be executed, and only the database name is passed from sapdbctrl to dbmcli. So the next test uses the following parameters:
Database name: NSP –o c:\test.txt Username: admin Database Type: ada
Causing the following command to be run:
Because the "NSP –o c:\test.txt" is in quotes it will be passed onto the dbmcli as a single argument:
This command creates a file, test.txt, on the server with the following contents:
OK VERSION = 7.8.01 BUILD = DBMServer 7.8.01 Build 018-121-240-252 OS = WIN32 INSTROOT = C:\sapdb\NSP\db LOGON = True CODE = UTF8 SWAP = full UNICODE = YES INSTANCE = OLTP SYSNAME = Windows Server 2003 Enterprise Edition (Service Pack 1) MASKING = YES REPLYTREATMENT = none,zlib,auto SDBDBM_IPCLOCATION = C:\sapdb\NSP\data\wrk
Unfortunately we do not control any of the contents of this file. Further digging around reveals we can generate an error message, which contains data that will be echoed back in the file. This is achieved by providing an extra argument of ‘–n’ for the hostname of the database we want to connect to. So by passing the following parameter:
Database name: NSP –o c:\test.txt –n hosttest
We get the following error written into c:\test.txt:
So we control the creation of a file and a small amount of its contents. However, we still need to have a line that we can control in its entirety. Luckily, programs can take arguments containing newline characters. Normally in cmd.exe you could not type the newline in; but when called from code, a newline can be provided within an argument.
So our final request (to create our pseudo script file) is the following:
Which creates the following file:
Error! Connection failed to node hosttest !calc for database NSP: unknown host name
When this file is run as a script it will error on the first and last two lines but interpret the ‘!calc’ correctly leading to the calculator being executed. With this file created we can then make a second SOAP request to run the file:
And calc is executed .
Calc is not Enough
Normally when doing PoC exploitation, calc is enough to show we have full control over a server. However, in this case there is a major limiting factor: we cannot use a space in the string being entered into the script file. If a space is entered then dbmcli.exe will think it is another argument and not write it in the error message in a file. There is very little you can do with only the power to execute the binaries on the server with no arguments, even shutdown needs arguments.
So we need to find a way around this limitation to allow the application we are running to have arguments. The use of double quotes does not work because the first application will see them and remove them. After various tries I went for the use of a substring on an environment variable to provide a space.
C:\echo %ProgramFiles% C:\Program Files
This variable is standard on a Windows server and importantly it has a space in it (there are others we could use). To extract the space we use the built-in substring function in DOS. So to run ping while avoiding spaces, the following command works:
Using this technique we can run arbitrary commands on the server as an administrator; allowing us to take complete control over the system without any authentication credentials.
This is a bug in SAP which has been fixed in ‘SAP security note 1341333’ which was released in May 2012. To protect yourself, ensure that your SAP system is fully patched and up to date. Furthermore; defense in depth would be to ensure that the network architecture limits access to ports like 50013 so that only administrative access is permitted.
It is potentially also a technique which will apply to other systems elsewhere. So, when you are trying to break into a complex multi-program system, ensure you check for parameter injection by using tools like procmon to see if any external inputs are being used as arguments. If they are; see if you can break out of them and add new arguments. If you can; see what ‘nasty’ arguments can be added to the application. The beauty of this type of attack is that it is 100% reliable, cross platform and service pack independent.