2020-12-28 20:00:07 +00:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"compress/gzip"
|
2021-06-09 05:45:21 +00:00
|
|
|
"compress/zlib"
|
|
|
|
"io"
|
2020-12-28 20:00:07 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httputil"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
|
2021-02-06 22:04:07 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/tostring"
|
2020-12-28 20:00:07 +00:00
|
|
|
"github.com/projectdiscovery/rawhttp"
|
2021-10-15 16:17:00 +00:00
|
|
|
"github.com/projectdiscovery/stringsutil"
|
2021-09-10 15:41:13 +00:00
|
|
|
"golang.org/x/text/encoding/simplifiedchinese"
|
|
|
|
"golang.org/x/text/transform"
|
2020-12-28 20:00:07 +00:00
|
|
|
)
|
|
|
|
|
2021-02-06 22:04:07 +00:00
|
|
|
// dumpResponseWithRedirectChain dumps a http response with the
|
|
|
|
// complete http redirect chain.
|
|
|
|
//
|
|
|
|
// It preserves the order in which responses were given to requests
|
|
|
|
// and returns the data to the user for matching and viewing in that order.
|
2021-02-06 22:06:08 +00:00
|
|
|
//
|
|
|
|
// Inspired from - https://github.com/ffuf/ffuf/issues/324#issuecomment-719858923
|
2021-02-06 22:04:07 +00:00
|
|
|
func dumpResponseWithRedirectChain(resp *http.Response, body []byte) ([]byte, error) {
|
|
|
|
redirects := []string{}
|
|
|
|
respData, err := httputil.DumpResponse(resp, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-02-07 18:14:19 +00:00
|
|
|
redirectChain := &bytes.Buffer{}
|
|
|
|
|
2021-02-06 22:04:07 +00:00
|
|
|
redirectChain.WriteString(tostring.UnsafeToString(respData))
|
|
|
|
redirectChain.Write(body)
|
|
|
|
redirects = append(redirects, redirectChain.String())
|
|
|
|
redirectChain.Reset()
|
|
|
|
|
2021-02-07 18:14:19 +00:00
|
|
|
var redirectResp *http.Response
|
2021-02-08 10:37:16 +00:00
|
|
|
if resp != nil && resp.Request != nil {
|
2021-02-07 18:14:19 +00:00
|
|
|
redirectResp = resp.Request.Response
|
|
|
|
}
|
2021-02-06 22:04:07 +00:00
|
|
|
for redirectResp != nil {
|
|
|
|
var body []byte
|
|
|
|
|
|
|
|
respData, err := httputil.DumpResponse(redirectResp, false)
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if redirectResp.Body != nil {
|
|
|
|
body, _ = ioutil.ReadAll(redirectResp.Body)
|
|
|
|
}
|
|
|
|
redirectChain.WriteString(tostring.UnsafeToString(respData))
|
|
|
|
if len(body) > 0 {
|
|
|
|
redirectChain.WriteString(tostring.UnsafeToString(body))
|
|
|
|
}
|
|
|
|
redirects = append(redirects, redirectChain.String())
|
|
|
|
redirectResp = redirectResp.Request.Response
|
|
|
|
redirectChain.Reset()
|
|
|
|
}
|
|
|
|
for i := len(redirects) - 1; i >= 0; i-- {
|
|
|
|
redirectChain.WriteString(redirects[i])
|
|
|
|
}
|
|
|
|
return redirectChain.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
2020-12-28 20:00:07 +00:00
|
|
|
// headersToString converts http headers to string
|
|
|
|
func headersToString(headers http.Header) string {
|
|
|
|
builder := &strings.Builder{}
|
|
|
|
|
|
|
|
for header, values := range headers {
|
|
|
|
builder.WriteString(header)
|
|
|
|
builder.WriteString(": ")
|
|
|
|
|
|
|
|
for i, value := range values {
|
|
|
|
builder.WriteString(value)
|
|
|
|
|
|
|
|
if i != len(values)-1 {
|
|
|
|
builder.WriteRune('\n')
|
|
|
|
builder.WriteString(header)
|
|
|
|
builder.WriteString(": ")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
builder.WriteRune('\n')
|
|
|
|
}
|
|
|
|
return builder.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// dump creates a dump of the http request in form of a byte slice
|
|
|
|
func dump(req *generatedRequest, reqURL string) ([]byte, error) {
|
|
|
|
if req.request != nil {
|
|
|
|
// Create a copy on the fly of the request body - ignore errors
|
|
|
|
bodyBytes, _ := req.request.BodyBytes()
|
|
|
|
req.request.Request.Body = ioutil.NopCloser(bytes.NewReader(bodyBytes))
|
2021-01-16 19:21:43 +00:00
|
|
|
return httputil.DumpRequestOut(req.request.Request, true)
|
2020-12-28 20:00:07 +00:00
|
|
|
}
|
2021-02-23 21:03:46 +00:00
|
|
|
return rawhttp.DumpRequestRaw(req.rawRequest.Method, reqURL, req.rawRequest.Path, generators.ExpandMapValues(req.rawRequest.Headers), ioutil.NopCloser(strings.NewReader(req.rawRequest.Data)), rawhttp.Options{CustomHeaders: req.rawRequest.UnsafeHeaders, CustomRawBytes: req.rawRequest.UnsafeRawBytes})
|
2020-12-28 20:00:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// handleDecompression if the user specified a custom encoding (as golang transport doesn't do this automatically)
|
2021-02-07 20:25:53 +00:00
|
|
|
func handleDecompression(resp *http.Response, bodyOrig []byte) (bodyDec []byte, err error) {
|
|
|
|
if resp == nil {
|
2020-12-28 20:00:07 +00:00
|
|
|
return bodyOrig, nil
|
|
|
|
}
|
|
|
|
|
2021-06-09 05:45:21 +00:00
|
|
|
var reader io.ReadCloser
|
|
|
|
switch resp.Header.Get("Content-Encoding") {
|
|
|
|
case "gzip":
|
2021-06-15 06:16:02 +00:00
|
|
|
reader, err = gzip.NewReader(bytes.NewReader(bodyOrig))
|
2021-06-09 05:45:21 +00:00
|
|
|
case "deflate":
|
2021-06-15 06:16:02 +00:00
|
|
|
reader, err = zlib.NewReader(bytes.NewReader(bodyOrig))
|
2021-06-09 05:45:21 +00:00
|
|
|
default:
|
2021-06-15 06:16:02 +00:00
|
|
|
return bodyOrig, nil
|
2021-06-09 05:45:21 +00:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer reader.Close()
|
2020-12-28 20:00:07 +00:00
|
|
|
|
2021-06-09 05:45:21 +00:00
|
|
|
bodyDec, err = ioutil.ReadAll(reader)
|
|
|
|
if err != nil {
|
|
|
|
return bodyOrig, err
|
2020-12-28 20:00:07 +00:00
|
|
|
}
|
2021-06-09 05:45:21 +00:00
|
|
|
return bodyDec, nil
|
2020-12-28 20:00:07 +00:00
|
|
|
}
|
2021-09-10 15:41:13 +00:00
|
|
|
|
|
|
|
// decodegbk converts GBK to UTF-8
|
|
|
|
func decodegbk(s []byte) ([]byte, error) {
|
|
|
|
I := bytes.NewReader(s)
|
|
|
|
O := transform.NewReader(I, simplifiedchinese.GBK.NewDecoder())
|
|
|
|
d, e := ioutil.ReadAll(O)
|
|
|
|
if e != nil {
|
|
|
|
return nil, e
|
|
|
|
}
|
|
|
|
return d, nil
|
|
|
|
}
|
2021-10-15 16:17:00 +00:00
|
|
|
|
|
|
|
// isContentTypeGbk checks if the content-type header is gbk
|
|
|
|
func isContentTypeGbk(contentType string) bool {
|
|
|
|
contentType = strings.ToLower(contentType)
|
|
|
|
return stringsutil.ContainsAny(contentType, "gbk", "gb2312", "gb18030")
|
|
|
|
}
|