~poptart/sprey

1f4812384fb021ead4c435665b59d2832083c05c — poptart 3 years ago c6c2490
Some more updates
5 files changed, 278 insertions(+), 19 deletions(-)

M .gitignore
M cmd/sprey.go
M content.go
A db.go
M timing.go
M .gitignore => .gitignore +2 -0
@@ 1,3 1,5 @@
*.db
cmd/cmd
*.swp
*.test
*.txt

M cmd/sprey.go => cmd/sprey.go +37 -9
@@ 1,12 1,13 @@
package main

import (
	".."
	"bufio"
	"fmt"
	"net/url"
	"os"
	"strings"

	sprey ".."
)

var test = `GET / HTTP/1.1


@@ 17,28 18,55 @@ Accept: */*
`

func main() {
	db, err := sprey.OpenDB("sprey.db")
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		os.Exit(1)
	}
	defer db.Close()
	err = sprey.SetupDB(db)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		os.Exit(1)
	}
	err = sprey.SetupDB(db)
	td := bufio.NewReader(strings.NewReader(test))
	u, err := url.Parse("https://r7.root.express/a")
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		os.Exit(1)
	}
	//s, err := sprey.NewSpray()
	//if err != nil {
	//	fmt.Printf("%s\n", err.Error())
	//	os.Exit(2)
	//}
	a, err := sprey.NewTimingCampaign(td, u)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		fmt.Printf("0> %s\n", err.Error())
		os.Exit(3)
	}
	a.Campaign, _ = sprey.NewSpray(a)
	a.SetTriggerLow()
	err = sprey.Start(a)
	//err = a.Do()
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		fmt.Printf("0> %s\n", err.Error())
		os.Exit(4)
	}
	//reset
	td = bufio.NewReader(strings.NewReader(test))
	b, err := sprey.NewContentCampaign(td, u, sprey.MatchStatus|sprey.MatchBody)
	if err != nil {
		fmt.Printf("1> %s\n", err.Error())
		os.Exit(4)
	}
	b.Campaign, _ = sprey.NewSpray(b)
	b.SuccessStatusCode(200)
	err = sprey.Start(b)
	if err != nil {
		fmt.Printf("1> %s\n", err.Error())
		os.Exit(4)
	}
	c, err := sprey.StartCampaign(db, sprey.PasswordPolicy{}, sprey.Delay{}, []byte(test))
	if err != nil {
		fmt.Printf("2> %#v %s\n", c, err.Error())
		os.Exit(4)
	}
	fmt.Printf("2> %#v\n", c)

}

M content.go => content.go +71 -9
@@ 2,15 2,21 @@ package sprey

import (
	"bufio"
	"crypto/tls"
	"errors"
	"log"
	"net/http"
	"net/http/httptrace"
	"net/url"
	"regexp"
	"time"
)

type MatchModes int

