Attacker can disclose information on confidential issues moved to private programs through Web-UI and GraphQL API

HackerOne report #758939 by 0xwintermute on 2019-12-15, assigned to @ankelly:

Summary

Issues opened in a public project and then moved to a private project reveal the private project namespace even though if that information is not accessible through the victim nor attacker activity page. An additional query to the GraphQL will reveal the private program, the issue name and issue number.

Steps to reproduce

As victim create two projects (one private and one public). As attacker create an issue in the public project.

Now, if the victim moves the issue to a private project, the attacker will have access to the private project namespace, but also will be able to gather information about the new issue by simply querying the GraphQL API

Impact

This attack, will let an attacker to reveal the name of a private project, including issue name and issue number.

This scenario only seems to happen, when the new issue remains confidential in a private program. If the issue becomes non-confidential but still belongs to a private program. Then the GraphQL endpoint will return different information.

Example

For exploiting this vulnerability, lets gonna use two different accounts:

  • victim@gitlab.com
  • attacker@gitlab.com

As victim@gitlab.com lets create two different projects:

  • 0xroot/0xroot-project: owner and public
  • sebasbf/private-program: maintainer and private.

Projects.png

The account attacker@gitlab.com is a guest of the project 0xroot/0xroot-project and decides to create a new confidential issue in the program, as shown below:

Projects_2.png

Projects_3.png

Getting back to our victim@gitlab.com account. As project owner, we decide to move the confidential issue from 0xroot/0xroot-project to the private program sebasbf/private-program and keep the issue confidential.

Issue_moved.png

As the attacker (attacker@gitlab.com), we will get the following notification in the issue ticket:

Attacker_Issue_moved.png

This is something I wouldn't expect to see. As it reveals that the issue has been moved to a private program, including the program namespace and the issue number, which reveals information about a private program and how many issues have been created at the time.

This information, is no available otherwise through the web application interface.

If as an attacker, we decide to visit the victim's profile overview tab and the personal projects tab. We will see that such information about the private program is not public:

victim_overview_tab.png

Victim_personal_project_tab.png

Additionally, if we open a a new graphql explorer tab as the attacker and submit the following query, we will gather similar results (please remember, that we are pinging a confidential issue in a private program, as we will get back to this later on):

GraphQL Query:

query HackerOne{  
  project(fullPath:"0xroot/0xroot-project"){  
    openIssuesCount  
    nameWithNamespace  
    issue(iid:"4"){  
      state  
      confidential  
      description  
      descriptionHtml  
      discussions{  
        nodes{  
          id  
          notes{  
            nodes{  
              author{  
                name  
              }  
             bodyHtml  
              project{  
                path  
              }  
            system  
            }  
          }  
        }  
      }  
      closedAt  
      state  
      webUrl  
      webPath  
    }  
  }   
}

GraphQL Response:

{
  "data": {  
    "project": {  
      "openIssuesCount": 1,  
      "nameWithNamespace": "0xroot / 0xroot-project",  
      "issue": {  
        "state": "closed",  
        "confidential": true,  
        "description": "This is a confidential issue in 0xroot-project",  
        "descriptionHtml": "<p data-sourcepos=\"1:1-1:46\" dir=\"auto\">This is a confidential issue in 0xroot-project</p>",  
        "discussions": {  
          "nodes": [  
            {  
              "id": "gid://gitlab/IndividualNoteDiscussion/79a246bb9440d09f704c88e451f14285458f49e8",  
              "notes": {  
                "nodes": [  
                  {  
                    "author": {  
                      "name": "Sebas"  
                    },  
                    "bodyHtml": "<p data-sourcepos=\"1:1-1:34\" dir=\"auto\">moved to <a href=\"/sebasbf/private-program/issues/2\" data-original=\"sebasbf/private-program#2\" data-link=\"false\" data-link-reference=\"false\" data-project=\"15883104\" data-issue=\"28720283\" data-reference-type=\"issue\" data-container=\"body\" data-placement=\"bottom\" title=\"Testing issue\" class=\"gfm gfm-issue has-tooltip\">sebasbf/private-program#2</a></p>",  
                    "project": {  
                      "path": "0xroot-project"  
                    },  
                    "system": true  
                  }  
                ]  
              }  
            }  
          ]  
        },  
        "closedAt": "2019-12-15T15:36:26Z",  
        "webUrl": "https://gitlab.com/0xroot/0xroot-project/issues/4",  
        "webPath": "/0xroot/0xroot-project/issues/4"  
      }  
    }  
  }  
}

