2018-11-08 09:26:32 +00:00
|
|
|
package output
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-11-09 12:26:55 +00:00
|
|
|
"os"
|
2018-11-08 09:26:32 +00:00
|
|
|
|
|
|
|
"github.com/ffuf/ffuf/pkg/ffuf"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
BANNER_HEADER = `
|
|
|
|
/'___\ /'___\ /'___\
|
|
|
|
/\ \__/ /\ \__/ __ __ /\ \__/
|
|
|
|
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
|
|
|
|
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
|
|
|
|
\ \_\ \ \_\ \ \____/ \ \_\
|
|
|
|
\/_/ \/_/ \/___/ \/_/
|
|
|
|
`
|
|
|
|
BANNER_SEP = "________________________________________________"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Stdoutput struct {
|
2019-03-29 23:02:41 +00:00
|
|
|
config *ffuf.Config
|
|
|
|
Results []Result
|
|
|
|
}
|
|
|
|
|
|
|
|
type Result struct {
|
|
|
|
Input string `json:"input"`
|
|
|
|
StatusCode int64 `json:"status"`
|
|
|
|
ContentLength int64 `json:"length"`
|
|
|
|
ContentWords int64 `json:"words"`
|
2018-11-08 09:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewStdoutput(conf *ffuf.Config) *Stdoutput {
|
|
|
|
var outp Stdoutput
|
|
|
|
outp.config = conf
|
2019-03-29 23:02:41 +00:00
|
|
|
outp.Results = []Result{}
|
2018-11-08 09:26:32 +00:00
|
|
|
return &outp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Stdoutput) Banner() error {
|
|
|
|
fmt.Printf("%s\n v%s\n%s\n\n", BANNER_HEADER, ffuf.VERSION, BANNER_SEP)
|
|
|
|
printOption([]byte("Method"), []byte(s.config.Method))
|
|
|
|
printOption([]byte("URL"), []byte(s.config.Url))
|
|
|
|
for _, f := range s.config.Matchers {
|
|
|
|
printOption([]byte("Matcher"), []byte(f.Repr()))
|
|
|
|
}
|
|
|
|
for _, f := range s.config.Filters {
|
|
|
|
printOption([]byte("Filter"), []byte(f.Repr()))
|
|
|
|
}
|
|
|
|
fmt.Printf("%s\n\n", BANNER_SEP)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-11-09 12:26:55 +00:00
|
|
|
func (s *Stdoutput) Error(errstring string) {
|
|
|
|
if s.config.Quiet {
|
|
|
|
fmt.Fprintf(os.Stderr, "%s", errstring)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(os.Stderr, "%s%s", TERMINAL_CLEAR_LINE, errstring)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Stdoutput) Finalize() error {
|
2019-03-29 23:02:41 +00:00
|
|
|
var err error
|
|
|
|
if s.config.OutputFile != "" {
|
|
|
|
if s.config.OutputFormat == "json" {
|
|
|
|
err = writeJSON(s.config, s.Results)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
s.Error(fmt.Sprintf("%s", err))
|
|
|
|
}
|
|
|
|
}
|
2018-11-09 12:26:55 +00:00
|
|
|
fmt.Fprintf(os.Stderr, "\n")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Stdoutput) Result(resp ffuf.Response) bool {
|
2018-11-08 09:26:32 +00:00
|
|
|
matched := false
|
|
|
|
for _, m := range s.config.Matchers {
|
|
|
|
match, err := m.Filter(&resp)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if match {
|
|
|
|
matched = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// The response was not matched, return before running filters
|
|
|
|
if !matched {
|
2018-11-09 12:26:55 +00:00
|
|
|
return false
|
2018-11-08 09:26:32 +00:00
|
|
|
}
|
|
|
|
for _, f := range s.config.Filters {
|
|
|
|
fv, err := f.Filter(&resp)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if fv {
|
2018-11-09 12:26:55 +00:00
|
|
|
return false
|
2018-11-08 09:26:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Response survived the filtering, output the result
|
|
|
|
s.printResult(resp)
|
2019-03-29 23:02:41 +00:00
|
|
|
if s.config.OutputFile != "" {
|
|
|
|
// No need to store results if we're not going to use them later
|
|
|
|
sResult := Result{
|
|
|
|
Input: string(resp.Request.Input),
|
|
|
|
StatusCode: resp.StatusCode,
|
|
|
|
ContentLength: resp.ContentLength,
|
|
|
|
ContentWords: resp.ContentWords,
|
|
|
|
}
|
|
|
|
s.Results = append(s.Results, sResult)
|
|
|
|
}
|
2018-11-09 12:26:55 +00:00
|
|
|
return true
|
2018-11-08 09:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Stdoutput) printResult(resp ffuf.Response) {
|
|
|
|
if s.config.Quiet {
|
|
|
|
s.resultQuiet(resp)
|
|
|
|
} else {
|
|
|
|
s.resultNormal(resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Stdoutput) resultQuiet(resp ffuf.Response) {
|
|
|
|
fmt.Println(string(resp.Request.Input))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Stdoutput) resultNormal(resp ffuf.Response) {
|
2018-11-12 17:06:49 +00:00
|
|
|
res_str := fmt.Sprintf("%s%-23s [Status: %s, Size: %d, Words: %d]", TERMINAL_CLEAR_LINE, resp.Request.Input, s.colorizeStatus(resp.StatusCode), resp.ContentLength, resp.ContentWords)
|
2018-11-08 09:26:32 +00:00
|
|
|
fmt.Println(res_str)
|
|
|
|
}
|
2018-11-09 12:26:55 +00:00
|
|
|
|
2018-11-09 13:21:23 +00:00
|
|
|
func (s *Stdoutput) colorizeStatus(status int64) string {
|
|
|
|
if !s.config.Colors {
|
|
|
|
return fmt.Sprintf("%d", status)
|
|
|
|
}
|
|
|
|
colorCode := ANSI_CLEAR
|
|
|
|
if status >= 200 && status < 300 {
|
|
|
|
colorCode = ANSI_GREEN
|
|
|
|
}
|
|
|
|
if status >= 300 && status < 400 {
|
|
|
|
colorCode = ANSI_BLUE
|
|
|
|
}
|
|
|
|
if status >= 400 && status < 500 {
|
|
|
|
colorCode = ANSI_YELLOW
|
|
|
|
}
|
|
|
|
if status >= 500 && status < 600 {
|
|
|
|
colorCode = ANSI_RED
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s%d%s", colorCode, status, ANSI_CLEAR)
|
|
|
|
}
|
|
|
|
|
2018-11-08 09:26:32 +00:00
|
|
|
func printOption(name []byte, value []byte) {
|
|
|
|
fmt.Printf(" :: %-12s : %s\n", name, value)
|
|
|
|
}
|