Skip to content

Commit 9c1d757

Browse files
author
metaStor
committed
CVE-2022-22947加入常见目录/prod-api
1 parent eaa18d9 commit 9c1d757

File tree

5 files changed

+75
-12
lines changed

5 files changed

+75
-12
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@
5858

5959
检测方法:
6060

61-
* 首先随机访问一个不存在的路径,根据特征`Whitelabel Error Page`判断是否是Spring框架(1.x/2.x);是则打POC,分五个请求:`包含恶意SpEL表达式的路由 -> 刷新路由 -> 访问添加的路由查看RCE结果 -> 删除路由 -> 刷新路由`
61+
* 两种方法判断是否是SpringGateway:
62+
* 1.随机访问一个不存在的路径,根据特征`Whitelabel Error Page`判断是否是Spring框架(1.x/2.x);
63+
* 2.直接访问/actuator/gateway/routes、/prod-api/actuator/gateway/routes,根据特征`route_id`判断;
64+
* 3.POC分五个请求:`包含恶意SpEL表达式的路由 -> 刷新路由 -> 访问添加的路由查看RCE结果 -> 删除路由 -> 刷新路由`
6265

6366
## 插件情况
6467

src/main/java/burp/BurpExtender.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
public class BurpExtender implements IBurpExtender, IExtensionStateListener
1515
{
1616
private final String NAME = "SpringScan";
17-
private final String VERSION = "1.6";
17+
private final String VERSION = "1.7";
1818

1919
public IBurpExtenderCallbacks callbacks;
2020
public IExtensionHelpers helpers;

src/main/java/burp/scan/Scanner.java

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,10 +481,18 @@ private byte[] CloudFunctionSpelPOC(IHttpRequestResponse httpRequestResponse, St
481481
* @return IScanIssue
482482
*/
483483
private IScanIssue CloudGatewayScan(IHttpRequestResponse httpRequestResponse) {
484+
boolean isProdApi = false;
484485
// 这里判断是否有spring 404的特征: Whitelabel Error Page
485-
if (!this.isSpringFinger(httpRequestResponse, false) && !this.isSpringFinger(httpRequestResponse, true)) return null;
486+
if (!this.isSpringFinger(httpRequestResponse, false) && !this.isSpringFinger(httpRequestResponse, true)) {
487+
// 无spring 404特征的情况下判断是否有routes
488+
if (this.isSpringGatewayFinger(httpRequestResponse, true)) {
489+
isProdApi = true;
490+
} else if (!this.isSpringGatewayFinger(httpRequestResponse, false)){
491+
return null;
492+
}
493+
}
486494
URL url = this.helpers.analyzeRequest(httpRequestResponse).getUrl();
487-
String uri = Utils.getUri(url.toString());
495+
String uri = Utils.getUri(url.toString()) + (isProdApi ? "prod-api/" : "");
488496
String random_uri = Utils.randomStr(5);
489497
if (this.CloudGatewayRegisterRoute(httpRequestResponse, uri, random_uri, "whoami")) {
490498
if (this.CloudGatewayRefresh(httpRequestResponse, uri)) {
@@ -684,6 +692,54 @@ private boolean isSpringFinger(IHttpRequestResponse httpRequestResponse, boolean
684692
return false;
685693
}
686694

695+
/**
696+
*
697+
* SpringGateway
698+
* 访问/actuator/gateway/routes、/prod-api/actuator/gateway/routes
699+
* 判断响应内容是否有SpringGateway特征: route_id
700+
*
701+
* @param httpRequestResponse
702+
* @return
703+
*/
704+
private boolean isSpringGatewayFinger(IHttpRequestResponse httpRequestResponse, boolean isProdApi) {
705+
try {
706+
IRequestInfo requestInfo = this.helpers.analyzeRequest(httpRequestResponse);
707+
IHttpService service = httpRequestResponse.getHttpService();
708+
String url = Utils.getUri(requestInfo.getUrl().toString());
709+
if (isProdApi) {
710+
url = url + "prod-api/actuator/gateway/routes";
711+
} else {
712+
url = url + "actuator/gateway/routes";
713+
}
714+
byte[] newRequest = this.helpers.buildHttpRequest(new URL(service.getProtocol(), service.getHost(), service.getPort(), url));
715+
requestInfo = this.helpers.analyzeRequest(service, newRequest); // 重新打包好新的uri请求
716+
// header中Accpet: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
717+
List<String> headers = requestInfo.getHeaders();
718+
for (String header: headers) {
719+
if (header.startsWith("Accept")) { // 坑点: 带冒号匹配会报错
720+
headers.remove(header);
721+
headers.add("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8");
722+
break;
723+
}
724+
}
725+
// 截取新请求, buildHttpRequest()之后会包含原本的GET请求内容和自定义构造的headers内容, 所以要截取
726+
IRequestInfo requestInfo1 = this.helpers.analyzeRequest(service, newRequest);
727+
newRequest = new String(newRequest).substring(requestInfo1.getBodyOffset()).getBytes();
728+
// 组装请求
729+
newRequest = this.helpers.buildHttpMessage(headers, newRequest);
730+
IHttpRequestResponse requestResponse = this.burpExtender.callbacks.makeHttpRequest(httpRequestResponse.getHttpService(), newRequest);
731+
String body = new String(requestResponse.getResponse()).substring(this.helpers.analyzeResponse(requestResponse.getResponse()).getBodyOffset()).toLowerCase();
732+
if (body.contains("route_id")) {
733+
this.burpExtender.stdout.println("[*] Detect SpringGateway Finger: " + url);
734+
return true;
735+
}
736+
} catch (MalformedURLException e) {
737+
e.printStackTrace();
738+
this.burpExtender.stderr.println(e.getMessage());
739+
}
740+
return false;
741+
}
742+
687743
/**
688744
* 随机Agent头
689745
* 先将原来的Agent删掉,再添加随机Agent

src/main/java/burp/util/Utils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ public static String getUriExt(String url) {
9393
*/
9494
public static String getUri(String url) {
9595
url = url.replace("https://", "").replace("http://", ""); // 截去http://或https://
96-
String pureUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length()); // 排除参数和锚点
96+
String pureUrl = url.substring(0, url.contains("#") ? url.indexOf("#") : url.length()); // 排除锚点
97+
pureUrl = pureUrl.substring(0, pureUrl.contains("?") ? pureUrl.indexOf("?") : pureUrl.length()); // 排除参数
9798
pureUrl = pureUrl.substring(pureUrl.contains("/") ? pureUrl.indexOf("/") : pureUrl.length(), pureUrl.contains("/") ? pureUrl.lastIndexOf("/") : pureUrl.length()); // 取最后一个/之前的uri
9899
return pureUrl + "/";
99100
}

src/test/java/Test.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import burp.util.Utils;
2+
13
/**
24
* @author : metaStor
35
* @date : Created 2022/4/6 10:05 PM
@@ -6,12 +8,13 @@
68
public class Test {
79

810
public static void main(String[] args) throws InterruptedException {
9-
String url = String.valueOf("http://localhost:8088/x?test=1").split("\\?")[0]; // 获取?之前的url
10-
String url2 = String.valueOf("http://localhost:8080/").split("\\?")[0]; // 获取?之前的url
11-
System.out.println(url);
12-
System.out.println(url2);
13-
String root = "u3yffici9aabcqyfm0gv616ih9nzbo.burpcollaborator.net";
14-
String test = "12345." + root;
15-
System.out.println(test.split("\\." + root)[0]);
11+
// String url = String.valueOf("http://localhost:8088/x?test=1").split("\\?")[0]; // 获取?之前的url
12+
// String url2 = String.valueOf("http://localhost:8080/").split("\\?")[0]; // 获取?之前的url
13+
// System.out.println(url);
14+
// System.out.println(url2);
15+
// String root = "u3yffici9aabcqyfm0gv616ih9nzbo.burpcollaborator.net";
16+
// String test = "12345." + root;
17+
// System.out.println(test.split("\\." + root)[0]);
18+
System.out.println(Utils.getUri("http://218.203.103.138:9110/#/login?redirect=%2F"));
1619
}
1720
}

0 commit comments

Comments
 (0)