├── springScan.exe ├── README.md ├── spring.txt ├── springScan_s.go └── springScan.go /springScan.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyroxenites/SpringScan/HEAD/springScan.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpringScan 2 | spring框架多线程漏洞扫描 3 | 4 | 默认 5 | .\springScan.exe -u x.x.x.x 启动扫描 6 | 7 | ![效果图2](https://pic.imgdb.cn/item/60fd6dec5132923bf8a51b4a.png) 8 | -------------------------------------------------------------------------------- /spring.txt: -------------------------------------------------------------------------------- 1 | /v2/api-docs 2 | /swagger-ui.html 3 | /swagger 4 | /api-docs 5 | /api.html 6 | /swagger-ui 7 | /swagger/codes 8 | /api/index.html 9 | /api/v2/api-docs 10 | /v2/swagger.json 11 | /swagger-ui/html 12 | /distv2/index.html 13 | /swagger/index.html 14 | /sw/swagger-ui.html 15 | /api/swagger-ui.html 16 | /static/swagger.json 17 | /user/swagger-ui.html 18 | /swagger-ui/index.html 19 | /swagger-dubbo/api-docs 20 | /template/swagger-ui.html 21 | /swagger/static/index.html 22 | /dubbo-provider/distv2/index.html 23 | /spring-security-rest/api/swagger-ui.html 24 | /spring-security-oauth-resource/swagger-ui.html 25 | /mappings 26 | /metrics 27 | /beans 28 | /configprops 29 | /actuator/metrics 30 | /actuator/mappings 31 | /actuator/beans 32 | /actuator/configprops 33 | /actuator 34 | /auditevents 35 | /autoconfig 36 | /beans 37 | /caches 38 | /conditions 39 | /configprops 40 | /docs 41 | /dump 42 | /env 43 | /jolokia/list 44 | /flyway 45 | /health 46 | /heapdump 47 | /httptrace 48 | /info 49 | /intergrationgraph 50 | /jolokia 51 | /logfile 52 | /loggers 53 | /liquibase 54 | /metrics 55 | /mappings 56 | /prometheus 57 | /refresh 58 | /scheduledtasks 59 | /sessions 60 | /shutdown 61 | /trace 62 | /threaddump 63 | /actuator/auditevents 64 | /actuator/beans 65 | /actuator/health 66 | /actuator/conditions 67 | /actuator/configprops 68 | /actuator/env 69 | /actuator/info 70 | /actuator/loggers 71 | /actuator/heapdump 72 | /actuator/threaddump 73 | /actuator/metrics 74 | /actuator/scheduledtasks 75 | /actuator/httptrace 76 | /actuator/mappings 77 | /actuator/jolokia 78 | /actuator/hystrix.stream 79 | /actuator/env 80 | /refresh 81 | /actuator/refresh 82 | /restart 83 | /actuator/restart 84 | /jolokia 85 | /actuator/jolokia 86 | /trace 87 | /actuator/httptrace 88 | /article?id=${7*7} 89 | /article?id=66 90 | /h2-console -------------------------------------------------------------------------------- /springScan_s.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "crypto/tls" 6 | "flag" 7 | "fmt" 8 | "net/http" 9 | "os" 10 | "strconv" 11 | "strings" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | var ( 17 | numberTasks []string 18 | the_returned_result_is_200 []string 19 | list_of_errors []string 20 | t = &http.Transport{ 21 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 22 | } 23 | src_file string 24 | target_file string 25 | des_file string 26 | routineCountTotal int 27 | url string 28 | targetPaths []string 29 | ) 30 | 31 | func title() { 32 | fmt.Println(` 33 | ▄████ ▒█████ 34 | ██▒ ▀█▒▒██▒ ██▒ 35 | ▒██░▄▄▄░▒██░ ██▒ 36 | ░▓█ ██▓▒██ ██░ 37 | ░▒▓███▀▒░ ████▓▒░ 38 | ░▒ ▒ ░ ▒░▒░▒░ 39 | ░ ░ ░ ▒ ▒░ 40 | ░ ░ ░ ░ ░ ░ ▒ 41 | ░ ░ ░ 42 | Here is springScan. 43 | `) 44 | } 45 | func main() { 46 | flag.StringVar(&src_file, "s", "spring.txt", "字典文件") 47 | flag.StringVar(&target_file, "f", "url.txt", "目标网站文件") 48 | flag.StringVar(&url, "u", "", "目标url") 49 | flag.StringVar(&des_file, "d", "result.txt", "结果文件") 50 | flag.IntVar(&routineCountTotal, "t", 40, "线程数量{默认为40}") 51 | flag.Parse() 52 | title() 53 | file, err := os.Open(src_file) 54 | if err != nil { 55 | fmt.Println("打开文件时候出错") 56 | } 57 | defer func() { 58 | file.Close() 59 | }() 60 | n := bufio.NewScanner(file) 61 | for n.Scan() { 62 | data := n.Text() 63 | numberTasks = append(numberTasks, data) 64 | 65 | } 66 | 67 | targetPaths, err = OpenTargetFile(target_file) 68 | if err != nil { 69 | fmt.Printf("open target file failed, msg:%v\n", err) 70 | } 71 | 72 | fmt.Printf("numberTasks: %v\n", numberTasks) 73 | client = &http.Client{ 74 | Transport: t, 75 | Timeout: 20 * time.Second, 76 | } 77 | beg := time.Now() 78 | wg := &sync.WaitGroup{} 79 | tasks := make(chan string) 80 | results := make(chan string) 81 | go func() { 82 | for result := range results { 83 | if result == "" { 84 | close(results) 85 | } else if strings.Contains(result, "200") { 86 | fmt.Printf("result loop:%v\n", result) 87 | the_returned_result_is_200 = append(the_returned_result_is_200, result) 88 | } else { 89 | list_of_errors = append(list_of_errors, result) 90 | } 91 | } 92 | }() 93 | 94 | for _, path := range targetPaths { 95 | for i := 0; i < routineCountTotal; i++ { 96 | wg.Add(1) 97 | go worker(wg, tasks, results, path) 98 | } 99 | for _, task := range numberTasks { 100 | tasks <- task 101 | } 102 | } 103 | 104 | tasks <- "" 105 | wg.Wait() 106 | results <- "" 107 | fmt.Println("\033[33m+++++++++++++++++++请求成功的++++++++++++++++++++++") 108 | 109 | file_1, err := os.OpenFile(des_file, os.O_WRONLY|os.O_CREATE, 0666) 110 | if err != nil { 111 | fmt.Println("文件打开失败", err) 112 | } 113 | defer file_1.Close() 114 | write_1 := bufio.NewWriter(file_1) 115 | for _, v := range the_returned_result_is_200 { 116 | fmt.Println(v) 117 | write_1.WriteString(v + "\n") 118 | } 119 | write_1.Flush() 120 | fmt.Println("发生了", len(list_of_errors), "个失败") 121 | fmt.Printf("time consumed: %fs\n", time.Now().Sub(beg).Seconds()) 122 | fmt.Println("具体接口用法请参考:https://github.com/LandGrey/SpringBootVulExploit") 123 | 124 | } 125 | 126 | func worker(group *sync.WaitGroup, tasks chan string, result chan string, path string) { 127 | for task := range tasks { 128 | if task == "" { 129 | close(tasks) 130 | } else { 131 | respBody, err := NumberQueryRequest(task, path) 132 | if err != nil { 133 | fmt.Printf("error occurred in NumberQueryRequest: %s\n", task) 134 | result <- err.Error() 135 | } else { 136 | result <- respBody 137 | } 138 | } 139 | } 140 | group.Done() 141 | } 142 | 143 | var client *http.Client 144 | 145 | func NumberQueryRequest(keyword string, path string) (body string, err error) { 146 | url := fmt.Sprintf("%s%s", path, keyword) 147 | fmt.Println(url) 148 | req, err := http.NewRequest("GET", url, nil) 149 | if err != nil { 150 | return "构造请求出错", err 151 | } 152 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36") 153 | resp, err := client.Get(url) 154 | if err != nil { 155 | return "发送请求出错", err 156 | } 157 | return_value := resp.StatusCode 158 | if resp != nil && resp.Body != nil { 159 | defer resp.Body.Close() 160 | } 161 | body = "url:" + url + " || " + "返回值:" + strconv.Itoa(return_value) 162 | return body, nil 163 | 164 | } 165 | 166 | func OpenTargetFile(targetFileName string) (targetTasks []string, err error) { 167 | file, err := os.Open(targetFileName) 168 | if err != nil { 169 | fmt.Println("打开文件时候出错") 170 | } 171 | defer func() { 172 | file.Close() 173 | }() 174 | n := bufio.NewScanner(file) 175 | for n.Scan() { 176 | data := n.Text() 177 | targetTasks = append(targetTasks, data) 178 | 179 | } 180 | fmt.Printf("targetTasks: %v\n", targetTasks) 181 | return 182 | } -------------------------------------------------------------------------------- /springScan.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "crypto/tls" 6 | "flag" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "strconv" 12 | "strings" 13 | "sync" 14 | "time" 15 | ) 16 | 17 | var ( 18 | numberTasks []string 19 | the_returned_result_is_200 []string 20 | list_of_errors []string 21 | t = &http.Transport{ 22 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 23 | } 24 | src_file string 25 | des_file string 26 | routineCountTotal int 27 | urls string 28 | ) 29 | 30 | func title() { 31 | fmt.Println(` 32 | ▄████ ▒█████ 33 | ██▒ ▀█▒▒██▒ ██▒ 34 | ▒██░▄▄▄░▒██░ ██▒ 35 | ░▓█ ██▓▒██ ██░ 36 | ░▒▓███▀▒░ ████▓▒░ 37 | ░▒ ▒ ░ ▒░▒░▒░ 38 | ░ ░ ░ ▒ ▒░ 39 | ░ ░ ░ ░ ░ ░ ▒ 40 | ░ ░ ░ 41 | `) 42 | } 43 | func main() { 44 | flag.StringVar(&src_file, "s", "spring.txt", "字典文件") 45 | flag.StringVar(&urls, "u", "", "目标url") 46 | flag.StringVar(&des_file, "d", "result.txt", "结果文件") 47 | flag.IntVar(&routineCountTotal, "t", 40, "线程数量{默认为40}") 48 | flag.Parse() 49 | title() 50 | file, err := os.Open(src_file) 51 | if err != nil { 52 | fmt.Println("打开文件时候出错") 53 | } 54 | defer func() { 55 | file.Close() 56 | }() 57 | n := bufio.NewScanner(file) 58 | for n.Scan() { 59 | data := n.Text() 60 | numberTasks = append(numberTasks, data) 61 | 62 | } 63 | client = &http.Client{ 64 | Transport: t, 65 | Timeout: 20 * time.Second, 66 | } 67 | beg := time.Now() 68 | wg := &sync.WaitGroup{} 69 | tasks := make(chan string) 70 | results := make(chan string) 71 | go func() { 72 | for result := range results { 73 | if result == "" { 74 | close(results) 75 | } else if strings.Contains(result, "200") || strings.Contains(result, "端点") { 76 | fmt.Println(result) 77 | the_returned_result_is_200 = append(the_returned_result_is_200, result) 78 | } else if strings.Contains(result, "500") { 79 | if strings.Contains(result, "article") { 80 | fmt.Println(result) 81 | the_returned_result_is_200 = append(the_returned_result_is_200, result) 82 | } 83 | } else { 84 | list_of_errors = append(list_of_errors, result) 85 | } 86 | } 87 | }() 88 | for i := 0; i < routineCountTotal; i++ { 89 | wg.Add(1) 90 | go worker(wg, tasks, results) 91 | } 92 | for _, task := range numberTasks { 93 | tasks <- task 94 | } 95 | tasks <- "" 96 | wg.Wait() 97 | results <- "" 98 | fmt.Println("\033[33m+++++++++++++++++++请求成功的++++++++++++++++++++++") 99 | 100 | file_1, err := os.OpenFile(des_file, os.O_WRONLY|os.O_CREATE, 0666) 101 | if err != nil { 102 | fmt.Println("文件打开失败", err) 103 | } 104 | defer file_1.Close() 105 | write_1 := bufio.NewWriter(file_1) 106 | for _, v := range the_returned_result_is_200 { 107 | fmt.Println(v) 108 | write_1.WriteString(v + "\n") 109 | } 110 | write_1.Flush() 111 | fmt.Println("发生了", len(list_of_errors), "个失败") 112 | fmt.Printf("time consumed: %fs\n", time.Now().Sub(beg).Seconds()) 113 | fmt.Println("具体接口用法请参考:https://github.com/LandGrey/SpringBootVulExploit") 114 | fmt.Println("小提醒:ctrl+单击会打开链接\033[0m") 115 | } 116 | 117 | func worker(group *sync.WaitGroup, tasks chan string, result chan string) { 118 | for task := range tasks { 119 | if task == "" { 120 | close(tasks) 121 | } else { 122 | respBody, err := NumberQueryRequest(task) 123 | if err != nil { 124 | fmt.Printf("error occurred in NumberQueryRequest: %s\n", task) 125 | result <- err.Error() 126 | } else { 127 | result <- respBody 128 | } 129 | } 130 | } 131 | group.Done() 132 | } 133 | 134 | var client *http.Client 135 | 136 | func NumberQueryRequest(keyword string) (body string, err error) { 137 | urls = strings.TrimRight(urls, "/") 138 | url := fmt.Sprintf("%s%s", urls, keyword) 139 | fmt.Println(url) 140 | req, err := http.NewRequest("GET", url, nil) 141 | if err != nil { 142 | return "构造请求出错", err 143 | } 144 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36") 145 | resp, err := client.Get(url) 146 | if err != nil { 147 | return "发送请求出错", err 148 | } 149 | return_value := resp.StatusCode 150 | if resp != nil && resp.Body != nil { 151 | defer resp.Body.Close() 152 | } 153 | if strings.Contains(keyword, "/env") { 154 | body22, _ := ioutil.ReadAll(resp.Body) 155 | if strings.Contains(string(body22), "spring.cloud.bootstrap.location") { 156 | body = "url: " + url + " || " + "目标站点开启了 env 端点且spring.cloud.bootstrap.location属性开启,可进行环境属性覆盖RCE测试" 157 | return body, nil 158 | } else if strings.Contains(string(body22), "eureka.client.serviceUrl.defaultZone") { 159 | body = "url: " + url + " || " + "目标站点开启了 env 端点且eureka.client.serviceUrl.defaultZone属性开启,可进行XStream反序列化RCE测试" 160 | return body, nil 161 | } 162 | } else if strings.Contains(keyword, "/jolokia/list") { 163 | body33, _ := ioutil.ReadAll(resp.Body) 164 | if strings.Contains(string(body33), "reloadByURL") { 165 | body = "url: " + url + " || " + "目标站点开启了 jolokia 端点且存在reloadByURL方法,可进行XXE/RCE测试" 166 | return body, nil 167 | } else if strings.Contains(string(body33), "createJNDIRealm") { 168 | body = "url: " + url + " || " + "目标站点开启了 jolokia 端点且存在createJNDIRealm方法,可进行JNDI注入RCE测试" 169 | return body, nil 170 | } 171 | } 172 | body = "url:" + url + " || " + "返回值:" + strconv.Itoa(return_value) 173 | return body, nil 174 | 175 | } 176 | --------------------------------------------------------------------------------