2018-11-08 09:26:32 +00:00
|
|
|
package runner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/tls"
|
2018-11-08 14:16:55 +00:00
|
|
|
"fmt"
|
2018-11-08 09:26:32 +00:00
|
|
|
"io/ioutil"
|
2020-09-24 09:04:31 +00:00
|
|
|
"net"
|
2018-11-08 09:26:32 +00:00
|
|
|
"net/http"
|
2020-02-01 00:36:03 +00:00
|
|
|
"net/http/httputil"
|
2020-02-21 20:43:19 +00:00
|
|
|
"net/textproto"
|
2020-01-07 16:27:43 +00:00
|
|
|
"net/url"
|
2018-11-08 09:26:32 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
"unicode/utf8"
|
|
|
|
|
|
|
|
"github.com/ffuf/ffuf/pkg/ffuf"
|
|
|
|
)
|
|
|
|
|
|
|
|
//Download results < 5MB
|
|
|
|
const MAX_DOWNLOAD_SIZE = 5242880
|
|
|
|
|
|
|
|
type SimpleRunner struct {
|
|
|
|
config *ffuf.Config
|
|
|
|
client *http.Client
|
|
|
|
}
|
|
|
|
|
2020-01-17 07:49:25 +00:00
|
|
|
func NewSimpleRunner(conf *ffuf.Config, replay bool) ffuf.RunnerProvider {
|
2018-11-08 09:26:32 +00:00
|
|
|
var simplerunner SimpleRunner
|
2020-01-07 16:27:43 +00:00
|
|
|
proxyURL := http.ProxyFromEnvironment
|
2020-01-17 07:49:25 +00:00
|
|
|
customProxy := ""
|
2020-01-07 16:27:43 +00:00
|
|
|
|
2020-01-17 07:49:25 +00:00
|
|
|
if replay {
|
|
|
|
customProxy = conf.ReplayProxyURL
|
|
|
|
} else {
|
|
|
|
customProxy = conf.ProxyURL
|
|
|
|
}
|
|
|
|
if len(customProxy) > 0 {
|
|
|
|
pu, err := url.Parse(customProxy)
|
2020-01-07 16:27:43 +00:00
|
|
|
if err == nil {
|
|
|
|
proxyURL = http.ProxyURL(pu)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-08 09:26:32 +00:00
|
|
|
simplerunner.config = conf
|
|
|
|
simplerunner.client = &http.Client{
|
|
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
|
2019-04-27 07:29:05 +00:00
|
|
|
Timeout: time.Duration(time.Duration(conf.Timeout) * time.Second),
|
2018-11-08 09:26:32 +00:00
|
|
|
Transport: &http.Transport{
|
2020-01-07 16:27:43 +00:00
|
|
|
Proxy: proxyURL,
|
2018-11-14 20:38:13 +00:00
|
|
|
MaxIdleConns: 1000,
|
|
|
|
MaxIdleConnsPerHost: 500,
|
|
|
|
MaxConnsPerHost: 500,
|
2020-09-24 09:04:31 +00:00
|
|
|
DialContext: (&net.Dialer{
|
|
|
|
Timeout: time.Duration(time.Duration(conf.Timeout) * time.Second),
|
|
|
|
}).DialContext,
|
|
|
|
TLSHandshakeTimeout: time.Duration(time.Duration(conf.Timeout) * time.Second),
|
2018-11-08 09:26:32 +00:00
|
|
|
TLSClientConfig: &tls.Config{
|
2020-01-07 16:25:42 +00:00
|
|
|
InsecureSkipVerify: true,
|
2020-03-20 10:41:13 +00:00
|
|
|
Renegotiation: tls.RenegotiateOnceAsClient,
|
2018-11-08 09:26:32 +00:00
|
|
|
},
|
|
|
|
}}
|
2019-04-03 09:54:32 +00:00
|
|
|
|
|
|
|
if conf.FollowRedirects {
|
|
|
|
simplerunner.client.CheckRedirect = nil
|
|
|
|
}
|
2018-11-08 09:26:32 +00:00
|
|
|
return &simplerunner
|
|
|
|
}
|
|
|
|
|
2019-11-10 21:30:54 +00:00
|
|
|
func (r *SimpleRunner) Prepare(input map[string][]byte) (ffuf.Request, error) {
|
2018-11-08 09:26:32 +00:00
|
|
|
req := ffuf.NewRequest(r.config)
|
2019-09-02 14:18:36 +00:00
|
|
|
|
2019-11-10 21:30:54 +00:00
|
|
|
req.Headers = r.config.Headers
|
|
|
|
req.Url = r.config.Url
|
|
|
|
req.Method = r.config.Method
|
|
|
|
req.Data = []byte(r.config.Data)
|
|
|
|
|
|
|
|
for keyword, inputitem := range input {
|
2020-10-02 14:12:40 +00:00
|
|
|
req.Method = strings.ReplaceAll(req.Method, keyword, string(inputitem))
|
2020-10-03 07:45:07 +00:00
|
|
|
headers := make(map[string]string, len(req.Headers))
|
2019-11-10 21:30:54 +00:00
|
|
|
for h, v := range req.Headers {
|
2020-10-02 14:12:40 +00:00
|
|
|
var CanonicalHeader string = textproto.CanonicalMIMEHeaderKey(strings.ReplaceAll(h, keyword, string(inputitem)))
|
|
|
|
headers[CanonicalHeader] = strings.ReplaceAll(v, keyword, string(inputitem))
|
2019-11-10 21:30:54 +00:00
|
|
|
}
|
|
|
|
req.Headers = headers
|
2020-10-02 14:12:40 +00:00
|
|
|
req.Url = strings.ReplaceAll(req.Url, keyword, string(inputitem))
|
|
|
|
req.Data = []byte(strings.ReplaceAll(string(req.Data), keyword, string(inputitem)))
|
2018-11-08 09:26:32 +00:00
|
|
|
}
|
2019-11-10 21:30:54 +00:00
|
|
|
|
2018-11-08 09:26:32 +00:00
|
|
|
req.Input = input
|
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *SimpleRunner) Execute(req *ffuf.Request) (ffuf.Response, error) {
|
|
|
|
var httpreq *http.Request
|
|
|
|
var err error
|
2020-02-09 11:29:12 +00:00
|
|
|
var rawreq []byte
|
2018-11-08 09:26:32 +00:00
|
|
|
data := bytes.NewReader(req.Data)
|
2020-09-24 09:04:31 +00:00
|
|
|
httpreq, err = http.NewRequestWithContext(r.config.Context, req.Method, req.Url, data)
|
2018-11-08 09:26:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return ffuf.Response{}, err
|
|
|
|
}
|
2020-02-21 20:43:19 +00:00
|
|
|
|
|
|
|
// set default User-Agent header if not present
|
2018-11-08 09:26:32 +00:00
|
|
|
if _, ok := req.Headers["User-Agent"]; !ok {
|
2021-03-04 20:04:04 +00:00
|
|
|
req.Headers["User-Agent"] = fmt.Sprintf("%s v%s", "Fuzz Faster U Fool", ffuf.Version())
|
2018-11-08 09:26:32 +00:00
|
|
|
}
|
2020-02-21 20:43:19 +00:00
|
|
|
|
2018-11-09 13:21:23 +00:00
|
|
|
// Handle Go http.Request special cases
|
|
|
|
if _, ok := req.Headers["Host"]; ok {
|
|
|
|
httpreq.Host = req.Headers["Host"]
|
|
|
|
}
|
2020-04-22 21:53:28 +00:00
|
|
|
|
|
|
|
req.Host = httpreq.Host
|
2018-11-08 09:26:32 +00:00
|
|
|
httpreq = httpreq.WithContext(r.config.Context)
|
|
|
|
for k, v := range req.Headers {
|
|
|
|
httpreq.Header.Set(k, v)
|
|
|
|
}
|
2020-02-09 11:29:12 +00:00
|
|
|
|
|
|
|
if len(r.config.OutputDirectory) > 0 {
|
|
|
|
rawreq, _ = httputil.DumpRequestOut(httpreq, true)
|
|
|
|
}
|
|
|
|
|
2018-11-08 09:26:32 +00:00
|
|
|
httpresp, err := r.client.Do(httpreq)
|
|
|
|
if err != nil {
|
|
|
|
return ffuf.Response{}, err
|
|
|
|
}
|
2019-12-28 15:46:44 +00:00
|
|
|
|
2018-11-08 09:26:32 +00:00
|
|
|
resp := ffuf.NewResponse(httpresp, req)
|
|
|
|
defer httpresp.Body.Close()
|
|
|
|
|
|
|
|
// Check if we should download the resource or not
|
|
|
|
size, err := strconv.Atoi(httpresp.Header.Get("Content-Length"))
|
|
|
|
if err == nil {
|
|
|
|
resp.ContentLength = int64(size)
|
2020-03-20 10:42:54 +00:00
|
|
|
if (r.config.IgnoreBody) || (size > MAX_DOWNLOAD_SIZE) {
|
2018-11-08 09:26:32 +00:00
|
|
|
resp.Cancelled = true
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-01 00:36:03 +00:00
|
|
|
if len(r.config.OutputDirectory) > 0 {
|
|
|
|
rawresp, _ := httputil.DumpResponse(httpresp, true)
|
|
|
|
resp.Request.Raw = string(rawreq)
|
|
|
|
resp.Raw = string(rawresp)
|
|
|
|
}
|
|
|
|
|
2018-11-08 09:26:32 +00:00
|
|
|
if respbody, err := ioutil.ReadAll(httpresp.Body); err == nil {
|
|
|
|
resp.ContentLength = int64(utf8.RuneCountInString(string(respbody)))
|
|
|
|
resp.Data = respbody
|
|
|
|
}
|
|
|
|
|
2018-11-12 17:06:49 +00:00
|
|
|
wordsSize := len(strings.Split(string(resp.Data), " "))
|
2019-11-09 20:09:12 +00:00
|
|
|
linesSize := len(strings.Split(string(resp.Data), "\n"))
|
2018-11-12 17:06:49 +00:00
|
|
|
resp.ContentWords = int64(wordsSize)
|
2019-11-09 20:09:12 +00:00
|
|
|
resp.ContentLines = int64(linesSize)
|
2018-11-12 17:06:49 +00:00
|
|
|
|
2018-11-08 09:26:32 +00:00
|
|
|
return resp, nil
|
|
|
|
}
|