Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When using methods such as Map.get(), if the return value is an array, the elements in the array will not be tainted. #102

Open
springkill opened this issue Apr 22, 2024 · 4 comments

Comments

@springkill
Copy link

Description

I used the following configuration:

  - { method: "<org.apache.catalina.connector.Request: java.util.Map getParameterMap()>", from: base, to: result }

When I use the return value of Map.get() directly as a sink, the taint is captured, but when I use the elements of the returned array, the taint is not added.
The reality is that the specific problem is:
When I was analysing the tomcat application, I needed to use the org.apache.catalina.connector.RequestFacade.getParameterMap() method, and the obtained ParameterMap was tainted with taints, and the array obtained by ParameterMap.get() also The array obtained by ParameterMap.get() is also tainted, but when I remove an element from the array, the taint disappears.
image
image
image
I tried adding TRANSFER to the methods in the two images above, but still not getting the desired results.

  - { method: "<org.apache.catalina.connector.RequestFacade: java.util.Map getParameterMap()>", from: base, to: base.request }
  - { method: "<org.apache.catalina.connector.Request: java.lang.String[] getParameterValues(java.lang.String)>", from: base, to: "result[*]" }

Is there any way to fix this?

@zhangt2333
Copy link
Collaborator

When I was analysing the tomcat application, I needed to use the org.apache.catalina.connector.RequestFacade.getParameterMap() method, and the obtained ParameterMap was tainted with taints, and the array obtained by ParameterMap.get() also The array obtained by ParameterMap.get() is also tainted, but when I remove an element from the array, the taint disappears.

I can't clearly understand what you're saying. And there are so many things circled in the chart that I don't know which to focus on. Would you be so kind as to illustrate with examples/current behavior/expected behavior?

@springkill
Copy link
Author

The problem I'm trying to solve is, when the return value of the Map.get() method is an array, how do I mark the elements of the returned array with taints?
For example, how do I make the elements in the "values" array in the doPost() method carry tags?

package org.owasp.benchmark.testcode;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(value="/cmdi-00/BenchmarkTest00004")
public class BenchmarkTest00004 extends HttpServlet {
	
	private static final long serialVersionUID = 1L;
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
	
		java.util.Map<String,String[]> map = request.getParameterMap();
		String param = "";
		if (!map.isEmpty()) {
			String[] values = map.get("BenchmarkTest00004");
			if (values != null) param = values[0];
		}
		
		
		
		org.owasp.benchmark.helpers.ThingInterface thing = org.owasp.benchmark.helpers.ThingFactory.createThing();
		String bar = thing.doSomething(param);
		
		
		String cmd = "";
        String osName = System.getProperty("os.name");
        if (osName.indexOf("Windows") != -1) {
        	cmd = org.owasp.benchmark.helpers.Utils.getOSCommandString("echo");
        }
        
		Runtime r = Runtime.getRuntime();

		try {
			Process p = r.exec(cmd + bar);
			org.owasp.benchmark.helpers.Utils.printOSCommandResults(p, response);
		} catch (IOException e) {
			System.out.println("Problem executing cmdi - TestCase");
			response.getWriter().println(
			  org.owasp.esapi.ESAPI.encoder().encodeForHTML(e.getMessage())
			);
			return;
		}
	}
	
}

@zhangt2333
Copy link
Collaborator

zhangt2333 commented Apr 24, 2024

I don't have your test environment and don't know how to reproduce your problem, in addition, you don't provide enough usable information (e.g. points-to-set, analysis options, complete analyzed program and complete taint-config.yml). All of the above makes it difficult for me to think, validate and answer your question.

