Stored XSS in Schoology

tl;dr: Schoology blog posts accept a plain HTML document via a tinymce editor, which may be injected with arbitrary elements, including iframes and event handlers.

Background

Schoology is an educational platform similar to Blackboard, Moodle, and Edmodo, that aims to coordinate the educational “full stack” with built-in grading, assignments, tests, discussions, and club pages. They claim to have “7.5 million users across 60,000 schools in about 200 countries”, so take that as you will.

The Bug

The bug lies in Schoology’s class and group discussion posts, which may be rich text formatted.

a Schoology group discussion, which features a rich text editor

Group discussions

a class discussion, featuring the same rich text editor

Class discussion

This editor, based on tinymce, is little more than an iframe containing a contenteditable body and some controls to insert elements. Example:

text in bold, italics, and list formats
the HTML corresponding to the rich text

Formatted text and the corresponding HTML

It turns out, you can add whatever HTML you want to this iframe, and Schoology will accept it after doing some weak filtering and sanitization.

Exploits

Injecting a plain <script> tag doesn’t work, and neither do all the filter evasion techniques I know (this might say more about my own inexperience than Schoology’s security). They appear to be disabling any <script> tags by setting its type to mce-text/javascript which won’t run, and surrounding the code inside with // <![CDATA[ ... // ]]>. I’m not entirely convinced this is secure, and there may be a way to break it.

schoology's sanitization method

Sanitizing script tags

In addition, event handlers like onmouseover are stripped out, meaning we can’t craft a clever attack that way either.

However, those event handlers still persist within the editor itself, even if they end up stripped out when published. In addition, we can insert iframes, meaning if we don’t care about getting user data, and instead only want to cause some trouble or mine cryptocurrency, it’s possible.

event handlers

Even though these handlers disappear in the final output, the HTML you submit gets rehydrated in the editor any time someone edits the post at a later date, and the event handlers stick around. This means you can attach an onmouseoverevent to the text, and the next time anyone edits your post, they will run your payload. The bonus is that users who have the permissions to edit your post are typically high value targets: club leaders, administrators, and teachers.

an injected mouseover handler on a <p> tag

injecting onmouseover

Boom!

iframes

iframes let you run any code you want, at the cost of accessing Schoology’s API or the user’s cookies. They are likely allowed in order to enable embedding YouTube videos, but we can use them for our own benefit, and as you’ll see, there are many potential applications for this.

Cryptocurrency miner

Running a crypto miner like Coinhive is trivial, and because posts show up on both the user’s main dashboard as well as on the club’s page, there is plenty of time for the miner to run. After a few days of testing, I was able to collect a few cents of revenue from my club’s members :)

Phishing

More worrying, by accessing window.top, we can redirect the user anywhere we want! All we need is an iframe whose contents contains something like

window.top.location.href = "http://example.com/"

Some advertisements use this method to redirect the entire page to a spam site, but we can use this to phish users very convincingly by directing them to a faked Google OAuth page or Schoology login page. This is easily fixed by using the sandbox property and disallowing allow-top-navigation.

Causing trouble

By redirecting window.top, one can deny access to Schoology’s main page, which makes it impossible for someone to delete the post, edit it, or leave the group.

Also, alert inside any level of nesting within an iframe will still show up in the top level window, meaning we can alert a message any time someone loads the page.

CSRF

While I haven’t found any CSRF bugs in Schoology (yet), an iframe hidden in a post would be a great place put it. We can guarantee that anyone who loads it is both part of the organization and logged in.

Response

Hi Jesse,

Thank you for taking the time to contact the Schoology Help Desk regarding this issue.

Unfortunately, while we greatly appreciate any help of this nature from users, we do not currently offer a bug bounty. Thank you for providing us this information and rest assured that I’ll pass it along to our Engineering team.

If you have any questions regarding this process, please let me know. I’m happy to help!

At the time of writing, this bug has not been fixed.