Therefore dynamic attributes are evaluated twice when OGNL forced evaluation is used ():For example, for the case where message is an action property under user control, visiting the URL https:/foo.com/action?message%251%2b1 will result in a double evaluation:
This issue is basically the same as S2-053 which describes a double evaluation where the first evaluation is a $ FreeMarker evaluation in UnifiedCall and the second evaluation is an OGNL evaluation (normally in UIBean). When I verified the examples described in the security bulletin, both the insecure constructions (eg: ) and the secure proposed ones (eg: ) were found to be vulnerable in 2.5.12 (first version where they should be fixed) and also in latest 2.5.22 version.
Please note that these are much more critical than the JSP tag ones (S2-059) since in this case all OGNL evaluations are evaluated an additional time. Thats it, single evaluations such as the value attribute ones are now evaluated twice, and double evaluations such as the id or name ones are now evaluated three times!
The recommendations from S2-053 suggest using % expressions rather than $ but since the latter are the ones that developers use in FTL templates, it is error-prone and may easily lead to critical bugs.
Velocity Struts tags are implemented as Velocity Directives and all of them extend from org.apache.struts2.views.velocity.components.AbstractDirective. They all work in a similar way where tag parameters are passed in the form of key=value strings, eg:
Directive parameters are parsed by AbstractDirective render method and then added as tag parameters by putProperty). In this method, the directive parameters are evaluated before splitting them by =. This means that there is an additional evaluation to the ones performed later by the Component and FTL template layers which causes simple evaluated attributes to be evaluated twice. Eg:
When I changed the Struts2 version to 2.5.33 and Struts2-convention-plugin to 2.5.33 in the pom.xml, it works and I see my index.jsp page. It doesn't work when I use the current version of struts2. I tried Maven Clean, Project Clean, rebuild the project, remove target folder contents, view that struts2 is in the buildpath, and verified that the class 'org.apache.struts2.RequestUtils' existed in struts2 package and I still received the error. I tried debugging the class 'StrutsPrepareAndExecuteFilter' when loading my index.jsp, and when it reaches to run a class 'org.apache.struts2.RequestUtils', it throws and error. Error from console log below:
This post will show how to create a Student Enrollment Application using MYSQL DB with Struts 2 framework. This is a simple application that aims to collect the input details from the user during signup, save the details in the MYSQL DB and authenticate the same during login.
Create a file signup.jsp to include a form to get the input details like UserName, Password, FirstName, LastName, DateOfBirth and EmailAddress of the student. A snapshot of the signup page is as follows:
The Struts 2 UI component tags are used to create the page elements like the form, textfield, password and hidden fields. The Struts Bootstrap tags are used for the Twitter Bootstrap integration. The Struts jQuery tags are used to include the AJAX functionality of submitting the form outside from the modal window and to use the UI widgets like the datepicker.
Create a file named db.properties under the src folder, where the properties of the MYSQL DB like url, username and password can be specified. Replace with the actual connection url for connecting to the MYSQL DB. Likewise, replace and with the actual username and password values.
Create a Repository tier POJO class named StudentRepository.java under the package com.github.elizabetht.repository to support the database operations of saving the student details, verifying the student login details and checking if the username exists when a save is attempted.
In most cases, the action class is extended from the general ActionSupport class, which has a lot of easy to use convenient features. So, extend the ActionSupport class in the Action class, unless you have a reason not to!
If SUCCESS (or the equivalent success string) is returned from the execute() method, the actual value of action will be the resulting view, unless there are no results specified using the @Result annotation. For instance, in LoginAction class, there are no @Result annotations used and hence, login-success.jsp will be rendered when execute() method returns a SUCCESS string.
Connect to the MySQL DB which is to be used for this application and create a new DB Schema named studentEnrollment using the MySQL Workbench.This is necessary as the DB Schema name of studentEnrollment is specified in the db.properties file.
The same can be deployed remotely on any native server that supports Tomcat by copying the WAR file (Right click on the project and choose Export as WAR File option) to /var/lib/tomcat7 folder (or appropriate tomcat directory) and restarting the tomcat server.
Object Graph Notation Language (OGNL) is a popular, Java-based, expression language used in popular frameworks and applications, such as Apache Struts and Atlassian Confluence. Learn more about bypassing certain OGNL injection protection mechanisms including those used by Struts and Atlassian Confluence, as well as different approaches to analyzing this form of protection so you can harden similar systems.
Object Graph Notation Language (OGNL) is a popular, Java-based, expression language used in popular frameworks and applications, such as Apache Struts and Atlassian Confluence. In the past, OGNL injections led to some serious remote code execution (RCE) vulnerabilities, such as the Equifax breach, and over the years, protection mechanisms and mitigations against OGNL injections have been developed and improved to limit the impact of these vulnerabilities.
In this blog post, I will describe how I was able to bypass certain OGNL injection protection mechanisms, including the one used by Struts and the one used by Atlassian Confluence. The purpose of this blog post is to share different approaches used when analyzing this kind of protection so they can be used to harden similar systems.
No new OGNL injections are being reported as part of this research, and unless future OGNL injections are found on the affected frameworks/applications, or known double evaluations affect an existing Struts application, this research does not constitute any immediate risk for Apache Struts or Atlassian Confluence.
My friend, Man Yue Mo, wrote a great article describing how the OGNL mitigations have been evolving over the years and there are few other posts that also describe in detail how these mitigations have been improving.
When the CVE-2022-26134 was released there was an initial understanding that the OGNL injection could not lead to direct RCE in the latest version 7.18.0 since the isSafeExpression method was not possible to bypass for that version
The first interesting thing I learned was that this method was actually parsing the OGNL expression into its AST form in order to analyze what it does and decide whether it should be allowed to be executed or not. Bye-bye to regexp-based bypasses.
ASTEval are nodes in the form of (expr)(root) and they will parse the expr string into a new AST and evaluate it with root as its root node. This will allow us to provide an OGNL expression in the form of a string (ASTConst) and evaluate it! We know that ASTConst nodes are parsed as OGNL expressions and verified to not be harmful. However, we already saw that if we split the string literal in multiple parts, only the individual parts will be checked and not the result of the concatenation. For example, for the payload below #application will never get checked, only # and application which are deemed to be safe:
This payload avoids calling the BeanMap constructor explicitly and, therefore, gets rid of the ASTCtor limitation. In addition, it allows us to call Object.getClass() implicitly by accessing the class item. However, we still have another problem: we need to be able to assign the map to a variable (map) so we can call the setBean() method on it and later call the get() method on the same map. Since ASTAssign was blocked, assignments were not an option. Fortunately, looking through the list of AST nodes, two more nodes got my attention: ASTChain and ASTSequence.
There was a final problem to solve. The OGNL injection sink was translateVariable() which resolves OGNL expressions wrapped in $expressions delimiters. Therefore, our payload was not allowed to contain any curly brackets. Fortunately, for us, OGNL will replace unicode escapes for us so we were able to use the final payload:
I submitted these bypasses to Atlassian through its bug bounty program and, even though I was not reporting any new OGNL injections but a bypass of its sandbox, they were kind enough to award me with a $3,600 bounty!
It turned out that the ongoing ActionInvocation object can be accessed through the OGNL context and, therefore, we can use it to force the building of the Result object in advance. Calling the Results doExecute() method will trigger the population of the so-called template model. For example, for Freemarker, ActionInvocation.createResult() will create a FreemarkerResult instance. Calling its doExecute() method will, in turn, call its createModel() method that will populate the template model.
After the invocation, the request scope and value stack will be populated with additional objects. These objects vary depending on the view layer used. What follows is a list of the most interesting ones (skipping most of them which do not lead to RCE):
We can achieve the same by calling the getAPI() on any freemarker.template.TemplateModelWithAPISupport instance. Many of the FreeMarker exposed objects inherit from this interface and will allow us to wrap them with a BeanModel. For example, to list all the keys in the Struts Value Stack we can use:
c80f0f1006