mirror of https://github.com/daffainfo/nuclei.git
Merge pull request #1432 from projectdiscovery/issue-1312-headless-matchers
Adding support for navigation history to matchersdev
commit
b0f16b724c
|
@ -2,6 +2,8 @@ package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-rod/rod"
|
"github.com/go-rod/rod"
|
||||||
|
@ -10,10 +12,18 @@ import (
|
||||||
|
|
||||||
// Page is a single page in an isolated browser instance
|
// Page is a single page in an isolated browser instance
|
||||||
type Page struct {
|
type Page struct {
|
||||||
page *rod.Page
|
page *rod.Page
|
||||||
rules []requestRule
|
rules []requestRule
|
||||||
instance *Instance
|
instance *Instance
|
||||||
router *rod.HijackRouter
|
router *rod.HijackRouter
|
||||||
|
historyMutex *sync.RWMutex
|
||||||
|
History []HistoryData
|
||||||
|
}
|
||||||
|
|
||||||
|
// HistoryData contains the page request/response pairs
|
||||||
|
type HistoryData struct {
|
||||||
|
RawRequest string
|
||||||
|
RawResponse string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs a list of actions by creating a new page in the browser.
|
// Run runs a list of actions by creating a new page in the browser.
|
||||||
|
@ -30,7 +40,7 @@ func (i *Instance) Run(baseURL *url.URL, actions []*Action, timeout time.Duratio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createdPage := &Page{page: page, instance: i}
|
createdPage := &Page{page: page, instance: i, historyMutex: &sync.RWMutex{}}
|
||||||
router := page.HijackRequests()
|
router := page.HijackRequests()
|
||||||
if routerErr := router.Add("*", "", createdPage.routingRuleHandler); routerErr != nil {
|
if routerErr := router.Add("*", "", createdPage.routingRuleHandler); routerErr != nil {
|
||||||
return nil, nil, routerErr
|
return nil, nil, routerErr
|
||||||
|
@ -81,3 +91,24 @@ func (p *Page) URL() string {
|
||||||
}
|
}
|
||||||
return info.URL
|
return info.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DumpHistory returns the full page navigation history
|
||||||
|
func (p *Page) DumpHistory() string {
|
||||||
|
p.historyMutex.RLock()
|
||||||
|
defer p.historyMutex.RUnlock()
|
||||||
|
|
||||||
|
var historyDump strings.Builder
|
||||||
|
for _, historyData := range p.History {
|
||||||
|
historyDump.WriteString(historyData.RawRequest)
|
||||||
|
historyDump.WriteString(historyData.RawResponse)
|
||||||
|
}
|
||||||
|
return historyDump.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// addToHistory adds a request/response pair to the page history
|
||||||
|
func (p *Page) addToHistory(historyData HistoryData) {
|
||||||
|
p.historyMutex.Lock()
|
||||||
|
defer p.historyMutex.Unlock()
|
||||||
|
|
||||||
|
p.History = append(p.History, historyData)
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http/httputil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-rod/rod"
|
"github.com/go-rod/rod"
|
||||||
)
|
)
|
||||||
|
@ -10,7 +12,6 @@ import (
|
||||||
func (p *Page) routingRuleHandler(ctx *rod.Hijack) {
|
func (p *Page) routingRuleHandler(ctx *rod.Hijack) {
|
||||||
// usually browsers don't use chunked transfer encoding, so we set the content-length nevertheless
|
// usually browsers don't use chunked transfer encoding, so we set the content-length nevertheless
|
||||||
ctx.Request.Req().ContentLength = int64(len(ctx.Request.Body()))
|
ctx.Request.Req().ContentLength = int64(len(ctx.Request.Body()))
|
||||||
|
|
||||||
for _, rule := range p.rules {
|
for _, rule := range p.rules {
|
||||||
if rule.Part != "request" {
|
if rule.Part != "request" {
|
||||||
continue
|
continue
|
||||||
|
@ -51,4 +52,32 @@ func (p *Page) routingRuleHandler(ctx *rod.Hijack) {
|
||||||
ctx.Response.SetBody(rule.Args["body"])
|
ctx.Response.SetBody(rule.Args["body"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// store history
|
||||||
|
req := ctx.Request.Req()
|
||||||
|
var rawReq string
|
||||||
|
if raw, err := httputil.DumpRequestOut(req, true); err == nil {
|
||||||
|
rawReq = string(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempts to rebuild the response
|
||||||
|
var rawResp strings.Builder
|
||||||
|
respPayloads := ctx.Response.Payload()
|
||||||
|
if respPayloads != nil {
|
||||||
|
rawResp.WriteString("HTTP/1.1 ")
|
||||||
|
rawResp.WriteString(fmt.Sprint(respPayloads.ResponseCode))
|
||||||
|
rawResp.WriteString(" " + respPayloads.ResponsePhrase + "+\n")
|
||||||
|
for _, header := range respPayloads.ResponseHeaders {
|
||||||
|
rawResp.WriteString(header.Name + ": " + header.Value + "\n")
|
||||||
|
}
|
||||||
|
rawResp.WriteString("\n")
|
||||||
|
rawResp.WriteString(ctx.Response.Body())
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump request
|
||||||
|
historyData := HistoryData{
|
||||||
|
RawRequest: rawReq,
|
||||||
|
RawResponse: rawResp.String(),
|
||||||
|
}
|
||||||
|
p.addToHistory(historyData)
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,8 @@ func (request *Request) getMatchPart(part string, data output.InternalEvent) (st
|
||||||
switch part {
|
switch part {
|
||||||
case "body", "resp", "":
|
case "body", "resp", "":
|
||||||
part = "data"
|
part = "data"
|
||||||
|
case "history":
|
||||||
|
part = "history"
|
||||||
}
|
}
|
||||||
|
|
||||||
item, ok := data[part]
|
item, ok := data[part]
|
||||||
|
@ -66,12 +68,13 @@ func (request *Request) getMatchPart(part string, data output.InternalEvent) (st
|
||||||
}
|
}
|
||||||
|
|
||||||
// responseToDSLMap converts a headless response to a map for use in DSL matching
|
// responseToDSLMap converts a headless response to a map for use in DSL matching
|
||||||
func (request *Request) responseToDSLMap(resp, req, host, matched string) output.InternalEvent {
|
func (request *Request) responseToDSLMap(resp, req, host, matched string, history string) output.InternalEvent {
|
||||||
return output.InternalEvent{
|
return output.InternalEvent{
|
||||||
"host": host,
|
"host": host,
|
||||||
"matched": matched,
|
"matched": matched,
|
||||||
"req": req,
|
"req": req,
|
||||||
"data": resp,
|
"data": resp,
|
||||||
|
"history": history,
|
||||||
"type": request.Type().String(),
|
"type": request.Type().String(),
|
||||||
"template-id": request.options.TemplateID,
|
"template-id": request.options.TemplateID,
|
||||||
"template-info": request.options.TemplateInfo,
|
"template-info": request.options.TemplateInfo,
|
||||||
|
|
|
@ -66,7 +66,7 @@ func (request *Request) ExecuteWithResults(inputURL string, metadata, previous o
|
||||||
if err == nil {
|
if err == nil {
|
||||||
responseBody, _ = html.HTML()
|
responseBody, _ = html.HTML()
|
||||||
}
|
}
|
||||||
outputEvent := request.responseToDSLMap(responseBody, reqBuilder.String(), inputURL, inputURL)
|
outputEvent := request.responseToDSLMap(responseBody, reqBuilder.String(), inputURL, inputURL, page.DumpHistory())
|
||||||
for k, v := range out {
|
for k, v := range out {
|
||||||
outputEvent[k] = v
|
outputEvent[k] = v
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue