Skip to content

Ghostwriter

Ghostwriter is an open-source reporting platform, released by SpecterOps. Source code is on GitHub and docs are on a wiki.

We have an AmberWolf Ghostwriter instance at https://gw.ts.amberwolf.com, which runs an internal fork of the upstream project.

Access to Ghostwriter

Ghostwriter is only accessible from the Tailscale network. There have been some problems with resolving the hostname when connected to particular exit nodes (thanks to the complexities of Tailscale DNS) but these should now be fixed.

AmberWolf consultants can use SSO authentication with the "Microsoft" button on the login page. (Note that an account for an SSO user is only created on first login, so e.g. it will be necessary for new employees to log in before they can be added to any projects). All SSO accounts are in the user role.

There are some manager accounts - these are local accounts and do not use SSO. Currently, Gavin Holt, Julian Storr and Richard Turnbull have manager accounts. These are required to create new clients and projects (see below). The manager accounts are not to be used for day-to-day reporting work.

Ghostwriter hosting

Our Ghostwriter instance is running on an EC2 server in the corporate AWS account. Port 443 (for the web interface) is exposed, along with port 22. It uses a local database (a Postgres container backed by a Docker volume on the host). Daily backups of the database are written to an S3 bucket.

Ghostwriter version

We are currently running v4.3.9, with some of our own modifications (see https://git.ts.amberwolf.com/Consulting/GhostwriterAW/src/branch/aw_modifications_4.3.9).

In general we want to stick closely to the released versions to avoid update headaches, but we have applied some changes: * Implemented the pull request which provides comment functionality in the rich text editor, to evaluate its use for QA * Implemented part of a (closed) pull request to patch an access control flaw/feature which we weren't comfortable with (more details on this below). There are two commits in this pull request - we have only added the one (7cf734fa) which fixes the issue rather than the one which adds some GUI functionality. * Modified the access controls on tag and taggeditem objects so that they can be manipulated by normal users via the GraphQL API. This allows synchronisation with our findings repository to be carried out by a normal user account. * Removed some fields (namely Impact, Host Detection Techniques and Network Detection Techniques) from the form used to create a finding - we don't use these in our report templates and the form is less cluttered without them. * Added a "Paste as text" button to the Edit menu in TinyMCE.

Ghostwriter templates

Ghostwriter requires Word template documents to produce reports. Our templates are stored in a Git repo. There are some notes in the readme there about template usage and development.

We currently have three different templates for producing reports: * AmberWolf Report Candidate (Split by Phase) * AmberWolf Report Candidate (No Phase Split) * AmberWolf Report Candidate (Hybrid)

The first of these splits up the findings in the report by the associated phase, the second does not, and the third is somewhere in between. More details on this below.

A lot of the discussion below about creating reports assumes the use of these templates, and the custom fields which we have added to Ghostwriter to support them, and does not necessarily apply to Ghostwriter in general.

Bugs / Suggestions / Requests

Raise these as issues on the GhostwriterAW repo if they relate to the Ghostwriter app, or the templates repo if they relate to the report templates. Obviously some things might fall somewhere in between, in which case either location is fine.

Testing

There is currently a test client, project and report in the Ghostwriter instance. Contact one of the Ghostwriter managers if you want to be added to these.

Clients, Projects and Reports

Ghostwriter has client objects which represent customers. Project objects are created, assigned to a single client - these represent client engagements. Each project can have a number of report objects.

This is superficially similar to the data model in Limb, but there is a key difference in that Ghostwriter findings belong to a report rather than a project. Therefore we can't, for example, cut two separate reports from the same set of findings in the way that Limb allowed, although we could achieve something similar through the use of different report templates in Ghostwriter.

Most of the project functionality relates to resourcing/scheduling and red team infrastructure rather than the process of producing a report. Some of this might be useful in the future but for now we are not making use of it, and therefore we can view the project object as simply a container for a report.

Ghostwriter does not allow normal users to create clients, or to create projects. Normal users can create a report within a project.

Access Controls

Ghostwriter access controls are implemented on clients and projects. There are no specific access controls on reports - a user can access a report if and only if they can access the containing project.

The manager role has access to all clients and projects. Normal users must be explicitly added to a project by a manager.

The standard Ghostwriter access model is that when a normal user has been added to Project X for Client Y, they also have access to all other projects for that client. This isn't suitable for our requirements so (as discussed above) we have implemented a fix so that normal users can only access those projects they have been specifically added to.

Note that normal users do not by default have permission to create, edit or delete items in the finding library. However, this can be explicitly enabled by the administrator for particular user accounts (pretty much the only instance of granular access controls supported by Ghostwriter) and the intention is that this will be done for all of our consultant user accounts (as we want everybody to be able to contribute to the findings library).

Workflow for a new engagement

A manager should create the client if it doesn't already exist, and then create the project. They should then add the relevant team members to the project. There is the possibility of automating this in future as part of the same new-job workflow that creates a Teams channel etc.

Creating a client

This requires the manager role. There is an Add New Client option in the left-hand menu, under the Pre-engagement->Clients dropdown.

Most of the fields are self-explanatory. The Client Codename won't appear in our reports. Probably makes sense to use the schedule code. It is possible to add client contacts via the Points of Contact although it is also possible to add these for individual projects.

Add relevant tags if you can.

Creating a project

This requires the manager role. There is an Add New Project option in the left-hand menu, under the Execution->Projects dropdown.

The client will need to have been created first. The Project Codename won't appear in reports, and defaults to an auto-generated value, but makes sense that it should be changed to the schedule code for the job.

Other than the codename, there isn't a field here to provide a name/title for the project. Don't worry, because this will be provided when the report object is created.

The Start Date and End Date fields are not important, but need to be completed. Similarly, the Project Type field needs to be chosen, but does not have any effect later on.

Add relevant tags if you can.

You should add the relevant team members via the Assignments tab. Consultants cannot add themselves to projects. Please add the firstname.surname SSO accounts, not the manager accounts that a few individuals also have. There are three possible "roles" that can be selected (Assessment Lead, Assessment Oversight, and Operator). These are not related to the RBAC "roles" in Ghostwriter (admin, manager, user) but probably do have some effect in relation to the various areas of project functionality which we are not currently using. However, apart from our reporting template distinguishing between the team lead and other members in the Distribution table, they are not particularly relevant to us. Select one user as Assessment Lead and mark others as Operators.

Managing a project

After a project has been created, there are lots of new tabs appear along the top.

Pasted image 20250823183403.png

Several of these relate to Ghostwriter project/asset management functionality which we are not currently using (this is not to say it is not potentially useful, but for now it is not relevant): - White Cards - Scope - Objectives - Targets - Logs - Infrastructure - Deconflictions

The People tab can be used to manage the existing team assignments, but also to add client contacts which will appear in the report. These can either be copied across from the existing points of contact for the client, or added for this project.

The Notes tab might be useful but currently we don't have any protocol for what should go here, or ensuring that people read it.

The Reporting tab can be used to add a Report object to the project.

Note that there is an In progress toggle on the Planning tab which can be used to mark it as complete.

Creating a report

This can be done by any user who has been assigned access to a particular project. Either use the Add New Report option in the left-hand menu, under Reporting->Reports (in which case you will need to specify one of the projects to which you have access), or navigate to the project and use the Add a report button on the Reporting tab.

The Title chosen here is going to be the title on the front page of the report. Add tags as appropriate.

Choose either AmberWolf Report Candidate (Split by Phase) or AmberWolf Report Candidate (No Phase Split) as appropriate - more on this below. We don't have any corporate PowerPoint templates yet so you can ignore that option.

Working on a report

There are a few tabs along the top of the report page.

Pasted image 20250823183446.png

The Status tab has toggles allowing you to set the Report Status to Draft or Complete, and the Delivery Status to Not Delivered or Delivered. Try to keep these up to date. There is also some read-only configuration relating to formatting and language.

The Generate tab is where you will go to output the report document.

The Findings, Observations, Report Evidence and Extra Fields tabs are all used to add content to the report. More on each of these below.

There is also a useful menu accessed via the hamburger in the top right.

Pasted image 20250823183452.png

Activate sounds significant but in reality it's a GUI feature that allows you to set this as your "active" report in the web interface, making it easier to navigate to the report from elsewhere in the GUI, and easier to add library findings to it.

Jump to Project and Jump to Client might be useful.

Normal users have permission to Delete reports, so be careful (although there is a big red warning to click through first).

Setting the classification

Go to the Extra Fields tab, click on the edit icon beside Classification and set it appropriately. This will be included in the page footer of the report.

Note that "extra fields" represent customisations which have been made to Ghostwriter (and other object types such as projects and findings can also have extra fields). There are a few relevant things in this tab, which are discussed further below.

Configuring finding codes

By default, finding codes will take the form FINDING-001, FINDING-002, etc, with the numbering based on the order that the findings appear in the report (which will in turn depend on findings being arranged by phase, sorted by severity, etc.)

We can change the FINDING- string using the Finding Code Prefix extra field. It will usually make sense to change this to something project specific.

There is also a Use Sequential Finding IDs extra field, which defaults to true, and configures the numbering scheme described above. If this is disabled, findings will be numbered based on their ID in the Ghostwriter database (this database is shared across all projects). The advantage of this approach is that you can guarantee that finding codes will remain constant (whereas with the other approach, add a finding and all the codes might change - not great if you are delivering multiple iterations of a report). When using the finding ID, the numeric field in the finding code is five digits rather than three, as these numbers may be much larger.

See this issue on the templates repo for more discussion.

Phases

Ghostwriter does not have native support for the concept of Phases (different parts of an assessment which might need to be separated out in the report). We have implemented this using extra fields, but the solution is slightly clunky.

If you require separate phases, put them into the Phases extra field for the report, comma-separated. (Make sure you don't put a space after the comma or it will throw off the string-matching when the document generator tries to categorise the findings!)

Pasted image 20250823183505.png

Then, for each finding you create, specify the relevant phase in the extra field at the bottom of the finding page.

Pasted image 20250823183509.png

Unfortunately, Ghostwriter doesn't support dropdowns for extra fields, so this will need to be an exact match for the phase as specified on the report page.

We then have three report templates to choose from.

Template name Phase Configuration
No Phase Split No concept of phases in the generated report. The extra fields are present, and will accept values, but these will be ignored at report generation time.
Phase Split The Findings Summary will consist of a separate table for each phase, and there will also be a separate section in the report containing the findings for each phase.
Hybrid There will be a single table in the Findings Summary, but it will include a column showing the phase for each finding. All the finding entries will be in the same section, but will have a row in the finding header which specifies the phase for that finding.

A situation we want to avoid is - assuming the Phase Split template is in use - findings being lost because no phase has been specified (meaning there isn't a section for them to be placed in) or because the phase has been specified incorrectly. The best we can do here at the moment is some logic in the template which will spit out the title of any such findings in large red text after the tables of findings. If you generate a report using this template, please have a quick look at the Word report to check if any of these are present (and fix their Phase value if so).

Pasted image 20250823183524.png

Hopefully we can add some functionality to Ghostwriter to perform this check automatically, at report generation time.

Writing the intro, scope, caveats and executive summary

Ghostwriter, slightly surprisingly, has no native support for any kind of executive summary content. Therefore, we have implemented these as extra fields (which are referenced appropriately in the report template).

The Intro, Scope & Caveats field has some boilerplate content to get you started. The Executive Summary field is currently empty. In both cases you can fire up a rich text editor by clicking on the edit button.

Attack Narrative

Similar situation for the Attack Narrative field, but in this case we also have a boolean extra field called Include Attack Narrative Section which controls whether or not this will be included in the report. It defaults to false.

Adding a finding

Findings for the report are listed on the Findings tab. As stated in the help text on the page, there are a few ways of adding new findings.

Pasted image 20250823183546.png

Confusingly, Ghostwriter uses the term "finding" to refer both to the entries in a report, but also the boilerplate entries which are stored in the Finding Library. Anyway, you can add a finding from the library by searching for it in the box,

You can also add library findings to the currently active report from the Finding Library page. There is no need to do it this way if you know the title of the finding you want to add (just use the search box), but the Finding Library allows you to view and filter the full list of library findings, and also - importantly - to search them by tag.

The Add a Blank Finding button is self-explanatory in function, but note that it will place a finding called Blank Template at the bottom of the list (with a default risk rating of "Unknown").

When findings in the list have a small flag icon preceding the title, this indicates that the finding is new for this report (rather than having been added from the findings library).

Adding supplemental data/extra sections

Ghostwriter has a concept of "Observations" which can be added to a report, and are distinct from findings. The docs suggest that these are intended to act more like informational findings, but we can repurpose them very nicely to act as supplemental data sections, or indeed any section that we want to include in the report after the findings.

Simply Add a Blank Observation on the Observations tab and then edit it. These are much simpler than findings, and only have a Title, Tags, and Description.

There is also Ordering, which is an extra field we have added. Put an integer in here and the report template will order the sections from lowest Ordering to highest. Actually, a similar effect can be achieved by dragging the items around on the Observations tab, but this approach does offer some support for doing something more complex in future, such as specifying that particular sections should appear before the findings, for example.

Tips for editing in Ghostwriter

  • Right-clicking within the Ghostwriter WYSIWYG editor (which is an instance of TinyMCE) will open Ghostwriter's own context menu. If you want the normal context menu provided by the browser (e.g. in order to interact with the spell-checker) then use Ctrl+Right Click.
  • If you get into a tangle with formatting and/or the controls don't seem to be working, you can often fix this by manually editing the HTML, via View->Source Code. Obviously this isn't ideal, and does require you to know HTML, but it's better than nothing.
  • Unfortunately, if you submit a form in Ghostwriter when your session cookie has expired, it will redirect you to the login page and you will lose any content you have entered in that form since it was last saved. The session cookie timeout is being increased to 24 hours to make this situation less common. However, if you do encounter this problem, you can take advantage of TinyMCE's autosave feature to recover your lost content. Autosave stores copies of the content from each TinyMCE field in local storage for the Ghostwriter domain - easy to find using Developer Tools in the browser.

Pasted image 20250823183552.png

Generating a report

Use the Generate tab in the top right of the report page. Select one of the Word templates (see discussion above) and hit the blue Word doc button.

We don't have any AmberWolf PowerPoint templates at this time.

Creating JSON output might be useful - this is the grey icon in the middle of the three at the bottom.

Post-processing the Word document

There are a few tasks that you should perform after generating a Word report from Ghostwriter. Most of these we would like to automate away, but that's not easy in all cases.

  • Ensure the title of the Word document is correct - possibly add the version number at the end.
  • Update the Document History section on the second page
  • Look for any spurious blank pages in the document and remove them. Ghostwriter is bad for creating these at the end of a loop (e.g. where it loops through all findings, adding them to the report) and eliminating them is a lot more difficult than you might think
  • Use Ctrl-A to select all text in the document and then F9 to update all fields. This will ensure that fields like "Figure #" in image captions are resolved to the correct value. Importantly, this will also cause the table of contents to be created correctly.
  • Remember that by default we deliver PDFs to clients, not Word docs

Findings

TKTK

This is not a guide on best practices for writing up findings - just an explanation on how to make use of the various Ghostwriter features.

Fields

Findings have the following fields. Where (Yes) is specified in the Required? column, this means that Ghostwriter will permit you to create a finding without completing this field, but this field is required from the point of view of writing a coherent finding.

Name Required? Notes
Title Yes Please title-case it appropriately. Confusingly, the Title Case Exceptions list on the Status page for a report relates to the automatic title-casing of captions, and is nothing to do with finding titles.
Assigned Editor No Change this if you want, to assign the finding to one of the other consultants on the project for completion
Tags No Not much point in using tags on findings, because there is no functionality in Ghostwriter to search the findings within a report based on their tag, and they also won't appear in the report document
Finding Type Yes Select from the dropdown. The mechanism to request additional finding types to be added (which is possible via the Ghostwriter admin interface) is still TBD
Severity Yes Select from the dropdown. This list can also be configured, but we do not have plans to change the options that are available at the moment.
CVSS Score No You can use the CVSS Calculator (button just below) to populate this. If you want to do it manually, it expects a number with one decimal place
CVSS Vector No If you want to add this manually, the format should be like CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
Affected Entities (Yes) (WYSIWYG field) Details of the hosts/app/whatever which are affected
Description (Yes) (WYSIWYG field) Description of the finding
Mitigation (Yes) (WYSIWYG field) Recommendation for the finding
Replication Steps No (WYSIWYG field) If you want to add replication steps then do so here, but they are not required
References No (WYSIWYG field) References
Phase No See discussion of Phases above. Only required for phase-split and hybrid templates.

Bugs

Great Failure

When attempting to generate a word doc the following error occurs.

Great Failure
Error: Invalid template operations: '<' not supported between instances of 'NoneType' and 'int' in the DOCX template

This is caused by using the ordering field in some observations but not others. It'll work fine if all or none of these fields are completed, but errors if only some are completed.

0

Sometimes you'll get </samlp:AuthenRequest>0 show up in your issues. Is caused by ampersands but not always.

Cross-Referencing

At present (I believe there is something in the works) there is no way to add x-refs within GhostWriter. The issueheading style also isn't made available for reference within Word, so there's no way to actually insert an xref to an issue. As a workaround to this, a bookmark can be added to each issue title, allowing it to be selected as a cross-reference destination. VBA to automatically go through the document and add bookmarks to issue headings is included below.

Sub AddBookmarksToStyledText()
    Dim para As Paragraph
    Dim bookmarkName As String

    For Each para In ActiveDocument.Paragraphs
        If para.Style = "issueheading" Then ' Replace with your style name
            bookmarkName = CleanBookmarkName(para.Range.text)
            If Not ActiveDocument.Bookmarks.Exists(bookmarkName) Then
                ActiveDocument.Bookmarks.Add Name:=bookmarkName, Range:=para.Range
            End If
        End If
    Next para
    MsgBox ("Done")
End Sub

Function CleanBookmarkName(text As String) As String
    Dim cleanText As String
    Dim i As Integer

    ' Remove non-alphanumeric characters and spaces
    cleanText = ""
    For i = 1 To Len(text)
        If Asc(Mid(text, i, 1)) >= 48 And Asc(Mid(text, i, 1)) <= 57 Or _
           Asc(Mid(text, i, 1)) >= 65 And Asc(Mid(text, i, 1)) <= 90 Or _
           Asc(Mid(text, i, 1)) >= 97 And Asc(Mid(text, i, 1)) <= 122 Then
            cleanText = cleanText & Mid(text, i, 1)
        End If
    Next i

    ' Ensure the bookmark name starts with a letter
    If Len(cleanText) > 0 And Not (Asc(Left(cleanText, 1)) >= 65 And Asc(Left(cleanText, 1)) <= 90 Or _
                                   Asc(Left(cleanText, 1)) >= 97 And Asc(Left(cleanText, 1)) <= 122) Then
        cleanText = "bm" & cleanText
    End If

    ' Truncate if longer than 40 characters (Word's limit for bookmark names)
    If Len(cleanText) > 40 Then
        cleanText = Left(cleanText, 40)
    End If

    CleanBookmarkName = cleanText
End Function

AWFL

TKTK

QA

TKTK