Adding Autocomplete to Gophish

Gophish provides powerful template tags making it easy to create customized emails and landing pages. Sometimes, however, it can be difficult to remember the exact syntax and what template tags are supported.

I’ve had multiple issues filed that are the result of incorrect tags being used. Generally, the crash looks something like this:

worker.go:90: template: html_template:6: function "Name" not defined
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x10 pc=0x61a4a5]

The error from template suggests that a certain function, Name wasn’t defined. In this case, the author likely used {{.Name}}, while only {{.FirstName}} and {{.LastName}} currently exist.

And I can’t blame them! It’s a pain to go to the template reference every time you want to create a new landing page or email template. I’ve tried to add better error handling to catch this when the HTML is submitted, but we can do even better by adding autocomplete that appears when you try to use a template tag.

Before going into the specifics of how this works, let’s talk a bit about the editor used for Gophish.

The Editor that Powers Gophish

When creating Gophish, I had a bit of a unique need. Most WYSIWYG editors focus on editing the content of a document, but I needed to be able to work with entire HTML documents.

At the time, the only editor I could find that could do this was CKEditor. The interface was clean, and it had the ability to work with entire HTML documents by setting the fullPage attribute in the configuration, which essentially causes CKEditor to orchestrate an iframe with your HTML content.

Note - I’m limited to using CKEditor 4, since version 5 moves away from the ability to edit full pages, instead focusing on a better document editing experience. Fortunately, CKEditor 4 seems to still be under active development.

It had been a while since I upgraded the version of CKEditor used by Gophish, so after applying the latest CKEditor 4 version, I was ready to focus on autocomplete.

Adding Autocomplete

With our upgrade, we got the ability to use some new plugins, including one that implements autocomplete. We can use this to add autocomplete for the various template tags that Gophish provides.

First, we need to define the template tags in a variable:

var TEMPLATE_TAGS = [{
        id: 1,
        name: 'RId',
        description: 'The unique ID for the recipient.'
    },
    {
        id: 2,
        name: 'FirstName',
        description: 'The recipient\'s first name.'
    },
    ...

With the valid template tags defined, we can start implementing the functions as described in CKEditor’s autocomplete documentation.

I won’t dive into every function, since many of them are near-verbatim from the documentation. It is, however, worth pointing out what we had to do differently.

Updating the Match Pattern

Golang template tags (and, by extension, Gophish template tags) are defined using the {{.Variable}} syntax, which is different than the autocomplete example which uses a [[variable]] syntax. This means we had to adjust the pattern we used to match text:

// Matches everything after "{{."
var pattern = /\{{2}\.?([A-z]|\})*$/,

You’ll notice that the pattern includes \.?, which means to match zero or one “.” characters. This was a little touch, since I wanted matches to appear as soon as the user typed “{{“. Having a leading “.” Is something that, from what I can tell, is a bit unique to Golang and is tricky to remember, so this lets us get around that.

Returning the Output

Once we get our text, show the user the matching options, and one is selected, we need to add the tag to the editor. This is done by providing an outputTemplate, and uses CKEditor’s own template syntax.

This causes a problem, since CKEditor’s templates do simple regex checks looking for the {variable} syntax, which clashes with our own template variables. Basically, we can’t tell CKEditor that we actually want to insert braces, not get a value from a variable.

To get around this, I took a fairly naive approach, which is to define my outputTemplate using the [[variable]] syntax, replacing the brackets with braces when the value is actually returned:

outputTemplate = '[[.{name}]]';
// Skipping some lines...
autocomplete.getHtmlToInsert = function (item) {
    var parsedTemplate = this.outputTemplate.output(item);
    parsedTemplate = parsedTemplate.replace("[[", "{{").replace("]]", "}}")
    return parsedTemplate
}

It’s not ideal, but it works!

Wrapping Up

With all the logic finished up, I just added some minor style tweaks and we’re off to the races!

Here’s a demo showing what the final result looks like:

While this is a great start, it’s unfortunately not perfect. For one thing, it doesn’t work in source mode - a limitation put in place by CKEditor.

The other issue is that the enter and tab keys don’t work for selecting a result from the drop down. This is a known bug that occurs when you switch from the source code view in the editor to the WYSIWYG view. Since we start in the source view, this will always be the case. Hopefully this bug will be fixed in a later release.

Minor issues aside, autocomplete is a great example of making things “just work”, which is one of my biggest goals with Gophish. I want to make it as easy as possible to use the software, and this change makes it easy to use all the power of template variables without the hassle of remembering them.

Enjoy!