Skip to content

Commit

Permalink
Merged in dev branch
Browse files Browse the repository at this point in the history
  • Loading branch information
Ne0nd0g committed Mar 21, 2024
2 parents e3529f3 + 3fdaf9e commit 83f3f56
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 146 deletions.
6 changes: 5 additions & 1 deletion clients/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,12 @@ func New(config Config) (*Client, error) {
// Parse additional HTTP Headers
if config.Headers != "" {
client.Headers = make(map[string]string)
for _, header := range strings.Split(config.Headers, "\\n") {
for _, header := range strings.Split(config.Headers, "\n") {
h := strings.Split(header, ":")
if len(h) < 2 {
cli.Message(cli.DEBUG, fmt.Sprintf("unable to parse HTTP header: '%s'", header))
continue
}
// Remove leading or trailing spaces
headerKey := strings.TrimSuffix(strings.TrimPrefix(h[0], " "), " ")
headerValue := strings.TrimSuffix(strings.TrimPrefix(h[1], " "), " ")
Expand Down
70 changes: 61 additions & 9 deletions clients/mythic/mythic.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ var socksConnection = sync.Map{}
// mythicSocksConnection is used to map Merlin's connection UUID to Mythic's integer server_id; Inverse of socksConnection
var mythicSocksConnection = sync.Map{}

// socksCounter is used to track and order the SOCKS data packets coming from Mythic
var socksCounter = sync.Map{}

// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server
type Client struct {
Authenticator authenticators.Authenticator
Expand Down Expand Up @@ -108,6 +111,7 @@ type Config struct {
AuthPackage string // AuthPackage is the type of authentication the agent should use when communicating with the server
PayloadID string // The UUID used with the Mythic framework
Protocol string // Proto contains the transportation protocol the agent is using (i.e., http2 or http3)
Headers string // Headers is a new-line separated string of additional HTTP headers to add to client requests
Host string // Host is used with the HTTP Host header for Domain Fronting activities
URL string // URL is the protocol, domain, and page that the agent will communicate with (e.g., https://google.com/test.aspx)
Proxy string // Proxy is the URL of the proxy that all traffic needs to go through, if applicable
Expand Down Expand Up @@ -218,6 +222,31 @@ func New(config Config) (*Client, error) {
cli.Message(cli.WARN, fmt.Sprintf("there was an error converting Padding string \"%s\" to an integer: %s", config.Padding, err))
}

// Parse additional HTTP Headers
if config.Headers != "" {
client.Headers = make(map[string]string)
for _, header := range strings.Split(config.Headers, "\n") {
h := strings.Split(header, ":")
if len(h) < 2 {
cli.Message(cli.DEBUG, fmt.Sprintf("unable to parse HTTP header: '%s'", header))
continue
}
// Remove leading or trailing spaces
headerKey := strings.TrimSuffix(strings.TrimPrefix(h[0], " "), " ")
headerValue := strings.TrimSuffix(strings.TrimPrefix(h[1], " "), " ")
cli.Message(
cli.DEBUG,
fmt.Sprintf("HTTP Header (%d): %s, Value (%d): %s\n",
len(headerKey),
headerKey,
len(headerValue),
headerValue,
),
)
client.Headers[headerKey] = headerValue
}
}

// Determine the HTTP client type
if client.Protocol == "http" || client.Protocol == "https" {
if config.ClientType == strings.ToLower("winhttp") {
Expand Down Expand Up @@ -269,6 +298,7 @@ func New(config Config) (*Client, error) {
cli.Message(cli.INFO, fmt.Sprintf("\tURL: %s", client.URL))
cli.Message(cli.INFO, fmt.Sprintf("\tUser-Agent: %s", client.UserAgent))
cli.Message(cli.INFO, fmt.Sprintf("\tHTTP Host Header: %s", client.Host))
cli.Message(cli.INFO, fmt.Sprintf("\tHTTP Headers: %s", client.Headers))
cli.Message(cli.INFO, fmt.Sprintf("\tProxy: %s", client.Proxy))
cli.Message(cli.INFO, fmt.Sprintf("\tPayload Padding Max: %d", client.PaddingMax))
cli.Message(cli.INFO, fmt.Sprintf("\tJA3 String: %s", client.JA3))
Expand Down Expand Up @@ -399,6 +429,9 @@ func (client *Client) Send(m messages.Base) (returnMessages []messages.Base, err
req.Host = client.Host
}
}
for header, value := range client.Headers {
req.Header.Set(header, value)
}

// Send the request
cli.Message(cli.DEBUG, fmt.Sprintf("Sending POST request size: %d to: %s", req.ContentLength, client.URL))
Expand Down Expand Up @@ -711,6 +744,17 @@ func (client *Client) Deconstruct(data []byte) (returnMessages []messages.Base,
err = fmt.Errorf("there was an error unmarshalling the JSON object to a mythic.ServerTaskResponse structure in the message handler:\n%s", err)
return
}
// SOCKS5
if len(msg.SOCKS) > 0 {
// There is SOCKS data to send to the SOCKS server
returnMessage, err = client.convertSocksToJobs(msg.SOCKS)
if err != nil {
cli.Message(cli.WARN, err.Error())
}
if len(returnMessage.Payload.([]jobs.Job)) > 0 {
returnMessages = append(returnMessages, returnMessage)
}
}
cli.Message(cli.DEBUG, fmt.Sprintf("post_response results from the server: %+v", msg))
for _, response := range msg.Responses {
if response.Error != "" {
Expand Down Expand Up @@ -786,7 +830,9 @@ func (client *Client) Construct(m messages.Base) ([]byte, error) {
// Convert Merlin jobs to mythic response
for _, job := range m.Payload.([]jobs.Job) {
var response ClientTaskResponse
response.ID = uuid.MustParse(job.ID)
if job.ID != "" {
response.ID = uuid.MustParse(job.ID)
}
response.Completed = true
cli.Message(cli.DEBUG, fmt.Sprintf("Converting Merlin job type: %d to Mythic response", job.Type))
switch job.Type {
Expand Down Expand Up @@ -892,6 +938,7 @@ func (client *Client) Construct(m messages.Base) ([]byte, error) {

// Base64 encode the data
sock.Data = base64.StdEncoding.EncodeToString(sockMsg.Data)
//fmt.Printf("\t[*] SOCKS Data size: %d\n", len(sockMsg.Data))

// Add to return messages
returnMessage.SOCKS = append(returnMessage.SOCKS, sock)
Expand Down Expand Up @@ -977,6 +1024,7 @@ func (client *Client) Construct(m messages.Base) ([]byte, error) {
// convertSocksToJobs takes in Mythic socks messages and translates them into Merlin jobs
func (client *Client) convertSocksToJobs(socks []Socks) (base messages.Base, err error) {
cli.Message(cli.DEBUG, fmt.Sprintf("Entering into clients.mythic.convertSocksToJobs() with %+v", socks))
//fmt.Printf("Entering into clients.mythic.convertSocksToJobs() with %d socks messages: %+v\n", len(socks), socks)

base.Type = messages.JOBS
base.ID = client.AgentID
Expand All @@ -999,10 +1047,11 @@ func (client *Client) convertSocksToJobs(socks []Socks) (base messages.Base, err
id = uuid.New()
socksConnection.Store(sock.ServerId, id)
mythicSocksConnection.Store(id, sock.ServerId)

socksCounter.Store(id, 0)
// Spoof SOCKS handshake with Merlin Agent
payload.ID = id.(uuid.UUID)
payload.Data = []byte{0x05, 0x01, 0x00}
payload.Index = 0
job.Payload = payload
returnJobs = append(returnJobs, job)
}
Expand All @@ -1014,7 +1063,17 @@ func (client *Client) convertSocksToJobs(socks []Socks) (base messages.Base, err
err = fmt.Errorf("there was an error base64 decoding the SOCKS message data: %s", err)
return
}
//fmt.Printf("\tID: %d, Data length: %d\n", sock.ServerId, len(payload.Data))
// Load the data packet counter
i, ok := socksCounter.Load(id)
if !ok {
err = fmt.Errorf("there was an error getting the SOCKS counter for the UUID: %s", id)
return
}

payload.Index = i.(int) + 1
job.Payload = payload
socksCounter.Store(id, i.(int)+1)
returnJobs = append(returnJobs, job)
}
base.Payload = returnJobs
Expand Down Expand Up @@ -1083,12 +1142,6 @@ func (client *Client) convertTasksToJobs(tasks []Task) (messages.Base, error) {
}
job.Payload = payload
returnJobs = append(returnJobs, job)
case jobs.SOCKS:
var payload jobs.Socks
err = json.Unmarshal([]byte(mythicJob.Payload), &payload)
if err != nil {
return base, fmt.Errorf("there was an error unmarshalling the Mythic job payload to a jobs.Socks structure:\n%s", err)
}
case 0:
// case 0 means that a job type was not added to the task from the Mythic server
// Commonly seen with SOCKS messages
Expand All @@ -1101,7 +1154,6 @@ func (client *Client) convertTasksToJobs(tasks []Task) (messages.Base, error) {
}
switch params.Action {
case "start", "stop":
// TODO Set agent sleep to 0 if start
// Send message back to Mythic that SOCKS has been started/stopped
job.Type = jobs.RESULT
job.Payload = jobs.Results{}
Expand Down
3 changes: 2 additions & 1 deletion clients/mythic/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ type Tasking struct {
type Tasks struct {
Action string `json:"action"`
Tasks []Task `json:"tasks"`
SOCKS []Socks `json:"socks"`
SOCKS []Socks `json:"socks,omitempty"`
}

// Task contains the task identifier, command, and parameters for the agent to execute
Expand Down Expand Up @@ -139,6 +139,7 @@ type ServerTaskResponse struct {
type ServerPostResponse struct {
Action string `json:"action"`
Responses []ServerTaskResponse `json:"responses"`
SOCKS []Socks `json:"socks,omitempty"`
}

// PostResponseFile is the structure used to send a list of messages from the agent to the server
Expand Down
24 changes: 24 additions & 0 deletions docs/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## 2.4.0 - 2024-03-21

### Added

- Mythic client handles multiple HTTP headers with the Mythic `http` C2 Profile

### Fixed

- Resolved several SOCKS5 issues
- Updated Mythic client to handle `post_response` actions with `ServerPostResponse` structure to include SOCKS information
- Created a go routine and a channel just for sending SOCKS data in place of using the Jobs channel

### Changed

- Upgraded the following libraries to their latest version
- upgraded golang.org/x/net v0.21.0 => v0.22.0
- upgraded github.com/google/uuid v1.5.0 => v1.6.0
- upgraded github.com/quic-go/quic-go v0.40.1 => v0.42.0
- upgraded github.com/refraction-networking/utls v1.6.0 => v1.6.3

### Security

- Upgraded go-jose/v3 to v3.0.3 to address CVE-2024-28180

## 2.3.0 - 2023-12-26

### Added
Expand Down
29 changes: 15 additions & 14 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/Ne0nd0g/merlin-agent/v2

go 1.21

toolchain go1.22.1

require (
github.com/C-Sto/BananaPhone v0.0.0-20220220002628-6585e5913761
github.com/Ne0nd0g/go-clr v1.0.3
Expand All @@ -11,14 +13,14 @@ require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/cretz/gopaque v0.1.0
github.com/fatih/color v1.16.0
github.com/go-jose/go-jose/v3 v3.0.1
github.com/go-jose/go-jose/v3 v3.0.3
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.5.0
github.com/quic-go/quic-go v0.41.0
github.com/refraction-networking/utls v1.6.2
golang.org/x/crypto v0.19.0
golang.org/x/net v0.20.0
golang.org/x/sys v0.17.0
github.com/google/uuid v1.6.0
github.com/quic-go/quic-go v0.42.0
github.com/refraction-networking/utls v1.6.3
golang.org/x/crypto v0.21.0
golang.org/x/net v0.22.0
golang.org/x/sys v0.18.0
golang.org/x/text v0.14.0
)

Expand All @@ -28,17 +30,16 @@ require (
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect
github.com/klauspost/compress v1.17.6 // indirect
github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
github.com/onsi/ginkgo/v2 v2.17.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
go.dedis.ch/fixbuf v1.0.3 // indirect
go.dedis.ch/kyber/v3 v3.1.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/tools v0.16.1 // indirect
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/tools v0.19.0 // indirect
)
Loading

0 comments on commit 83f3f56

Please sign in to comment.