mirror of
https://github.com/MythicC2Profiles/http
synced 2026-06-08 11:56:36 +00:00
updating to return custom error code/files
added error_file_path and error_status_code options
This commit is contained in:
@@ -6,7 +6,7 @@ toolchain go1.22.2
|
||||
|
||||
//replace github.com/MythicMeta/MythicContainer => ../../../../MythicMeta/MythicContainer
|
||||
|
||||
require github.com/MythicMeta/MythicContainer v1.4.1
|
||||
require github.com/MythicMeta/MythicContainer v1.4.5
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
@@ -28,11 +28,11 @@ require (
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/grpc v1.66.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
||||
@@ -2,6 +2,8 @@ github.com/MythicMeta/MythicContainer v1.4.0 h1:vy8LsoSu5JBIEMWZn9ZnwQUqFvR6/35k
|
||||
github.com/MythicMeta/MythicContainer v1.4.0/go.mod h1:KMFetMj5A+s3IKt+oSgIFyQRXashXZzCABRnaETzPeY=
|
||||
github.com/MythicMeta/MythicContainer v1.4.1 h1:/2jixKxwrPjN+qT14ZZNogXMHp9bfLfxVuEsEfqsCaE=
|
||||
github.com/MythicMeta/MythicContainer v1.4.1/go.mod h1:f9nIBe8teDUW+NP/jYw9Otor1RZHXpC/4cxMv88+KQE=
|
||||
github.com/MythicMeta/MythicContainer v1.4.5 h1:bnXH3eA3NXpENk5ljUi9j91M2nfupztzfPxLArsVwng=
|
||||
github.com/MythicMeta/MythicContainer v1.4.5/go.mod h1:f9nIBe8teDUW+NP/jYw9Otor1RZHXpC/4cxMv88+KQE=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -82,10 +84,14 @@ golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJI
|
||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -93,14 +99,20 @@ golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5 h1:SbSDUWW1PAO24TNpLdeheoYPd7kllICcLU52x6eD4kQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
{
|
||||
"instances": [
|
||||
{
|
||||
"port": 80,
|
||||
"key_path": "privkey.pem",
|
||||
"cert_path": "fullchain.pem",
|
||||
"debug": false,
|
||||
"use_ssl": false,
|
||||
"ServerHeaders": {
|
||||
"Cache-Control": "max-age=0, no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Content-Type": "application/javascript; charset=utf-8",
|
||||
"Pragma": "no-cache",
|
||||
"Server": "NetDNA-cache/2.2"
|
||||
},
|
||||
"payloads": {
|
||||
}
|
||||
}
|
||||
]
|
||||
"instances": [
|
||||
{
|
||||
"port": 80,
|
||||
"key_path": "privkey.pem",
|
||||
"cert_path": "fullchain.pem",
|
||||
"debug": true,
|
||||
"use_ssl": false,
|
||||
"ServerHeaders": {
|
||||
"Cache-Control": "max-age=0, no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Content-Type": "application/javascript; charset=utf-8",
|
||||
"Pragma": "no-cache",
|
||||
"Server": "NetDNA-cache/2.2"
|
||||
},
|
||||
"error_file_path": "",
|
||||
"error_status_code": 404,
|
||||
"payloads": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,6 +20,8 @@ type instanceConfig struct {
|
||||
Headers map[string]string `json:"ServerHeaders"`
|
||||
PayloadHostPaths map[string]string `json:"payloads"`
|
||||
BindIP string `json:"bind_ip"`
|
||||
ErrorFilePath string `json:"error_file_path"`
|
||||
ErrorStatusCode int `json:"error_status_code"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -25,16 +26,17 @@ func Initialize(configInstance instanceConfig) *gin.Engine {
|
||||
//if mythicConfig.MythicConfig.DebugLevel == "warning" {
|
||||
// gin.SetMode(gin.ReleaseMode)
|
||||
//} else {
|
||||
gin.DisableConsoleColor()
|
||||
gin.SetMode(gin.DebugMode)
|
||||
//}
|
||||
r := gin.New()
|
||||
gin.DisableConsoleColor()
|
||||
|
||||
// Global middleware
|
||||
r.Use(InitializeGinLogger(configInstance))
|
||||
// Recovery middleware recovers from any panics and writes a 500 if there was one.
|
||||
r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
|
||||
if err, ok := recovered.(string); ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("error: %s", err)})
|
||||
logging.LogError(nil, err)
|
||||
}
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
}))
|
||||
@@ -135,7 +137,35 @@ func setRoutes(r *gin.Engine, configInstance instanceConfig) {
|
||||
req.URL.Path = "/agent_message"
|
||||
req.Header.Add("mythic", "http")
|
||||
}
|
||||
proxy := &httputil.ReverseProxy{Director: director,
|
||||
modifyResponse := func(resp *http.Response) error {
|
||||
//logging.LogInfo("hitting modify response", "responseCode", resp.StatusCode)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if configInstance.ErrorFilePath != "" {
|
||||
statusCode := 200
|
||||
if configInstance.ErrorStatusCode > 0 {
|
||||
statusCode = configInstance.ErrorStatusCode
|
||||
}
|
||||
file, err := os.Open(configInstance.ErrorFilePath)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to get error_file_path")
|
||||
return err
|
||||
}
|
||||
fileStat, err := file.Stat()
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to stat error_file_path")
|
||||
return err
|
||||
}
|
||||
resp.Body = io.NopCloser(file)
|
||||
resp.Header["Content-Length"] = []string{fmt.Sprint(fileStat.Size())}
|
||||
resp.StatusCode = statusCode
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
proxy := &httputil.ReverseProxy{
|
||||
Director: director,
|
||||
ModifyResponse: modifyResponse,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
@@ -160,7 +190,9 @@ func setRoutes(r *gin.Engine, configInstance instanceConfig) {
|
||||
req.URL.Path = fmt.Sprintf("/direct/download/%s", localVal)
|
||||
req.Header.Add("mythic", "http")
|
||||
}
|
||||
proxyForFiles := httputil.ReverseProxy{Director: directorForFiles,
|
||||
proxyForFiles := httputil.ReverseProxy{
|
||||
Director: directorForFiles,
|
||||
ModifyResponse: modifyResponse,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
|
||||
@@ -25,7 +25,7 @@ Legend:
|
||||
- Dotted line is a message within that connection
|
||||
|
||||
## Configuration Options
|
||||
The profile reads a `config.json` file for a set of instances of `Sanic` webservers to stand up (`80` by default) and redirects the content.
|
||||
The profile reads a `config.json` file for a set of instances of `gin` webservers to stand up (`80` by default) and redirects the content.
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -42,15 +42,20 @@ The profile reads a `config.json` file for a set of instances of `Sanic` webserv
|
||||
"key_path": "privkey.pem",
|
||||
"cert_path": "fullchain.pem",
|
||||
"debug": true,
|
||||
"use_ssl": false
|
||||
"use_ssl": false,
|
||||
"error_file_path": "",
|
||||
"error_status_code": 404,
|
||||
"payloads": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You can specify the headers that the profile will set on Server responses. If there's an error, the server will return a `404` message based on the `fake.html` file contents in `C2_Profiles/HTTP/c2_code`.
|
||||
You can specify the headers that the profile will set on Server responses.
|
||||
If there's an error, the server will return a `error_status_code` message based on the `error_file_path` file contents in `C2_Profiles/HTTP/c2_code`.
|
||||
|
||||
If you want to use SSL within this container specifically, then you can put your key and cert in the `C2_Profiles/HTTP/c2_code` folder and update the `key_path` and `cert_path` variables to have the `names` of those files. Alternatively, if you specify `use_ssl` as true and you don't have any certs already placed on disk, then the profile will automatically generate some self-signed certs for you to use.
|
||||
If you want to use SSL within this container specifically, then you can put your key and cert in the `C2_Profiles/HTTP/c2_code` folder and update the `key_path` and `cert_path` variables to have the `names` of those files.
|
||||
Alternatively, if you specify `use_ssl` as true and you don't have any certs already placed on disk, then the profile will automatically generate some self-signed certs for you to use.
|
||||
You should get a notification when the server starts with information about the configuration:
|
||||
|
||||
```
|
||||
@@ -115,5 +120,3 @@ If you need to manually specify a proxy endpoint, this is where you specify the
|
||||
This profile doesn't do any randomization of network components outside of allowing operators to specify internals/jitter. Every GET request for tasking will be the same. This is important to take into consideration for profiling/beaconing analytics.
|
||||
|
||||
## Development
|
||||
|
||||
All of the code for the server is Python3 using `Sanic` and located in `C2_Profiles/HTTP/c2_code/server`. It loops through the `instances` in the `config.json` file and stands up those individual web servers.
|
||||
|
||||
Reference in New Issue
Block a user