Postmortem: Trollcave

This is a post-mortem of my Trollcave vulnerable VM. As it goes into implementation details of the different components and their vulnerabilities, it can be considered a comprehensive spoiler. If you’re going to, complete the VM before reading this.

By the period between its inception and release, Trollcave was the labour of two years and a bit. But in terms of when I actually worked on it, it was the labour of about three weeks. Over the first week following its initial commit, I coded in a mad frenzy. Then I forgot about it for exactly a year, spent another frenzied week on it, and shelved it again. Six months later, I dug it up and forked it for use in an internal challenge at my workplace (this version had 2FA). Finally, around the two-year anniversary of the VM’s creation, I made the final push and released it. Maybe I can only work on vulnerable VMs in October. Must be down to the phases of the moon or something.

Trollcave has three main component parts:

The Trollcave website
A mostly functional Ruby on Rails community forum/blogging site. It’s written in Rails 4, which was the stable version of Rails available in October 2015.
cooldude89
A simulated moderator that does little more than visiting a page repeatedly and waiting to be targeted by a cross-site scripting attack. Written in an unholy combination of Ruby and JavaScript.
The King’s Calculator
A node.js application available on the Trollcave server’s local loopback address. It’s supposed to be the site admin’s personal development learning project, but I didn’t really hint about that much anywhere. It’s used as the final privesc once you’re on the machine.

Below, a bit about each of these.

The Trollcave website

The main idea behind the Trollcave website was privilege escalation, but on a webapp.

I built a fairly functional web application for my vulnerable VM because it’s not something I see very often in vulnerable VMs and CTFs. Usually, if there’s a web component, it’ll be a very basic set of pages at random URLS that obviously exist to be exploited, or it’ll be a default deployment of a vulnerable version of some open-source software. There’s nothing wrong with that, and it generally does serve the purpose, but I wanted more. I wanted the people who completed my VM to feel like they were hacking a real website people actually used, and so I went totally overboard with features like blogs, private messages, and even a reporting system that is completely irrelevant to the compromise path.

I wrote it in Rails mostly because I know Rails better than just about any other web framework, but also because there aren’t a lot of vulnerable VMs with Rails websites. Ruby has long been my favourite scripting language, so it was inevitable that I’d try out Ruby on Rails at some point. The first time I used it, I was enthralled by the power of its generators. By just typing a bunch of rails generate commands, it was possible to build a whole CRUD application with a staggering number of out-of-the-box features1. After that experience, I taught myself to write Rails websites one semester break while I was at university, in preparation for a website project that never ended up getting off the ground.

My main source for learning Rails was Michael Hartl’s Rails Tutorial, and I returned it when I got the idea of building the Trollcave site. The tutorial’s example site had about 70% of the features I needed in the Trollcave site, and the remaining 30% were easy to rails generate, so I was able to get something very functional up and running pretty swiftly.

The plan with the first vulnerability, which hinged on the specifically named password_resets controller, was that people doing the VM would find the relevant section of the Rails Tutorial in their research and use it to learn about the site’s inner workings.2 I’m really proud of that one, because it forces you to get some understanding of how the site actually works.

The second vulnerability was stealing cooldude89’s cookie using XSS – a pretty standard attack. To get this one to work, I had to explicitly disable the HttpOnly flag on the site’s cookies (Rails sensibly sets HttpOnly for all cookies by default). I originally wanted to avoid this by enabling HTTP TRACE on the site’s webserver and making the XSS attack into a much cooler cross-site tracing one, but I couldn’t find a version of any *nix webserver that supported TRACE, so I settled for boring old XSS.

The application’s third vulnerability was an authorisation bypass that let you promote other users to the role of admin. This too required some understanding of how Rails in general and the Trollcave site’s user privilege model in specific worked.

The fourth vulnerability is my favourite, because it requires you to combine what you’ve learnt from the first and the third vulnerabilities. You have to use your new admin powers to demote other users in the same way you previously promoted your user, and then use the initial password reset vulnerability to gain access to the account of the user you just demoted. I like to believe that’s some solid game design.

Throughout all of this, you can read blogs and comments posted on the Trollcave site by its users. Some of these are merely for flavour, but most provide oblique hints about the next step. Because the site includes a feature that lets users control the visiblity of their blogposts, as you privesc through the app, you see more posts, with the most recently visible ones containing the most immediately relevant hints.

Together, the posts also make up a very loose story of sorts. This is an area I initially wanted to put more focus on, as a way of tying together my interests in information security and interactive narrative3, but I haven’t quite figured out an entirely satisfactory way to tell a story in a vulnerable VM. While the blogpost clearance system presents a seemingly ideal mechanism for just this, conveying narrative becomes much more difficult once the player (?) gets shell access – especially if you’re going for immersion and trying to do an epistolary kind of thing rather than having wall spew out narration or whatever. You also implicitly have much less ability to railroad your audience than you would in a game, because the technologies you’re using aren’t all ones you’ve custom-built to deliver one particular experience.

I’m going to have to experiment a bit more.

cooldude89

Initially, I wrote the cooldude89 user as a Mechanize script called coolguymod.rb. The script would log into the website and then visit a particular page over and over, about once every 50 seconds. This worked very well until I belatedly realised that I needed this user to execute JavaScript on the page they were visiting. Mechanize, being a set of helpful wrappers for constructing and sending HTTP requests, did not provide this capability.