type ContentCampaign struct {
	Campaign           *Meta
	MatchMode          int //Use MATCH_* iota
	MatchMode          MatchModes
	matchStatus        int
	matchHeader        []*regexp.Regexp
	matchBody          []*regexp.Regexp


@@ 23,17 29,17 @@ type ContentCampaign struct {
}

const (
	MATCH_STATUS = iota
	MATCH_HEADER
	MATCH_BODY
	MATCH_VARIATION
	MatchStatus MatchModes = 1 << iota
	MatchHeader
	MatchBody
	MatchVariation
)

const (
	DefaultVariationTheshold = 90
)

func NewContentCampaign(b *bufio.Reader, url *url.URL, mode int) (*ContentCampaign, error) {
func NewContentCampaign(b *bufio.Reader, url *url.URL, mode MatchModes) (*ContentCampaign, error) {
	br, err := http.ReadRequest(b)
	if err != nil {
		return &ContentCampaign{}, err


@@ 41,7 47,7 @@ func NewContentCampaign(b *bufio.Reader, url *url.URL, mode int) (*ContentCampai
	var cc = ContentCampaign{
		Validate:      false,
		ValidateTries: 0,
		MatchMode:     mode,
		MatchMode:     MatchModes(mode),
		req:           br,
		url:           url,
	}


@@ 52,8 58,39 @@ func NewContentCampaign(b *bufio.Reader, url *url.URL, mode int) (*ContentCampai
func (cc *ContentCampaign) Pre(e []Exchange) (func(), error) {
	return func() {}, nil
}
func (cc *ContentCampaign) Start()              { return }
func (cc *ContentCampaign) Do() error           { return nil }

func (cc *ContentCampaign) Do() (Attempt, error) {
	var attempt = Attempt{
		Exchange: Exchange{
			Request:  cc.req,
			Response: &http.Response{},
			Timing:   &Timing{},
		},
	}
	req := cc.req
	trace := &httptrace.ClientTrace{
		DNSStart:             func(dsi httptrace.DNSStartInfo) { attempt.Exchange.Timing.DNSStart = time.Now() },
		DNSDone:              func(ddi httptrace.DNSDoneInfo) { attempt.Exchange.Timing.DNSStop = time.Now() },
		TLSHandshakeStart:    func() { attempt.Exchange.Timing.TLSStart = time.Now() },
		TLSHandshakeDone:     func(cs tls.ConnectionState, err error) { attempt.Exchange.Timing.TLSStop = time.Now() },
		ConnectStart:         func(network, addr string) { attempt.Exchange.Timing.ConnectStart = time.Now() },
		ConnectDone:          func(network, addr string, err error) { attempt.Exchange.Timing.ConnectStop = time.Now() },
		GotFirstResponseByte: func() { attempt.Exchange.Timing.FirstByte = time.Now() },
	}
	req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
	attempt.Exchange.Timing.InitialStart = time.Now()
	//workaround(poptart) stupid workaround for leftside error bug
	x, err := http.DefaultTransport.RoundTrip(req)
	attempt.Exchange.Response = x
	if err != nil {
		log.Fatal(err)
		return attempt, err
	}
	attempt.Exchange.Timing.LastByte = time.Now()
	attempt.Exchange.Timing.Real = attempt.Exchange.Timing.LastByte.Sub(attempt.Exchange.Timing.FirstByte)
	return attempt, nil

}
func (cc *ContentCampaign) Success() bool       { return false }
func (cc *ContentCampaign) Fail() error         { return nil }
func (cc *ContentCampaign) IsLocked(error) bool { return false }


@@ 62,14 99,39 @@ func (cc *ContentCampaign) SuccessStatusCode(sc int) {
}

func (cc *ContentCampaign) HeaderRegexp(rs []string) error {
	if (cc.MatchMode & MatchHeader) == 0 {
		return errors.New("Match mode not enabled")
	}
	for _, s := range rs {
		r, err := regexp.Compile(s)
		if err != nil {
			cc.matchHeader = []*regexp.Regexp{}
			return err
		}
		cc.matchHeader = append(cc.matchHeader, r)
	}
	return nil
}

func (cc *ContentCampaign) BodyRegexp(rs []string) error {
	if (cc.MatchMode & MatchBody) == 0 {
		return errors.New("Match mode not enabled")
	}
	for _, s := range rs {
		r, err := regexp.Compile(s)
		if err != nil {
			cc.matchBody = []*regexp.Regexp{}
			return err
		}
		cc.matchBody = append(cc.matchBody, r)
	}
	return nil
}

func (cc *ContentCampaign) UseVariation(threshold int) error {
	if (cc.MatchMode & MatchVariation) == 0 {
		return errors.New("Match mode not enabled")
	}
	if threshold >= 100 || threshold <= 0 {
		return errors.New("Threshold must be between 1-99")
	}

A db.go => db.go +167 -0
@@ 0,0 1,167 @@
package sprey

import (
	"database/sql"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

type DBEntry struct {
	ID              int
	RunID           int
	Previous        int
	Start           *time.Time
	Stop            *time.Time
	Username        string
	Password        string
	Response        []byte
	Success         bool
	TimingInitial   *time.Time
	TimingDNSStart  *time.Time
	TimingDNSStop   *time.Time
	TimingTLSStart  *time.Time
	TimingTLSStop   *time.Time
	TimingConnStart *time.Time
	TimingConnStop  *time.Time
	TimingFirstByte *time.Time
	TimingLastByte  *time.Time
	Meta            []byte
}

type DBCampaign struct {
	RunID           int
	RequestData     []byte
	PolicyDuration  time.Duration
	PolicyThreshold time.Duration
	PolicyWindow    time.Duration
	DelayGlobal     time.Duration
	DelayPerUser    time.Duration
	DelayPerLockout time.Duration
	DelayWindow     time.Duration
	DelayRandom     time.Duration
}

const (
	sqlcreatespreytable = `CREATE TABLE IF NOT EXISTS sprey 
(
	id INTEGER PRIMARY KEY AUTOINCREMENT, 
	runid            INTEGER,
	previous         INTEGER,
	start            DATETIME,
	stop             DATETIME,
	username         TEXT NOT NULL,
	password         TEXT,
	response_data    BLOB,
	success          BOOLEAN,
	timing_initial   DATETIME,
	timing_dnsstart  DATETIME,
	timing_dnsstop   DATETIME,
	timing_tlsstart  DATETIME,
	timing_tlsstop   DATETIME,
	timing_connstart DATETIME,
	timing_connstop  DATETIME,
	timing_firstbyte DATETIME,
	timing_lastbyte  DATETIME,
	meta		 TEXT
);`
	sqlcreatecampaigntable = `CREATE TABLE IF NOT EXISTS campaign 
(
	runid INTEGER PRIMARY KEY AUTOINCREMENT, 
	request_data BLOB,
	policy_duration INTEGER,
	policy_threshold INTEGER,
	policy_window INTEGER,
	delay_global INTEGER,
	delay_peruser INTEGER,
	delay_perlockout INTEGER,
	delay_window INTEGER,
	delay_random INTEGER
);`
	sqlstartcampaign = `INSERT INTO campaign(request_data, policy_duration, policy_threshold, policy_window, delay_global, delay_peruser, delay_perlockout, delay_window, delay_random) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?);`
	sqlrecordattempt = `INSERT INTO sprey(runid, previous, start, stop, username, password, response_data, success, timing_initial, timing_dnsstart, timing_dnsstop, timing_tlsstart, timing_tlsstop, timing_connstart, timing_connstop, timing_firstbyte, timing_lastbyte, meta) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`
)

func OpenDB(path string) (*sql.DB, error) {
	db, err := sql.Open("sqlite3", path)
	if err != nil {
		return &sql.DB{}, err
	}
	//defer db.Close()
	return db, nil
}

func SetupDB(db *sql.DB) error {
	_, err := db.Exec(sqlcreatecampaigntable)
	if err != nil {
		return err
	}
	_, err = db.Exec(sqlcreatespreytable)
	if err != nil {
		return err
	}
	return nil
}

func StartCampaign(db *sql.DB, policy PasswordPolicy, delay Delay, reqdata []byte) (*DBCampaign, error) {
	tx, err := db.Begin()
	if err != nil {
		return &DBCampaign{}, err
	}
	stmt, err := tx.Prepare(sqlstartcampaign)
	if err != nil {
		return &DBCampaign{}, err
	}
	defer stmt.Close()

	_, err = stmt.Exec(reqdata, policy.Duration, policy.Threshold, policy.Window, delay.Global, delay.PerUsername, delay.PerLockout, delay.Window, delay.Random)
	if err != nil {
		return &DBCampaign{}, err
	}
	tx.Commit()
	rows, err := db.Query(`SELECT last_insert_rowid();`)
	defer rows.Close()
	var id int
	for rows.Next() {
		err = rows.Scan(&id)
		if err != nil {
			return &DBCampaign{}, err
		}
	}
	return &DBCampaign{
		RunID:           id,
		RequestData:     reqdata,
		PolicyDuration:  policy.Duration,
		PolicyThreshold: policy.Threshold,
		PolicyWindow:    policy.Window,
		DelayGlobal:     delay.Global,
		DelayPerUser:    delay.PerUsername,
		DelayPerLockout: delay.PerLockout,
		DelayWindow:     delay.Window,
	}, nil
}

func (*DBCampaign) RecordAttempt(db *sql.DB, attempt *Attempt) error {
	//&Entry{
	//	RunID:           1,
	//	Previous:        1,
	//	Start:           1,
	//	Stop:            1,
	//	Username:        "",
	//	Password:        "",
	//	Response:        []byte{},
	//	Success:         false,
	//	TimingInitial:   1,
	//	TimingDNSStart:  1,
	//	TimingDNSStop:   1,
	//	TimingTLSStart:  1,
	//	TimingTLSStop:   1,
	//	TimingConnStart: 1,
	//	TimingConnStop:  1,
	//	TimingFirstByte: 1,
	//	TimingLastByte:  1,
	//	Meta:            []byte{},
	//}, nil

	return nil
}

M timing.go => timing.go +1 -1
@@ 32,7 32,6 @@ func NewTimingCampaign(b *bufio.Reader, url *url.URL) (*TimingCampaign, error) {
		return &TimingCampaign{}, err
	}
	var tc = TimingCampaign{
		//Campaign:      m,
		Samples:       DefaultSamples,
		Threshold:     DefaultThreshold,
		Validate:      false,


@@ 73,6 72,7 @@ func (tc *TimingCampaign) Do() (Attempt, error) {
	attempt.Exchange.Response = x
	if err != nil {
		log.Fatal(err)
		return attempt, err
	}
	attempt.Exchange.Timing.LastByte = time.Now()
	attempt.Exchange.Timing.Real = attempt.Exchange.Timing.LastByte.Sub(attempt.Exchange.Timing.FirstByte)