It's not every day you find a CSRF-RCE, where sending an admin to a malicious webpage gives you a shell on their server, but that's what I discovered while exploring the security of the Oculus developer portal. But I’m getting ahead of myself.
Oculus, known best for their Oculus Rift virtual reality headset, was founded in 2012 and quickly grew, raising over $2 million through Kickstarter for their first VR development kit, the DK1. Development continued towards the DK2, and in March 2014 Facebook announced that they would acquire Oculus VR for $2 billion, placing the Oculus websites in scope of the Facebook whitehat bug bounty programme in August 2014. That's when I decided to take a look.
It started with a BSQLi
There are many places to try to inject SQL during a pentest; parameters, cookies, headers, etc. The latter, header injection doesn't often pay off, but in this case the Oculus developers had decided that somewhere down the line in certain scripts they'd insert the X-Forwarded-For header into the database. Without proper escaping.
A little single-quote magic and we're in. With a little help from sqlmap, I enumerated the structure of the Oculus database and noticed something rather interesting. While Oculus passwords are stored in some bizarre multi-hashed format making cracking a tough route to go down, user session information is stored unprotected in a table in the database. Opening up sqlmap again, I built an SQL query to extract the freshest session token for the admin user and plugged it in. Bingo.
How could I resist
Having got this far it was hard to turn back and I decided to push on. The Oculus admin user has access to a special admin panel containing all sorts of goodies. I could edit users and projects, add news articles, edit the dashboard, upload SDK files, all sorts. I was about to try uploading a PHP shell when something else caught my eye.
In PHP, the eval() function is a dangerous thing. It allows you to directly execute a string as PHP code, which in turn lets you do fun things like execute system() commands, so I was surprised to find it used freely in the admin portal. I'll let the screenshot speak for itself, but suffice it to say I now had a shell on the Oculus development centre server.
I was even more surprised to find that the AJAX eval() preview script called from the Oculus database management tab wasn't protected by a CSRF token, allowing anybody to force an admin user to call the script. For example, a malicious attacker could embed an image on a forum pointing to the script. When the admin loaded the page, the image would load and trigger execution of the attacker's commands on the server. Ouch.
You and I were inseparable
I enjoyed finding the first exploit and Facebook's quick and friendly responses, so I thought I'd keep looking. I subsequently found another couple of blind SQL injection vulnerabilities, an XSS in an old script, and a logic issue which allowed me to reset the admin password and re-gain access to the admin panel.
The logic problem - an insecure direct object reference - was probably the most fun vulnerability. In the Oculus developer portal, any user who is a company admin can manage other users working for the same company. With a little playing around I discovered that the username passed to the management script didn't have to belong to the current company, allowing me to move the admin user into my company and manage the account. I reset the password and logged in.
All said and done, I gained access to personal information belonging to couple of hundred thousand Oculus Rift developers (including myself!) and could plant malicious code on the server and in Rift SDK releases to be downloaded and installed on individual developers' machines.
For the first vulnerability and privilege escalation attack the Facebook security team awarded $15,000; for subsequent SQLis and the admin account takeover vulnerability they awarded $5,000 each. All in all, it's been a pretty productive week :-)