As you can see, the bodyHtml tag reveals a pretty fare amount of sensitive information about a private program for which I should not have access, including the data-project, the data-issue and the data-project tags.

Something that additionally caught my attention, is that if the victim set this new issue as non-confidential in the private program, the attacker will not have access to the information shown above. Let's see this with a practical example:

As the victim, I'm going to turn off the confidential attribute for this new moved finding into a private program:

Victim_non-confidential_issue_private_program.png

If as an attacker I try to repeate the same GraphQL query listed above, the results may differ considerably:

GraphQL Query

query HackerOne{  
  project(fullPath:"0xroot/0xroot-project"){  
    openIssuesCount  
    nameWithNamespace  
    createdAt  
    issue(iid:"4"){  
      state  
      confidential  
      description  
      createdAt  
      descriptionHtml  
      discussions{  
        nodes{  
          id  
          notes{  
            nodes{  
              author{  
                name  
              }  
             bodyHtml  
              project{  
                path  
              }  
            system  
            }  
          }  
        }  
      }  
      closedAt  
      state  
      webUrl  
      webPath  
    }

  }  
}

GraphQL Response

{
  "data": {  
    "project": {  
      "openIssuesCount": 1,  
      "nameWithNamespace": "0xroot / 0xroot-project",  
      "createdAt": "2019-12-14T21:17:42Z",  
      "issue": {  
        "state": "closed",  
        "confidential": true,  
        "description": "This is a confidential issue in 0xroot-project",  
        "createdAt": "2019-12-15T15:35:48Z",  
        "descriptionHtml": "<p data-sourcepos=\"1:1-1:46\" dir=\"auto\">This is a confidential issue in 0xroot-project</p>",  
        "discussions": {  
          "nodes": []  
        },  
        "closedAt": "2019-12-15T15:36:26Z",  
        "webUrl": "https://gitlab.com/0xroot/0xroot-project/issues/4",  
        "webPath": "/0xroot/0xroot-project/issues/4"  
      }  
    }  
  }  
}

As you can see, this time the GraphQL endpoint does not reveal any sort of sensitive information about the issue.

What's the current bug behaviour?

Moved confidential issues into a private program, lets an attacker gather sensitive information about the program name, the issues counter, the program creation date, and the issue's name. Which otherwise shouldn't be accessible.

Additionally, the web application interface shows information about a private program, which otherwise is not publicly accessible neither through the victim nor attacker overview activity tab.

What's the expected correct behaviour?

The way I see this I would expect few things to happen:

  • If a confidential issue is moved to a private program, for which I do not have access. I shouldn't be able to query the GraphQL endpoint and gather sensitive information about that private program. This is something that is currently happening when the moved issue to a private program is no longer confidential. But if the issue remains confidential, the attacker will be able to extract such sensitive information.

  • Additionally, I would expect the user interface to do not expose information about the private program and the issue number that has been generated, if the attacker does not have prior access to that program. This is some sort of information that otherwise is not being disclosed through the overview activity tab in the vctim and attacker account. So, I would expect a behavior in the same line for the issue page.

Relevant logs and/or screenshots

Please see the attached screenshots on the example section

Output of checks

This bug happens on GitLab.com

Results of GitLab environment info

This is affecting the gitlab.com site

Impact

Please see the above description provided

Attachments

Warning: Attachments received through HackerOne, please exercise caution!