pascal-lab/Tai-e

InterTaint transfer and invoke issues in Taint Analysis

Closed this issue · 2 comments

Description

Hi,
When I was testing the taint propagation analysis plugin, I found two problems or doubts about the inter-process analysis. But I'm not sure if the framework could be optimized for them.
Thanks!

The first case
is in interface implicit invoke in spring boot controller-service framework which is commonly used i think.
The test case is SSRF in java-sec-code benchmark, could see detail code in below:
https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SSRF.java
in function RestTemplateUrlBanRedirects and RestTemplateUrl.

@GetMapping("/restTemplate/vuln1")
    public String RestTemplateUrlBanRedirects(String url){
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        return httpService.RequestHttpBanRedirects(url, headers);
    }


    @GetMapping("/restTemplate/vuln2")
    public String RestTemplateUrl(String url){
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        return httpService.RequestHttp(url, headers);
    }

Because private field variable httpService is interface, not initialize an object here (instead of @resource) and implicit invoke the method in the impl class impl/HttpServiceImpl.java.
My test result is our engine and taint analysis plugin could not detect these two vulnerabilities due to tai-e could not identify the call edge?

However, when i modify the test cases by manually adding initialization of the variable httpService,
it work!

@GetMapping("/restTemplate/vuln2")
    public String RestTemplateUrl(String url){
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        httpService = new HttpServiceImpl();  // add the line manually
        return httpService.RequestHttp(url, headers);
    }

So, i think here maybe some feature could be optimized?

The second case
in Tai-e test cases provided in:
https://github.com/pascal-lab/Tai-e/blob/master/src/test/resources/pta/taint/InterTaintTransfer.java
the excepted results is 3 vulnerabilities should be detected, but in my testing 9 vulnerabilities detected due to the variable mapping / transfer in inter taint.

[Pointer analysis] elapsed time: 0.05s
Detected 9 taint flow(s):
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[0@L4] $r0 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[6@L8] invokestatic SourceSink.sink($r4)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[0@L4] $r0 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[8@L9] invokestatic SourceSink.sink($r5)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[0@L4] $r0 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[10@L10] invokestatic SourceSink.sink($r6)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[1@L5] $r1 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[6@L8] invokestatic SourceSink.sink($r4)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[1@L5] $r1 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[8@L9] invokestatic SourceSink.sink($r5)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[1@L5] $r1 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[10@L10] invokestatic SourceSink.sink($r6)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[2@L6] $r2 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[6@L8] invokestatic SourceSink.sink($r4)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[2@L6] $r2 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[8@L9] invokestatic SourceSink.sink($r5)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[2@L6] $r2 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[10@L10] invokestatic SourceSink.sink($r6)/0}

Many wrong results are logged.

Environment

MacOs
IDEA
Ta-e version:master branch, commit id: 4c6b337(Commits on Dec 29, 2023)

Thank you very much for your attention and if you have any questions please feel free to let me know!

The first case
...
Because private field variable httpService is interface, not initialize an object here (instead of @resource) and implicit invoke the method in the impl class impl/HttpServiceImpl.java.

There may be a related issue for for your reference: #19 . To handle this scenario, some modifications may be necessary, e.g., add a mocked obj (instance of HttpServiceImpl) into the httpService's Instance Field Pointer.

The second case in Tai-e test cases provided in: https://github.com/pascal-lab/Tai-e/blob/master/src/test/resources/pta/taint/InterTaintTransfer.java the excepted results is 3 vulnerabilities should be detected, but in my testing 9 vulnerabilities detected due to the variable mapping / transfer in inter taint.
...
Many wrong results are logged.

2-call context sensitivity is required to execute this test case to detect 3 taint flow.

@MultiStringsSource({"InterTaintTransfer", "cs:2-call", TAINT_CONFIG})

Detected 3 taint flow(s):
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[0@L4] temp$0 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[10@L8] invokestatic SourceSink.sink(temp$4)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[2@L5] temp$1 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[12@L9] invokestatic SourceSink.sink(temp$5)/0}
TaintFlow{<InterTaintTransfer: void main(java.lang.String[])>[4@L6] temp$2 = invokestatic SourceSink.source()/result -> <InterTaintTransfer: void main(java.lang.String[])>[14@L10] invokestatic SourceSink.sink(temp$6)/0}

Thanks a lot!
The second case has resulted right output (3 taint flows) after i change the exec mode to 2-call.

And base on your suggestions to the first case, i tested it in the OnStart() method by adding some codes like below, and it finally worked right to detect this implicit service invoke.

JField serviceField = solver.getHierarchy().getField("<org.joychou.controller.SSRF: org.joychou.service.HttpService httpService>");
JClass serviceClass = World.get().getClassHierarchy().getClass("org.joychou.impl.HttpServiceImpl");
Obj mockFieldObj = heapModel.getMockObj(clazzDescriptor, "<joychou-httpservice-impl>", serviceClass.getType());
builder.addFieldObj(mockThisObj, serviceField, mockFieldObj);

Thank you very much, and i would continue to use and study Tai-e !