Discourse offers the possibility to install themes from remote Git repositories. Before this commit it was possible to inject OS commands via a maliciously crafted theme which is pulled via Git.

The root cause for the issue lay in the parsing of the .discourse-compatibility file which is a yaml file containing a mapping of the target discourse version and a git version to be checked out for that specific discourse version.

The version information is passed to

lib/theme_store/git_importer.rb:

  def import!
    if @private_key
      import_private!
    else
      import_public!
    end
    if version = Discourse.find_compatible_git_resource(@temp_folder)
      Discourse::Utils.execute_command(chdir: @temp_folder) do |runner|
        return runner.exec("git cat-file -e #{version} || git fetch --depth 1 $(git rev-parse --symbolic-full-name @{upstream} | awk -F '/' '{print $3}') #{version}; git reset --hard #{version}")
      end
    end
  end

Here we can inject shell commands into the version variable simply by providing a .discourse-compatibility file in the git repo containing:

2.6.4: master`id>/tmp/haxx`

On a discourse installation running version 2.6.4 this will write the current user id to /tmp/haxx when importing or updating the theme, as the find_compatible_git_resource method did not further sanitize the .discourse-compatibility entires.

The issue has been reported to discourse on April 11th 2021 via their bug bounty program and was resolved three days later.