updating to return custom error code/files

added error_file_path and error_status_code options
This commit is contained in:
its-a-feature
2024-09-10 15:00:31 -05:00
parent cf4f25a54a
commit 4d11b7b43d
6 changed files with 84 additions and 34 deletions
+6 -6
View File
@@ -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
+12
View File
@@ -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=
+19 -18
View File
@@ -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,
+9 -6
View File
@@ -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.