Please note: this occurred in 2018, and has been properly patched and archived. The vulnerabilities were immediately reported to Blackboard and the NTNU Security Operations Center on the 9th of February 2018. They were confirmed patched 61 days later.

How did the Blackboard exploit work?

A student (attacker) submits an assignment to his professor (victim). The professor clicks the link and evaluates the assignment. The student has now gained access to the professor’s account unbeknownst to the professor – and has full access to whatever the professor is authorized to do on Blackboard; change grades, download files not intended for students, e-mail other users and so on.

So what made this possible? Let’s walk through it.

Same-origin policy for user uploaded files

Like most learning platforms, Blackboard allows its users to upload files. This feature is primarily used by students to upload assignments and by professors to upload slides. But unlike the other platforms, Blackboard serves the uploaded files on the same origin as the application itself.

Needless to say, this is disastrous from a security perspective and in violation of one of the fundamental tenets of software security: Never trust user input. Consequently, this means that a user uploaded file on Blackboard is indistinguishable from Blackboard’s application. An unsuspecting user who views an uploaded file on Blackboard automatically grants it access to perform actions on his behalf.

The cross-site scripting filter that could be fooled

It turns out that Blackboard had anticipated that user uploaded files could abuse the same-origin policy by including malicious code. Their solution? Filter out anything that looks malicious. If your alarm bell didn’t go off at the previous section, it’s hopefully making a lot of noise now.

After a lot of trial and error we found two ways of fooling the cross-site scripting filter.

Smuggling malicious code with a null byte

\x00

Blackboard let’s users upload files using the WebDAV protocol – a true 20th century Frankenstein brought to life by Microsoft. With some technical acrobatics we discovered that the cross-site scripting filter tripped up when we uploaded a file with a null byte through WebDAV.

For some inexplainable reason a different set of filters were applied when uploading files that contained a leading null byte.

However, the null byte was no silver bullet – it only opened the door to a cleverly crafted exploit as certain characters re-activated the original filters. The malicious code in the uploaded file could therefore only consist of a small subset of harmless-looking characters.

To our surprise we discovered that with only 6 characters ([, ], (, ), !, +) one can craft perverse, yet valid JavaScript. Coincidentally, these characters did not trigger the filter after fooling it with the null byte. This meant that an attacker could simply encode his malicious code and smuggle it right past the filter, as demonstrated below.

$ hexdump -C null-byte.html
00000000  00 3c 69 6d 67 20 73 72  63 3d 2e 20 6f 6e 65 72  .<img src=. oner
00000010  72 6f 72 3d 5b 5d 5b 28  21 5b 5d 2b 5b 5d 29 5b  ror=[][(![]+[])[
00000020  2b 5b 5d 5d 2b 28 5b 21  5b 5d 5d 2b 5b 5d 5b 5b  +[]]+([![]]+[][[
00000030  5d 5d 29 5b 2b 21 2b 5b  5d 2b 5b 2b 5b 5d 5d 5d  ]])[+!+[]+[+[]]]
[...]

When omitting the null byte the filter correctly disarms the onerror attribute by replacing it with xx.

$ hexdump -C without-null-byte.html
00000000  3c 69 6d 67 20 73 72 63  3d 2e 20 78 78 3d 5b 5d  <img src=. xx=[]
00000010  5b 28 21 5b 5d 2b 5b 5d  29 5b 2b 5b 5d 5d 2b 28  [(![]+[])[+[]]+(
00000020  5b 21 5b 5d 5d 2b 5b 5d  5b 5b 5d 5d 29 5b 2b 21  [![]]+[][[]])[+!
00000030  2b 5b 5d 2b 5b 2b 5b 5d  5d 5d 2b 28 21 5b 5d 2b  +[]+[+[]]]+(![]+

Upload an XML or SVG file

A more trivial way of bypassing the cross-site script filter was to upload an XML or SVG file that referenced the HTML namespace. This blindspot allowed us to drop straight into a script element and run whatever JavaScript we pleased.

$ cat malicious.xml
<html>
  <head></head>
  <body>
    <ouch:script xmlns:ouch="http://www.w3.org/1999/xhtml">
      alert('?');
    </ouch:script>
  </body>
</html>

Just as effective, but not nearly as cool as the null byte.

The httpOnly session that wasn’t

Blackboard had taken the preventive measure of adding an httpOnly flag to the session identifier. This flag blocks JavaScript running on the same origin from retrieving session identifiers via. the document.cookie object.

Unfortunately, Blackboard rendered this protection useless by also including the session identifier in their WYSIWYG editor. This meant that an attacker only had to scrape the session identifier by sending an XMLHttpRequest (XHR) on behalf of the authenticated user – which we showed how to do in the previous sections – and forward it to himself.

The following snippet was embedded in the source code of the “Send message” feature.

new tinyMceWrapper.Editor('messagetext', {
  [...]
  tempWirisWebLocation: "https://ntnu.blackboard.com/sessions/78CFDDD4C957[...]",
  [...]
}

About the Author

USA

Umair Akbar | Cloud Engineer

Umair Akbar is a Senior Information Security Engineer with over 5 years of experience leading the development and daily management of InfoSec systems.

View All Articles