If Kroki has been enabled, it’s possible to craft a pre
block so that arbitrary attributes can be injected into the resulting img
tag.
The css selector for finding a valid node to convert into a kroki diagram checks for either pre[lang="#{diagram_type}"] > code
or for pre > code[lang="#{diagram_type}"]
, but the diagram type is then set using node.parent['lang'] || node['lang']
.
So if the code
block has a valid lang (such as wavedrom
) then the css selector will match, but if the parent pre
also has a lang
attribute then it will be the one that is used and can be an arbitrary value.
https://gitlab.com/gitlab-org/gitlab/-/blob/v15.6.2-ee/lib/banzai/filter/kroki_filter.rb#L17
diagram_selectors = ::Gitlab::Kroki.formats(settings)
.map do |diagram_type|
%(pre[lang="#{diagram_type}"] > code,
pre > code[lang="#{diagram_type}"])
end
.join(', ')
xpath = Gitlab::Utils::Nokogiri.css_to_xpath(diagram_selectors)
return doc unless doc.at_xpath(xpath)
diagram_format = "svg"
doc.xpath(xpath).each do |node|
diagram_type = node.parent['lang'] || node['lang']
diagram_src = node.content
image_src = create_image_src(diagram_type, diagram_format, diagram_src)
The diagram_type
is then used as-is to create a url, which is used to create an image with <img src="#{image_src}" />
. So if a double quote is used in the diagram_type
then arbitrary attributes can be added (apart from class
as that is replaced just below).
https://gitlab.com/gitlab-org/gitlab/-/blob/v15.6.2-ee/lib/banzai/filter/kroki_filter.rb#L31
image_src = create_image_src(diagram_type, diagram_format, diagram_src)
img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{image_src}" />))
img_tag = img_tag.children.first
next if img_tag.nil?
lazy_load = diagram_src.length > MAX_CHARACTER_LIMIT
img_tag.set_attribute('hidden', '') if lazy_load
img_tag.set_attribute('class', 'js-render-kroki')
img_tag.set_attribute('data-diagram', diagram_type)
img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(diagram_src)}")
node.parent.replace(img_tag)
Kroki
is enabled at /admin/application_settings/general
{F2080922}<a><pre><code>xss</code></pre></a>
Refused to execute inline event handler because it violates the following Content Security Policy directive
Since the class
attribute cannot be set finding a CSP bypass was a bit tricky but there are still a few data
based attributes that can be used, one of them being data-diff-for-path
from single_file_diff.js
. This is used as the path to load when the “expand diff” chevron is clicked allowing an arbitrary json file to be loaded and have jquery execute it to bypass the CSP.
return axios
.get(this.diffForPath)
.then(({ data }) => {
this.loadingContent.hide();
if (data.html) {
this.content = $(data.html);
Since clicking the chevron is a bit unlikely, we can inject the style
attribute to make the kroki overlay the entire page, which when clicked injects some styles to make the chevron
now overlay the entire page.
aaa.json
containing {"html":"<script>alert(document.domain)</script>"}
, then open the raw
version and make note of the path.data-diff-for-path
with the path to your json file noted above:<a>
<pre>
<code>csp</code>
</pre>
<pre>
<code>
bypass
</code>
</pre>
</a>
{F2080931}
Allows arbitrary javascript to be executed when a victim views a comment
The the lang attribute from the parent node is always used even if the css selector matches the child node
The lang attribute should only be used if it is actually valid. The img
tag should also be created using content_tag
instead of string concatination.
$ sudo gitlab-rake gitlab:env:info
System information
System: Ubuntu 20.04
Proxy: no
Current User: git
Using RVM: no
Ruby Version: 2.7.6p219
Gem Version: 3.1.6
Bundler Version:2.3.15
Rake Version: 13.0.6
Redis Version: 6.2.7
Sidekiq Version:6.5.7
Go Version: unknown
GitLab information
Version: 15.6.2-ee
Revision: 08b668e8740
Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
DB Version: 12.12
URL: http://gitlab.wbowling.info
HTTP Clone URL: http://gitlab.wbowling.info/some-group/some-project.git
SSH Clone URL: [email protected]:some-group/some-project.git
Elasticsearch: no
Geo: no
Using LDAP: no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version: 14.13.0
Repository storage paths:
- default: /var/opt/gitlab/git-data/repositories
GitLab Shell path: /opt/gitlab/embedded/service/gitlab-shell
Allows arbitrary javascript to be executed when a victim views a comment