Please offer the following infomation (otherwise, I don't know how to proceed.):

  • The source code or Tai-e IR of org.apache.catalina.connector.RequestFacade, org.apache.catalina.connector.Request. (I don't even know what org.apache.catalina.connector.RequestFacade is and I'm not sure if what I searched for online is the same as yours either)
  • The pta-results.txt, tai-e-plan.yml, tai-e.log in your output directory.
  • any other helpful infomation (better yet, tell me how I can reproduce your issue).

@springkill
Copy link
Author

OK, I will describe my problem in detail.
Here's the test code:

package org.owasp.benchmark.testcode;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(value="/cmdi-00/BenchmarkTest00016")
public class BenchmarkTest00016 extends HttpServlet {
	
	private static final long serialVersionUID = 1L;
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");

		java.util.Map<String,String[]> map = request.getParameterMap();
		String param = "";
		if (!map.isEmpty()) {
			String[] values = map.get("BenchmarkTest00016");
			if (values != null) param = values[0];
		}
		

		String bar = doSomething(request, param);
		
		String cmd = "";
        String osName = System.getProperty("os.name");
        if (osName.indexOf("Windows") != -1) {
        	cmd = org.owasp.benchmark.helpers.Utils.getOSCommandString("echo");
        }
        
		Runtime r = Runtime.getRuntime();

		try {
			Process p = r.exec(cmd + bar);
			org.owasp.benchmark.helpers.Utils.printOSCommandResults(p, response);
		} catch (IOException e) {
			System.out.println("Problem executing cmdi - TestCase");
			response.getWriter().println(
			  org.owasp.esapi.ESAPI.encoder().encodeForHTML(e.getMessage())
			);
			return;
		}
	}  // end doPost
	
		
	private static String doSomething(HttpServletRequest request, String param) throws ServletException, IOException {

		String bar = param;
	
		return bar;	
	}
}

At line 24 of this code, I use getParameterMap() to get map, the source method is doPost and the tain is marked in org.apache.catalina.connector.RequestFacade which implements HttpServletRequest.
So if I want to propagate the taint, I need to use RequestFacade.getParameterMap() as a transfer.
Like this:

  - { method: "<org.apache.catalina.connector.RequestFacade: java.util.Map getParameterMap()>", from: base, to: result }

And in line 28 of this test code, I uses the Map.get() method to get the value of a param, the value obtained is of array type, but since the return value of Map.get() is of type Object, there is no way for me to propagate the taint by using to: "result [*]" , I can only use to: result.
Like this:

  - { method: "<org.apache.catalina.util.ParameterMap: java.lang.Object get(java.lang.Object)>", from: base, to: result }

After I did this, the taint propagated to the map object on line 24 and from the map object to the values array on line 28, but not to the elements inside the values array.

There is the source code of org.apache.catalina.connector.RequestFacade.getParameterMap().

public Map<String,String[]> getParameterMap() {
        checkFacade();

        if (Globals.IS_SECURITY_ENABLED) {
            return AccessController.doPrivileged(new GetParameterMapPrivilegedAction());
        } else {
            return request.getParameterMap();
        }
    }

In line 7 of the above code request.getParameterMap() is used, where request is a filed of RequestFacade of type org.apache.catalina.connector.Request.
There is the source code of org.apache.catalina.connector.Request.getParameterMap():

public Map<String,String[]> getParameterMap() {

        if (parameterMap.isLocked()) {
            return parameterMap;
        }

        Enumeration<String> enumeration = getParameterNames();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String[] values = getParameterValues(name);
            parameterMap.put(name, values);
        }

        parameterMap.setLocked(true);

        return parameterMap;

    }

I try to usr getParameterValues() as transfer to tain the array in map, like this :

  - { method: "<org.apache.catalina.connector.Request: java.lang.String[] getParameterValues(java.lang.String)>", from: base, to: "result[*]" }

But ultimately the tainflow is still undetectable.
Above is what I mentioned: how to pass taints to elements inside an array when the return value type of Map.get() is an array?

tai-e.log
tai-e-plan.tml:

- id: pta
  options:
    cs: 1-obj
    only-app: false
    implicit-entries: true
    distinguish-string-constants: all
    merge-string-objects: false
    merge-string-builders: false
    merge-exception-objects: false
    handle-invokedynamic: false
    propagate-types:
    - reference
    advanced: zipper
    dump: false
    dump-ci: false
    dump-yaml: false
    expected-file: null
    reflection-inference: solar
    reflection-log: null
    taint-config: config/common/taint-config.yml
    plugins:
    - pascal.taie.analysis.pta.plugin.taint.TomcatHandler
    time-limit: -1

