├── README.md
├── Test.jpg
├── Test.png
├── Test2.png
├── pom.xml
├── springboot-vuldb.iml
├── src
└── main
│ └── java
│ └── me
│ └── gv7
│ └── woodpecker
│ └── plugin
│ ├── Bean
│ └── PropertiesBean.java
│ ├── InfoDetec
│ └── SpringBootInfoDetec.java
│ ├── SpringBootActuatorsInfoPlugin.java
│ ├── SpringBootInfoDetecPlugin.java
│ ├── SpringBootJolokiaPlugin.java
│ ├── SpringbootUtils.java
│ ├── WoodpeckerPluginManager.java
│ ├── exploits
│ ├── SpringBootActuatorsInfoExploit.java
│ └── SpringBootJolokiaExploit.java
│ └── pocs
│ └── SpringBootPocs.java
└── target
└── classes
└── me
└── gv7
└── woodpecker
└── plugin
├── Bean
└── PropertiesBean.class
├── SpringBootActuatorsInfoPlugin.class
├── SpringBootJolokiaPlugin.class
├── SpringbootUtils.class
├── WoodpeckerPluginManager.class
├── exploits
├── SpringBootActuatorsInfoExploit.class
└── SpringBootJolokiaExploit.class
└── pocs
└── SpringBootPocs.class
/README.md:
--------------------------------------------------------------------------------
1 | # Springboot漏洞全家桶
2 |
3 | - [x] SpringBoot Actuators 信息探测
4 | - [ ] Jolokia 端点 JNDI注入和RMI RCE
5 | - [ ] eureka 端点 XStream反序列化
6 |
7 | 
8 | 
--------------------------------------------------------------------------------
/Test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/Test.jpg
--------------------------------------------------------------------------------
/Test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/Test.png
--------------------------------------------------------------------------------
/Test2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/Test2.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | UTF-8
9 | 1.8
10 | 1.8
11 |
12 |
13 | me.gv7.woodpecker
14 | springboot-vuldb
15 | 0.1.1
16 |
17 |
18 |
19 | me.gv7.woodpecker
20 | woodpecker-sdk
21 | 0.1.0.beta5
22 |
23 |
24 |
25 | com.alibaba
26 | fastjson
27 | 1.2.69
28 |
29 |
30 | junit
31 | junit
32 | 4.11
33 | test
34 |
35 |
36 |
37 | net.dongliu
38 | requests
39 | 5.0.8
40 |
41 |
42 | me.gv7.woodpecker
43 | woodpecker-requests
44 | 0.2.0
45 | compile
46 |
47 |
48 | me.gv7.woodpecker
49 | woodpecker-sdk
50 | 0.3.0
51 | compile
52 |
53 |
54 |
--------------------------------------------------------------------------------
/springboot-vuldb.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/Bean/PropertiesBean.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin.Bean;
2 |
3 | public class PropertiesBean {
4 |
5 | String jvmName;
6 | String serverPort;
7 | String javaVersion;
8 | String userName;
9 | Boolean haveInfo;
10 |
11 | public Boolean getHaveInfo() {
12 | return haveInfo;
13 | }
14 |
15 | public void setHaveInfo(Boolean haveInfo) {
16 | this.haveInfo = haveInfo;
17 | }
18 |
19 | public String getJvmName() {
20 | return jvmName;
21 | }
22 |
23 | public void setJvmName(String jvmName) {
24 | this.jvmName = jvmName;
25 | }
26 |
27 | public String getServerPort() {
28 | return serverPort;
29 | }
30 |
31 | public void setServerPort(String serverPort) {
32 | this.serverPort = serverPort;
33 | }
34 |
35 | public String getJavaVersion() {
36 | return javaVersion;
37 | }
38 |
39 | public void setJavaVersion(String javaVersion) {
40 | this.javaVersion = javaVersion;
41 | }
42 |
43 |
44 | public String getUserName() {
45 | return userName;
46 | }
47 |
48 | public void setUserName(String userName) {
49 | this.userName = userName;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/InfoDetec/SpringBootInfoDetec.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin.InfoDetec;
2 |
3 | import me.gv7.woodpecker.plugin.*;
4 | import me.gv7.woodpecker.plugin.Bean.PropertiesBean;
5 | import me.gv7.woodpecker.requests.RawResponse;
6 | import me.gv7.woodpecker.requests.Requests;
7 |
8 | import java.util.*;
9 |
10 | public class SpringBootInfoDetec implements InfoDetector {
11 |
12 | List pointListV1 = new ArrayList<>();
13 | List pointListV2 = new ArrayList<>();
14 | Map h2Headers = new HashMap<>();
15 | boolean SpringbootVersionV1 = false;
16 | String[] basicPoint = new String[]{"cloudfoundryapplication","hystrix.stream" };
17 | PropertiesBean properties;
18 |
19 | public SpringBootInfoDetec(){
20 | h2Headers.put("Cache-Control", "max-age=0");
21 |
22 | pointListV1.add("autoconfig");
23 | pointListV1.add("heapdump");
24 | pointListV1.add("dump");
25 | pointListV1.add("mappings");
26 | pointListV1.add("auditevents");
27 | pointListV1.add("beans");
28 | pointListV1.add("health");
29 | pointListV1.add("configprops");
30 | pointListV1.add("info");
31 | pointListV1.add("loggers");
32 | pointListV1.add("threaddump");
33 | pointListV1.add("metrics");
34 | pointListV1.add("trace");
35 | pointListV1.add("env/spring.jmx.enabled");
36 |
37 |
38 | pointListV2.add("actuator/auditevents");
39 | pointListV2.add("actuator/beans");
40 | pointListV2.add("actuator/health");
41 | pointListV2.add("actuator/conditions");
42 | pointListV2.add("actuator/configprops");
43 | pointListV2.add("actuator/info");
44 | pointListV2.add("actuator/loggers");
45 | pointListV2.add("actuator/threaddump");
46 | pointListV2.add("actuator/metrics");
47 | pointListV2.add("actuator/httptrace");
48 | pointListV2.add("actuator/mappings");
49 | pointListV2.add("actuator/jolokia");
50 | pointListV2.add("actuator/hystrix.stream");
51 | pointListV2.add("actuator/env/spring.jmx.enabled");
52 |
53 | pointListV2.add("monitor/auditevents");
54 | pointListV2.add("monitor/beans");
55 | pointListV2.add("monitor/conditions");
56 | pointListV2.add("monitor/configprops");
57 | pointListV2.add("monitor/env");
58 | pointListV2.add("monitor/info");
59 | pointListV2.add("monitor/loggers");
60 | pointListV2.add("monitor/heapdump");
61 | pointListV2.add("monitor/threaddump");
62 | pointListV2.add("monitor/metrics");
63 | pointListV2.add("monitor/scheduledtasks");
64 | pointListV2.add("monitor/httptrace");
65 | pointListV2.add("monitor/mappings");
66 | pointListV2.add("monitor/jolokia");
67 | pointListV2.add("monitor/hystrix.stream");
68 | }
69 |
70 | @Override
71 | public String getInfoDetectorTabCaption() {
72 | return "SpringBoot Actuators Info探测";
73 | }
74 |
75 | @Override
76 | public IArgsUsageBinder getInfoDetectorCustomArgs() {
77 | return null;
78 | }
79 |
80 | @Override
81 | public LinkedHashMap doDetect(ITarget target, Map customArgs, IResultOutput result) throws Throwable {
82 | LinkedHashMap infos = new LinkedHashMap();
83 |
84 | String address = target.getRootAddress();
85 | try {
86 | if (SpringbootUtils.SpringbootCheck(address)){
87 | result.successPrintln("检测到springboot 404特征!");
88 | }
89 |
90 | if (SpringbootUtils.check404(address)){
91 | result.errorPrintln("默认404页面返回200,无法准确爆破!");
92 | }
93 | }catch (Exception e){
94 | result.errorPrintln(e.toString());
95 | }
96 | checkActuatorPointV1(address, result, infos);
97 | checkEnvPointV1(address, result, infos);
98 | if (!SpringbootVersionV1){
99 | checkActuatorPointV2(address, result, infos);
100 | checkEnvPointV2(address, result, infos);
101 | }
102 | checkJolokiaActuatorPoint(address, result, infos);
103 | checkJolokiaListPoint(address, result, infos);
104 | checkBasicPoint(address, result, infos);
105 | checkH2(address, result, infos);
106 | checkJenkins(address, result, infos);
107 | result.infoPrintln("检测流程结束");
108 |
109 | return infos;
110 | }
111 |
112 | // Spring Boot env端点存在环境属性覆盖和XStream反序列化漏洞
113 | private void checkEnvPointV1(String addr,IResultOutput result,LinkedHashMap infos){
114 | final String url = addr+"env";
115 | result.infoPrintln("正在检测:\t"+url);
116 | RawResponse response = getResponse(url);
117 | String resp = SpringbootUtils.scannerOutput(new Scanner(response.body()));
118 | if (response.statusCode() ==200){
119 | SpringbootVersionV1 = true;
120 | result.successPrintln("检测到env端点,Springboot 1.x: "+ url);
121 | infos.put(url, "Success");
122 | properties = SpringbootUtils.EnvParser(resp);
123 | parseProperties(properties, result);
124 | if (resp.contains("spring.cloud.bootstrap.location")){
125 | result.successPrintln(" [*]检测到spring.cloud.bootstrap.location属性,可进行环境属性覆盖RCE!");
126 | }else if(resp.contains("eureka.client.serviceUrl.defaultZone")){
127 | result.successPrintln(" [*]检测到eureka.client.serviceUrl.defaultZone属性,可进行XStream反序列化RCE!");
128 | }else if (resp.contains("spring.h2.console.enabled")){
129 | result.successPrintln(" [*]检测到配置了H2 console属性,可能可以进行h2反序列化RCE!");
130 | }
131 | }
132 | }
133 |
134 | // Spring Boot 2.x版本存在H2配置不当导致的RCE,目前非正则判断,测试阶段
135 | private void checkEnvPointV2(String addr,IResultOutput result,LinkedHashMap infos){
136 | final String url = addr+"actuator/env";
137 | result.infoPrintln("正在检测:\t"+url);
138 | RawResponse response = getResponse(url);
139 | String resp = SpringbootUtils.scannerOutput(new Scanner(response.body()));
140 | //String resp = response.readToText();
141 | if (response.statusCode() ==200){
142 | infos.put(url, "Success");
143 | result.successPrintln("检测到 env端点,Springboot 2.x: "+ url);
144 | properties = SpringbootUtils.EnvParser(resp);
145 | parseProperties(properties, result);
146 | if (resp.contains("spring.cloud.bootstrap.location")){
147 | result.successPrintln(" [*]检测到spring.cloud.bootstrap.location属性,可进行环境属性覆盖RCE!");
148 | }else if(resp.contains("eureka.client.serviceUrl.defaultZone")){
149 | result.successPrintln(" [*]检测到eureka.client.serviceUrl.defaultZone属性,可进行XStream反序列化RCE!");
150 | }else if (resp.contains("spring.h2.console.enabled")){
151 | result.successPrintln(" [*]检测到配置了H2 console属性,可能可以进行h2反序列化RCE!");
152 | }
153 | }
154 | RawResponse h2Response = Requests.post(addr + "actuator/restart")
155 | .verify(false)
156 | .headers(h2Headers)
157 | .send();
158 | result.infoPrintln("正在检测:\t"+addr + "actuator/restart");
159 | if (h2Response.statusCode() == 200){
160 | infos.put(addr + "actuator/restart", "Success");
161 | result.successPrintln(" [*]检测到env restart端点,可进行H2 RCE!");
162 | }
163 | }
164 |
165 | // Spring Boot 1.x版本端点在根URL下注册。
166 | private void checkActuatorPointV1(String addr,IResultOutput result,LinkedHashMap infos){
167 | for (String point: pointListV1){
168 | if (checkPoint(addr, point, result, infos)){
169 | SpringbootVersionV1 = true;
170 | }
171 | }
172 | }
173 |
174 | // Spring Boot 2.x版本端点移动到/actuator/路径
175 | private void checkActuatorPointV2(String addr,IResultOutput result,LinkedHashMap infos){
176 | for (String point: pointListV1){
177 | checkPoint(addr, point, result, infos);
178 | }
179 | }
180 |
181 | private void checkBasicPoint(String addr,IResultOutput result,LinkedHashMap infos){
182 | for (String point: basicPoint){
183 | checkPoint(addr, point, result, infos);
184 | }
185 | }
186 |
187 | private void checkJolokiaListPoint(String addr,IResultOutput result,LinkedHashMap infos){
188 | final String url = addr+"jolokia/list";
189 | RawResponse response = getResponse(url);
190 | result.infoPrintln("正在检测:\t"+url);
191 | checkJolokiaPoint(response, url, result, infos);
192 | }
193 |
194 | private void checkJolokiaActuatorPoint(String addr, IResultOutput result,LinkedHashMap infos){
195 | final String url = addr+"actuator/jolokia/list";
196 | RawResponse response = getResponse(url);
197 | result.infoPrintln("正在检测:\t"+url);
198 | checkJolokiaPoint(response, url, result, infos);
199 | }
200 |
201 | private void checkH2(String addr, IResultOutput result,LinkedHashMap infos){
202 | final String h2Url1 = addr+"h2";
203 | final String h2Url2 = addr+"h2-console";
204 | result.infoPrintln("正在检测:\t"+h2Url1);
205 | result.infoPrintln("正在检测:\t"+h2Url2);
206 | if(getResponse(h2Url1).statusCode()==200){
207 | infos.put(h2Url1, "Success");
208 | result.successPrintln("[*]检测到存在h2 console!");
209 | result.successPrintln(h2Url1);
210 | }else if(getResponse(h2Url2).statusCode()==200){
211 | infos.put(h2Url2, "Success");
212 | result.successPrintln("[*]检测到存在h2 console!");
213 | result.successPrintln(h2Url2);
214 | }
215 | }
216 |
217 | private void checkJenkins(String addr, IResultOutput result,LinkedHashMap infos) {
218 | final String jenkinsUrl = addr+"jenkins";
219 | result.infoPrintln("正在检测:\t"+jenkinsUrl);
220 | if (getResponse(jenkinsUrl).statusCode()==200){
221 | infos.put(jenkinsUrl, "Success");
222 | result.successPrintln("[*]检测到存在Jenkins!");
223 | result.successPrintln(jenkinsUrl);
224 | }
225 | }
226 |
227 | private void checkJolokiaPoint(RawResponse response,String url,IResultOutput result, LinkedHashMap infos){
228 | if (response.statusCode() ==200){
229 | String resp = SpringbootUtils.scannerOutput(new Scanner(response.body()));
230 | result.successPrintln("检测到 jolokia端点: "+ url);
231 | infos.put(url, "success");
232 | if (resp.contains("reloadByURL")){
233 | result.successPrintln(" [*]检测到reloadByURL方法,可进行XXE/RCE!");
234 | }else if(resp.contains("createJNDIRealm")){
235 | result.successPrintln(" [*]检测到createJNDIRealm方法,可进行JNDI注入!");
236 | }
237 | }
238 | }
239 |
240 | private boolean checkPoint(String addr,String point,IResultOutput result, LinkedHashMap infos){
241 | final String url = addr+point;
242 | RawResponse response = getResponse(url);
243 | result.infoPrintln("正在检测:\t"+url);
244 | if (response.statusCode()==200){
245 | if (SpringbootUtils.checkPoint(url, response)){
246 | result.successPrintln("检测到 "+ point +"端点,已做验证: "+ url);
247 | infos.put(url, "success");
248 | return true;
249 | }else {
250 | infos.put(url, "maybe");
251 | result.infoPrintln("检测到 "+ point +"端点,请手工验证: "+ url);
252 | }
253 |
254 | }
255 | return false;
256 | }
257 |
258 | private RawResponse getResponse(String url){
259 | return Requests.get(url).verify(false).timeout(10000).send();
260 | }
261 | private void parseProperties(PropertiesBean properties, IResultOutput result){
262 | if (properties.getHaveInfo()){
263 | result.infoPrintln("\tJVM信息:\t\t"+properties.getJvmName());
264 | result.infoPrintln("\t端口信息:\t\t"+properties.getServerPort());
265 | result.infoPrintln("\tJava版本:\t\t"+properties.getJavaVersion());
266 | result.infoPrintln("\t用户名:\t\t"+properties.getUserName());
267 | }
268 | }
269 |
270 | }
271 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/SpringBootActuatorsInfoPlugin.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin;
2 |
3 | import me.gv7.woodpecker.plugin.exploits.SpringBootActuatorsInfoExploit;
4 | import me.gv7.woodpecker.plugin.pocs.SpringBootPocs;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | public class SpringBootActuatorsInfoPlugin implements IVulPlugin {
10 | public static IVulPluginCallbacks callbacks;
11 | public static IPluginHelper pluginHelper;
12 |
13 | @Override
14 | public void VulPluginMain(IVulPluginCallbacks vulPluginCallbacks) {
15 | SpringBootActuatorsInfoPlugin.callbacks = vulPluginCallbacks;
16 | SpringBootActuatorsInfoPlugin.pluginHelper = callbacks.getPluginHelper();
17 |
18 | callbacks.setVulPluginName("Springboot接口探测");
19 | callbacks.setVulPluginVersion("0.1.0");
20 | callbacks.setVulPluginAuthor("notyeat");
21 | callbacks.setVulCVSS(9.5);
22 | callbacks.setVulName("Springboot接口探测");
23 | callbacks.setVulDescription("接口信息泄露,某些敏感接口会造成RCE");
24 | callbacks.setVulCategory("RCE");
25 | callbacks.setVulProduct("SpringBoot");
26 | callbacks.setVulId("woodpecker-2020-1021");
27 | callbacks.setVulSeverity("high");
28 |
29 | List exploitList = new ArrayList<>();
30 | exploitList.add(new SpringBootActuatorsInfoExploit());
31 | callbacks.registerExploit(exploitList);
32 |
33 | final List payloadGeneratorList = new ArrayList();
34 | callbacks.registerPayloadGenerator(payloadGeneratorList);
35 | callbacks.registerPoc(new SpringBootPocs());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/SpringBootInfoDetecPlugin.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin;
2 |
3 | import me.gv7.woodpecker.plugin.InfoDetec.SpringBootInfoDetec;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class SpringBootInfoDetecPlugin implements InfoDetectorPlugin{
9 |
10 | IPluginHelper pluginHelper;
11 |
12 | @Override
13 | public void InfoDetectorPluginMain(InfoDetectorPluginCallbacks callbacks) {
14 | pluginHelper = callbacks.getPluginHelper();
15 | callbacks.setInfoDetectorPluginAuthor("Ppsoft1991");
16 | callbacks.setInfoDetectorPluginName("SpringBoot漏洞检测");
17 | callbacks.setInfoDetectorPluginDescription("通过检测路径来判断Springboot漏洞");
18 | callbacks.setInfoDetectorPluginVersion("woodpecker-2020-1021");
19 | List infoDetecList = new ArrayList<>();
20 |
21 | infoDetecList.add(new SpringBootInfoDetec());
22 | callbacks.registerInfoDetector(infoDetecList);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/SpringBootJolokiaPlugin.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin;
2 |
3 | import me.gv7.woodpecker.plugin.exploits.SpringBootJolokiaExploit;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class SpringBootJolokiaPlugin implements IVulPlugin {
9 | public static IVulPluginCallbacks callbacks;
10 | public static IPluginHelper pluginHelper;
11 |
12 | @Override
13 | public void VulPluginMain(IVulPluginCallbacks vulPluginCallbacks) {
14 | SpringBootJolokiaPlugin.callbacks = vulPluginCallbacks;
15 | SpringBootJolokiaPlugin.pluginHelper = callbacks.getPluginHelper();
16 | callbacks.setVulPluginName("SpringBoot Jolokia 反序列化");
17 | callbacks.setVulPluginVersion("0.1.0");
18 | callbacks.setVulPluginAuthor("Frost Blue");
19 | callbacks.setVulCVSS(9.5);
20 | callbacks.setVulName("SpringBoot Jolokia 反序列化");
21 | callbacks.setVulDescription("反序列化可直接造成RCE");
22 | callbacks.setVulCategory("RCE");
23 | callbacks.setVulProduct("SpringBoot");
24 | callbacks.setVulSeverity("high");
25 |
26 | List exploitList = new ArrayList();
27 | exploitList.add(new SpringBootJolokiaExploit());
28 | callbacks.registerExploit(exploitList);
29 |
30 | final List payloadGeneratorList = new ArrayList();
31 | //payloadGeneratorList.add(new SeeyonHOSUploadPayloadGenerate());
32 | //callbacks.registerPoc(new SeeyonPoc());
33 | callbacks.registerPayloadGenerator(payloadGeneratorList);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/SpringbootUtils.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibaba.fastjson.JSONArray;
5 | import com.alibaba.fastjson.JSONObject;
6 | import me.gv7.woodpecker.plugin.Bean.PropertiesBean;
7 | import me.gv7.woodpecker.requests.RawResponse;
8 | import me.gv7.woodpecker.requests.Requests;
9 |
10 | import java.util.Map;
11 | import java.util.Scanner;
12 | import java.util.UUID;
13 |
14 | public class SpringbootUtils {
15 |
16 | public static boolean SpringbootCheck(String addr){
17 | final String url = addr+"404";
18 | RawResponse response = Requests.get(url).verify(false).timeout(10000).send();
19 | final int statusCode = response.statusCode();
20 | final String respText = response.readToText();
21 | if (statusCode == 404 || statusCode == 403){
22 | return respText.contains("Whitelabel Error Page") || respText.contains("There was an unexpected error");
23 | }
24 | return false;
25 | }
26 |
27 | public static boolean check404(String addr){
28 | final String url = addr + UUID.randomUUID();
29 | RawResponse response = Requests.get(url).verify(false).timeout(10000).send();
30 | final int statusCode = response.statusCode();
31 | return statusCode == 200;
32 | }
33 |
34 | // https://blog.csdn.net/testcs_dn/article/details/79033009/
35 | public static boolean checkPoint(String url, RawResponse resp){
36 | String s = resp.readToText();
37 | // hystrix.stream
38 | if (url.contains("hystrix.stream")){
39 | return "ping:".contains(s)||"data:".contains(s);
40 | // health
41 | }else if (url.contains("health")){
42 | try {
43 | Map res = (Map)JSON.parse(s);
44 | return res.containsKey("status")||res.containsKey("diskSpace");
45 | }catch (Exception e){
46 | return false;
47 | }
48 | // beans
49 | }else if (url.contains("beans")){
50 | Map res;
51 | try {
52 | JSONArray objects = JSON.parseArray(s);
53 | for (Object o:objects){
54 | res = (Map)o;
55 | return res.containsKey("bean")||res.containsKey("scope")||res.containsKey("dependencies");
56 | }
57 | }catch (Exception e){
58 | return false;
59 | }
60 | // configprops
61 | }else if (url.contains("configprops")){
62 | try {
63 | Map res = (Map)JSON.parse(s);
64 | return res.containsKey("configurationPropertiesReportEndpoint");
65 | }catch (Exception e){
66 | return false;
67 | }
68 | }else if (url.contains("mappings")){
69 | return s.contains("bean")||s.contains("method");
70 | }else if (url.contains("metrics")){
71 | return s.contains("threads")||s.contains("heap");
72 | }
73 | return false;
74 | }
75 |
76 | public static String scannerOutput(Scanner scanner){
77 | StringBuilder builder = new StringBuilder();
78 | while (scanner.hasNext()){
79 | builder.append(scanner.nextLine()).append("\n");
80 | }
81 | return builder.toString();
82 | }
83 |
84 | public static PropertiesBean EnvParser(String result){
85 | PropertiesBean propertiesBean = new PropertiesBean();
86 | try {
87 | JSONObject jsonObject = JSON.parseObject(result);
88 | JSONArray propertySources = jsonObject.getJSONArray("propertySources");
89 | JSONObject propertySource;
90 | if (propertySources.size() > 0) {
91 | for (int i = 0; i < propertySources.size(); i++) {
92 | propertySource = propertySources.getJSONObject(i);
93 | String name = (String) propertySource.get("name");
94 | if ("systemProperties".equals(name)) {
95 | JSONObject properties = propertySource.getJSONObject("properties");
96 | String jvmName = properties.getJSONObject("java.vm.name").getString("value");
97 | String javaVersion = properties.getJSONObject("java.runtime.version").getString("value");
98 | String userName = properties.getJSONObject("user.name").getString("value");
99 | propertiesBean.setHaveInfo(true);
100 | propertiesBean.setJvmName(jvmName);
101 | propertiesBean.setJavaVersion(javaVersion);
102 | propertiesBean.setUserName(userName);
103 | } else if ("server.ports".equals(name)) {
104 | propertiesBean.setHaveInfo(true);
105 | JSONObject properties = propertySource.getJSONObject("properties");
106 | String serverPort = properties.getJSONObject("local.server.port").getString("value");
107 | propertiesBean.setServerPort(serverPort);
108 | }
109 |
110 | }
111 |
112 | }
113 | }catch (Exception e){
114 | propertiesBean.setHaveInfo(false);
115 | }
116 | return propertiesBean;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/WoodpeckerPluginManager.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin;
2 |
3 | public class WoodpeckerPluginManager implements IPluginManager {
4 | public void registerPluginManagerCallbacks(IPluginManagerCallbacks pluginManagerCallbacks) {
5 | //pluginManagerCallbacks.registerVulPlugin(new SpringBootActuatorsInfoPlugin());
6 | pluginManagerCallbacks.registerInfoDetectorPlugin(new SpringBootInfoDetecPlugin());
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/exploits/SpringBootActuatorsInfoExploit.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin.exploits;
2 |
3 | import me.gv7.woodpecker.plugin.*;
4 | import me.gv7.woodpecker.plugin.Bean.PropertiesBean;
5 | import me.gv7.woodpecker.requests.RawResponse;
6 | import me.gv7.woodpecker.requests.Requests;
7 |
8 | import java.util.*;
9 |
10 | public class SpringBootActuatorsInfoExploit implements IExploit {
11 | List pointListV1 = new ArrayList<>();
12 | List pointListV2 = new ArrayList<>();
13 | Map h2Headers = new HashMap<>();
14 | boolean SpringbootVersionV1 = false;
15 | String[] basicPoint = new String[]{"cloudfoundryapplication","hystrix.stream" };
16 | PropertiesBean properties;
17 |
18 | public SpringBootActuatorsInfoExploit(){
19 | h2Headers.put("Cache-Control", "max-age=0");
20 |
21 | pointListV1.add("autoconfig");
22 | pointListV1.add("heapdump");
23 | pointListV1.add("dump");
24 | pointListV1.add("mappings");
25 | pointListV1.add("auditevents");
26 | pointListV1.add("beans");
27 | pointListV1.add("health");
28 | pointListV1.add("configprops");
29 | pointListV1.add("info");
30 | pointListV1.add("loggers");
31 | pointListV1.add("threaddump");
32 | pointListV1.add("metrics");
33 | pointListV1.add("trace");
34 | pointListV1.add("env/spring.jmx.enabled");
35 |
36 |
37 | pointListV2.add("actuator/auditevents");
38 | pointListV2.add("actuator/beans");
39 | pointListV2.add("actuator/health");
40 | pointListV2.add("actuator/conditions");
41 | pointListV2.add("actuator/configprops");
42 | pointListV2.add("actuator/info");
43 | pointListV2.add("actuator/loggers");
44 | pointListV2.add("actuator/threaddump");
45 | pointListV2.add("actuator/metrics");
46 | pointListV2.add("actuator/httptrace");
47 | pointListV2.add("actuator/mappings");
48 | pointListV2.add("actuator/jolokia");
49 | pointListV2.add("actuator/hystrix.stream");
50 | pointListV2.add("actuator/env/spring.jmx.enabled");
51 |
52 | pointListV2.add("monitor/auditevents");
53 | pointListV2.add("monitor/beans");
54 | pointListV2.add("monitor/conditions");
55 | pointListV2.add("monitor/configprops");
56 | pointListV2.add("monitor/env");
57 | pointListV2.add("monitor/info");
58 | pointListV2.add("monitor/loggers");
59 | pointListV2.add("monitor/heapdump");
60 | pointListV2.add("monitor/threaddump");
61 | pointListV2.add("monitor/metrics");
62 | pointListV2.add("monitor/scheduledtasks");
63 | pointListV2.add("monitor/httptrace");
64 | pointListV2.add("monitor/mappings");
65 | pointListV2.add("monitor/jolokia");
66 | pointListV2.add("monitor/hystrix.stream");
67 | }
68 |
69 | @Override
70 | public String getExploitTabCaption() {
71 | return "SpringBoot Actuators Info探测";
72 | }
73 |
74 | @Override
75 | public IArgsUsageBinder getExploitCustomArgs() {
76 | return null;
77 | }
78 |
79 | @Override
80 | public void doExploit(ITarget target, Map customArgs, IResultOutput result) {
81 | String address = target.getRootAddress();
82 | try {
83 | if (SpringbootUtils.SpringbootCheck(address)){
84 | result.successPrintln("检测到springboot 404特征!");
85 | }
86 |
87 | if (SpringbootUtils.check404(address)){
88 | result.errorPrintln("默认404页面返回200,无法准确爆破!");
89 | }
90 | }catch (Exception e){
91 | result.errorPrintln(e.toString());
92 | }
93 | checkActuatorPointV1(address, result);
94 | checkEnvPointV1(address, result);
95 | if (!SpringbootVersionV1){
96 | checkActuatorPointV2(address, result);
97 | checkEnvPointV2(address, result);
98 | }
99 | checkJolokiaActuatorPoint(address, result);
100 | checkJolokiaListPoint(address, result);
101 | checkBasicPoint(address, result);
102 | checkH2(address, result);
103 | checkJenkins(address, result);
104 | result.infoPrintln("检测流程结束");
105 | }
106 |
107 | // Spring Boot env端点存在环境属性覆盖和XStream反序列化漏洞
108 | private void checkEnvPointV1(String addr,IResultOutput result){
109 | final String url = addr+"env";
110 | RawResponse response = getResponse(url);
111 | String resp = SpringbootUtils.scannerOutput(new Scanner(response.body()));
112 | if (response.statusCode() ==200){
113 | SpringbootVersionV1 = true;
114 | result.successPrintln("检测到env端点,Springboot 1.x: "+ url);
115 | properties = SpringbootUtils.EnvParser(resp);
116 | parseProperties(properties, result);
117 | if (resp.contains("spring.cloud.bootstrap.location")){
118 | result.successPrintln(" [*]检测到spring.cloud.bootstrap.location属性,可进行环境属性覆盖RCE!");
119 | }else if(resp.contains("eureka.client.serviceUrl.defaultZone")){
120 | result.successPrintln(" [*]检测到eureka.client.serviceUrl.defaultZone属性,可进行XStream反序列化RCE!");
121 | }else if (resp.contains("spring.h2.console.enabled")){
122 | result.successPrintln(" [*]检测到配置了H2 console属性,可能可以进行h2反序列化RCE!");
123 | }
124 | }
125 | }
126 |
127 | // Spring Boot 2.x版本存在H2配置不当导致的RCE,目前非正则判断,测试阶段
128 | private void checkEnvPointV2(String addr,IResultOutput result){
129 | final String url = addr+"actuator/env";
130 | RawResponse response = getResponse(url);
131 | String resp = SpringbootUtils.scannerOutput(new Scanner(response.body()));
132 | //String resp = response.readToText();
133 | if (response.statusCode() ==200){
134 | result.successPrintln("检测到 env端点,Springboot 2.x: "+ url);
135 | properties = SpringbootUtils.EnvParser(resp);
136 | parseProperties(properties, result);
137 | if (resp.contains("spring.cloud.bootstrap.location")){
138 | result.successPrintln(" [*]检测到spring.cloud.bootstrap.location属性,可进行环境属性覆盖RCE!");
139 | }else if(resp.contains("eureka.client.serviceUrl.defaultZone")){
140 | result.successPrintln(" [*]检测到eureka.client.serviceUrl.defaultZone属性,可进行XStream反序列化RCE!");
141 | }else if (resp.contains("spring.h2.console.enabled")){
142 | result.successPrintln(" [*]检测到配置了H2 console属性,可能可以进行h2反序列化RCE!");
143 | }
144 | }
145 | RawResponse h2Response = Requests.post(addr + "actuator/restart")
146 | .verify(false)
147 | .headers(h2Headers)
148 | .send();
149 | if (h2Response.statusCode() == 200){
150 | result.successPrintln(" [*]检测到env restart端点,可进行H2 RCE!");
151 | }
152 | }
153 |
154 | // Spring Boot 1.x版本端点在根URL下注册。
155 | private void checkActuatorPointV1(String addr,IResultOutput result){
156 | for (String point: pointListV1){
157 | if (checkPoint(addr, point, result)){
158 | SpringbootVersionV1 = true;
159 | }
160 | }
161 | }
162 |
163 | // Spring Boot 2.x版本端点移动到/actuator/路径
164 | private void checkActuatorPointV2(String addr,IResultOutput result){
165 | for (String point: pointListV1){
166 | checkPoint(addr, point, result);
167 | }
168 | }
169 |
170 | private void checkBasicPoint(String addr,IResultOutput result){
171 | for (String point: basicPoint){
172 | checkPoint(addr, point, result);
173 | }
174 | }
175 |
176 | private void checkJolokiaListPoint(String addr,IResultOutput result){
177 | final String url = addr+"jolokia/list";
178 | RawResponse response = getResponse(url);
179 | checkJolokiaPoint(response, url, result);
180 | }
181 |
182 | private void checkJolokiaActuatorPoint(String addr, IResultOutput result){
183 | final String url = addr+"actuator/jolokia/list";
184 | RawResponse response = getResponse(url);
185 | checkJolokiaPoint(response, url, result);
186 | }
187 |
188 | private void checkH2(String addr, IResultOutput result){
189 | final String h2Url1 = addr+"h2";
190 | final String h2Url2 = addr+"h2-console";
191 | if(getResponse(h2Url1).statusCode()==200){
192 | result.successPrintln("[*]检测到存在h2 console!");
193 | result.successPrintln(h2Url1);
194 | }else if(getResponse(h2Url2).statusCode()==200){
195 | result.successPrintln("[*]检测到存在h2 console!");
196 | result.successPrintln(h2Url2);
197 | }
198 | }
199 |
200 | private void checkJenkins(String addr, IResultOutput result) {
201 | final String jenkinsUrl = addr+"jenkins";
202 | if (getResponse(jenkinsUrl).statusCode()==200){
203 | result.successPrintln("[*]检测到存在Jenkins!");
204 | result.successPrintln(jenkinsUrl);
205 | }
206 | }
207 |
208 | private void checkJolokiaPoint(RawResponse response,String url,IResultOutput result){
209 | if (response.statusCode() ==200){
210 | String resp = SpringbootUtils.scannerOutput(new Scanner(response.body()));
211 | result.successPrintln("检测到 jolokia端点: "+ url);
212 | if (resp.contains("reloadByURL")){
213 | result.successPrintln(" [*]检测到reloadByURL方法,可进行XXE/RCE!");
214 | }else if(resp.contains("createJNDIRealm")){
215 | result.successPrintln(" [*]检测到createJNDIRealm方法,可进行JNDI注入!");
216 | }
217 | }
218 | }
219 |
220 | private boolean checkPoint(String addr,String point,IResultOutput result){
221 | final String url = addr+point;
222 | RawResponse response = getResponse(url);
223 | if (response.statusCode()==200){
224 | if (SpringbootUtils.checkPoint(url, response)){
225 | result.successPrintln("检测到 "+ point +"端点,已做验证: "+ url);
226 | return true;
227 | }else {
228 | result.infoPrintln("检测到 "+ point +"端点,请手工验证: "+ url);
229 | }
230 |
231 | }
232 | return false;
233 | }
234 |
235 | private RawResponse getResponse(String url){
236 | return Requests.get(url).verify(false).timeout(10000).send();
237 | }
238 | private void parseProperties(PropertiesBean properties, IResultOutput result){
239 | if (properties.getHaveInfo()){
240 | result.infoPrintln("\tJVM信息:\t\t"+properties.getJvmName());
241 | result.infoPrintln("\t端口信息:\t\t"+properties.getServerPort());
242 | result.infoPrintln("\tJava版本:\t\t"+properties.getJavaVersion());
243 | result.infoPrintln("\t用户名:\t\t"+properties.getUserName());
244 | }
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/exploits/SpringBootJolokiaExploit.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin.exploits;
2 |
3 | import me.gv7.woodpecker.plugin.*;
4 | import java.util.Map;
5 |
6 | public class SpringBootJolokiaExploit implements IExploit {
7 | @Override
8 | public String getExploitTabCaption() {
9 | return "SpringBoot Jolokia RCE";
10 | }
11 |
12 | @Override
13 | public IArgsUsageBinder getExploitCustomArgs() {
14 | return null;
15 | }
16 |
17 | @Override
18 | public void doExploit(ITarget target, Map customArgs, IResultOutput result) {
19 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/me/gv7/woodpecker/plugin/pocs/SpringBootPocs.java:
--------------------------------------------------------------------------------
1 | package me.gv7.woodpecker.plugin.pocs;
2 |
3 | import me.gv7.woodpecker.plugin.*;
4 | import me.gv7.woodpecker.plugin.exploits.SpringBootActuatorsInfoExploit;
5 | import net.dongliu.requests.Requests;
6 |
7 | public class SpringBootPocs implements IPoc {
8 | @Override
9 | public IScanResult doVerify(ITarget target, IResultOutput resultOutput) {
10 | IScanResult result = SpringBootActuatorsInfoPlugin.pluginHelper.createScanResult();
11 | final String[] actuator = {"actuator/env", "env"};
12 | if (SpringbootUtils.SpringbootCheck(target.getAddress())){
13 | for (String a:actuator){
14 | String url = target.getAddress()+a;
15 | if (Requests.post(url).verify(false).send().statusCode()==200){
16 | resultOutput.infoPrintln("[+] "+url+"存在env端点!\n");
17 | }
18 | }
19 | result.setExists(true);
20 | }
21 | return result;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/target/classes/me/gv7/woodpecker/plugin/Bean/PropertiesBean.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/target/classes/me/gv7/woodpecker/plugin/Bean/PropertiesBean.class
--------------------------------------------------------------------------------
/target/classes/me/gv7/woodpecker/plugin/SpringBootActuatorsInfoPlugin.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/target/classes/me/gv7/woodpecker/plugin/SpringBootActuatorsInfoPlugin.class
--------------------------------------------------------------------------------
/target/classes/me/gv7/woodpecker/plugin/SpringBootJolokiaPlugin.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/target/classes/me/gv7/woodpecker/plugin/SpringBootJolokiaPlugin.class
--------------------------------------------------------------------------------
/target/classes/me/gv7/woodpecker/plugin/SpringbootUtils.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/target/classes/me/gv7/woodpecker/plugin/SpringbootUtils.class
--------------------------------------------------------------------------------
/target/classes/me/gv7/woodpecker/plugin/WoodpeckerPluginManager.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/target/classes/me/gv7/woodpecker/plugin/WoodpeckerPluginManager.class
--------------------------------------------------------------------------------
/target/classes/me/gv7/woodpecker/plugin/exploits/SpringBootActuatorsInfoExploit.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/target/classes/me/gv7/woodpecker/plugin/exploits/SpringBootActuatorsInfoExploit.class
--------------------------------------------------------------------------------
/target/classes/me/gv7/woodpecker/plugin/exploits/SpringBootJolokiaExploit.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/target/classes/me/gv7/woodpecker/plugin/exploits/SpringBootJolokiaExploit.class
--------------------------------------------------------------------------------
/target/classes/me/gv7/woodpecker/plugin/pocs/SpringBootPocs.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woodpecker-appstore/springboot-vuldb/838f70e35049d68a84f6f3cd46b941865e6c8a4a/target/classes/me/gv7/woodpecker/plugin/pocs/SpringBootPocs.class
--------------------------------------------------------------------------------