I recalled that Selenium, Python’s premiere browser automation library, did actual browser automation by physically opening a browser and programmatically interacting with it, and so considered rewriting cooldude89 with that. But I was pretty sure that wouldn’t work in a VM without a graphical environment installed, and I didn’t want to add all that bloat to Trollcave.4

So, being too lazy to rewrite any code, I did a bit of Googling and eventually came upon PhantomJS, a headless JavaScript environment. I installed it on the VM and wrote a little script – coolguymod.js – that browses to a page and executes any JavaScript it finds there. I then had my original coolguymod.rb script run this new script with a shell command, passing the page it was visiting and all its cookies as two command-line arguments. More accurately, the second argument was a JavaScript function that set PhantomJS’s cookies, and would be blindly evaled by coolguymod.js.

def pass_cookies
  l = ""
    @agent.cookies.each do |c|
      l = l + ("phantom.addCookie({ " + "name: \""+ c.name+"\", " + "value: \""+c.value+"\", domain: \"trollcave.com\"});")
    end
  l
end

Gross and thoroughly insecure, but it works.

The King’s Calculator

Once you get a shell on the machine under the context of the rails user, you’ll notice a strange HTTP service running on localhost:8888. This is the King’s Calculator, a node.js web application you must use to privesc to root and complete the VM.5

You can view the application’s source code in /home/king/calc/calc.js to help you with this. The first thing you’ll probably notice is this:

function route(pathname, request, query, response)
{
	//...
	switch (pathname)
	{
		// security risk	
		/* case "/ping"
			pingit(pathname, request, query, response);
			break; */

		//...
	}

}

The function associated with this commented out route is a classic ping panel. The ping panel is often included in beginner VMs and instructional course on web application security to illustrate the command injection vulnerability. The user provides an IP address, and the server injects that IP address into a shell command that uses ping. As with SQL injection, the server will execute ping 8.8.8.8 just as well as it will execute ping 8.8.8.8 && cat /etc/passwd, and so you can use the ping panel to do all sorts of destructive stuff on the server.

In Trollcave’s case, however, the /ping route is a red herring. The functionality you should be interested in is the standard /sum route. As with coolguymod.js, I used an eval for this route, but this time as an intentional vulnerability. It was inspired by a couple of things:

  1. A vulnerable VM I completed a long time ago had an internal web application as its final privesc step. I thought it was a really clever and unique thing to have as a privesc vector – not the sort you’ll find with any of the standard checks in any Linux privesc cheatsheet.
  2. Server-side script injection, which is especially amusing when your server-side language is JavaScript. I remember reading about a really cool exploit where the attacker passed node.js code that actually started up a new node.js server on a different port, serving a webshell. Totally unnecessary of course, but very stylish.

What I love about this vulnerability is all the different ways people have gone about exploiting and getting a shell from it. My first instinct was that it would be done with SUID binary, as I detailed in the walkthrough, but other people have injected additional functions by overwriting the calc.js file, used reverse shells and – most cleanly – copied the SSH key they would have gotten on the box previously into /home/king/.ssh.

Final thoughts

Were I to go back in time and make Trollcave all over again, I would get more prerelease testing and feedback. This is something I’ve always been lousy about, whether the product is a written story, a game, a vulnerable VM or anything else. Part of it is not wanting to impose on others’ time, and part of it is this fantasy I cling to of being this lone auteur who goes into deep seclusion, labours for a long time to create something perfect, and then releases it into the world. This is stupid idea, because it makes the final product flawed and inaccessible in ways that are impossible to fix or even notice without some outside feedback.

One obvious example of such a flaw the unintended vulnerability I discussed in at the beginning of the official walkthrough, but there are deeper ones as well. The whole conceit of escalating through user roles on the application, for example, was very poorly hinted prior to version 1.1, because I had been working on the VM so long it seemed completely obvious to me what you had to do and which users had what roles.

Another problem creeps in with all the extra functionality and atmospheric blogpost fluff I included in the application – because the blogposts were full of legitimate hints, things that I never intended as hints were also read that way. This made the XSSable user somewhat difficult to reason about – he was clearly online and using the site, so could you XSS him by sending him a PM with the perfectly functional PM system? Could you do so by creating a blogpost with moderator clearance? I had anticipated and programmed one very specific thing, which was hinted somewhere, but it wasn’t necessarily logical that that worked but other, similar things didn’t. And it wouldn’t have been difficult to extend coolguymod.{rb,js} – I just didn’t think to.

All that said, I am still basically pleased with Trollcave as it currently is and have no plans to change or update it. Improvements will have to wait for the next VM. Which, going by my current track record, should be ready for release in late 2020 or thereabouts. Unless we finish making computers secure by then, obviously.


  1. I later resolved that Rails was a lot more fun in the initial building stages than when you had to maintain it, or bend its model to do something slightly unusual. ↩︎

  2. My next VM will be stitched together from insecure code snippets in popular Stack Overflow answers. ↩︎

  3. An intersection I also dabble with in my dayjob sometimes, through my involvement in HackFu↩︎

  4. I didn’t know about Headless Chrome at the time, or I might have used that. ↩︎

  5. The calculator runs under the context of the king user, who has set up his sudoers to let him use sudo without a password, which makes access to king as good as access to root↩︎


similar posts
webmentions(?)