Teacher's Pinboard Write-up

I found the address of the teacher's pinboard! Can you try to get in and read all teachers' notes? Maybe you need to attack the admin account as well.

The fluxfingers (again) hosted the Capture The Flag (CTF) event for the hack.lu security conference in Luxembourg. It's long since I've left the university but I'm still fond of both the hack.lu CTF and fluxfingers, so I contributed two tasks for the event.

Since nobody bothered to publish their solution for the challenge, I have decided to release my own. By the way, you can still access the challenge in the archive and I recommend you take a look yourself, before you continue reading this spoiler.

screenshot

The teacher's pinboard is a website that provides you a log-in form that accepts any kind of username and password combination. After logging in, you are provided with a typical pinboard and you might notice that the website has provided you three cookies of the following format:

session=7341e6fa2ab3b37f26e0cac6fedc25f3438e5fb9b12a9daed890459aa21f349b12676b3d;
accountinfo=(dp0%0AS'username'%0Ap1%0AS'foo'%0Ap2%0AsS'password'%0Ap3%0AS'bar'%0Ap4%0AsS'admin'%0Ap5%0AI00%0As.;
signature=81a380499ae8a9801c1b791da88d93bb5c74224ce59c60ae4a5010bc943ad85f

The accountinfo looks interesting!

Lets decode it:

(dp0\nS'username'\np1\nS'foo'\np2\nsS'password'\np3\nS'bar'\np4\nsS'admin'\np5\nI00\ns.

With some research (or experience), one might notice that this is the output of Python's pickle module. Unpickling this thus leads the following dictionary:

{'admin': False, 'password': 'bar', 'username': 'foo'}

Setting admin to True does not work, as the other cookie (signature) seems to enforce integrity of the accountinfo data. Another idea would be to attempt generic pickle exploits (like so), which did not work out as well.

After some further close inspection, the user might notice that the JavaScript that comes with the pinboard does not only provide the nice user interface for playing with the notes. It also deals with the cookies! First it reads the cleartext password in your cookies and uses that to derive an encryption key. This key is then used to store the current notes in localStorage. Why? Mostly decoy, actually.

The interesting part is how it looks a the cookies. It does so by executing pickle.loads() in JavaScript.

"Pickle in JavaScript??" you say. Yes, in JavaScript. For this challenge, I have re-implemented Python's pickle module in JavaScript. Pickle is not actually a storage format but a nice little stack machine, that is easy to understand and fun to read. Take a look at /usr/lib/python2.7/pickle.py for example. The JavaScript pickle code is worth a read too, but the concept is generally the same: If you want to execute a function, you load a reference to a global on the stack and call it using R (reduce). Parameters of said function should be a tuple on the stack just below the function.

So what would we do in JavaScript, if we wanted to execute arbitrary code but not write a pickle thing by hand? Well, throw our source code on the stack and call eval() of course. Turns out, the pickle.js author (me) thought of that too and implemented a blacklist, that disallows certain things:

  const BLACKLIST = ["require", "eval", "setTimeout", "setInterval", "setImmediate"];

So that still leaves us with the Function constructor. I also know that Teams who solved this challenge used process.mainModule.require, which should also work. In general, the blacklist was more of a hint that one was going in the right direction than a real blocker.

I ended up writing a compiler that takes JavaScript source code and produces a pickle object that executes said code:

function compiler(s) {
  // c= load global "Function" on stack
  // ( = set marker on stack, S = create String
  // t = create tuple from marker to top of stack
  //     (i.e. put string in a tuple)
  // R = call top-of-stack - 1 with top-of-stack as the argument
  //
  // i.e., call Function(), with s as param  
  var pickle = "cglobal\nFunction\n(S'"+s+"'\ntR";
  pickle +=    "(tR."
  return pickle;
};

Executing arbitrary code means you can now go through the directory and either find the file with the flag or find the source code and the secret key to produce an HMAC for a cookie that has admin set to True.

Other posts

  1. Help Test Firefox's built-in HTML Sanitizer to protect against UXSS bugs
  2. Remote Code Execution in Firefox beyond memory corruptions
  3. XSS in The Digital #ClimateStrike Widget
  4. Chrome switching the XSSAuditor to filter mode re-enables old attack
  5. Challenge Write-up: Subresource Integrity in Service Workers
  6. Finding the SqueezeBox Radio Default SSH Passwort
  7. New CSP directive to make Subresource Integrity mandatory (`require-sri-for`)
  8. Firefox OS apps and beyond
  9. Teacher's Pinboard Write-up
  10. A CDN that can not XSS you: Using Subresource Integrity
  11. The Twitter Gazebo
  12. German Firefox 1.0 ad (OCR)
  13. My thoughts on Tor appliances
  14. Subresource Integrity
  15. Revoke App Permissions on Firefox OS
  16. (Self) XSS at Mozilla's internal Phonebook
  17. Tales of Python's Encoding
  18. On the X-Frame-Options Security Header
  19. html2dom
  20. Security Review: HTML sanitizer in Thunderbird
  21. Week 29 2013
  22. The First Post