pascal-lab/Tai-e

How to taint a parameter of a custom entrypoint?

Y4er opened this issue · 10 comments

Y4er commented
package com.example.demo.controller;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@RestController
public class TestController {

    public static void exec(String[] cmd) {
        try {
            new ProcessBuilder(cmd).start();
            Runtime.getRuntime().exec(cmd);
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    @RequestMapping(value = {"/aaa/{uuid:.+}"}, method = {RequestMethod.GET}, produces = {"application/json"})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public String index(HttpServletRequest request, @PathVariable String[] uuid) throws Exception {
        exec(uuid);
        return "asd";
    }
}

I need to use the index function as an entrypoint, how should I mock the String[] uuid parameter?

        HeapModel heapModel = solver.getHeapModel();
        JClass httpRequestWrapper = World.get().getClassHierarchy().getClass("javax.servlet.http.HttpServletRequestWrapper");
        JClass servletRequestWrapper = World.get().getClassHierarchy().getClass("javax.servlet.ServletRequestWrapper");
        JClass jClass = World.get().getClassHierarchy().getClass("com.example.demo.controller.TestController");
        JMethod index = jClass.getDeclaredMethod("index");

        SpecifiedParamProvider.Builder builder = new SpecifiedParamProvider.Builder(index);
        builder.addThisObj(heapModel.getMockObj("entryobj", "class", jClass.getType()));
        builder.addParamObj(0, heapModel.getMockObj("entryobj", "method1", httpRequestWrapper.getType(), index));

        TypeSystem typeSystem = solver.getTypeSystem();
        ClassType string = typeSystem.getClassType(ClassNames.STRING);
        builder.addParamObj(1, heapModel.getMockObj("entryobj", "method2", typeSystem.getArrayType(string, 1), index));

        solver.addEntryPoint(new EntryPoint(index, builder.build()));

Here is my code, but it doesn't work.

Your code looks fine to me (the only possible problem is that the mock String array (uuid) is empty). What do you mean by "it doesn't work"?

Y4er commented

Detected 0 taint flow(s).

Undetectable taint flow from String[] uuid to Runtime.getRuntime().exec(cmd)

Here is my complete code.

    @Override
    public void onStart() {
        HeapModel heapModel = solver.getHeapModel();
        JClass httpRequestWrapper = World.get().getClassHierarchy().getClass("javax.servlet.http.HttpServletRequestWrapper");
        JClass servletRequestWrapper = World.get().getClassHierarchy().getClass("javax.servlet.ServletRequestWrapper");
        JClass jClass = World.get().getClassHierarchy().getClass("com.example.demo.controller.TestController");
        JMethod index = jClass.getDeclaredMethod("index");

        SpecifiedParamProvider.Builder builder = new SpecifiedParamProvider.Builder(index);
        builder.addThisObj(heapModel.getMockObj("entryobj", "class", jClass.getType()));
        builder.addParamObj(0, heapModel.getMockObj("entryobj", "method1", httpRequestWrapper.getType(), index));

        TypeSystem typeSystem = solver.getTypeSystem();
        ClassType string = typeSystem.getClassType(ClassNames.STRING);
        Obj mockObj = heapModel.getMockObj("entryobj", "method2", typeSystem.getArrayType(string, 1), index);
        Context ctx = solver.getContextSelector().getEmptyContext();
        CSObj csArgs = solver.getCSManager().getCSObj(ctx, mockObj);
        ArrayIndex argsIndex = solver.getCSManager().getArrayIndex(csArgs);
        Obj argsElem = heapModel.getMockObj("entryobj", "method2-element", string, index);
        solver.addPointsTo(argsIndex, ctx, argsElem);

        builder.addParamObj(1, mockObj);
        solver.addEntryPoint(new EntryPoint(index, builder.build()));


        httpRequestWrapper.getDeclaredMethods().stream().filter(jMethod -> jMethod.getName().startsWith("get")).forEach(m -> {
            sources.put(m, m.getReturnType());
        });
        servletRequestWrapper.getDeclaredMethods().stream().filter(jMethod -> jMethod.getName().startsWith("get")).forEach(m -> {
            sources.put(m, m.getReturnType());
        });
        // print sources
        System.out.println("sources....");
        sources.forEach((k, v) -> System.out.println(k.getMethodSource() + "\t" + v.getName()));
        System.out.println();


        // sink
        World.get().getClassHierarchy().getClass("java.lang.Runtime").getDeclaredMethods().stream().filter(jMethod -> jMethod.getName().equals("exec")).forEach(this::add2Sink);
        World.get().getClassHierarchy().getClass("java.lang.ProcessBuilder").getDeclaredMethods().stream().forEach(this::add2Sink);
        World.get().getClassHierarchy().getClass("java.io.ObjectInputStream").getDeclaredMethods().stream().filter(jMethod -> jMethod.getName().equals("readObject") || jMethod.getName().equals("<init>")).forEach(this::add2Sink);
//        World.get().getClassHierarchy().getClass("com.vmware.vcops.casa.support.subprocess.GeneralCommand").getDeclaredMethods().stream().forEach(this::add2Sink);

        // print sink
        System.out.println("sinks....");
        sinks.forEach(s -> System.out.println(s));
        System.out.println();
    }

    Sink add2Sink(JMethod jMethod) {
        for (int i = 0; i < jMethod.getParamCount(); i++) {
            sinks.add(new Sink(jMethod, i));
        }
        return null;
    }

How should I mock uuid parameters so that it can output a taint flow from uuid to exec?

So your actual question is "how to taint parameter of custom entrypoint", is my understanding correct?

Y4er commented

Yes.

in this issue #19 (comment) I tainted the request object and successfully output the tainted flow from request.getParameter("cmd") to the exec function.

But when the request object becomes a string array like java.lang.String[] uuid

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Runtime.getRuntime().exec(request.getParameter("cmd"));
    }
    public String index(HttpServletRequest request, @PathVariable String[] uuid) throws Exception {
        exec(uuid);
        return "asd";
    }

no taint flow is output.

So I would like to ask how to simulate array object?

Thank you for your info.

The problem is irrelevant to array. It is about how to taint method parameters, or how to specify method parameters as sources.

Currently, our taint analysis only supports to specify return value as sources. So it works for #19 (comment) (taint value comes from request.getParameter("cmd")). But in this case, uuid is a parameter and not returned from any source methods, thus it is not tainted.

To address this issue, the ad-hoc solution is to implement Plugin.onNewCSMethod(), intercept entry method (index() in your case), and manually add taint object to points-to set of parameter uuid.

If it is a general requirement to specify method parameters as sources, we can implement it in our taint analysis, so that this issue can be addressed by modifying configuration file.

Y4er commented

Thank you so much!

To address this issue, the ad-hoc solution is to implement Plugin.onNewCSMethod(), intercept entry method (index() in your case), and manually add taint object to points-to set of parameter uuid.

i will try it tomorrow.

If it is a general requirement to specify method parameters as sources, we can implement it in our taint analysis, so that this issue can be addressed by modifying configuration file.

That's great! it is very helpful for software analysis of java web. It is best to provide the API related to pollution parameters.

Thank you so much again!

不客气,老哥方便加个vx吗?

Y4er commented

二维码发到你的nju邮箱了

Now our taint analysis supports to taint method parameter after commit c5d8788.

Currently, there are two kinds of sources, result (generates taint obj as result of a call site, equivalent to the original sources) and param (generates taint obj for specified method parameter, which you required), and you need to specify source kind in configuration, for example:

sources:
  - { kind: result, method: "<SourceSink: java.lang.String source()>", type: "java.lang.String" }
  - { kind: param, method: "<TaintParam: void paramSource(java.lang.String[],java.lang.String[])>", index: 0, type: "java.lang.String[]" } // the 0th parameter of TaintParam.paramSource() will be tainted
Y4er commented

This worked, thanks!