forked from teknogeek/ssrf-sheriff
/
handler.go
161 lines (141 loc) · 4.5 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package handler
import (
"encoding/json"
"encoding/xml"
"fmt"
"io/ioutil"
"mime"
"net/http"
"path"
"path/filepath"
"github.com/gorilla/mux"
"github.com/wh1tenoise/ssrf-sheriff/generators"
"github.com/wh1tenoise/ssrf-sheriff/httpserver"
"go.uber.org/config"
"go.uber.org/fx"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// SerializableResponse is a generic type which both can be safely serialized to both XML and JSON
type SerializableResponse struct {
SecretToken string `json:"token" xml:"token"`
}
// SSRFSheriffRouter is a wrapper around mux.Router to handle HTTP requests to the sheriff, with logging
type SSRFSheriffRouter struct {
logger *zap.Logger
ssrfToken string
}
// NewHTTPServer provides a new HTTP server listener
func NewHTTPServer(
mux *mux.Router,
cfg config.Provider,
) *http.Server {
return &http.Server{
Addr: cfg.Get("http.address").String(),
Handler: mux,
}
}
// NewSSRFSheriffRouter returns a new SSRFSheriffRouter which is used to route and handle all HTTP requests
func NewSSRFSheriffRouter(
logger *zap.Logger,
cfg config.Provider,
) *SSRFSheriffRouter {
return &SSRFSheriffRouter{
logger: logger,
ssrfToken: cfg.Get("ssrf_token").String(),
}
}
// StartFilesGenerator starts the function which is dynamically generating JPG/PNG formats
// with the secret token rendered in the media
func StartFilesGenerator(cfg config.Provider) {
generators.InitMediaGenerators(cfg.Get("ssrf_token").String())
}
// StartServer starts the HTTP server
func StartServer(server *http.Server, lc fx.Lifecycle) {
h := httpserver.NewHandle(server)
lc.Append(fx.Hook{
OnStart: h.Start,
OnStop: h.Shutdown,
})
}
// PathHandler is the main handler for all inbound requests
func (s *SSRFSheriffRouter) PathHandler(w http.ResponseWriter, r *http.Request) {
fileExtension := filepath.Ext(r.URL.Path)
contentType := mime.TypeByExtension(fileExtension)
var response string
switch fileExtension {
case ".json":
res, _ := json.Marshal(SerializableResponse{SecretToken: s.ssrfToken})
response = string(res)
case ".xml":
res, _ := xml.Marshal(SerializableResponse{SecretToken: s.ssrfToken})
response = string(res)
case ".html":
tmpl := readTemplateFile("html.html")
response = fmt.Sprintf(tmpl, s.ssrfToken, s.ssrfToken)
case ".csv":
tmpl := readTemplateFile("csv.csv")
response = fmt.Sprintf(tmpl, s.ssrfToken)
case ".txt":
response = fmt.Sprintf("token=%s", s.ssrfToken)
case ".png":
response = readTemplateFile("png.png")
case ".jpg", ".jpeg":
response = readTemplateFile("jpeg.jpg")
// TODO: dynamically generate these formats with the secret token rendered in the media
case ".gif":
response = readTemplateFile("gif.gif")
case ".mp3":
response = readTemplateFile("mp3.mp3")
case ".mp4":
response = readTemplateFile("mp4.mp4")
default:
response = s.ssrfToken
}
if contentType == "" {
contentType = "text/plain"
}
s.logger.Info("New inbound HTTP request",
zap.String("IP", r.RemoteAddr),
zap.String("Path", r.URL.Path),
zap.String("Response Content-Type", contentType),
zap.Any("Request Headers", r.Header),
)
responseBytes := []byte(response)
w.Header().Set("Content-Type", contentType)
w.Header().Set("X-Secret-Token", s.ssrfToken)
w.WriteHeader(http.StatusOK)
w.Write(responseBytes)
}
func readTemplateFile(templateFileName string) string {
data, err := ioutil.ReadFile(path.Join("templates", path.Clean(templateFileName)))
if err != nil {
return ""
}
return string(data)
}
// NewServerRouter returns a new mux.Router for handling any HTTP request to /.*
func NewServerRouter(s *SSRFSheriffRouter) *mux.Router {
router := mux.NewRouter()
router.PathPrefix("/").HandlerFunc(s.PathHandler)
return router
}
// NewConfigProvider returns a config.Provider for YAML configuration
func NewConfigProvider() (config.Provider, error) {
return config.NewYAMLProviderFromFiles("config/base.yaml")
}
// NewLogger returns a new *zap.Logger
func NewLogger(cfg config.Provider) (*zap.Logger, error) {
zapConfig := zap.NewProductionConfig()
zapConfig.Encoding = cfg.Get("logging.format").String()
if cfg.Get("logging.timeEncoder").String() == "EpochMillisTimeEncoder" {
zapConfig.EncoderConfig.EncodeTime = zapcore.EpochMillisTimeEncoder
} else if cfg.Get("logging.timeEncoder").String() == "ISO8601TimeEncoder" {
zapConfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
}
zapConfig.DisableStacktrace = true
if cfg.Get("logging.file").String() != "" {
zapConfig.OutputPaths = []string{"stdout", cfg.Get("logging.file").String()}
}
return zapConfig.Build()
}