This vulnerability was analyzed during Episode 175 on 12 December 2022
Great documentation of the process finding a WAF process, building up the final payload bit by bit.
The actual bug being exploited is this Spring bug. Its an SSTI so you can inject SpEL (Spring Expression Language) expressions which give you access to sort of “navigate” Java objects using standard dot notation and call functions, chain responses. The classic way to exploit this might be:
${T(java.lang.Runtime).getRuntime().exec("<my command here>")}
Unfortunately there is a WAF that must be bypassed and it is blocking the most direct route. Most of the post follows his path towards finding a bypass.
The first step, was “try the obvious,” or as I tend to say “test your assumptions”. Kinda the same idea, do the obvious things, test everything, and establish some ground truth/things that definitely work to work from.
Then we just get into the problem solving flow:
First, trying to gain access to an arbitrary class. This ended up being easy ${2.class}
worked. From there he discovered that the common technique of using .forName
was easily detected by the WAF and did not work because the '
and "
were being transformed in some way. So they couldn’t use strings directly.
So the hunt for a way to instantiate a string begun…and died as the means to directly instantiate a new class were all blocked by the WAF. So they could not simply instantiate a new String, instead they found a round-about way to craft a String object containing a single character.
${(2.toString()+2).charAt(0).class.toString(99)}
Basically what they get here is access to the Character
class in Java which contains a .toString
method that takes in character value (avoiding the need for quotes) and returns a String.
2.toString()
giving an original instance of a string object.charAt(0)
returning aCharacter
object for the first character.class
giving access to theCharacter
class.toString(99)
- the desiredtoString
class that will give a controllable output.
This string crafting method then became the basis for accessing arbitrary Classes, as now the forName
method could be called without tripping the WAF. With arbitrary Classes the java.lang.Runtime
could be accessed, then .getRuntime()
which has the .exec
method to execute commands.