Skip to content

Commit

Permalink
Merge pull request #81 from cmu-sei/6_0_development
Browse files Browse the repository at this point in the history
Release of v6.0
  • Loading branch information
sei-dupdyke committed Dec 20, 2021
2 parents d7acbcf + b9b0174 commit 8c0c310
Show file tree
Hide file tree
Showing 93 changed files with 3,727 additions and 1,603 deletions.
73 changes: 41 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
![GHOSTS Logo](https://github.com/cmu-sei/GHOSTS/blob/master/assets/ghosts-logo.jpg)

Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms.

# GHOSTS NPC AUTOMATION

GHOSTS is a framework for highly-complex, realistic non-player character (NPC) orchestration. It essentially realistically mimics the behavior of the different types of people you might encounter on any array of different typical office or enterprise networks. The system makes it possible for cybersecurity experts to test their skills and realistically train to defend real networks with real NPC players operating on those networks doing the things we might expect them to do: Create documents, access systems, browse the web, click, run commands, and so on.

As a result of the system checks required in order for NPCs to be situationally aware, GHOSTS also does health reporting for all configured clients on a given instance.

## Key Links

[Installation and configuration information is maintained on our wiki](https://github.com/cmu-sei/GHOSTS/wiki)

[Don't hesitate to submit issues and feature requests here](https://github.com/cmu-sei/GHOSTS/issues)

## Platform Components

### Ghosts.Client (Windows)
.NET Console app (but built as forms app so that it is hidden) - requires .NET framework v4.6.1 or higher. Client works on both Windows 7 and Windows 10.

### Ghosts.Client (Linux)
dotnetcore app built to run silently. Client tested on centos, alpine and kali distributions. We typically use this for red teaming and "outside" traffic generation or administration simulation.

### Ghosts.Api
Dotnetcore API containing both the api calls for the client (and corresponding api calls you need for integration into other systems) in one.

Uses postgres on the backend because there is not much that postgres can't do.

## LEGAL

[DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution.
![GHOSTS Logo](https://github.com/cmu-sei/GHOSTS/blob/master/assets/ghosts-logo.jpg)

# GHOSTS NPC AUTOMATION

GHOSTS is a framework for highly-complex, realistic non-player character (NPC) orchestration. It essentially realistically mimics the behavior of the different types of people you might encounter on typical office or enterprise networks. The system makes it possible for cybersecurity experts to test their skills and realistically train to defend real networks with real NPC players operating on those networks doing the things we might expect them to do: Create documents, access systems, browse the web, click, run commands, and so on.

As a result of the system checks required for NPCs to be situationally aware, GHOSTS also does health reporting for all configured clients on a given instance.

## Key Links

* [Quick start: Installation from distribution binaries](https://github.com/cmu-sei/GHOSTS/wiki/Installation-from-distribution-binaries)

* [Detailed installation and configuration information](https://github.com/cmu-sei/GHOSTS/wiki)

* [Don't hesitate to submit issues and feature requests](https://github.com/cmu-sei/GHOSTS/issues)

## Platform Components

### Ghosts Clients (Windows & Linux)

GHOSTS clients simulate users on a machine doing "user-like" things. They [can be configured](https://github.com/cmu-sei/GHOSTS/wiki/Configuring-the-Windows-Client) to perform actions including:

* Browse the web
* Create and edit office documents
* Send and respond to email
* Run terminal commands
* Etc.

### Ghosts API Server

The API server is a RESTful web service that provides a way for clients to interact with the GHOSTS system and its clients. It can:

* Manage clients, add/remove them from groups, etc.
* Get/manage information from clients with regards to their activity, current activities, etc.
* Orchestrate new activities for particular clients to perform

---

[DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution.

Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms.
8 changes: 4 additions & 4 deletions src/Dockerfile-api
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#
#multi-stage target: dev
#
FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS dev
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS dev

ENV ASPNETCORE_URLS=http://*:5000 \
ASPNETCORE_ENVIRONMENT=DEVELOPMENT
Expand All @@ -20,14 +20,14 @@ CMD ["dotnet", "run"]
#
#multi-stage target: prod
#
FROM mcr.microsoft.com/dotnet/aspnet:5.0-alpine AS prod
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS prod
ARG commit
ENV COMMIT=$commit
COPY --from=dev /app/dist /app
WORKDIR /app

ENV GHOSTS_VERSION=5.0.0.0
ENV GHOSTS_API_VERSION=v5
ENV GHOSTS_VERSION=6.0.0.0
ENV GHOSTS_API_VERSION=v6
ENV ASPNETCORE_URLS=http://*:5000

EXPOSE 5000
Expand Down
4 changes: 2 additions & 2 deletions src/Ghosts.Api/Controllers/ClientTimeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ namespace ghosts.api.Controllers
public class ClientTimelineController : Controller
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private readonly IMachineTimelineService _service;
private readonly IMachineTimelinesService _service;
private readonly IMachineService _machineService;

public ClientTimelineController(IMachineTimelineService service, IMachineService machineService)
public ClientTimelineController(IMachineTimelinesService service, IMachineService machineService)
{
_service = service;
_machineService = machineService;
Expand Down
15 changes: 15 additions & 0 deletions src/Ghosts.Api/Controllers/ClientUpdatesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Ghosts.Domain;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using NLog;

namespace Ghosts.Api.Controllers
Expand Down Expand Up @@ -86,6 +87,20 @@ public async Task<IActionResult> Index(CancellationToken ct)

await _updateService.DeleteAsync(u.Id, ct);

// integrators want to know that a timeline was actually delivered
// (the service only guarantees that the update was received)
_queue.Enqueue(
new QueueEntry
{
Payload =
new NotificationQueueEntry()
{
Type = NotificationQueueEntry.NotificationType.TimelineDelivered,
Payload = (JObject) JToken.FromObject(update)
},
Type = QueueEntry.Types.Notification
});

return Json(update);
}
}
Expand Down
34 changes: 34 additions & 0 deletions src/Ghosts.Api/Controllers/SurveysController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Ghosts.Api.Services;
using Ghosts.Domain.Messages.MesssagesForServer;
using Microsoft.AspNetCore.Mvc;

namespace ghosts.api.Controllers
{
public class SurveysController : Controller
{
private readonly ISurveyService _surveyService;

public SurveysController(ISurveyService surveyService)
{
_surveyService = surveyService;
}

[ProducesResponseType(typeof(Survey), 200)]
[HttpGet("surveys/{machineId}")]
public async Task<IActionResult> Survey([FromRoute] Guid machineId, CancellationToken ct)
{
return Ok(await _surveyService.GetLatestAsync(machineId, ct));
}

[ProducesResponseType(typeof(IEnumerable<Survey>), 200)]
[HttpGet("surveys/{machineId}/all")]
public async Task<IActionResult> SurveyAll([FromRoute] Guid machineId, CancellationToken ct)
{
return Ok(await _surveyService.GetAllAsync(machineId, ct));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,44 +1,57 @@
// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms.

using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Ghosts.Api.Models;
using Ghosts.Api.Services;
using Ghosts.Api.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.Annotations;

namespace ghosts.api.Controllers
{
/// <summary>
/// Get or update a machine timeline via the API
/// </summary>
public class TimelineController : Controller
public class TimelinesController : Controller
{
private readonly ITimelineService _timelineService;
private readonly IMachineTimelineService _machineTimelineService;
private readonly IMachineTimelinesService _machineTimelinesService;

public TimelineController(ITimelineService timelineService, IMachineTimelineService machineTimelineService)
public TimelinesController(ITimelineService timelineService, IMachineTimelinesService machineTimelinesService)
{
_timelineService = timelineService;
_machineTimelineService = machineTimelineService;
_machineTimelinesService = machineTimelinesService;
}

/// <summary>
/// This returns the timeline for a requested machine. If the timeline is not available,
/// This returns all timelines for a requested machine. If all or a specific timeline is not available,
/// a MachineUpdate request can be made to retrieve the machine timelines
/// </summary>
/// <param name="machineId">Machine Guid</param>
/// <param name="ct">Cancellation token</param>
/// <returns>MachineTimelines</returns>
[ProducesResponseType(typeof(MachineTimeline), 200)]
[HttpGet("timelines/{machineId}")]
public async Task<IActionResult> Timeline([FromRoute] Guid machineId, CancellationToken ct)
{
return Ok(await _machineTimelinesService.GetByMachineIdAsync(machineId, ct));
}

/// <summary>
/// This returns a specific timeline for a requested machine. If the timeline is not available,
/// a MachineUpdate request can be made to retrieve the machine timeline
/// </summary>
/// <param name="machineId">Machine Guid</param>
/// /// <param name="timelineId">Timeline Id Guid</param>
/// <param name="ct">Cancellation token</param>
/// <returns>MachineTimeline</returns>
[ProducesResponseType(typeof(MachineTimeline), 200)]
[HttpGet("timeline/{machineId}")]
public async Task<IActionResult> Timeline([FromRoute] Guid machineId, CancellationToken ct)
[HttpGet("timelines/{machineId}/{timelineId}")]
public async Task<IActionResult> TimelineById([FromRoute] Guid machineId, [FromRoute] Guid timelineId, CancellationToken ct)
{
return Ok(await _machineTimelineService.GetByMachineIdAsync(machineId, ct));
return Ok(await _machineTimelinesService.GetByMachineIdAndTimelineIdAsync(machineId, timelineId, ct));
}

/// <summary>
Expand All @@ -47,14 +60,22 @@ public async Task<IActionResult> Timeline([FromRoute] Guid machineId, Cancellati
/// <param name="machineUpdate">The update to send</param>
/// <param name="ct">Cancellation token</param>
/// <returns>204 No content</returns>
[HttpPost("timeline")]
[HttpPost("timelines")]
// [ProducesResponseType(typeof(Task<IActionResult>), (int) HttpStatusCode.NoContent)] Swagger hates this https://stackoverflow.com/questions/35605427/swagger-ui-freezes-after-api-fetch-and-browser-crashes
[SwaggerOperation(OperationId = "createTimeline")]
public async Task<IActionResult> Timeline([FromBody] MachineUpdateViewModel machineUpdate, CancellationToken ct)
{
await _timelineService.UpdateAsync(machineUpdate, ct);
return NoContent();
}

[HttpPost("timelines/{machineId}/{timelineId}/stop")]
[SwaggerOperation(OperationId = "stopTimeline")]
public async Task<IActionResult> Timeline([FromRoute] Guid machineId, [FromRoute] Guid timelineId, CancellationToken ct)
{
await _timelineService.StopAsync(machineId, timelineId, ct);
return NoContent();
}

/// <summary>
/// Send a new timeline to an entire group of machines
Expand All @@ -63,7 +84,7 @@ public async Task<IActionResult> Timeline([FromBody] MachineUpdateViewModel mach
/// <param name="machineUpdate">The update to send</param>
/// <param name="ct">Cancellation token</param>
/// <returns>204 No content</returns>
[HttpPost("timeline/bygroup/{groupId}")]
[HttpPost("timelines/bygroup/{groupId}")]
// [ProducesResponseType(typeof(Task<IActionResult>), (int) HttpStatusCode.NoContent)] Swagger hates this
[SwaggerOperation(OperationId = "createTimelineForGroup")]
public async Task<IActionResult> GroupTimeline([FromRoute] int groupId, [FromBody] MachineUpdateViewModel machineUpdate, CancellationToken ct)
Expand Down
42 changes: 42 additions & 0 deletions src/Ghosts.Api/Controllers/TrackablesController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Ghosts.Api.Services;
using Microsoft.AspNetCore.Mvc;

namespace ghosts.api.Controllers
{
[Produces("application/json")]
[Route("api/[controller]")]
[ResponseCache(Duration = 5)]
public class TrackablesController : Controller
{
private readonly ITrackableService _service;

public TrackablesController(ITrackableService service)
{
_service = service;
}

/// <summary>
/// Gets all trackables in the system
/// </summary>
/// <param name="ct">Cancellation Token</param>
/// <returns>List of Trackables</returns>
[HttpGet]
public async Task<IActionResult> GetTrackables(CancellationToken ct)
{
var list = await _service.GetAsync(ct);
if (list == null) return NotFound();
return Ok(list);
}

[HttpGet("{id}")]
public async Task<IActionResult> GetTrackableHistory([FromRoute] Guid id, CancellationToken ct)
{
var list = await _service.GetActivityByTrackableId(id, ct);
if (list == null) return NotFound();
return Ok(list);
}
}
}
3 changes: 2 additions & 1 deletion src/Ghosts.Api/Models/QueueEntries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public class NotificationQueueEntry
public enum NotificationType
{
Timeline = 0,
WebhookCreate = 1
WebhookCreate = 1,
TimelineDelivered = 10
}

public NotificationType Type { get; set; }
Expand Down
3 changes: 1 addition & 2 deletions src/Ghosts.Api/Models/WebHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ public Webhook()

public Webhook(WebhookViewModel model)
{
var id = Guid.NewGuid();
if (Guid.TryParse(model.Id, out id))
if (Guid.TryParse(model.Id, out var id))
Id = id;
Status = model.Status;
Description = model.Description;
Expand Down

0 comments on commit 8c0c310

Please sign in to comment.