pascal.taie.analysis.pta.plugin.taint.TomcatHandler:

package pascal.taie.analysis.pta.plugin.taint;

import pascal.taie.World;
import pascal.taie.analysis.pta.core.cs.context.Context;
import pascal.taie.analysis.pta.core.cs.element.CSMethod;
import pascal.taie.analysis.pta.core.heap.HeapModel;
import pascal.taie.analysis.pta.core.heap.Obj;
import pascal.taie.analysis.pta.core.solver.EntryPoint;
import pascal.taie.analysis.pta.core.solver.Solver;
import pascal.taie.analysis.pta.core.solver.SpecifiedParamProvider;
import pascal.taie.analysis.pta.plugin.Plugin;
import pascal.taie.ir.IR;
import pascal.taie.ir.exp.Var;
import pascal.taie.language.classes.JClass;
import pascal.taie.language.classes.JMethod;
import pascal.taie.language.type.Type;

import java.util.List;

import static pascal.taie.analysis.pta.core.heap.Descriptor.ENTRY_DESC;

public class TomcatHandler implements Plugin {
    private Solver solver;
    private TaintManager manager;

    private boolean isCalled;

    final JClass requestFacade = World.get().getClassHierarchy().getClass("org.apache.catalina.connector.RequestFacade");
    final JClass responseFacade = World.get().getClassHierarchy().getClass("org.apache.catalina.connector.ResponseFacade");


    @Override
    public void setSolver(Solver solver) {
        this.solver = solver;
        manager = new TaintManager(solver.getHeapModel());
    }

    @Override
    public void onStart() {
        List<JClass> list = solver.getHierarchy().applicationClasses().toList();
        for (JClass jClass : list) {
            String a = jClass.getName();
            if (a.matches("^(javax\\.servlet\\.ServletRequest).+$")) {
                System.out.println("found: " + a);
            }
            HeapModel heapModel = solver.getHeapModel();
            jClass.getAnnotations().forEach(annotation -> {
                        if (annotation.getType().matches("javax.servlet.annotation.WebServlet")) {
                            jClass.getDeclaredMethods().forEach(jMethod -> {
                                if (jMethod.getName().matches("\\b(doGet|doPost|doPut|doDelete)\\b")) {
                                    Type requestFacadeType = jMethod.getParamType(0);
                                    Type responseFacadeType = jMethod.getParamType(1);
                                    String requestFacadeAlloc = "<" + requestFacadeType.toString() + ">";
                                    String responseFacadeAlloc = "<" + responseFacadeType.toString() + ">";
                                    Obj mockRequest = heapModel.getMockObj(ENTRY_DESC, requestFacadeAlloc, requestFacade.getType(), jMethod);
                                    Obj mockResponse = heapModel.getMockObj(ENTRY_DESC, responseFacadeAlloc, responseFacade.getType(), jMethod);
                                    Obj mockServlet = heapModel.getMockObj(ENTRY_DESC, "<http-controller>", jClass.getType());
                                    SpecifiedParamProvider paramProvider = new SpecifiedParamProvider.Builder(jMethod)
                                            .addThisObj(mockServlet)
                                            .addParamObj(0, mockRequest)
                                            .addParamObj(1, mockResponse)
                                            .build();
                                    solver.addEntryPoint(new EntryPoint(jMethod, paramProvider));
                                }
                            });
                        }
                    }
            );
        }
    }

    @Override
    public void onNewCSMethod(CSMethod csMethod) {

        JMethod method = csMethod.getMethod();
        Context context = csMethod.getContext();
        boolean isDoMethod = method.getName().matches("\\b(doGet|doPost|doPut|doDelete)\\b");
        if (isDoMethod) {
//            System.out.println("find"+method.toString());
            IR ir = method.getIR();
            Var param = ir.getParam(0);
            SourcePoint sourcePoint = new ParamSourcePoint(method, new IndexRef(IndexRef.Kind.VAR, 0, null));
            Obj taint = manager.makeTaint(sourcePoint, param.getType());
            solver.addVarPointsTo(context, param, taint);
        }
    }
}

You can download the full source code from here:
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core/9.0.85

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants