Hacking Piazza with Cross-Site Scripting

Piazza is a free classroom discussion service marketed for science and mathematics classes. It is best described as a hybrid wiki and forum; students can post questions, and other students can collaborate on answers. Like WordPress, content can be formatted with a rich-text editor or with plain HTML with a restricted set of features. Piazza’s distinguishing feature is the ability to post anonymously, which it claims makes underrepresented groups in the sciences more comfortable with interacting with the class. At UT, the computer science department makes extensive use of Piazza for most of its classes.

Piazza is primarily accessed through the web interface on piazza.com. Of great interest, there is also a “lite” web interface designed for mobile devices and accessible browsers at piazza.com/lite. I will demonstrate that Piazza is susceptible to common client-side web attacks, such as cross-site scripting, as a result of its reliance on web apps. (There are also native iOS and Android apps, but they are awful, and nobody uses them.)

The setup

After a lecture in my network security class on cross-site request forgery, my teacher hinted to me that there was a way to embed other webpages in a Piazza post. At first, I considered the most obvious strategy: external images. But images are uninteresting because they can only be (ab)used to send GET requests to fixed URLs.

<img src="https://utdirect.utexas.edu/security-443/logoff.cgi" alt="">

After poking around the formatting controls, I discovered that Piazza allows users to embed inline frames in posts for the purpose of embedding videos. This makes sense, since most video sites (such as YouTube) use iframes in the HTML embedding code they generate for their users. But iframe is a dangerous element because it grants the ability to embed any arbitrary webpage. Therefore, Piazza takes some steps to inhibit the abuse of iframes in posts, such as whitelisting the src, width, height, and frameborder attributes and requiring HTTP or HTTPS URLs. But it does not enforce any kind of domain whitelisting or filtering, so we’re free to embed any URL of our choosing as long as it is served over HTTP or HTTPS.

<iframe src="https://utexas.edu" width="500" height="500" frameborder="0"></iframe>

I shared this finding with my teacher. Naturally, his immediate reaction was to share a private post with me that included a hidden frame embedding superlogout.com

<iframe src="http://www.superlogout.com" width="0" height="0" frameborder="0"></iframe>

XSS’d

Another interesting feature of Piazza is that its web interfaces have a number of markup injection and cross-site scripting (XSS) vulnerabilities.

One has already been publicly disclosed on openbugbounty.org. It’s within the last webpage in the Piazza registration process, which is always accessible to a client, even if he or she did not initiate a registration or is already signed into a Piazza account.

Some MIT students performed a security audit of Piazza’s interface for their network security class. (Curiously, the paper has since been taken down.) They found one XSS attack in the Piazza Careers interface, which is of limited use because it can’t be used to attack other users. They found no vulnerabilities in Piazza’s main web interface.

I have discovered two new XSS attacks myself, both within Piazza’s lite interface. The first is within the search function. If a query returns no results, it’s reflected back to the client as HTML markup without escaping or filtering.

Requesting the URL

https://piazza.com/lite/feed?query=<script>console.log("hello")</script>

returns the markup

</form>


<strong>No results for <script>console.log("hello")</script></strong>


<p>

and the browser executes the JavaScript code.

XSS attack in Piazza's search function

The second is within the post creation function. A Piazza post can be of “type” question or note. The interface forces the selection of one or the other, but in fact it is possible to fill in any value within the query string, and it will also be reflected as markup.

Requesting the URL

https://piazza.com/lite/posts/new?type=xx<script>console.log("Hello World!")</script>

returns the markup (note capitalization transformations)

</ul>



<a id="main"></a>
<h1>New Xx<script>console.log("hello world!")</script></h1>





<form action=/lite/posts method="POST" class="form-horizontal">
      <input type="checkbox" name="make_private" id="make_private" aria-labelledby="make_private_label visible_class_instructors">
      <label for="make_private" id="make_private_label">Make this a private xx<script>console.log("Hello World!")</script></label>
      <br />

and the browser again executes the JavaScript code.

XSS attack in Piazza's new post function

The second attack is more reliable because it always works; the first requires a search query that is guaranteed to return no results in any of the victim’s classes. The second attack does inject the markup twice, but this is easily accounted for with a JavaScript imitation of C-style include guards.

if (!beEvil) { beEvil=true;
...
}

Fun with JavaScript

Active content that we can inject using these XSS attacks is run within the context of the piazza.com domain because that’s where it appears to have been served from. Combined with the ability to embed an iframe to any webpage, we can now embed arbitrary JavaScript in a Piazza post that runs within the same origin, thus bypassing the same-origin policy.

Once we have the ability to execute our “trusted” JavaScript, we can do some truly frightening things to other Piazza users. In principle, anything they can do within their browsers, we can also do with our script code. For example, we can change profile pictures – this proof of concept changes one’s profile picture to a picture of Mr. Anderson.

We can also manipulate the DOM, changing, forging, and hiding posts. Worse still, since Piazza does not require reauthentication to add new email addresses, we can even change the email addresses associated with other accounts.

All of this can occur silently, in the background, by the mere act of viewing a malicious post. The silver lining is that Piazza sets the HTTP-only flag on its cookies, so its session tokens can’t be read and stolen by JavaScript.

When I tested the XSS attacks, they worked perfectly in Firefox. But my injected JavaScript code refused to run in other browsers. A glance at the developer console revealed why.

Chromium's anti-XSS filter in action

Most other browsers (Chrome/Chromium, Safari, Edge) feature “XSS filters” that reject any active content, like a section of JavaScript code, an iframe tag, a base tag, or an object tag, that is part of the URL. This is intended to be a last-resort defense against XSS attacks. In this case, it works. In Chromium, you can see the lexer flagging the JavaScript code we attempted to inject.

Chromium's anti-XSS filter flagging injected JavaScript code

How can we get around this? Well, in web development, the browser makers always make backwards compatibility concessions to please that one webmaster who would file a bug report for the broken behavior of his shoddily written site.

In the case of XSS filters, that concession is that the filters overlook injected content that comes from the first-party domain. That means we’re allowed to insert any script that is hosted on piazza.com.

query=<script src="https://piazza.com/.../path/to/something.js"></script>

At first, this doesn’t seem terribly useful. We aren’t Piazza, and we don’t control any files on their servers. Right?

Being resourceful

Instructors on Piazza can upload “resources” for their classes, files that can be accessed and downloaded by students. Resources are hosted on the Cloudfront content distribution network, but critically, Piazza also generates permalinks on the piazza.com domain.

https://piazza.com/class_profile/get_resource/<UID of class>/<UID of resource>

Furthermore, resources are accessible to anyone, as a Google search reveals. Thus, to get a malicious script hosted on the piazza.com domain, all we have to do is create a class to gain access to the resource upload function and upload the script.

And there we have it – embedded JavaScript in a Piazza post that defeats the same-origin policy as well as browser-side XSS filters!

Proof of concept Piazza exploit on a real post

I reported these vulnerabilities to Piazza’s support team on February 9. They acknowledged them and will be rolling out fixes over the “next several weeks.”

While the main Piazza web interface appears reasonably secure against XSS attacks, it seems lesser used components, such as the lite interface and the email registration process, did not receive the same amount of developer scrutiny.

The lesson here is to be security conscious about every part of your application. Anything can become a markup injection vector.

Leave a Reply